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.rs723
1 files changed, 483 insertions, 240 deletions
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 16fc2bfc06..c5a51dfc4c 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -1,12 +1,10 @@
//! Type inference for expressions.
-use std::{
- iter::{repeat, repeat_with},
- mem,
-};
+use std::{iter::repeat_with, mem};
use chalk_ir::{DebruijnIndex, Mutability, TyVariableKind, cast::Cast};
use either::Either;
+use hir_def::hir::ClosureKind;
use hir_def::{
BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId,
expr_store::path::{GenericArg, GenericArgs, Path},
@@ -19,29 +17,37 @@ use hir_def::{
};
use hir_expand::name::Name;
use intern::sym;
+use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _};
use stdx::always;
use syntax::ast::RangeOp;
+use tracing::debug;
+use crate::autoderef::overloaded_deref_ty;
+use crate::next_solver::ErrorGuaranteed;
+use crate::next_solver::infer::DefineOpaqueTypes;
+use crate::next_solver::obligation_ctxt::ObligationCtxt;
use crate::{
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext,
- DeclOrigin, IncorrectGenericsLenKind, Interner, Rawness, Scalar, Substitution,
- TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
- autoderef::{Autoderef, builtin_deref, deref_by_trait},
- consteval,
+ DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar,
+ Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, consteval,
generics::generics,
infer::{
- BreakableKind,
- coerce::{CoerceMany, CoerceNever, CoercionCause},
+ AllowTwoPhase, BreakableKind,
+ coerce::{CoerceMany, CoerceNever},
find_continuable,
pat::contains_explicit_ref_binding,
},
lang_items::lang_items_for_bin_op,
lower::{
- LifetimeElisionKind, ParamLoweringMode, lower_to_chalk_mutability,
+ ParamLoweringMode, lower_to_chalk_mutability,
path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings},
},
mapping::{ToChalk, from_chalk},
method_resolution::{self, VisibleFromModule},
+ next_solver::{
+ infer::traits::ObligationCause,
+ mapping::{ChalkToNextSolver, NextSolverToChalk},
+ },
primitive::{self, UintTy},
static_lifetime, to_chalk_trait_id,
traits::FnTrait,
@@ -49,7 +55,7 @@ use crate::{
use super::{
BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch,
- cast::CastCheck, coerce::auto_deref_adjust_steps, find_breakable,
+ cast::CastCheck, find_breakable,
};
#[derive(Clone, Copy, PartialEq, Eq)]
@@ -58,7 +64,7 @@ pub(crate) enum ExprIsRead {
No,
}
-impl InferenceContext<'_> {
+impl<'db> InferenceContext<'db> {
pub(crate) fn infer_expr(
&mut self,
tgt_expr: ExprId,
@@ -97,8 +103,14 @@ impl InferenceContext<'_> {
} else {
CoerceNever::No
};
- match self.coerce(Some(expr), &ty, &target, coerce_never) {
- Ok(res) => res,
+ match self.coerce(
+ expr.into(),
+ ty.to_nextsolver(self.table.interner),
+ target.to_nextsolver(self.table.interner),
+ AllowTwoPhase::No,
+ coerce_never,
+ ) {
+ Ok(res) => res.to_chalk(self.table.interner),
Err(_) => {
self.result.type_mismatches.insert(
expr.into(),
@@ -259,8 +271,15 @@ impl InferenceContext<'_> {
}
if let Some(target) = expected.only_has_type(&mut self.table) {
- self.coerce(Some(expr), &ty, &target, CoerceNever::Yes)
- .expect("never-to-any coercion should always succeed")
+ self.coerce(
+ expr.into(),
+ ty.to_nextsolver(self.table.interner),
+ target.to_nextsolver(self.table.interner),
+ AllowTwoPhase::No,
+ CoerceNever::Yes,
+ )
+ .expect("never-to-any coercion should always succeed")
+ .to_chalk(self.table.interner)
} else {
ty
}
@@ -278,6 +297,7 @@ impl InferenceContext<'_> {
}
}
+ #[tracing::instrument(level = "debug", skip(self, is_read), ret)]
fn infer_expr_inner(
&mut self,
tgt_expr: ExprId,
@@ -286,7 +306,9 @@ impl InferenceContext<'_> {
) -> Ty {
self.db.unwind_if_revision_cancelled();
- let ty = match &self.body[tgt_expr] {
+ let expr = &self.body[tgt_expr];
+ tracing::trace!(?expr);
+ let ty = match expr {
Expr::Missing => self.err_ty(),
&Expr::If { condition, then_branch, else_branch } => {
let expected = &expected.adjust_for_branches(&mut self.table);
@@ -300,27 +322,41 @@ impl InferenceContext<'_> {
let then_ty = self.infer_expr_inner(then_branch, expected, ExprIsRead::Yes);
let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
- let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table));
- coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch));
+ let mut coercion_sites = [then_branch, tgt_expr];
+ if let Some(else_branch) = else_branch {
+ coercion_sites[1] = else_branch;
+ }
+ let mut coerce = CoerceMany::with_coercion_sites(
+ expected
+ .coercion_target_type(&mut self.table)
+ .to_nextsolver(self.table.interner),
+ &coercion_sites,
+ );
+ coerce.coerce(
+ self,
+ &ObligationCause::new(),
+ then_branch,
+ then_ty.to_nextsolver(self.table.interner),
+ );
match else_branch {
Some(else_branch) => {
let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes);
let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
coerce.coerce(
self,
- Some(else_branch),
- &else_ty,
- CoercionCause::Expr(else_branch),
+ &ObligationCause::new(),
+ else_branch,
+ else_ty.to_nextsolver(self.table.interner),
);
self.diverges = condition_diverges | then_diverges & else_diverges;
}
None => {
- coerce.coerce_forced_unit(self, CoercionCause::Expr(tgt_expr));
+ coerce.coerce_forced_unit(self, tgt_expr, &ObligationCause::new(), true);
self.diverges = condition_diverges;
}
}
- coerce.complete(self)
+ coerce.complete(self).to_chalk(self.table.interner)
}
&Expr::Let { pat, expr } => {
let child_is_read = if self.pat_guaranteed_to_constitute_read_for_never(pat) {
@@ -373,7 +409,15 @@ impl InferenceContext<'_> {
}
}
Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => self
- .infer_closure(body, args, ret_type, arg_types, *closure_kind, tgt_expr, expected),
+ .infer_closure(
+ *body,
+ args,
+ *ret_type,
+ arg_types,
+ *closure_kind,
+ tgt_expr,
+ expected,
+ ),
Expr::Call { callee, args, .. } => self.infer_call(tgt_expr, *callee, args, expected),
Expr::MethodCall { receiver, args, method_name, generic_args } => self
.infer_method_call(
@@ -399,7 +443,7 @@ impl InferenceContext<'_> {
let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let mut all_arms_diverge = Diverges::Always;
for arm in arms.iter() {
- let input_ty = self.resolve_ty_shallow(&input_ty);
+ let input_ty = self.table.structurally_resolve_type(&input_ty);
self.infer_top_pat(arm.pat, &input_ty, None);
}
@@ -412,7 +456,7 @@ impl InferenceContext<'_> {
}
_ => self.table.new_type_var(),
};
- let mut coerce = CoerceMany::new(result_ty);
+ let mut coerce = CoerceMany::new(result_ty.to_nextsolver(self.table.interner));
for arm in arms.iter() {
if let Some(guard_expr) = arm.guard {
@@ -427,12 +471,17 @@ impl InferenceContext<'_> {
let arm_ty = self.infer_expr_inner(arm.expr, &expected, ExprIsRead::Yes);
all_arms_diverge &= self.diverges;
- coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr));
+ coerce.coerce(
+ self,
+ &ObligationCause::new(),
+ arm.expr,
+ arm_ty.to_nextsolver(self.table.interner),
+ );
}
self.diverges = matchee_diverges | all_arms_diverge;
- coerce.complete(self)
+ coerce.complete(self).to_chalk(self.table.interner)
}
}
Expr::Path(p) => self.infer_expr_path(p, tgt_expr.into(), tgt_expr),
@@ -450,7 +499,7 @@ impl InferenceContext<'_> {
let val_ty = if let Some(expr) = expr {
let opt_coerce_to = match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match &ctxt.coerce {
- Some(coerce) => coerce.expected_ty(),
+ Some(coerce) => coerce.expected_ty().to_chalk(self.table.interner),
None => {
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
expr: tgt_expr,
@@ -474,11 +523,12 @@ impl InferenceContext<'_> {
match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match ctxt.coerce.take() {
Some(mut coerce) => {
- let cause = match expr {
- Some(expr) => CoercionCause::Expr(expr),
- None => CoercionCause::Expr(tgt_expr),
- };
- coerce.coerce(self, expr, &val_ty, cause);
+ coerce.coerce(
+ self,
+ &ObligationCause::new(),
+ expr.unwrap_or(tgt_expr),
+ val_ty.to_nextsolver(self.table.interner),
+ );
// Avoiding borrowck
let ctxt = find_breakable(&mut self.breakables, label)
@@ -510,7 +560,13 @@ impl InferenceContext<'_> {
);
} else {
let unit = self.result.standard_types.unit.clone();
- let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty, CoerceNever::Yes);
+ let _ = self.coerce(
+ tgt_expr.into(),
+ unit.to_nextsolver(self.table.interner),
+ yield_ty.to_nextsolver(self.table.interner),
+ AllowTwoPhase::No,
+ CoerceNever::Yes,
+ );
}
resume_ty
} else {
@@ -649,7 +705,7 @@ impl InferenceContext<'_> {
&Expr::Box { expr } => self.infer_expr_box(expr, expected),
Expr::UnaryOp { expr, op } => {
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes);
- let inner_ty = self.resolve_ty_shallow(&inner_ty);
+ let inner_ty = self.table.structurally_resolve_type(&inner_ty);
// FIXME: Note down method resolution her
match op {
UnaryOp::Deref => {
@@ -666,11 +722,23 @@ impl InferenceContext<'_> {
Substitution::empty(Interner),
);
}
- if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) {
- self.resolve_ty_shallow(derefed)
+ if let Some(derefed) =
+ inner_ty.to_nextsolver(self.table.interner).builtin_deref(self.db, true)
+ {
+ self.table
+ .structurally_resolve_type(&derefed.to_chalk(self.table.interner))
} else {
- deref_by_trait(&mut self.table, inner_ty, false)
- .unwrap_or_else(|| self.err_ty())
+ let infer_ok = overloaded_deref_ty(
+ &self.table,
+ inner_ty.to_nextsolver(self.table.interner),
+ );
+ match infer_ok {
+ Some(infer_ok) => self
+ .table
+ .register_infer_ok(infer_ok)
+ .to_chalk(self.table.interner),
+ None => self.err_ty(),
+ }
}
}
UnaryOp::Neg => {
@@ -823,10 +891,10 @@ impl InferenceContext<'_> {
let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes);
if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) {
- let canonicalized = self.canonicalize(base_ty.clone());
+ let canonicalized =
+ self.canonicalize(base_ty.clone().to_nextsolver(self.table.interner));
let receiver_adjustments = method_resolution::resolve_indexing_op(
- self.db,
- self.table.trait_env.clone(),
+ &mut self.table,
canonicalized,
index_trait,
);
@@ -929,6 +997,7 @@ impl InferenceContext<'_> {
}
None => {
let expected_ty = expected.to_option(&mut self.table);
+ tracing::debug!(?expected_ty);
let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) {
Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty,
Some(TyKind::Scalar(Scalar::Char)) => {
@@ -998,18 +1067,30 @@ impl InferenceContext<'_> {
// allows them to be inferred based on how they are used later in the
// function.
if is_input {
- let ty = this.resolve_ty_shallow(&ty);
+ let ty = this.table.structurally_resolve_type(&ty);
match ty.kind(Interner) {
TyKind::FnDef(def, parameters) => {
let fnptr_ty = TyKind::Function(
CallableSig::from_def(this.db, *def, parameters).to_fn_ptr(),
)
.intern(Interner);
- _ = this.coerce(Some(expr), &ty, &fnptr_ty, CoerceNever::Yes);
+ _ = this.coerce(
+ expr.into(),
+ ty.to_nextsolver(this.table.interner),
+ fnptr_ty.to_nextsolver(this.table.interner),
+ AllowTwoPhase::No,
+ CoerceNever::Yes,
+ );
}
TyKind::Ref(mutbl, _, base_ty) => {
let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner);
- _ = this.coerce(Some(expr), &ty, &ptr_ty, CoerceNever::Yes);
+ _ = this.coerce(
+ expr.into(),
+ ty.to_nextsolver(this.table.interner),
+ ptr_ty.to_nextsolver(this.table.interner),
+ AllowTwoPhase::No,
+ CoerceNever::Yes,
+ );
}
_ => {}
}
@@ -1087,15 +1168,23 @@ impl InferenceContext<'_> {
let ret_ty = self.table.new_type_var();
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
- let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(ret_ty.clone()));
+ let prev_ret_coercion = self
+ .return_coercion
+ .replace(CoerceMany::new(ret_ty.to_nextsolver(self.table.interner)));
// FIXME: We should handle async blocks like we handle closures
let expected = &Expectation::has_type(ret_ty);
let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
let ty = this.infer_block(tgt_expr, *id, statements, *tail, None, expected);
if let Some(target) = expected.only_has_type(&mut this.table) {
- match this.coerce(Some(tgt_expr), &ty, &target, CoerceNever::Yes) {
- Ok(res) => res,
+ match this.coerce(
+ tgt_expr.into(),
+ ty.to_nextsolver(this.table.interner),
+ target.to_nextsolver(this.table.interner),
+ AllowTwoPhase::No,
+ CoerceNever::Yes,
+ ) {
+ Ok(res) => res.to_chalk(this.table.interner),
Err(_) => {
this.result.type_mismatches.insert(
tgt_expr.into(),
@@ -1204,13 +1293,21 @@ impl InferenceContext<'_> {
(elem_ty, consteval::usize_const(self.db, Some(0), krate))
}
Array::ElementList { elements, .. } => {
- let mut coerce = CoerceMany::new(elem_ty);
+ let mut coerce = CoerceMany::with_coercion_sites(
+ elem_ty.to_nextsolver(self.table.interner),
+ elements,
+ );
for &expr in elements.iter() {
let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes);
- coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr));
+ coerce.coerce(
+ self,
+ &ObligationCause::new(),
+ expr,
+ cur_elem_ty.to_nextsolver(self.table.interner),
+ );
}
(
- coerce.complete(self),
+ coerce.complete(self).to_chalk(self.table.interner),
consteval::usize_const(self.db, Some(elements.len() as u128), krate),
)
}
@@ -1249,11 +1346,17 @@ impl InferenceContext<'_> {
.return_coercion
.as_mut()
.expect("infer_return called outside function body")
- .expected_ty();
+ .expected_ty()
+ .to_chalk(self.table.interner);
let return_expr_ty =
self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes);
let mut coerce_many = self.return_coercion.take().unwrap();
- coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr));
+ coerce_many.coerce(
+ self,
+ &ObligationCause::new(),
+ expr,
+ return_expr_ty.to_nextsolver(self.table.interner),
+ );
self.return_coercion = Some(coerce_many);
}
@@ -1264,7 +1367,7 @@ impl InferenceContext<'_> {
self.infer_return(expr);
} else {
let mut coerce = self.return_coercion.take().unwrap();
- coerce.coerce_forced_unit(self, CoercionCause::Expr(ret));
+ coerce.coerce_forced_unit(self, ret, &ObligationCause::new(), true);
self.return_coercion = Some(coerce);
}
}
@@ -1281,7 +1384,7 @@ impl InferenceContext<'_> {
fn infer_expr_become(&mut self, expr: ExprId) -> Ty {
match &self.return_coercion {
Some(return_coercion) => {
- let ret_ty = return_coercion.expected_ty();
+ let ret_ty = return_coercion.expected_ty().to_chalk(self.table.interner);
let call_expr_ty = self.infer_expr_inner(
expr,
@@ -1414,15 +1517,16 @@ impl InferenceContext<'_> {
None => self.err_ty(),
};
- let ret_ty = self.normalize_associated_types_in(ret_ty);
+ let ret_ty = self.process_remote_user_written_ty(ret_ty);
if self.is_builtin_binop(&lhs_ty, &rhs_ty, op) {
// use knowledge of built-in binary ops, which can sometimes help inference
let builtin_ret = self.enforce_builtin_binop_types(&lhs_ty, &rhs_ty, op);
self.unify(&builtin_ret, &ret_ty);
+ builtin_ret
+ } else {
+ ret_ty
}
-
- ret_ty
}
fn infer_block(
@@ -1534,9 +1638,10 @@ impl InferenceContext<'_> {
};
if this
.coerce(
- Some(expr),
- &this.result.standard_types.unit.clone(),
- &t,
+ expr.into(),
+ this.result.standard_types.unit.to_nextsolver(this.table.interner),
+ t.to_nextsolver(this.table.interner),
+ AllowTwoPhase::No,
coerce_never,
)
.is_err()
@@ -1557,6 +1662,7 @@ impl InferenceContext<'_> {
});
self.resolver.reset_to_guard(g);
if let Some(prev_env) = prev_env {
+ self.table.param_env = prev_env.env.to_nextsolver(self.table.interner);
self.table.trait_env = prev_env;
}
@@ -1568,50 +1674,49 @@ impl InferenceContext<'_> {
receiver_ty: &Ty,
name: &Name,
) -> Option<(Ty, Either<FieldId, TupleFieldId>, Vec<Adjustment>, bool)> {
- let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false);
+ let interner = self.table.interner;
+ let mut autoderef = self.table.autoderef(receiver_ty.to_nextsolver(self.table.interner));
let mut private_field = None;
let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
- let (field_id, parameters) = match derefed_ty.kind(Interner) {
- TyKind::Tuple(_, substs) => {
+ let (field_id, parameters) = match derefed_ty.kind() {
+ crate::next_solver::TyKind::Tuple(substs) => {
return name.as_tuple_index().and_then(|idx| {
- substs
- .as_slice(Interner)
- .get(idx)
- .map(|a| a.assert_ty_ref(Interner))
- .cloned()
- .map(|ty| {
- (
- Either::Right(TupleFieldId {
- tuple: TupleId(
- self.tuple_field_accesses_rev
- .insert_full(substs.clone())
- .0
- as u32,
- ),
- index: idx as u32,
- }),
- ty,
- )
- })
+ substs.as_slice().get(idx).copied().map(|ty| {
+ (
+ Either::Right(TupleFieldId {
+ tuple: TupleId(
+ self.tuple_field_accesses_rev
+ .insert_full(substs.to_chalk(interner))
+ .0 as u32,
+ ),
+ index: idx as u32,
+ }),
+ ty.to_chalk(interner),
+ )
+ })
});
}
- &TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), ref parameters) => {
- let local_id = s.fields(self.db).field(name)?;
- let field = FieldId { parent: s.into(), local_id };
- (field, parameters.clone())
- }
- &TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), ref parameters) => {
- let local_id = u.fields(self.db).field(name)?;
- let field = FieldId { parent: u.into(), local_id };
- (field, parameters.clone())
- }
+ crate::next_solver::TyKind::Adt(adt, parameters) => match adt.def_id().0 {
+ hir_def::AdtId::StructId(s) => {
+ let local_id = s.fields(self.db).field(name)?;
+ let field = FieldId { parent: s.into(), local_id };
+ (field, parameters)
+ }
+ hir_def::AdtId::UnionId(u) => {
+ let local_id = u.fields(self.db).field(name)?;
+ let field = FieldId { parent: u.into(), local_id };
+ (field, parameters)
+ }
+ hir_def::AdtId::EnumId(_) => return None,
+ },
_ => return None,
};
+ let parameters: crate::Substitution = parameters.to_chalk(interner);
let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id]
.is_visible_from(self.db, self.resolver.module());
if !is_visible {
if private_field.is_none() {
- private_field = Some((field_id, parameters));
+ private_field = Some((field_id, parameters.clone()));
}
return None;
}
@@ -1623,20 +1728,18 @@ impl InferenceContext<'_> {
Some(match res {
Some((field_id, ty)) => {
- let adjustments = auto_deref_adjust_steps(&autoderef);
- let ty = self.insert_type_vars(ty);
- let ty = self.normalize_associated_types_in(ty);
+ let adjustments = autoderef.adjust_steps();
+ let ty = self.process_remote_user_written_ty(ty);
(ty, field_id, adjustments, true)
}
None => {
let (field_id, subst) = private_field?;
- let adjustments = auto_deref_adjust_steps(&autoderef);
+ let adjustments = autoderef.adjust_steps();
let ty = self.db.field_types(field_id.parent)[field_id.local_id]
.clone()
.substitute(Interner, &subst);
- let ty = self.insert_type_vars(ty);
- let ty = self.normalize_associated_types_in(ty);
+ let ty = self.process_remote_user_written_ty(ty);
(ty, Either::Left(field_id), adjustments, false)
}
@@ -1675,7 +1778,8 @@ impl InferenceContext<'_> {
None => {
// no field found, lets attempt to resolve it like a function so that IDE things
// work out while people are typing
- let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
+ let canonicalized_receiver =
+ self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner));
let resolved = method_resolution::lookup_method(
self.db,
&canonicalized_receiver,
@@ -1720,11 +1824,13 @@ impl InferenceContext<'_> {
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 interner = self.table.interner;
+ let mut derefs = self.table.autoderef(callee_ty.to_nextsolver(interner));
let (res, derefed_callee) = loop {
let Some((callee_deref_ty, _)) = derefs.next() else {
break (None, callee_ty.clone());
};
+ let callee_deref_ty = callee_deref_ty.to_chalk(interner);
if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) {
break (Some(res), callee_deref_ty);
}
@@ -1735,28 +1841,30 @@ impl InferenceContext<'_> {
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)
- {
- self.add_current_closure_dependency(*c);
- self.deferred_closures.entry(*c).or_default().push((
- derefed_callee.clone(),
- callee_ty.clone(),
- params.clone(),
- tgt_expr,
- ));
- }
+ let params_chalk =
+ params.iter().map(|param| param.to_chalk(interner)).collect::<Vec<_>>();
+ let mut adjustments = derefs.adjust_steps();
if let Some(fn_x) = func {
self.write_fn_trait_method_resolution(
fn_x,
&derefed_callee,
&mut adjustments,
&callee_ty,
- &params,
+ &params_chalk,
tgt_expr,
);
}
+ if let &TyKind::Closure(c, _) =
+ self.table.resolve_completely(callee_ty.clone()).kind(Interner)
+ {
+ self.add_current_closure_dependency(c.into());
+ self.deferred_closures.entry(c.into()).or_default().push((
+ derefed_callee.clone(),
+ callee_ty.clone(),
+ params_chalk,
+ tgt_expr,
+ ));
+ }
self.write_expr_adj(callee, adjustments.into_boxed_slice());
(params, ret_ty)
}
@@ -1765,7 +1873,7 @@ impl InferenceContext<'_> {
call_expr: tgt_expr,
found: callee_ty.clone(),
});
- (Vec::new(), self.err_ty())
+ (Vec::new(), crate::next_solver::Ty::new_error(interner, ErrorGuaranteed))
}
};
let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
@@ -1786,29 +1894,24 @@ impl InferenceContext<'_> {
tgt_expr: ExprId,
args: &[ExprId],
callee_ty: Ty,
- param_tys: &[Ty],
- ret_ty: Ty,
+ param_tys: &[crate::next_solver::Ty<'db>],
+ ret_ty: crate::next_solver::Ty<'db>,
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,
+ ret_ty,
+ expected,
+ args,
indices_to_skip,
is_varargs,
);
- self.normalize_associated_types_in(ret_ty)
+ self.table.normalize_associated_types_in_ns(ret_ty).to_chalk(self.table.interner)
}
fn infer_method_call(
@@ -1821,7 +1924,23 @@ impl InferenceContext<'_> {
expected: &Expectation,
) -> Ty {
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes);
- let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
+ let receiver_ty = self.table.structurally_resolve_type(&receiver_ty);
+
+ if matches!(
+ receiver_ty.kind(Interner),
+ TyKind::Error | TyKind::InferenceVar(_, TyVariableKind::General)
+ ) {
+ // Don't probe on error type, or on a fully unresolved infer var.
+ // FIXME: Emit an error if we're probing on an infer var (type annotations needed).
+ for &arg in args {
+ // Make sure we infer and record the arguments.
+ self.infer_expr_no_expect(arg, ExprIsRead::Yes);
+ }
+ return receiver_ty;
+ }
+
+ let canonicalized_receiver =
+ self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner));
let resolved = method_resolution::lookup_method(
self.db,
@@ -1912,14 +2031,21 @@ impl InferenceContext<'_> {
tgt_expr,
args,
callee_ty,
- sig.params().get(strip_first as usize..).unwrap_or(&[]),
- sig.ret().clone(),
+ &sig.params()
+ .get(strip_first as usize..)
+ .unwrap_or(&[])
+ .iter()
+ .map(|param| param.to_nextsolver(self.table.interner))
+ .collect::<Vec<_>>(),
+ sig.ret().to_nextsolver(self.table.interner),
&[],
true,
expected,
),
None => {
- self.check_call_arguments(tgt_expr, args, &[], &[], &[], true);
+ for &arg in args.iter() {
+ self.infer_expr_no_expect(arg, ExprIsRead::Yes);
+ }
self.err_ty()
}
}
@@ -1938,147 +2064,252 @@ impl InferenceContext<'_> {
) -> Ty {
let method_ty = method_ty.substitute(Interner, &substs);
self.register_obligations_for_call(&method_ty);
+ let interner = self.table.interner;
let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) =
match method_ty.callable_sig(self.db) {
Some(sig) => (
if !sig.params().is_empty() {
- (sig.params()[0].clone(), sig.params()[1..].to_vec())
+ (
+ sig.params()[0].to_nextsolver(interner),
+ sig.params()[1..]
+ .iter()
+ .map(|param| param.to_nextsolver(interner))
+ .collect(),
+ )
} else {
- (self.err_ty(), Vec::new())
+ (crate::next_solver::Ty::new_error(interner, ErrorGuaranteed), Vec::new())
},
- sig.ret().clone(),
+ sig.ret().to_nextsolver(interner),
sig.is_varargs,
),
- None => ((self.err_ty(), Vec::new()), self.err_ty(), true),
+ None => {
+ let formal_receiver_ty = self.table.next_ty_var();
+ let ret_ty = self.table.next_ty_var();
+ ((formal_receiver_ty, Vec::new()), ret_ty, true)
+ }
};
- self.unify(&formal_receiver_ty, &receiver_ty);
+ self.table.unify_ns(formal_receiver_ty, receiver_ty.to_nextsolver(interner));
- 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, &[], is_varargs);
- self.normalize_associated_types_in(ret_ty)
+ self.check_call_arguments(tgt_expr, &param_tys, ret_ty, expected, args, &[], is_varargs);
+ self.table.normalize_associated_types_in_ns(ret_ty).to_chalk(interner)
}
- fn expected_inputs_for_expected_output(
+ /// Generic function that factors out common logic from function calls,
+ /// method calls and overloaded operators.
+ pub(in super::super) fn check_call_arguments(
&mut self,
- expected_output: &Expectation,
- output: Ty,
- inputs: Vec<Ty>,
- ) -> Vec<Ty> {
- if let Some(expected_ty) = expected_output.only_has_type(&mut self.table) {
- self.table.fudge_inference(|table| {
- if table.try_unify(&expected_ty, &output).is_ok() {
- table.resolve_with_fallback(inputs, &|var, kind, _, _| match kind {
- chalk_ir::VariableKind::Ty(tk) => var.to_ty(Interner, tk).cast(Interner),
- chalk_ir::VariableKind::Lifetime => {
- var.to_lifetime(Interner).cast(Interner)
- }
- chalk_ir::VariableKind::Const(ty) => {
- var.to_const(Interner, ty).cast(Interner)
+ call_expr: ExprId,
+ // Types (as defined in the *signature* of the target function)
+ formal_input_tys: &[crate::next_solver::Ty<'db>],
+ formal_output: crate::next_solver::Ty<'db>,
+ // Expected output from the parent expression or statement
+ expectation: &Expectation,
+ // The expressions for each provided argument
+ provided_args: &[ExprId],
+ skip_indices: &[u32],
+ // Whether the function is variadic, for example when imported from C
+ c_variadic: bool,
+ ) {
+ let interner = self.table.interner;
+
+ // First, let's unify the formal method signature with the expectation eagerly.
+ // We use this to guide coercion inference; it's output is "fudged" which means
+ // any remaining type variables are assigned to new, unrelated variables. This
+ // is because the inference guidance here is only speculative.
+ let formal_output = self.table.resolve_vars_with_obligations(formal_output);
+ let expected_input_tys: Option<Vec<_>> = expectation
+ .only_has_type(&mut self.table)
+ .and_then(|expected_output| {
+ self.table
+ .infer_ctxt
+ .fudge_inference_if_ok(|| {
+ let mut ocx = ObligationCtxt::new(&self.table.infer_ctxt);
+
+ // Attempt to apply a subtyping relationship between the formal
+ // return type (likely containing type variables if the function
+ // is polymorphic) and the expected return type.
+ // No argument expectations are produced if unification fails.
+ let origin = ObligationCause::new();
+ ocx.sup(
+ &origin,
+ self.table.param_env,
+ expected_output.to_nextsolver(interner),
+ formal_output,
+ )?;
+ if !ocx.select_where_possible().is_empty() {
+ return Err(crate::next_solver::TypeError::Mismatch);
}
+
+ // Record all the argument types, with the args
+ // produced from the above subtyping unification.
+ Ok(Some(
+ formal_input_tys
+ .iter()
+ .map(|&ty| self.table.infer_ctxt.resolve_vars_if_possible(ty))
+ .collect(),
+ ))
})
- } else {
- Vec::new()
- }
+ .ok()
})
+ .unwrap_or_default();
+
+ // If there are no external expectations at the call site, just use the types from the function defn
+ let expected_input_tys = if let Some(expected_input_tys) = &expected_input_tys {
+ assert_eq!(expected_input_tys.len(), formal_input_tys.len());
+ expected_input_tys
} else {
- Vec::new()
- }
- }
+ formal_input_tys
+ };
- fn check_call_arguments(
- &mut self,
- expr: ExprId,
- args: &[ExprId],
- expected_inputs: &[Ty],
- param_tys: &[Ty],
- skip_indices: &[u32],
- ignore_arg_param_mismatch: bool,
- ) {
- let arg_count_mismatch =
- !ignore_arg_param_mismatch && args.len() != param_tys.len() + skip_indices.len();
- if arg_count_mismatch {
+ let minimum_input_count = expected_input_tys.len();
+ let provided_arg_count = provided_args.len() - skip_indices.len();
+
+ // Keep track of whether we *could possibly* be satisfied, i.e. whether we're on the happy path
+ // if the wrong number of arguments were supplied, we CAN'T be satisfied,
+ // and if we're c_variadic, the supplied arguments must be >= the minimum count from the function
+ // otherwise, they need to be identical, because rust doesn't currently support variadic functions
+ let args_count_matches = if c_variadic {
+ provided_arg_count >= minimum_input_count
+ } else {
+ provided_arg_count == minimum_input_count
+ };
+
+ if !args_count_matches {
self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount {
- call_expr: expr,
- expected: param_tys.len() + skip_indices.len(),
- found: args.len(),
+ call_expr,
+ expected: expected_input_tys.len() + skip_indices.len(),
+ found: provided_args.len(),
});
+ }
+
+ // We introduce a helper function to demand that a given argument satisfy a given input
+ // This is more complicated than just checking type equality, as arguments could be coerced
+ // This version writes those types back so further type checking uses the narrowed types
+ let demand_compatible = |this: &mut InferenceContext<'db>, idx| {
+ let formal_input_ty: crate::next_solver::Ty<'db> = formal_input_tys[idx];
+ let expected_input_ty: crate::next_solver::Ty<'db> = expected_input_tys[idx];
+ let provided_arg = provided_args[idx];
+
+ debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty);
+
+ // We're on the happy path here, so we'll do a more involved check and write back types
+ // To check compatibility, we'll do 3 things:
+ // 1. Unify the provided argument with the expected type
+ let expectation = Expectation::rvalue_hint(this, expected_input_ty.to_chalk(interner));
+
+ let checked_ty = this
+ .infer_expr_inner(provided_arg, &expectation, ExprIsRead::Yes)
+ .to_nextsolver(interner);
+
+ // 2. Coerce to the most detailed type that could be coerced
+ // to, which is `expected_ty` if `rvalue_hint` returns an
+ // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
+ let coerced_ty = expectation
+ .only_has_type(&mut this.table)
+ .map(|it| it.to_nextsolver(interner))
+ .unwrap_or(formal_input_ty);
+
+ // Cause selection errors caused by resolving a single argument to point at the
+ // argument and not the call. This lets us customize the span pointed to in the
+ // fulfillment error to be more accurate.
+ let coerced_ty = this.table.resolve_vars_with_obligations(coerced_ty);
+
+ let coerce_never = if this
+ .expr_guaranteed_to_constitute_read_for_never(provided_arg, ExprIsRead::Yes)
+ {
+ CoerceNever::Yes
+ } else {
+ CoerceNever::No
+ };
+ let coerce_error = this
+ .coerce(
+ provided_arg.into(),
+ checked_ty,
+ coerced_ty,
+ AllowTwoPhase::Yes,
+ coerce_never,
+ )
+ .err();
+ if coerce_error.is_some() {
+ return Err((coerce_error, coerced_ty, checked_ty));
+ }
+
+ // 3. Check if the formal type is actually equal to the checked one
+ // and register any such obligations for future type checks.
+ let formal_ty_error = this
+ .table
+ .infer_ctxt
+ .at(&ObligationCause::new(), this.table.param_env)
+ .eq(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty);
+
+ // If neither check failed, the types are compatible
+ match formal_ty_error {
+ Ok(crate::next_solver::infer::InferOk { obligations, value: () }) => {
+ this.table.register_predicates(obligations);
+ Ok(())
+ }
+ Err(err) => Err((Some(err), coerced_ty, checked_ty)),
+ }
};
- // Quoting https://github.com/rust-lang/rust/blob/6ef275e6c3cb1384ec78128eceeb4963ff788dca/src/librustc_typeck/check/mod.rs#L3325 --
+ // Check the arguments.
// We do this in a pretty awful way: first we type-check any arguments
// that are not closures, then we type-check the closures. This is so
// that we have more information about the types of arguments when we
// type-check the functions. This isn't really the right way to do this.
for check_closures in [false, true] {
- let mut skip_indices = skip_indices.iter().copied().fuse().peekable();
- let param_iter = param_tys.iter().cloned().chain(repeat(self.err_ty()));
- let expected_iter = expected_inputs
- .iter()
- .cloned()
- .chain(param_iter.clone().skip(expected_inputs.len()));
- for (idx, ((&arg, param_ty), expected_ty)) in
- args.iter().zip(param_iter).zip(expected_iter).enumerate()
- {
- let is_closure = matches!(&self.body[arg], Expr::Closure { .. });
- if is_closure != check_closures {
+ // More awful hacks: before we check argument types, try to do
+ // an "opportunistic" trait resolution of any trait bounds on
+ // the call. This helps coercions.
+ if check_closures {
+ self.table.select_obligations_where_possible();
+ }
+
+ let mut skip_indices = skip_indices.iter().copied();
+ // Check each argument, to satisfy the input it was provided for
+ // Visually, we're traveling down the diagonal of the compatibility matrix
+ for (idx, arg) in provided_args.iter().enumerate() {
+ if skip_indices.clone().next() == Some(idx as u32) {
+ skip_indices.next();
continue;
}
- while skip_indices.peek().is_some_and(|&i| i < idx as u32) {
- skip_indices.next();
+ // For this check, we do *not* want to treat async coroutine closures (async blocks)
+ // as proper closures. Doing so would regress type inference when feeding
+ // the return value of an argument-position async block to an argument-position
+ // closure wrapped in a block.
+ // See <https://github.com/rust-lang/rust/issues/112225>.
+ let is_closure = if let Expr::Closure { closure_kind, .. } = self.body[*arg] {
+ !matches!(closure_kind, ClosureKind::Coroutine(_))
+ } else {
+ false
+ };
+ if is_closure != check_closures {
+ continue;
}
- if skip_indices.peek().copied() == Some(idx as u32) {
+
+ if idx >= minimum_input_count {
+ // Make sure we've checked this expr at least once.
+ self.infer_expr_no_expect(*arg, ExprIsRead::Yes);
continue;
}
- // the difference between param_ty and expected here is that
- // expected is the parameter when the expected *return* type is
- // taken into account. So in `let _: &[i32] = identity(&[1, 2])`
- // the expected type is already `&[i32]`, whereas param_ty is
- // still an unbound type variable. We don't always want to force
- // the parameter to coerce to the expected type (for example in
- // `coerce_unsize_expected_type_4`).
- let param_ty = self.normalize_associated_types_in(param_ty);
- let expected_ty = self.normalize_associated_types_in(expected_ty);
- let expected = Expectation::rvalue_hint(self, expected_ty);
- // infer with the expected type we have...
- let ty = self.infer_expr_inner(arg, &expected, ExprIsRead::Yes);
-
- // then coerce to either the expected type or just the formal parameter type
- let coercion_target = if let Some(ty) = expected.only_has_type(&mut self.table) {
- // if we are coercing to the expectation, unify with the
- // formal parameter type to connect everything
- self.unify(&ty, &param_ty);
- ty
- } else {
- param_ty
- };
- // The function signature may contain some unknown types, so we need to insert
- // type vars here to avoid type mismatch false positive.
- let coercion_target = self.insert_type_vars(coercion_target);
-
- // Any expression that produces a value of type `!` must have diverged,
- // unless it's a place expression that isn't being read from, in which case
- // diverging would be unsound since we may never actually read the `!`.
- // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`.
- let coerce_never =
- if self.expr_guaranteed_to_constitute_read_for_never(arg, ExprIsRead::Yes) {
- CoerceNever::Yes
- } else {
- CoerceNever::No
- };
- if self.coerce(Some(arg), &ty, &coercion_target, coerce_never).is_err()
- && !arg_count_mismatch
+ if let Err((_error, expected, found)) = demand_compatible(self, idx)
+ && args_count_matches
{
+ // Don't report type mismatches if there is a mismatch in args count.
self.result.type_mismatches.insert(
- arg.into(),
- TypeMismatch { expected: coercion_target, actual: ty.clone() },
+ (*arg).into(),
+ TypeMismatch {
+ expected: expected.to_chalk(interner),
+ actual: found.to_chalk(interner),
+ },
);
}
}
}
+
+ if !args_count_matches {}
}
fn substs_for_method_call(
@@ -2227,7 +2458,7 @@ impl InferenceContext<'_> {
}
fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
- let callable_ty = self.resolve_ty_shallow(callable_ty);
+ let callable_ty = self.table.structurally_resolve_type(callable_ty);
if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(Interner) {
let def: CallableDefId = from_chalk(self.db, *fn_def);
let generic_predicates =
@@ -2316,9 +2547,9 @@ impl InferenceContext<'_> {
/// Dereferences a single level of immutable referencing.
fn deref_ty_if_possible(&mut self, ty: &Ty) -> Ty {
- let ty = self.resolve_ty_shallow(ty);
+ let ty = self.table.structurally_resolve_type(ty);
match ty.kind(Interner) {
- TyKind::Ref(Mutability::Not, _, inner) => self.resolve_ty_shallow(inner),
+ TyKind::Ref(Mutability::Not, _, inner) => self.table.structurally_resolve_type(inner),
_ => ty,
}
}
@@ -2438,10 +2669,22 @@ impl InferenceContext<'_> {
cb: impl FnOnce(&mut Self) -> T,
) -> (Option<Ty>, T) {
self.breakables.push({
- BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label }
+ BreakableContext {
+ kind,
+ may_break: false,
+ coerce: ty.map(|ty| CoerceMany::new(ty.to_nextsolver(self.table.interner))),
+ label,
+ }
});
let res = cb(self);
let ctx = self.breakables.pop().expect("breakable stack broken");
- (if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res)
+ (
+ if ctx.may_break {
+ ctx.coerce.map(|ctx| ctx.complete(self).to_chalk(self.table.interner))
+ } else {
+ None
+ },
+ res,
+ )
}
}