Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/expr.rs')
-rw-r--r--crates/hir-ty/src/infer/expr.rs263
1 files changed, 166 insertions, 97 deletions
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 86e5afdb50..80e3ca1fa2 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -489,78 +489,7 @@ impl InferenceContext<'_> {
ty
}
- Expr::Call { callee, args, .. } => {
- let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes);
- let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true);
- let (res, derefed_callee) = loop {
- let Some((callee_deref_ty, _)) = derefs.next() else {
- break (None, callee_ty.clone());
- };
- if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) {
- break (Some(res), callee_deref_ty);
- }
- };
- // if the function is unresolved, we use is_varargs=true to
- // suppress the arg count diagnostic here
- let is_varargs =
- derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs)
- || res.is_none();
- let (param_tys, ret_ty) = match res {
- Some((func, params, ret_ty)) => {
- let mut adjustments = auto_deref_adjust_steps(&derefs);
- if let TyKind::Closure(c, _) =
- self.table.resolve_completely(callee_ty.clone()).kind(Interner)
- {
- if let Some(par) = self.current_closure {
- self.closure_dependencies.entry(par).or_default().push(*c);
- }
- self.deferred_closures.entry(*c).or_default().push((
- derefed_callee.clone(),
- callee_ty.clone(),
- params.clone(),
- tgt_expr,
- ));
- }
- if let Some(fn_x) = func {
- self.write_fn_trait_method_resolution(
- fn_x,
- &derefed_callee,
- &mut adjustments,
- &callee_ty,
- &params,
- tgt_expr,
- );
- }
- self.write_expr_adj(*callee, adjustments);
- (params, ret_ty)
- }
- None => {
- self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
- call_expr: tgt_expr,
- found: callee_ty.clone(),
- });
- (Vec::new(), self.err_ty())
- }
- };
- let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
- self.register_obligations_for_call(&callee_ty);
-
- let expected_inputs = self.expected_inputs_for_expected_output(
- expected,
- ret_ty.clone(),
- param_tys.clone(),
- );
-
- self.check_call_arguments(
- tgt_expr,
- args,
- &expected_inputs,
- &param_tys,
- &indices_to_skip,
- is_varargs,
- );
- self.normalize_associated_types_in(ret_ty)
- }
+ Expr::Call { callee, args, .. } => self.infer_call(tgt_expr, *callee, args, expected),
Expr::MethodCall { receiver, args, method_name, generic_args } => self
.infer_method_call(
tgt_expr,
@@ -1872,6 +1801,107 @@ impl InferenceContext<'_> {
}
}
+ fn infer_call(
+ &mut self,
+ tgt_expr: ExprId,
+ callee: ExprId,
+ args: &[ExprId],
+ expected: &Expectation,
+ ) -> Ty {
+ let callee_ty = self.infer_expr(callee, &Expectation::none(), ExprIsRead::Yes);
+ let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true);
+ let (res, derefed_callee) = loop {
+ let Some((callee_deref_ty, _)) = derefs.next() else {
+ break (None, callee_ty.clone());
+ };
+ if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) {
+ break (Some(res), callee_deref_ty);
+ }
+ };
+ // if the function is unresolved, we use is_varargs=true to
+ // suppress the arg count diagnostic here
+ let is_varargs =
+ derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs) || res.is_none();
+ let (param_tys, ret_ty) = match res {
+ Some((func, params, ret_ty)) => {
+ let mut adjustments = auto_deref_adjust_steps(&derefs);
+ if let TyKind::Closure(c, _) =
+ self.table.resolve_completely(callee_ty.clone()).kind(Interner)
+ {
+ if let Some(par) = self.current_closure {
+ self.closure_dependencies.entry(par).or_default().push(*c);
+ }
+ self.deferred_closures.entry(*c).or_default().push((
+ derefed_callee.clone(),
+ callee_ty.clone(),
+ params.clone(),
+ tgt_expr,
+ ));
+ }
+ if let Some(fn_x) = func {
+ self.write_fn_trait_method_resolution(
+ fn_x,
+ &derefed_callee,
+ &mut adjustments,
+ &callee_ty,
+ &params,
+ tgt_expr,
+ );
+ }
+ self.write_expr_adj(callee, adjustments);
+ (params, ret_ty)
+ }
+ None => {
+ self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
+ call_expr: tgt_expr,
+ found: callee_ty.clone(),
+ });
+ (Vec::new(), self.err_ty())
+ }
+ };
+ let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
+ self.check_call(
+ tgt_expr,
+ args,
+ callee_ty,
+ &param_tys,
+ ret_ty,
+ &indices_to_skip,
+ is_varargs,
+ expected,
+ )
+ }
+
+ fn check_call(
+ &mut self,
+ tgt_expr: ExprId,
+ args: &[ExprId],
+ callee_ty: Ty,
+ param_tys: &[Ty],
+ ret_ty: Ty,
+ indices_to_skip: &[u32],
+ is_varargs: bool,
+ expected: &Expectation,
+ ) -> Ty {
+ self.register_obligations_for_call(&callee_ty);
+
+ let expected_inputs = self.expected_inputs_for_expected_output(
+ expected,
+ ret_ty.clone(),
+ param_tys.to_owned(),
+ );
+
+ self.check_call_arguments(
+ tgt_expr,
+ args,
+ &expected_inputs,
+ param_tys,
+ indices_to_skip,
+ is_varargs,
+ );
+ self.normalize_associated_types_in(ret_ty)
+ }
+
fn infer_method_call(
&mut self,
tgt_expr: ExprId,
@@ -1892,21 +1922,32 @@ impl InferenceContext<'_> {
VisibleFromModule::Filter(self.resolver.module()),
method_name,
);
- let (receiver_ty, method_ty, substs) = match resolved {
+ match resolved {
Some((adjust, func, visible)) => {
- let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
- let generics = generics(self.db.upcast(), func.into());
- let substs = self.substs_for_method_call(generics, generic_args);
- self.write_expr_adj(receiver, adjustments);
- self.write_method_resolution(tgt_expr, func, substs.clone());
if !visible {
self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem {
id: tgt_expr.into(),
item: func.into(),
})
}
- (ty, self.db.value_ty(func.into()).unwrap(), substs)
+
+ let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
+ self.write_expr_adj(receiver, adjustments);
+
+ let generics = generics(self.db.upcast(), func.into());
+ let substs = self.substs_for_method_call(generics, generic_args);
+ self.write_method_resolution(tgt_expr, func, substs.clone());
+ self.check_method_call(
+ tgt_expr,
+ args,
+ self.db.value_ty(func.into()).expect("we have a function def"),
+ substs,
+ ty,
+ expected,
+ )
}
+ // Failed to resolve, report diagnostic and try to resolve as call to field access or
+ // assoc function
None => {
let field_with_same_name_exists = match self.lookup_field(&receiver_ty, method_name)
{
@@ -1926,12 +1967,11 @@ impl InferenceContext<'_> {
VisibleFromModule::Filter(self.resolver.module()),
Some(method_name),
method_resolution::LookupMode::Path,
- |_ty, item, visible| {
- if visible {
- Some(item)
- } else {
- None
+ |_ty, item, visible| match item {
+ hir_def::AssocItemId::FunctionId(function_id) if visible => {
+ Some(function_id)
}
+ _ => None,
},
);
@@ -1939,17 +1979,45 @@ impl InferenceContext<'_> {
expr: tgt_expr,
receiver: receiver_ty.clone(),
name: method_name.clone(),
- field_with_same_name: field_with_same_name_exists,
+ field_with_same_name: field_with_same_name_exists.clone(),
assoc_func_with_same_name,
});
- (
- receiver_ty,
- Binders::empty(Interner, self.err_ty()),
- Substitution::empty(Interner),
- )
+
+ let recovered = match assoc_func_with_same_name {
+ Some(f) => {
+ let generics = generics(self.db.upcast(), f.into());
+ let substs = self.substs_for_method_call(generics, generic_args);
+ let f = self
+ .db
+ .value_ty(f.into())
+ .expect("we have a function def")
+ .substitute(Interner, &substs);
+ let sig = f.callable_sig(self.db).expect("we have a function def");
+ Some((f, sig, true))
+ }
+ None => field_with_same_name_exists.and_then(|field_ty| {
+ let callable_sig = field_ty.callable_sig(self.db)?;
+ Some((field_ty, callable_sig, false))
+ }),
+ };
+ match recovered {
+ Some((callee_ty, sig, strip_first)) => self.check_call(
+ tgt_expr,
+ args,
+ callee_ty,
+ sig.params().get(strip_first as usize..).unwrap_or(&[]),
+ sig.ret().clone(),
+ &[],
+ true,
+ expected,
+ ),
+ None => {
+ self.check_call_arguments(tgt_expr, args, &[], &[], &[], true);
+ self.err_ty()
+ }
+ }
}
- };
- self.check_method_call(tgt_expr, args, method_ty, substs, receiver_ty, expected)
+ }
}
fn check_method_call(
@@ -2019,9 +2087,10 @@ impl InferenceContext<'_> {
expected_inputs: &[Ty],
param_tys: &[Ty],
skip_indices: &[u32],
- is_varargs: bool,
+ ignore_arg_param_mismatch: bool,
) {
- let arg_count_mismatch = args.len() != param_tys.len() + skip_indices.len() && !is_varargs;
+ let arg_count_mismatch =
+ !ignore_arg_param_mismatch && args.len() != param_tys.len() + skip_indices.len();
if arg_count_mismatch {
self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount {
call_expr: expr,
@@ -2050,7 +2119,7 @@ impl InferenceContext<'_> {
continue;
}
- while skip_indices.peek().is_some_and(|i| *i < idx as u32) {
+ while skip_indices.peek().is_some_and(|&i| i < idx as u32) {
skip_indices.next();
}
if skip_indices.peek().copied() == Some(idx as u32) {
@@ -2132,8 +2201,8 @@ impl InferenceContext<'_> {
for kind_id in def_generics.iter_self_id().take(self_params) {
let arg = args.peek();
let arg = match (kind_id, arg) {
- // Lifetimes can be elided.
- // Once we have implemented lifetime elision correctly,
+ // Lifetimes can be inferred.
+ // Once we have implemented lifetime inference correctly,
// this should be handled in a proper way.
(
GenericParamId::LifetimeParamId(_),