Skip to content

Commit bbf1e8b

Browse files
committed
Added support for inferring trait implementations for callees with polymorphic #[using] implementation parameters (and even for traits with associated types)
1 parent f6be88b commit bbf1e8b

File tree

4 files changed

+118
-35
lines changed

4 files changed

+118
-35
lines changed

Diff for: src/parser/parse_expr/primary/call.rs

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ impl<'a, I: Inflow<Token>> Parser<'a, I> {
7373
assert!(self.input.advance().is_static_member());
7474
assert!(self.input.advance().is_open_paren());
7575

76+
self.ignore_newlines();
77+
7678
while !self.input.peek_is_or_eof(TokenKind::CloseParen) {
7779
if using.len() > starting_args_len {
7880
self.parse_token(

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

+23-28
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
resolve::{
55
error::ResolveError,
66
expr::{static_member::resolve_impl_mention_from_type, ResolveExprCtx},
7-
PolyCatalog, PolyValue,
7+
MatchTypesError, PolyCatalog, PolyValue,
88
},
99
source_files::{source::Sourced, Source},
1010
};
@@ -177,36 +177,31 @@ fn try_register_specified_impl(
177177
));
178178
}
179179

180-
if param_generic_trait.args.len() != arg_concrete_trait.args.len() {
181-
return Err(ResolveError::other(
182-
"Mismatching number of arguments expected for trait implementation",
183-
target_param.source,
184-
));
185-
}
186-
187-
for (pattern, concrete) in param_generic_trait
188-
.args
189-
.iter()
190-
.zip(arg_concrete_trait.args.iter())
191-
{
192-
match catalog.match_type(ctx, pattern, concrete) {
193-
Ok(()) => {}
194-
Err(_) => {
195-
return Err(ResolveError::other(
196-
format!(
197-
"Implementation of '{}' cannot be used for '{}'",
198-
arg_concrete_trait.display(ctx.asg),
199-
param_generic_trait.display(ctx.asg)
200-
),
201-
impl_arg_source,
202-
));
203-
}
180+
match catalog.match_types(
181+
ctx,
182+
param_generic_trait.args.as_slice(),
183+
arg_concrete_trait.args.as_slice(),
184+
) {
185+
Ok(()) => {}
186+
Err(MatchTypesError::LengthMismatch) => {
187+
return Err(ResolveError::other(
188+
"Mismatching number of arguments expected for trait implementation",
189+
target_param.source,
190+
));
191+
}
192+
Err(MatchTypesError::Incongruent(_) | MatchTypesError::NoMatch(_)) => {
193+
return Err(ResolveError::other(
194+
format!(
195+
"Implementation of '{}' cannot be used for '{}'",
196+
arg_concrete_trait.display(&ctx.asg),
197+
param_generic_trait.display(&ctx.asg),
198+
),
199+
impl_arg_source,
200+
));
204201
}
205202
}
206203

207-
// TODO: PERFORMANCE: We shouldn't need to clone this
208-
let recipe = catalog.clone().bake();
209-
let param_concrete_trait = recipe.resolve_trait(param_generic_trait)?;
204+
let param_concrete_trait = catalog.resolver().resolve_trait(param_generic_trait)?;
210205

211206
if *arg_concrete_trait != param_concrete_trait {
212207
return Err(ResolveError::other(

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

+15-6
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ pub fn call_callee(
7979
for using in call.using.iter() {
8080
resolve_impl_arg(ctx, &mut callee, using, &mut used_names, &mut catalog)?;
8181
}
82-
callee.recipe = catalog.bake();
8382

8483
let function = ctx.asg.funcs.get(callee.func_ref).unwrap();
8584
let num_required = function.params.required.len();
@@ -98,16 +97,24 @@ pub fn call_callee(
9897

9998
// NOTE: PERFORMANCE: TODO: This could probably be optimized
10099
let from_env = caller.impl_params.params.iter().filter(|(_, param_trait)| {
101-
callee
102-
.recipe
100+
if catalog
101+
.match_types(ctx, &expected_trait.args, &param_trait.args)
102+
.is_err()
103+
{
104+
return false;
105+
}
106+
107+
catalog
108+
.resolver()
103109
.resolve_trait(expected_trait)
104-
.map_or(false, |expected_trait| **param_trait == expected_trait)
110+
.map_or(false, |expected_trait| {
111+
param_trait.trait_ref == expected_trait.trait_ref
112+
})
105113
});
106114

107115
match from_env.exactly_one() {
108116
Ok((param_name, _)) => {
109-
if callee
110-
.recipe
117+
if catalog
111118
.polymorphs
112119
.insert(expected_name.into(), PolyValue::PolyImpl(param_name.into()))
113120
.is_some()
@@ -146,6 +153,8 @@ pub fn call_callee(
146153
// We shouldn't use used_names after this, since we know all names were satisfied
147154
drop(used_names);
148155

156+
callee.recipe = catalog.bake();
157+
149158
for (i, arg) in args.iter_mut().enumerate() {
150159
let function = ctx.asg.funcs.get(callee.func_ref).unwrap();
151160

Diff for: src/resolve/polymorph.rs

+78-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,34 @@ impl Display for PolymorphErrorKind {
105105
}
106106

107107
impl PolyRecipe {
108-
pub fn resolve_type<'a>(&self, ty: &asg::Type) -> Result<asg::Type, PolymorphError> {
108+
pub fn resolver<'a>(&'a self) -> PolyRecipeResolver<'a> {
109+
PolyRecipeResolver {
110+
polymorphs: &self.polymorphs,
111+
}
112+
}
113+
114+
pub fn resolve_type(&self, ty: &asg::Type) -> Result<asg::Type, PolymorphError> {
115+
self.resolver().resolve_type(ty)
116+
}
117+
118+
pub fn resolve_impl(&self, name: &str, source: Source) -> Result<asg::ImplRef, PolymorphError> {
119+
self.resolver().resolve_impl(name, source)
120+
}
121+
122+
pub fn resolve_trait(
123+
&self,
124+
generic_trait: &GenericTraitRef,
125+
) -> Result<GenericTraitRef, PolymorphError> {
126+
self.resolver().resolve_trait(generic_trait)
127+
}
128+
}
129+
130+
pub struct PolyRecipeResolver<'a> {
131+
polymorphs: &'a IndexMap<String, PolyValue>,
132+
}
133+
134+
impl<'a> PolyRecipeResolver<'a> {
135+
pub fn resolve_type(&self, ty: &asg::Type) -> Result<asg::Type, PolymorphError> {
109136
let polymorphs = &self.polymorphs;
110137

111138
Ok(match &ty.kind {
@@ -218,6 +245,12 @@ impl PolyCatalog {
218245
}
219246
}
220247

248+
pub fn resolver<'a>(&'a self) -> PolyRecipeResolver<'a> {
249+
PolyRecipeResolver {
250+
polymorphs: &self.polymorphs,
251+
}
252+
}
253+
221254
pub fn bake(self) -> PolyRecipe {
222255
PolyRecipe {
223256
polymorphs: self.polymorphs,
@@ -314,6 +347,37 @@ impl PolyCatalog {
314347
}
315348
}
316349

350+
pub fn match_types<'a>(
351+
&'a mut self,
352+
ctx: &ResolveExprCtx,
353+
pattern_types: &'a [Type],
354+
concrete_types: &'a [Type],
355+
) -> Result<(), MatchTypesError<'a>> {
356+
if concrete_types.len() != pattern_types.len() {
357+
return Err(MatchTypesError::LengthMismatch);
358+
}
359+
360+
for (pattern, concrete) in pattern_types.iter().zip(concrete_types.iter()) {
361+
match self.match_type(ctx, pattern, concrete) {
362+
Ok(()) => {}
363+
Err(None) => {
364+
return Err(MatchTypesError::NoMatch(TypePatternAttempt {
365+
pattern,
366+
concrete,
367+
}))
368+
}
369+
Err(Some(PolyCatalogInsertError::Incongruent)) => {
370+
return Err(MatchTypesError::Incongruent(TypePatternAttempt {
371+
pattern,
372+
concrete,
373+
}));
374+
}
375+
}
376+
}
377+
378+
Ok(())
379+
}
380+
317381
pub fn put_type(&mut self, name: &str, new_type: &Type) -> Result<(), PolyCatalogInsertError> {
318382
if let Some(existing) = self.polymorphs.get_mut(name) {
319383
match existing {
@@ -338,3 +402,16 @@ impl PolyCatalog {
338402
self.polymorphs.get(name)
339403
}
340404
}
405+
406+
#[derive(Clone, Debug)]
407+
pub struct TypePatternAttempt<'a> {
408+
pub pattern: &'a Type,
409+
pub concrete: &'a Type,
410+
}
411+
412+
#[derive(Clone, Debug)]
413+
pub enum MatchTypesError<'a> {
414+
LengthMismatch,
415+
NoMatch(TypePatternAttempt<'a>),
416+
Incongruent(TypePatternAttempt<'a>),
417+
}

0 commit comments

Comments
 (0)