Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Divide by zero should fail to satisfy constraints for Field and ints #2475

Merged
merged 10 commits into from
Aug 29, 2023
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,6 @@ url = "2.2.0"
wasm-bindgen = { version = "=0.2.86", features = ["serde-serialize"] }
wasm-bindgen-test = "0.3.33"
base64 = "0.21.2"

# [patch.crates-io]
# acvm = { path = "/mnt/user-data/maxim/acvm/acvm" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "divide_by_zero"
type = "bin"
authors = [""]
compiler_version = "0.10.3"

[dependencies]
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use dep::std;

fn main() {
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
let a: Field = 3 / 0;
std::println(a);
}
3 changes: 3 additions & 0 deletions crates/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,9 @@ impl Binary {
let operand_type = dfg.type_of_value(self.lhs);

if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
if rhs == FieldElement::zero() {
return SimplifyResult::None;
}
return match self.eval_constants(dfg, lhs, rhs, operand_type) {
Some(value) => SimplifyResult::SimplifiedTo(value),
None => SimplifyResult::None,
Expand Down
9 changes: 9 additions & 0 deletions crates/noirc_evaluator/src/ssa/ssa_gen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,15 @@ impl<'a> FunctionContext<'a> {
{
return self.insert_array_equality(lhs, operator, rhs, location)
}
BinaryOpKind::Divide => {
let zero = self.builder.numeric_constant(FieldElement::zero(), Type::field());
let divisor_is_zero = self.builder.insert_binary(zero, BinaryOp::Eq, rhs);
let divisor_is_not_zero = self.builder.insert_not(divisor_is_zero);
self.builder.set_location(location).insert_constrain(divisor_is_not_zero);

let op = convert_operator(operator);
self.builder.set_location(location).insert_binary(lhs, op, rhs)
}
_ => {
let op = convert_operator(operator);
if operator_requires_swapped_operands(operator) {
Expand Down
3 changes: 2 additions & 1 deletion crates/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ impl<'a> Resolver<'a> {
// expect() here is valid, because the only places we don't have a span are omitted types
// e.g. a function without return type implicitly has a spanless UnresolvedType::Unit return type
// To get an invalid env type, the user must explicitly specify the type, which will have a span
let env_span = env.span.expect("Unexpected missing span for closure environment type");
let env_span =
env.span.expect("Unexpected missing span for closure environment type");

let env = Box::new(self.resolve_type_inner(*env, new_variables));

Expand Down
22 changes: 9 additions & 13 deletions crates/noirc_frontend/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ use crate::{
BinaryOp, BinaryOpKind, BlockExpression, ConstrainStatement, Distinctness, FunctionDefinition,
FunctionReturnType, Ident, IfExpression, InfixExpression, LValue, Lambda, Literal,
NoirFunction, NoirStruct, NoirTrait, NoirTypeAlias, Path, PathKind, Pattern, Recoverable,
TraitConstraint, TraitImpl, TraitImplItem, TraitItem, TypeImpl, UnaryOp,
UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, TraitBound,
TraitBound, TraitConstraint, TraitImpl, TraitImplItem, TraitItem, TypeImpl, UnaryOp,
UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility,
};

use chumsky::prelude::*;
Expand Down Expand Up @@ -527,19 +527,17 @@ fn trait_implementation_body() -> impl NoirParser<Vec<TraitImplItem>> {
}

fn where_clause() -> impl NoirParser<Vec<TraitConstraint>> {

struct MultiTraitConstraint {
typ: UnresolvedType,
trait_bounds: Vec<TraitBound>,
}

let constraints = parse_type()
.then_ignore(just(Token::Colon))
.then(trait_bounds())
.validate(|(typ, trait_bounds), span, emit| {
let constraints = parse_type().then_ignore(just(Token::Colon)).then(trait_bounds()).validate(
|(typ, trait_bounds), span, emit| {
emit(ParserError::with_reason(ParserErrorReason::ExperimentalFeature("Traits"), span));
MultiTraitConstraint { typ, trait_bounds }
});
},
);

keyword(Keyword::Where)
.ignore_then(constraints.separated_by(just(Token::Comma)))
Expand All @@ -549,18 +547,16 @@ fn where_clause() -> impl NoirParser<Vec<TraitConstraint>> {
let mut result: Vec<TraitConstraint> = Vec::new();
for constraint in x {
for bound in constraint.trait_bounds {
result.push(TraitConstraint { typ:constraint.typ.clone(), trait_bound:bound } );
result
.push(TraitConstraint { typ: constraint.typ.clone(), trait_bound: bound });
}
}
result
})
}

fn trait_bounds() -> impl NoirParser<Vec<TraitBound>> {
trait_bound()
.separated_by(just(Token::Plus))
.at_least(1)
.allow_trailing()
trait_bound().separated_by(just(Token::Plus)).at_least(1).allow_trailing()
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
}

fn trait_bound() -> impl NoirParser<TraitBound> {
Expand Down