From 057d1eaae39a2faba160d8b322fbf121a90541c1 Mon Sep 17 00:00:00 2001 From: Marcos Henrich Date: Tue, 28 Jan 2025 17:43:36 +0000 Subject: [PATCH] Removes TraitMap::insert_for_type. Some use cases require the compiler to call insert_implementation_for_type on every call of find_method_for_type. This would double the compile time of a simple script. To avoid this we removed the TraitMap::insert_for_type. The TraitMap now only stores the original implementations and uses a less restrictive unify_check to get those that match the concrete implementations. This reduces significantly the size of the TraitMap and speeds up the lookup times. On the other hand, it also introduces a type substitution on every find_method_for_type. A future PR will address the performance of doing type substitutions. --- .../src/language/ty/declaration/function.rs | 94 +++ .../ty/expression/expression_variant.rs | 2 - .../ast_node/declaration/abi.rs | 5 +- .../ast_node/declaration/impl_trait.rs | 5 +- .../typed_expression/method_application.rs | 66 +- .../typed_expression/struct_instantiation.rs | 6 +- .../src/semantic_analysis/namespace/mod.rs | 1 - .../src/semantic_analysis/namespace/root.rs | 6 +- .../semantic_analysis/namespace/trait_map.rs | 586 ++++++------------ .../semantic_analysis/type_check_analysis.rs | 2 +- .../semantic_analysis/type_check_context.rs | 240 ++++--- .../ast_elements/type_parameter.rs | 4 +- sway-core/src/type_system/id.rs | 283 +++++++-- .../src/type_system/substitute/subst_map.rs | 4 + .../src/type_system/unify/unify_check.rs | 24 +- .../for_mismatch_pattern_type/test.toml | 7 +- .../should_fail/generic_traits/test.toml | 2 +- .../test.toml | 10 +- .../generics_in_contract/test.toml | 6 +- .../type_alias_shadowing_methods/test.toml | 2 +- .../should_fail/type_propagation/test.toml | 12 +- .../test_programs/should_fail/vec/test.toml | 30 +- 22 files changed, 745 insertions(+), 652 deletions(-) diff --git a/sway-core/src/language/ty/declaration/function.rs b/sway-core/src/language/ty/declaration/function.rs index 6849ccaa7db..bcad51f06a4 100644 --- a/sway-core/src/language/ty/declaration/function.rs +++ b/sway-core/src/language/ty/declaration/function.rs @@ -133,6 +133,81 @@ impl DisplayWithEngines for TyFunctionDecl { } } +impl DeclRefFunction { + /// Makes method with a copy of type_id. + /// This avoids altering the type_id already in the type map. + /// Without this it is possible to retrieve a method from the type map unify its types and + /// the second time it won't be possible to retrieve the same method. + pub fn get_method_safe_to_unify(&self, engines: &Engines, type_id: TypeId) -> Self { + let decl_engine = engines.de(); + + let mut method = (*decl_engine.get_function(self)).clone(); + + if let Some(method_implementing_for_typeid) = method.implementing_for_typeid { + let mut type_id_type_subst_map = TypeSubstMap::new(); + match &method.implementing_type { + Some(TyDecl::ImplSelfOrTrait(t)) => { + let impl_self_or_trait = &*engines.de().get(&t.decl_id); + let mut type_id_type_parameters = vec![]; + type_id.extract_type_parameters( + engines, + 0, + &mut type_id_type_parameters, + impl_self_or_trait.implementing_for.type_id, + ); + + for impl_type_parameter in impl_self_or_trait.impl_type_parameters.clone() { + let matches = type_id_type_parameters + .iter() + .filter(|(_, orig_tp)| { + engines.te().get(orig_tp.type_id).eq( + &*engines.te().get(impl_type_parameter.type_id), + &PartialEqWithEnginesContext::new(engines), + ) + }) + .collect::>(); + if matches.len() > 0 { + type_id_type_subst_map + .insert(impl_type_parameter.type_id, matches[0].0.type_id); + } + if engines + .te() + .get(impl_self_or_trait.implementing_for.initial_type_id) + .eq( + &*engines.te().get(impl_type_parameter.initial_type_id), + &PartialEqWithEnginesContext::new(engines), + ) + { + type_id_type_subst_map.insert(impl_type_parameter.type_id, type_id); + } + } + } + _ => {} + } + + let mut method_type_subst_map = TypeSubstMap::new(); + method_type_subst_map.extend(&type_id_type_subst_map); + method_type_subst_map.insert(method_implementing_for_typeid, type_id); + + method.subst(&SubstTypesContext::new( + engines, + &method_type_subst_map, + true, + )); + + return engines + .de() + .insert( + method.clone(), + engines.de().get_parsed_decl_id(self.id()).as_ref(), + ) + .with_parent(decl_engine, self.id().into()); + } + + self.clone() + } +} + impl Named for TyFunctionDecl { fn name(&self) -> &Ident { &self.name @@ -481,6 +556,25 @@ impl TyFunctionDecl { _ => Some(false), } } + + pub fn is_from_blanket_impl(&self, engines: &Engines) -> bool { + if let Some(TyDecl::ImplSelfOrTrait(existing_impl_trait)) = self.implementing_type.clone() { + let existing_trait_decl = engines + .de() + .get_impl_self_or_trait(&existing_impl_trait.decl_id); + if !existing_trait_decl.impl_type_parameters.is_empty() + && matches!( + *engines + .te() + .get(existing_trait_decl.implementing_for.type_id), + TypeInfo::UnknownGeneric { .. } + ) + { + return true; + } + } + false + } } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/sway-core/src/language/ty/expression/expression_variant.rs b/sway-core/src/language/ty/expression/expression_variant.rs index 1e127d80dca..476a42e7df9 100644 --- a/sway-core/src/language/ty/expression/expression_variant.rs +++ b/sway-core/src/language/ty/expression/expression_variant.rs @@ -3,7 +3,6 @@ use crate::{ engine_threading::*, has_changes, language::{ty::*, *}, - namespace::TryInsertingTraitImplOnFailure, semantic_analysis::{ TyNodeDepGraphEdge, TyNodeDepGraphEdgeInfo, TypeCheckAnalysis, TypeCheckAnalysisContext, TypeCheckContext, TypeCheckFinalization, TypeCheckFinalizationContext, @@ -833,7 +832,6 @@ impl ReplaceDecls for TyExpressionVariant { .map(|a| a.1.return_type) .collect::>(), None, - TryInsertingTraitImplOnFailure::Yes, )?; method = (*decl_engine.get(&implementing_type_method_ref)).clone(); } diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/abi.rs b/sway-core/src/semantic_analysis/ast_node/declaration/abi.rs index fbdbc6d9c3e..8540832821f 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/abi.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/abi.rs @@ -9,7 +9,7 @@ use crate::{ DeclId, }, language::ty::{TyAbiDecl, TyFunctionDecl}, - namespace::{IsExtendingExistingImpl, IsImplSelf, TryInsertingTraitImplOnFailure}, + namespace::{IsExtendingExistingImpl, IsImplSelf}, semantic_analysis::{ symbol_collection_context::SymbolCollectionContext, TypeCheckAnalysis, TypeCheckAnalysisContext, TypeCheckFinalization, TypeCheckFinalizationContext, @@ -113,7 +113,6 @@ impl ty::TyAbiDecl { ctx.type_annotation(), &Default::default(), None, - TryInsertingTraitImplOnFailure::No, ) { let superabi_impl_method = ctx.engines.de().get_function(&superabi_impl_method_ref); @@ -279,7 +278,6 @@ impl ty::TyAbiDecl { ctx.type_annotation(), &Default::default(), None, - TryInsertingTraitImplOnFailure::No, ) { let superabi_method = ctx.engines.de().get_function(&superabi_method_ref); @@ -354,7 +352,6 @@ impl ty::TyAbiDecl { ctx.type_annotation(), &Default::default(), None, - TryInsertingTraitImplOnFailure::No, ) { let superabi_impl_method = ctx.engines.de().get_function(&superabi_impl_method_ref); diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs b/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs index e4b825c2e6b..09441b3c428 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs @@ -22,7 +22,7 @@ use crate::{ }, *, }, - namespace::{IsExtendingExistingImpl, IsImplSelf, TraitMap, TryInsertingTraitImplOnFailure}, + namespace::{IsExtendingExistingImpl, IsImplSelf, TraitMap}, semantic_analysis::{ symbol_collection_context::SymbolCollectionContext, AbiMode, ConstShadowingMode, TyNodeDepGraphNodeId, TypeCheckAnalysis, TypeCheckAnalysisContext, TypeCheckContext, @@ -692,7 +692,6 @@ fn type_check_trait_implementation( let type_engine = ctx.engines.te(); let decl_engine = ctx.engines.de(); let engines = ctx.engines(); - let code_block_first_pass = ctx.code_block_first_pass(); // Check to see if the type that we are implementing for implements the // supertraits of this trait. @@ -709,8 +708,6 @@ fn type_check_trait_implementation( .collect::>(), block_span, engines, - TryInsertingTraitImplOnFailure::Yes, - code_block_first_pass.into(), ) })?; diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index 99d7bd2f578..4baee95dfbb 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -8,7 +8,6 @@ use crate::{ ty::{self, TyDecl, TyExpression, TyFunctionSig}, *, }, - namespace::TryInsertingTraitImplOnFailure, semantic_analysis::*, type_system::*, }; @@ -36,6 +35,7 @@ pub(crate) fn type_check_method_application( let type_engine = ctx.engines.te(); let decl_engine = ctx.engines.de(); let engines = ctx.engines(); + let coercion_check = UnifyCheck::coercion(engines); // type check the function arguments (1st pass) // Some arguments may fail on this first pass because they may require the type_annotation to the parameter type. @@ -120,34 +120,46 @@ pub(crate) fn type_check_method_application( // type check the function arguments (2nd pass) let mut args_buf = VecDeque::new(); for (arg, index, arg_opt) in izip!(arguments.iter(), 0.., args_opt_buf.iter().cloned()) { - if let (Some(arg), false) = arg_opt { - args_buf.push_back(arg); + let param_index = if method.is_contract_call { + if index == 0 { + continue; + } + index - 1 //contract call methods don't have self parameter. } else { - // We type check the argument expression again this time throwing out the error. - let param_index = if method.is_contract_call { - index - 1 //contract call methods don't have self parameter. - } else { - index - }; + index + }; - let ctx = if let Some(param) = method.parameters.get(param_index) { - // We now try to type check it again, this time with the type annotation. - ctx.by_ref() - .with_help_text( - "Function application argument type must match function parameter type.", - ) - .with_type_annotation(param.type_argument.type_id) + if let (Some(arg), false) = arg_opt { + if let Some(param) = method.parameters.get(param_index) { + // If argument type is compcoerces to resolved method parameter type skip second type_check. + if coercion_check.check(arg.return_type, param.type_argument.type_id) { + args_buf.push_back(arg); + continue; + } } else { - ctx.by_ref() - .with_help_text("") - .with_type_annotation(type_engine.new_unknown()) - }; - - args_buf.push_back( - ty::TyExpression::type_check(handler, ctx, arg) - .unwrap_or_else(|err| ty::TyExpression::error(err, span.clone(), engines)), - ); + args_buf.push_back(arg); + continue; + } } + + // We type check the argument expression again this time throwing out the error. + let ctx = if let Some(param) = method.parameters.get(param_index) { + // We now try to type check it again, this time with the type annotation. + ctx.by_ref() + .with_help_text( + "Function application argument type must match function parameter type.", + ) + .with_type_annotation(param.type_argument.type_id) + } else { + ctx.by_ref() + .with_help_text("") + .with_type_annotation(type_engine.new_unknown()) + }; + + args_buf.push_back( + ty::TyExpression::type_check(handler, ctx, arg) + .unwrap_or_else(|err| ty::TyExpression::error(err, span.clone(), engines)), + ); } // check the method visibility @@ -821,7 +833,6 @@ pub(crate) fn resolve_method_name( ctx.type_annotation(), &arguments_types, None, - TryInsertingTraitImplOnFailure::Yes, )?; (decl_ref, type_id) @@ -866,7 +877,6 @@ pub(crate) fn resolve_method_name( ctx.type_annotation(), &arguments_types, None, - TryInsertingTraitImplOnFailure::Yes, )?; (decl_ref, type_id) @@ -890,7 +900,6 @@ pub(crate) fn resolve_method_name( ctx.type_annotation(), &arguments_types, None, - TryInsertingTraitImplOnFailure::Yes, )?; (decl_ref, type_id) @@ -915,7 +924,6 @@ pub(crate) fn resolve_method_name( ctx.type_annotation(), &arguments_types, Some(*as_trait), - TryInsertingTraitImplOnFailure::Yes, )?; (decl_ref, type_id) diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/struct_instantiation.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/struct_instantiation.rs index 5479b29bd37..7a4565d4a69 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/struct_instantiation.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/struct_instantiation.rs @@ -339,11 +339,13 @@ fn collect_struct_constructors( .filter_map(|item| match item { ResolvedTraitImplItem::Parsed(_) => unreachable!(), ResolvedTraitImplItem::Typed(item) => match item { - ty::TyTraitItem::Fn(fn_decl_id) => Some(fn_decl_id), + ty::TyTraitItem::Fn(fn_decl_id) => { + Some(fn_decl_id.get_method_safe_to_unify(engines, struct_type_id)) + } _ => None, }, }) - .map(|fn_decl_id| engines.de().get_function(fn_decl_id)) + .map(|fn_decl_id| engines.de().get_function(&fn_decl_id)) .filter(|fn_decl| { matches!(fn_decl.visibility, Visibility::Public) && fn_decl diff --git a/sway-core/src/semantic_analysis/namespace/mod.rs b/sway-core/src/semantic_analysis/namespace/mod.rs index c7b49c7b349..3f837b90863 100644 --- a/sway-core/src/semantic_analysis/namespace/mod.rs +++ b/sway-core/src/semantic_analysis/namespace/mod.rs @@ -13,7 +13,6 @@ pub use module::Module; pub use namespace::Namespace; pub use root::ResolvedDeclaration; pub use root::Root; -pub(super) use trait_map::CodeBlockFirstPass; pub(crate) use trait_map::IsExtendingExistingImpl; pub(crate) use trait_map::IsImplSelf; pub(super) use trait_map::ResolvedTraitImplItem; diff --git a/sway-core/src/semantic_analysis/namespace/root.rs b/sway-core/src/semantic_analysis/namespace/root.rs index 13617f1c998..b27651fb5a2 100644 --- a/sway-core/src/semantic_analysis/namespace/root.rs +++ b/sway-core/src/semantic_analysis/namespace/root.rs @@ -560,11 +560,7 @@ impl Root { src_mod .root_items() .implemented_traits - .filter_by_type_item_import( - type_id, - engines, - super::CodeBlockFirstPass::No, - ), + .filter_by_type_item_import(type_id, engines), engines, ); } diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index 92dc42dfcc3..1a2322356bd 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -119,6 +119,15 @@ pub enum ResolvedTraitImplItem { Typed(TyImplItem), } +impl DebugWithEngines for ResolvedTraitImplItem { + fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result { + match self { + ResolvedTraitImplItem::Parsed(_) => panic!(), + ResolvedTraitImplItem::Typed(ty) => write!(f, "{:?}", engines.help_out(ty)), + } + } +} + impl ResolvedTraitImplItem { fn expect_typed(self) -> TyImplItem { match self { @@ -228,6 +237,8 @@ impl TraitMap { is_extending_existing_impl: IsExtendingExistingImpl, engines: &Engines, ) -> Result<(), ErrorEmitted> { + let type_id = engines.te().get_unaliased_type_id(type_id); + handler.scope(|handler| { let mut trait_items: TraitItems = HashMap::new(); for item in items.iter() { @@ -488,118 +499,6 @@ impl TraitMap { self.extend(trait_map, engines); } - /// Given a [TypeId] `type_id`, retrieves entries in the [TraitMap] `self` - /// for which `type_id` is a subset and re-inserts them under `type_id`. - /// - /// Here is an example of what this means. Imagine we have this Sway code: - /// - /// ```ignore - /// struct Data { - /// first: T, - /// second: F, - /// } - /// - /// impl Data { - /// fn get_first(self) -> T { - /// self.first - /// } - /// - /// fn get_second(self) -> F { - /// self.second - /// } - /// } - /// - /// impl Data { - /// fn switch(ref mut self) { - /// let first = self.first; - /// self.first = self.second; - /// self.second = first; - /// } - /// } - /// - /// impl Data { - /// fn add_u8(ref mut self, input: u8) { - /// self.first += input; - /// self.second += input; - /// } - /// } - /// - /// impl Data { - /// fn inner_and(self) -> bool { - /// self.first && self.second - /// } - /// } - /// - /// fn main() { - /// let mut foo = Data { - /// first: 1u8, - /// second: 2u8, - /// }; - /// - /// let a = foo.get_first(); - /// let b = foo.get_second(); - /// foo.switch(); - /// let c = foo.add_u8(3u8); - /// let d = foo.inner_and(); // fails to compile - /// - /// let mut bar = Data { - /// first: true, - /// second: false, - /// }; - /// - /// let e = bar.get_first(); - /// let f = bar.get_second(); - /// bar.switch(); - /// let g = bar.add_u8(3u8); // fails to compile - /// let h = bar.inner_and(); - /// - /// let mut baz = Data { - /// first: 1u8, - /// second: false, - /// }; - /// - /// let i = baz.get_first(); - /// let j = baz.get_second(); - /// baz.switch(); // fails to compile - /// let k = baz.add_u8(3u8); // fails to compile - /// let l = baz.inner_and(); // fails to compile - /// } - /// ``` - /// - /// When we first create the type of `Data` when we declare the - /// variable `foo`, we need some way of gathering all of the applicable - /// traits that have been implemented for `Data`, even if they were - /// not implemented for `Data` directly. That's why we look for - /// entries in the [TraitMap] `self` for which `type_id` is a subset and - /// re-insert them under `type_id`. Moreover, the impl block for - /// `Data` needs to be able to call methods that are defined in the - /// impl block of `Data` - pub(crate) fn insert_for_type( - engines: &Engines, - module: &mut Module, - type_id: TypeId, - code_block_first_pass: CodeBlockFirstPass, - ) { - let type_id = engines.te().get_unaliased_type_id(type_id); - - let mut base_trait_map = TraitMap::default(); - let _ = module.walk_scope_chain_mut(|lexical_scope| { - let trait_map = &mut lexical_scope.items.implemented_traits; - - base_trait_map.extend( - trait_map.filter_by_type(type_id, engines, code_block_first_pass.clone()), - engines, - ); - Ok(None::<()>) - }); - - module - .current_lexical_scope_mut() - .items - .implemented_traits - .extend(base_trait_map, engines); - } - /// Given [TraitMap]s `self` and `other`, extend `self` with `other`, /// extending existing entries when possible. pub(crate) fn extend(&mut self, other: TraitMap, engines: &Engines) { @@ -663,129 +562,6 @@ impl TraitMap { trait_map } - /// Filters the entries in `self` with the given [TypeId] `type_id` and - /// return a new [TraitMap] with all of the entries from `self` for which - /// `type_id` is a subtype. Additionally, the new [TraitMap] contains the - /// entries for the inner types of `self`. - /// - /// An "inner type" of `self` is one that is contained within `self`, but - /// not including `self`. So the types of the fields of a struct would be - /// inner types, for instance. - /// - /// The new [TraitMap] must contain entries for the inner types of `self` - /// because users will want to chain field access's and method calls. - /// Here is some example Sway code to demonstrate this: - /// - /// `data.sw`: - /// ```ignore - /// library; - /// - /// enum MyResult { - /// Ok: T, - /// Err: E, - /// } - /// - /// impl MyResult { - /// fn is_ok(self) -> bool { - /// match self { - /// MyResult::Ok(_) => true, - /// _ => false, - /// } - /// } - /// } - /// - /// pub struct Data { - /// value: MyResult, - /// } - /// - /// impl Data { - /// fn new(value: T) -> Data { - /// Data { - /// value: MyResult::Ok(value) - /// } - /// } - /// } - /// ``` - /// - /// `main.sw`: - /// ```ignore - /// script; - /// - /// mod data; - /// - /// use data::Data; - /// - /// fn main() { - /// let foo = Data::new(true); - /// let bar = foo.value.is_ok(); - /// } - /// ``` - /// - /// In this example, we need to be able to find the definition of the - /// `is_ok` method for the correct type, but we need to do that without - /// requiring the user to import the whole `MyResult` enum. Because if - /// this was required, this would make users make large portions of their - /// libraries public with `pub`. Moreover, we wouldn't need to import the - /// whole `MyResult` enum anyway, because the only type that we are - /// seeing in `main.sw` is `MyResult`! - /// - /// When an entry is found from `self` with type `type_id'` for which - /// `type_id` is a subtype, we take the methods defined upon `type_id'` and - /// translate them to be defined upon `type_id`. - /// - /// Here is an example of what this looks like. Take this Sway code: - /// - /// ```ignore - /// impl Data { - /// fn get_first(self) -> T { - /// self.first - /// } - /// - /// fn get_second(self) -> F { - /// self.second - /// } - /// } - /// - /// impl Data { - /// fn switch(ref mut self) { - /// let first = self.first; - /// self.first = self.second; - /// self.second = first; - /// } - /// } - /// ``` - /// - /// If we were to list all of the methods by hand defined for `Data`, - /// these would be `get_first()`, `get_second()`, and `switch()`. But if we - /// were to list all of the methods by hand for `Data`, these would - /// just include `get_first()` and `get_second()`. So, for any given - /// [TraitMap], in order to find all of the methods defined for a `type_id`, - /// we must iterate through the [TraitMap] and extract all methods that are - /// defined upon any type for which `type_id` is a subset. - /// - /// Once those methods are identified, we need to translate them to be - /// defined upon `type_id`. Imagine that `type_id` is `Data`, when - /// we iterate on `self` we find `Data: get_first(self) -> T`, - /// `Data: get_second(self) -> F`. Once we translate these methods, we - /// have `Data: get_first(self) -> T` and - /// `Data: get_second(self) -> T`, and we can create a new [TraitMap] - /// with those entries for `Data`. - pub(crate) fn filter_by_type( - &self, - type_id: TypeId, - engines: &Engines, - code_block_first_pass: CodeBlockFirstPass, - ) -> TraitMap { - let unify_checker = UnifyCheck::constraint_subset(engines); - - // a curried version of the decider protocol to use in the helper functions - let decider = |left: TypeId, right: TypeId| unify_checker.check(left, right); - let mut all_types = type_id.extract_inner_types(engines, IncludeSelf::No); - all_types.insert(type_id); - let all_types = all_types.into_iter().collect::>(); - self.filter_by_type_inner(engines, all_types, decider, code_block_first_pass) - } - /// Filters the entries in `self` with the given [TypeId] `type_id` and /// return a new [TraitMap] with all of the entries from `self` for which /// `type_id` is a subtype or a supertype. Additionally, the new [TraitMap] @@ -848,7 +624,6 @@ impl TraitMap { &self, type_id: TypeId, engines: &Engines, - code_block_first_pass: CodeBlockFirstPass, ) -> TraitMap { let unify_checker = UnifyCheck::constraint_subset(engines); let unify_checker_for_item_import = UnifyCheck::non_generic_constraint_subset(engines); @@ -857,12 +632,7 @@ impl TraitMap { let decider = |left: TypeId, right: TypeId| { unify_checker.check(left, right) || unify_checker_for_item_import.check(right, left) }; - let mut trait_map = self.filter_by_type_inner( - engines, - vec![type_id], - decider, - code_block_first_pass.clone(), - ); + let mut trait_map = self.filter_by_type_inner(engines, vec![type_id], decider); let all_types = type_id .extract_inner_types(engines, IncludeSelf::No) .into_iter() @@ -871,7 +641,7 @@ impl TraitMap { let decider2 = |left: TypeId, right: TypeId| unify_checker.check(left, right); trait_map.extend( - self.filter_by_type_inner(engines, all_types, decider2, code_block_first_pass), + self.filter_by_type_inner(engines, all_types, decider2), engines, ); trait_map @@ -882,10 +652,8 @@ impl TraitMap { engines: &Engines, mut all_types: Vec, decider: impl Fn(TypeId, TypeId) -> bool, - code_block_first_pass: CodeBlockFirstPass, ) -> TraitMap { let type_engine = engines.te(); - let decl_engine = engines.de(); let mut trait_map = TraitMap::default(); for type_id in all_types.iter_mut() { let type_info = type_engine.get(*type_id); @@ -915,96 +683,17 @@ impl TraitMap { engines, ); } else if decider(*type_id, *map_type_id) { - let mut insertable = true; - if let TypeInfo::UnknownGeneric { - is_from_type_parameter, - .. - } = *engines.te().get(*map_type_id) - { - insertable = !is_from_type_parameter - || matches!( - *engines.te().get(*type_id), - TypeInfo::UnknownGeneric { .. } - ); - } - let type_mapping = TypeSubstMap::from_superset_and_subset( - type_engine, - decl_engine, - *map_type_id, - *type_id, - ); - let trait_items: TraitItems = map_trait_items - .clone() - .into_iter() - .filter_map(|(name, item)| match &item { - ResolvedTraitImplItem::Parsed(_item) => todo!(), - ResolvedTraitImplItem::Typed(item) => match item { - ty::TyTraitItem::Fn(decl_ref) => { - let mut decl = (*decl_engine.get(decl_ref.id())).clone(); - if decl.is_trait_method_dummy && !insertable { - None - } else { - decl.subst(&SubstTypesContext::new( - engines, - &type_mapping, - matches!(code_block_first_pass, CodeBlockFirstPass::No), - )); - let new_ref = decl_engine - .insert( - decl, - decl_engine - .get_parsed_decl_id(decl_ref.id()) - .as_ref(), - ) - .with_parent(decl_engine, decl_ref.id().into()); - Some(( - name, - ResolvedTraitImplItem::Typed(TyImplItem::Fn(new_ref)), - )) - } - } - ty::TyTraitItem::Constant(decl_ref) => { - let mut decl = (*decl_engine.get(decl_ref.id())).clone(); - decl.subst(&SubstTypesContext::new( - engines, - &type_mapping, - matches!(code_block_first_pass, CodeBlockFirstPass::No), - )); - let new_ref = decl_engine.insert( - decl, - decl_engine.get_parsed_decl_id(decl_ref.id()).as_ref(), - ); - Some(( - name, - ResolvedTraitImplItem::Typed(TyImplItem::Constant(new_ref)), - )) - } - ty::TyTraitItem::Type(decl_ref) => { - let mut decl = (*decl_engine.get(decl_ref.id())).clone(); - decl.subst(&SubstTypesContext::new( - engines, - &type_mapping, - matches!(code_block_first_pass, CodeBlockFirstPass::No), - )); - let new_ref = decl_engine.insert( - decl, - decl_engine.get_parsed_decl_id(decl_ref.id()).as_ref(), - ); - Some(( - name, - ResolvedTraitImplItem::Typed(TyImplItem::Type(new_ref)), - )) - } - }, - }) - .collect(); - trait_map.insert_inner( map_trait_name.clone(), impl_span.clone(), map_trait_decl_span.clone(), - *type_id, - trait_items, + *map_type_id, + Self::filter_dummy_methods( + map_trait_items.clone(), + *type_id, + *map_type_id, + engines, + ), engines, ); } @@ -1013,6 +702,102 @@ impl TraitMap { trait_map } + fn filter_dummy_methods( + map_trait_items: TraitItems, + type_id: TypeId, + map_type_id: TypeId, + engines: &Engines, + ) -> TraitItems { + let mut insertable = true; + if let TypeInfo::UnknownGeneric { + is_from_type_parameter, + .. + } = *engines.te().get(map_type_id) + { + insertable = !is_from_type_parameter + || matches!(*engines.te().get(type_id), TypeInfo::UnknownGeneric { .. }); + } + + map_trait_items + .clone() + .into_iter() + .filter_map(|(name, item)| match item { + ResolvedTraitImplItem::Parsed(_item) => todo!(), + ResolvedTraitImplItem::Typed(item) => match item { + ty::TyTraitItem::Fn(decl_ref) => { + let decl = (*engines.de().get(decl_ref.id())).clone(); + if decl.is_trait_method_dummy && !insertable { + None + } else { + Some((name, ResolvedTraitImplItem::Typed(TyImplItem::Fn(decl_ref)))) + } + } + ty::TyTraitItem::Constant(decl_ref) => Some(( + name, + ResolvedTraitImplItem::Typed(TyImplItem::Constant(decl_ref)), + )), + ty::TyTraitItem::Type(decl_ref) => Some(( + name, + ResolvedTraitImplItem::Typed(TyImplItem::Type(decl_ref)), + )), + }, + }) + .collect() + } + + fn make_item_for_type_mapping( + engines: &Engines, + item: ResolvedTraitImplItem, + mut type_mapping: TypeSubstMap, + type_id: TypeId, + code_block_first_pass: CodeBlockFirstPass, + ) -> ResolvedTraitImplItem { + let decl_engine = engines.de(); + match &item { + ResolvedTraitImplItem::Parsed(_item) => todo!(), + ResolvedTraitImplItem::Typed(item) => match item { + ty::TyTraitItem::Fn(decl_ref) => { + let mut decl = (*decl_engine.get(decl_ref.id())).clone(); + if let Some(decl_implementing_for_typeid) = decl.implementing_for_typeid { + type_mapping.insert(decl_implementing_for_typeid, type_id); + } + decl.subst(&SubstTypesContext::new( + engines, + &type_mapping, + matches!(code_block_first_pass, CodeBlockFirstPass::No), + )); + let new_ref = decl_engine + .insert(decl, decl_engine.get_parsed_decl_id(decl_ref.id()).as_ref()) + .with_parent(decl_engine, decl_ref.id().into()); + + ResolvedTraitImplItem::Typed(TyImplItem::Fn(new_ref)) + } + ty::TyTraitItem::Constant(decl_ref) => { + let mut decl = (*decl_engine.get(decl_ref.id())).clone(); + decl.subst(&SubstTypesContext::new( + engines, + &type_mapping, + matches!(code_block_first_pass, CodeBlockFirstPass::No), + )); + let new_ref = decl_engine + .insert(decl, decl_engine.get_parsed_decl_id(decl_ref.id()).as_ref()); + ResolvedTraitImplItem::Typed(TyImplItem::Constant(new_ref)) + } + ty::TyTraitItem::Type(decl_ref) => { + let mut decl = (*decl_engine.get(decl_ref.id())).clone(); + decl.subst(&SubstTypesContext::new( + engines, + &type_mapping, + matches!(code_block_first_pass, CodeBlockFirstPass::No), + )); + let new_ref = decl_engine + .insert(decl, decl_engine.get_parsed_decl_id(decl_ref.id()).as_ref()); + ResolvedTraitImplItem::Typed(TyImplItem::Type(new_ref)) + } + }, + } + } + /// Find the entries in `self` that are equivalent to `type_id`. /// /// Notes: @@ -1039,7 +824,9 @@ impl TraitMap { type_id: TypeId, ) -> Vec<(ResolvedTraitImplItem, TraitKey)> { let type_engine = engines.te(); - let unify_check = UnifyCheck::non_dynamic_equality(engines).with_unify_ref_mut(false); + let unify_check = UnifyCheck::constraint_subset(engines); + + let type_id = engines.te().get_unaliased_type_id(type_id); let mut items = vec![]; // small performance gain in bad case @@ -1051,18 +838,21 @@ impl TraitMap { let impls = lexical_scope .items .implemented_traits - .get_impls(engines, type_id, false); + .get_impls(engines, type_id, true); for entry in impls { if unify_check.check(type_id, entry.key.type_id) { - let mut trait_items = entry - .value - .trait_items - .values() - .cloned() - .map(|i| (i, entry.key.clone())) - .collect::>(); + let trait_items = Self::filter_dummy_methods( + entry.value.trait_items, + type_id, + entry.key.type_id, + engines, + ) + .values() + .cloned() + .map(|i| (i, entry.key.clone())) + .collect::>(); - items.append(&mut trait_items); + items.extend(trait_items); } } Ok(None::<()>) @@ -1085,7 +875,9 @@ impl TraitMap { type_id: &TypeId, ) -> Vec { let type_engine = engines.te(); - let unify_check = UnifyCheck::non_dynamic_equality(engines); + let unify_check = UnifyCheck::constraint_subset(engines); + + let type_id = &engines.te().get_unaliased_type_id(*type_id); let mut spans = vec![]; // small performance gain in bad case @@ -1175,8 +967,10 @@ impl TraitMap { trait_name: &CallPath, trait_type_args: &[TypeArgument], ) -> Vec { + let type_id = engines.te().get_unaliased_type_id(type_id); + let type_engine = engines.te(); - let unify_check = UnifyCheck::non_dynamic_equality(engines); + let unify_check = UnifyCheck::constraint_subset(engines); let mut items = vec![]; // small performance gain in bad case if matches!(&*type_engine.get(type_id), TypeInfo::ErrorRecovery(_)) { @@ -1201,7 +995,32 @@ impl TraitMap { .zip(e.key.name.suffix.args.iter()) .all(|(t1, t2)| unify_check.check(t1.type_id, t2.type_id)) { - let mut trait_items = e.value.trait_items.values().cloned().collect::>(); + let type_mapping = TypeSubstMap::from_superset_and_subset( + engines.te(), + engines.de(), + e.key.type_id, + type_id, + ); + + let mut trait_items = Self::filter_dummy_methods( + e.value.trait_items, + type_id, + e.key.type_id, + engines, + ) + .values() + .cloned() + .map(|i| { + Self::make_item_for_type_mapping( + engines, + i, + type_mapping.clone(), + type_id, + CodeBlockFirstPass::No, + ) + }) + .collect::>(); + items.append(&mut trait_items); } } @@ -1244,8 +1063,10 @@ impl TraitMap { engines: &Engines, type_id: TypeId, ) -> Vec<(CallPath, Vec)> { + let type_id = engines.te().get_unaliased_type_id(type_id); + let type_engine = engines.te(); - let unify_check = UnifyCheck::non_dynamic_equality(engines); + let unify_check = UnifyCheck::constraint_subset(engines); let mut trait_names = vec![]; // small performance gain in bad case if matches!(&*type_engine.get(type_id), TypeInfo::ErrorRecovery(_)) { @@ -1279,6 +1100,8 @@ impl TraitMap { type_id: TypeId, as_trait: Option, ) -> Result { + let type_id = engines.te().get_unaliased_type_id(type_id); + let mut candidates = HashMap::::new(); for (trait_item, trait_key) in TraitMap::get_items_and_trait_key_for_type(module, engines, type_id) @@ -1408,11 +1231,11 @@ impl TraitMap { constraints: &[TraitConstraint], access_span: &Span, engines: &Engines, - try_inserting_trait_impl_on_failure: TryInsertingTraitImplOnFailure, - code_block_first_pass: CodeBlockFirstPass, ) -> Result<(), ErrorEmitted> { let type_engine = engines.te(); + let type_id = type_engine.get_unaliased_type_id(type_id); + // resolving trait constraints require a concrete type, we need to default numeric to u64 type_engine.decay_numeric(handler, engines, type_id, access_span)?; @@ -1441,14 +1264,11 @@ impl TraitMap { // Call the real implementation and cache when true match Self::check_if_trait_constraints_are_satisfied_for_type_inner( handler, - module, type_id, constraints, access_span, engines, all_impld_traits, - try_inserting_trait_impl_on_failure, - code_block_first_pass, ) { Ok(()) => { let trait_map = &mut module.current_lexical_scope_mut().items.implemented_traits; @@ -1483,9 +1303,9 @@ impl TraitMap { engines: &Engines, ) -> BTreeSet<(Ident, TypeId)> { let type_engine = engines.te(); - let unify_check = UnifyCheck::non_dynamic_equality(engines); + let unify_check = UnifyCheck::constraint_subset(engines); - let impls = self.get_impls(engines, type_id, false); + let impls = self.get_impls(engines, type_id, true); let all_impld_traits: BTreeSet<(Ident, TypeId)> = impls .iter() .filter_map(|e| { @@ -1514,17 +1334,14 @@ impl TraitMap { #[allow(clippy::too_many_arguments)] fn check_if_trait_constraints_are_satisfied_for_type_inner( handler: &Handler, - module: &mut Module, type_id: TypeId, constraints: &[TraitConstraint], access_span: &Span, engines: &Engines, all_impld_traits: BTreeSet<(Ident, TypeId)>, - try_inserting_trait_impl_on_failure: TryInsertingTraitImplOnFailure, - code_block_first_pass: CodeBlockFirstPass, ) -> Result<(), ErrorEmitted> { let type_engine = engines.te(); - let unify_check = UnifyCheck::non_dynamic_equality(engines); + let unify_check = UnifyCheck::constraint_subset(engines); let required_traits: BTreeSet<(Ident, TypeId)> = constraints .iter() @@ -1560,45 +1377,22 @@ impl TraitMap { handler.scope(|handler| { for (trait_name, constraint_type_id) in traits_not_found.iter() { - if matches!( - try_inserting_trait_impl_on_failure, - TryInsertingTraitImplOnFailure::Yes - ) { - TraitMap::insert_for_type( - engines, - module, - type_id, - code_block_first_pass.clone(), - ); - - return Self::check_if_trait_constraints_are_satisfied_for_type( - handler, - module, - type_id, - constraints, - access_span, - engines, - TryInsertingTraitImplOnFailure::No, - code_block_first_pass.clone(), - ); - } else { - let mut type_arguments_string = "".to_string(); - if let TypeInfo::Custom { - qualified_call_path: _, - type_arguments: Some(type_arguments), - } = &*type_engine.get(*constraint_type_id) - { - type_arguments_string = format!("<{}>", engines.help_out(type_arguments)); - } - - // TODO: use a better span - handler.emit_err(CompileError::TraitConstraintNotSatisfied { - type_id: type_id.index(), - ty: engines.help_out(type_id).to_string(), - trait_name: format!("{}{}", trait_name, type_arguments_string), - span: access_span.clone(), - }); + let mut type_arguments_string = "".to_string(); + if let TypeInfo::Custom { + qualified_call_path: _, + type_arguments: Some(type_arguments), + } = &*type_engine.get(*constraint_type_id) + { + type_arguments_string = format!("<{}>", engines.help_out(type_arguments)); } + + // TODO: use a better span + handler.emit_err(CompileError::TraitConstraintNotSatisfied { + type_id: type_id.index(), + ty: engines.help_out(type_id).to_string(), + trait_name: format!("{}{}", trait_name, type_arguments_string), + span: access_span.clone(), + }); } Ok(()) @@ -1612,9 +1406,11 @@ impl TraitMap { constraints: &[TraitConstraint], engines: &Engines, ) -> Result, ErrorEmitted> { + let type_id = engines.te().get_unaliased_type_id(type_id); + let _decl_engine = engines.de(); let unify_check = UnifyCheck::coercion(engines); - let unify_check_equality = UnifyCheck::non_dynamic_equality(engines); + let unify_check_equality = UnifyCheck::constraint_subset(engines); let mut impld_traits_type_ids: Vec> = vec![]; let _ = module.walk_scope_chain(|lexical_scope| { diff --git a/sway-core/src/semantic_analysis/type_check_analysis.rs b/sway-core/src/semantic_analysis/type_check_analysis.rs index 6ad5337561d..f1aa66e322e 100644 --- a/sway-core/src/semantic_analysis/type_check_analysis.rs +++ b/sway-core/src/semantic_analysis/type_check_analysis.rs @@ -317,7 +317,7 @@ impl TypeCheckAnalysisContext<'_> { .collect::>(); if !parents.is_empty() { - *parents.first().unwrap() + self.get_normalized_fn_node_id(parents.first().unwrap()) } else { *fn_decl_id } diff --git a/sway-core/src/semantic_analysis/type_check_context.rs b/sway-core/src/semantic_analysis/type_check_context.rs index fb68d4e6ca6..9bf090ccdbc 100644 --- a/sway-core/src/semantic_analysis/type_check_context.rs +++ b/sway-core/src/semantic_analysis/type_check_context.rs @@ -12,7 +12,7 @@ use crate::{ monomorphization::{monomorphize_with_modpath, MonomorphizeHelper}, namespace::{ IsExtendingExistingImpl, IsImplSelf, ModulePath, ResolvedDeclaration, - ResolvedTraitImplItem, TraitMap, TryInsertingTraitImplOnFailure, + ResolvedTraitImplItem, TraitMap, }, semantic_analysis::{ ast_node::{AbiMode, ConstShadowingMode}, @@ -799,13 +799,12 @@ impl<'a> TypeCheckContext<'a> { annotation_type: TypeId, arguments_types: &VecDeque, as_trait: Option, - try_inserting_trait_impl_on_failure: TryInsertingTraitImplOnFailure, ) -> Result { let decl_engine = self.engines.de(); let type_engine = self.engines.te(); - let eq_check = UnifyCheck::non_dynamic_equality(self.engines); - let coercion_check = UnifyCheck::coercion(self.engines); + let eq_check = UnifyCheck::constraint_subset(self.engines); + let coercion_check = UnifyCheck::coercion(self.engines).with_ignore_generic_names(true); // default numeric types to u64 if type_engine.contains_numeric(self.engines, type_id) { @@ -880,9 +879,7 @@ impl<'a> TypeCheckContext<'a> { &*type_engine.get(method.return_type.type_id), TypeInfo::Never ) - || coercion_check - .with_ignore_generic_names(true) - .check(annotation_type, method.return_type.type_id)) + || coercion_check.check(annotation_type, method.return_type.type_id)) { maybe_method_decl_refs.push(decl_ref); } @@ -904,6 +901,20 @@ impl<'a> TypeCheckContext<'a> { method.implementing_type.clone() { let trait_decl = decl_engine.get_impl_self_or_trait(&impl_trait.decl_id); + + let trait_methods_key = ( + trait_decl.trait_name.clone(), + trait_decl + .trait_type_arguments + .iter() + .cloned() + .map(|a| self.engines.help_out(a)) + .collect::>(), + method.implementing_for_typeid.map(|t| { + self.engines.help_out((*self.engines.te().get(t)).clone()) + }), + ); + let mut skip_insert = false; if let Some(as_trait) = as_trait { if let TypeInfo::Custom { @@ -948,56 +959,20 @@ impl<'a> TypeCheckContext<'a> { } } if params_equal { - trait_methods.insert( - ( - trait_decl.trait_name.clone(), - trait_decl - .trait_type_arguments - .iter() - .cloned() - .map(|a| self.engines.help_out(a)) - .collect::>(), - method.implementing_for_typeid.map(|t| { - self.engines.help_out( - (*self.engines.te().get(t)).clone(), - ) - }), - ), - method_ref.clone(), - ); + trait_methods + .insert(trait_methods_key.clone(), method_ref.clone()); } } skip_insert = true; } } - let trait_methods_key = ( - trait_decl.trait_name.clone(), - trait_decl - .trait_type_arguments - .iter() - .cloned() - .map(|a| self.engines.help_out(a)) - .collect::>(), - method.implementing_for_typeid.map(|t| { - self.engines.help_out((*self.engines.te().get(t)).clone()) - }), - ); - - // If we have: impl FromBytes for T - // and: impl FromBytes for DataPoint - // We pick the second implementation. if let Some(existing_value) = trait_methods.get(&trait_methods_key) { let existing_method = decl_engine.get_function(existing_value); - if let Some(ty::TyDecl::ImplSelfOrTrait(existing_impl_trait)) = - existing_method.implementing_type.clone() + if existing_method.is_type_check_finalized + && !method.is_type_check_finalized { - let existing_trait_decl = decl_engine - .get_impl_self_or_trait(&existing_impl_trait.decl_id); - if existing_trait_decl.impl_type_parameters.is_empty() { - // We already have an impl without type parameters so we skip the others. - skip_insert = true; - } + skip_insert = true; } } @@ -1010,6 +985,25 @@ impl<'a> TypeCheckContext<'a> { } } + // If we have: impl FromBytes for T + // and: impl FromBytes for DataPoint + // We pick the second implementation. + let mut non_blanket_impl_exists = false; + let mut impls_with_type_params = vec![]; + let trait_method_clone = trait_methods.clone(); + let existing_values = trait_method_clone.values().collect::>(); + for existing_value in existing_values.iter() { + let existing_method = decl_engine.get_function(*existing_value); + if !existing_method.is_from_blanket_impl(self.engines) { + non_blanket_impl_exists = true; + } else { + impls_with_type_params.push(existing_value.id()); + } + } + if non_blanket_impl_exists { + trait_methods.retain(|_, v| !impls_with_type_params.contains(&v.id())); + } + if trait_methods.len() == 1 { trait_methods.values().next().cloned() } else if trait_methods.len() > 1 { @@ -1017,51 +1011,75 @@ impl<'a> TypeCheckContext<'a> { // In case we have trait methods and a impl self method we use the impl self method. impl_self_method } else { - fn to_string( - trait_name: CallPath, - trait_type_args: Vec>, - ) -> String { - format!( - "{}{}", - trait_name.suffix, - if trait_type_args.is_empty() { - String::new() - } else { - format!( - "<{}>", - trait_type_args - .iter() - .map(|type_arg| type_arg.to_string()) - .collect::>() - .join(", ") + // Avoids following error when we already know the exact type: + // Multiple applicable items in scope. + // Disambiguate the associated function for candidate #0 + // <&&&u64 as Trait>::val + // Disambiguate the associated function for candidate #1 + // <&mut &mut &u64 as Trait>::val + // If one method has the exact type an the others don't we can use that method. + let mut exact_matching_methods = vec![]; + let trait_method_values = trait_methods.values().collect::>(); + for trait_method_ref in trait_method_values.iter() { + let method = decl_engine.get_function(*trait_method_ref); + if let Some(implementing_for_type) = method.implementing_for_typeid { + if eq_check + .with_unify_ref_mut(false) + .check(implementing_for_type, type_id) + { + exact_matching_methods.push(*trait_method_ref); + } + } + } + if exact_matching_methods.len() == 1 { + exact_matching_methods.into_iter().next().cloned() + } else { + fn to_string( + trait_name: CallPath, + trait_type_args: Vec>, + ) -> String { + format!( + "{}{}", + trait_name.suffix, + if trait_type_args.is_empty() { + String::new() + } else { + format!( + "<{}>", + trait_type_args + .iter() + .map(|type_arg| type_arg.to_string()) + .collect::>() + .join(", ") + ) + }, + ) + } + let mut trait_strings = trait_methods + .keys() + .map(|t| { + ( + to_string(t.0.clone(), t.1.clone()), + t.2.clone() + .map(|t| t.to_string()) + .or_else(|| { + Some(self.engines().help_out(type_id).to_string()) + }) + .unwrap(), ) + }) + .collect::>(); + // Sort so the output of the error is always the same. + trait_strings.sort(); + return Err(handler.emit_err( + CompileError::MultipleApplicableItemsInScope { + item_name: method_name.as_str().to_string(), + item_kind: "function".to_string(), + as_traits: trait_strings, + span: method_name.span(), }, - ) + )); } - let mut trait_strings = trait_methods - .keys() - .map(|t| { - ( - to_string(t.0.clone(), t.1.clone()), - t.2.clone() - .map(|t| t.to_string()) - .or_else(|| { - Some(self.engines().help_out(type_id).to_string()) - }) - .unwrap(), - ) - }) - .collect::>(); - // Sort so the output of the error is always the same. - trait_strings.sort(); - return Err(handler.emit_err( - CompileError::MultipleApplicableItemsInScope { - item_name: method_name.as_str().to_string(), - item_kind: "function".to_string(), - as_traits: trait_strings, - span: method_name.span(), - }, - )); } } else if qualified_call_path.is_some() { // When we use a qualified path the expected method should be in trait_methods. @@ -1096,7 +1114,7 @@ impl<'a> TypeCheckContext<'a> { }; if let Some(method_decl_ref) = matching_method_decl_ref { - return Ok(method_decl_ref); + return Ok(method_decl_ref.get_method_safe_to_unify(self.engines, type_id)); } if let Some(TypeInfo::ErrorRecovery(err)) = arguments_types @@ -1105,26 +1123,6 @@ impl<'a> TypeCheckContext<'a> { { Err(err) } else { - if matches!( - try_inserting_trait_impl_on_failure, - TryInsertingTraitImplOnFailure::Yes - ) { - // Retrieve the implemented traits for the type and insert them in the namespace. - // insert_trait_implementation_for_type is done lazily only when required because of a failure. - self.insert_trait_implementation_for_type(type_id); - - return self.find_method_for_type( - handler, - type_id, - method_prefix, - method_name, - annotation_type, - arguments_types, - as_trait, - TryInsertingTraitImplOnFailure::No, - ); - } - let type_name = if let Some(call_path) = qualified_call_path { format!( "{} as {}", @@ -1231,11 +1229,7 @@ impl<'a> TypeCheckContext<'a> { lexical_scope .items .implemented_traits - .filter_by_type_item_import( - type_id, - engines, - self.code_block_first_pass().into(), - ), + .filter_by_type_item_import(type_id, engines), engines, ); Ok(None::<()>) @@ -1354,17 +1348,6 @@ impl<'a> TypeCheckContext<'a> { ) } - pub(crate) fn insert_trait_implementation_for_type(&mut self, type_id: TypeId) { - let engines = self.engines; - let code_block_first_pass = self.code_block_first_pass(); - TraitMap::insert_for_type( - engines, - self.namespace_mut().current_module_mut(), - type_id, - code_block_first_pass.into(), - ); - } - pub fn check_type_impls_traits( &mut self, type_id: TypeId, @@ -1372,7 +1355,6 @@ impl<'a> TypeCheckContext<'a> { ) -> bool { let handler = Handler::default(); let engines = self.engines; - let code_block_first_pass = self.code_block_first_pass(); TraitMap::check_if_trait_constraints_are_satisfied_for_type( &handler, self.namespace_mut().current_module_mut(), @@ -1380,8 +1362,6 @@ impl<'a> TypeCheckContext<'a> { constraints, &Span::dummy(), engines, - crate::namespace::TryInsertingTraitImplOnFailure::Yes, - code_block_first_pass.into(), ) .is_ok() } diff --git a/sway-core/src/type_system/ast_elements/type_parameter.rs b/sway-core/src/type_system/ast_elements/type_parameter.rs index d6ab976517a..0743d576fc4 100644 --- a/sway-core/src/type_system/ast_elements/type_parameter.rs +++ b/sway-core/src/type_system/ast_elements/type_parameter.rs @@ -3,7 +3,7 @@ use crate::{ engine_threading::*, has_changes, language::{ty, CallPath}, - namespace::{TraitMap, TryInsertingTraitImplOnFailure}, + namespace::TraitMap, semantic_analysis::{GenericShadowingMode, TypeCheckContext}, type_system::priv_prelude::*, }; @@ -573,8 +573,6 @@ impl TypeParameter { trait_constraints, access_span, engines, - TryInsertingTraitImplOnFailure::Yes, - code_block_first_pass.into(), ) { Ok(res) => res, Err(_) => continue, diff --git a/sway-core/src/type_system/id.rs b/sway-core/src/type_system/id.rs index 9d9c0ebbd8a..3c8558726ea 100644 --- a/sway-core/src/type_system/id.rs +++ b/sway-core/src/type_system/id.rs @@ -190,6 +190,229 @@ impl TypeId { found } + pub(crate) fn extract_type_parameters( + self, + engines: &Engines, + depth: usize, + type_parameters: &mut Vec<(TypeParameter, TypeParameter)>, + orig_type_id: TypeId, + ) { + if depth >= EXTRACT_ANY_MAX_DEPTH { + panic!("Possible infinite recursion at extract_type_parameters"); + } + + let decl_engine = engines.de(); + match (&*engines.te().get(self), &*engines.te().get(orig_type_id)) { + (TypeInfo::Unknown, TypeInfo::Unknown) + | (TypeInfo::Never, TypeInfo::Never) + | (TypeInfo::Placeholder(_), TypeInfo::Placeholder(_)) + | (TypeInfo::TypeParam(_), TypeInfo::TypeParam(_)) + | (TypeInfo::StringArray(_), TypeInfo::StringArray(_)) + | (TypeInfo::StringSlice, TypeInfo::StringSlice) + | (TypeInfo::UnsignedInteger(_), TypeInfo::UnsignedInteger(_)) + | (TypeInfo::RawUntypedPtr, TypeInfo::RawUntypedPtr) + | (TypeInfo::RawUntypedSlice, TypeInfo::RawUntypedSlice) + | (TypeInfo::Boolean, TypeInfo::Boolean) + | (TypeInfo::B256, TypeInfo::B256) + | (TypeInfo::Numeric, TypeInfo::Numeric) + | (TypeInfo::Contract, TypeInfo::Contract) + | (TypeInfo::ErrorRecovery(_), TypeInfo::ErrorRecovery(_)) + | (TypeInfo::TraitType { .. }, TypeInfo::TraitType { .. }) => {} + (TypeInfo::UntypedEnum(decl_id), TypeInfo::UntypedEnum(orig_decl_id)) => { + let enum_decl = engines.pe().get_enum(decl_id); + let orig_enum_decl = engines.pe().get_enum(orig_decl_id); + assert_eq!( + enum_decl.type_parameters.len(), + orig_enum_decl.type_parameters.len() + ); + for (type_param, orig_type_param) in enum_decl + .type_parameters + .iter() + .zip(orig_enum_decl.type_parameters.iter()) + { + type_parameters.push((type_param.clone(), orig_type_param.clone())); + type_param.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_type_param.type_id, + ); + } + } + (TypeInfo::UntypedStruct(decl_id), TypeInfo::UntypedStruct(orig_decl_id)) => { + let struct_decl = engines.pe().get_struct(decl_id); + let orig_struct_decl = engines.pe().get_struct(orig_decl_id); + assert_eq!( + struct_decl.type_parameters.len(), + orig_struct_decl.type_parameters.len() + ); + for (type_param, orig_type_param) in struct_decl + .type_parameters + .iter() + .zip(orig_struct_decl.type_parameters.iter()) + { + type_parameters.push((type_param.clone(), orig_type_param.clone())); + type_param.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_type_param.type_id, + ); + } + } + (TypeInfo::Enum(enum_ref), TypeInfo::Enum(orig_enum_ref)) => { + let enum_decl = decl_engine.get_enum(enum_ref); + let orig_enum_decl = decl_engine.get_enum(orig_enum_ref); + assert_eq!( + enum_decl.type_parameters.len(), + orig_enum_decl.type_parameters.len() + ); + for (type_param, orig_type_param) in enum_decl + .type_parameters + .iter() + .zip(orig_enum_decl.type_parameters.iter()) + { + type_parameters.push((type_param.clone(), orig_type_param.clone())); + type_param.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_type_param.type_id, + ); + } + } + (TypeInfo::Struct(struct_id), TypeInfo::Struct(orig_struct_id)) => { + let struct_decl = decl_engine.get_struct(struct_id); + let orig_struct_decl = decl_engine.get_struct(orig_struct_id); + assert_eq!( + struct_decl.type_parameters.len(), + orig_struct_decl.type_parameters.len() + ); + for (type_param, orig_type_param) in struct_decl + .type_parameters + .iter() + .zip(orig_struct_decl.type_parameters.iter()) + { + type_parameters.push((type_param.clone(), orig_type_param.clone())); + type_param.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_type_param.type_id, + ); + } + } + (TypeInfo::Tuple(elems), TypeInfo::Tuple(orig_elems)) => { + assert_eq!(elems.len(), orig_elems.len()); + for (elem, orig_elem) in elems.iter().zip(orig_elems.iter()) { + elem.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_elem.type_id, + ); + } + } + ( + TypeInfo::ContractCaller { + abi_name: _, + address, + }, + TypeInfo::ContractCaller { + abi_name: _, + address: orig_address, + }, + ) => { + if let Some(address) = address { + address.return_type.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_address.clone().unwrap().return_type, + ); + } + } + ( + TypeInfo::Custom { + qualified_call_path: _, + type_arguments, + }, + TypeInfo::Custom { + qualified_call_path: _, + type_arguments: orig_type_arguments, + }, + ) => { + if let Some(type_arguments) = type_arguments { + for (type_arg, orig_type_arg) in type_arguments + .iter() + .zip(orig_type_arguments.clone().unwrap().iter()) + { + type_arg.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_type_arg.type_id, + ); + } + } + } + (TypeInfo::Array(ty, _), TypeInfo::Array(orig_ty, _)) => { + ty.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_ty.type_id, + ); + } + (TypeInfo::Alias { name: _, ty }, _) => { + ty.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_type_id, + ); + } + (_, TypeInfo::Alias { name: _, ty }) => { + self.extract_type_parameters(engines, depth + 1, type_parameters, ty.type_id); + } + (TypeInfo::UnknownGeneric { .. }, TypeInfo::UnknownGeneric { .. }) => {} + (TypeInfo::Ptr(ty), TypeInfo::Ptr(orig_ty)) => { + ty.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_ty.type_id, + ); + } + (TypeInfo::Slice(ty), TypeInfo::Slice(orig_ty)) => { + ty.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_ty.type_id, + ); + } + ( + TypeInfo::Ref { + referenced_type, .. + }, + TypeInfo::Ref { + referenced_type: orig_referenced_type, + .. + }, + ) => { + referenced_type.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + orig_referenced_type.type_id, + ); + } + (_, TypeInfo::UnknownGeneric { .. }) => {} + (_, _) => {} + } + } + pub(crate) fn extract_any( self, engines: &Engines, @@ -607,46 +830,34 @@ impl TypeId { } } } else { - let found_error = self.check_trait_constraints_errors( + self.check_trait_constraints_errors( handler, ctx.by_ref(), structure_type_id, structure_trait_constraints, - |_| {}, + |structure_trait_constraint| { + let mut type_arguments_string = String::new(); + if !structure_trait_constraint.type_arguments.is_empty() { + type_arguments_string = format!( + "<{}>", + engines.help_out( + structure_trait_constraint.type_arguments.clone() + ) + ); + } + + handler.emit_err(CompileError::TraitConstraintNotSatisfied { + type_id: structure_type_id.index(), + ty: structure_type_info_with_engines.to_string(), + trait_name: format!( + "{}{}", + structure_trait_constraint.trait_name.suffix, + type_arguments_string + ), + span: span.clone(), + }); + }, ); - if found_error { - // Retrieve the implemented traits for the type and insert them in the namespace. - // insert_trait_implementation_for_type is done lazily only when required because of a failure. - ctx.insert_trait_implementation_for_type(*structure_type_id); - self.check_trait_constraints_errors( - handler, - ctx.by_ref(), - structure_type_id, - structure_trait_constraints, - |structure_trait_constraint| { - let mut type_arguments_string = String::new(); - if !structure_trait_constraint.type_arguments.is_empty() { - type_arguments_string = format!( - "<{}>", - engines.help_out( - structure_trait_constraint.type_arguments.clone() - ) - ); - } - - handler.emit_err(CompileError::TraitConstraintNotSatisfied { - type_id: structure_type_id.index(), - ty: structure_type_info_with_engines.to_string(), - trait_name: format!( - "{}{}", - structure_trait_constraint.trait_name.suffix, - type_arguments_string - ), - span: span.clone(), - }); - }, - ); - } } } Ok(()) @@ -663,7 +874,7 @@ impl TypeId { ) -> bool { let engines = ctx.engines(); - let unify_check = UnifyCheck::non_dynamic_equality(engines); + let unify_check = UnifyCheck::constraint_subset(engines); let mut found_error = false; let generic_trait_constraints_trait_names_and_args = TraitMap::get_trait_names_and_type_arguments_for_type( diff --git a/sway-core/src/type_system/substitute/subst_map.rs b/sway-core/src/type_system/substitute/subst_map.rs index 36de9b4846a..02623511030 100644 --- a/sway-core/src/type_system/substitute/subst_map.rs +++ b/sway-core/src/type_system/substitute/subst_map.rs @@ -308,6 +308,10 @@ impl TypeSubstMap { self.mapping.extend(subst_map.mapping.iter()); } + pub(crate) fn insert(&mut self, source: SourceType, destination: DestinationType) { + self.mapping.insert(source, destination); + } + /// Given a [TypeId] `type_id`, find (or create) a match for `type_id` in /// this [TypeSubstMap] and return it, if there is a match. Importantly, this /// function is recursive, so any `type_id` it's given will undergo diff --git a/sway-core/src/type_system/unify/unify_check.rs b/sway-core/src/type_system/unify/unify_check.rs index 04e3473d5b1..8773fc5e114 100644 --- a/sway-core/src/type_system/unify/unify_check.rs +++ b/sway-core/src/type_system/unify/unify_check.rs @@ -4,7 +4,7 @@ use crate::{ ty::{TyEnumDecl, TyStructDecl}, CallPathType, }, - type_system::{priv_prelude::*, unify::occurs_check::OccursCheck}, + type_system::priv_prelude::*, }; #[derive(Debug, Clone)] @@ -271,6 +271,11 @@ impl<'a> UnifyCheck<'a> { // common recursion patterns match (&*left_info, &*right_info) { + // when a type alias is encountered, defer the decision to the type it contains (i.e. the + // type it aliases with) + (Alias { ty, .. }, _) => return self.check_inner(ty.type_id, right), + (_, Alias { ty, .. }) => return self.check_inner(left, ty.type_id), + (Never, Never) => { return true; } @@ -448,10 +453,6 @@ impl<'a> UnifyCheck<'a> { // any type can be coerced into the placeholder type (_, Placeholder(_)) => true, - // Type aliases and the types they encapsulate coerce to each other. - (Alias { ty, .. }, _) => self.check_inner(ty.type_id, right), - (_, Alias { ty, .. }) => self.check_inner(left, ty.type_id), - (Unknown, _) => true, (_, Unknown) => true, @@ -508,24 +509,15 @@ impl<'a> UnifyCheck<'a> { } // any type can be coerced into a generic, - // except if the type already contains the generic (_e, _g @ UnknownGeneric { .. }) => { - matches!(self.mode, ConstraintSubset) - || !OccursCheck::new(self.engines).check(right, left) + // Perform this check otherwise &T and T would return true + !matches!(&*left_info, TypeInfo::Ref { .. }) } - (Alias { ty: l_ty, .. }, Alias { ty: r_ty, .. }) => { - self.check_inner(l_ty.type_id, r_ty.type_id) - } (a, b) => a.eq(b, &PartialEqWithEnginesContext::new(self.engines)), } } NonDynamicEquality => match (&*left_info, &*right_info) { - // when a type alias is encountered, defer the decision to the type it contains (i.e. the - // type it aliases with) - (Alias { ty, .. }, _) => self.check_inner(ty.type_id, right), - (_, Alias { ty, .. }) => self.check_inner(left, ty.type_id), - // these cases are false because, unless left and right have the same // TypeId, they may later resolve to be different types in the type // engine diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/for_mismatch_pattern_type/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/for_mismatch_pattern_type/test.toml index b1175daf9b9..d5c2e8f9d2c 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/for_mismatch_pattern_type/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/for_mismatch_pattern_type/test.toml @@ -1,6 +1,7 @@ category = "fail" # check: $()for (_n,_m) in vector.iter() -# nextln: $()No method "unwrap(Option) -> (_, _)" found for type "Option". -# nextln: $()Matching method: -# nextln: $()unwrap(Option) -> u64 in Option +# nextln: $()Mismatched types. +# nextln: $()expected: (_, _) +# nextln: $()found: u64. +# nextln: $()help: Function return type does not match up with local type annotation. \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/generic_traits/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/generic_traits/test.toml index 7d6aa0ac218..92fb0cbb759 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/generic_traits/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/generic_traits/test.toml @@ -55,4 +55,4 @@ category = "fail" # check: $()let b = a.set(42); # check: $()No method "set(FooBarData, numeric) -> bool" found for type "FooBarData". -# check: $()Aborting due to 21 errors. +# check: $()Aborting due to 22 errors. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/generic_traits_bad_type_inference/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/generic_traits_bad_type_inference/test.toml index 7da98e8a5e3..6f6996bc726 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/generic_traits_bad_type_inference/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/generic_traits_bad_type_inference/test.toml @@ -1,6 +1,10 @@ category = "fail" # check: $()let _c = a.set_it(false); -# nextln: $()No method "set_it(FooBarData, bool)" found for type "FooBarData". -# nextln: $()Matching method: -# nextln: $()set_it(FooBarData, u8) -> FooBarData in FooBarData +# nextln: $()Mismatched types. +# nextln: $()expected: u8 +# nextln: $()found: bool. +# nextln: $()help: Function application argument type must match function parameter type. + +# check: $()let _c = a.set_it(false); +# nextln: $()This parameter was declared as type u8, but argument of type bool was provided. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/generics_in_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/generics_in_contract/test.toml index 86e23d2f128..4ba4bed68ea 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/generics_in_contract/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/generics_in_contract/test.toml @@ -1,11 +1,9 @@ category = "fail" # check: $()error -# nextln: generics_in_contract/src/main.sw:19:17 +# nextln: generics_in_contract/src/main.sw:19:22 # check: $()vec.push(item); -# check: $()No method "push(Vec, K)" found for type "Vec". -# nextln: $()Matching method: -# nextln: $()push(Vec, V) -> () in Vec +# check: $()This parameter was declared as type V, but argument of type K was provided. # check: $()error # nextln: generics_in_contract/src/main.sw:39:9 diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_shadowing_methods/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_shadowing_methods/test.toml index 5b95b05e2d6..dde5c71514e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_shadowing_methods/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_shadowing_methods/test.toml @@ -2,4 +2,4 @@ category = "fail" #check: $()impl Alias2 { #nextln: $()fn foo() {} -#nextln: $()Duplicate definitions for the method "foo" for type "Alias2". +#nextln: $()Duplicate definitions for the method "foo" for type "MyStruct1". diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_propagation/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/type_propagation/test.toml index 70a975a3606..073567615da 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/type_propagation/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_propagation/test.toml @@ -1,7 +1,13 @@ category = "fail" # check: $()a.push("str"); + +# check: $()a.push("str"); +# nextln: $()Mismatched types. +# nextln: $()expected: u8 +# nextln: $()found: str. +# nextln: $()help: Function application argument type must match function parameter type. + + # check: $()a.push("str"); -# nextln: $()No method "push(Vec, str)" found for type "Vec". -# nextln: $()Matching method: -# nextln: $()push(Vec, u8) -> () in Vec \ No newline at end of file +# nextln: $()This parameter was declared as type u8, but argument of type str was provided. \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/vec/test.toml index bf0719c436e..a49ba855183 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/vec/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec/test.toml @@ -1,16 +1,28 @@ category = "fail" # check: vector.push(false); -# nextln: $()No method "push(Vec, bool)" found for type "Vec". -# nextln: $()Matching method: -# nextln: $()push(Vec, u8) -> () in Vec +# nextln: $()Mismatched types. +# nextln: $()expected: u8 +# nextln: $()found: bool. +# nextln: $()help: Function application argument type must match function parameter type. + +# check: vector.push(false); +# nextln: $()This parameter was declared as type u8, but argument of type bool was provided. + +# check: vector.push("this should break it 1"); +# nextln: $()Mismatched types. +# nextln: $()expected: u8 +# nextln: $()found: str. +# nextln: $()help: Function application argument type must match function parameter type. # check: vector.push("this should break it 1"); -# nextln: $()No method "push(Vec, str)" found for type "Vec". -# nextln: $()Matching method: -# nextln: $()push(Vec, u8) -> () in Vec +# nextln: $()This parameter was declared as type u8, but argument of type str was provided. + +# check: vector.push("this should break it 2"); +# nextln: $()Mismatched types. +# nextln: $()expected: u8 +# nextln: $()found: str. +# nextln: $()help: Function application argument type must match function parameter type. # check: vector.push("this should break it 2"); -# nextln: $()No method "push(Vec, str)" found for type "Vec". -# nextln: $()Matching method: -# nextln: $()push(Vec, u8) -> () in Vec +# nextln: $()This parameter was declared as type u8, but argument of type str was provided.