diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 9b695eb3e59..285431d2040 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::fmt::Display; use crate::token::{Attributes, Token}; @@ -688,10 +689,12 @@ impl Display for FunctionDefinition { } impl FunctionReturnType { - pub fn get_type(&self) -> &UnresolvedTypeData { + pub fn get_type(&self) -> Cow { match self { - FunctionReturnType::Default(_span) => &UnresolvedTypeData::Unit, - FunctionReturnType::Ty(typ) => &typ.typ, + FunctionReturnType::Default(span) => { + Cow::Owned(UnresolvedType { typ: UnresolvedTypeData::Unit, span: Some(*span) }) + } + FunctionReturnType::Ty(typ) => Cow::Borrowed(typ), } } } diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 11321d673a7..407dafc0e38 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -9,13 +9,14 @@ use crate::hir::resolution::{ import::{resolve_imports, ImportDirective}, path_resolver::StandardPathResolver, }; -use crate::hir::type_check::{type_check_func, TypeChecker}; +use crate::hir::type_check::{type_check_func, TypeCheckError, TypeChecker}; use crate::hir::Context; +use crate::hir_def::traits::{TraitConstant, TraitFunction, TraitType}; use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TraitId, TypeAliasId}; use crate::{ - ExpressionKind, FunctionReturnType, Generics, Ident, LetStatement, Literal, NoirFunction, - NoirStruct, NoirTrait, NoirTypeAlias, ParsedModule, Shared, StructType, TraitItem, - TraitItemType, Type, TypeBinding, UnresolvedGenerics, UnresolvedType, + ExpressionKind, Generics, Ident, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait, + NoirTypeAlias, ParsedModule, Shared, StructType, TraitItem, Type, TypeBinding, + TypeVariableKind, UnresolvedGenerics, UnresolvedType, }; use fm::FileId; use iter_extended::vecmap; @@ -244,8 +245,8 @@ impl DefCollector { // Type check all of the functions in the crate type_check_functions(&mut context.def_interner, file_func_ids, errors); - type_check_functions(&mut context.def_interner, file_trait_impls_ids, errors); type_check_functions(&mut context.def_interner, file_method_ids, errors); + type_check_functions(&mut context.def_interner, file_trait_impls_ids, errors); } } @@ -458,7 +459,7 @@ fn resolve_trait_types( _crate_id: CrateId, _unresolved_trait: &UnresolvedTrait, _errors: &mut [FileDiagnostic], -) -> Vec { +) -> Vec { // TODO vec![] } @@ -467,17 +468,18 @@ fn resolve_trait_constants( _crate_id: CrateId, _unresolved_trait: &UnresolvedTrait, _errors: &mut [FileDiagnostic], -) -> Vec { +) -> Vec { // TODO vec![] } fn resolve_trait_methods( context: &mut Context, + trait_id: TraitId, crate_id: CrateId, unresolved_trait: &UnresolvedTrait, errors: &mut Vec, -) -> Vec { +) -> Vec { let interner = &mut context.def_interner; let def_maps = &mut context.def_maps; @@ -499,19 +501,23 @@ fn resolve_trait_methods( body: _, } = item { + let the_trait = interner.get_trait(trait_id); + let self_type = Type::TypeVariable( + the_trait.borrow().self_type_typevar.clone(), + TypeVariableKind::Normal, + ); + let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); + resolver.set_self_type(Some(self_type)); + let arguments = vecmap(parameters, |param| resolver.resolve_type(param.1.clone())); - let resolved_return_type = match return_type { - FunctionReturnType::Default(_) => None, - FunctionReturnType::Ty(unresolved_type) => { - Some(resolver.resolve_type(unresolved_type.clone())) - } - }; + let resolved_return_type = resolver.resolve_type(return_type.get_type().into_owned()); + let name = name.clone(); // TODO let generics: Generics = vec![]; let span: Span = name.span(); - let f = TraitItemType::Function { + let f = TraitFunction { name, generics, arguments, @@ -552,16 +558,16 @@ fn resolve_traits( context.def_interner.push_empty_trait(*trait_id, unresolved_trait); } for (trait_id, unresolved_trait) in traits { - let mut items: Vec = vec![]; // Resolve order - // 1. Trait Types ( Trait contants can have a trait type, therefore types before constants) - items.append(&mut resolve_trait_types(context, crate_id, &unresolved_trait, errors)); - // 2. Trait Constants ( Trait's methods can use trait types & constants, threfore they should be after) - items.append(&mut resolve_trait_constants(context, crate_id, &unresolved_trait, errors)); + // 1. Trait Types ( Trait constants can have a trait type, therefore types before constants) + let _ = resolve_trait_types(context, crate_id, &unresolved_trait, errors); + // 2. Trait Constants ( Trait's methods can use trait types & constants, therefore they should be after) + let _ = resolve_trait_constants(context, crate_id, &unresolved_trait, errors); // 3. Trait Methods - items.append(&mut resolve_trait_methods(context, crate_id, &unresolved_trait, errors)); + let methods = resolve_trait_methods(context, trait_id, crate_id, &unresolved_trait, errors); + context.def_interner.update_trait(trait_id, |trait_def| { - trait_def.set_items(items); + trait_def.set_methods(methods); }); } } @@ -690,6 +696,12 @@ fn resolve_trait_impls( errors, ); + let mut new_resolver = + Resolver::new(interner, &path_resolver, &context.def_maps, trait_impl.file_id); + new_resolver.set_self_type(Some(self_type.clone())); + + check_methods_signatures(&mut new_resolver, &impl_methods, trait_id, errors); + let trait_definition_ident = &trait_impl.trait_impl_ident; let key = (self_type.clone(), trait_id); if let Some(prev_trait_impl_ident) = interner.get_previous_trait_implementation(&key) { @@ -709,6 +721,88 @@ fn resolve_trait_impls( methods } + +// TODO(vitkov): Move this out of here and into type_check +fn check_methods_signatures( + resolver: &mut Resolver, + impl_methods: &Vec<(FileId, FuncId)>, + trait_id: TraitId, + errors: &mut Vec, +) { + let the_trait_shared = resolver.interner.get_trait(trait_id); + let the_trait = the_trait_shared.borrow(); + + let self_type = resolver.get_self_type().expect("trait impl must have a Self type"); + + // Temporarily bind the trait's Self type to self_type so we can type check + let _ = the_trait.self_type_typevar.borrow_mut().bind_to(self_type.clone(), the_trait.span); + + for (file_id, func_id) in impl_methods { + let meta = resolver.interner.function_meta(func_id); + let func_name = resolver.interner.function_name(func_id).to_owned(); + + let mut typecheck_errors = Vec::new(); + + // `method` is None in the case where the impl block has a method that's not part of the trait. + // If that's the case, a `MethodNotInTrait` error has already been thrown, and we can ignore + // the impl method, since there's nothing in the trait to match its signature against. + if let Some(method) = + the_trait.methods.iter().find(|method| method.name.0.contents == func_name) + { + let function_typ = meta.typ.instantiate(resolver.interner); + + if let Type::Function(params, _, _) = function_typ.0 { + if method.arguments.len() == params.len() { + // Check the parameters of the impl method against the parameters of the trait method + for (parameter_index, ((expected, actual), (hir_pattern, _, _))) in + method.arguments.iter().zip(¶ms).zip(&meta.parameters.0).enumerate() + { + expected.unify(actual, &mut typecheck_errors, || { + TypeCheckError::TraitMethodParameterTypeMismatch { + method_name: func_name.to_string(), + expected_typ: expected.to_string(), + actual_typ: actual.to_string(), + parameter_span: hir_pattern.span(), + parameter_index: parameter_index + 1, + } + }); + } + } else { + errors.push( + DefCollectorErrorKind::MismatchTraitImplementationNumParameters { + actual_num_parameters: meta.parameters.0.len(), + expected_num_parameters: method.arguments.len(), + trait_name: the_trait.name.to_string(), + method_name: func_name.to_string(), + span: meta.location.span, + } + .into_file_diagnostic(*file_id), + ); + } + } + + // Check that impl method return type matches trait return type: + let resolved_return_type = + resolver.resolve_type(meta.return_type.get_type().into_owned()); + + method.return_type.unify(&resolved_return_type, &mut typecheck_errors, || { + let ret_type_span = + meta.return_type.get_type().span.expect("return type must always have a span"); + + TypeCheckError::TypeMismatch { + expected_typ: method.return_type.to_string(), + expr_typ: meta.return_type().to_string(), + expr_span: ret_type_span, + } + }); + + extend_errors(errors, *file_id, typecheck_errors); + } + } + + the_trait.self_type_typevar.borrow_mut().unbind(the_trait.self_type_typevar_id); +} + fn resolve_free_functions( interner: &mut NodeInterner, crate_id: CrateId, 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 813c222319e..a72e30ea97e 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use fm::FileId; use noirc_errors::{FileDiagnostic, Location}; @@ -212,6 +214,9 @@ impl<'a> ModCollector<'a> { } } + // set of function ids that have a corresponding method in the trait + let mut func_ids_in_trait = HashSet::new(); + for item in &trait_def.items { // TODO(Maddiaa): Investigate trait implementations with attributes see: https://github.com/noir-lang/noir/issues/2629 if let TraitItem::Function { @@ -223,13 +228,19 @@ impl<'a> ModCollector<'a> { body, } = item { - let is_implemented = unresolved_functions + // List of functions in the impl block with the same name as the method + // `matching_fns.len() == 0` => missing method impl + // `matching_fns.len() > 1` => duplicate definition (collect_functions will throw a Duplicate error) + let matching_fns: Vec<_> = unresolved_functions .functions .iter() - .any(|(_, _, func_impl)| func_impl.name() == name.0.contents); - if !is_implemented { + .filter(|(_, _, func_impl)| func_impl.name() == name.0.contents) + .collect(); + + if matching_fns.is_empty() { match body { Some(body) => { + // if there's a default implementation for the method, use it let method_name = name.0.contents.clone(); let func_id = context.def_interner.push_empty_fn(); context.def_interner.push_function_definition(method_name, func_id); @@ -241,10 +252,11 @@ impl<'a> ModCollector<'a> { where_clause, return_type, )); + func_ids_in_trait.insert(func_id); unresolved_functions.push_fn(self.module_id, func_id, impl_method); } None => { - let error = DefCollectorErrorKind::TraitMissedMethodImplementation { + let error = DefCollectorErrorKind::TraitMissingMethod { trait_name: trait_def.name.clone(), method_name: name.clone(), trait_impl_span: trait_impl.object_type_span, @@ -252,9 +264,26 @@ impl<'a> ModCollector<'a> { errors.push(error.into_file_diagnostic(self.file_id)); } } + } else { + for (_, func_id, _) in &matching_fns { + func_ids_in_trait.insert(*func_id); + } } } } + + // Emit MethodNotInTrait error for methods in the impl block that + // don't have a corresponding method signature defined in the trait + for (_, func_id, func) in &unresolved_functions.functions { + if !func_ids_in_trait.contains(func_id) { + let error = DefCollectorErrorKind::MethodNotInTrait { + trait_name: trait_def.name.clone(), + impl_method: func.name_ident().clone(), + }; + errors.push(error.into_file_diagnostic(self.file_id)); + } + } + unresolved_functions } diff --git a/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/compiler/noirc_frontend/src/hir/def_collector/errors.rs index ec5de088574..afd0599ef4e 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/errors.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/errors.rs @@ -1,6 +1,5 @@ use crate::hir::resolution::import::PathResolutionError; use crate::Ident; -use crate::UnresolvedType; use noirc_errors::CustomDiagnostic as Diagnostic; use noirc_errors::FileDiagnostic; @@ -34,21 +33,13 @@ pub enum DefCollectorErrorKind { NonStructTraitImpl { trait_ident: Ident, span: Span }, #[error("Cannot `impl` a type defined outside the current crate")] ForeignImpl { span: Span, type_name: String }, - #[error("Mismatch signature of trait")] - MismatchTraitImlementationParameter { - trait_name: String, - impl_method: String, - parameter: Ident, - expected_type: UnresolvedType, - }, - #[error("Mismatch return type of trait implementation")] - MismatchTraitImplementationReturnType { trait_name: String, impl_ident: Ident }, #[error("Mismatch number of parameters in of trait implementation")] MismatchTraitImplementationNumParameters { actual_num_parameters: usize, expected_num_parameters: usize, trait_name: String, - impl_ident: Ident, + method_name: String, + span: Span, }, #[error("Method is not defined in trait")] MethodNotInTrait { trait_name: Ident, impl_method: Ident }, @@ -57,7 +48,7 @@ pub enum DefCollectorErrorKind { #[error("Trait not found")] TraitNotFound { trait_ident: Ident }, #[error("Missing Trait method implementation")] - TraitMissedMethodImplementation { trait_name: Ident, method_name: Ident, trait_impl_span: Span }, + TraitMissingMethod { trait_name: Ident, method_name: Ident, trait_impl_span: Span }, } impl DefCollectorErrorKind { @@ -133,51 +124,25 @@ impl From for Diagnostic { "".to_string(), trait_ident.span(), ), - DefCollectorErrorKind::MismatchTraitImplementationReturnType { - trait_name, - impl_ident, - } => { - let span = impl_ident.span(); - let method_name = impl_ident.0.contents; - Diagnostic::simple_error( - format!("Mismatch return type of method with name {method_name} that implements trait {trait_name}"), - "".to_string(), - span, - ) - } DefCollectorErrorKind::MismatchTraitImplementationNumParameters { expected_num_parameters, actual_num_parameters, trait_name, - impl_ident, - } => { - let method_name = impl_ident.0.contents.clone(); - let primary_message = format!( - "Mismatch - expected {expected_num_parameters} arguments, but got {actual_num_parameters} of trait `{trait_name}` implementation `{method_name}`"); - Diagnostic::simple_error(primary_message, "".to_string(), impl_ident.span()) - } - DefCollectorErrorKind::MismatchTraitImlementationParameter { - trait_name, - impl_method, - parameter, - expected_type, + method_name, + span, } => { let primary_message = format!( - "Mismatch signature of method {impl_method} that implements trait {trait_name}" - ); - let secondary_message = - format!("`{}: {}` expected", parameter.0.contents, expected_type,); - let span = parameter.span(); - Diagnostic::simple_error(primary_message, secondary_message, span) + "Method `{method_name}` of trait `{trait_name}` needs {expected_num_parameters} parameters, but has {actual_num_parameters}"); + Diagnostic::simple_error(primary_message, "".to_string(), span) } DefCollectorErrorKind::MethodNotInTrait { trait_name, impl_method } => { let trait_name = trait_name.0.contents; let impl_method_span = impl_method.span(); let impl_method_name = impl_method.0.contents; - let primary_message = format!("method with name {impl_method_name} is not part of trait {trait_name}, therefore it can't be implemented"); + let primary_message = format!("Method with name `{impl_method_name}` is not part of trait `{trait_name}`, therefore it can't be implemented"); Diagnostic::simple_error(primary_message, "".to_owned(), impl_method_span) } - DefCollectorErrorKind::TraitMissedMethodImplementation { + DefCollectorErrorKind::TraitMissingMethod { trait_name, method_name, trait_impl_span, @@ -185,7 +150,7 @@ impl From for Diagnostic { let trait_name = trait_name.0.contents; let impl_method_name = method_name.0.contents; let primary_message = format!( - "method `{impl_method_name}` from trait `{trait_name}` is not implemented" + "Method `{impl_method_name}` from trait `{trait_name}` is not implemented" ); Diagnostic::simple_error( primary_message, diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 411e91f2cf4..ddc2d38b2f7 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -17,6 +17,7 @@ use crate::hir_def::expr::{ HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirLiteral, HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, }; +use crate::hir_def::traits::Trait; use crate::token::PrimaryAttribute; use regex::Regex; use std::collections::{BTreeMap, HashSet}; @@ -35,9 +36,9 @@ use crate::{ }; use crate::{ ArrayLiteral, ContractFunctionType, Distinctness, Generics, LValue, NoirStruct, NoirTypeAlias, - Path, Pattern, Shared, StructType, Trait, Type, TypeAliasType, TypeBinding, TypeVariable, - UnaryOp, UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, - Visibility, ERROR_IDENT, + Path, Pattern, Shared, StructType, Type, TypeAliasType, TypeBinding, TypeVariable, UnaryOp, + UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, Visibility, + ERROR_IDENT, }; use fm::FileId; use iter_extended::vecmap; @@ -76,7 +77,7 @@ pub struct Resolver<'a> { scopes: ScopeForest, path_resolver: &'a dyn PathResolver, def_maps: &'a BTreeMap, - interner: &'a mut NodeInterner, + pub interner: &'a mut NodeInterner, errors: Vec, file: FileId, @@ -127,6 +128,10 @@ impl<'a> Resolver<'a> { self.self_type = self_type; } + pub fn get_self_type(&mut self) -> Option<&Type> { + self.self_type.as_ref() + } + fn push_err(&mut self, err: ResolverError) { self.errors.push(err); } diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 3190c7a24a2..ece3a4c61ef 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -100,6 +100,14 @@ pub enum TypeCheckError { ResolverError(ResolverError), #[error("Unused expression result of type {expr_type}")] UnusedResultError { expr_type: Type, expr_span: Span }, + #[error("Expected type {expected_typ:?} is not the same as {actual_typ:?}")] + TraitMethodParameterTypeMismatch { + method_name: String, + expected_typ: String, + actual_typ: String, + parameter_span: Span, + parameter_index: usize, + }, } impl TypeCheckError { @@ -133,6 +141,13 @@ impl From for Diagnostic { expr_span, ) } + TypeCheckError::TraitMethodParameterTypeMismatch { method_name, expected_typ, actual_typ, parameter_index, parameter_span } => { + Diagnostic::simple_error( + format!("Parameter #{parameter_index} of method `{method_name}` must be of type {expected_typ}, not {actual_typ}"), + String::new(), + parameter_span, + ) + } TypeCheckError::NonHomogeneousArray { first_span, first_type, diff --git a/compiler/noirc_frontend/src/hir_def/mod.rs b/compiler/noirc_frontend/src/hir_def/mod.rs index 3dc2407486d..206fc3ddda5 100644 --- a/compiler/noirc_frontend/src/hir_def/mod.rs +++ b/compiler/noirc_frontend/src/hir_def/mod.rs @@ -18,4 +18,5 @@ pub mod expr; pub mod function; pub mod stmt; +pub mod traits; pub mod types; diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 0dcb7192be2..d7f0d2e466f 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -79,6 +79,15 @@ impl HirPattern { other => panic!("Tried to iterate over the fields of '{other:?}', which has none"), } } + + pub fn span(&self) -> Span { + match self { + HirPattern::Identifier(ident) => ident.location.span, + HirPattern::Mutable(_, span) + | HirPattern::Tuple(_, span) + | HirPattern::Struct(_, _, span) => *span, + } + } } /// Represents an Ast form that can be assigned to. These diff --git a/compiler/noirc_frontend/src/hir_def/traits.rs b/compiler/noirc_frontend/src/hir_def/traits.rs new file mode 100644 index 00000000000..4176e4fc89b --- /dev/null +++ b/compiler/noirc_frontend/src/hir_def/traits.rs @@ -0,0 +1,122 @@ +use crate::{ + node_interner::{FuncId, TraitId}, + Generics, Ident, Type, TypeVariable, TypeVariableId, +}; +use noirc_errors::Span; + +#[derive(Debug, PartialEq, Eq)] +pub struct TraitFunction { + pub name: Ident, + pub generics: Generics, + pub arguments: Vec, + pub return_type: Type, + pub span: Span, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct TraitConstant { + pub name: Ident, + pub ty: Type, + pub span: Span, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct TraitType { + pub name: Ident, + pub ty: Type, + pub span: Span, +} + +/// Represents a trait in the type system. Each instance of this struct +/// will be shared across all Type::Trait variants that represent +/// the same trait. +#[derive(Debug)] +pub struct Trait { + /// A unique id representing this trait type. Used to check if two + /// struct traits are equal. + pub id: TraitId, + + pub methods: Vec, + pub constants: Vec, + pub types: Vec, + + pub name: Ident, + pub generics: Generics, + pub span: Span, + + /// When resolving the types of Trait elements, all references to `Self` resolve + /// to this TypeVariable. Then when we check if the types of trait impl elements + /// match the definition in the trait, we bind this TypeVariable to whatever + /// the correct Self type is for that particular impl block. + pub self_type_typevar_id: TypeVariableId, + pub self_type_typevar: TypeVariable, +} + +pub struct TraitImpl { + pub ident: Ident, + pub typ: Type, + pub trait_id: TraitId, + pub methods: Vec, // methods[i] is the implementation of trait.methods[i] for Type typ +} + +#[derive(Debug, Clone)] +pub struct TraitConstraint { + pub typ: Type, + pub trait_id: Option, + // pub trait_generics: Generics, TODO +} + +impl std::hash::Hash for Trait { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } +} + +impl PartialEq for Trait { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Trait { + pub fn new( + id: TraitId, + name: Ident, + span: Span, + generics: Generics, + self_type_typevar_id: TypeVariableId, + self_type_typevar: TypeVariable, + ) -> Trait { + Trait { + id, + name, + span, + methods: Vec::new(), + constants: Vec::new(), + types: Vec::new(), + generics, + self_type_typevar_id, + self_type_typevar, + } + } + + pub fn set_methods(&mut self, methods: Vec) { + self.methods = methods; + } +} + +impl std::fmt::Display for Trait { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name) + } +} + +impl TraitFunction { + pub fn get_type(&self) -> Type { + Type::Function( + self.arguments.clone(), + Box::new(self.return_type.clone()), + Box::new(Type::Unit), + ) + } +} diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 8372f7a0355..eb837ec5f55 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -12,7 +12,7 @@ use iter_extended::vecmap; use noirc_errors::Span; use noirc_printable_type::PrintableType; -use crate::{node_interner::StructId, node_interner::TraitId, Ident, Signedness}; +use crate::{node_interner::StructId, Ident, Signedness}; use super::expr::{HirCallExpression, HirExpression, HirIdent}; @@ -123,39 +123,6 @@ pub struct StructType { pub span: Span, } -#[derive(Debug, PartialEq, Eq)] -pub enum TraitItemType { - /// A function declaration in a trait. - Function { - name: Ident, - generics: Generics, - arguments: Vec, - return_type: Option, - span: Span, - }, - - /// A constant declaration in a trait. - Constant { name: Ident, ty: Type, span: Span }, - - /// A type declaration in a trait. - Type { name: Ident, ty: Type, span: Span }, -} -/// Represents a trait type in the type system. Each instance of this -/// rust struct will be shared across all Type::Trait variants that represent -/// the same trait type. -#[derive(Debug, Eq)] -pub struct Trait { - /// A unique id representing this trait type. Used to check if two - /// struct traits are equal. - pub id: TraitId, - - pub items: Vec, - - pub name: Ident, - pub generics: Generics, - pub span: Span, -} - /// Corresponds to generic lists such as `` in the source /// program. The `TypeVariableId` portion is used to match two /// type variables to check for equality, while the `TypeVariable` is @@ -174,40 +141,6 @@ impl PartialEq for StructType { } } -impl std::hash::Hash for Trait { - fn hash(&self, state: &mut H) { - self.id.hash(state); - } -} - -impl PartialEq for Trait { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Trait { - pub fn new( - id: TraitId, - name: Ident, - span: Span, - items: Vec, - generics: Generics, - ) -> Trait { - Trait { id, name, span, items, generics } - } - - pub fn set_items(&mut self, items: Vec) { - self.items = items; - } -} - -impl std::fmt::Display for Trait { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.name) - } -} - impl StructType { pub fn new( id: StructId, @@ -462,6 +395,10 @@ impl TypeBinding { } } } + + pub fn unbind(&mut self, id: TypeVariableId) { + *self = TypeBinding::Unbound(id); + } } /// A unique ID used to differentiate different type variables diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index e0e404b48d8..10a007bdbdb 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -13,11 +13,12 @@ use crate::hir::def_collector::dc_crate::{ use crate::hir::def_map::{LocalModuleId, ModuleId}; use crate::hir::StorageSlot; use crate::hir_def::stmt::HirLetStatement; -use crate::hir_def::types::{StructType, Trait, Type}; +use crate::hir_def::types::{StructType, Type}; use crate::hir_def::{ expr::HirExpression, function::{FuncMeta, HirFunction}, stmt::HirStatement, + traits::Trait, }; use crate::token::Attributes; use crate::{ @@ -349,13 +350,15 @@ impl NodeInterner { } pub fn push_empty_trait(&mut self, type_id: TraitId, typ: &UnresolvedTrait) { + let self_type_typevar_id = self.next_type_variable_id(); + let self_type_typevar = Shared::new(TypeBinding::Unbound(self_type_typevar_id)); + self.traits.insert( type_id, Shared::new(Trait::new( type_id, typ.trait_def.name.clone(), typ.trait_def.span, - Vec::new(), vecmap(&typ.trait_def.generics, |_| { // Temporary type variable ids before the trait is resolved to its actual ids. // This lets us record how many arguments the type expects so that other types @@ -364,6 +367,8 @@ impl NodeInterner { let id = TypeVariableId(0); (id, Shared::new(TypeBinding::Unbound(id))) }), + self_type_typevar_id, + self_type_typevar, )), ); } diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_declaration/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dup_trait_declaration/src/main.nr index f4c246c786a..052d7762438 100644 --- a/tooling/nargo_cli/tests/compile_failure/dup_trait_declaration/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/dup_trait_declaration/src/main.nr @@ -21,6 +21,4 @@ trait Default { } fn main(x: Field, y: Field) { - let first = Foo::default(x,y); - assert(first.bar == x); } diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation/src/main.nr index cfc098a6ff7..fd4ebe95519 100644 --- a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation/src/main.nr @@ -1,7 +1,7 @@ use dep::std; trait Default { - fn default(x: Field, y: Field) -> Self; + fn default(x: Field, y: Field) -> Field; } struct Foo { @@ -9,20 +9,18 @@ struct Foo { array: [Field; 2], } -// Duplicate trait implementations should not compile impl Default for Foo { - fn default(x: Field,y: Field) -> Self { - Self { bar: x, array: [x,y] } + // Duplicate trait methods should not compile + fn default(x: Field, y: Field) -> Field { + y + 2 * x } -} - -// Duplicate trait implementations should not compile -impl Default for Foo { - fn default(x: Field, y: Field) -> Self { - Self { bar: y, array: [y,x] } + // Duplicate trait methods should not compile + fn default(x: Field, y: Field) -> Field { + y + 2 * x } } + fn main(x: Field, y: Field) { } diff --git a/tooling/nargo_cli/tests/compile_failure/impl_struct_not_trait/src/main.nr b/tooling/nargo_cli/tests/compile_failure/impl_struct_not_trait/src/main.nr index e25465378b1..50c142e2f5e 100644 --- a/tooling/nargo_cli/tests/compile_failure/impl_struct_not_trait/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/impl_struct_not_trait/src/main.nr @@ -18,6 +18,4 @@ impl Default for Foo { } fn main(x: Field, y: Field) { - let first = Foo::default(x,y); - assert(first.bar == x); } diff --git a/tooling/nargo_cli/tests/compile_failure/impl_trait_for_non_type/Nargo.toml b/tooling/nargo_cli/tests/compile_failure/impl_trait_for_non_type/Nargo.toml new file mode 100644 index 00000000000..35f174bf546 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/impl_trait_for_non_type/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "impl_trait_for_non_type" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] diff --git a/tooling/nargo_cli/tests/compile_failure/impl_trait_for_non_type/Prover.toml b/tooling/nargo_cli/tests/compile_failure/impl_trait_for_non_type/Prover.toml new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/impl_trait_for_non_type/Prover.toml @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/tooling/nargo_cli/tests/compile_failure/impl_trait_for_non_type/src/main.nr b/tooling/nargo_cli/tests/compile_failure/impl_trait_for_non_type/src/main.nr new file mode 100644 index 00000000000..9dce82e94bf --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/impl_trait_for_non_type/src/main.nr @@ -0,0 +1,17 @@ +use dep::std; + +trait Foo { + fn foo() -> Field; +} + + +// This should not compile, as only types should have impl blocks +// TODO(https://github.com/noir-lang/noir/issues/2568): Right now we only allow structs, but arbitrary types should be allowed. +impl Foo for main { + fn foo() -> Field { + x + y + } +} + +fn main(x: Field, y: Field) { +} diff --git a/tooling/nargo_cli/tests/compile_failure/trait_missing_implementation/src/main.nr b/tooling/nargo_cli/tests/compile_failure/trait_missing_implementation/src/main.nr index bc74b328592..1f69d09924b 100644 --- a/tooling/nargo_cli/tests/compile_failure/trait_missing_implementation/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/trait_missing_implementation/src/main.nr @@ -19,6 +19,4 @@ impl Default for Foo { } fn main(x: Field) { - let first = Foo::method2(x); - assert(first == x); } diff --git a/tooling/nargo_cli/tests/compile_failure/trait_not_in_scope/src/main.nr b/tooling/nargo_cli/tests/compile_failure/trait_not_in_scope/src/main.nr index 9dc57ee395f..2f236e622f0 100644 --- a/tooling/nargo_cli/tests/compile_failure/trait_not_in_scope/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/trait_not_in_scope/src/main.nr @@ -13,6 +13,4 @@ impl Default for Foo { } fn main(x: Field, y: Field) { - let first = Foo::default(x,y); - assert(first.bar == x); } diff --git a/tooling/nargo_cli/tests/compile_failure/trait_wrong_method_name/src/main.nr b/tooling/nargo_cli/tests/compile_failure/trait_wrong_method_name/src/main.nr index 0ba10815efa..470bed9b354 100644 --- a/tooling/nargo_cli/tests/compile_failure/trait_wrong_method_name/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/trait_wrong_method_name/src/main.nr @@ -1,7 +1,6 @@ use dep::std; trait Default { - fn default(x: Field, y: Field) -> Self; } struct Foo { @@ -11,12 +10,10 @@ struct Foo { // wrong trait name method should not compile impl Default for Foo { - fn default_wrong_name(x: Field, y: Field) -> Self { + fn doesnt_exist(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } } fn main(x: Field, y: Field) { - let first = Foo::default_wrong_name(x,y); - assert(first.bar == x); } diff --git a/tooling/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/src/main.nr b/tooling/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/src/main.nr index acd930a6d49..23e46430dbc 100644 --- a/tooling/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/src/main.nr @@ -1,21 +1,16 @@ -use dep::std; - trait Default { - fn default(x: Field, y: Field) -> Self; + fn default() -> Self; } struct Foo { - bar: Field, - array: [Field; 2], } +// This should fail to compile as `default()` should return `Foo` impl Default for Foo { - fn default(x: Field, y: Field) -> Field { + fn default() -> Field { x } } -fn main(x: Field, y: Field) { - let first = Foo::default(x,y); - assert(first.bar == x); +fn main() { } diff --git a/tooling/nargo_cli/tests/compile_failure/trait_wrong_parameter/src/main.nr b/tooling/nargo_cli/tests/compile_failure/trait_wrong_parameter/src/main.nr index 2975aa6b1dd..ae7888e010f 100644 --- a/tooling/nargo_cli/tests/compile_failure/trait_wrong_parameter/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/trait_wrong_parameter/src/main.nr @@ -1,21 +1,15 @@ -use dep::std; - -trait Default { - fn default(x: Field, y: Field) -> Self; +trait FromField { + fn default(x: Field) -> Self; } struct Foo { bar: Field, - array: [Field; 2], } impl Default for Foo { - fn default(x: Field, y: Foo) -> Self { - Self { bar: x, array: [x, y.bar] } + fn default(x: u32) -> Self { } } -fn main(x: Field, y: Field) { - let first = Foo::default(x,y); - assert(first.bar == x); +fn main() { } diff --git a/tooling/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/src/main.nr b/tooling/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/src/main.nr index 92469ae8fdb..4d011ddf737 100644 --- a/tooling/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/src/main.nr @@ -16,6 +16,4 @@ impl Default for Foo { } fn main(x: Field, y: Field) { - let first = Foo::default(x,y); - assert(first.bar == x); } diff --git a/tooling/nargo_cli/tests/execution_success/trait_self/Nargo.toml b/tooling/nargo_cli/tests/execution_success/trait_self/Nargo.toml new file mode 100644 index 00000000000..0dfaea44862 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/trait_self/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trait_self" +type = "bin" +authors = [""] +compiler_version = "0.10.5" + +[dependencies] \ No newline at end of file diff --git a/tooling/nargo_cli/tests/execution_success/trait_self/src/main.nr b/tooling/nargo_cli/tests/execution_success/trait_self/src/main.nr new file mode 100644 index 00000000000..c116795a128 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/trait_self/src/main.nr @@ -0,0 +1,27 @@ +trait ATrait { + fn asd() -> Self; +} + +struct Foo { + x: Field +} +impl ATrait for Foo { + fn asd() -> Self { + // This should pass as Self should be bound to Foo while typechecking this + Foo{x: 100} + } +} + +struct Bar { + x: Field +} +impl ATrait for Bar { + // The trait method is declared as returning `Self` + // but explicitly specifying the type in the impl should work + fn asd() -> Bar { + Bar{x: 100} + } +} + +fn main() { +} \ No newline at end of file