Skip to content

Commit 8667975

Browse files
committed
Started refactoring trait implementation inference code in preparation for further improvements
1 parent 4306e40 commit 8667975

File tree

4 files changed

+139
-110
lines changed

4 files changed

+139
-110
lines changed

Diff for: src/resolve/expr/call/infer_impl.rs

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use crate::{
2+
asg,
3+
resolve::{error::ResolveError, expr::ResolveExprCtx, PolyCatalog, PolyValue},
4+
source_files::Source,
5+
};
6+
use itertools::Itertools;
7+
use std::collections::HashSet;
8+
9+
pub fn infer_callee_missing_impl_args(
10+
ctx: &ResolveExprCtx,
11+
function: &asg::Func,
12+
used_names: &mut HashSet<String>,
13+
catalog: &mut PolyCatalog,
14+
source: Source,
15+
) -> Result<(), ResolveError> {
16+
for (expected_name, expected_trait) in function.impl_params.params.iter() {
17+
if used_names.contains(expected_name) {
18+
continue;
19+
}
20+
21+
let Some(caller) = ctx
22+
.func_ref
23+
.map(|caller_func_ref| ctx.asg.funcs.get(caller_func_ref).unwrap())
24+
else {
25+
continue;
26+
};
27+
28+
// TODO: PERFORMANCE: Optimize this and remove unnecessary cloning.
29+
// We should really change `match_type` and friends to produce a type match solution
30+
// instead of modifying the poly catalog.
31+
let from_env = caller.impl_params.params.iter().filter(|(_, param_trait)| {
32+
let mut catalog_plus_match = catalog.clone();
33+
34+
if catalog_plus_match
35+
.match_types(ctx, &expected_trait.args, &param_trait.args)
36+
.is_err()
37+
{
38+
return false;
39+
}
40+
41+
catalog_plus_match
42+
.resolver()
43+
.resolve_trait(expected_trait)
44+
.map_or(false, |expected_trait| {
45+
if param_trait.trait_ref == expected_trait.trait_ref {
46+
*catalog = catalog_plus_match;
47+
true
48+
} else {
49+
false
50+
}
51+
})
52+
});
53+
54+
match from_env.exactly_one() {
55+
Ok((param_name, _)) => {
56+
if catalog
57+
.polymorphs
58+
.insert(expected_name.into(), PolyValue::PolyImpl(param_name.into()))
59+
.is_some()
60+
{
61+
return Err(ResolveError::other(
62+
format!(
63+
"Could not automatically supply trait implementation for '${} {}' required by function call, since the polymorph is already in use",
64+
expected_name,
65+
expected_trait.display(&ctx.asg),
66+
),
67+
source,
68+
));
69+
}
70+
}
71+
Err(mut non_unique) => {
72+
return Err(ResolveError::other(
73+
if non_unique.next().is_some() {
74+
format!(
75+
"Ambiguous trait implementation for '${} {}' required by function call, please specify manually",
76+
expected_name,
77+
expected_trait.display(&ctx.asg),
78+
)
79+
} else {
80+
format!(
81+
"Missing '${} {}' trait implementation required by function call",
82+
expected_name,
83+
expected_trait.display(&ctx.asg),
84+
)
85+
},
86+
source,
87+
));
88+
}
89+
}
90+
}
91+
92+
Ok(())
93+
}

Diff for: src/resolve/expr/call/mod.rs

+6-78
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod cast;
2-
mod impl_arg;
2+
mod infer_impl;
3+
mod specified_impl;
34

45
use super::{resolve_expr, PreferredType, ResolveExprCtx, ResolveExprMode};
56
use crate::{
@@ -8,13 +9,14 @@ use crate::{
89
resolve::{
910
conform::{conform_expr, to_default::conform_expr_to_default, ConformMode, Perform},
1011
error::{ResolveError, ResolveErrorKind},
11-
Initialized, PolyCatalog, PolyValue,
12+
Initialized, PolyCatalog,
1213
},
1314
source_files::Source,
1415
};
1516
use cast::cast;
16-
use impl_arg::resolve_impl_arg;
17+
use infer_impl::infer_callee_missing_impl_args;
1718
use itertools::Itertools;
19+
use specified_impl::resolve_impl_arg;
1820
use std::collections::HashSet;
1921

2022
pub fn resolve_call_expr(
@@ -83,81 +85,7 @@ pub fn call_callee(
8385
let function = ctx.asg.funcs.get(callee.func_ref).unwrap();
8486
let num_required = function.params.required.len();
8587

86-
for (expected_name, expected_trait) in function.impl_params.params.iter() {
87-
if used_names.contains(expected_name) {
88-
continue;
89-
}
90-
91-
let Some(caller) = ctx
92-
.func_ref
93-
.map(|caller_func_ref| ctx.asg.funcs.get(caller_func_ref).unwrap())
94-
else {
95-
continue;
96-
};
97-
98-
// TODO: PERFORMANCE: Optimize this and remove unnecessary cloning.
99-
// We should really change `match_type` and friends to produce a type match solution
100-
// instead of modifying the poly catalog.
101-
let from_env = caller.impl_params.params.iter().filter(|(_, param_trait)| {
102-
let mut catalog_plus_match = catalog.clone();
103-
104-
if catalog_plus_match
105-
.match_types(ctx, &expected_trait.args, &param_trait.args)
106-
.is_err()
107-
{
108-
return false;
109-
}
110-
111-
catalog_plus_match
112-
.resolver()
113-
.resolve_trait(expected_trait)
114-
.map_or(false, |expected_trait| {
115-
if param_trait.trait_ref == expected_trait.trait_ref {
116-
catalog = catalog_plus_match;
117-
true
118-
} else {
119-
false
120-
}
121-
})
122-
});
123-
124-
match from_env.exactly_one() {
125-
Ok((param_name, _)) => {
126-
if catalog
127-
.polymorphs
128-
.insert(expected_name.into(), PolyValue::PolyImpl(param_name.into()))
129-
.is_some()
130-
{
131-
return Err(ResolveError::other(
132-
format!(
133-
"Could not automatically supply trait implementation for '${} {}' required by function call, since the polymorph is already in use",
134-
expected_name,
135-
expected_trait.display(&ctx.asg),
136-
),
137-
source,
138-
));
139-
}
140-
}
141-
Err(mut non_unique) => {
142-
return Err(ResolveError::other(
143-
if non_unique.next().is_some() {
144-
format!(
145-
"Ambiguous trait implementation for '${} {}' required by function call, please specify manually",
146-
expected_name,
147-
expected_trait.display(&ctx.asg),
148-
)
149-
} else {
150-
format!(
151-
"Missing '${} {}' trait implementation required by function call",
152-
expected_name,
153-
expected_trait.display(&ctx.asg),
154-
)
155-
},
156-
source,
157-
));
158-
}
159-
}
160-
}
88+
infer_callee_missing_impl_args(ctx, function, &mut used_names, &mut catalog, source)?;
16189

16290
// We shouldn't use used_names after this, since we know all names were satisfied
16391
drop(used_names);
File renamed without changes.

Diff for: src/resolve/func_head.rs

+40-32
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::{
1414
workspace::fs::FsNodeId,
1515
};
1616
use indexmap::IndexMap;
17+
use std::borrow::Cow;
1718

1819
pub fn create_func_heads<'a>(
1920
ctx: &mut ResolveCtx,
@@ -101,38 +102,7 @@ pub fn create_func_head<'a>(
101102
.then(|| collect_constraints(&params, &return_type))
102103
.unwrap_or_default();
103104

104-
let impl_params = {
105-
let mut params = IndexMap::default();
106-
107-
for given in &head.givens {
108-
let trait_ty = type_ctx.resolve(&given.ty)?;
109-
110-
let asg::TypeKind::Trait(_, trait_ref, trait_args) = &trait_ty.kind else {
111-
return Err(ResolveError::other("Expected trait", trait_ty.source));
112-
};
113-
114-
let generic_trait_ref = GenericTraitRef {
115-
trait_ref: *trait_ref,
116-
args: trait_args.to_vec(),
117-
};
118-
119-
let Some((name, name_source)) = &given.name else {
120-
return Err(ResolveError::other(
121-
"Anonymous trait implementation polymorphs are not supported yet",
122-
trait_ty.source,
123-
));
124-
};
125-
126-
if params.insert(name.clone(), generic_trait_ref).is_some() {
127-
return Err(ResolveError::other(
128-
format!("Trait implementation polymorph '${}' already exists", name),
129-
*name_source,
130-
));
131-
}
132-
}
133-
134-
ImplParams { params }
135-
};
105+
let impl_params = create_func_impl_params(&type_ctx, head)?;
136106

137107
Ok(asg.funcs.insert(asg::Func {
138108
name,
@@ -172,3 +142,41 @@ pub fn resolve_parameters(
172142
is_cstyle_vararg: parameters.is_cstyle_vararg,
173143
})
174144
}
145+
146+
pub fn create_func_impl_params<'a>(
147+
type_ctx: &ResolveTypeCtx<'a>,
148+
head: &FuncHead,
149+
) -> Result<ImplParams, ResolveError> {
150+
let mut params = IndexMap::default();
151+
152+
for (i, given) in head.givens.iter().enumerate() {
153+
let trait_ty = type_ctx.resolve(&given.ty)?;
154+
155+
let asg::TypeKind::Trait(_, trait_ref, trait_args) = &trait_ty.kind else {
156+
return Err(ResolveError::other("Expected trait", trait_ty.source));
157+
};
158+
159+
let generic_trait_ref = GenericTraitRef {
160+
trait_ref: *trait_ref,
161+
args: trait_args.to_vec(),
162+
};
163+
164+
let (name, name_source) = given
165+
.name
166+
.as_ref()
167+
.map(|(name, name_source)| (Cow::Borrowed(name), *name_source))
168+
.unwrap_or_else(|| (Cow::Owned(format!(".{}", i)), given.ty.source));
169+
170+
if params
171+
.insert(name.as_ref().clone(), generic_trait_ref)
172+
.is_some()
173+
{
174+
return Err(ResolveError::other(
175+
format!("Trait implementation polymorph '${}' already exists", name),
176+
name_source,
177+
));
178+
}
179+
}
180+
181+
Ok(ImplParams { params })
182+
}

0 commit comments

Comments
 (0)