Skip to content

Commit 4f8e767

Browse files
authored
fix: Fix comptime casts of negative integer to field (#8696)
1 parent 03796c0 commit 4f8e767

File tree

1 file changed

+16
-29
lines changed
  • compiler/noirc_frontend/src/hir/comptime/interpreter

1 file changed

+16
-29
lines changed

compiler/noirc_frontend/src/hir/comptime/interpreter/cast.rs

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,13 @@ enum CastType {
2929
old_bit_size: u32,
3030
new_bit_size: u32,
3131
},
32-
/// SignedField makes casting signed values more difficult since we need
33-
/// to add an offset to make the signed value positive if it is negative,
34-
/// and need to store a boolean to remember it is negative
35-
SignedToField {
36-
old_bit_size: u32,
37-
},
3832
/// No-op also covers the zero-extend case since we convert between
3933
/// field elements rather than concrete bit sizes
34+
///
35+
/// This is also the case for casting signed integers to fields.
36+
/// We represent negatives with two's complement, so e.g.
37+
/// `-1 as i8` is stored as the field value for `255`, and `255`
38+
/// is also the expected result of casting these to a field.
4039
Noop,
4140
}
4241

@@ -52,7 +51,7 @@ fn classify_cast(input: &Type, output: &Type) -> CastType {
5251
Ordering::Less => {
5352
if input_signed {
5453
if output.is_field() {
55-
CastType::SignedToField { old_bit_size: input_size }
54+
CastType::Noop // We always zero-extend when casting to a field
5655
} else {
5756
CastType::SignExtend { old_bit_size: input_size, new_bit_size: output_size }
5857
}
@@ -93,21 +92,6 @@ fn perform_cast(kind: CastType, lhs: FieldElement) -> FieldElement {
9392
lhs
9493
}
9594
}
96-
CastType::SignedToField { old_bit_size } => {
97-
assert!(old_bit_size < 128, "i128 and above are not supported");
98-
let max_positive_value = 2u128.pow(old_bit_size - 1) - 1;
99-
let is_negative = lhs > max_positive_value.into();
100-
101-
if !is_negative {
102-
lhs
103-
} else {
104-
// We only return the FieldElement component of the SignedField, the caller
105-
// needs to add the negative sign themselves
106-
let max_negative_value = FieldElement::from(2u128.pow(old_bit_size));
107-
// E.g. `256 - 255 = 1` or `256 - 128 = 128`
108-
max_negative_value - lhs
109-
}
110-
}
11195
CastType::Noop => lhs,
11296
}
11397
}
@@ -126,11 +110,12 @@ fn convert_to_field(value: Value, location: Location) -> IResult<(FieldElement,
126110
Value::U32(value) => ((value as u128).into(), false),
127111
Value::U64(value) => ((value as u128).into(), false),
128112
Value::U128(value) => (value.into(), false),
129-
// Shared logic from ssa::interpreter::Value::convert_to_field
130-
Value::I8(value) => (FieldElement::from(value as u8 as i128), value < 0),
131-
Value::I16(value) => (FieldElement::from(value as u16 as i128), value < 0),
132-
Value::I32(value) => (FieldElement::from(value as u32 as i128), value < 0),
133-
Value::I64(value) => (FieldElement::from(value as u64 as i128), value < 0),
113+
// `is_negative` is only used for conversions to Field in which case
114+
// these should always be positive so that `-1 as i8 as Field == 255`
115+
Value::I8(value) => (FieldElement::from(value as u8 as i128), false),
116+
Value::I16(value) => (FieldElement::from(value as u16 as i128), false),
117+
Value::I32(value) => (FieldElement::from(value as u32 as i128), false),
118+
Value::I64(value) => (FieldElement::from(value as u64 as i128), false),
134119
Value::Bool(value) => (FieldElement::from(value), false),
135120
value => {
136121
let typ = value.get_type().into_owned();
@@ -264,10 +249,12 @@ mod tests {
264249
// Widen
265250
(Value::I8(127), unsigned(SixtyFour), Value::U64(127)),
266251
(Value::I8(127), signed(SixtyFour), Value::I64(127)),
267-
// Widen negative: zero extend
252+
// Widen signed->unsigned: sign extend
268253
(Value::I8(-1), unsigned(Sixteen), Value::U16(65535)),
269254
(Value::I8(-100), unsigned(Sixteen), Value::U16(65436)),
270-
(Value::I8(-100), Type::FieldElement, Value::Field(SignedField::negative(100u32))),
255+
// Casting a negative integer to a field always results in a positive value
256+
// This is the only case we zero-extend signed integers instead of sign-extending them
257+
(Value::I8(-1), Type::FieldElement, Value::Field(SignedField::positive(255u32))),
271258
// Widen negative: sign extend
272259
(Value::I8(-1), signed(Sixteen), Value::I16(-1)),
273260
(Value::I8(-100), signed(Sixteen), Value::I16(-100)),

0 commit comments

Comments
 (0)