diff --git a/src/resolve/expr/call/infer_impl.rs b/src/resolve/expr/call/infer_impl.rs new file mode 100644 index 0000000..c6eac13 --- /dev/null +++ b/src/resolve/expr/call/infer_impl.rs @@ -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, + 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, ¶m_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(()) +} diff --git a/src/resolve/expr/call/mod.rs b/src/resolve/expr/call/mod.rs index 667f62e..30234a8 100644 --- a/src/resolve/expr/call/mod.rs +++ b/src/resolve/expr/call/mod.rs @@ -1,5 +1,6 @@ mod cast; -mod impl_arg; +mod infer_impl; +mod specified_impl; use super::{resolve_expr, PreferredType, ResolveExprCtx, ResolveExprMode}; use crate::{ @@ -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( @@ -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, ¶m_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); diff --git a/src/resolve/expr/call/impl_arg.rs b/src/resolve/expr/call/specified_impl.rs similarity index 100% rename from src/resolve/expr/call/impl_arg.rs rename to src/resolve/expr/call/specified_impl.rs diff --git a/src/resolve/func_head.rs b/src/resolve/func_head.rs index 6534f2b..431c452 100644 --- a/src/resolve/func_head.rs +++ b/src/resolve/func_head.rs @@ -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, @@ -101,38 +102,7 @@ pub fn create_func_head<'a>( .then(|| collect_constraints(¶ms, &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, @@ -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 { + 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 }) +}