diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index e4016809218..2a20a60b5cc 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -27,13 +27,13 @@ use crate::{ HirConstrainExpression, HirConstructorExpression, HirExpression, HirIdent, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirLiteral, HirMatch, HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, ImplKind, - TraitMethod, + TraitItem, }, stmt::{HirLetStatement, HirPattern, HirStatement}, traits::{ResolvedTraitBound, TraitConstraint}, }, node_interner::{ - DefinitionId, DefinitionKind, ExprId, FuncId, InternedStatementKind, StmtId, TraitMethodId, + DefinitionId, DefinitionKind, ExprId, FuncId, InternedStatementKind, StmtId, TraitItemId, }, shared::Signedness, token::{FmtStrFragment, IntegerTypeSuffix, Tokens}, @@ -405,7 +405,7 @@ impl Elaborator<'_> { let rhs_location = prefix.rhs.location; let (rhs, rhs_type) = self.elaborate_expression(prefix.rhs); - let trait_id = self.interner.get_prefix_operator_trait_method(&prefix.operator); + let trait_method_id = self.interner.get_prefix_operator_trait_method(&prefix.operator); let operator = prefix.operator; @@ -417,14 +417,18 @@ impl Elaborator<'_> { } } - let expr = - HirExpression::Prefix(HirPrefixExpression { operator, rhs, trait_method_id: trait_id }); + let expr = HirExpression::Prefix(HirPrefixExpression { operator, rhs, trait_method_id }); let expr_id = self.interner.push_expr(expr); self.interner.push_expr_location(expr_id, location); let result = self.prefix_operand_type_rules(&operator, &rhs_type, location); - let typ = - self.handle_operand_type_rules_result(result, &rhs_type, trait_id, expr_id, location); + let typ = self.handle_operand_type_rules_result( + result, + &rhs_type, + trait_method_id, + expr_id, + location, + ); self.interner.push_expr_type(expr_id, typ.clone()); (expr_id, typ) @@ -1078,32 +1082,31 @@ impl Elaborator<'_> { &mut self, result: Result<(Type, bool), TypeCheckError>, operand_type: &Type, - trait_id: Option, + trait_method_id: Option, expr_id: ExprId, location: Location, ) -> Type { match result { Ok((typ, use_impl)) => { if use_impl { - let trait_id = - trait_id.expect("ice: expected some trait_id when use_impl is true"); + let trait_method_id = trait_method_id + .expect("ice: expected some trait_method_id when use_impl is true"); // Delay checking the trait constraint until the end of the function. // Checking it now could bind an unbound type variable to any type // that implements the trait. - let constraint = TraitConstraint { - typ: operand_type.clone(), - trait_bound: ResolvedTraitBound { - trait_id: trait_id.trait_id, - trait_generics: TraitGenerics::default(), - location, - }, - }; - self.push_trait_constraint( - constraint, expr_id, - true, // this constraint should lead to choosing a trait impl + let trait_id = trait_method_id.trait_id; + let trait_generics = TraitGenerics::default(); + let trait_bound = ResolvedTraitBound { trait_id, trait_generics, location }; + let constraint = TraitConstraint { typ: operand_type.clone(), trait_bound }; + let select_impl = true; // this constraint should lead to choosing a trait impl + self.push_trait_constraint(constraint, expr_id, select_impl); + self.type_check_operator_method( + expr_id, + trait_method_id, + operand_type, + location, ); - self.type_check_operator_method(expr_id, trait_id, operand_type, location); } typ } @@ -1471,7 +1474,9 @@ impl Elaborator<'_> { let constraint = TraitConstraint { typ, trait_bound }; let the_trait = self.interner.get_trait(constraint.trait_bound.trait_id); - let Some(method) = the_trait.find_method(path.impl_item.as_str()) else { + let Some(definition) = + the_trait.find_method_or_constant(path.impl_item.as_str(), self.interner) + else { let trait_name = the_trait.name.to_string(); let method_name = path.impl_item.to_string(); let location = path.impl_item.location(); @@ -1480,15 +1485,12 @@ impl Elaborator<'_> { return (error, Type::Error); }; - let trait_method = - TraitMethod { method_id: method, constraint: constraint.clone(), assumed: true }; - - let definition_id = self.interner.trait_method_id(trait_method.method_id); + let trait_item = TraitItem { definition, constraint: constraint.clone(), assumed: true }; let ident = HirIdent { location: path.impl_item.location(), - id: definition_id, - impl_kind: ImplKind::TraitMethod(trait_method), + id: definition, + impl_kind: ImplKind::TraitItem(trait_item), }; let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), None)); diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 706936a61ab..6f455179cc8 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -18,7 +18,7 @@ use crate::{ type_check::{Source, TypeCheckError}, }, hir_def::{ - expr::{HirExpression, HirIdent, HirLiteral, HirMethodReference, ImplKind, TraitMethod}, + expr::{HirExpression, HirIdent, HirLiteral, HirMethodReference, ImplKind, TraitItem}, stmt::HirPattern, }, node_interner::{ @@ -904,11 +904,11 @@ impl Elaborator<'_> { trait_path_resolution.item, ), - TraitPathResolutionMethod::TraitMethod(trait_method) => ( + TraitPathResolutionMethod::TraitItem(item) => ( HirIdent { location: path.location, - id: self.interner.trait_method_id(trait_method.method_id), - impl_kind: ImplKind::TraitMethod(trait_method), + id: item.definition, + impl_kind: ImplKind::TraitItem(item), }, trait_path_resolution.item, ), @@ -1006,7 +1006,7 @@ impl Elaborator<'_> { // We need to do this first since otherwise instantiating the type below // will replace each trait generic with a fresh type variable, rather than // the type used in the trait constraint (if it exists). See #4088. - if let ImplKind::TraitMethod(method) = &ident.impl_kind { + if let ImplKind::TraitItem(method) = &ident.impl_kind { self.bind_generics_from_trait_constraint( &method.constraint, method.assumed, @@ -1053,7 +1053,7 @@ impl Elaborator<'_> { } } - if let ImplKind::TraitMethod(mut method) = ident.impl_kind { + if let ImplKind::TraitItem(mut method) = ident.impl_kind { method.constraint.apply_bindings(&bindings); if method.assumed { let trait_generics = method.constraint.trait_bound.trait_generics.clone(); @@ -1196,11 +1196,11 @@ impl Elaborator<'_> { let impl_kind = match method { HirMethodReference::FuncId(_) => ImplKind::NotATraitMethod, - HirMethodReference::TraitMethodId(method_id, generics, _) => { + HirMethodReference::TraitItemId(definition, trait_id, generics, _) => { let mut constraint = - self.interner.get_trait(method_id.trait_id).as_constraint(ident_location); + self.interner.get_trait(trait_id).as_constraint(ident_location); constraint.trait_bound.trait_generics = generics; - ImplKind::TraitMethod(TraitMethod { method_id, constraint, assumed: false }) + ImplKind::TraitItem(TraitItem { definition, constraint, assumed: false }) } }; diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index d7c2e5876cb..66c6f86fd1a 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -26,7 +26,7 @@ use crate::{ hir_def::{ expr::{ HirBinaryOp, HirCallExpression, HirExpression, HirLiteral, HirMemberAccess, - HirMethodReference, HirPrefixExpression, TraitMethod, + HirMethodReference, HirPrefixExpression, TraitItem, }, function::FuncMeta, stmt::HirStatement, @@ -34,7 +34,7 @@ use crate::{ }, node_interner::{ DependencyId, ExprId, FuncId, GlobalValue, ImplSearchErrorKind, TraitId, TraitImplKind, - TraitMethodId, + TraitItemId, }, shared::Signedness, token::SecondaryAttributeKind, @@ -55,7 +55,7 @@ pub(super) struct TraitPathResolution { pub(super) enum TraitPathResolutionMethod { NotATraitMethod(FuncId), - TraitMethod(TraitMethod), + TraitItem(TraitItem), } impl Elaborator<'_> { @@ -734,10 +734,12 @@ impl Elaborator<'_> { if name == SELF_TYPE_NAME { let the_trait = self.interner.get_trait(trait_id); - let method = the_trait.find_method(method.as_str())?; + // Allow referring to trait constants via Self:: as well + let definition = + the_trait.find_method_or_constant(method.as_str(), self.interner)?; let constraint = the_trait.as_constraint(path.location); - let trait_method = TraitMethod { method_id: method, constraint, assumed: true }; - let method = TraitPathResolutionMethod::TraitMethod(trait_method); + let trait_method = TraitItem { definition, constraint, assumed: true }; + let method = TraitPathResolutionMethod::TraitItem(trait_method); return Some(TraitPathResolution { method, item: None, errors: Vec::new() }); } } @@ -753,10 +755,10 @@ impl Elaborator<'_> { let func_id = path_resolution.item.function_id()?; let meta = self.interner.try_function_meta(&func_id)?; let the_trait = self.interner.get_trait(meta.trait_id?); - let method = the_trait.find_method(path.last_name())?; + let method = the_trait.find_method(path.last_name(), self.interner)?; let constraint = the_trait.as_constraint(path.location); - let trait_method = TraitMethod { method_id: method, constraint, assumed: false }; - let method = TraitPathResolutionMethod::TraitMethod(trait_method); + let trait_method = TraitItem { definition: method, constraint, assumed: false }; + let method = TraitPathResolutionMethod::TraitItem(trait_method); let item = Some(path_resolution.item); Some(TraitPathResolution { method, item, errors: path_resolution.errors }) } @@ -782,9 +784,11 @@ impl Elaborator<'_> { } let the_trait = self.interner.get_trait(constraint.trait_bound.trait_id); - if let Some(method) = the_trait.find_method(path.last_name()) { - let trait_method = TraitMethod { method_id: method, constraint, assumed: true }; - let method = TraitPathResolutionMethod::TraitMethod(trait_method); + if let Some(definition) = + the_trait.find_method_or_constant(path.last_name(), self.interner) + { + let trait_item = TraitItem { definition, constraint, assumed: true }; + let method = TraitPathResolutionMethod::TraitItem(trait_item); return Some(TraitPathResolution { method, item: None, errors: Vec::new() }); } } @@ -863,14 +867,12 @@ impl Elaborator<'_> { let method = TraitPathResolutionMethod::NotATraitMethod(func_id); Some(TraitPathResolution { method, item: None, errors }) } - HirMethodReference::TraitMethodId(trait_method_id, _, _) => { - let trait_id = trait_method_id.trait_id; + HirMethodReference::TraitItemId(definition, trait_id, _, _) => { let trait_ = self.interner.get_trait(trait_id); let mut constraint = trait_.as_constraint(location); constraint.typ = typ.clone(); - let trait_method = - TraitMethod { method_id: trait_method_id, constraint, assumed: false }; + let trait_method = TraitItem { definition, constraint, assumed: false }; let item = PathResolutionItem::TypeTraitFunction(typ, trait_id, func_id); let mut errors = path_resolution.errors; @@ -878,7 +880,7 @@ impl Elaborator<'_> { errors.push(error); } - let method = TraitPathResolutionMethod::TraitMethod(trait_method); + let method = TraitPathResolutionMethod::TraitItem(trait_method); Some(TraitPathResolution { method, item: Some(item), errors }) } } @@ -1515,14 +1517,12 @@ impl Elaborator<'_> { pub(super) fn type_check_operator_method( &mut self, expr_id: ExprId, - trait_method_id: TraitMethodId, + trait_method_id: TraitItemId, object_type: &Type, location: Location, ) { - let the_trait = self.interner.get_trait(trait_method_id.trait_id); - - let method = &the_trait.methods[trait_method_id.method_index]; - let (method_type, mut bindings) = method.typ.clone().instantiate(self.interner); + let method_type = self.interner.definition_type(trait_method_id.item_id); + let (method_type, mut bindings) = method_type.instantiate(self.interner); match method_type { Type::Function(args, _, _, _) => { @@ -1829,8 +1829,8 @@ impl Elaborator<'_> { // Return a TraitMethodId with unbound generics. These will later be bound by the type-checker. let trait_ = self.interner.get_trait(trait_id); let generics = trait_.get_trait_generics(location); - let trait_method_id = trait_.find_method(method_name).unwrap(); - HirMethodReference::TraitMethodId(trait_method_id, generics, false) + let trait_method_id = trait_.find_method(method_name, self.interner).unwrap(); + HirMethodReference::TraitItemId(trait_method_id, trait_id, generics, false) } fn lookup_method_in_trait_constraints( @@ -1851,8 +1851,8 @@ impl Elaborator<'_> { if Some(object_type) == self.self_type.as_ref() { let the_trait = self.interner.get_trait(trait_id); let constraint = the_trait.as_constraint(the_trait.name.location()); - if let Some(HirMethodReference::TraitMethodId(method_id, generics, _)) = self - .lookup_method_in_trait( + if let Some(HirMethodReference::TraitItemId(method_id, trait_id, generics, _)) = + self.lookup_method_in_trait( the_trait, method_name, &constraint.trait_bound, @@ -1860,7 +1860,11 @@ impl Elaborator<'_> { ) { // If it is, it's an assumed trait - return Some(HirMethodReference::TraitMethodId(method_id, generics, true)); + // Note that here we use the `trait_id` from `TraitItemId` because looking a method on a trait + // might return a method on a parent trait. + return Some(HirMethodReference::TraitItemId( + method_id, trait_id, generics, true, + )); } } } @@ -1904,12 +1908,11 @@ impl Elaborator<'_> { trait_bound: &ResolvedTraitBound, starting_trait_id: TraitId, ) -> Option { - if let Some(trait_method) = the_trait.find_method(method_name) { - return Some(HirMethodReference::TraitMethodId( - trait_method, - trait_bound.trait_generics.clone(), - false, - )); + if let Some(trait_method) = the_trait.find_method(method_name, self.interner) { + let trait_generics = trait_bound.trait_generics.clone(); + let trait_item_id = + HirMethodReference::TraitItemId(trait_method, the_trait.id, trait_generics, false); + return Some(trait_item_id); } // Search in the parent traits, if any diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 51ae66d7252..7db1fa8444d 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -14,8 +14,9 @@ use crate::elaborator::{ElaborateReason, Elaborator}; use crate::graph::CrateId; use crate::hir::def_map::ModuleId; use crate::hir::type_check::TypeCheckError; +use crate::hir_def::expr::TraitItem; use crate::monomorphization::{ - perform_impl_bindings, perform_instantiation_bindings, resolve_trait_method, + perform_impl_bindings, perform_instantiation_bindings, resolve_trait_item, undo_instantiation_bindings, }; use crate::node_interner::GlobalValue; @@ -23,7 +24,7 @@ use crate::shared::Signedness; use crate::signed_field::SignedField; use crate::token::{FmtStrFragment, Tokens}; use crate::{ - Shared, Type, TypeBinding, TypeBindings, + Shared, Type, TypeBindings, hir_def::{ expr::{ HirArrayLiteral, HirBlockExpression, HirCallExpression, HirCastExpression, @@ -97,25 +98,21 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { mut instantiation_bindings: TypeBindings, location: Location, ) -> IResult { - let trait_method = self.elaborator.interner.get_trait_method_id(function); + let trait_method = self.elaborator.interner.get_trait_item_id(function); // To match the monomorphizer, we need to call follow_bindings on each of // the instantiation bindings before we unbind the generics from the previous function. // This is because the instantiation bindings refer to variables from the call site. - for (_, kind, binding) in instantiation_bindings.values_mut() { + for (_tvar, kind, binding) in instantiation_bindings.values_mut() { *kind = kind.follow_bindings(); *binding = binding.follow_bindings(); } self.unbind_generics_from_previous_function(); perform_instantiation_bindings(&instantiation_bindings); - let mut impl_bindings = - perform_impl_bindings(self.elaborator.interner, trait_method, function, location)?; - for (_, kind, binding) in impl_bindings.values_mut() { - *kind = kind.follow_bindings(); - *binding = binding.follow_bindings(); - } + let impl_bindings = + perform_impl_bindings(self.elaborator.interner, trait_method, function, location)?; self.remember_bindings(&instantiation_bindings, &impl_bindings); self.elaborator.interpreter_call_stack.push_back(location); @@ -579,11 +576,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { InterpreterError::VariableNotInScope { location } })?; - if let ImplKind::TraitMethod(method) = ident.impl_kind { - let method_id = resolve_trait_method(self.elaborator.interner, method.method_id, id)?; - let typ = self.elaborator.interner.id_type(id).follow_bindings(); - let bindings = self.elaborator.interner.get_instantiation_bindings(id).clone(); - return Ok(Value::Function(method_id, typ, Rc::new(bindings))); + if let ImplKind::TraitItem(item) = ident.impl_kind { + return self.evaluate_trait_item(item, id); } match &definition.kind { @@ -632,29 +626,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } } DefinitionKind::NumericGeneric(type_variable, numeric_typ) => { - let value = match &*type_variable.borrow() { - TypeBinding::Unbound(_, _) => { - let typ = self.elaborator.interner.id_type(id); - let location = self.elaborator.interner.expr_location(&id); - Err(InterpreterError::NonIntegerArrayLength { typ, err: None, location }) - } - TypeBinding::Bound(binding) => { - let location = self.elaborator.interner.id_location(id); - binding - .evaluate_to_field_element( - &Kind::Numeric(numeric_typ.clone()), - location, - ) - .map_err(|err| { - let typ = Type::TypeVariable(type_variable.clone()); - let err = Some(Box::new(err)); - let location = self.elaborator.interner.expr_location(&id); - InterpreterError::NonIntegerArrayLength { typ, err, location } - }) - } - }?; - - self.evaluate_integer(value.into(), id) + let value = Type::TypeVariable(type_variable.clone()); + self.evaluate_numeric_generic(value, numeric_typ, id) } DefinitionKind::AssociatedConstant(trait_impl_id, name) => { let associated_types = @@ -682,6 +655,35 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } } + /// Evaluates a numeric generic with the value `value` (expected to be `Type::Constant`) + /// and an expected integer type `expected`. + fn evaluate_numeric_generic(&self, value: Type, expected: &Type, id: ExprId) -> IResult { + let location = self.elaborator.interner.id_location(id); + let value = value + .evaluate_to_field_element(&Kind::Numeric(Box::new(expected.clone())), location) + .map_err(|err| { + let typ = value; + let err = Some(Box::new(err)); + let location = self.elaborator.interner.expr_location(&id); + InterpreterError::NonIntegerArrayLength { typ, err, location } + })?; + + self.evaluate_integer(value.into(), id) + } + + fn evaluate_trait_item(&mut self, item: TraitItem, id: ExprId) -> IResult { + let typ = self.elaborator.interner.id_type(id).follow_bindings(); + match resolve_trait_item(self.elaborator.interner, item.id(), id)? { + crate::monomorphization::TraitItem::Method(func_id) => { + let bindings = self.elaborator.interner.get_instantiation_bindings(id).clone(); + Ok(Value::Function(func_id, typ, Rc::new(bindings))) + } + crate::monomorphization::TraitItem::Constant { id: _, expected_type, value } => { + self.evaluate_numeric_generic(value, &expected_type, id) + } + } + } + fn evaluate_literal(&mut self, literal: HirLiteral, id: ExprId) -> IResult { match literal { HirLiteral::Unit => Ok(Value::Unit), @@ -850,7 +852,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let method = infix.trait_method_id; let operator = infix.operator.kind; - let method_id = resolve_trait_method(self.elaborator.interner, method, id)?; + let method_id = resolve_trait_item(self.elaborator.interner, method, id)?.unwrap_method(); let type_bindings = self.elaborator.interner.get_instantiation_bindings(id).clone(); let lhs = (lhs, self.elaborator.interner.expr_location(&infix.lhs)); @@ -880,7 +882,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { prefix.trait_method_id.expect("ice: expected prefix operator trait at this point"); let operator = prefix.operator; - let method_id = resolve_trait_method(self.elaborator.interner, method, id)?; + let method_id = resolve_trait_item(self.elaborator.interner, method, id)?.unwrap_method(); let type_bindings = self.elaborator.interner.get_instantiation_bindings(id).clone(); let rhs = (rhs, self.elaborator.interner.expr_location(&prefix.rhs)); diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 4c5cc176b2b..c3cf965bf37 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -38,11 +38,11 @@ use crate::{ }, hir_def::{ self, - expr::{HirExpression, HirIdent, HirLiteral, ImplKind, TraitMethod}, + expr::{HirExpression, HirIdent, HirLiteral, ImplKind, TraitItem}, function::FunctionBody, traits::{ResolvedTraitBound, TraitConstraint}, }, - node_interner::{DefinitionKind, NodeInterner, TraitImplKind, TraitMethodId}, + node_interner::{DefinitionKind, NodeInterner, TraitImplKind}, parser::{Parser, StatementOrExpressionOrLValue}, shared::{Signedness, Visibility}, signed_field::SignedField, @@ -2473,12 +2473,9 @@ fn function_def_as_typed_expr( let trait_bound = ResolvedTraitBound { trait_id: trait_impl.trait_id, trait_generics, location }; let constraint = TraitConstraint { typ: trait_impl.typ.clone(), trait_bound }; - let method_index = trait_impl.methods.iter().position(|id| *id == func_id); - let method_index = method_index.expect("Expected to find the method"); - let method_id = TraitMethodId { trait_id: trait_impl.trait_id, method_index }; - let trait_method = TraitMethod { method_id, constraint, assumed: true }; - let id = interpreter.elaborator.interner.trait_method_id(trait_method.method_id); - HirIdent { location, id, impl_kind: ImplKind::TraitMethod(trait_method) } + let id = interpreter.elaborator.interner.get_trait_item_id(func_id).unwrap().item_id; + let trait_method = TraitItem { definition: id, constraint, assumed: true }; + HirIdent { location, id, impl_kind: ImplKind::TraitItem(trait_method) } } else { HirIdent::non_trait_method(definition_id, location) }; diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 221e7ebc521..58a2db56e87 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -18,7 +18,7 @@ use crate::ast::{ }; use crate::elaborator::PrimitiveType; use crate::hir::resolution::errors::ResolverError; -use crate::node_interner::{ModuleAttributes, NodeInterner, ReferenceId, TypeId}; +use crate::node_interner::{DefinitionKind, ModuleAttributes, NodeInterner, ReferenceId, TypeId}; use crate::token::{SecondaryAttribute, TestScope}; use crate::usage_tracker::{UnusedItem, UsageTracker}; use crate::{Generics, Kind, ResolvedGeneric, Type, TypeVariable}; @@ -513,6 +513,7 @@ impl ModCollector<'_> { let mut method_ids = HashMap::default(); let mut associated_types = Generics::new(); + let mut associated_constant_ids = HashMap::default(); for item in &mut trait_definition.items { if let TraitItem::Function { generics, where_clause, .. } = &mut item.item { @@ -626,15 +627,27 @@ impl ModCollector<'_> { } else { let type_variable_id = context.def_interner.next_type_variable_id(); let typ = self.resolve_associated_constant_type(typ, &mut errors); + let type_var = + TypeVariable::unbound(type_variable_id, Kind::numeric(typ.clone())); - associated_types.push(ResolvedGeneric { - name: Rc::new(name.to_string()), - type_var: TypeVariable::unbound( - type_variable_id, - Kind::numeric(typ), - ), - location: name.location(), - }); + let definition = DefinitionKind::NumericGeneric( + type_var.clone(), + Box::new(typ.clone()), + ); + + let location = name.location(); + let definition_id = context.def_interner.push_definition( + name.to_string(), + false, + false, + definition, + location, + ); + context.def_interner.push_definition_type(definition_id, typ); + + associated_constant_ids.insert(name.to_string(), definition_id); + let name = Rc::new(name.to_string()); + associated_types.push(ResolvedGeneric { name, type_var, location }); } } TraitItem::Type { name, bounds: _ } => { @@ -682,6 +695,7 @@ impl ModCollector<'_> { &unresolved, resolved_generics, associated_types, + associated_constant_ids, ); if context.def_interner.is_in_lsp_mode() { diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index 9f6bdb90d31..5e15c92b941 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -6,7 +6,7 @@ use crate::Shared; use crate::ast::{BinaryOp, BinaryOpKind, Ident, UnaryOp}; use crate::hir::type_check::generics::TraitGenerics; use crate::node_interner::{ - DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId, + DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId, TraitId, TraitItemId, }; use crate::signed_field::SignedField; use crate::token::{FmtStrFragment, Tokens}; @@ -73,16 +73,25 @@ pub enum ImplKind { /// and eventually linked to this id. The boolean indicates whether the impl /// is already assumed to exist - e.g. when resolving a path such as `T::default` /// when there is a corresponding `T: Default` constraint in scope. - TraitMethod(TraitMethod), + TraitItem(TraitItem), } +/// A method or constant defined in a trait #[derive(Debug, Clone)] -pub struct TraitMethod { - pub method_id: TraitMethodId, +pub struct TraitItem { + /// Note that this _must_ be the id of the function or constant within the trait, + /// not the id within the impl. + pub definition: DefinitionId, pub constraint: TraitConstraint, pub assumed: bool, } +impl TraitItem { + pub fn id(&self) -> TraitItemId { + TraitItemId { item_id: self.definition, trait_id: self.constraint.trait_bound.trait_id } + } +} + impl Eq for HirIdent {} impl PartialEq for HirIdent { fn eq(&self, other: &Self) -> bool { @@ -134,7 +143,7 @@ pub struct HirPrefixExpression { /// The trait method id for the operator trait method that corresponds to this operator, /// if such a trait exists (for example, there's no trait for the dereference operator). - pub trait_method_id: Option, + pub trait_method_id: Option, } #[derive(Debug, Clone)] @@ -147,7 +156,7 @@ pub struct HirInfixExpression { /// For derived operators like `!=`, this will lead to the method `Eq::eq`. For these /// cases, it is up to the monomorphization pass to insert the appropriate `not` operation /// after the call to `Eq::eq` to get the result of the `!=` operator. - pub trait_method_id: TraitMethodId, + pub trait_method_id: TraitItemId, } /// This is always a struct field access `my_struct.field` @@ -219,16 +228,15 @@ pub enum HirMethodReference { /// Or a method can come from a Trait impl block, in which case /// the actual function called will depend on the instantiated type, /// which can be only known during monomorphization. - TraitMethodId(TraitMethodId, TraitGenerics, bool /* assumed */), + TraitItemId(DefinitionId, TraitId, TraitGenerics, bool /* assumed */), } impl HirMethodReference { pub fn func_id(&self, interner: &NodeInterner) -> Option { match self { HirMethodReference::FuncId(func_id) => Some(*func_id), - HirMethodReference::TraitMethodId(method_id, _, _) => { - let id = interner.trait_method_id(*method_id); - match &interner.try_definition(id)?.kind { + HirMethodReference::TraitItemId(method_id, _, _, _) => { + match &interner.try_definition(*method_id)?.kind { DefinitionKind::Function(func_id) => Some(*func_id), _ => None, } @@ -247,15 +255,13 @@ impl HirMethodReference { HirMethodReference::FuncId(func_id) => { (interner.function_definition_id(func_id), ImplKind::NotATraitMethod) } - HirMethodReference::TraitMethodId(method_id, trait_generics, assumed) => { - let id = interner.trait_method_id(method_id); - let trait_id = method_id.trait_id; + HirMethodReference::TraitItemId(definition, trait_id, trait_generics, assumed) => { let constraint = TraitConstraint { typ: object_type, trait_bound: ResolvedTraitBound { trait_id, trait_generics, location }, }; - (id, ImplKind::TraitMethod(TraitMethod { method_id, constraint, assumed })) + (definition, ImplKind::TraitItem(TraitItem { definition, constraint, assumed })) } }; let func_var = HirIdent { location, id, impl_kind }; diff --git a/compiler/noirc_frontend/src/hir_def/traits.rs b/compiler/noirc_frontend/src/hir_def/traits.rs index 049f7fd46bc..d9cc1f2d4de 100644 --- a/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/compiler/noirc_frontend/src/hir_def/traits.rs @@ -4,11 +4,11 @@ use rustc_hash::FxHashMap as HashMap; use crate::ResolvedGeneric; use crate::ast::{Ident, ItemVisibility, NoirFunction}; use crate::hir::type_check::generics::TraitGenerics; -use crate::node_interner::NodeInterner; +use crate::node_interner::{DefinitionId, NodeInterner}; use crate::{ Generics, Type, TypeBindings, TypeVariable, graph::CrateId, - node_interner::{FuncId, TraitId, TraitMethodId}, + node_interner::{FuncId, TraitId}, }; use fm::FileId; use noirc_errors::{Location, Span}; @@ -82,6 +82,9 @@ pub struct Trait { pub where_clause: Vec, pub all_generics: Generics, + + /// Map from each associated constant's name to a unique DefinitionId for that constant. + pub associated_constant_ids: HashMap, } #[derive(Debug)] @@ -191,15 +194,27 @@ impl Trait { self.associated_type_bounds = associated_type_bounds; } - pub fn find_method(&self, name: &str) -> Option { - for (idx, method) in self.methods.iter().enumerate() { + pub fn find_method(&self, name: &str, interner: &NodeInterner) -> Option { + for method in self.methods.iter() { if &method.name == name { - return Some(TraitMethodId { trait_id: self.id, method_index: idx }); + let id = *self.method_ids.get(name).unwrap(); + return Some(interner.function_definition_id(id)); } } None } + pub fn find_method_or_constant( + &self, + name: &str, + interner: &NodeInterner, + ) -> Option { + if let Some(method) = self.find_method(name, interner) { + return Some(method); + } + self.associated_constant_ids.get(name).copied() + } + pub fn get_associated_type(&self, last_name: &str) -> Option<&ResolvedGeneric> { self.associated_types.iter().find(|typ| typ.name.as_ref() == last_name) } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 9c67c1b8ae6..23ea6b0c90c 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -12,7 +12,7 @@ use crate::NamedGeneric; use crate::ast::{FunctionKind, IntegerBitSize, ItemVisibility, UnaryOp}; use crate::hir::comptime::InterpreterError; use crate::hir::type_check::{NoMatchingImplFoundError, TypeCheckError}; -use crate::node_interner::{ExprId, GlobalValue, ImplSearchErrorKind}; +use crate::node_interner::{ExprId, GlobalValue, ImplSearchErrorKind, TraitItemId}; use crate::shared::{Signedness, Visibility}; use crate::signed_field::SignedField; use crate::token::FmtStrFragment; @@ -25,7 +25,7 @@ use crate::{ stmt::{HirAssignStatement, HirLValue, HirLetStatement, HirPattern, HirStatement}, types, }, - node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, + node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind}, }; use acvm::{FieldElement, acir::AcirField}; use ast::{GlobalId, IdentId, While}; @@ -86,7 +86,7 @@ pub(super) struct Monomorphizer<'interner> { node_interner::FuncId, FuncId, TypeBindings, - Option, + Option, bool, Location, )>, @@ -263,7 +263,7 @@ impl<'interner> Monomorphizer<'interner> { expr_id: node_interner::ExprId, typ: &HirType, turbofish_generics: &[HirType], - trait_method: Option, + trait_method: Option, ) -> Definition { let typ = typ.follow_bindings(); let turbofish_generics = vecmap(turbofish_generics, |typ| typ.follow_bindings()); @@ -537,7 +537,7 @@ impl<'interner> Monomorphizer<'interner> { let method = prefix .trait_method_id .expect("ice: missing trait method if when impl was found"); - let func = self.resolve_trait_method_expr(expr, function_type, method)?; + let func = self.resolve_trait_item_expr(expr, function_type, method)?; self.create_prefix_operator_impl_call(func, rhs, ret, location)? } else { let operator = prefix.operator; @@ -559,7 +559,7 @@ impl<'interner> Monomorphizer<'interner> { self.interner.get_infix_operator_type(infix.lhs, operator, expr); let method = infix.trait_method_id; - let func = self.resolve_trait_method_expr(expr, function_type, method)?; + let func = self.resolve_trait_item_expr(expr, function_type, method)?; let operator = infix.operator; self.create_infix_operator_impl_call(func, lhs, operator, rhs, ret, location)? } else { @@ -1047,8 +1047,8 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result { let typ = self.interner.id_type(expr_id); - if let ImplKind::TraitMethod(method) = ident.impl_kind { - return self.resolve_trait_method_expr(expr_id, typ, method.method_id); + if let ImplKind::TraitItem(item) = ident.impl_kind { + return self.resolve_trait_item_expr(expr_id, typ, item.id()); } // Ensure all instantiation bindings are bound. @@ -1067,7 +1067,7 @@ impl<'interner> Monomorphizer<'interner> { } let definition = self.interner.definition(ident.id); - let ident = match &definition.kind { + match &definition.kind { DefinitionKind::Function(func_id) => { let mutable = definition.mutable; let location = Some(ident.location); @@ -1085,60 +1085,32 @@ impl<'interner> Monomorphizer<'interner> { ast::Ident { location, mutable, definition, name, typ: typ.clone(), id }; let ident_expression = ast::Expression::Ident(ident); if self.is_function_closure_type(&typ) { - ast::Expression::Tuple(vec![ - ast::Expression::ExtractTupleField( - Box::new(ident_expression.clone()), - 0usize, - ), - ast::Expression::ExtractTupleField(Box::new(ident_expression), 1usize), - ]) + let ident_clone = Box::new(ident_expression.clone()); + let function = ast::Expression::ExtractTupleField(ident_clone, 0); + let env = ast::Expression::ExtractTupleField(Box::new(ident_expression), 1); + Ok(ast::Expression::Tuple(vec![function, env])) } else { - ident_expression + Ok(ident_expression) } } DefinitionKind::Global(global_id) => { - self.global_ident(*global_id, definition.name.clone(), &typ, ident.location)? + self.global_ident(*global_id, definition.name.clone(), &typ, ident.location) } DefinitionKind::Local(_) => match self.lookup_captured_expr(ident.id) { - Some(expr) => expr, + Some(expr) => Ok(expr), None => { let Some(ident) = self.local_ident(&ident, &typ)? else { let location = self.interner.id_location(expr_id); let message = "ICE: Variable not found during monomorphization"; return Err(MonomorphizationError::InternalError { location, message }); }; - ast::Expression::Ident(ident) + Ok(ast::Expression::Ident(ident)) } }, DefinitionKind::NumericGeneric(type_variable, numeric_typ) => { - let value = match &*type_variable.borrow() { - TypeBinding::Unbound(_, _) => { - unreachable!("Unbound type variable used in expression") - } - TypeBinding::Bound(binding) => { - let location = self.interner.id_location(expr_id); - binding - .evaluate_to_field_element( - &Kind::Numeric(numeric_typ.clone()), - location, - ) - .map_err(|err| MonomorphizationError::UnknownArrayLength { - length: binding.clone(), - err, - location, - })? - } - }; let location = self.interner.id_location(expr_id); - - if !Kind::Numeric(numeric_typ.clone()).unifies(&Kind::numeric(typ.clone())) { - let message = "ICE: Generic's kind does not match expected type"; - return Err(MonomorphizationError::InternalError { location, message }); - } - - let typ = Self::convert_type(&typ, ident.location)?; - let value = SignedField::positive(value); - ast::Expression::Literal(ast::Literal::Integer(value, typ, location)) + let value = Type::TypeVariable(type_variable.clone()); + self.numeric_generic(value, numeric_typ.as_ref().clone(), typ, location) } DefinitionKind::AssociatedConstant(trait_impl_id, name) => { let location = ident.location; @@ -1157,20 +1129,45 @@ impl<'interner> Monomorphizer<'interner> { Ok(value) => { let typ = Self::convert_type(&numeric_type, location)?; let value = SignedField::positive(value); - ast::Expression::Literal(ast::Literal::Integer(value, typ, location)) - } - Err(err) => { - return Err(MonomorphizationError::CannotComputeAssociatedConstant { - name: name.clone(), - err, - location, - }); + Ok(ast::Expression::Literal(ast::Literal::Integer(value, typ, location))) } + Err(err) => Err(MonomorphizationError::CannotComputeAssociatedConstant { + name: name.clone(), + err, + location, + }), } } - }; + } + } - Ok(ident) + /// Monomorphize a numeric generic as a numeric constant. + /// Expects arguments to correspond to `let N: $expected_type = $value;` + fn numeric_generic( + &self, + value: Type, + expected_type: Type, + expr_type: Type, + location: Location, + ) -> Result { + let expected_kind = Kind::Numeric(Box::new(expected_type.clone())); + let value = value.evaluate_to_field_element(&expected_kind, location).map_err(|err| { + MonomorphizationError::UnknownArrayLength { + length: value.follow_bindings(), + err, + location, + } + })?; + + let expr_kind = Kind::Numeric(Box::new(expr_type.clone())); + if !expected_kind.unifies(&expr_kind) { + let message = "ICE: Generic's kind does not match expected type"; + return Err(MonomorphizationError::InternalError { location, message }); + } + + let typ = Self::convert_type(&expected_type, location)?; + let value = SignedField::positive(value); + Ok(ast::Expression::Literal(ast::Literal::Integer(value, typ, location))) } fn global_ident( @@ -1606,29 +1603,43 @@ impl<'interner> Monomorphizer<'interner> { } } - fn resolve_trait_method_expr( + fn resolve_trait_item_expr( &mut self, expr_id: node_interner::ExprId, function_type: HirType, - method: TraitMethodId, + trait_item_id: TraitItemId, ) -> Result { - let func_id = resolve_trait_method(self.interner, method, expr_id) + let item = resolve_trait_item(self.interner, trait_item_id, expr_id) .map_err(MonomorphizationError::InterpreterError)?; - let func_id = - match self.lookup_function(func_id, expr_id, &function_type, &[], Some(method)) { - Definition::Function(func_id) => func_id, - _ => unreachable!(), - }; + let func_id = match item { + TraitItem::Method(func_id) => func_id, + TraitItem::Constant { id, expected_type, value } => { + let location = self.interner.definition(id).location; + let expr_type = self.interner.id_type(expr_id); + return self.numeric_generic(value, expected_type, expr_type, location); + } + }; + + let func_id = match self.lookup_function( + func_id, + expr_id, + &function_type, + &[], + Some(trait_item_id), + ) { + Definition::Function(func_id) => func_id, + _ => unreachable!(), + }; - let the_trait = self.interner.get_trait(method.trait_id); let location = self.interner.expr_location(&expr_id); + let name = self.interner.definition_name(trait_item_id.item_id).to_string(); Ok(ast::Expression::Ident(ast::Ident { definition: Definition::Function(func_id), mutable: false, location: None, - name: the_trait.methods[method.method_index].name.to_string(), + name, typ: Self::convert_type(&function_type, location)?, id: self.next_ident_id(), })) @@ -1847,7 +1858,7 @@ impl<'interner> Monomorphizer<'interner> { expr_id: node_interner::ExprId, function_type: HirType, turbofish_generics: Vec, - trait_method: Option, + trait_method: Option, ) -> FuncId { let new_id = self.next_function_id(); let is_unconstrained = self.is_unconstrained(id); @@ -2466,20 +2477,17 @@ pub fn undo_instantiation_bindings(bindings: TypeBindings) { /// the correct type bindings during monomorphization. pub fn perform_impl_bindings( interner: &NodeInterner, - trait_method: Option, + trait_method: Option, impl_method: node_interner::FuncId, location: Location, ) -> Result { let mut bindings = TypeBindings::default(); if let Some(trait_method) = trait_method { - let the_trait = interner.get_trait(trait_method.trait_id); - let mut trait_method_type = - the_trait.methods[trait_method.method_index].typ.as_monotype().clone(); + interner.definition_type(trait_method.item_id).as_monotype().clone(); - let mut impl_method_type = - interner.function_meta(&impl_method).typ.unwrap_forall().1.clone(); + let mut impl_method_type = interner.function_meta(&impl_method).typ.as_monotype().clone(); // Make each NamedGeneric in this type bindable by replacing it with a TypeVariable // with the same internal id, binding. @@ -2494,30 +2502,36 @@ pub fn perform_impl_bindings( } })?; + for (_, kind, binding) in bindings.values_mut() { + *kind = kind.follow_bindings(); + *binding = binding.follow_bindings(); + } + perform_instantiation_bindings(&bindings); } Ok(bindings) } -pub fn resolve_trait_method( +/// Resolve a trait item to a particular impl +fn resolve_trait_item_impl( interner: &mut NodeInterner, - method: TraitMethodId, + method_id: TraitItemId, expr_id: ExprId, -) -> Result { +) -> Result { let trait_impl = interner.get_selected_impl_for_expression(expr_id).ok_or_else(|| { let location = interner.expr_location(&expr_id); InterpreterError::NoImpl { location } })?; - let impl_id = match trait_impl { - TraitImplKind::Normal(impl_id) => impl_id, + match trait_impl { + TraitImplKind::Normal(impl_id) => Ok(impl_id), TraitImplKind::Assumed { object_type, trait_generics } => { let location = interner.expr_location(&expr_id); match interner.lookup_trait_implementation( &object_type, - method.trait_id, + method_id.trait_id, &trait_generics.ordered, &trait_generics.named, ) { @@ -2527,35 +2541,81 @@ pub fn resolve_trait_method( let mut bindings = interner.get_instantiation_bindings(expr_id).clone(); bindings.extend(instantiation_bindings); interner.store_instantiation_bindings(expr_id, bindings); - impl_id + Ok(impl_id) } Ok((TraitImplKind::Assumed { .. }, _instantiation_bindings)) => { - return Err(InterpreterError::NoImpl { location }); + Err(InterpreterError::NoImpl { location }) } Err(ImplSearchErrorKind::TypeAnnotationsNeededOnObjectType) => { - return Err(InterpreterError::TypeAnnotationsNeededForMethodCall { location }); + Err(InterpreterError::TypeAnnotationsNeededForMethodCall { location }) } Err(ImplSearchErrorKind::Nested(constraints)) => { if let Some(error) = NoMatchingImplFoundError::new(interner, constraints, location) { - return Err(InterpreterError::NoMatchingImplFound { error }); + Err(InterpreterError::NoMatchingImplFound { error }) } else { - return Err(InterpreterError::NoImpl { location }); + Err(InterpreterError::NoImpl { location }) } } Err(ImplSearchErrorKind::MultipleMatching(candidates)) => { - return Err(InterpreterError::MultipleMatchingImpls { + Err(InterpreterError::MultipleMatchingImpls { object_type, location, candidates, - }); + }) } } } - }; + } +} + +pub(crate) fn resolve_trait_item( + interner: &mut NodeInterner, + method_id: TraitItemId, + expr_id: ExprId, +) -> Result { + let impl_id = resolve_trait_item_impl(interner, method_id, expr_id)?; + + let name = interner.definition_name(method_id.item_id); + let impl_ = interner.get_trait_implementation(impl_id); + let impl_ = impl_.borrow(); - Ok(interner.get_trait_implementation(impl_id).borrow().methods[method.method_index]) + for method in &impl_.methods { + if interner.function_name(method) == name { + return Ok(TraitItem::Method(*method)); + } + } + + if let Some((id, expected_type)) = interner.get_trait_impl_associated_constant(impl_id, name) { + // The lookup above returns the expected type but not the value that + // is expected to resolve to a Type::Constant - we have to look that up separately. + for item in interner.get_associated_types_for_impl(impl_id) { + if item.name.as_str() == name { + let expected_type = expected_type.clone(); + let id = *id; + return Ok(TraitItem::Constant { id, expected_type, value: item.typ.clone() }); + } + } + } + + unreachable!("No method named `{name}` in impl") +} + +pub(crate) enum TraitItem { + Method(node_interner::FuncId), + Constant { id: node_interner::DefinitionId, expected_type: Type, value: Type }, +} + +impl TraitItem { + pub(crate) fn unwrap_method(&self) -> node_interner::FuncId { + match self { + TraitItem::Method(func_id) => *func_id, + TraitItem::Constant { .. } => { + panic!("Expected `TraitItem::Method`, but found `TraitItem::Constant`") + } + } + } } /// Extend the arguments to `print` (which is a `bool` to show if newline is needed and diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 92afeb3bbc0..d52f45dff89 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -526,10 +526,11 @@ impl TraitId { #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] pub struct TraitImplId(pub usize); -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct TraitMethodId { +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct TraitItemId { pub trait_id: TraitId, - pub method_index: usize, // index in Trait::methods + /// This is the definition id of the method or associated constant in the trait, not an impl + pub item_id: DefinitionId, } macro_rules! into_index { @@ -773,6 +774,7 @@ impl NodeInterner { unresolved_trait: &UnresolvedTrait, generics: Generics, associated_types: Generics, + associated_constant_ids: HashMap, ) { let new_trait = Trait { id: type_id, @@ -789,6 +791,7 @@ impl NodeInterner { trait_bounds: Vec::new(), where_clause: Vec::new(), all_generics: Vec::new(), + associated_constant_ids, }; self.traits.insert(type_id, new_trait); @@ -1417,14 +1420,29 @@ impl NodeInterner { self.function_definition_ids[&function] } - /// Returns the DefinitionId of a trait's method, panics if the given trait method - /// is not a valid method of the trait or if the trait has not yet had - /// its methods ids set during name resolution. - pub fn trait_method_id(&self, trait_method: TraitMethodId) -> DefinitionId { - let the_trait = self.get_trait(trait_method.trait_id); - let method_name = &the_trait.methods[trait_method.method_index].name; - let function_id = the_trait.method_ids[method_name.as_str()]; - self.function_definition_id(function_id) + /// Returns the definition id and trait id for a given trait or impl function. + /// + /// If this is an impl function, the DefinitionId inside the TraitItemId will still + /// be that of the function in the parent trait. + pub fn get_trait_item_id(&self, function_id: FuncId) -> Option { + let function = self.function_meta(&function_id); + + match function.trait_impl { + Some(impl_id) => { + let trait_id = self.get_trait_implementation(impl_id).borrow().trait_id; + let the_trait = self.get_trait(trait_id); + let name = self.definition_name(function.name.id); + let definition_id = the_trait + .find_method_or_constant(name, self) + .expect("Expected parent trait to have function from impl"); + Some(TraitItemId { item_id: definition_id, trait_id }) + } + None => { + let trait_id = function.trait_id?; + let definition_id = self.function_definition_id(function_id); + Some(TraitItemId { item_id: definition_id, trait_id }) + } + } } /// Adds a non-trait method to a type. @@ -1511,17 +1529,6 @@ impl NodeInterner { format!("{object_type:?}: {name}{generics}") } - /// If the given function belongs to a trait impl, return its trait method id. - /// Otherwise, return None. - pub fn get_trait_method_id(&self, function: FuncId) -> Option { - let impl_id = self.function_meta(&function).trait_impl?; - let trait_impl = self.get_trait_implementation(impl_id); - let trait_impl = trait_impl.borrow(); - - let method_index = trait_impl.methods.iter().position(|id| *id == function)?; - Some(TraitMethodId { trait_id: trait_impl.trait_id, method_index }) - } - /// Given a `ObjectType: TraitId` pair, try to find an existing impl that satisfies the /// constraint. If an impl cannot be found, this will return a vector of each constraint /// in the path to get to the failing constraint. Usually this is just the single failing @@ -1981,22 +1988,22 @@ impl NodeInterner { /// to the same trait (such as `==` and `!=`). /// `self.infix_operator_traits` is expected to be filled before name resolution, /// during definition collection. - pub fn get_operator_trait_method(&self, operator: BinaryOpKind) -> TraitMethodId { + pub fn get_operator_trait_method(&self, operator: BinaryOpKind) -> TraitItemId { let trait_id = self.infix_operator_traits[&operator]; - - // Assume that the operator's method to be overloaded is the first method of the trait. - TraitMethodId { trait_id, method_index: 0 } + let the_trait = self.get_trait(trait_id); + let func_id = *the_trait.method_ids.values().next().unwrap(); + TraitItemId { trait_id, item_id: self.function_definition_id(func_id) } } /// Retrieves the trait id for a given unary operator. /// Only some unary operators correspond to a trait: `-` and `!`, but for example `*` does not. /// `self.prefix_operator_traits` is expected to be filled before name resolution, /// during definition collection. - pub fn get_prefix_operator_trait_method(&self, operator: &UnaryOp) -> Option { - let trait_id = self.prefix_operator_traits.get(operator)?; - - // Assume that the operator's method to be overloaded is the first method of the trait. - Some(TraitMethodId { trait_id: *trait_id, method_index: 0 }) + pub fn get_prefix_operator_trait_method(&self, operator: &UnaryOp) -> Option { + let trait_id = *self.prefix_operator_traits.get(operator)?; + let the_trait = self.get_trait(trait_id); + let func_id = *the_trait.method_ids.values().next().unwrap(); + Some(TraitItemId { trait_id, item_id: self.function_definition_id(func_id) }) } /// Add the given trait as an operator trait if its name matches one of the @@ -2068,25 +2075,63 @@ impl NodeInterner { /// to `get_operator_trait` do not panic when the stdlib isn't present. #[cfg(any(test, feature = "test_utils"))] pub fn populate_dummy_operator_traits(&mut self) { - let dummy_trait = TraitId(ModuleId::dummy_id()); - self.infix_operator_traits.insert(BinaryOpKind::Add, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::Subtract, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::Multiply, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::Divide, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::Modulo, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::Equal, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::NotEqual, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::Less, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::LessEqual, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::Greater, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::GreaterEqual, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::And, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::Or, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::Xor, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::ShiftLeft, dummy_trait); - self.infix_operator_traits.insert(BinaryOpKind::ShiftRight, dummy_trait); - self.prefix_operator_traits.insert(UnaryOp::Minus, dummy_trait); - self.prefix_operator_traits.insert(UnaryOp::Not, dummy_trait); + // Populate a dummy trait with a single method, as the trait, and its single methods, + // are looked up during `get_operator_trait`. + let mut usize_arena = Arena::default(); + let index = usize_arena.insert(0); + let stdlib = CrateId::Stdlib(0); + let func_id = FuncId::dummy_id(); + // Use a definition ID that won't clash with anything else, and isn't the dummy one + let definition_id = DefinitionId(usize::MAX - 1); + self.function_definition_ids.insert(func_id, definition_id); + let module_id = ModuleId { krate: stdlib, local_id: LocalModuleId::new(index) }; + let trait_id = TraitId(module_id); + let self_type_typevar = self.next_type_variable_id(); + let mut method_ids: HashMap = Default::default(); + method_ids.insert("dummy_method".to_string(), func_id); + + let trait_ = Trait { + id: trait_id, + crate_id: stdlib, + methods: vec![], + method_ids, + associated_types: vec![], + associated_type_bounds: Default::default(), + name: Ident::new("Dummy".to_string(), Location::dummy()), + generics: vec![], + location: Location::dummy(), + visibility: ItemVisibility::Public, + self_type_typevar: TypeVariable::unbound(self_type_typevar, Kind::Normal), + trait_bounds: vec![], + where_clause: vec![], + all_generics: vec![], + associated_constant_ids: Default::default(), + }; + self.traits.insert(trait_id, trait_); + + let operators = [ + BinaryOpKind::Add, + BinaryOpKind::Subtract, + BinaryOpKind::Multiply, + BinaryOpKind::Divide, + BinaryOpKind::Modulo, + BinaryOpKind::Equal, + BinaryOpKind::NotEqual, + BinaryOpKind::Less, + BinaryOpKind::LessEqual, + BinaryOpKind::Greater, + BinaryOpKind::GreaterEqual, + BinaryOpKind::And, + BinaryOpKind::Or, + BinaryOpKind::Xor, + BinaryOpKind::ShiftLeft, + BinaryOpKind::ShiftRight, + ]; + + // It's fine to use the same trait for all operators, at least in tests + for operator in operators { + self.infix_operator_traits.insert(operator, trait_id); + } } pub(crate) fn ordering_type(&self) -> Type { diff --git a/test_programs/compile_success_empty/associated_constants_in_as_trait_expr/Nargo.toml b/test_programs/compile_success_empty/associated_constants_in_as_trait_expr/Nargo.toml new file mode 100644 index 00000000000..1dbfa026fb6 --- /dev/null +++ b/test_programs/compile_success_empty/associated_constants_in_as_trait_expr/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "associated_constants_in_as_trait_expr" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/associated_constants_in_as_trait_expr/src/main.nr b/test_programs/compile_success_empty/associated_constants_in_as_trait_expr/src/main.nr new file mode 100644 index 00000000000..f8a0d19878e --- /dev/null +++ b/test_programs/compile_success_empty/associated_constants_in_as_trait_expr/src/main.nr @@ -0,0 +1,29 @@ +trait Serialize { + let N: u32; +} + +impl Serialize for Field { + let N: u32 = 1; +} + +fn main() { + let x = ::N; + assert_eq(x, 1); + assert_eq(get_n_1::(), 1); + assert_eq(get_n_2::(), 1); + + comptime { + let x = ::N; + assert_eq(x, 1); + assert_eq(get_n_1::(), 1); + assert_eq(get_n_2::(), 1); + } +} + +fn get_n_1() -> u32 { + T::N +} + +fn get_n_2() -> u32 { + ::N +} diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 107c7cfa751..4e356524bcf 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -176,7 +176,7 @@ const TESTS_WITHOUT_STDOUT_CHECK: [&str; 0] = []; /// These tests are ignored because of existing bugs in `nargo expand`. /// As the bugs are fixed these tests should be removed from this list. /// (some are ignored on purpose for the same reason as `IGNORED_NARGO_EXPAND_EXECUTION_TESTS`) -const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 13] = [ +const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 14] = [ // bug "associated_type_bounds", // bug @@ -204,6 +204,8 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 13] = [ "workspace_reexport_bug", // bug "nested_trait_associated_type_regression_8252", + // bug: the `::N` syntax is dropped and becomes just `N`. + "associated_constants_in_as_trait_expr", ]; /// These tests are ignored because of existing bugs in `nargo expand`. diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_empty/associated_constants_in_as_trait_expr/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/compile_success_empty/associated_constants_in_as_trait_expr/execute__tests__expanded.snap new file mode 100644 index 00000000000..990d812ef39 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_empty/associated_constants_in_as_trait_expr/execute__tests__expanded.snap @@ -0,0 +1,16 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +trait Serialize { + let N: u32; +} + +impl Serialize for Field { + let N: u32 = 1; +} + +fn main() { + let x: u32 = N; + assert(x == 1_u32); +} diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_empty/associated_constants_in_as_trait_expr/execute__tests__force_brillig_false_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/compile_success_empty/associated_constants_in_as_trait_expr/execute__tests__force_brillig_false_inliner_0.snap new file mode 100644 index 00000000000..7de940827c8 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_empty/associated_constants_in_as_trait_expr/execute__tests__force_brillig_false_inliner_0.snap @@ -0,0 +1,26 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": null, + "error_types": {} + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : []" + ], + "debug_symbols": "XY5BCsQwCEXv4rqLWfcqw1BsaosgJtikMITefWyYQOlK/3/6tcJCc9km1jXuML4rzMYivE0SA2aO6m49B+hyykbkFty4byU00gyjFpEBDpTShvaE2mpGc/oagHTx6oErC13d+XGBge158UBjnIX+ci0abjR/Uyf942Qx0FKMrqTGPPsH", + "file_map": {}, + "names": [ + "main" + ], + "brillig_names": [] +} diff --git a/tooling/nargo_expand/src/printer/hir.rs b/tooling/nargo_expand/src/printer/hir.rs index 9209030f222..99dd3844f17 100644 --- a/tooling/nargo_expand/src/printer/hir.rs +++ b/tooling/nargo_expand/src/printer/hir.rs @@ -387,7 +387,7 @@ impl ItemPrinter<'_, '_> { }; // Special case: assumed trait method - if let ImplKind::TraitMethod(trait_method) = hir_ident.impl_kind { + if let ImplKind::TraitItem(trait_method) = hir_ident.impl_kind { if trait_method.assumed { // Is this `self.foo()` where `self` is currently a trait? // If so, show it as `self.foo()` instead of `Self::foo(self)`.