Skip to content

Commit

Permalink
Started refactoring trait implementation inference code in preparatio…
Browse files Browse the repository at this point in the history
…n for further improvements
  • Loading branch information
IsaacShelton committed Jan 25, 2025
1 parent 4306e40 commit 8667975
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 110 deletions.
93 changes: 93 additions & 0 deletions src/resolve/expr/call/infer_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use crate::{
asg,
resolve::{error::ResolveError, expr::ResolveExprCtx, PolyCatalog, PolyValue},
source_files::Source,
};
use itertools::Itertools;
use std::collections::HashSet;

pub fn infer_callee_missing_impl_args(
ctx: &ResolveExprCtx,
function: &asg::Func,
used_names: &mut HashSet<String>,
catalog: &mut PolyCatalog,
source: Source,
) -> Result<(), ResolveError> {
for (expected_name, expected_trait) in function.impl_params.params.iter() {
if used_names.contains(expected_name) {
continue;
}

let Some(caller) = ctx
.func_ref
.map(|caller_func_ref| ctx.asg.funcs.get(caller_func_ref).unwrap())
else {
continue;
};

// TODO: PERFORMANCE: Optimize this and remove unnecessary cloning.
// We should really change `match_type` and friends to produce a type match solution
// instead of modifying the poly catalog.
let from_env = caller.impl_params.params.iter().filter(|(_, param_trait)| {
let mut catalog_plus_match = catalog.clone();

if catalog_plus_match
.match_types(ctx, &expected_trait.args, &param_trait.args)
.is_err()
{
return false;
}

catalog_plus_match
.resolver()
.resolve_trait(expected_trait)
.map_or(false, |expected_trait| {
if param_trait.trait_ref == expected_trait.trait_ref {
*catalog = catalog_plus_match;
true
} else {
false
}
})
});

match from_env.exactly_one() {
Ok((param_name, _)) => {
if catalog
.polymorphs
.insert(expected_name.into(), PolyValue::PolyImpl(param_name.into()))
.is_some()
{
return Err(ResolveError::other(
format!(
"Could not automatically supply trait implementation for '${} {}' required by function call, since the polymorph is already in use",
expected_name,
expected_trait.display(&ctx.asg),
),
source,
));
}
}
Err(mut non_unique) => {
return Err(ResolveError::other(
if non_unique.next().is_some() {
format!(
"Ambiguous trait implementation for '${} {}' required by function call, please specify manually",
expected_name,
expected_trait.display(&ctx.asg),
)
} else {
format!(
"Missing '${} {}' trait implementation required by function call",
expected_name,
expected_trait.display(&ctx.asg),
)
},
source,
));
}
}
}

Ok(())
}
84 changes: 6 additions & 78 deletions src/resolve/expr/call/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod cast;
mod impl_arg;
mod infer_impl;
mod specified_impl;

use super::{resolve_expr, PreferredType, ResolveExprCtx, ResolveExprMode};
use crate::{
Expand All @@ -8,13 +9,14 @@ use crate::{
resolve::{
conform::{conform_expr, to_default::conform_expr_to_default, ConformMode, Perform},
error::{ResolveError, ResolveErrorKind},
Initialized, PolyCatalog, PolyValue,
Initialized, PolyCatalog,
},
source_files::Source,
};
use cast::cast;
use impl_arg::resolve_impl_arg;
use infer_impl::infer_callee_missing_impl_args;
use itertools::Itertools;
use specified_impl::resolve_impl_arg;
use std::collections::HashSet;

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

for (expected_name, expected_trait) in function.impl_params.params.iter() {
if used_names.contains(expected_name) {
continue;
}

let Some(caller) = ctx
.func_ref
.map(|caller_func_ref| ctx.asg.funcs.get(caller_func_ref).unwrap())
else {
continue;
};

// TODO: PERFORMANCE: Optimize this and remove unnecessary cloning.
// We should really change `match_type` and friends to produce a type match solution
// instead of modifying the poly catalog.
let from_env = caller.impl_params.params.iter().filter(|(_, param_trait)| {
let mut catalog_plus_match = catalog.clone();

if catalog_plus_match
.match_types(ctx, &expected_trait.args, &param_trait.args)
.is_err()
{
return false;
}

catalog_plus_match
.resolver()
.resolve_trait(expected_trait)
.map_or(false, |expected_trait| {
if param_trait.trait_ref == expected_trait.trait_ref {
catalog = catalog_plus_match;
true
} else {
false
}
})
});

match from_env.exactly_one() {
Ok((param_name, _)) => {
if catalog
.polymorphs
.insert(expected_name.into(), PolyValue::PolyImpl(param_name.into()))
.is_some()
{
return Err(ResolveError::other(
format!(
"Could not automatically supply trait implementation for '${} {}' required by function call, since the polymorph is already in use",
expected_name,
expected_trait.display(&ctx.asg),
),
source,
));
}
}
Err(mut non_unique) => {
return Err(ResolveError::other(
if non_unique.next().is_some() {
format!(
"Ambiguous trait implementation for '${} {}' required by function call, please specify manually",
expected_name,
expected_trait.display(&ctx.asg),
)
} else {
format!(
"Missing '${} {}' trait implementation required by function call",
expected_name,
expected_trait.display(&ctx.asg),
)
},
source,
));
}
}
}
infer_callee_missing_impl_args(ctx, function, &mut used_names, &mut catalog, source)?;

// We shouldn't use used_names after this, since we know all names were satisfied
drop(used_names);
Expand Down
File renamed without changes.
72 changes: 40 additions & 32 deletions src/resolve/func_head.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::{
workspace::fs::FsNodeId,
};
use indexmap::IndexMap;
use std::borrow::Cow;

pub fn create_func_heads<'a>(
ctx: &mut ResolveCtx,
Expand Down Expand Up @@ -101,38 +102,7 @@ pub fn create_func_head<'a>(
.then(|| collect_constraints(&params, &return_type))
.unwrap_or_default();

let impl_params = {
let mut params = IndexMap::default();

for given in &head.givens {
let trait_ty = type_ctx.resolve(&given.ty)?;

let asg::TypeKind::Trait(_, trait_ref, trait_args) = &trait_ty.kind else {
return Err(ResolveError::other("Expected trait", trait_ty.source));
};

let generic_trait_ref = GenericTraitRef {
trait_ref: *trait_ref,
args: trait_args.to_vec(),
};

let Some((name, name_source)) = &given.name else {
return Err(ResolveError::other(
"Anonymous trait implementation polymorphs are not supported yet",
trait_ty.source,
));
};

if params.insert(name.clone(), generic_trait_ref).is_some() {
return Err(ResolveError::other(
format!("Trait implementation polymorph '${}' already exists", name),
*name_source,
));
}
}

ImplParams { params }
};
let impl_params = create_func_impl_params(&type_ctx, head)?;

Ok(asg.funcs.insert(asg::Func {
name,
Expand Down Expand Up @@ -172,3 +142,41 @@ pub fn resolve_parameters(
is_cstyle_vararg: parameters.is_cstyle_vararg,
})
}

pub fn create_func_impl_params<'a>(
type_ctx: &ResolveTypeCtx<'a>,
head: &FuncHead,
) -> Result<ImplParams, ResolveError> {
let mut params = IndexMap::default();

for (i, given) in head.givens.iter().enumerate() {
let trait_ty = type_ctx.resolve(&given.ty)?;

let asg::TypeKind::Trait(_, trait_ref, trait_args) = &trait_ty.kind else {
return Err(ResolveError::other("Expected trait", trait_ty.source));
};

let generic_trait_ref = GenericTraitRef {
trait_ref: *trait_ref,
args: trait_args.to_vec(),
};

let (name, name_source) = given
.name
.as_ref()
.map(|(name, name_source)| (Cow::Borrowed(name), *name_source))
.unwrap_or_else(|| (Cow::Owned(format!(".{}", i)), given.ty.source));

if params
.insert(name.as_ref().clone(), generic_trait_ref)
.is_some()
{
return Err(ResolveError::other(
format!("Trait implementation polymorph '${}' already exists", name),
name_source,
));
}
}

Ok(ImplParams { params })
}

0 comments on commit 8667975

Please sign in to comment.