Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/coerce.rs')
-rw-r--r--crates/hir-ty/src/infer/coerce.rs454
1 files changed, 290 insertions, 164 deletions
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 40de9234ab..4acf964bbc 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -44,7 +44,8 @@ use hir_def::{
use intern::sym;
use rustc_ast_ir::Mutability;
use rustc_type_ir::{
- BoundVar, TypeAndMut,
+ BoundVar, DebruijnIndex, TyVid, TypeAndMut, TypeFoldable, TypeFolder, TypeSuperFoldable,
+ TypeVisitableExt,
error::TypeError,
inherent::{Const as _, GenericArg as _, IntoKind, Safety, SliceLike, Ty as _},
};
@@ -56,13 +57,16 @@ use crate::{
Adjust, Adjustment, AutoBorrow, PointerCast, TargetFeatures, TraitEnvironment,
autoderef::Autoderef,
db::{HirDatabase, InternedClosureId},
- infer::{AllowTwoPhase, InferenceContext, TypeMismatch, unify::InferenceTable},
+ infer::{
+ AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch, expr::ExprIsRead,
+ },
next_solver::{
Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper,
Canonical, ClauseKind, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed,
GenericArgs, PolyFnSig, PredicateKind, Region, RegionKind, TraitRef, Ty, TyKind,
+ TypingMode,
infer::{
- InferCtxt, InferOk, InferResult,
+ DbInternerInferExt, InferCtxt, InferOk, InferResult,
relate::RelateResult,
select::{ImplSource, SelectionError},
traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations},
@@ -72,10 +76,20 @@ use crate::{
utils::TargetFeatureIsSafeInTarget,
};
-struct Coerce<'a, 'b, 'db> {
- table: &'a mut InferenceTable<'db>,
- has_errors: &'a mut bool,
- target_features: &'a mut dyn FnMut() -> (&'b TargetFeatures, TargetFeatureIsSafeInTarget),
+trait CoerceDelegate<'db> {
+ fn infcx(&self) -> &InferCtxt<'db>;
+ fn env(&self) -> &TraitEnvironment<'db>;
+ fn target_features(&self) -> (&TargetFeatures, TargetFeatureIsSafeInTarget);
+
+ fn set_diverging(&mut self, diverging_ty: Ty<'db>);
+
+ fn set_tainted_by_errors(&mut self);
+
+ fn type_var_is_sized(&mut self, var: TyVid) -> bool;
+}
+
+struct Coerce<D> {
+ delegate: D,
use_lub: bool,
/// Determines whether or not allow_two_phase_borrow is set on any
/// autoref adjustments we create while coercing. We don't want to
@@ -109,43 +123,56 @@ fn success<'db>(
Ok(InferOk { value: (adj, target), obligations })
}
-impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
+impl<'db, D> Coerce<D>
+where
+ D: CoerceDelegate<'db>,
+{
#[inline]
fn set_tainted_by_errors(&mut self) {
- *self.has_errors = true;
+ self.delegate.set_tainted_by_errors();
+ }
+
+ #[inline]
+ fn infcx(&self) -> &InferCtxt<'db> {
+ self.delegate.infcx()
+ }
+
+ #[inline]
+ fn env(&self) -> &TraitEnvironment<'db> {
+ self.delegate.env()
}
#[inline]
fn interner(&self) -> DbInterner<'db> {
- self.table.interner()
+ self.infcx().interner
}
#[inline]
- fn infer_ctxt(&self) -> &InferCtxt<'db> {
- &self.table.infer_ctxt
+ fn db(&self) -> &'db dyn HirDatabase {
+ self.interner().db
}
pub(crate) fn commit_if_ok<T, E>(
&mut self,
f: impl FnOnce(&mut Self) -> Result<T, E>,
) -> Result<T, E> {
- let snapshot = self.table.snapshot();
+ let snapshot = self.infcx().start_snapshot();
let result = f(self);
match result {
Ok(_) => {}
Err(_) => {
- self.table.rollback_to(snapshot);
+ self.infcx().rollback_to(snapshot);
}
}
result
}
- fn unify_raw(&mut self, a: Ty<'db>, b: Ty<'db>) -> InferResult<'db, Ty<'db>> {
+ fn unify_raw(&self, a: Ty<'db>, b: Ty<'db>) -> InferResult<'db, Ty<'db>> {
debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub);
- self.commit_if_ok(|this| {
- let at = this.infer_ctxt().at(&this.cause, this.table.trait_env.env);
+ self.infcx().commit_if_ok(|_| {
+ let at = self.infcx().at(&self.cause, self.env().env);
- let res = if this.use_lub {
+ let res = if self.use_lub {
at.lub(b, a)
} else {
at.sup(b, a)
@@ -157,7 +184,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
// Filter these cases out to make sure our coercion is more accurate.
match res {
Ok(InferOk { value, obligations }) => {
- let mut ocx = ObligationCtxt::new(this.infer_ctxt());
+ let mut ocx = ObligationCtxt::new(self.infcx());
ocx.register_obligations(obligations);
if ocx.try_evaluate_obligations().is_empty() {
Ok(InferOk { value, obligations: ocx.into_pending_obligations() })
@@ -182,7 +209,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
a: Ty<'db>,
b: Ty<'db>,
adjustments: impl IntoIterator<Item = Adjustment<'db>>,
- final_adjustment: Adjust<'db>,
+ final_adjustment: Adjust,
) -> CoerceResult<'db> {
self.unify_raw(a, b).and_then(|InferOk { value: ty, obligations }| {
success(
@@ -199,15 +226,15 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
#[instrument(skip(self))]
fn coerce(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> {
// First, remove any resolved type variables (at the top level, at least):
- let a = self.table.shallow_resolve(a);
- let b = self.table.shallow_resolve(b);
+ let a = self.infcx().shallow_resolve(a);
+ let b = self.infcx().shallow_resolve(b);
debug!("Coerce.tys({:?} => {:?})", a, b);
// Coercing from `!` to any type is allowed:
if a.is_never() {
// If we're coercing into an inference var, mark it as possibly diverging.
if b.is_infer() {
- self.table.set_diverging(b);
+ self.delegate.set_diverging(b);
}
if self.coerce_never {
@@ -290,12 +317,12 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
/// fall back to subtyping (`unify_and`).
fn coerce_from_inference_variable(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> {
debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b);
- debug_assert!(a.is_infer() && self.table.shallow_resolve(a) == a);
- debug_assert!(self.table.shallow_resolve(b) == b);
+ debug_assert!(a.is_infer() && self.infcx().shallow_resolve(a) == a);
+ debug_assert!(self.infcx().shallow_resolve(b) == b);
if b.is_infer() {
// Two unresolved type variables: create a `Coerce` predicate.
- let target_ty = if self.use_lub { self.table.next_ty_var() } else { b };
+ let target_ty = if self.use_lub { self.infcx().next_ty_var() } else { b };
let mut obligations = PredicateObligations::with_capacity(2);
for &source_ty in &[a, b] {
@@ -303,7 +330,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
obligations.push(Obligation::new(
self.interner(),
self.cause.clone(),
- self.table.trait_env.env,
+ self.env().env,
Binder::dummy(PredicateKind::Coerce(CoercePredicate {
a: source_ty,
b: target_ty,
@@ -335,8 +362,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
mutbl_b: Mutability,
) -> CoerceResult<'db> {
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
- debug_assert!(self.table.shallow_resolve(a) == a);
- debug_assert!(self.table.shallow_resolve(b) == b);
+ debug_assert!(self.infcx().shallow_resolve(a) == a);
+ debug_assert!(self.infcx().shallow_resolve(b) == b);
// If we have a parameter of type `&M T_a` and the value
// provided is `expr`, we will be adding an implicit borrow,
@@ -355,10 +382,10 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
let mut first_error = None;
let mut r_borrow_var = None;
- let mut autoderef = Autoderef::new(self.table, a);
+ let mut autoderef = Autoderef::new_with_tracking(self.infcx(), self.env(), a);
let mut found = None;
- while let Some((referent_ty, autoderefs)) = autoderef.next() {
+ for (referent_ty, autoderefs) in autoderef.by_ref() {
if autoderefs == 0 {
// Don't let this pass, otherwise it would cause
// &T to autoref to &&T.
@@ -442,28 +469,18 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
} else {
if r_borrow_var.is_none() {
// create var lazily, at most once
- let r = autoderef.table.next_region_var();
+ let r = self.infcx().next_region_var();
r_borrow_var = Some(r); // [4] above
}
r_borrow_var.unwrap()
};
let derefd_ty_a = Ty::new_ref(
- autoderef.table.interner(),
+ self.interner(),
r,
referent_ty,
mutbl_b, // [1] above
);
- // We need to construct a new `Coerce` because of lifetimes.
- let mut coerce = Coerce {
- table: autoderef.table,
- has_errors: self.has_errors,
- target_features: self.target_features,
- use_lub: self.use_lub,
- allow_two_phase: self.allow_two_phase,
- coerce_never: self.coerce_never,
- cause: self.cause.clone(),
- };
- match coerce.unify_raw(derefd_ty_a, b) {
+ match self.unify_raw(derefd_ty_a, b) {
Ok(ok) => {
found = Some(ok);
break;
@@ -515,15 +532,9 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
autoderef.adjust_steps_as_infer_ok();
obligations.extend(o);
- // Now apply the autoref. We have to extract the region out of
- // the final ref type we got.
- let TyKind::Ref(region, _, _) = ty.kind() else {
- panic!("expected a ref type, got {:?}", ty);
- };
- adjustments.push(Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl_b)),
- target: ty,
- });
+ // Now apply the autoref.
+ let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase);
+ adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: ty });
debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments);
@@ -538,8 +549,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
#[instrument(skip(self), level = "debug")]
fn coerce_unsized(&mut self, source: Ty<'db>, target: Ty<'db>) -> CoerceResult<'db> {
debug!(?source, ?target);
- debug_assert!(self.table.shallow_resolve(source) == source);
- debug_assert!(self.table.shallow_resolve(target) == target);
+ debug_assert!(self.infcx().shallow_resolve(source) == source);
+ debug_assert!(self.infcx().shallow_resolve(target) == target);
// We don't apply any coercions incase either the source or target
// aren't sufficiently well known but tend to instead just equate
@@ -602,8 +613,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
}
let traits = (
- LangItem::Unsize.resolve_trait(self.table.db, self.table.trait_env.krate),
- LangItem::CoerceUnsized.resolve_trait(self.table.db, self.table.trait_env.krate),
+ LangItem::Unsize.resolve_trait(self.db(), self.env().krate),
+ LangItem::CoerceUnsized.resolve_trait(self.db(), self.env().krate),
);
let (Some(unsize_did), Some(coerce_unsized_did)) = traits else {
debug!("missing Unsize or CoerceUnsized traits");
@@ -620,18 +631,17 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
(TyKind::Ref(_, ty_a, mutbl_a), TyKind::Ref(_, _, mutbl_b)) => {
coerce_mutbls(mutbl_a, mutbl_b)?;
- let r_borrow = self.table.next_region_var();
+ let r_borrow = self.infcx().next_region_var();
// We don't allow two-phase borrows here, at least for initial
// implementation. If it happens that this coercion is a function argument,
// the reborrow in coerce_borrowed_ptr will pick it up.
- // let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No);
- let mutbl = mutbl_b;
+ let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No);
Some((
Adjustment { kind: Adjust::Deref(None), target: ty_a },
Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)),
+ kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
target: Ty::new_ref(self.interner(), r_borrow, ty_a, mutbl_b),
},
))
@@ -655,7 +665,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
// the `CoerceUnsized` target type and the expected type.
// We only have the latter, so we use an inference variable
// for the former and let type inference do the rest.
- let coerce_target = self.table.next_ty_var();
+ let coerce_target = self.infcx().next_ty_var();
let mut coercion = self.unify_and(
coerce_target,
@@ -677,7 +687,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
let mut queue: SmallVec<[PredicateObligation<'db>; 4]> = smallvec![Obligation::new(
self.interner(),
cause,
- self.table.trait_env.env,
+ self.env().env,
TraitRef::new(
self.interner(),
coerce_unsized_did.into(),
@@ -694,14 +704,14 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
Some(PredicateKind::Clause(ClauseKind::Trait(trait_pred)))
if traits.contains(&trait_pred.def_id().0) =>
{
- self.infer_ctxt().resolve_vars_if_possible(trait_pred)
+ self.infcx().resolve_vars_if_possible(trait_pred)
}
// Eagerly process alias-relate obligations in new trait solver,
// since these can be emitted in the process of solving trait goals,
// but we need to constrain vars before processing goals mentioning
// them.
Some(PredicateKind::AliasRelate(..)) => {
- let mut ocx = ObligationCtxt::new(self.infer_ctxt());
+ let mut ocx = ObligationCtxt::new(self.infcx());
ocx.register_obligation(obligation);
if !ocx.try_evaluate_obligations().is_empty() {
return Err(TypeError::Mismatch);
@@ -715,7 +725,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
}
};
debug!("coerce_unsized resolve step: {:?}", trait_pred);
- match self.infer_ctxt().select(&obligation.with(self.interner(), trait_pred)) {
+ match self.infcx().select(&obligation.with(self.interner(), trait_pred)) {
// Uncertain or unimplemented.
Ok(None) => {
if trait_pred.def_id().0 == unsize_did {
@@ -724,7 +734,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred);
match (self_ty.kind(), unsize_ty.kind()) {
(TyKind::Infer(rustc_type_ir::TyVar(v)), TyKind::Dynamic(..))
- if self.table.type_var_is_sized(v) =>
+ if self.delegate.type_var_is_sized(v) =>
{
debug!("coerce_unsized: have sized infer {:?}", v);
coercion.obligations.push(obligation);
@@ -794,9 +804,9 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
&mut self,
fn_ty_a: PolyFnSig<'db>,
b: Ty<'db>,
- adjustment: Option<Adjust<'db>>,
+ adjustment: Option<Adjust>,
) -> CoerceResult<'db> {
- debug_assert!(self.table.shallow_resolve(b) == b);
+ debug_assert!(self.infcx().shallow_resolve(b) == b);
self.commit_if_ok(|this| {
if let TyKind::FnPtr(_, hdr_b) = b.kind()
@@ -825,15 +835,15 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
fn coerce_from_fn_pointer(&mut self, fn_ty_a: PolyFnSig<'db>, b: Ty<'db>) -> CoerceResult<'db> {
debug!(?fn_ty_a, ?b, "coerce_from_fn_pointer");
- debug_assert!(self.table.shallow_resolve(b) == b);
+ debug_assert!(self.infcx().shallow_resolve(b) == b);
self.coerce_from_safe_fn(fn_ty_a, b, None)
}
fn coerce_from_fn_item(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> {
debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b);
- debug_assert!(self.table.shallow_resolve(a) == a);
- debug_assert!(self.table.shallow_resolve(b) == b);
+ debug_assert!(self.infcx().shallow_resolve(a) == a);
+ debug_assert!(self.infcx().shallow_resolve(b) == b);
match b.kind() {
TyKind::FnPtr(_, b_hdr) => {
@@ -841,11 +851,11 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
if let TyKind::FnDef(def_id, _) = a.kind() {
// Intrinsics are not coercible to function pointers
if let CallableDefId::FunctionId(def_id) = def_id.0 {
- if FunctionSignature::is_intrinsic(self.table.db, def_id) {
+ if FunctionSignature::is_intrinsic(self.db(), def_id) {
return Err(TypeError::IntrinsicCast);
}
- let attrs = self.table.db.attrs(def_id.into());
+ let attrs = self.db().attrs(def_id.into());
if attrs.by_key(sym::rustc_force_inline).exists() {
return Err(TypeError::ForceInlineCast);
}
@@ -856,7 +866,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
// Allow the coercion if the current function has all the features that would be
// needed to call the coercee safely.
let (target_features, target_feature_is_safe) =
- (self.target_features)();
+ self.delegate.target_features();
if target_feature_is_safe == TargetFeatureIsSafeInTarget::No
&& !target_features.enabled.is_superset(&fn_target_features.enabled)
{
@@ -887,8 +897,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
args_a: GenericArgs<'db>,
b: Ty<'db>,
) -> CoerceResult<'db> {
- debug_assert!(self.table.shallow_resolve(a) == a);
- debug_assert!(self.table.shallow_resolve(b) == b);
+ debug_assert!(self.infcx().shallow_resolve(a) == a);
+ debug_assert!(self.infcx().shallow_resolve(b) == b);
match b.kind() {
// FIXME: We need to have an `upvars_mentioned()` query:
@@ -930,8 +940,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
fn coerce_raw_ptr(&mut self, a: Ty<'db>, b: Ty<'db>, mutbl_b: Mutability) -> CoerceResult<'db> {
debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b);
- debug_assert!(self.table.shallow_resolve(a) == a);
- debug_assert!(self.table.shallow_resolve(b) == b);
+ debug_assert!(self.infcx().shallow_resolve(a) == a);
+ debug_assert!(self.infcx().shallow_resolve(b) == b);
let (is_ref, mt_a) = match a.kind() {
TyKind::Ref(_, ty, mutbl) => (true, TypeAndMut::<DbInterner<'db>> { ty, mutbl }),
@@ -960,10 +970,36 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
}
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub(crate) enum CoerceNever {
- No,
- Yes,
+struct InferenceCoercionDelegate<'a, 'b, 'db>(&'a mut InferenceContext<'b, 'db>);
+
+impl<'db> CoerceDelegate<'db> for InferenceCoercionDelegate<'_, '_, 'db> {
+ #[inline]
+ fn infcx(&self) -> &InferCtxt<'db> {
+ &self.0.table.infer_ctxt
+ }
+ #[inline]
+ fn env(&self) -> &TraitEnvironment<'db> {
+ &self.0.table.trait_env
+ }
+ #[inline]
+ fn target_features(&self) -> (&TargetFeatures, TargetFeatureIsSafeInTarget) {
+ self.0.target_features()
+ }
+
+ #[inline]
+ fn set_diverging(&mut self, diverging_ty: Ty<'db>) {
+ self.0.table.set_diverging(diverging_ty);
+ }
+
+ #[inline]
+ fn set_tainted_by_errors(&mut self) {
+ self.0.set_tainted_by_errors();
+ }
+
+ #[inline]
+ fn type_var_is_sized(&mut self, var: TyVid) -> bool {
+ self.0.table.type_var_is_sized(var)
+ }
}
impl<'db> InferenceContext<'_, 'db> {
@@ -977,24 +1013,26 @@ impl<'db> InferenceContext<'_, 'db> {
expr_ty: Ty<'db>,
mut target: Ty<'db>,
allow_two_phase: AllowTwoPhase,
- coerce_never: CoerceNever,
+ expr_is_read: ExprIsRead,
) -> RelateResult<'db, Ty<'db>> {
let source = self.table.try_structurally_resolve_type(expr_ty);
target = self.table.try_structurally_resolve_type(target);
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
let cause = ObligationCause::new();
- let krate = self.krate();
+ let coerce_never = match expr {
+ ExprOrPatId::ExprId(idx) => {
+ self.expr_guaranteed_to_constitute_read_for_never(idx, expr_is_read)
+ }
+ // `PatId` is passed for `PatKind::Path`.
+ ExprOrPatId::PatId(_) => false,
+ };
let mut coerce = Coerce {
- table: &mut self.table,
- has_errors: &mut self.result.has_errors,
+ delegate: InferenceCoercionDelegate(self),
cause,
allow_two_phase,
- coerce_never: matches!(coerce_never, CoerceNever::Yes),
+ coerce_never,
use_lub: false,
- target_features: &mut || {
- Self::target_features(self.db, &self.target_features, self.owner, krate)
- },
};
let ok = coerce.commit_if_ok(|coerce| coerce.coerce(source, target))?;
@@ -1157,23 +1195,18 @@ impl<'db> InferenceContext<'_, 'db> {
//
// NOTE: we set `coerce_never` to `true` here because coercion LUBs only
// operate on values and not places, so a never coercion is valid.
- let krate = self.krate();
let mut coerce = Coerce {
- table: &mut self.table,
- has_errors: &mut self.result.has_errors,
+ delegate: InferenceCoercionDelegate(self),
cause: ObligationCause::new(),
allow_two_phase: AllowTwoPhase::No,
coerce_never: true,
use_lub: true,
- target_features: &mut || {
- Self::target_features(self.db, &self.target_features, self.owner, krate)
- },
};
// First try to coerce the new expression to the type of the previous ones,
// but only if the new expression has no coercion already applied to it.
let mut first_error = None;
- if !self.result.expr_adjustments.contains_key(&new) {
+ if !coerce.delegate.0.result.expr_adjustments.contains_key(&new) {
let result = coerce.commit_if_ok(|coerce| coerce.coerce(new_ty, prev_ty));
match result {
Ok(ok) => {
@@ -1335,8 +1368,9 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> {
cause: &ObligationCause,
expression: ExprId,
expression_ty: Ty<'db>,
+ expr_is_read: ExprIsRead,
) {
- self.coerce_inner(icx, cause, expression, expression_ty, false, false)
+ self.coerce_inner(icx, cause, expression, expression_ty, false, false, expr_is_read)
}
/// Indicates that one of the inputs is a "forced unit". This
@@ -1357,8 +1391,17 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> {
expr: ExprId,
cause: &ObligationCause,
label_unit_as_expected: bool,
+ expr_is_read: ExprIsRead,
) {
- self.coerce_inner(icx, cause, expr, icx.types.unit, true, label_unit_as_expected)
+ self.coerce_inner(
+ icx,
+ cause,
+ expr,
+ icx.types.unit,
+ true,
+ label_unit_as_expected,
+ expr_is_read,
+ )
}
/// The inner coercion "engine". If `expression` is `None`, this
@@ -1372,6 +1415,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> {
mut expression_ty: Ty<'db>,
force_unit: bool,
label_expression_as_expected: bool,
+ expr_is_read: ExprIsRead,
) {
// Incorporate whatever type inference information we have
// until now; in principle we might also want to process
@@ -1408,7 +1452,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> {
expression_ty,
self.expected_ty,
AllowTwoPhase::No,
- CoerceNever::Yes,
+ expr_is_read,
)
} else {
match self.expressions {
@@ -1504,88 +1548,170 @@ pub fn could_coerce<'db>(
coerce(db, env, tys).is_ok()
}
+struct HirCoercionDelegate<'a, 'db> {
+ infcx: &'a InferCtxt<'db>,
+ env: &'a TraitEnvironment<'db>,
+ target_features: &'a TargetFeatures,
+}
+
+impl<'db> CoerceDelegate<'db> for HirCoercionDelegate<'_, 'db> {
+ #[inline]
+ fn infcx(&self) -> &InferCtxt<'db> {
+ self.infcx
+ }
+ #[inline]
+ fn env(&self) -> &TraitEnvironment<'db> {
+ self.env
+ }
+ fn target_features(&self) -> (&TargetFeatures, TargetFeatureIsSafeInTarget) {
+ (self.target_features, TargetFeatureIsSafeInTarget::No)
+ }
+ fn set_diverging(&mut self, _diverging_ty: Ty<'db>) {}
+ fn set_tainted_by_errors(&mut self) {}
+ fn type_var_is_sized(&mut self, _var: TyVid) -> bool {
+ false
+ }
+}
+
fn coerce<'db>(
db: &'db dyn HirDatabase,
env: Arc<TraitEnvironment<'db>>,
tys: &Canonical<'db, (Ty<'db>, Ty<'db>)>,
) -> Result<(Vec<Adjustment<'db>>, Ty<'db>), TypeError<DbInterner<'db>>> {
- let mut table = InferenceTable::new(db, env, None);
- let interner = table.interner();
- let ((ty1_with_vars, ty2_with_vars), vars) = table.infer_ctxt.instantiate_canonical(tys);
+ let interner = DbInterner::new_with(db, Some(env.krate), env.block);
+ let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
+ let ((ty1_with_vars, ty2_with_vars), vars) = infcx.instantiate_canonical(tys);
let cause = ObligationCause::new();
// FIXME: Target features.
let target_features = TargetFeatures::default();
let mut coerce = Coerce {
- table: &mut table,
- has_errors: &mut false,
+ delegate: HirCoercionDelegate {
+ infcx: &infcx,
+ env: &env,
+ target_features: &target_features,
+ },
cause,
allow_two_phase: AllowTwoPhase::No,
coerce_never: true,
use_lub: false,
- target_features: &mut || (&target_features, TargetFeatureIsSafeInTarget::No),
};
- let InferOk { value: (adjustments, ty), obligations } =
- coerce.coerce(ty1_with_vars, ty2_with_vars)?;
- table.register_predicates(obligations);
+ let infer_ok = coerce.coerce(ty1_with_vars, ty2_with_vars)?;
+ let mut ocx = ObligationCtxt::new(&infcx);
+ let (adjustments, ty) = ocx.register_infer_ok_obligations(infer_ok);
+ _ = ocx.try_evaluate_obligations();
+ let (adjustments, ty) = infcx.resolve_vars_if_possible((adjustments, ty));
// default any type vars that weren't unified back to their original bound vars
// (kind of hacky)
- let mut fallback_ty = |debruijn, infer| {
- let var = vars.var_values.iter().position(|arg| {
- arg.as_type().is_some_and(|ty| match ty.kind() {
- TyKind::Infer(it) => infer == it,
- _ => false,
- })
- });
- var.map_or_else(
- || Ty::new_error(interner, ErrorGuaranteed),
- |i| {
- Ty::new_bound(
- interner,
- debruijn,
- BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_usize(i) },
+
+ struct Resolver<'db> {
+ interner: DbInterner<'db>,
+ debruijn: DebruijnIndex,
+ var_values: GenericArgs<'db>,
+ }
+
+ impl<'db> TypeFolder<DbInterner<'db>> for Resolver<'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.interner
+ }
+
+ fn fold_binder<T>(&mut self, t: Binder<'db, T>) -> Binder<'db, T>
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ self.debruijn.shift_in(1);
+ let result = t.super_fold_with(self);
+ self.debruijn.shift_out(1);
+ result
+ }
+
+ fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
+ if !t.has_infer() {
+ return t;
+ }
+
+ if let TyKind::Infer(infer) = t.kind() {
+ let var = self.var_values.iter().position(|arg| {
+ arg.as_type().is_some_and(|ty| match ty.kind() {
+ TyKind::Infer(it) => infer == it,
+ _ => false,
+ })
+ });
+ var.map_or_else(
+ || Ty::new_error(self.interner, ErrorGuaranteed),
+ |i| {
+ Ty::new_bound(
+ self.interner,
+ self.debruijn,
+ BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_usize(i) },
+ )
+ },
)
- },
- )
- };
- let mut fallback_const = |debruijn, infer| {
- let var = vars.var_values.iter().position(|arg| {
- arg.as_const().is_some_and(|ty| match ty.kind() {
- ConstKind::Infer(it) => infer == it,
- _ => false,
- })
- });
- var.map_or_else(
- || Const::new_error(interner, ErrorGuaranteed),
- |i| Const::new_bound(interner, debruijn, BoundConst { var: BoundVar::from_usize(i) }),
- )
- };
- let mut fallback_region = |debruijn, infer| {
- let var = vars.var_values.iter().position(|arg| {
- arg.as_region().is_some_and(|ty| match ty.kind() {
- RegionKind::ReVar(it) => infer == it,
- _ => false,
- })
- });
- var.map_or_else(
- || Region::error(interner),
- |i| {
- Region::new_bound(
- interner,
- debruijn,
- BoundRegion { kind: BoundRegionKind::Anon, var: BoundVar::from_usize(i) },
+ } else {
+ t.super_fold_with(self)
+ }
+ }
+
+ fn fold_const(&mut self, c: Const<'db>) -> Const<'db> {
+ if !c.has_infer() {
+ return c;
+ }
+
+ if let ConstKind::Infer(infer) = c.kind() {
+ let var = self.var_values.iter().position(|arg| {
+ arg.as_const().is_some_and(|ty| match ty.kind() {
+ ConstKind::Infer(it) => infer == it,
+ _ => false,
+ })
+ });
+ var.map_or_else(
+ || Const::new_error(self.interner, ErrorGuaranteed),
+ |i| {
+ Const::new_bound(
+ self.interner,
+ self.debruijn,
+ BoundConst { var: BoundVar::from_usize(i) },
+ )
+ },
)
- },
- )
- };
- // FIXME also map the types in the adjustments
- // FIXME: We don't fallback correctly since this is done on `InferenceContext` and we only have `InferenceTable`.
- let ty = table.resolve_with_fallback(
- ty,
- &mut fallback_ty,
- &mut fallback_const,
- &mut fallback_region,
- );
+ } else {
+ c.super_fold_with(self)
+ }
+ }
+
+ fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
+ if let RegionKind::ReVar(infer) = r.kind() {
+ let var = self.var_values.iter().position(|arg| {
+ arg.as_region().is_some_and(|ty| match ty.kind() {
+ RegionKind::ReVar(it) => infer == it,
+ _ => false,
+ })
+ });
+ var.map_or_else(
+ || Region::error(self.interner),
+ |i| {
+ Region::new_bound(
+ self.interner,
+ self.debruijn,
+ BoundRegion {
+ kind: BoundRegionKind::Anon,
+ var: BoundVar::from_usize(i),
+ },
+ )
+ },
+ )
+ } else {
+ r
+ }
+ }
+ }
+
+ // FIXME: We don't fallback correctly since this is done on `InferenceContext` and we only have `InferCtxt`.
+ let (adjustments, ty) = (adjustments, ty).fold_with(&mut Resolver {
+ interner,
+ debruijn: DebruijnIndex::ZERO,
+ var_values: vars.var_values,
+ });
Ok((adjustments, ty))
}