diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index d3e5e270b3c..8c00fcde2ef 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -361,7 +361,16 @@ pub enum ImplSearchErrorKind { #[derive(Default, Debug, Clone)] pub struct Methods { pub direct: Vec, - pub trait_impl_methods: Vec, + pub trait_impl_methods: Vec, +} + +#[derive(Debug, Clone)] +pub struct TraitImplMethod { + // This type is only stored for primitive types to be able to + // select the correct static methods between multiple options keyed + // under TypeMethodKey::FieldOrInt + pub typ: Option, + pub method: FuncId, } /// All the information from a function that is filled out during definition collection rather than @@ -1383,7 +1392,7 @@ impl NodeInterner { .or_default() .entry(method_name) .or_default() - .add_method(method_id, is_trait_method); + .add_method(method_id, None, is_trait_method); None } Type::Error => None, @@ -1395,12 +1404,16 @@ impl NodeInterner { let key = get_type_method_key(self_type).unwrap_or_else(|| { unreachable!("Cannot add a method to the unsupported type '{}'", other) }); + // Only remember the actual type if it's FieldOrInt, + // so later we can disambiguate on calls like `u32::call`. + let typ = + if key == TypeMethodKey::FieldOrInt { Some(self_type.clone()) } else { None }; self.primitive_methods .entry(key) .or_default() .entry(method_name) .or_default() - .add_method(method_id, is_trait_method); + .add_method(method_id, typ, is_trait_method); None } } @@ -2246,23 +2259,29 @@ impl Methods { if self.direct.len() == 1 { Some(self.direct[0]) } else if self.direct.is_empty() && self.trait_impl_methods.len() == 1 { - Some(self.trait_impl_methods[0]) + Some(self.trait_impl_methods[0].method) } else { None } } - fn add_method(&mut self, method: FuncId, is_trait_method: bool) { + fn add_method(&mut self, method: FuncId, typ: Option, is_trait_method: bool) { if is_trait_method { - self.trait_impl_methods.push(method); + let trait_impl_method = TraitImplMethod { typ, method }; + self.trait_impl_methods.push(trait_impl_method); } else { self.direct.push(method); } } /// Iterate through each method, starting with the direct methods - pub fn iter(&self) -> impl Iterator + '_ { - self.direct.iter().copied().chain(self.trait_impl_methods.iter().copied()) + pub fn iter(&self) -> impl Iterator)> + '_ { + let trait_impl_methods = self.trait_impl_methods.iter().map(|m| (m.method, &m.typ)); + let direct = self.direct.iter().copied().map(|func_id| { + let typ: &Option = &None; + (func_id, typ) + }); + direct.chain(trait_impl_methods) } /// Select the 1 matching method with an object type matching `typ` @@ -2274,28 +2293,32 @@ impl Methods { ) -> Option { // When adding methods we always check they do not overlap, so there should be // at most 1 matching method in this list. - for method in self.iter() { + for (method, method_type) in self.iter() { match interner.function_meta(&method).typ.instantiate(interner).0 { Type::Function(args, _, _, _) => { if has_self_param { if let Some(object) = args.first() { - let mut bindings = TypeBindings::new(); - - if object.try_unify(typ, &mut bindings).is_ok() { - Type::apply_type_bindings(bindings); + if object.unify(typ).is_ok() { return Some(method); } } } else { - // Just return the first method whose name matches since we - // can't match object types on static methods. - return Some(method); + // If we recorded the concrete type this trait impl method belongs to, + // and it matches typ, it's an exact match and we return that. + if let Some(method_type) = method_type { + if method_type.unify(typ).is_ok() { + return Some(method); + } + } else { + return Some(method); + } } } Type::Error => (), other => unreachable!("Expected function type, found {other}"), } } + None } } diff --git a/test_programs/compile_success_empty/field_or_integer_static_trait_method/Nargo.toml b/test_programs/compile_success_empty/field_or_integer_static_trait_method/Nargo.toml new file mode 100644 index 00000000000..18d9fc90d9e --- /dev/null +++ b/test_programs/compile_success_empty/field_or_integer_static_trait_method/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "field_or_int_static_trait_method" +type = "bin" +authors = [""] +compiler_version = ">=0.32.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/field_or_integer_static_trait_method/src/main.nr b/test_programs/compile_success_empty/field_or_integer_static_trait_method/src/main.nr new file mode 100644 index 00000000000..f5a54c82ce5 --- /dev/null +++ b/test_programs/compile_success_empty/field_or_integer_static_trait_method/src/main.nr @@ -0,0 +1,25 @@ +trait Read { + fn read(data: [Field; 1]) -> Self; +} + +impl Read for Field { + fn read(data: [Field; 1]) -> Self { + data[0] * 10 + } +} + +impl Read for u32 { + fn read(data: [Field; 1]) -> Self { + data[0] as u32 + } +} + +fn main() { + let data = [1]; + + let value: u32 = u32::read(data); + assert_eq(value, 1); + + let value: Field = Field::read(data); + assert_eq(value, 10); +} diff --git a/tooling/lsp/src/requests/completion.rs b/tooling/lsp/src/requests/completion.rs index c59544a36ea..be30d0cbc8c 100644 --- a/tooling/lsp/src/requests/completion.rs +++ b/tooling/lsp/src/requests/completion.rs @@ -627,7 +627,7 @@ impl<'a> NodeFinder<'a> { }; for (name, methods) in methods_by_name { - for func_id in methods.iter() { + for (func_id, _method_type) in methods.iter() { if name_matches(name, prefix) { let completion_items = self.function_completion_items( name,