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.rs624
1 files changed, 378 insertions, 246 deletions
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 40de9234ab..e79868f4ae 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -37,45 +37,61 @@
use hir_def::{
CallableDefId,
+ attrs::AttrFlags,
hir::{ExprId, ExprOrPatId},
- lang_item::LangItem,
signatures::FunctionSignature,
};
-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 _},
+ inherent::{
+ Const as _, GenericArg as _, GenericArgs as _, IntoKind, Safety as _, SliceLike, Ty as _,
+ },
};
use smallvec::{SmallVec, smallvec};
use tracing::{debug, instrument};
-use triomphe::Arc;
use crate::{
- Adjust, Adjustment, AutoBorrow, PointerCast, TargetFeatures, TraitEnvironment,
+ Adjust, Adjustment, AutoBorrow, ParamEnvAndCrate, PointerCast, TargetFeatures,
autoderef::Autoderef,
- db::{HirDatabase, InternedClosureId},
- infer::{AllowTwoPhase, InferenceContext, TypeMismatch, unify::InferenceTable},
+ db::{HirDatabase, InternedClosure, InternedClosureId},
+ 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,
+ GenericArgs, ParamEnv, PolyFnSig, PredicateKind, Region, RegionKind, TraitRef, Ty, TyKind,
+ TypingMode,
+ abi::Safety,
infer::{
- InferCtxt, InferOk, InferResult,
+ DbInternerInferExt, InferCtxt, InferOk, InferResult,
relate::RelateResult,
select::{ImplSource, SelectionError},
traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations},
},
obligation_ctxt::ObligationCtxt,
},
+ upvars::upvars_mentioned,
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 param_env(&self) -> ParamEnv<'db>;
+ fn target_features(&self) -> (&TargetFeatures<'db>, 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
@@ -92,7 +108,7 @@ struct Coerce<'a, 'b, 'db> {
cause: ObligationCause,
}
-type CoerceResult<'db> = InferResult<'db, (Vec<Adjustment<'db>>, Ty<'db>)>;
+type CoerceResult<'db> = InferResult<'db, (Vec<Adjustment>, Ty<'db>)>;
/// Coercing a mutable reference to an immutable works, while
/// coercing `&T` to `&mut T` should be forbidden.
@@ -102,50 +118,63 @@ fn coerce_mutbls<'db>(from_mutbl: Mutability, to_mutbl: Mutability) -> RelateRes
/// This always returns `Ok(...)`.
fn success<'db>(
- adj: Vec<Adjustment<'db>>,
+ adj: Vec<Adjustment>,
target: Ty<'db>,
obligations: PredicateObligations<'db>,
) -> CoerceResult<'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 param_env(&self) -> ParamEnv<'db> {
+ self.delegate.param_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.param_env());
- let res = if this.use_lub {
+ let res = if self.use_lub {
at.lub(b, a)
} else {
at.sup(b, a)
@@ -157,7 +186,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() })
@@ -181,14 +210,17 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
&mut self,
a: Ty<'db>,
b: Ty<'db>,
- adjustments: impl IntoIterator<Item = Adjustment<'db>>,
- final_adjustment: Adjust<'db>,
+ adjustments: impl IntoIterator<Item = Adjustment>,
+ final_adjustment: Adjust,
) -> CoerceResult<'db> {
self.unify_raw(a, b).and_then(|InferOk { value: ty, obligations }| {
success(
adjustments
.into_iter()
- .chain(std::iter::once(Adjustment { target: ty, kind: final_adjustment }))
+ .chain(std::iter::once(Adjustment {
+ target: ty.store(),
+ kind: final_adjustment,
+ }))
.collect(),
ty,
obligations,
@@ -199,20 +231,20 @@ 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 {
return success(
- vec![Adjustment { kind: Adjust::NeverToAny, target: b }],
+ vec![Adjustment { kind: Adjust::NeverToAny, target: b.store() }],
b,
PredicateObligations::new(),
);
@@ -290,12 +322,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 +335,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
obligations.push(Obligation::new(
self.interner(),
self.cause.clone(),
- self.table.trait_env.env,
+ self.param_env(),
Binder::dummy(PredicateKind::Coerce(CoercePredicate {
a: source_ty,
b: target_ty,
@@ -335,8 +367,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 +387,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.param_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 +474,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 +537,10 @@ 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.store() });
debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments);
@@ -538,8 +555,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
@@ -601,10 +618,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
return Err(TypeError::Mismatch);
}
- 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),
- );
+ let lang_items = self.interner().lang_items();
+ let traits = (lang_items.Unsize, lang_items.CoerceUnsized);
let (Some(unsize_did), Some(coerce_unsized_did)) = traits else {
debug!("missing Unsize or CoerceUnsized traits");
return Err(TypeError::Mismatch);
@@ -620,19 +635,18 @@ 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::Deref(None), target: ty_a.store() },
Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)),
- target: Ty::new_ref(self.interner(), r_borrow, ty_a, mutbl_b),
+ kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
+ target: Ty::new_ref(self.interner(), r_borrow, ty_a, mutbl_b).store(),
},
))
}
@@ -640,22 +654,22 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
coerce_mutbls(mt_a, mt_b)?;
Some((
- Adjustment { kind: Adjust::Deref(None), target: ty_a },
+ Adjustment { kind: Adjust::Deref(None), target: ty_a.store() },
Adjustment {
kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)),
- target: Ty::new_ptr(self.interner(), ty_a, mt_b),
+ target: Ty::new_ptr(self.interner(), ty_a, mt_b).store(),
},
))
}
_ => None,
};
- let coerce_source = reborrow.as_ref().map_or(source, |(_, r)| r.target);
+ let coerce_source = reborrow.as_ref().map_or(source, |(_, r)| r.target.as_ref());
// Setup either a subtyping or a LUB relationship between
// 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 +691,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.param_env(),
TraitRef::new(
self.interner(),
coerce_unsized_did.into(),
@@ -694,14 +708,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,16 +729,16 @@ 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 {
let self_ty = trait_pred.self_ty();
- let unsize_ty = trait_pred.trait_ref.args.inner()[1].expect_ty();
+ let unsize_ty = trait_pred.trait_ref.args[1].expect_ty();
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 +808,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()
@@ -809,7 +823,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
b,
adjustment.map(|kind| Adjustment {
kind,
- target: Ty::new_fn_ptr(this.interner(), fn_ty_a),
+ target: Ty::new_fn_ptr(this.interner(), fn_ty_a).store(),
}),
Adjust::Pointer(PointerCast::UnsafeFnPointer),
)
@@ -825,15 +839,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,22 +855,22 @@ 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());
- if attrs.by_key(sym::rustc_force_inline).exists() {
+ let attrs = AttrFlags::query(self.db(), def_id.into());
+ if attrs.contains(AttrFlags::RUSTC_FORCE_INLINE) {
return Err(TypeError::ForceInlineCast);
}
- if b_hdr.safety.is_safe() && attrs.by_key(sym::target_feature).exists() {
+ if b_hdr.safety.is_safe() && attrs.contains(AttrFlags::HAS_TARGET_FEATURE) {
let fn_target_features =
- TargetFeatures::from_attrs_no_implications(&attrs);
+ TargetFeatures::from_fn_no_implications(self.db(), def_id);
// 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)
{
@@ -883,27 +897,15 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
fn coerce_closure_to_fn(
&mut self,
a: Ty<'db>,
- _closure_def_id_a: InternedClosureId,
+ closure_def_id_a: InternedClosureId,
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:
- // At this point we haven't done capture analysis, which means
- // that the ClosureArgs just contains an inference variable instead
- // of tuple of captured types.
- //
- // All we care here is if any variable is being captured and not the exact paths,
- // so we check `upvars_mentioned` for root variables being captured.
- TyKind::FnPtr(_, hdr) =>
- // if self
- // .db
- // .upvars_mentioned(closure_def_id_a.expect_local())
- // .is_none_or(|u| u.is_empty()) =>
- {
+ TyKind::FnPtr(_, hdr) if !is_capturing_closure(self.db(), closure_def_id_a) => {
// We coerce the closure, which has fn type
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
// to
@@ -911,10 +913,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
// or
// `unsafe fn(arg0,arg1,...) -> _`
let safety = hdr.safety;
- let closure_sig = args_a.closure_sig_untupled().map_bound(|mut sig| {
- sig.safety = hdr.safety;
- sig
- });
+ let closure_sig =
+ self.interner().signature_unclosure(args_a.as_closure().sig(), safety);
let pointer_ty = Ty::new_fn_ptr(self.interner(), closure_sig);
debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty);
self.unify_and(
@@ -930,8 +930,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 }),
@@ -949,7 +949,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
self.unify_and(
a_raw,
b,
- [Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }],
+ [Adjustment { kind: Adjust::Deref(None), target: mt_a.ty.store() }],
Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)),
)
} else if mt_a.mutbl != mutbl_b {
@@ -960,10 +960,37 @@ 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 param_env(&self) -> ParamEnv<'db> {
+ self.0.table.param_env
+ }
+
+ #[inline]
+ fn target_features(&self) -> (&TargetFeatures<'db>, 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 +1004,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))?;
@@ -1037,7 +1066,7 @@ impl<'db> InferenceContext<'_, 'db> {
let is_force_inline = |ty: Ty<'db>| {
if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(did)), _) = ty.kind() {
- self.db.attrs(did.into()).by_key(sym::rustc_force_inline).exists()
+ AttrFlags::query(self.db, did.into()).contains(AttrFlags::RUSTC_FORCE_INLINE)
} else {
false
}
@@ -1049,14 +1078,12 @@ impl<'db> InferenceContext<'_, 'db> {
// Special-case that coercion alone cannot handle:
// Function items or non-capturing closures of differing IDs or GenericArgs.
let (a_sig, b_sig) = {
- let is_capturing_closure = |_ty: Ty<'db>| {
- // FIXME:
- // if let TyKind::Closure(closure_def_id, _args) = ty.kind() {
- // self.db.upvars_mentioned(closure_def_id.expect_local()).is_some()
- // } else {
- // false
- // }
- false
+ let is_capturing_closure = |ty: Ty<'db>| {
+ if let TyKind::Closure(closure_def_id, _args) = ty.kind() {
+ is_capturing_closure(self.db, closure_def_id.0)
+ } else {
+ false
+ }
};
if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) {
(None, None)
@@ -1068,12 +1095,8 @@ impl<'db> InferenceContext<'_, 'db> {
match self.table.commit_if_ok(|table| {
// We need to eagerly handle nested obligations due to lazy norm.
let mut ocx = ObligationCtxt::new(&table.infer_ctxt);
- let value = ocx.lub(
- &ObligationCause::new(),
- table.trait_env.env,
- prev_ty,
- new_ty,
- )?;
+ let value =
+ ocx.lub(&ObligationCause::new(), table.param_env, prev_ty, new_ty)?;
if ocx.try_evaluate_obligations().is_empty() {
Ok(InferOk { value, obligations: ocx.into_pending_obligations() })
} else {
@@ -1090,23 +1113,28 @@ impl<'db> InferenceContext<'_, 'db> {
}
(TyKind::Closure(_, args), TyKind::FnDef(..)) => {
let b_sig = new_ty.fn_sig(self.table.interner());
- let a_sig = args.closure_sig_untupled().map_bound(|mut sig| {
- sig.safety = b_sig.safety();
- sig
- });
+ let a_sig = self
+ .interner()
+ .signature_unclosure(args.as_closure().sig(), b_sig.safety());
(Some(a_sig), Some(b_sig))
}
(TyKind::FnDef(..), TyKind::Closure(_, args)) => {
let a_sig = prev_ty.fn_sig(self.table.interner());
- let b_sig = args.closure_sig_untupled().map_bound(|mut sig| {
- sig.safety = a_sig.safety();
- sig
- });
+ let b_sig = self
+ .interner()
+ .signature_unclosure(args.as_closure().sig(), a_sig.safety());
(Some(a_sig), Some(b_sig))
}
- (TyKind::Closure(_, args_a), TyKind::Closure(_, args_b)) => {
- (Some(args_a.closure_sig_untupled()), Some(args_b.closure_sig_untupled()))
- }
+ (TyKind::Closure(_, args_a), TyKind::Closure(_, args_b)) => (
+ Some(
+ self.interner()
+ .signature_unclosure(args_a.as_closure().sig(), Safety::Safe),
+ ),
+ Some(
+ self.interner()
+ .signature_unclosure(args_b.as_closure().sig(), Safety::Safe),
+ ),
+ ),
_ => (None, None),
}
}
@@ -1116,7 +1144,7 @@ impl<'db> InferenceContext<'_, 'db> {
let sig = self
.table
.infer_ctxt
- .at(&ObligationCause::new(), self.table.trait_env.env)
+ .at(&ObligationCause::new(), self.table.param_env)
.lub(a_sig, b_sig)
.map(|ok| self.table.register_infer_ok(ok))?;
@@ -1139,12 +1167,15 @@ impl<'db> InferenceContext<'_, 'db> {
for &expr in exprs {
self.write_expr_adj(
expr,
- Box::new([Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }]),
+ Box::new([Adjustment {
+ kind: prev_adjustment.clone(),
+ target: fn_ptr.store(),
+ }]),
);
}
self.write_expr_adj(
new,
- Box::new([Adjustment { kind: next_adjustment, target: fn_ptr }]),
+ Box::new([Adjustment { kind: next_adjustment, target: fn_ptr.store() }]),
);
return Ok(fn_ptr);
}
@@ -1157,23 +1188,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) => {
@@ -1200,7 +1226,7 @@ impl<'db> InferenceContext<'_, 'db> {
.commit_if_ok(|table| {
table
.infer_ctxt
- .at(&ObligationCause::new(), table.trait_env.env)
+ .at(&ObligationCause::new(), table.param_env)
.lub(prev_ty, new_ty)
})
.unwrap_err())
@@ -1335,8 +1361,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 +1384,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.types.unit,
+ true,
+ label_unit_as_expected,
+ expr_is_read,
+ )
}
/// The inner coercion "engine". If `expression` is `None`, this
@@ -1372,6 +1408,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 +1445,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> {
expression_ty,
self.expected_ty,
AllowTwoPhase::No,
- CoerceNever::Yes,
+ expr_is_read,
)
} else {
match self.expressions {
@@ -1441,7 +1478,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> {
//
// Another example is `break` with no argument expression.
assert!(expression_ty.is_unit(), "if let hack without unit type");
- icx.table.infer_ctxt.at(cause, icx.table.trait_env.env).eq(expected, found).map(
+ icx.table.infer_ctxt.at(cause, icx.table.param_env).eq(expected, found).map(
|infer_ok| {
icx.table.register_infer_ok(infer_ok);
expression_ty
@@ -1468,14 +1505,14 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> {
// emit or provide suggestions on how to fix the initial error.
icx.set_tainted_by_errors();
- self.final_ty = Some(icx.types.error);
+ self.final_ty = Some(icx.types.types.error);
- icx.result.type_mismatches.insert(
+ icx.result.type_mismatches.get_or_insert_default().insert(
expression.into(),
if label_expression_as_expected {
- TypeMismatch { expected: found, actual: expected }
+ TypeMismatch { expected: found.store(), actual: expected.store() }
} else {
- TypeMismatch { expected, actual: found }
+ TypeMismatch { expected: expected.store(), actual: found.store() }
},
);
}
@@ -1491,101 +1528,196 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> {
// If we only had inputs that were of type `!` (or no
// inputs at all), then the final type is `!`.
assert_eq!(self.pushed, 0);
- icx.types.never
+ icx.types.types.never
}
}
}
pub fn could_coerce<'db>(
db: &'db dyn HirDatabase,
- env: Arc<TraitEnvironment<'db>>,
+ env: ParamEnvAndCrate<'db>,
tys: &Canonical<'db, (Ty<'db>, Ty<'db>)>,
) -> bool {
coerce(db, env, tys).is_ok()
}
+struct HirCoercionDelegate<'a, 'db> {
+ infcx: &'a InferCtxt<'db>,
+ param_env: ParamEnv<'db>,
+ target_features: &'a TargetFeatures<'db>,
+}
+
+impl<'db> CoerceDelegate<'db> for HirCoercionDelegate<'_, 'db> {
+ #[inline]
+ fn infcx(&self) -> &InferCtxt<'db> {
+ self.infcx
+ }
+ #[inline]
+ fn param_env(&self) -> ParamEnv<'db> {
+ self.param_env
+ }
+ fn target_features(&self) -> (&TargetFeatures<'db>, 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>>,
+ env: ParamEnvAndCrate<'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);
+) -> Result<(Vec<Adjustment>, Ty<'db>), TypeError<DbInterner<'db>>> {
+ let interner = DbInterner::new_with(db, env.krate);
+ 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,
+ param_env: env.param_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();
// 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 mut resolver =
+ Resolver { interner, debruijn: DebruijnIndex::ZERO, var_values: vars.var_values };
+ let ty = infcx.resolve_vars_if_possible(ty).fold_with(&mut resolver);
+ let adjustments = adjustments
+ .into_iter()
+ .map(|adjustment| Adjustment {
+ kind: adjustment.kind,
+ target: infcx
+ .resolve_vars_if_possible(adjustment.target.as_ref())
+ .fold_with(&mut resolver)
+ .store(),
+ })
+ .collect();
Ok((adjustments, ty))
}
+
+fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> bool {
+ let InternedClosure(owner, expr) = closure.loc(db);
+ upvars_mentioned(db, owner)
+ .is_some_and(|upvars| upvars.get(&expr).is_some_and(|upvars| !upvars.is_empty()))
+}