Skip to content

Commit

Permalink
Fixes issue with nested method type args. (#6658)
Browse files Browse the repository at this point in the history
## Description
When a method application called another one that uses more type args
than the impl trait type args the type substitution was not working.

This was fixed by making a type substitution of placeholders of impl
trait type parameters.

## 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).
- [ ] If my change requires substantial documentation changes, I have
[requested support from the DevRel
team](https://github.com/FuelLabs/devrel-requests/issues/new/choose)
- [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.

Co-authored-by: Sophie Dankel <47993817+sdankel@users.noreply.github.com>
Co-authored-by: João Matos <joao@tritao.eu>
  • Loading branch information
3 people authored Oct 24, 2024
1 parent ba8951e commit cc842e3
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Ident, TypeId>::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(),
));
}
}

Expand Down
17 changes: 16 additions & 1 deletion sway-core/src/type_system/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>()
.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(),
Expand Down
27 changes: 24 additions & 3 deletions sway-core/src/type_system/substitute/subst_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,13 +538,34 @@ fn iter_for_match(
type_info: &TypeInfo,
) -> Option<TypeId> {
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
}
Original file line number Diff line number Diff line change
@@ -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"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
license = "Apache-2.0"
name = "method_nested_type_args"
entry = "main.sw"

[dependencies]
std = { path = "../../../../reduced_std_libs/sway-lib-std-assert" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
script;

trait T1 {}
trait T2 {}

struct S<T> where T: T1 {
x: T,
}

impl<T> S<T> 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::<u64>{x: 42};
assert_eq(a.not_just_t1(), 42);
}
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit cc842e3

Please sign in to comment.