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 7fb6d3eaff6..ec621b7e0b9 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 @@ -644,28 +644,65 @@ pub(crate) fn type_check_method_application( { fn_ref = cached_fn_ref; } else { - // This handles the case of substituting the generic blanket type by call_path_typeid. if let Some(TyDecl::ImplSelfOrTrait(t)) = method.clone().implementing_type { let t = &engines.de().get(&t.decl_id).implementing_for; if let TypeInfo::Custom { qualified_call_path, - type_arguments: _, + type_arguments, root_type_id: _, } = &*type_engine.get(t.initial_type_id) { + let mut subst_type_parameters = vec![]; + let mut subst_type_arguments = vec![]; + + let mut names_type_ids = HashMap::::new(); + if let Some(type_arguments) = type_arguments { + for t_arg in type_arguments.iter() { + if let TypeInfo::Custom { + qualified_call_path, + .. + } = &*type_engine.get(t_arg.initial_type_id) + { + names_type_ids.insert( + qualified_call_path.call_path.suffix.clone(), + t_arg.type_id, + ); + } + } + } + + // This handles the case of substituting the generic blanket type by call_path_typeid. for p in method.type_parameters.clone() { if p.name_ident.as_str() == qualified_call_path.call_path.suffix.as_str() { - let type_subst = TypeSubstMap::from_type_parameters_and_type_arguments( - vec![t.initial_type_id], - vec![call_path_typeid], - ); - method.subst(&SubstTypesContext::new( + subst_type_parameters.push(t.initial_type_id); + subst_type_arguments.push(call_path_typeid); + break; + } + } + + // This will subst inner method_application placeholders with the already resolved + // current method application type parameter + for p in method.type_parameters.clone() { + if names_type_ids.contains_key(&p.name_ident) { + subst_type_parameters.push(engines.te().insert( engines, - &type_subst, - !ctx.code_block_first_pass(), + TypeInfo::Placeholder(p.clone()), + p.span().source_id(), )); + subst_type_arguments.push(p.type_id); } } + + let type_subst = TypeSubstMap::from_type_parameters_and_type_arguments( + subst_type_parameters, + subst_type_arguments, + ); + + method.subst(&SubstTypesContext::new( + engines, + &type_subst, + !ctx.code_block_first_pass(), + )); } } diff --git a/sway-core/src/type_system/info.rs b/sway-core/src/type_system/info.rs index d3825e51b51..30b3af262a8 100644 --- a/sway-core/src/type_system/info.rs +++ b/sway-core/src/type_system/info.rs @@ -672,7 +672,22 @@ impl DebugWithEngines for TypeInfo { let s = match self { Unknown => "unknown".into(), Never => "!".into(), - UnknownGeneric { name, .. } => name.to_string(), + UnknownGeneric { + name, + trait_constraints, + .. + } => { + let tc_str = trait_constraints + .iter() + .map(|tc| engines.help_out(tc).to_string()) + .collect::>() + .join("+"); + if tc_str.is_empty() { + name.to_string() + } else { + format!("{}:{}", name, tc_str) + } + } Placeholder(t) => format!("placeholder({:?})", engines.help_out(t)), TypeParam(n) => format!("typeparam({n})"), StringSlice => "str".into(), diff --git a/sway-core/src/type_system/substitute/subst_map.rs b/sway-core/src/type_system/substitute/subst_map.rs index 7b70aade0aa..8020e1c4d63 100644 --- a/sway-core/src/type_system/substitute/subst_map.rs +++ b/sway-core/src/type_system/substitute/subst_map.rs @@ -538,13 +538,34 @@ fn iter_for_match( type_info: &TypeInfo, ) -> Option { let type_engine = engines.te(); + for (source_type, dest_type) in &type_mapping.mapping { - if type_engine - .get(*source_type) - .eq(type_info, &PartialEqWithEnginesContext::new(engines)) + let source_type_info = type_engine.get(*source_type); + + // Allows current placeholder(T:T1+T2) to match source placeholder(T:T1) + if let ( + TypeInfo::Placeholder(source_type_param), + TypeInfo::Placeholder(current_type_param), + ) = ((*source_type_info).clone(), type_info) { + if source_type_param.name_ident.as_str() == current_type_param.name_ident.as_str() + && current_type_param + .trait_constraints + .iter() + .all(|current_tc| { + source_type_param.trait_constraints.iter().any(|source_tc| { + source_tc.eq(current_tc, &PartialEqWithEnginesContext::new(engines)) + }) + }) + { + return Some(*dest_type); + } + } + + if source_type_info.eq(type_info, &PartialEqWithEnginesContext::new(engines)) { return Some(*dest_type); } } + None } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/Forc.lock new file mode 100644 index 00000000000..d279ad45f8d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "core" +source = "path+from-root-765123F2C684AE5A" + +[[package]] +name = "method_nested_type_args" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-765123F2C684AE5A" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/Forc.toml new file mode 100644 index 00000000000..9f43c4d89f5 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "method_nested_type_args" +entry = "main.sw" + +[dependencies] +std = { path = "../../../../reduced_std_libs/sway-lib-std-assert" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/src/main.sw new file mode 100644 index 00000000000..47e8fee5518 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/src/main.sw @@ -0,0 +1,26 @@ +script; + +trait T1 {} +trait T2 {} + +struct S where T: T1 { + x: T, +} + +impl S where T: T1{ + fn not_just_t1(self) -> u64 where T: T2 { + Self::also_t2(self) + } + fn also_t2(self) -> u64 where T: T2 { + 42 + } +} + +impl T1 for u8 {} +impl T1 for u64 {} +impl T2 for u64 {} + +fn main() { + let a = S::{x: 42}; + assert_eq(a.not_just_t1(), 42); +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/test.toml new file mode 100644 index 00000000000..4bffac353e9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/method_nested_type_args/test.toml @@ -0,0 +1,5 @@ +category = "run" +expected_result = { action = "return", value = 0 } +expected_result_new_encoding = { action = "return_data", value = "" } +validate_abi = false +expected_warnings = 1