diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index c867cfe211a..0fb87dceaa2 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -773,7 +773,6 @@ fn are_equal_minus_dynamic_types(type_engine: &TypeEngine, left: TypeId, right: (TypeInfo::Unknown, TypeInfo::Unknown) => false, (TypeInfo::SelfType, TypeInfo::SelfType) => false, (TypeInfo::Numeric, TypeInfo::Numeric) => false, - (TypeInfo::UnknownGeneric { .. }, TypeInfo::UnknownGeneric { .. }) => false, (TypeInfo::Contract, TypeInfo::Contract) => false, (TypeInfo::Storage { .. }, TypeInfo::Storage { .. }) => false, @@ -785,6 +784,11 @@ fn are_equal_minus_dynamic_types(type_engine: &TypeEngine, left: TypeId, right: (TypeInfo::UnsignedInteger(l), TypeInfo::UnsignedInteger(r)) => l == r, (TypeInfo::RawUntypedPtr, TypeInfo::RawUntypedPtr) => true, (TypeInfo::RawUntypedSlice, TypeInfo::RawUntypedSlice) => true, + (TypeInfo::UnknownGeneric { .. }, TypeInfo::UnknownGeneric { .. }) => { + // return true if left and right were unified previously + type_engine.get_unified_types(left).contains(&right) + || type_engine.get_unified_types(right).contains(&left) + } // these cases may contain dynamic types ( diff --git a/sway-core/src/type_system/type_engine.rs b/sway-core/src/type_system/type_engine.rs index 9b83c1cd102..1a3b8725511 100644 --- a/sway-core/src/type_system/type_engine.rs +++ b/sway-core/src/type_system/type_engine.rs @@ -20,6 +20,7 @@ pub struct TypeEngine { pub(super) slab: ConcurrentSlab<TypeInfo>, storage_only_types: ConcurrentSlab<TypeInfo>, id_map: RwLock<HashMap<TypeInfo, TypeId>>, + unify_map: RwLock<HashMap<TypeId, Vec<TypeId>>>, } impl fmt::Display for TypeEngine { @@ -69,6 +70,40 @@ impl TypeEngine { } } + pub(crate) fn insert_unified_type(&self, received: TypeId, expected: TypeId) { + let mut unify_map = self.unify_map.write().unwrap(); + if let Some(type_ids) = unify_map.get(&received) { + if type_ids.contains(&expected) { + return; + } + let mut type_ids = type_ids.clone(); + type_ids.push(expected); + unify_map.insert(received, type_ids); + return; + } + + unify_map.insert(received, vec![expected]); + } + + pub(crate) fn get_unified_types(&self, type_id: TypeId) -> Vec<TypeId> { + let mut final_unify_ids: Vec<TypeId> = vec![]; + self.get_unified_types_rec(type_id, &mut final_unify_ids); + final_unify_ids + } + + fn get_unified_types_rec(&self, type_id: TypeId, final_unify_ids: &mut Vec<TypeId>) { + let unify_map = self.unify_map.read().unwrap(); + if let Some(unify_ids) = unify_map.get(&type_id) { + for unify_id in unify_ids { + if final_unify_ids.contains(unify_id) { + continue; + } + final_unify_ids.push(*unify_id); + self.get_unified_types_rec(*unify_id, final_unify_ids); + } + } + } + /// Currently the [TypeEngine] is a lazy static object, so when we run /// cargo tests, we can either choose to use a local [TypeEngine] and bypass /// all of the global methods or we can use the lazy static [TypeEngine]. diff --git a/sway-core/src/type_system/unify.rs b/sway-core/src/type_system/unify.rs index 2b21d00d6a0..08536497d94 100644 --- a/sway-core/src/type_system/unify.rs +++ b/sway-core/src/type_system/unify.rs @@ -236,7 +236,11 @@ pub(super) fn unify( name: en, trait_constraints: etc, }, - ) if rn.as_str() == en.as_str() && rtc.eq(&etc, type_engine) => (vec![], vec![]), + ) if rn.as_str() == en.as_str() && rtc.eq(&etc, type_engine) => { + type_engine.insert_unified_type(received, expected); + type_engine.insert_unified_type(expected, received); + (vec![], vec![]) + } (ref r @ UnknownGeneric { .. }, e) => { match type_engine.slab.replace(received, r, e, type_engine) { None => (vec![], vec![]), @@ -444,8 +448,12 @@ pub(super) fn unify_right( name: en, trait_constraints: etc, }, - ) if rn.as_str() == en.as_str() && rtc.eq(&etc, type_engine) => (vec![], vec![]), + ) if rn.as_str() == en.as_str() && rtc.eq(&etc, type_engine) => { + type_engine.insert_unified_type(received, expected); + (vec![], vec![]) + } (r, ref e @ UnknownGeneric { .. }) => { + type_engine.insert_unified_type(received, expected); match type_engine.slab.replace(expected, e, r, type_engine) { None => (vec![], vec![]), Some(_) => unify_right(type_engine, received, expected, span, help_text), diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/Forc.lock new file mode 100644 index 00000000000..1375faff5cb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = 'core' +source = 'path+from-root-D8A8CF23B4069FF9' + +[[package]] +name = 'eq_generic' +source = 'member' +dependencies = ['std'] + +[[package]] +name = 'std' +source = 'path+from-root-D8A8CF23B4069FF9' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/Forc.toml new file mode 100644 index 00000000000..179bc98016b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs <contact@fuel.sh>"] +entry = "main.sw" +license = "Apache-2.0" +name = "eq_generic" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/json_abi_oracle.json new file mode 100644 index 00000000000..542c5d96b7c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/json_abi_oracle.json @@ -0,0 +1,22 @@ +{ + "functions": [ + { + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + } + } + ], + "loggedTypes": [], + "types": [ + { + "components": [], + "type": "()", + "typeId": 0, + "typeParameters": null + } + ] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/src/main.sw new file mode 100644 index 00000000000..28278791386 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/src/main.sw @@ -0,0 +1,21 @@ +script; + +use core::ops::*; + +impl<T> Option<T> { + pub fn ok_or<E>(self, err: E) -> Result<T, E> { + match self { + Option::Some(v) => Result::Ok(v), + Option::None => Result::Err(err), + } + } +} + +fn test_ok_or<T, E>(val: T, default: E) where T: Eq { + match Option::Some(val).ok_or(default) { + Result::Ok(inner) => assert(inner == val), + Result::Err(_) => revert(0), + }; +} + +fn main() {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true