diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 5e8ea9f08d3..8222f212190 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -53,6 +53,19 @@ impl<'interner> TypeChecker<'interner> { // variable to handle generic functions. let t = self.interner.id_type_substitute_trait_as_type(ident.id); let (typ, bindings) = t.instantiate(self.interner); + + // Push any trait constraints required by this definition to the context + // to be checked later when the type of this variable is further constrained. + if let Some(definition) = self.interner.try_definition(ident.id) { + if let DefinitionKind::Function(function) = definition.kind { + let function = self.interner.function_meta(&function); + for mut constraint in function.trait_constraints.clone() { + constraint.typ = constraint.typ.substitute(&bindings); + self.trait_constraints.push((constraint, *expr_id)); + } + } + } + self.interner.store_instantiation_bindings(*expr_id, bindings); typ } @@ -294,7 +307,7 @@ impl<'interner> TypeChecker<'interner> { typ } - fn verify_trait_constraint( + pub fn verify_trait_constraint( &mut self, object_type: &Type, trait_id: TraitId, diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 01c68adbb6d..956992a54d0 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -14,7 +14,7 @@ mod stmt; pub use errors::TypeCheckError; use crate::{ - hir_def::{expr::HirExpression, stmt::HirStatement}, + hir_def::{expr::HirExpression, stmt::HirStatement, traits::TraitConstraint}, node_interner::{ExprId, FuncId, NodeInterner, StmtId}, Type, }; @@ -28,6 +28,12 @@ pub struct TypeChecker<'interner> { interner: &'interner mut NodeInterner, errors: Vec, current_function: Option, + + /// Trait constraints are collected during type checking until they are + /// verified at the end of a function. This is because constraints arise + /// on each variable, but it is only until function calls when the types + /// needed for the trait constraint may become known. + trait_constraints: Vec<(TraitConstraint, ExprId)>, } /// Type checks a function and assigns the @@ -66,11 +72,9 @@ pub fn type_check_func(interner: &mut NodeInterner, func_id: FuncId) -> Vec Vec (noirc_e impl<'interner> TypeChecker<'interner> { fn new(interner: &'interner mut NodeInterner) -> Self { - Self { delayed_type_checks: Vec::new(), interner, errors: vec![], current_function: None } + Self { + delayed_type_checks: Vec::new(), + interner, + errors: Vec::new(), + trait_constraints: Vec::new(), + current_function: None, + } } pub fn push_delayed_type_check(&mut self, f: TypeCheckFn) { self.delayed_type_checks.push(f); } - fn check_function_body( - mut self, - body: &ExprId, - ) -> (Type, Vec, Vec) { + fn check_function_body(&mut self, body: &ExprId) -> (Type, Vec) { let body_type = self.check_expression(body); - (body_type, self.delayed_type_checks, self.errors) + (body_type, std::mem::take(&mut self.delayed_type_checks)) } pub fn check_global(id: &StmtId, interner: &'interner mut NodeInterner) -> Vec { let mut this = Self { delayed_type_checks: Vec::new(), interner, - errors: vec![], + errors: Vec::new(), + trait_constraints: Vec::new(), current_function: None, }; this.check_statement(id); diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 634569bbc7a..57e4e6cdeb0 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -837,7 +837,14 @@ impl<'interner> Monomorphizer<'interner> { "There should be no remaining Assumed impls during monomorphization" ), Err(constraints) => { - unreachable!("Failed to find trait impl during monomorphization. The failed constraint(s) are:\n {constraints:?}") + let failed_constraints = vecmap(constraints, |constraint| { + let id = constraint.trait_id; + let name = self.interner.get_trait(id).name.to_string(); + format!(" {}: {name}", constraint.typ) + }) + .join("\n"); + + unreachable!("Failed to find trait impl during monomorphization. The failed constraint(s) are:\n{failed_constraints}") } } } diff --git a/tooling/nargo_cli/tests/compile_failure/no_impl_from_function/Nargo.toml b/tooling/nargo_cli/tests/compile_failure/no_impl_from_function/Nargo.toml new file mode 100644 index 00000000000..0d243d0029d --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/no_impl_from_function/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "no_impl_from_function" +type = "bin" +authors = [""] + +[dependencies] diff --git a/tooling/nargo_cli/tests/compile_failure/no_impl_from_function/src/main.nr b/tooling/nargo_cli/tests/compile_failure/no_impl_from_function/src/main.nr new file mode 100644 index 00000000000..b0c485c2bf5 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/no_impl_from_function/src/main.nr @@ -0,0 +1,26 @@ +fn main() { + let array: [Field; 3] = [1, 2, 3]; + assert(foo(array)); + + // Ensure this still works if we have to infer the type of the integer literals + let array = [1, 2, 3]; + assert(foo(array)); +} + +fn foo(x: T) -> bool where T: Eq { + x.eq(x) +} + +trait Eq { + fn eq(self, other: Self) -> bool; +} + +impl Eq for [T; N] where T: Eq { + fn eq(self, other: Self) -> bool { + let mut ret = true; + for i in 0 .. self.len() { + ret &= self[i].eq(other[i]); + } + ret + } +}