From b59aae9b3e254b2633ed6abea2d3a71ad68febac Mon Sep 17 00:00:00 2001 From: Marcos Henrich Date: Fri, 20 Oct 2023 16:20:19 +0100 Subject: [PATCH] Fixes unexpected error while using generic Into trait. (#5211) ## Description Most of the changes in this commit are moving the TypeInfo extract methods into TypeId, this was required by the fix which requires to work with a TypeId instead of TypeInfo. This commit also improves multiple Display and Debug traits. The fix changes are in https://github.com/FuelLabs/sway/compare/esdrubal/fix_into_trait_error?expand=1#diff-1b4791151cae4e7986e4d18bcb97e88ec47dfb10231ed1cf50dd247fc5f0bc80R334-R341 After fixing this issue this one appeared #5208 Closes #5209 ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [ ] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. --- .../src/language/ty/declaration/function.rs | 10 +- .../ast_node/declaration/impl_trait.rs | 9 +- .../analysis/constructor_factory.rs | 2 +- .../semantic_analysis/namespace/trait_map.rs | 9 +- .../ast_elements/trait_constraint.rs | 21 ++ .../ast_elements/type_parameter.rs | 19 +- sway-core/src/type_system/id.rs | 241 ++++++++++++++++- sway-core/src/type_system/info.rs | 248 ++---------------- .../src/type_system/unify/occurs_check.rs | 4 +- sway-core/src/type_system/unify/unifier.rs | 6 +- .../src/type_system/unify/unify_check.rs | 8 +- sway-types/src/ident.rs | 8 +- .../generic_trait_constraints/src/main.sw | 42 ++- .../generic_trait_constraints/test.toml | 3 +- 14 files changed, 347 insertions(+), 283 deletions(-) diff --git a/sway-core/src/language/ty/declaration/function.rs b/sway-core/src/language/ty/declaration/function.rs index 5c86ac5c1fe..87d98dfab52 100644 --- a/sway-core/src/language/ty/declaration/function.rs +++ b/sway-core/src/language/ty/declaration/function.rs @@ -165,17 +165,11 @@ impl UnconstrainedTypeParameters for TyFunctionDecl { .map(|type_param| type_param.type_id) .collect(); all_types.extend(self.parameters.iter().flat_map(|param| { - let mut inner = type_engine - .get(param.type_argument.type_id) - .extract_inner_types(engines); + let mut inner = param.type_argument.type_id.extract_inner_types(engines); inner.insert(param.type_argument.type_id); inner })); - all_types.extend( - type_engine - .get(self.return_type.type_id) - .extract_inner_types(engines), - ); + all_types.extend(self.return_type.type_id.extract_inner_types(engines)); all_types.insert(self.return_type.type_id); let type_parameter_info = type_engine.get(type_parameter.type_id); all_types 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 a3639e073a5..8b2199d749f 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 @@ -1325,14 +1325,9 @@ fn check_for_unconstrained_type_parameters( // create a list of the generics in use in the impl signature let mut generics_in_use = HashSet::new(); for type_arg in trait_type_arguments.iter() { - generics_in_use.extend( - engines - .te() - .get(type_arg.type_id) - .extract_nested_generics(engines), - ); + generics_in_use.extend(type_arg.type_id.extract_nested_generics(engines)); } - generics_in_use.extend(engines.te().get(self_type).extract_nested_generics(engines)); + generics_in_use.extend(self_type.extract_nested_generics(engines)); // TODO: add a lookup in the trait constraints here and add it to // generics_in_use diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/constructor_factory.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/constructor_factory.rs index 71d6f00d36a..1737845e650 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/constructor_factory.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/constructor_factory.rs @@ -20,7 +20,7 @@ pub(crate) struct ConstructorFactory { impl ConstructorFactory { pub(crate) fn new(engines: &Engines, type_id: TypeId) -> Self { - let possible_types = engines.te().get(type_id).extract_nested_types(engines); + let possible_types = type_id.extract_nested_types(engines); ConstructorFactory { possible_types } } diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index bf5b936d1ed..00181eca7f3 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -591,13 +591,11 @@ impl TraitMap { /// `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) -> TraitMap { - let type_engine = engines.te(); - 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_engine.get(type_id).extract_inner_types(engines); + let mut all_types = type_id.extract_inner_types(engines); all_types.insert(type_id); let all_types = all_types.into_iter().collect::>(); self.filter_by_type_inner(engines, all_types, decider) @@ -666,8 +664,6 @@ impl TraitMap { type_id: TypeId, engines: &Engines, ) -> TraitMap { - let type_engine = engines.te(); - let unify_checker = UnifyCheck::constraint_subset(engines); let unify_checker_for_item_import = UnifyCheck::non_generic_constraint_subset(engines); @@ -676,8 +672,7 @@ impl TraitMap { 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); - let all_types = type_engine - .get(type_id) + let all_types = type_id .extract_inner_types(engines) .into_iter() .collect::>(); diff --git a/sway-core/src/type_system/ast_elements/trait_constraint.rs b/sway-core/src/type_system/ast_elements/trait_constraint.rs index e47fa84b89a..215c5432791 100644 --- a/sway-core/src/type_system/ast_elements/trait_constraint.rs +++ b/sway-core/src/type_system/ast_elements/trait_constraint.rs @@ -1,5 +1,6 @@ use std::{ cmp::Ordering, + fmt, hash::{Hash, Hasher}, }; @@ -56,6 +57,26 @@ impl OrdWithEngines for TraitConstraint { } } +impl DisplayWithEngines for TraitConstraint { + fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result { + write!(f, "{:?}", engines.help_out(self)) + } +} + +impl DebugWithEngines for TraitConstraint { + fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result { + let mut res = write!(f, "{}", self.trait_name); + if !self.type_arguments.is_empty() { + write!(f, "<")?; + for ty_arg in self.type_arguments.clone() { + write!(f, "{:?}", engines.help_out(ty_arg))?; + } + res = write!(f, ">"); + } + res + } +} + impl Spanned for TraitConstraint { fn span(&self) -> sway_types::Span { self.trait_name.span() 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 dc2cecb0161..7765398b117 100644 --- a/sway-core/src/type_system/ast_elements/type_parameter.rs +++ b/sway-core/src/type_system/ast_elements/type_parameter.rs @@ -106,12 +106,19 @@ impl Spanned for TypeParameter { impl DebugWithEngines for TypeParameter { fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result { - write!( - f, - "{}: {:?}", - self.name_ident, - engines.help_out(self.type_id) - ) + write!(f, "{}", self.name_ident)?; + if !self.trait_constraints.is_empty() { + write!( + f, + ":{}", + self.trait_constraints + .iter() + .map(|c| format!("{:?}", engines.help_out(c))) + .collect::>() + .join("+") + )?; + } + Ok(()) } } diff --git a/sway-core/src/type_system/id.rs b/sway-core/src/type_system/id.rs index abc3b64abe8..b9bab2c868b 100644 --- a/sway-core/src/type_system/id.rs +++ b/sway-core/src/type_system/id.rs @@ -10,7 +10,7 @@ use crate::{ }; use std::{ - collections::{BTreeSet, HashMap}, + collections::{BTreeSet, HashMap, HashSet}, fmt, }; @@ -88,7 +88,7 @@ impl UnconstrainedTypeParameters for TypeId { type_parameter: &TypeParameter, ) -> bool { let type_engine = engines.te(); - let mut all_types: BTreeSet = type_engine.get(*self).extract_inner_types(engines); + let mut all_types: BTreeSet = self.extract_inner_types(engines); all_types.insert(*self); let type_parameter_info = type_engine.get(type_parameter.type_id); all_types @@ -172,13 +172,243 @@ impl TypeId { { let type_engine = engines.te(); let type_info = type_engine.get(*self); - let mut found = type_info.extract_any(engines, filter_fn); + let mut found = self.extract_any(engines, filter_fn); if filter_fn(&type_info) { found.insert(*self, trait_constraints); } found } + pub(crate) fn extract_any( + &self, + engines: &Engines, + filter_fn: &F, + ) -> HashMap> + where + F: Fn(&TypeInfo) -> bool, + { + fn extend( + hashmap: &mut HashMap>, + hashmap_other: HashMap>, + ) { + for (type_id, trait_constraints) in hashmap_other { + if let Some(existing_trait_constraints) = hashmap.get_mut(&type_id) { + existing_trait_constraints.extend(trait_constraints); + } else { + hashmap.insert(type_id, trait_constraints); + } + } + } + + let decl_engine = engines.de(); + let mut found: HashMap> = HashMap::new(); + match engines.te().get(*self) { + TypeInfo::Unknown + | TypeInfo::Placeholder(_) + | TypeInfo::TypeParam(_) + | TypeInfo::StringArray(_) + | TypeInfo::StringSlice + | TypeInfo::UnsignedInteger(_) + | TypeInfo::RawUntypedPtr + | TypeInfo::RawUntypedSlice + | TypeInfo::Boolean + | TypeInfo::B256 + | TypeInfo::Numeric + | TypeInfo::Contract + | TypeInfo::ErrorRecovery(_) + | TypeInfo::TraitType { .. } => {} + TypeInfo::Enum(enum_ref) => { + let enum_decl = decl_engine.get_enum(&enum_ref); + for type_param in enum_decl.type_parameters.iter() { + extend( + &mut found, + type_param.type_id.extract_any_including_self( + engines, + filter_fn, + type_param.trait_constraints.clone(), + ), + ); + } + for variant in enum_decl.variants.iter() { + extend( + &mut found, + variant.type_argument.type_id.extract_any_including_self( + engines, + filter_fn, + vec![], + ), + ); + } + } + TypeInfo::Struct(struct_ref) => { + let struct_decl = decl_engine.get_struct(&struct_ref); + for type_param in struct_decl.type_parameters.iter() { + extend( + &mut found, + type_param.type_id.extract_any_including_self( + engines, + filter_fn, + type_param.trait_constraints.clone(), + ), + ); + } + for field in struct_decl.fields.iter() { + extend( + &mut found, + field.type_argument.type_id.extract_any_including_self( + engines, + filter_fn, + vec![], + ), + ); + } + } + TypeInfo::Tuple(elems) => { + for elem in elems.iter() { + extend( + &mut found, + elem.type_id + .extract_any_including_self(engines, filter_fn, vec![]), + ); + } + } + TypeInfo::ContractCaller { + abi_name: _, + address, + } => { + if let Some(address) = address { + extend( + &mut found, + address + .return_type + .extract_any_including_self(engines, filter_fn, vec![]), + ); + } + } + TypeInfo::Custom { + qualified_call_path: _, + type_arguments, + root_type_id: _, + } => { + if let Some(type_arguments) = type_arguments { + for type_arg in type_arguments.iter() { + extend( + &mut found, + type_arg + .type_id + .extract_any_including_self(engines, filter_fn, vec![]), + ); + } + } + } + TypeInfo::Array(ty, _) => { + extend( + &mut found, + ty.type_id + .extract_any_including_self(engines, filter_fn, vec![]), + ); + } + TypeInfo::Storage { fields } => { + for field in fields.iter() { + extend( + &mut found, + field.type_argument.type_id.extract_any_including_self( + engines, + filter_fn, + vec![], + ), + ); + } + } + TypeInfo::Alias { name: _, ty } => { + extend( + &mut found, + ty.type_id + .extract_any_including_self(engines, filter_fn, vec![]), + ); + } + TypeInfo::UnknownGeneric { + name: _, + trait_constraints, + } => { + found.insert(*self, trait_constraints.to_vec()); + for trait_constraint in trait_constraints.iter() { + for type_arg in trait_constraint.type_arguments.iter() { + extend( + &mut found, + type_arg + .type_id + .extract_any_including_self(engines, filter_fn, vec![]), + ); + } + } + } + TypeInfo::Ptr(ty) => { + extend( + &mut found, + ty.type_id + .extract_any_including_self(engines, filter_fn, vec![]), + ); + } + TypeInfo::Slice(ty) => { + extend( + &mut found, + ty.type_id + .extract_any_including_self(engines, filter_fn, vec![]), + ); + } + } + found + } + + /// Given a `TypeId` `self`, analyze `self` and return all inner + /// `TypeId`'s of `self`, not including `self`. + pub(crate) fn extract_inner_types(&self, engines: &Engines) -> BTreeSet { + fn filter_fn(_type_info: &TypeInfo) -> bool { + true + } + self.extract_any(engines, &filter_fn) + .keys() + .cloned() + .collect() + } + + pub(crate) fn extract_inner_types_with_trait_constraints( + &self, + engines: &Engines, + ) -> HashMap> { + fn filter_fn(_type_info: &TypeInfo) -> bool { + true + } + self.extract_any(engines, &filter_fn) + } + + /// Given a `TypeId` `self`, analyze `self` and return all nested + /// `TypeInfo`'s found in `self`, including `self`. + pub(crate) fn extract_nested_types(self, engines: &Engines) -> Vec { + let type_engine = engines.te(); + let mut inner_types: Vec = self + .extract_inner_types(engines) + .into_iter() + .map(|type_id| type_engine.get(type_id)) + .collect(); + inner_types.push(type_engine.get(self)); + inner_types + } + + pub(crate) fn extract_nested_generics<'a>( + &self, + engines: &'a Engines, + ) -> HashSet> { + let nested_types = (*self).extract_nested_types(engines); + HashSet::from_iter( + nested_types + .into_iter() + .filter(|x| matches!(x, TypeInfo::UnknownGeneric { .. })) + .map(|thing| WithEngines::new(thing, engines)), + ) + } + /// `check_type_parameter_bounds` does two types of checks. Lets use the example below for demonstrating the two checks: /// ```ignore /// enum MyEnum where T: MyAdd { @@ -208,10 +438,7 @@ impl TypeId { ) -> Result<(), ErrorEmitted> { let engines = ctx.engines(); - let mut structure_generics = engines - .te() - .get(*self) - .extract_inner_types_with_trait_constraints(engines); + let mut structure_generics = self.extract_inner_types_with_trait_constraints(engines); if !trait_constraints.is_empty() { structure_generics.insert(*self, trait_constraints); diff --git a/sway-core/src/type_system/info.rs b/sway-core/src/type_system/info.rs index b97716bf217..c28a13c5f34 100644 --- a/sway-core/src/type_system/info.rs +++ b/sway-core/src/type_system/info.rs @@ -13,7 +13,6 @@ use sway_types::{integer_bits::IntegerBits, span::Span, Spanned}; use std::{ cmp::Ordering, - collections::{BTreeSet, HashMap, HashSet}, fmt, hash::{Hash, Hasher}, }; @@ -579,9 +578,23 @@ impl DebugWithEngines for TypeInfo { Boolean => "bool".into(), Custom { qualified_call_path: call_path, + type_arguments, .. } => { - format!("unresolved {}", call_path.call_path.suffix.as_str()) + let mut s = "".to_string(); + if let Some(type_arguments) = type_arguments { + if !type_arguments.is_empty() { + s = format!( + "<{}>", + type_arguments + .iter() + .map(|a| format!("{:?}", engines.help_out(a))) + .collect::>() + .join(", ") + ); + } + } + format!("unresolved {}{}", call_path.call_path, s) } Tuple(fields) => { let field_strs = fields @@ -1080,28 +1093,6 @@ impl TypeInfo { } } - /// Given a `TypeInfo` `self`, analyze `self` and return all inner - /// `TypeId`'s of `self`, not including `self`. - pub(crate) fn extract_inner_types(&self, engines: &Engines) -> BTreeSet { - fn filter_fn(_type_info: &TypeInfo) -> bool { - true - } - self.extract_any(engines, &filter_fn) - .keys() - .cloned() - .collect() - } - - pub(crate) fn extract_inner_types_with_trait_constraints( - &self, - engines: &Engines, - ) -> HashMap> { - fn filter_fn(_type_info: &TypeInfo) -> bool { - true - } - self.extract_any(engines, &filter_fn) - } - /// Given a `TypeInfo` `self`, check to see if `self` is currently /// supported in match expressions, and return an error if it is not. pub(crate) fn expect_is_supported_in_match_expressions( @@ -1184,215 +1175,6 @@ impl TypeInfo { } } - /// Given a `TypeInfo` `self`, analyze `self` and return all nested - /// `TypeInfo`'s found in `self`, including `self`. - pub(crate) fn extract_nested_types(self, engines: &Engines) -> Vec { - let type_engine = engines.te(); - let mut inner_types: Vec = self - .extract_inner_types(engines) - .into_iter() - .map(|type_id| type_engine.get(type_id)) - .collect(); - inner_types.push(self); - inner_types - } - - pub(crate) fn extract_any( - &self, - engines: &Engines, - filter_fn: &F, - ) -> HashMap> - where - F: Fn(&TypeInfo) -> bool, - { - fn extend( - hashmap: &mut HashMap>, - hashmap_other: HashMap>, - ) { - for (type_id, trait_constraints) in hashmap_other { - if let Some(existing_trait_constraints) = hashmap.get_mut(&type_id) { - existing_trait_constraints.extend(trait_constraints); - } else { - hashmap.insert(type_id, trait_constraints); - } - } - } - - let decl_engine = engines.de(); - let mut found: HashMap> = HashMap::new(); - match self { - TypeInfo::Unknown - | TypeInfo::Placeholder(_) - | TypeInfo::TypeParam(_) - | TypeInfo::StringArray(_) - | TypeInfo::StringSlice - | TypeInfo::UnsignedInteger(_) - | TypeInfo::RawUntypedPtr - | TypeInfo::RawUntypedSlice - | TypeInfo::Boolean - | TypeInfo::B256 - | TypeInfo::Numeric - | TypeInfo::Contract - | TypeInfo::ErrorRecovery(_) - | TypeInfo::TraitType { .. } => {} - TypeInfo::Enum(enum_ref) => { - let enum_decl = decl_engine.get_enum(enum_ref); - for type_param in enum_decl.type_parameters.iter() { - extend( - &mut found, - type_param.type_id.extract_any_including_self( - engines, - filter_fn, - type_param.trait_constraints.clone(), - ), - ); - } - for variant in enum_decl.variants.iter() { - extend( - &mut found, - variant.type_argument.type_id.extract_any_including_self( - engines, - filter_fn, - vec![], - ), - ); - } - } - TypeInfo::Struct(struct_ref) => { - let struct_decl = decl_engine.get_struct(struct_ref); - for type_param in struct_decl.type_parameters.iter() { - extend( - &mut found, - type_param.type_id.extract_any_including_self( - engines, - filter_fn, - type_param.trait_constraints.clone(), - ), - ); - } - for field in struct_decl.fields.iter() { - extend( - &mut found, - field.type_argument.type_id.extract_any_including_self( - engines, - filter_fn, - vec![], - ), - ); - } - } - TypeInfo::Tuple(elems) => { - for elem in elems.iter() { - extend( - &mut found, - elem.type_id - .extract_any_including_self(engines, filter_fn, vec![]), - ); - } - } - TypeInfo::ContractCaller { - abi_name: _, - address, - } => { - if let Some(address) = address { - extend( - &mut found, - address - .return_type - .extract_any_including_self(engines, filter_fn, vec![]), - ); - } - } - TypeInfo::Custom { - qualified_call_path: _, - type_arguments, - root_type_id: _, - } => { - if let Some(type_arguments) = type_arguments { - for type_arg in type_arguments.iter() { - extend( - &mut found, - type_arg - .type_id - .extract_any_including_self(engines, filter_fn, vec![]), - ); - } - } - } - TypeInfo::Array(ty, _) => { - extend( - &mut found, - ty.type_id - .extract_any_including_self(engines, filter_fn, vec![]), - ); - } - TypeInfo::Storage { fields } => { - for field in fields.iter() { - extend( - &mut found, - field.type_argument.type_id.extract_any_including_self( - engines, - filter_fn, - vec![], - ), - ); - } - } - TypeInfo::Alias { name: _, ty } => { - extend( - &mut found, - ty.type_id - .extract_any_including_self(engines, filter_fn, vec![]), - ); - } - TypeInfo::UnknownGeneric { - name: _, - trait_constraints, - } => { - for trait_constraint in trait_constraints.iter() { - for type_arg in trait_constraint.type_arguments.iter() { - extend( - &mut found, - type_arg.type_id.extract_any_including_self( - engines, - filter_fn, - vec![trait_constraint.clone()], - ), - ); - } - } - } - TypeInfo::Ptr(ty) => { - extend( - &mut found, - ty.type_id - .extract_any_including_self(engines, filter_fn, vec![]), - ); - } - TypeInfo::Slice(ty) => { - extend( - &mut found, - ty.type_id - .extract_any_including_self(engines, filter_fn, vec![]), - ); - } - } - found - } - - pub(crate) fn extract_nested_generics<'a>( - &self, - engines: &'a Engines, - ) -> HashSet> { - let nested_types = self.clone().extract_nested_types(engines); - HashSet::from_iter( - nested_types - .into_iter() - .filter(|x| matches!(x, TypeInfo::UnknownGeneric { .. })) - .map(|thing| WithEngines::new(thing, engines)), - ) - } - /// Given a `TypeInfo` `self` and a list of `Ident`'s `subfields`, /// iterate through the elements of `subfields` as `subfield`, /// and recursively apply `subfield` to `self`. diff --git a/sway-core/src/type_system/unify/occurs_check.rs b/sway-core/src/type_system/unify/occurs_check.rs index 3c3f440dd5b..8aefcc6e7d9 100644 --- a/sway-core/src/type_system/unify/occurs_check.rs +++ b/sway-core/src/type_system/unify/occurs_check.rs @@ -30,8 +30,8 @@ impl<'a> OccursCheck<'a> { /// NOTE: This implementation assumes that `other` =/ `generic`, in which /// case the occurs check would return `false`, as this is a valid /// unification. - pub(super) fn check(&self, generic: TypeInfo, other: &TypeInfo) -> bool { + pub(super) fn check(&self, generic: TypeId, other: TypeId) -> bool { let other_generics = other.extract_nested_generics(self.engines); - other_generics.contains(&self.engines.help_out(generic)) + other_generics.contains(&self.engines.help_out(self.engines.te().get(generic))) } } diff --git a/sway-core/src/type_system/unify/unifier.rs b/sway-core/src/type_system/unify/unifier.rs index d6a2b34affc..86ce582d466 100644 --- a/sway-core/src/type_system/unify/unifier.rs +++ b/sway-core/src/type_system/unify/unifier.rs @@ -159,10 +159,10 @@ impl<'a> Unifier<'a> { }, ) if rn.as_str() == en.as_str() && rtc.eq(&etc, self.engines) => (), - (r @ UnknownGeneric { .. }, e) if !self.occurs_check(r.clone(), &e) => { + (r @ UnknownGeneric { .. }, e) if !self.occurs_check(received, expected) => { self.replace_received_with_expected(handler, received, expected, &r, e, span) } - (r, e @ UnknownGeneric { .. }) if !self.occurs_check(e.clone(), &r) => { + (r, e @ UnknownGeneric { .. }) if !self.occurs_check(expected, received) => { self.replace_expected_with_received(handler, received, expected, r, &e, span) } @@ -270,7 +270,7 @@ impl<'a> Unifier<'a> { } } - fn occurs_check(&self, generic: TypeInfo, other: &TypeInfo) -> bool { + fn occurs_check(&self, generic: TypeId, other: TypeId) -> bool { OccursCheck::new(self.engines).check(generic, other) } diff --git a/sway-core/src/type_system/unify/unify_check.rs b/sway-core/src/type_system/unify/unify_check.rs index 14059e404c8..e5b3643deaf 100644 --- a/sway-core/src/type_system/unify/unify_check.rs +++ b/sway-core/src/type_system/unify/unify_check.rs @@ -348,7 +348,9 @@ impl<'a> UnifyCheck<'a> { ) => ln == rn && rtc.eq(<c, self.engines), // any type can be coerced into a generic, // except if the type already contains the generic - (e, g @ UnknownGeneric { .. }) => !OccursCheck::new(self.engines).check(g, &e), + (_e, _g @ UnknownGeneric { .. }) => { + !OccursCheck::new(self.engines).check(right, left) + } // Let empty enums to coerce to any other type. This is useful for Never enum. (Enum(r_decl_ref), _) @@ -443,7 +445,9 @@ impl<'a> UnifyCheck<'a> { ) => rtc.eq(<c, self.engines), // any type can be coerced into a generic, // except if the type already contains the generic - (e, g @ UnknownGeneric { .. }) => !OccursCheck::new(self.engines).check(g, &e), + (_e, _g @ UnknownGeneric { .. }) => { + !OccursCheck::new(self.engines).check(right, left) + } (Enum(l_decl_ref), Enum(r_decl_ref)) => { let l_decl = self.engines.de().get_enum(&l_decl_ref); diff --git a/sway-types/src/ident.rs b/sway-types/src/ident.rs index 20b65b38579..8ad4fdec646 100644 --- a/sway-types/src/ident.rs +++ b/sway-types/src/ident.rs @@ -12,7 +12,7 @@ pub trait Named { fn name(&self) -> &BaseIdent; } -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct BaseIdent { name_override_opt: Option, span: Span, @@ -133,6 +133,12 @@ impl fmt::Display for Ident { } } +impl fmt::Debug for Ident { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "{}", self.as_str()) + } +} + /// An [IdentUnique] is an _identifier_ with a corresponding `span` from which it was derived. /// Its hash and equality implementation takes the full span into account, meaning that identifiers /// are considered unique if they originate from different files. diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_trait_constraints/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_trait_constraints/src/main.sw index 417b65ffa6a..a0efb458480 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_trait_constraints/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_trait_constraints/src/main.sw @@ -19,13 +19,45 @@ impl MyAdd for u64 { } } +pub trait MyFrom { + fn from(b: T) -> Self; +} + + +pub trait MyInto { + fn into(self) -> T; +} + + +impl MyInto for T +where + U: MyFrom, +{ + fn into(self) -> U { + U::from(self) + } +} + +struct Struct3 { + data: u64, +} + +impl MyFrom for Struct3 { + fn from(i: u64) -> Struct3 { + Struct3 {data: i} + } +} + fn main() -> bool { - let s = Struct {data: 1_u64 }; - assert_eq(s.data.my_add(1,2),3); + let s1 = Struct {data: 1_u64 }; + assert_eq(s1.data.my_add(1,2),3); + + let s2 = Struct2 {data_a: 1_u64, data_b: 1_u64 }; + assert_eq(s2.data_a.my_add(1,2),3); + assert_eq(s2.data_b.my_add(1,2),3); - let s = Struct2 {data_a: 1_u64, data_b: 1_u64 }; - assert_eq(s.data_a.my_add(1,2),3); - assert_eq(s.data_b.my_add(1,2),3); + // TODO Uncomment this after #5208 is fixed + //let _i: Struct3 = 1_u64.into(); true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_trait_constraints/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_trait_constraints/test.toml index 4c8fb54e539..f9ad984775d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_trait_constraints/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_trait_constraints/test.toml @@ -1,3 +1,4 @@ category = "run" expected_result = { action = "return", value = 1 } -validate_abi = false \ No newline at end of file +validate_abi = false +expected_warnings = 3 \ No newline at end of file