diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index 74d8b2ace33..0d280f2c743 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -237,7 +237,7 @@ impl TraitMap { is_extending_existing_impl: IsExtendingExistingImpl, engines: &Engines, ) -> Result<(), ErrorEmitted> { - let type_id = engines.te().get_unaliased_type_id(type_id); + let unaliased_type_id = engines.te().get_unaliased_type_id(type_id); handler.scope(|handler| { let mut trait_items: TraitItems = HashMap::new(); @@ -267,7 +267,7 @@ impl TraitMap { } } - let trait_impls = self.get_impls_mut(engines, type_id); + let trait_impls = self.get_impls_mut(engines, unaliased_type_id); // check to see if adding this trait will produce a conflicting definition for TraitEntry { @@ -295,11 +295,11 @@ impl TraitMap { let unify_checker = UnifyCheck::non_generic_constraint_subset(engines); - // Types are subset if the `type_id` that we want to insert can unify with the + // Types are subset if the `unaliased_type_id` that we want to insert can unify with the // existing `map_type_id`. In addition we need to additionally check for the case of // `&mut ` and `&`. - let types_are_subset = unify_checker.check(type_id, *map_type_id) - && is_unified_type_subset(engines.te(), type_id, *map_type_id); + let types_are_subset = unify_checker.check(unaliased_type_id, *map_type_id) + && is_unified_type_subset(engines.te(), unaliased_type_id, *map_type_id); /// `left` can unify into `right`. Additionally we need to check subset condition in case of /// [TypeInfo::Ref] types. Although `&mut ` can unify with `&` @@ -367,7 +367,8 @@ impl TraitMap { { handler.emit_err(CompileError::ConflictingImplsForTraitAndType { trait_name: trait_name.to_string_with_args(engines, &trait_type_args), - type_implementing_for: engines.help_out(type_id).to_string(), + type_implementing_for: engines.help_out(unaliased_type_id).to_string(), + type_implementing_for_alias: engines.help_out(type_id).to_string(), existing_impl_span: existing_impl_span.clone(), second_impl_span: impl_span.clone(), }); @@ -442,7 +443,7 @@ impl TraitMap { trait_name, impl_span.clone(), trait_decl_span, - type_id, + unaliased_type_id, trait_items, engines, ); diff --git a/sway-error/src/error.rs b/sway-error/src/error.rs index 349ac2fc7a3..f0f613b7d4f 100644 --- a/sway-error/src/error.rs +++ b/sway-error/src/error.rs @@ -621,10 +621,11 @@ pub enum CompileError { }, #[error("An ABI can only be implemented for the `Contract` type, so this implementation of an ABI for type \"{ty}\" is invalid.")] ImplAbiForNonContract { span: Span, ty: String }, - #[error("Conflicting implementations of trait \"{trait_name}\" for type \"{type_implementing_for}\".")] + #[error("Conflicting implementations of trait \"{trait_name}\" for type \"{type_implementing_for_alias}\".")] ConflictingImplsForTraitAndType { trait_name: String, type_implementing_for: String, + type_implementing_for_alias: String, existing_impl_span: Span, second_impl_span: Span, }, @@ -2317,20 +2318,30 @@ impl ToDiagnostic for CompileError { format!("{}- referencing a mutable copy of \"{decl_name}\", by returning it from a block: `&mut {{ {decl_name} }}`.", Indent::Single) ], }, - ConflictingImplsForTraitAndType { trait_name, type_implementing_for, existing_impl_span, second_impl_span } => Diagnostic { + ConflictingImplsForTraitAndType { trait_name, type_implementing_for, type_implementing_for_alias, existing_impl_span, second_impl_span } => Diagnostic { reason: Some(Reason::new(code(1), "Trait is already implemented for type".to_string())), issue: Issue::error( source_engine, second_impl_span.clone(), - format!("Trait \"{trait_name}\" is already implemented for type \"{type_implementing_for}\".") + if type_implementing_for == type_implementing_for_alias { + format!("Trait \"{trait_name}\" is already implemented for type \"{type_implementing_for}\".") + } else { + format!("Trait \"{trait_name}\" is already implemented for type \"{type_implementing_for_alias}\", possibly using an alias (the unaliased type name is \"{type_implementing_for}\").") + } ), hints: vec![ Hint::info( source_engine, existing_impl_span.clone(), - format!("This is the already existing implementation of \"{}\" for \"{type_implementing_for}\".", - call_path_suffix_with_args(trait_name) - ) + if type_implementing_for == type_implementing_for_alias { + format!("This is the already existing implementation of \"{}\" for \"{type_implementing_for}\".", + call_path_suffix_with_args(trait_name) + ) + } else { + format!("This is the already existing implementation of \"{}\" for \"{type_implementing_for_alias}\", possibly using an alias (the unaliased type name is \"{type_implementing_for}\").", + call_path_suffix_with_args(trait_name) + ) + } ), ], help: vec![ diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/Forc.lock new file mode 100644 index 00000000000..1f8113a3e20 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "core" +source = "path+from-root-6E306998EEBF0DBD" + +[[package]] +name = "std" +source = "path+from-root-6E306998EEBF0DBD" +dependencies = ["core"] + +[[package]] +name = "type_alias_unification" +source = "member" +dependencies = ["std"] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/Forc.toml new file mode 100644 index 00000000000..c3ac933766e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "type_alias_unification" + +[dependencies] +std = { path = "../../../reduced_std_libs/sway-lib-std-assert" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/src/main.sw new file mode 100644 index 00000000000..10a35a26f0b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/src/main.sw @@ -0,0 +1,32 @@ +script; + +trait MyTrait { + fn extract_a(self) -> u64; +} + +struct A { + a: u64, +} + +impl MyTrait for A { + fn extract_a(self) -> u64 { + self.a + } +} + +type B = A; + +// B is an alias for A, and A already has an implementation of MyTrait, +// so this should cause a compilation error. +impl MyTrait for B { + fn extract_a(self) -> u64 { + self.a + 1 + } +} + +fn main() { + let struct_a = A { a: 1 }; + let struct_b = B { a: 42 }; + assert(struct_a.extract_a() == 1); + assert(struct_b.extract_a() == 42); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/test.toml new file mode 100644 index 00000000000..696197d0e7a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_alias_unification/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +#not: $()error \ No newline at end of file