diff --git a/compiler/noirc_frontend/src/elaborator/enums.rs b/compiler/noirc_frontend/src/elaborator/enums.rs index 2e3662e7d3a..9917460dc87 100644 --- a/compiler/noirc_frontend/src/elaborator/enums.rs +++ b/compiler/noirc_frontend/src/elaborator/enums.rs @@ -936,7 +936,7 @@ impl<'elab, 'ctx> MatchCompiler<'elab, 'ctx> { | Type::TypeVariable(_) | Type::FmtString(_, _) | Type::TraitAsType(_, _, _) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_) | Type::CheckedCast { .. } | Type::Function(_, _, _, _) | Type::Reference(..) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 31567d00991..cf648560db7 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -4,7 +4,7 @@ use std::{ }; use crate::{ - DataType, StructField, TypeBindings, + DataType, NamedGeneric, StructField, TypeBindings, ast::{IntegerBitSize, ItemVisibility, UnresolvedType}, graph::CrateGraph, hir_def::traits::ResolvedTraitBound, @@ -659,7 +659,11 @@ impl<'context> Elaborator<'context> { generics.push(new_generic.clone()); let name = format!("impl {trait_path}"); - let generic_type = Type::NamedGeneric(new_generic, Rc::new(name)); + let generic_type = Type::NamedGeneric(NamedGeneric { + type_var: new_generic, + name: Rc::new(name), + implicit: false, + }); let trait_bound = TraitBound { trait_path, trait_id: None, trait_generics }; if let Some(trait_bound) = self.resolve_trait_bound(&trait_bound) { @@ -728,7 +732,9 @@ impl<'context> Elaborator<'context> { // previous macro call being inserted into a generics list. UnresolvedGeneric::Resolved(id, location) => { match self.interner.get_quoted_type(*id).follow_bindings() { - Type::NamedGeneric(type_variable, name) => Ok((type_variable.clone(), name)), + Type::NamedGeneric(NamedGeneric { type_var, name, .. }) => { + Ok((type_var.clone(), name)) + } other => Err(ResolverError::MacroResultInGenericsListNotAGeneric { location: *location, typ: other.clone(), @@ -886,7 +892,11 @@ impl<'context> Elaborator<'context> { let location = bound.trait_path.location; let name = format!("<{object} as {trait_name}>::{}", associated_type.name); let name = Rc::new(name); - let typ = Type::NamedGeneric(type_var.clone(), name.clone()); + let typ = Type::NamedGeneric(NamedGeneric { + type_var: type_var.clone(), + name: name.clone(), + implicit: true, + }); let typ = self.interner.push_quoted_type(typ); let typ = UnresolvedTypeData::Resolved(typ).with_location(location); let ident = Ident::new(associated_type.name.as_ref().clone(), location); @@ -2242,7 +2252,7 @@ impl<'context> Elaborator<'context> { idents.insert(ident.clone()); } UnresolvedGeneric::Resolved(quoted_type_id, span) => { - if let Type::NamedGeneric(_type_variable, name) = + if let Type::NamedGeneric(NamedGeneric { name, .. }) = self.interner.get_quoted_type(*quoted_type_id).follow_bindings() { idents.insert(Ident::new(name.to_string(), *span)); diff --git a/compiler/noirc_frontend/src/elaborator/trait_impls.rs b/compiler/noirc_frontend/src/elaborator/trait_impls.rs index 34d9864b075..26d8d546004 100644 --- a/compiler/noirc_frontend/src/elaborator/trait_impls.rs +++ b/compiler/noirc_frontend/src/elaborator/trait_impls.rs @@ -1,5 +1,5 @@ use crate::{ - ResolvedGeneric, + NamedGeneric, ResolvedGeneric, ast::{Ident, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, graph::CrateId, hir::def_collector::{ @@ -161,7 +161,11 @@ impl Elaborator<'_> { ) in method.direct_generics.iter().zip(&override_meta.direct_generics) { let trait_fn_kind = trait_fn_generic.kind(); - let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone()); + let arg = Type::NamedGeneric(NamedGeneric { + type_var: impl_fn_generic.clone(), + name: name.clone(), + implicit: false, + }); bindings.insert( trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind.clone(), arg), @@ -193,10 +197,13 @@ impl Elaborator<'_> { continue; } + let override_trait_generics = + override_trait_constraint.trait_bound.trait_generics.clone(); + if !substituted_method_ids.contains(&( override_trait_constraint.typ.clone(), override_trait_constraint.trait_bound.trait_id, - override_trait_constraint.trait_bound.trait_generics.clone(), + override_trait_generics, )) { let the_trait = self.interner.get_trait(override_trait_constraint.trait_bound.trait_id); diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 585c726d8cc..997b984a1fe 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -4,7 +4,7 @@ use iter_extended::vecmap; use noirc_errors::Location; use crate::{ - ResolvedGeneric, Type, TypeBindings, + NamedGeneric, ResolvedGeneric, Type, TypeBindings, ast::{ BlockExpression, FunctionDefinition, FunctionKind, FunctionReturnType, Ident, ItemVisibility, NoirFunction, TraitItem, UnresolvedGeneric, UnresolvedGenerics, @@ -330,7 +330,11 @@ pub(crate) fn check_trait_impl_method_matches_declaration( ) in trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics) { let trait_fn_kind = trait_fn_generic.kind(); - let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone()); + let arg = Type::NamedGeneric(NamedGeneric { + type_var: impl_fn_generic.clone(), + name: name.clone(), + implicit: false, + }); bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind, arg)); } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index a1ca171e95a..5695a309c19 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -6,7 +6,8 @@ use noirc_errors::Location; use rustc_hash::FxHashMap as HashMap; use crate::{ - Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeBindings, UnificationError, + Generics, Kind, NamedGeneric, ResolvedGeneric, Type, TypeBinding, TypeBindings, + UnificationError, ast::{ AsTraitPath, BinaryOpKind, GenericTypeArgs, Ident, IntegerBitSize, Path, PathKind, UnaryOp, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, @@ -155,7 +156,7 @@ impl Elaborator<'_> { let env = Box::new(self.resolve_type_with_kind_inner(*env, kind, mode)); match *env { - Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _) => { + Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_) => { Type::Function(args, ret, env, unconstrained) } _ => { @@ -498,7 +499,7 @@ impl Elaborator<'_> { let name = path.last_name(); if let Some(generic) = self.find_generic(name) { let generic = generic.clone(); - return Some(Type::NamedGeneric(generic.type_var, generic.name)); + return Some(generic.as_named_generic()); } } else if let Some(typ) = self.lookup_associated_type_on_self(path) { return Some(typ); @@ -744,7 +745,7 @@ impl Elaborator<'_> { } for constraint in self.trait_bounds.clone() { - if let Type::NamedGeneric(_, name) = &constraint.typ { + if let Type::NamedGeneric(NamedGeneric { name, .. }) = &constraint.typ { // if `path` is `T::method_name`, we're looking for constraint of the form `T: SomeTrait` if path.segments[0].ident.as_str() != name.as_str() { continue; @@ -1515,7 +1516,7 @@ impl Elaborator<'_> { }); None } - Type::NamedGeneric(_, _) => self.lookup_method_in_trait_constraints( + Type::NamedGeneric(_) => self.lookup_method_in_trait_constraints( object_type, method_name, location, diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index 439cbe268af..451cf14cea5 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -2,6 +2,7 @@ use fm::FileId; use iter_extended::vecmap; use noirc_errors::{Located, Location, Span}; +use crate::NamedGeneric; use crate::ast::{ ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainKind, ConstructorExpression, ExpressionKind, ForLoopStatement, ForRange, GenericTypeArgs, Ident, @@ -438,7 +439,7 @@ impl Type { let name = Path::from_single(name.as_ref().clone(), Location::dummy()); UnresolvedTypeData::TraitAsType(name, generics) } - Type::NamedGeneric(_var, name) => { + Type::NamedGeneric(NamedGeneric { name, .. }) => { let name = Path::from_single(name.as_ref().clone(), Location::dummy()); UnresolvedTypeData::Named(name, GenericTypeArgs::default(), true) } @@ -479,7 +480,7 @@ impl Type { match self.follow_bindings() { Type::Constant(length, _kind) => UnresolvedTypeExpression::Constant(length, location), - Type::NamedGeneric(_var, name) => { + Type::NamedGeneric(NamedGeneric { name, .. }) => { let path = Path::from_single(name.as_ref().clone(), location); UnresolvedTypeExpression::Variable(path) } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 688be1eddcc..8c96ab4bc78 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -17,7 +17,7 @@ use num_bigint::BigUint; use rustc_hash::FxHashMap as HashMap; use crate::{ - Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, + Kind, NamedGeneric, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, ast::{ ArrayLiteral, BlockExpression, ConstrainKind, Expression, ExpressionKind, ForRange, FunctionKind, FunctionReturnType, Ident, IntegerBitSize, ItemVisibility, LValue, Literal, @@ -446,7 +446,11 @@ fn type_def_add_generic( let type_var_kind = Kind::Normal; let type_var = TypeVariable::unbound(interner.next_type_variable_id(), type_var_kind); - let typ = Type::NamedGeneric(type_var.clone(), name.clone()); + let typ = Type::NamedGeneric(NamedGeneric { + type_var: type_var.clone(), + name: name.clone(), + implicit: false, + }); let new_generic = ResolvedGeneric { name, type_var, location: generic_location }; the_struct.generics.push(new_generic); @@ -464,9 +468,7 @@ fn type_def_as_type( let type_def_rc = interner.get_type(struct_id); let type_def = type_def_rc.borrow(); - let generics = vecmap(&type_def.generics, |generic| { - Type::NamedGeneric(generic.type_var.clone(), generic.name.clone()) - }); + let generics = vecmap(&type_def.generics, |generic| generic.clone().as_named_generic()); drop(type_def); Ok(Value::Type(Type::DataType(type_def_rc, generics))) @@ -1503,7 +1505,7 @@ fn zeroed(return_type: Type, location: Location) -> Value { | Type::Quoted(_) | Type::Error | Type::TraitAsType(..) - | Type::NamedGeneric(_, _) => Value::Zeroed(return_type), + | Type::NamedGeneric(_) => Value::Zeroed(return_type), } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 63aaf716f5a..ed0fa4a05de 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -13,6 +13,7 @@ use acvm::{AcirField, FieldElement}; use crate::{ ast::{IntegerBitSize, ItemVisibility}, hir::type_check::{TypeCheckError, generics::TraitGenerics}, + hir_def::types, node_interner::{ExprId, NodeInterner, TraitId, TypeAliasId}, signed_field::{AbsU128, SignedField}, }; @@ -91,7 +92,7 @@ pub enum Type { /// NamedGenerics are the 'T' or 'U' in a user-defined generic function /// like `fn foo(...) {}`. Unlike TypeVariables, they cannot be bound over. - NamedGeneric(TypeVariable, Rc), + NamedGeneric(NamedGeneric), /// A cast (to, from) that's checked at monomorphization. /// @@ -143,6 +144,14 @@ pub enum Type { Error, } +#[derive(PartialEq, Eq, Clone, Ord, PartialOrd, Debug)] +pub struct NamedGeneric { + pub type_var: TypeVariable, + pub name: Rc, + /// Was this named generic implicitly added? + pub implicit: bool, +} + /// A Kind is the type of a Type. These are used since only certain kinds of types are allowed in /// certain positions. /// @@ -394,7 +403,11 @@ pub struct ResolvedGeneric { impl ResolvedGeneric { pub fn as_named_generic(self) -> Type { - Type::NamedGeneric(self.type_var, self.name) + Type::NamedGeneric(NamedGeneric { + type_var: self.type_var, + name: self.name, + implicit: false, + }) } pub fn kind(&self) -> Kind { @@ -491,9 +504,7 @@ impl DataType { /// Return the generics on this type as a vector of types pub fn generic_types(&self) -> Vec { - vecmap(&self.generics, |generic| { - Type::NamedGeneric(generic.type_var.clone(), generic.name.clone()) - }) + vecmap(&self.generics, |generic| generic.clone().as_named_generic()) } /// Returns the field matching the given field name, as well as its visibility and field index. @@ -1019,8 +1030,8 @@ impl std::fmt::Display for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name) => match &*binding.borrow() { - TypeBinding::Bound(binding) => binding.fmt(f), + Type::NamedGeneric(NamedGeneric { type_var, name, .. }) => match &*type_var.borrow() { + TypeBinding::Bound(type_var) => type_var.fmt(f), TypeBinding::Unbound(_, _) if name.is_empty() => write!(f, "_"), TypeBinding::Unbound(_, _) => write!(f, "{name}"), }, @@ -1266,7 +1277,7 @@ impl Type { Type::FmtString(_, _) | Type::TypeVariable(_) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_) | Type::Function(_, _, _, _) | Type::Reference(..) | Type::Forall(_, _) @@ -1317,7 +1328,7 @@ impl Type { | Type::Unit | Type::Constant(_, _) | Type::TypeVariable(_) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_) | Type::InfixExpr(..) | Type::Error => true, @@ -1370,7 +1381,7 @@ impl Type { | Type::InfixExpr(..) | Type::Error => true, - Type::TypeVariable(type_var) | Type::NamedGeneric(type_var, _) => { + Type::TypeVariable(type_var) | Type::NamedGeneric(NamedGeneric { type_var, .. }) => { if let TypeBinding::Bound(typ) = &*type_var.borrow() { typ.is_valid_for_unconstrained_boundary() } else { @@ -1415,7 +1426,8 @@ impl Type { match self { Type::Forall(generics, _) => generics.len(), Type::CheckedCast { to, .. } => to.generic_count(), - Type::TypeVariable(type_variable) | Type::NamedGeneric(type_variable, _) => { + Type::TypeVariable(type_variable) + | Type::NamedGeneric(NamedGeneric { type_var: type_variable, .. }) => { match &*type_variable.borrow() { TypeBinding::Bound(binding) => binding.generic_count(), TypeBinding::Unbound(_, _) => 0, @@ -1455,7 +1467,7 @@ impl Type { pub(crate) fn kind(&self) -> Kind { match self { Type::CheckedCast { to, .. } => to.kind(), - Type::NamedGeneric(var, _) => var.kind(), + Type::NamedGeneric(NamedGeneric { type_var, .. }) => type_var.kind(), Type::Constant(_, kind) => kind.clone(), Type::TypeVariable(var) => match &*var.borrow() { TypeBinding::Bound(typ) => typ.kind(), @@ -1571,7 +1583,7 @@ impl Type { | Type::Unit | Type::TypeVariable(_) | Type::TraitAsType(..) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_) | Type::Function(_, _, _, _) | Type::Reference(..) | Type::Forall(_, _) @@ -1739,7 +1751,9 @@ impl Type { fn get_inner_type_variable(&self) -> Option<(Shared, Kind)> { match self { Type::TypeVariable(var) => Some((var.1.clone(), var.kind())), - Type::NamedGeneric(var, _) => Some((var.1.clone(), var.kind())), + Type::NamedGeneric(NamedGeneric { type_var, .. }) => { + Some((type_var.1.clone(), type_var.kind())) + } Type::CheckedCast { to, .. } => to.get_inner_type_variable(), _ => None, } @@ -1869,17 +1883,21 @@ impl Type { to.try_unify(other, bindings) } - (NamedGeneric(binding, _), other) | (other, NamedGeneric(binding, _)) - if !binding.borrow().is_unbound() => + (NamedGeneric(types::NamedGeneric { type_var, .. }), other) + | (other, NamedGeneric(types::NamedGeneric { type_var, .. })) + if !type_var.borrow().is_unbound() => { - if let TypeBinding::Bound(link) = &*binding.borrow() { + if let TypeBinding::Bound(link) = &*type_var.borrow() { link.try_unify(other, bindings) } else { unreachable!("If guard ensures binding is bound") } } - (NamedGeneric(binding_a, name_a), NamedGeneric(binding_b, name_b)) => { + ( + NamedGeneric(types::NamedGeneric { type_var: binding_a, name: name_a, .. }), + NamedGeneric(types::NamedGeneric { type_var: binding_b, name: name_b, .. }), + ) => { // Bound NamedGenerics are caught by the check above assert!(binding_a.borrow().is_unbound()); assert!(binding_b.borrow().is_unbound()); @@ -2337,7 +2355,9 @@ impl Type { fn type_variable_id(&self) -> Option { match self { - Type::TypeVariable(variable) | Type::NamedGeneric(variable, _) => Some(variable.0), + Type::TypeVariable(type_var) | Type::NamedGeneric(NamedGeneric { type_var, .. }) => { + Some(type_var.0) + } _ => None, } } @@ -2438,8 +2458,8 @@ impl Type { let to = to.substitute_helper(type_bindings, substitute_bound_typevars); Type::CheckedCast { from: Box::new(from), to: Box::new(to) } } - Type::NamedGeneric(binding, _) | Type::TypeVariable(binding) => { - substitute_binding(binding) + Type::NamedGeneric(NamedGeneric { type_var, .. }) | Type::TypeVariable(type_var) => { + substitute_binding(type_var) } // Do not substitute_helper fields, it can lead to infinite recursion @@ -2530,7 +2550,7 @@ impl Type { } Type::Tuple(fields) => fields.iter().any(|field| field.occurs(target_id)), Type::CheckedCast { from, to } => from.occurs(target_id) || to.occurs(target_id), - Type::NamedGeneric(type_var, _) | Type::TypeVariable(type_var) => { + Type::NamedGeneric(NamedGeneric { type_var, .. }) | Type::TypeVariable(type_var) => { match &*type_var.borrow() { TypeBinding::Bound(binding) => { type_var.id() == target_id || binding.occurs(target_id) @@ -2593,7 +2613,7 @@ impl Type { let to = Box::new(to.follow_bindings()); CheckedCast { from, to } } - TypeVariable(var) | NamedGeneric(var, _) => { + TypeVariable(var) | NamedGeneric(types::NamedGeneric { type_var: var, .. }) => { if let TypeBinding::Bound(typ) = &*var.borrow() { return typ.follow_bindings(); } @@ -2635,7 +2655,7 @@ impl Type { /// fields or arguments of this type. pub fn follow_bindings_shallow(&self) -> Cow { match self { - Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { + Type::TypeVariable(var) | Type::NamedGeneric(NamedGeneric { type_var: var, .. }) => { if let TypeBinding::Bound(typ) = &*var.borrow() { return Cow::Owned(typ.follow_bindings_shallow().into_owned()); } @@ -2712,8 +2732,8 @@ impl Type { from.replace_named_generics_with_type_variables(); to.replace_named_generics_with_type_variables(); } - Type::NamedGeneric(var, _) => { - let type_binding = var.borrow(); + Type::NamedGeneric(NamedGeneric { type_var, .. }) => { + let type_binding = type_var.borrow(); if let TypeBinding::Bound(binding) = &*type_binding { let mut binding = binding.clone(); drop(type_binding); @@ -2721,7 +2741,7 @@ impl Type { *self = binding; } else { drop(type_binding); - *self = Type::TypeVariable(var.clone()); + *self = Type::TypeVariable(type_var.clone()); } } Type::Function(args, ret, env, _unconstrained) => { @@ -2771,7 +2791,7 @@ impl Type { } Type::Alias(alias, args) => alias.borrow().get_type(args).integral_maximum_size(), Type::CheckedCast { to, .. } => to.integral_maximum_size(), - Type::NamedGeneric(binding, _name) => match &*binding.borrow() { + Type::NamedGeneric(NamedGeneric { type_var, .. }) => match &*type_var.borrow() { TypeBinding::Bound(typ) => typ.integral_maximum_size(), TypeBinding::Unbound(_, kind) => kind.integral_maximum_size(), }, @@ -3069,12 +3089,12 @@ impl std::fmt::Debug for Type { Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), Type::CheckedCast { to, .. } => write!(f, "{:?}", to), - Type::NamedGeneric(binding, name) => match binding.kind() { + Type::NamedGeneric(NamedGeneric { type_var, name, .. }) => match type_var.kind() { Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { - write!(f, "{}{:?}", name, binding) + write!(f, "{}{:?}", name, type_var) } Kind::Numeric(typ) => { - write!(f, "({} : {}){:?}", name, typ, binding) + write!(f, "({} : {}){:?}", name, typ, type_var) } }, Type::Constant(x, kind) => write!(f, "({}: {})", x, kind), @@ -3170,7 +3190,16 @@ impl std::hash::Hash for Type { alias.hash(state); args.hash(state); } - Type::TypeVariable(var) | Type::NamedGeneric(var, ..) => var.hash(state), + Type::NamedGeneric(NamedGeneric { type_var, implicit: true, .. }) => { + // An implicitly added unbound named generic's hash must be the same as any other + // implicitly added unbound named generic's hash. + if !type_var.borrow().is_unbound() { + type_var.hash(state); + } + } + Type::TypeVariable(var) | Type::NamedGeneric(NamedGeneric { type_var: var, .. }) => { + var.hash(state); + } Type::TraitAsType(trait_id, _, args) => { trait_id.hash(state); args.hash(state); @@ -3266,14 +3295,22 @@ impl PartialEq for Type { (InfixExpr(l_lhs, l_op, l_rhs, _), InfixExpr(r_lhs, r_op, r_rhs, _)) => { l_lhs == r_lhs && l_op == r_op && l_rhs == r_rhs } + // Two implicitly added unbound named generics are equal + ( + NamedGeneric(types::NamedGeneric { type_var: lhs_var, implicit: true, .. }), + NamedGeneric(types::NamedGeneric { type_var: rhs_var, implicit: true, .. }), + ) => { + lhs_var.borrow().is_unbound() && rhs_var.borrow().is_unbound() + || lhs_var.id() == rhs_var.id() + } // Special case: we consider unbound named generics and type variables to be equal to each // other if their type variable ids match. This is important for some corner cases in // monomorphization where we call `replace_named_generics_with_type_variables` but // still want them to be equal for canonicalization checks in arithmetic generics. // Without this we'd fail the `serialize` test. ( - NamedGeneric(lhs_var, _) | TypeVariable(lhs_var), - NamedGeneric(rhs_var, _) | TypeVariable(rhs_var), + NamedGeneric(types::NamedGeneric { type_var: lhs_var, .. }) | TypeVariable(lhs_var), + NamedGeneric(types::NamedGeneric { type_var: rhs_var, .. }) | TypeVariable(rhs_var), ) => lhs_var.id() == rhs_var.id(), _ => false, } diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 7e9f12bfd86..04142bf89df 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -380,17 +380,21 @@ impl Type { mod tests { use acvm::{AcirField, FieldElement}; - use crate::hir_def::types::{BinaryTypeOperator, Kind, Type, TypeVariable, TypeVariableId}; + use crate::{ + NamedGeneric, + hir_def::types::{BinaryTypeOperator, Kind, Type, TypeVariable, TypeVariableId}, + }; #[test] fn solves_n_minus_one_plus_one_through_checked_casts() { // We want to test that the inclusion of a `CheckedCast` won't prevent us from canonicalizing // the expression `(N - 1) + 1` to `N` if there exists a `CheckedCast` on the `N - 1` term. - let n = Type::NamedGeneric( - TypeVariable::unbound(TypeVariableId(0), Kind::u32()), - std::rc::Rc::new("N".to_owned()), - ); + let n = Type::NamedGeneric(NamedGeneric { + type_var: TypeVariable::unbound(TypeVariableId(0), Kind::u32()), + name: std::rc::Rc::new("N".to_owned()), + implicit: false, + }); let n_minus_one = Type::infix_expr( Box::new(n.clone()), BinaryTypeOperator::Subtraction, diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 2b7f69336ea..39175b2de30 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -15,7 +15,7 @@ use crate::node_interner::{ExprId, GlobalValue, ImplSearchErrorKind}; use crate::shared::{Signedness, Visibility}; use crate::signed_field::SignedField; use crate::token::FmtStrFragment; -use crate::{DataType, Shared, TypeVariableId}; +use crate::{DataType, NamedGeneric, Shared, TypeVariableId}; use crate::{ Kind, Type, TypeBinding, TypeBindings, debug::DebugInstrumenter, @@ -1215,15 +1215,15 @@ impl<'interner> Monomorphizer<'interner> { HirType::TraitAsType(..) => { unreachable!("All TraitAsType should be replaced before calling convert_type"); } - HirType::NamedGeneric(binding, _) => { - if let TypeBinding::Bound(binding) = &*binding.borrow() { + HirType::NamedGeneric(NamedGeneric { type_var, .. }) => { + if let TypeBinding::Bound(binding) = &*type_var.borrow() { return Self::convert_type_helper(binding, location, seen_types); } // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - binding.bind(HirType::default_int_or_field_type()); + type_var.bind(HirType::default_int_or_field_type()); ast::Type::Field } @@ -1467,8 +1467,8 @@ impl<'interner> Monomorphizer<'interner> { HirType::FmtString(_size, fields) => Self::check_type(fields.as_ref(), location), HirType::Array(_length, element) => Self::check_type(element.as_ref(), location), HirType::Slice(element) => Self::check_type(element.as_ref(), location), - HirType::NamedGeneric(binding, _) => { - if let TypeBinding::Bound(binding) = &*binding.borrow() { + HirType::NamedGeneric(NamedGeneric { type_var, .. }) => { + if let TypeBinding::Bound(binding) = &*type_var.borrow() { return Self::check_type(binding, location); } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index d644518bdcb..865c5bd28ba 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -2503,7 +2503,7 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Unit => Some(Unit), Type::Tuple(_) => Some(Tuple), Type::Function(_, _, _, _) => Some(Function), - Type::NamedGeneric(_, _) => Some(Generic), + Type::NamedGeneric(_) => Some(Generic), Type::Quoted(quoted) => Some(Quoted(*quoted)), Type::Reference(element, _) => get_type_method_key(element), Type::Alias(alias, _) => get_type_method_key(&alias.borrow().typ), diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index c2d658b72c7..5af5df8bcd3 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -1495,6 +1495,56 @@ fn returns_self_in_trait_method_3() { assert_no_errors!(src); } +#[named] +#[test] +fn trait_impl_with_where_clause_with_trait_with_associated_numeric() { + let src = " + trait Bar { + let N: Field; + } + + impl Bar for Field { + let N: Field = 42; + } + + trait Foo { + fn foo(b: B) where B: Bar; + } + + impl Foo for Field{ + fn foo(_: B) where B: Bar {} + } + + fn main() {} + "; + assert_no_errors!(src); +} + +#[named] +#[test] +fn trait_impl_with_where_clause_with_trait_with_associated_type() { + let src = " + trait Bar { + type typ; + } + + impl Bar for Field { + type typ = Field; + } + + trait Foo { + fn foo(b: B) where B: Bar; + } + + impl Foo for Field{ + fn foo(_: B) where B: Bar {} + } + + fn main() {} + "; + assert_no_errors!(src); +} + #[named] #[test] fn errors_if_constrained_trait_definition_has_unconstrained_impl() { diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/Nargo.toml new file mode 100644 index 00000000000..c39ff3a42d7 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/src/main.nr new file mode 100644 index 00000000000..c3af9704026 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/src/main.nr @@ -0,0 +1,19 @@ + + trait Bar { + let N: Field; + } + + impl Bar for Field { + let N: Field = 42; + } + + trait Foo { + fn foo(b: B) where B: Bar; + } + + impl Foo for Field{ + fn foo(_: B) where B: Bar {} + } + + fn main() {} + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/src_hash.txt new file mode 100644 index 00000000000..9cb186db11f --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/src_hash.txt @@ -0,0 +1 @@ +3535248747414696897 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type/Nargo.toml new file mode 100644 index 00000000000..30b7eaf9683 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type/src/main.nr new file mode 100644 index 00000000000..09c8ed5c082 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type/src/main.nr @@ -0,0 +1,19 @@ + + trait Bar { + type typ; + } + + impl Bar for Field { + type typ = Field; + } + + trait Foo { + fn foo(b: B) where B: Bar; + } + + impl Foo for Field{ + fn foo(_: B) where B: Bar {} + } + + fn main() {} + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type/src_hash.txt new file mode 100644 index 00000000000..66a035510ee --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type/src_hash.txt @@ -0,0 +1 @@ +9758927565621756882 \ No newline at end of file diff --git a/tooling/lsp/src/requests/completion.rs b/tooling/lsp/src/requests/completion.rs index 6fb32298782..7fdd8212eed 100644 --- a/tooling/lsp/src/requests/completion.rs +++ b/tooling/lsp/src/requests/completion.rs @@ -17,7 +17,7 @@ use fm::{FileId, FileMap, PathString}; use kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}; use noirc_errors::{Location, Span}; use noirc_frontend::{ - DataType, ParsedModule, Type, TypeBinding, + DataType, NamedGeneric, ParsedModule, Type, TypeBinding, ast::{ AsTraitPath, AttributeTarget, BlockExpression, CallExpression, ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, GenericTypeArgs, Ident, IfExpression, @@ -608,7 +608,7 @@ impl<'a> NodeFinder<'a> { Type::Tuple(types) => { self.complete_tuple_fields(types, self_prefix); } - Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { + Type::TypeVariable(var) | Type::NamedGeneric(NamedGeneric { type_var: var, .. }) => { if let TypeBinding::Bound(typ) = &*var.borrow() { return self.complete_type_fields_and_methods( typ, @@ -1911,10 +1911,12 @@ fn get_field_type(typ: &Type, name: &str) -> Option { } } Type::Alias(alias_type, generics) => Some(alias_type.borrow().get_type(generics)), - Type::TypeVariable(var) | Type::NamedGeneric(var, _) => match &*var.borrow() { - TypeBinding::Bound(typ) => get_field_type(typ, name), - _ => None, - }, + Type::TypeVariable(var) | Type::NamedGeneric(NamedGeneric { type_var: var, .. }) => { + match &*var.borrow() { + TypeBinding::Bound(typ) => get_field_type(typ, name), + _ => None, + } + } _ => None, } } @@ -1926,10 +1928,12 @@ fn get_array_element_type(typ: Type) -> Option { let typ = alias_type.borrow().get_type(&generics); get_array_element_type(typ) } - Type::TypeVariable(var) | Type::NamedGeneric(var, _) => match &*var.borrow() { - TypeBinding::Bound(typ) => get_array_element_type(typ.clone()), - _ => None, - }, + Type::TypeVariable(var) | Type::NamedGeneric(NamedGeneric { type_var: var, .. }) => { + match &*var.borrow() { + TypeBinding::Bound(typ) => get_array_element_type(typ.clone()), + _ => None, + } + } _ => None, } } diff --git a/tooling/lsp/src/requests/hover/from_reference.rs b/tooling/lsp/src/requests/hover/from_reference.rs index 7c5019a5ba8..ec33d2bb894 100644 --- a/tooling/lsp/src/requests/hover/from_reference.rs +++ b/tooling/lsp/src/requests/hover/from_reference.rs @@ -1,6 +1,7 @@ use async_lsp::lsp_types; use async_lsp::lsp_types::{Hover, HoverContents, MarkupContent, MarkupKind, Position}; use fm::{FileId, FileMap}; +use noirc_frontend::NamedGeneric; use noirc_frontend::hir::comptime::Value; use noirc_frontend::node_interner::GlobalValue; use noirc_frontend::shared::Visibility; @@ -337,14 +338,17 @@ fn format_function(id: FuncId, args: &ProcessRequestCallbackArgs) -> String { let trait_impl = trait_impl.borrow(); let trait_ = args.interner.get_trait(trait_impl.trait_id); - let generics: Vec<_> = - trait_impl - .trait_generics - .iter() - .filter_map(|generic| { - if let Type::NamedGeneric(_, name) = generic { Some(name) } else { None } - }) - .collect(); + let generics = trait_impl + .trait_generics + .iter() + .filter_map(|generic| { + if let Type::NamedGeneric(generic) = generic { + Some(generic.name.as_str()) + } else { + None + } + }) + .collect::>(); string.push('\n'); string.push_str(" impl"); @@ -751,8 +755,8 @@ impl TypeLinksGatherer<'_> { self.gather_type_links(&named_type.typ); } } - Type::NamedGeneric(var, _) => { - self.gather_type_variable_links(var); + Type::NamedGeneric(NamedGeneric { type_var, .. }) => { + self.gather_type_variable_links(type_var); } Type::Function(args, return_type, env, _) => { for arg in args { diff --git a/tooling/lsp/src/trait_impl_method_stub_generator.rs b/tooling/lsp/src/trait_impl_method_stub_generator.rs index 9fdf2b9ccea..e09aee79530 100644 --- a/tooling/lsp/src/trait_impl_method_stub_generator.rs +++ b/tooling/lsp/src/trait_impl_method_stub_generator.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use noirc_frontend::{ - Kind, ResolvedGeneric, Type, + Kind, NamedGeneric, ResolvedGeneric, Type, ast::{NoirTraitImpl, UnresolvedTypeData}, graph::CrateId, hir::{ @@ -324,8 +324,8 @@ impl<'a> TraitImplMethodStubGenerator<'a> { self.string.push_str("error"); } - Type::NamedGeneric(typevar, _name) => { - self.append_type(&Type::TypeVariable(typevar.clone())); + Type::NamedGeneric(NamedGeneric { type_var, .. }) => { + self.append_type(&Type::TypeVariable(type_var.clone())); } Type::Function(args, ret, env, unconstrained) => { if *unconstrained {