From d8a584d59cb7d75afc54ea38f8d906d8d82e6f7b Mon Sep 17 00:00:00 2001 From: tritao Date: Mon, 20 Jan 2025 11:46:07 +0000 Subject: [PATCH 1/2] Improved trait coherence checking --- sway-core/src/language/ty/program.rs | 25 +++++++++++ .../semantic_analysis/namespace/trait_map.rs | 43 +++++++++++++++++++ .../impl_self_overlap/Forc.lock | 8 ++++ .../impl_self_overlap/Forc.toml | 9 ++++ .../impl_self_overlap/src/lib.sw | 8 ++++ .../impl_self_overlap/test.toml | 3 ++ .../impl_self_overlap_lib/Forc.lock | 3 ++ .../impl_self_overlap_lib/Forc.toml | 6 +++ .../impl_self_overlap_lib/src/lib.sw | 10 +++++ .../trait_impl_constraint_overlap/Forc.lock | 3 ++ .../trait_impl_constraint_overlap/Forc.toml | 6 +++ .../trait_impl_constraint_overlap/src/lib.sw | 24 +++++++++++ .../trait_impl_constraint_overlap/test.toml | 3 ++ .../trait_impl_overlap/Forc.lock | 3 ++ .../trait_impl_overlap/Forc.toml | 6 +++ .../trait_impl_overlap/src/lib.sw | 29 +++++++++++++ .../trait_impl_overlap/test.toml | 3 ++ 17 files changed, 192 insertions(+) create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/test.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/test.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/test.toml diff --git a/sway-core/src/language/ty/program.rs b/sway-core/src/language/ty/program.rs index 82987399e40..0ac68abc09a 100644 --- a/sway-core/src/language/ty/program.rs +++ b/sway-core/src/language/ty/program.rs @@ -423,6 +423,31 @@ impl TyProgram { } } + // check trait overlap + let mut unified_trait_map = root + .namespace + .root_module() + .root_lexical_scope() + .items + .implemented_traits + .clone(); + unified_trait_map.check_overlap_and_extend(handler, unified_trait_map.clone(), engines)?; + + for (_, submodule) in root.submodules.iter() { + unified_trait_map.check_overlap_and_extend( + handler, + submodule + .module + .namespace + .module(engines) + .root_lexical_scope() + .items + .implemented_traits + .clone(), + engines, + )?; + } + Ok((typed_program_kind, declarations, configurables)) } diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index 6551432e7b0..3fc9b0ecb75 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -631,6 +631,49 @@ impl TraitMap { } } + /// Given [TraitMap]s `self` and `other`, checks for overlaps between `self` and `other`. + /// If no overlaps are found extends `self` with `other`. + pub(crate) fn check_overlap_and_extend( + &mut self, + handler: &Handler, + other: TraitMap, + engines: &Engines, + ) -> Result<(), ErrorEmitted> { + let mut overlap_err = None; + let unify_check = UnifyCheck::coercion(engines); + let mut keys = self.trait_impls.keys().clone().collect::>(); + keys.sort(); + for key in keys { + for self_entry in self.trait_impls[key].iter() { + for other_entry in other.get_impls(engines, self_entry.key.type_id, true) { + if self_entry.key.name.eq( + &*other_entry.key.name, + &PartialEqWithEnginesContext::new(engines), + ) && self_entry.value.impl_span != other_entry.value.impl_span + && unify_check.check(self_entry.key.type_id, other_entry.key.type_id) + { + handler.emit_err(CompileError::InternalOwned( + format!("Overlapped types"), + self_entry.value.impl_span.clone(), + )); + overlap_err = Some(handler.emit_err(CompileError::InternalOwned( + format!("Overlapped types"), + other_entry.value.impl_span.clone(), + ))); + } + } + } + } + + if let Some(overlap_err) = overlap_err { + return Err(overlap_err); + } + + self.extend(other, engines); + + Ok(()) + } + /// Filters the entries in `self` and return a new [TraitMap] with all of /// the entries from `self` that implement a trait from the declaration with that span. pub(crate) fn filter_by_trait_decl_span(&self, trait_decl_span: Span) -> TraitMap { diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/Forc.lock new file mode 100644 index 00000000000..cf2f76b32e0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "impl_self_overlap" +source = "member" +dependencies = ["impl_self_overlap_lib"] + +[[package]] +name = "impl_self_overlap_lib" +source = "path+from-root-42AAAB4D17F2A85B" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/Forc.toml new file mode 100644 index 00000000000..332b01f2549 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/Forc.toml @@ -0,0 +1,9 @@ +[project] +name = "impl_self_overlap" +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +implicit-std = false + +[dependencies] +impl_self_overlap_lib = { path = "../impl_self_overlap_lib" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/src/lib.sw new file mode 100644 index 00000000000..3949828e006 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/src/lib.sw @@ -0,0 +1,8 @@ +library; + +use impl_self_overlap_lib::MyType; + +impl MyType { + fn do_something(self) { + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/test.toml new file mode 100644 index 00000000000..7d5c3d82c03 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +#check: $()Duplicate definitions for the method "do_something" for type "MyType". diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/Forc.lock new file mode 100644 index 00000000000..a4b80ddd4dd --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "impl_self_overlap_lib" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/Forc.toml new file mode 100644 index 00000000000..74da5ffe02a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/Forc.toml @@ -0,0 +1,6 @@ +[project] +name = "impl_self_overlap_lib" +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/src/lib.sw new file mode 100644 index 00000000000..924622a4f0c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/impl_self_overlap_lib/src/lib.sw @@ -0,0 +1,10 @@ +library; + +pub struct MyType {} + +impl MyType { + fn do_something(self) { + } +} + + diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/Forc.lock new file mode 100644 index 00000000000..b16420cdf3a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "trait_impl_constraint_overlap" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/Forc.toml new file mode 100644 index 00000000000..5d9703e2a47 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/Forc.toml @@ -0,0 +1,6 @@ +[project] +name = "trait_impl_constraint_overlap" +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/src/lib.sw new file mode 100644 index 00000000000..717da6010aa --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/src/lib.sw @@ -0,0 +1,24 @@ +library; + +trait Cat { + fn speak(self) -> u64; +} + +struct S { + x: T, +} + +impl S +where +Z: Cat, +{ + fn foo(self) -> u64 { + 1 + } +} + +impl S { + fn foo(self) -> u64 { + 1 + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/test.toml new file mode 100644 index 00000000000..1e8f67fadea --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +#check: $()Internal compiler error: Overlapped types diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/Forc.lock new file mode 100644 index 00000000000..7966af218fd --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "trait_impl_overlap" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/Forc.toml new file mode 100644 index 00000000000..a3f089d1c56 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/Forc.toml @@ -0,0 +1,6 @@ +[project] +name = "trait_impl_overlap" +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/src/lib.sw new file mode 100644 index 00000000000..5a7390d2978 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/src/lib.sw @@ -0,0 +1,29 @@ +library; + +// Define two traits +trait TraitA { + fn do_something(self); +} + +trait TraitB { + fn do_something_else(self); +} + +// Implement both traits for a concrete type +struct MyType {} + +impl TraitA for MyType { + fn do_something(self) { + } +} + +impl TraitB for MyType { + fn do_something_else(self) { + } +} + +// Blanket implementation that may cause overlap +impl TraitA for T { + fn do_something(self) { + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/test.toml new file mode 100644 index 00000000000..1e8f67fadea --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +#check: $()Internal compiler error: Overlapped types From ec4e13e626e7a77754c38f41d0a5d3d58989efa4 Mon Sep 17 00:00:00 2001 From: tritao Date: Mon, 27 Jan 2025 10:40:37 +0000 Subject: [PATCH 2/2] Adds improved coherence checking for trait constraints. --- .../ast_node/declaration/abi.rs | 1 + .../ast_node/declaration/auto_impl.rs | 1 + .../ast_node/declaration/declaration.rs | 2 + .../ast_node/declaration/impl_trait.rs | 2 + .../ast_node/declaration/trait.rs | 3 + .../ast_node/expression/typed_expression.rs | 1 + .../semantic_analysis/namespace/trait_map.rs | 128 ++++++++++++++++-- .../semantic_analysis/type_check_context.rs | 15 +- .../src/type_system/substitute/subst_map.rs | 4 + .../trait_impl_constraint_overlap/src/lib.sw | 6 + .../trait_impl_constraint_overlap/test.toml | 2 +- .../trait_impl_constraint_overlap2/Forc.lock | 3 + .../trait_impl_constraint_overlap2/Forc.toml | 6 + .../trait_impl_constraint_overlap2/src/lib.sw | 31 +++++ .../trait_impl_constraint_overlap2/test.toml | 3 + .../trait_impl_constraint_overlap3/Forc.lock | 3 + .../trait_impl_constraint_overlap3/Forc.toml | 6 + .../trait_impl_constraint_overlap3/src/lib.sw | 38 ++++++ .../trait_impl_constraint_overlap3/test.toml | 3 + .../trait_impl_overlap/test.toml | 2 +- .../Forc.lock | 3 + .../Forc.toml | 6 + .../src/lib.sw | 24 ++++ .../test.toml | 2 + .../Forc.lock | 3 + .../Forc.toml | 6 + .../src/lib.sw | 32 +++++ .../test.toml | 2 + 28 files changed, 323 insertions(+), 15 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/test.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/test.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/test.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/test.toml 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..e0f9b32d824 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/abi.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/abi.rs @@ -429,6 +429,7 @@ impl ty::TyAbiDecl { CallPath::ident_to_fullpath(self.name.clone(), ctx.namespace()), vec![], type_id, + vec![], &all_items, &self.span, Some(self.span()), diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs index 735484b8479..e4f696868d9 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs @@ -416,6 +416,7 @@ where impl_trait.trait_name.clone(), impl_trait.trait_type_arguments.clone(), impl_trait.implementing_for.type_id, + impl_trait.impl_type_parameters.clone(), &impl_trait.items, &impl_trait.span, impl_trait diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs b/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs index 32f1d1cf63f..f4b497d15a4 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs @@ -243,6 +243,7 @@ impl TyDecl { impl_trait.trait_name.clone(), impl_trait.trait_type_arguments.clone(), impl_trait.implementing_for.type_id, + impl_trait.impl_type_parameters.clone(), &impl_trait.items, &impl_trait.span, impl_trait @@ -310,6 +311,7 @@ impl TyDecl { impl_trait.trait_name.clone(), impl_trait.trait_type_arguments.clone(), impl_trait.implementing_for.type_id, + impl_trait.impl_type_parameters.clone(), impl_trait_items, &impl_trait.span, impl_trait 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..8ebfb993908 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 @@ -504,6 +504,7 @@ impl TyImplSelfOrTrait { impl_trait.trait_name.clone(), impl_trait.trait_type_arguments.clone(), impl_trait.implementing_for.type_id, + impl_trait.impl_type_parameters.clone(), &impl_trait.items, &impl_trait.span, impl_trait @@ -764,6 +765,7 @@ fn type_check_trait_implementation( trait_name.clone(), trait_type_arguments.to_vec(), implementing_for, + impl_type_parameters.to_vec(), &this_supertrait_impld_method_refs .values() .cloned() diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/trait.rs b/sway-core/src/semantic_analysis/ast_node/declaration/trait.rs index 88fdedc1c63..5f53638f6e8 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/trait.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/trait.rs @@ -167,6 +167,7 @@ impl TyTraitDecl { CallPath::ident_to_fullpath(name.clone(), ctx.namespace), new_type_parameters.iter().map(|x| x.into()).collect(), self_type, + vec![], &dummy_interface_surface, &span, None, @@ -235,6 +236,7 @@ impl TyTraitDecl { CallPath::ident_to_fullpath(name.clone(), ctx.namespace()), new_type_parameters.iter().map(|x| x.into()).collect(), self_type, + vec![], &dummy_interface_surface, &span, None, @@ -614,6 +616,7 @@ impl TyTraitDecl { trait_name.clone(), type_arguments.to_vec(), type_id, + vec![], &all_items, &trait_name.span(), Some(self.span()), diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 65d645dffed..8b776f3ad4b 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -1987,6 +1987,7 @@ impl ty::TyExpression { abi_name.clone(), vec![], return_type, + vec![], &abi_items, span, Some(span.clone()), diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index 3fc9b0ecb75..a258fda7103 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -24,7 +24,7 @@ use crate::{ }, type_system::{SubstTypes, TypeId}, IncludeSelf, SubstTypesContext, TraitConstraint, TypeArgument, TypeEngine, TypeInfo, - TypeSubstMap, UnifyCheck, + TypeParameter, TypeSubstMap, UnifyCheck, }; use super::Module; @@ -102,14 +102,18 @@ type TraitName = Arc>; struct TraitKey { name: TraitName, type_id: TypeId, + impl_type_parameters: Vec, trait_decl_span: Option, } impl OrdWithEngines for TraitKey { fn cmp(&self, other: &Self, ctx: &OrdWithEnginesContext) -> std::cmp::Ordering { - self.name - .cmp(&other.name, ctx) - .then_with(|| self.type_id.cmp(&other.type_id)) + self.name.cmp(&other.name, ctx).then_with(|| { + self.type_id.cmp(&other.type_id).then_with(|| { + self.impl_type_parameters + .cmp(&other.impl_type_parameters, ctx) + }) + }) } } @@ -221,6 +225,7 @@ impl TraitMap { trait_name: CallPath, trait_type_args: Vec, type_id: TypeId, + impl_type_parameters: Vec, items: &[ResolvedTraitImplItem], impl_span: &Span, trait_decl_span: Option, @@ -265,6 +270,7 @@ impl TraitMap { name: map_trait_name, type_id: map_type_id, trait_decl_span: _, + impl_type_parameters: _, }, value: TraitValue { @@ -448,6 +454,7 @@ impl TraitMap { impl_span.clone(), trait_decl_span, type_id, + impl_type_parameters, trait_items, engines, ); @@ -462,6 +469,7 @@ impl TraitMap { impl_span: Span, trait_decl_span: Option, type_id: TypeId, + impl_type_parameters: Vec, trait_methods: TraitItems, engines: &Engines, ) { @@ -469,6 +477,7 @@ impl TraitMap { name: trait_name, type_id, trait_decl_span, + impl_type_parameters, }; let value = TraitValue { trait_items: trait_methods, @@ -631,6 +640,34 @@ impl TraitMap { } } + fn get_traits_types( + &self, + traits_types: &mut HashMap>, + ) -> Result<(), ErrorEmitted> { + let mut keys = self.trait_impls.keys().clone().collect::>(); + keys.sort(); + for key in keys { + for self_entry in self.trait_impls[key].iter() { + let callpath = CallPath { + prefixes: self_entry.key.name.prefixes.clone(), + suffix: self_entry.key.name.suffix.name.clone(), + is_absolute: self_entry.key.name.is_absolute, + }; + println!( + "get_traits_types {:?} {}", + callpath, + self_entry.key.type_id.index() + ); + if let Some(vec) = traits_types.get_mut(&callpath) { + vec.push(self_entry.key.type_id); + } else { + traits_types.insert(callpath, vec![self_entry.key.type_id]); + } + } + } + Ok(()) + } + /// Given [TraitMap]s `self` and `other`, checks for overlaps between `self` and `other`. /// If no overlaps are found extends `self` with `other`. pub(crate) fn check_overlap_and_extend( @@ -643,8 +680,20 @@ impl TraitMap { let unify_check = UnifyCheck::coercion(engines); let mut keys = self.trait_impls.keys().clone().collect::>(); keys.sort(); + let mut traits_types = HashMap::>::new(); + self.get_traits_types(&mut traits_types)?; + other.get_traits_types(&mut traits_types)?; + for key in keys { for self_entry in self.trait_impls[key].iter() { + let self_tcs = self_entry + .key + .impl_type_parameters + .iter() + .map(|tp| tp.trait_constraints.iter().map(|tc| (tc, tp.clone()))) + .flatten() + .collect::>(); + for other_entry in other.get_impls(engines, self_entry.key.type_id, true) { if self_entry.key.name.eq( &*other_entry.key.name, @@ -652,14 +701,66 @@ impl TraitMap { ) && self_entry.value.impl_span != other_entry.value.impl_span && unify_check.check(self_entry.key.type_id, other_entry.key.type_id) { - handler.emit_err(CompileError::InternalOwned( - format!("Overlapped types"), - self_entry.value.impl_span.clone(), - )); - overlap_err = Some(handler.emit_err(CompileError::InternalOwned( - format!("Overlapped types"), - other_entry.value.impl_span.clone(), - ))); + let other_tcs = other_entry + .key + .impl_type_parameters + .iter() + .map(|tp| tp.trait_constraints.iter().map(|tc| (tc, tp.clone()))) + .flatten() + .collect::>(); + let other_tcs_satisfied = other_tcs.iter().all(|(tc, tp)| { + if let Some(tc_type_ids) = traits_types.get(&tc.trait_name) { + tc_type_ids.iter().any(|tc_type_id| { + let mut type_mapping = TypeSubstMap::new(); + type_mapping.insert(tp.type_id, *tc_type_id); + let mut type_id = other_entry.key.type_id; + type_id.subst(&SubstTypesContext::new( + engines, + &type_mapping, + false, + )); + unify_check.check(self_entry.key.type_id, type_id) + }) + } else { + false + } + }); + + let self_tcs_satisfied = self_tcs.iter().all(|(tc, tp)| { + if let Some(tc_type_ids) = traits_types.get(&tc.trait_name) { + tc_type_ids.iter().any(|tc_type_id| { + let mut type_mapping = TypeSubstMap::new(); + type_mapping.insert(tp.type_id, *tc_type_id); + let mut type_id = self_entry.key.type_id; + type_id.subst(&SubstTypesContext::new( + engines, + &type_mapping, + false, + )); + unify_check.check(other_entry.key.type_id, type_id) + }) + } else { + false + } + }); + + if other_tcs_satisfied && self_tcs_satisfied { + for (trait_item_name1, _) in self_entry.value.trait_items.clone() { + for (trait_item_name2, _) in other_entry.value.trait_items.clone() { + if trait_item_name1 == trait_item_name2 { + handler.emit_err(CompileError::InternalOwned( + format!("Overlapped item {}", trait_item_name1), + self_entry.value.impl_span.clone(), + )); + overlap_err = + Some(handler.emit_err(CompileError::InternalOwned( + format!("Overlapped item {}", trait_item_name1), + other_entry.value.impl_span.clone(), + ))); + } + } + } + } } } } @@ -939,6 +1040,7 @@ impl TraitMap { name: map_trait_name, type_id: map_type_id, trait_decl_span: map_trait_decl_span, + impl_type_parameters: map_impl_type_parameters, }, value: TraitValue { @@ -954,6 +1056,7 @@ impl TraitMap { impl_span.clone(), map_trait_decl_span.clone(), *type_id, + map_impl_type_parameters.clone(), map_trait_items.clone(), engines, ); @@ -1052,6 +1155,7 @@ impl TraitMap { impl_span.clone(), map_trait_decl_span.clone(), *type_id, + map_impl_type_parameters.clone(), trait_items, engines, ); diff --git a/sway-core/src/semantic_analysis/type_check_context.rs b/sway-core/src/semantic_analysis/type_check_context.rs index fb68d4e6ca6..a61d49e378a 100644 --- a/sway-core/src/semantic_analysis/type_check_context.rs +++ b/sway-core/src/semantic_analysis/type_check_context.rs @@ -19,7 +19,8 @@ use crate::{ Namespace, }, type_system::{SubstTypes, TypeArgument, TypeId, TypeInfo}, - EnforceTypeArguments, SubstTypesContext, TraitConstraint, TypeSubstMap, UnifyCheck, + EnforceTypeArguments, SubstTypesContext, TraitConstraint, TypeParameter, TypeSubstMap, + UnifyCheck, }; use sway_error::{ error::CompileError, @@ -1292,6 +1293,7 @@ impl<'a> TypeCheckContext<'a> { trait_name: CallPath, trait_type_args: Vec, type_id: TypeId, + mut impl_type_parameters: Vec, items: &[ty::TyImplItem], impl_span: &Span, trait_decl_span: Option, @@ -1299,6 +1301,16 @@ impl<'a> TypeCheckContext<'a> { is_extending_existing_impl: IsExtendingExistingImpl, ) -> Result<(), ErrorEmitted> { let engines = self.engines; + + // Use trait name with full path, improves consistency between + // this inserting and getting in `get_methods_for_type_and_trait_name`. + let full_trait_name = trait_name.to_fullpath(self.engines(), self.namespace()); + impl_type_parameters.iter_mut().for_each(|tp| { + tp.trait_constraints.iter_mut().for_each(|tc| { + tc.trait_name = tc.trait_name.to_fullpath(self.engines(), self.namespace()) + }) + }); + // CallPath::to_fullpath gives a resolvable path, but is not guaranteed to provide the path // to the actual trait declaration. Since the path of the trait declaration is used as a key // in the trait map, we need to find the actual declaration path. @@ -1317,6 +1329,7 @@ impl<'a> TypeCheckContext<'a> { canonical_trait_path, trait_type_args, type_id, + impl_type_parameters, &items, impl_span, trait_decl_span, diff --git a/sway-core/src/type_system/substitute/subst_map.rs b/sway-core/src/type_system/substitute/subst_map.rs index 36de9b4846a..d7d71b98633 100644 --- a/sway-core/src/type_system/substitute/subst_map.rs +++ b/sway-core/src/type_system/substitute/subst_map.rs @@ -66,6 +66,10 @@ impl TypeSubstMap { } } + pub(crate) fn insert(&mut self, source: SourceType, destination: DestinationType) { + self.mapping.insert(source, destination); + } + pub(crate) fn source_ids_contains_concrete_type(&self, engines: &Engines) -> bool { for source_id in self.mapping.keys() { if source_id.is_concrete(engines, TreatNumericAs::Concrete) { diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/src/lib.sw index 717da6010aa..68da9d33203 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/src/lib.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/src/lib.sw @@ -22,3 +22,9 @@ impl S { 1 } } + +impl Cat for u32 { + fn speak(self) -> u64 { + 1 + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/test.toml index 1e8f67fadea..166e61dfcf8 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap/test.toml @@ -1,3 +1,3 @@ category = "fail" -#check: $()Internal compiler error: Overlapped types +#check: $()Internal compiler error: Overlapped item foo diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/Forc.lock new file mode 100644 index 00000000000..c14cc20e70a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "trait_impl_constraint_overlap2" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/Forc.toml new file mode 100644 index 00000000000..a7b655a9ca4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/Forc.toml @@ -0,0 +1,6 @@ +[project] +name = "trait_impl_constraint_overlap2" +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/src/lib.sw new file mode 100644 index 00000000000..fb76fe02772 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/src/lib.sw @@ -0,0 +1,31 @@ + +library; + +trait Cat { + fn speak(self) -> u64; +} + +struct S { + x: T, +} + +impl S> +where +Z: Cat, +{ + fn foo(self) -> u64 { + 1 + } +} + +impl S> { + fn foo(self) -> u64 { + 1 + } +} + +impl Cat for u32 { + fn speak(self) -> u64 { + 1 + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/test.toml new file mode 100644 index 00000000000..166e61dfcf8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap2/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +#check: $()Internal compiler error: Overlapped item foo diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/Forc.lock new file mode 100644 index 00000000000..a61f4ae860a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "trait_impl_constraint_overlap3" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/Forc.toml new file mode 100644 index 00000000000..c3fae8acc67 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/Forc.toml @@ -0,0 +1,6 @@ +[project] +name = "trait_impl_constraint_overlap3" +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/src/lib.sw new file mode 100644 index 00000000000..527604da9b1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/src/lib.sw @@ -0,0 +1,38 @@ + +library; + +trait Cat { + fn speak(self) -> u64; +} + +struct S { + x: T, + y: Y, +} + +impl S { + fn foo(self) -> u64 { + 1 + } +} + +impl S +where +Z: Cat, Y: Cat +{ + fn foo(self) -> u64 { + 1 + } +} + +impl Cat for u32 { + fn speak(self) -> u64 { + 1 + } +} + +impl Cat for u64 { + fn speak(self) -> u64 { + 1 + } +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/test.toml new file mode 100644 index 00000000000..166e61dfcf8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_constraint_overlap3/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +#check: $()Internal compiler error: Overlapped item foo diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/test.toml index 1e8f67fadea..ed27a75f07e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_coherence/trait_impl_overlap/test.toml @@ -1,3 +1,3 @@ category = "fail" -#check: $()Internal compiler error: Overlapped types +#check: $()Internal compiler error: Overlapped item do_something diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/Forc.lock new file mode 100644 index 00000000000..f11dc3912b6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "trait_impl_constraint_no_overlap" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/Forc.toml new file mode 100644 index 00000000000..436a22e99f3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/Forc.toml @@ -0,0 +1,6 @@ +[project] +name = "trait_impl_constraint_no_overlap" +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/src/lib.sw new file mode 100644 index 00000000000..fccea9cda2a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/src/lib.sw @@ -0,0 +1,24 @@ +library; + +trait Cat { + fn speak(self) -> u64; +} + +struct S { + x: T, +} + +impl S { + fn foo(self) -> u64 { + 1 + } +} + +impl S +where +Z: Cat, +{ + fn foo(self) -> u64 { + 1 + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/test.toml new file mode 100644 index 00000000000..b56b73017fd --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap/test.toml @@ -0,0 +1,2 @@ +category = "compile" +expected_warnings = 2 \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/Forc.lock new file mode 100644 index 00000000000..b2432db2631 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "trait_impl_constraint_no_overlap2" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/Forc.toml new file mode 100644 index 00000000000..21ed3e7bd39 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/Forc.toml @@ -0,0 +1,6 @@ +[project] +name = "trait_impl_constraint_no_overlap2" +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/src/lib.sw new file mode 100644 index 00000000000..275276088c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/src/lib.sw @@ -0,0 +1,32 @@ +library; + +trait Cat { + fn speak(self) -> u64; +} + +struct S { + x: T, +} + +impl S +where +Z: Cat, +{ + fn bar(self) -> u64 { + 1 + } +} + +impl S { + fn foo(self) -> u64 { + 1 + } +} + +impl Cat for u32 { + fn speak(self) -> u64 { + 1 + } +} + + diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/test.toml new file mode 100644 index 00000000000..b56b73017fd --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/trait_coherence/trait_impl_constraint_no_overlap2/test.toml @@ -0,0 +1,2 @@ +category = "compile" +expected_warnings = 2 \ No newline at end of file