Skip to content

Commit c8a4312

Browse files
jfecherTomAFrench
authored andcommitted
fix: Verify impls arising from function calls exist (#3472)
1 parent a002e8c commit c8a4312

File tree

5 files changed

+81
-13
lines changed

5 files changed

+81
-13
lines changed

compiler/noirc_frontend/src/hir/type_check/expr.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ impl<'interner> TypeChecker<'interner> {
5353
// variable to handle generic functions.
5454
let t = self.interner.id_type_substitute_trait_as_type(ident.id);
5555
let (typ, bindings) = t.instantiate(self.interner);
56+
57+
// Push any trait constraints required by this definition to the context
58+
// to be checked later when the type of this variable is further constrained.
59+
if let Some(definition) = self.interner.try_definition(ident.id) {
60+
if let DefinitionKind::Function(function) = definition.kind {
61+
let function = self.interner.function_meta(&function);
62+
for mut constraint in function.trait_constraints.clone() {
63+
constraint.typ = constraint.typ.substitute(&bindings);
64+
self.trait_constraints.push((constraint, *expr_id));
65+
}
66+
}
67+
}
68+
5669
self.interner.store_instantiation_bindings(*expr_id, bindings);
5770
typ
5871
}
@@ -294,7 +307,7 @@ impl<'interner> TypeChecker<'interner> {
294307
typ
295308
}
296309

297-
fn verify_trait_constraint(
310+
pub fn verify_trait_constraint(
298311
&mut self,
299312
object_type: &Type,
300313
trait_id: TraitId,

compiler/noirc_frontend/src/hir/type_check/mod.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ mod stmt;
1414
pub use errors::TypeCheckError;
1515

1616
use crate::{
17-
hir_def::{expr::HirExpression, stmt::HirStatement},
17+
hir_def::{expr::HirExpression, stmt::HirStatement, traits::TraitConstraint},
1818
node_interner::{ExprId, FuncId, NodeInterner, StmtId},
1919
Type,
2020
};
@@ -28,6 +28,12 @@ pub struct TypeChecker<'interner> {
2828
interner: &'interner mut NodeInterner,
2929
errors: Vec<TypeCheckError>,
3030
current_function: Option<FuncId>,
31+
32+
/// Trait constraints are collected during type checking until they are
33+
/// verified at the end of a function. This is because constraints arise
34+
/// on each variable, but it is only until function calls when the types
35+
/// needed for the trait constraint may become known.
36+
trait_constraints: Vec<(TraitConstraint, ExprId)>,
3137
}
3238

3339
/// Type checks a function and assigns the
@@ -66,18 +72,24 @@ pub fn type_check_func(interner: &mut NodeInterner, func_id: FuncId) -> Vec<Type
6672
type_checker.bind_pattern(&param.0, param.1);
6773
}
6874

69-
let (function_last_type, delayed_type_check_functions, mut body_errors) =
75+
let (function_last_type, delayed_type_check_functions) =
7076
type_checker.check_function_body(function_body_id);
7177

72-
errors.append(&mut body_errors);
73-
7478
// Go through any delayed type checking errors to see if they are resolved, or error otherwise.
7579
for type_check_fn in delayed_type_check_functions {
7680
if let Err(error) = type_check_fn() {
7781
errors.push(error);
7882
}
7983
}
8084

85+
// Verify any remaining trait constraints arising from the function body
86+
for (constraint, expr_id) in std::mem::take(&mut type_checker.trait_constraints) {
87+
let span = type_checker.interner.expr_span(&expr_id);
88+
type_checker.verify_trait_constraint(&constraint.typ, constraint.trait_id, expr_id, span);
89+
}
90+
91+
errors.append(&mut type_checker.errors);
92+
8193
// Now remove all the `where` clause constraints we added
8294
for constraint in &meta.trait_constraints {
8395
interner.remove_assumed_trait_implementations_for_trait(constraint.trait_id);
@@ -146,26 +158,30 @@ fn function_info(interner: &NodeInterner, function_body_id: &ExprId) -> (noirc_e
146158

147159
impl<'interner> TypeChecker<'interner> {
148160
fn new(interner: &'interner mut NodeInterner) -> Self {
149-
Self { delayed_type_checks: Vec::new(), interner, errors: vec![], current_function: None }
161+
Self {
162+
delayed_type_checks: Vec::new(),
163+
interner,
164+
errors: Vec::new(),
165+
trait_constraints: Vec::new(),
166+
current_function: None,
167+
}
150168
}
151169

152170
pub fn push_delayed_type_check(&mut self, f: TypeCheckFn) {
153171
self.delayed_type_checks.push(f);
154172
}
155173

156-
fn check_function_body(
157-
mut self,
158-
body: &ExprId,
159-
) -> (Type, Vec<TypeCheckFn>, Vec<TypeCheckError>) {
174+
fn check_function_body(&mut self, body: &ExprId) -> (Type, Vec<TypeCheckFn>) {
160175
let body_type = self.check_expression(body);
161-
(body_type, self.delayed_type_checks, self.errors)
176+
(body_type, std::mem::take(&mut self.delayed_type_checks))
162177
}
163178

164179
pub fn check_global(id: &StmtId, interner: &'interner mut NodeInterner) -> Vec<TypeCheckError> {
165180
let mut this = Self {
166181
delayed_type_checks: Vec::new(),
167182
interner,
168-
errors: vec![],
183+
errors: Vec::new(),
184+
trait_constraints: Vec::new(),
169185
current_function: None,
170186
};
171187
this.check_statement(id);

compiler/noirc_frontend/src/monomorphization/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,14 @@ impl<'interner> Monomorphizer<'interner> {
837837
"There should be no remaining Assumed impls during monomorphization"
838838
),
839839
Err(constraints) => {
840-
unreachable!("Failed to find trait impl during monomorphization. The failed constraint(s) are:\n {constraints:?}")
840+
let failed_constraints = vecmap(constraints, |constraint| {
841+
let id = constraint.trait_id;
842+
let name = self.interner.get_trait(id).name.to_string();
843+
format!(" {}: {name}", constraint.typ)
844+
})
845+
.join("\n");
846+
847+
unreachable!("Failed to find trait impl during monomorphization. The failed constraint(s) are:\n{failed_constraints}")
841848
}
842849
}
843850
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "no_impl_from_function"
3+
type = "bin"
4+
authors = [""]
5+
6+
[dependencies]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
fn main() {
2+
let array: [Field; 3] = [1, 2, 3];
3+
assert(foo(array));
4+
5+
// Ensure this still works if we have to infer the type of the integer literals
6+
let array = [1, 2, 3];
7+
assert(foo(array));
8+
}
9+
10+
fn foo<T>(x: T) -> bool where T: Eq {
11+
x.eq(x)
12+
}
13+
14+
trait Eq {
15+
fn eq(self, other: Self) -> bool;
16+
}
17+
18+
impl<T, N> Eq for [T; N] where T: Eq {
19+
fn eq(self, other: Self) -> bool {
20+
let mut ret = true;
21+
for i in 0 .. self.len() {
22+
ret &= self[i].eq(other[i]);
23+
}
24+
ret
25+
}
26+
}

0 commit comments

Comments
 (0)