Skip to content

Commit 59bc465

Browse files
committed
Model Bool < U32 < Felt during type inference
1 parent da4b36e commit 59bc465

File tree

3 files changed

+160
-7
lines changed

3 files changed

+160
-7
lines changed

src/types/domain.rs

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,31 @@ impl InferredType {
2323
/// Combine two values from different control-flow paths.
2424
///
2525
/// `Unknown` means at least one path is opaque, so the result is unknown.
26-
/// Distinct known subtypes join to `Felt`.
26+
/// Distinct known subtypes join to their least upper bound in the lattice
27+
/// `Bool <: U32 <: Felt`, with `Address` as a sibling of `U32`.
2728
pub fn combine_paths(self, other: Self) -> Self {
2829
match (self, other) {
2930
(a, b) if a == b => a,
3031
(Self::Unknown, _) | (_, Self::Unknown) => Self::Unknown,
32+
// Bool <: U32
33+
(Self::Bool, Self::U32) | (Self::U32, Self::Bool) => Self::U32,
3134
_ => Self::Felt,
3235
}
3336
}
3437

3538
/// Refine a stored type with newly inferred information.
3639
///
3740
/// Existing `Unknown` values are replaced by `new_type`. Existing known
38-
/// values are widened conservatively if they disagree.
41+
/// values are widened to their least upper bound in the lattice
42+
/// `Bool <: U32 <: Felt` if they disagree, with `Address` as a sibling
43+
/// of `U32`.
3944
pub fn refine(self, new_type: Self) -> Self {
4045
match (self, new_type) {
4146
(Self::Unknown, t) => t,
4247
(t, Self::Unknown) => t,
4348
(a, b) if a == b => a,
49+
// Bool <: U32
50+
(Self::Bool, Self::U32) | (Self::U32, Self::Bool) => Self::U32,
4451
_ => Self::Felt,
4552
}
4653
}
@@ -140,7 +147,7 @@ pub fn check_compatibility(actual: InferredType, expected: TypeRequirement) -> C
140147
},
141148
TypeRequirement::U32 => match actual {
142149
InferredType::Unknown => Compatibility::Indeterminate,
143-
InferredType::U32 => Compatibility::Compatible,
150+
InferredType::U32 | InferredType::Bool => Compatibility::Compatible,
144151
_ => Compatibility::Incompatible,
145152
},
146153
TypeRequirement::Address => match actual {
@@ -187,6 +194,8 @@ impl VarKey {
187194
mod tests {
188195
use super::*;
189196

197+
// -- check_compatibility --------------------------------------------------
198+
190199
#[test]
191200
fn felt_accepts_promotable_types() {
192201
assert_eq!(
@@ -203,16 +212,36 @@ mod tests {
203212
);
204213
}
205214

215+
#[test]
216+
fn bool_is_compatible_with_u32() {
217+
assert_eq!(
218+
check_compatibility(InferredType::Bool, TypeRequirement::U32),
219+
Compatibility::Compatible
220+
);
221+
}
222+
206223
#[test]
207224
fn mismatched_exact_types_are_incompatible() {
208225
assert_eq!(
209226
check_compatibility(InferredType::Felt, TypeRequirement::Bool),
210227
Compatibility::Incompatible
211228
);
229+
assert_eq!(
230+
check_compatibility(InferredType::Felt, TypeRequirement::U32),
231+
Compatibility::Incompatible
232+
);
233+
assert_eq!(
234+
check_compatibility(InferredType::U32, TypeRequirement::Bool),
235+
Compatibility::Incompatible
236+
);
212237
assert_eq!(
213238
check_compatibility(InferredType::Bool, TypeRequirement::Address),
214239
Compatibility::Incompatible
215240
);
241+
assert_eq!(
242+
check_compatibility(InferredType::Address, TypeRequirement::Bool),
243+
Compatibility::Incompatible
244+
);
216245
}
217246

218247
#[test]
@@ -226,4 +255,124 @@ mod tests {
226255
Compatibility::Indeterminate
227256
);
228257
}
258+
259+
// -- combine_paths --------------------------------------------------------
260+
261+
#[test]
262+
fn combine_paths_same_type() {
263+
assert_eq!(
264+
InferredType::Bool.combine_paths(InferredType::Bool),
265+
InferredType::Bool
266+
);
267+
assert_eq!(
268+
InferredType::U32.combine_paths(InferredType::U32),
269+
InferredType::U32
270+
);
271+
assert_eq!(
272+
InferredType::Felt.combine_paths(InferredType::Felt),
273+
InferredType::Felt
274+
);
275+
}
276+
277+
#[test]
278+
fn combine_paths_bool_u32_yields_u32() {
279+
assert_eq!(
280+
InferredType::Bool.combine_paths(InferredType::U32),
281+
InferredType::U32
282+
);
283+
assert_eq!(
284+
InferredType::U32.combine_paths(InferredType::Bool),
285+
InferredType::U32
286+
);
287+
}
288+
289+
#[test]
290+
fn combine_paths_with_felt_yields_felt() {
291+
assert_eq!(
292+
InferredType::Bool.combine_paths(InferredType::Felt),
293+
InferredType::Felt
294+
);
295+
assert_eq!(
296+
InferredType::U32.combine_paths(InferredType::Felt),
297+
InferredType::Felt
298+
);
299+
assert_eq!(
300+
InferredType::Address.combine_paths(InferredType::Felt),
301+
InferredType::Felt
302+
);
303+
}
304+
305+
#[test]
306+
fn combine_paths_address_siblings_yield_felt() {
307+
assert_eq!(
308+
InferredType::Bool.combine_paths(InferredType::Address),
309+
InferredType::Felt
310+
);
311+
assert_eq!(
312+
InferredType::U32.combine_paths(InferredType::Address),
313+
InferredType::Felt
314+
);
315+
}
316+
317+
#[test]
318+
fn combine_paths_unknown_yields_unknown() {
319+
assert_eq!(
320+
InferredType::Bool.combine_paths(InferredType::Unknown),
321+
InferredType::Unknown
322+
);
323+
assert_eq!(
324+
InferredType::Unknown.combine_paths(InferredType::U32),
325+
InferredType::Unknown
326+
);
327+
}
328+
329+
// -- refine ---------------------------------------------------------------
330+
331+
#[test]
332+
fn refine_unknown_takes_new() {
333+
assert_eq!(
334+
InferredType::Unknown.refine(InferredType::Bool),
335+
InferredType::Bool
336+
);
337+
assert_eq!(
338+
InferredType::Unknown.refine(InferredType::U32),
339+
InferredType::U32
340+
);
341+
}
342+
343+
#[test]
344+
fn refine_bool_u32_yields_u32() {
345+
assert_eq!(
346+
InferredType::Bool.refine(InferredType::U32),
347+
InferredType::U32
348+
);
349+
assert_eq!(
350+
InferredType::U32.refine(InferredType::Bool),
351+
InferredType::U32
352+
);
353+
}
354+
355+
#[test]
356+
fn refine_with_felt_yields_felt() {
357+
assert_eq!(
358+
InferredType::Bool.refine(InferredType::Felt),
359+
InferredType::Felt
360+
);
361+
assert_eq!(
362+
InferredType::U32.refine(InferredType::Felt),
363+
InferredType::Felt
364+
);
365+
}
366+
367+
#[test]
368+
fn refine_address_siblings_yield_felt() {
369+
assert_eq!(
370+
InferredType::Bool.refine(InferredType::Address),
371+
InferredType::Felt
372+
);
373+
assert_eq!(
374+
InferredType::U32.refine(InferredType::Address),
375+
InferredType::Felt
376+
);
377+
}
229378
}

src/types/intra.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,13 @@ impl<'a> ProcTypeAnalyzer<'a> {
314314
}
315315

316316
/// Infer type for a constant expression.
317+
///
318+
/// `Felt(0)` and `Felt(1)` are inferred as `Bool` since they are the
319+
/// most precise type for these values. The lattice `Bool <: U32 <: Felt`
320+
/// ensures they widen correctly in arithmetic or u32 contexts.
317321
fn infer_constant_type(&self, constant: &Constant) -> InferredType {
318322
match constant {
323+
Constant::Felt(0 | 1) => InferredType::Bool,
319324
Constant::Felt(_) | Constant::Defined(_) => InferredType::Felt,
320325
Constant::Word(_) => InferredType::Unknown,
321326
}

tests/type_inference.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ fn setup_decompiler() -> Decompiler<'static> {
6464
end
6565
6666
pub proc caller_bad_and_bool
67-
push.1
67+
push.3
6868
push.2
6969
exec.needs_and_bool
7070
end
@@ -116,13 +116,12 @@ fn setup_decompiler() -> Decompiler<'static> {
116116
push.2
117117
push.3
118118
u32wrapping_add
119-
push.1.1
120-
eq
119+
push.2
121120
exec.needs_bool_u32
122121
end
123122
124123
pub proc out_mixed
125-
push.1
124+
push.2
126125
push.1.1
127126
eq
128127
end

0 commit comments

Comments
 (0)