Skip to content
Merged
26 changes: 21 additions & 5 deletions compiler/noirc_evaluator/src/brillig/brillig_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,27 @@ impl<F: AcirField + DebugToString, Registers: RegisterAllocator> BrilligContext<
BrilligBinaryOp::Xor,
);

// If result has to be negative, perform two's complement
self.codegen_if(result_is_negative.address, |ctx| {
let zero = ctx.make_constant_instruction(0_usize.into(), result.bit_size);
ctx.binary_instruction(zero, result, result, BrilligBinaryOp::Sub);
ctx.deallocate_single_addr(zero);
self.codegen_branch(result_is_negative.address, |ctx, is_negative| {
if is_negative {
// If result has to be negative, perform two's complement
let zero = ctx.make_constant_instruction(0_usize.into(), result.bit_size);
ctx.binary_instruction(zero, result, result, BrilligBinaryOp::Sub);
ctx.deallocate_single_addr(zero);
} else {
// else the result is positive and so it must be less than '2**(bit_size-1)'
let max = 1_u128 << (left.bit_size - 1);
let max = ctx.make_constant_instruction(max.into(), left.bit_size);
let no_overflow = SingleAddrVariable::new(ctx.allocate_register(), 1);
ctx.binary_instruction(result, max, no_overflow, BrilligBinaryOp::LessThan);
ctx.codegen_if_not(no_overflow.address, |ctx2| {
ctx2.codegen_constrain(
no_overflow,
Some("Attempt to divide with overflow".to_string()),
);
});
ctx.deallocate_single_addr(max);
ctx.deallocate_single_addr(no_overflow);
}
});

self.deallocate_single_addr(left_is_negative);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ impl<F: AcirField + DebugToString, Registers: RegisterAllocator> BrilligContext<
}

/// This codegen issues a branch that jumps over the code generated by the given function if the condition is false
#[expect(unused)]
pub(crate) fn codegen_if(
&mut self,
condition: MemoryAddress,
Expand Down
25 changes: 13 additions & 12 deletions compiler/noirc_frontend/src/hir/comptime/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,22 +510,23 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic {
CustomDiagnostic::simple_error(msg, String::new(), *location)
}
InterpreterError::InvalidValuesForBinary { lhs, rhs, operator, location } => {
let msg = format!("No implementation for `{lhs}` {operator} `{rhs}`");
CustomDiagnostic::simple_error(msg, String::new(), *location)
}
InterpreterError::BinaryOperationOverflow { operator, location } => {
let msg = if *operator == "/" {
"Attempt to divide by zero".to_string()
} else {
let operator = match *operator {
"+" => "add",
"-" => "subtract",
"*" => "multiply",
">>" | "<<" => "bit-shift",
_ => operator,
};
format!("Attempt to {operator} with overflow")
format!("No implementation for `{lhs}` {operator} `{rhs}`")
};
CustomDiagnostic::simple_error(msg, String::new(), *location)
}
InterpreterError::BinaryOperationOverflow { operator, location } => {
let operator = match *operator {
"+" => "add",
"-" => "subtract",
"*" => "multiply",
">>" | "<<" => "bit-shift",
_ => operator,
};
let msg = format!("Attempt to {operator} with overflow");

CustomDiagnostic::simple_error(msg, String::new(), *location)
}
InterpreterError::NegateWithOverflow { location } => {
Expand Down
21 changes: 11 additions & 10 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/infix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ pub(super) fn evaluate_infix(
let lhs_overflow = InterpreterError::BinaryOperationOverflow { operator: "<<", location };
let rhs_overflow = InterpreterError::BinaryOperationOverflow { operator: ">>", location };
let math_error = |operator| InterpreterError::BinaryOperationOverflow { location, operator };

if operator.kind == BinaryOpKind::Divide && rhs_value.is_zero() {
return Err(InterpreterError::InvalidValuesForBinary {
lhs: lhs_type,
rhs: rhs_type,
location,
operator: "/",
});
}
/// Generate matches that can promote the type of one side to the other if they are compatible.
macro_rules! match_values {
(($lhs_value:ident as $lhs:ident $op:literal $rhs_value:ident as $rhs:ident) {
Expand Down Expand Up @@ -149,7 +156,7 @@ pub(super) fn evaluate_infix(
BinaryOpKind::Divide => match_arithmetic! {
(lhs_value as lhs "/" rhs_value as rhs) {
field: if rhs.absolute_value().is_zero() {
return Err(math_error("/"));
return Err( InterpreterError::InvalidValuesForBinary { lhs: lhs_type, rhs: rhs_type, location, operator: "/" });
} else {
lhs / rhs
},
Expand Down Expand Up @@ -407,10 +414,7 @@ mod test {
}
"#;
let result = interpret_expect_error(src);
assert!(matches!(
result,
InterpreterError::BinaryOperationOverflow { operator: "/", location: _ }
));
assert!(matches!(result, InterpreterError::InvalidValuesForBinary { operator: "/", .. }));
}

#[test]
Expand All @@ -421,10 +425,7 @@ mod test {
}
"#;
let result = interpret_expect_error(src);
assert!(matches!(
result,
InterpreterError::BinaryOperationOverflow { operator: "/", location: _ }
));
assert!(matches!(result, InterpreterError::InvalidValuesForBinary { operator: "/", .. }));
}

#[test]
Expand Down
18 changes: 18 additions & 0 deletions compiler/noirc_frontend/src/hir/comptime/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,24 @@ impl Value {
)
}

pub(crate) fn is_zero(&self) -> bool {
use Value::*;
match self {
Field(value) => value.is_zero(),
I8(value) => *value == 0,
I16(value) => *value == 0,
I32(value) => *value == 0,
I64(value) => *value == 0,
U1(value) => !*value,
U8(value) => *value == 0,
U16(value) => *value == 0,
U32(value) => *value == 0,
U64(value) => *value == 0,
U128(value) => *value == 0,
_ => false,
}
}

pub(crate) fn contains_function_or_closure(&self) -> bool {
match self {
Value::Function(..) => true,
Expand Down
6 changes: 6 additions & 0 deletions test_programs/compile_failure/div_signed/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "div_signed"
type = "bin"
authors = [""]

[dependencies]
Empty file.
13 changes: 13 additions & 0 deletions test_programs/compile_failure/div_signed/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
fn main() -> pub i8 {
let a = comptime {
// Saftety: test
unsafe {
func_1(-128_i8)
}
};
a
}

unconstrained fn func_1(a: i8) -> i8 {
(a / -1)
}
6 changes: 6 additions & 0 deletions test_programs/execution_failure/div_signed/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "div_signed"
type = "bin"
authors = [""]

[dependencies]
Empty file.
12 changes: 12 additions & 0 deletions test_programs/execution_failure/div_signed/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
fn main() -> pub i8 {
// Saftety: test
let b = unsafe {
func_1(-128_i8)
};
assert(b != b - 1);
b
}

unconstrained fn func_1(a: i8) -> i8 {
(a / -1)
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading