Auto merge of #122077 - oli-obk:eager_opaque_checks4, r=lcnr
Pass list of defineable opaque types into canonical queries This eliminates `DefiningAnchor::Bubble` for good and brings the old solver closer to the new one wrt cycles and nested obligations. At that point the difference between `DefiningAnchor::Bind([])` and `DefiningAnchor::Error` was academic. We only used the difference for some sanity checks, which actually had to be worked around in places, so I just removed `DefiningAnchor` entirely and just stored the list of opaques that may be defined. fixes #108498 fixes https://github.com/rust-lang/rust/issues/116877 * [x] run crater - https://github.com/rust-lang/rust/pull/122077#issuecomment-2013293931
This commit is contained in:
commit
b234e44944
59 changed files with 469 additions and 387 deletions
|
@ -75,7 +75,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
pub fn fork_with_intercrate(&self, intercrate: bool) -> Self {
|
||||
Self {
|
||||
tcx: self.tcx,
|
||||
defining_use_anchor: self.defining_use_anchor,
|
||||
defining_opaque_types: self.defining_opaque_types,
|
||||
considering_regions: self.considering_regions,
|
||||
skip_leak_check: self.skip_leak_check,
|
||||
inner: self.inner.clone(),
|
||||
|
|
|
@ -42,7 +42,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
V: TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
let (param_env, value) = value.into_parts();
|
||||
let param_env = self.tcx.canonical_param_env_cache.get_or_insert(
|
||||
let mut param_env = self.tcx.canonical_param_env_cache.get_or_insert(
|
||||
self.tcx,
|
||||
param_env,
|
||||
query_state,
|
||||
|
@ -59,6 +59,8 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
},
|
||||
);
|
||||
|
||||
param_env.defining_opaque_types = self.defining_opaque_types;
|
||||
|
||||
Canonicalizer::canonicalize_with_base(
|
||||
param_env,
|
||||
value,
|
||||
|
@ -541,6 +543,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
|
|||
max_universe: ty::UniverseIndex::ROOT,
|
||||
variables: List::empty(),
|
||||
value: (),
|
||||
defining_opaque_types: infcx.map(|i| i.defining_opaque_types).unwrap_or_default(),
|
||||
};
|
||||
Canonicalizer::canonicalize_with_base(
|
||||
base,
|
||||
|
@ -610,7 +613,15 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
|
|||
.max()
|
||||
.unwrap_or(ty::UniverseIndex::ROOT);
|
||||
|
||||
Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) }
|
||||
assert!(
|
||||
!infcx.is_some_and(|infcx| infcx.defining_opaque_types != base.defining_opaque_types)
|
||||
);
|
||||
Canonical {
|
||||
max_universe,
|
||||
variables: canonical_variables,
|
||||
value: (base.value, out_value),
|
||||
defining_opaque_types: base.defining_opaque_types,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a canonical variable replacing `kind` from the input,
|
||||
|
|
|
@ -505,12 +505,9 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
let b = instantiate_value(self.tcx, &result_args, b);
|
||||
debug!(?a, ?b, "constrain opaque type");
|
||||
// We use equate here instead of, for example, just registering the
|
||||
// opaque type's hidden value directly, because we may be instantiating
|
||||
// a query response that was canonicalized in an InferCtxt that had
|
||||
// a different defining anchor. In that case, we may have inferred
|
||||
// `NonLocalOpaque := LocalOpaque` but can only instantiate it in
|
||||
// the other direction as `LocalOpaque := NonLocalOpaque`. Using eq
|
||||
// here allows us to try both directions (in `InferCtxt::handle_opaque_type`).
|
||||
// opaque type's hidden value directly, because the hidden type may have been an inference
|
||||
// variable that got constrained to the opaque type itself. In that case we want to equate
|
||||
// the generic args of the opaque with the generic params of its hidden type version.
|
||||
obligations.extend(
|
||||
self.at(cause, param_env)
|
||||
.eq(
|
||||
|
|
|
@ -34,7 +34,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
|
|||
use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey};
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::traits::{select, DefiningAnchor};
|
||||
use rustc_middle::traits::select;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::fold::BoundVarReplacerDelegate;
|
||||
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||
|
@ -243,18 +243,8 @@ impl<'tcx> InferCtxtInner<'tcx> {
|
|||
pub struct InferCtxt<'tcx> {
|
||||
pub tcx: TyCtxt<'tcx>,
|
||||
|
||||
/// The `DefId` of the item in whose context we are performing inference or typeck.
|
||||
/// It is used to check whether an opaque type use is a defining use.
|
||||
///
|
||||
/// If it is `DefiningAnchor::Bubble`, we can't resolve opaque types here and need to bubble up
|
||||
/// the obligation. This frequently happens for
|
||||
/// short lived InferCtxt within queries. The opaque type obligations are forwarded
|
||||
/// to the outside until the end up in an `InferCtxt` for typeck or borrowck.
|
||||
///
|
||||
/// Its default value is `DefiningAnchor::Bind(&[])`, which means no opaque types may be defined.
|
||||
/// This way it is easier to catch errors that
|
||||
/// might come up during inference or typeck.
|
||||
pub defining_use_anchor: DefiningAnchor<'tcx>,
|
||||
/// The `DefIds` of the opaque types that may have their hidden types constrained.
|
||||
defining_opaque_types: &'tcx ty::List<LocalDefId>,
|
||||
|
||||
/// Whether this inference context should care about region obligations in
|
||||
/// the root universe. Most notably, this is used during hir typeck as region
|
||||
|
@ -401,6 +391,10 @@ impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> {
|
|||
fn probe_ct_var(&self, vid: ConstVid) -> Option<ty::Const<'tcx>> {
|
||||
self.probe_const_var(vid).ok()
|
||||
}
|
||||
|
||||
fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> {
|
||||
self.defining_opaque_types
|
||||
}
|
||||
}
|
||||
|
||||
/// See the `error_reporting` module for more details.
|
||||
|
@ -615,7 +609,7 @@ impl fmt::Display for FixupError {
|
|||
/// Used to configure inference contexts before their creation.
|
||||
pub struct InferCtxtBuilder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
defining_use_anchor: DefiningAnchor<'tcx>,
|
||||
defining_opaque_types: &'tcx ty::List<LocalDefId>,
|
||||
considering_regions: bool,
|
||||
skip_leak_check: bool,
|
||||
/// Whether we are in coherence mode.
|
||||
|
@ -630,7 +624,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> {
|
||||
InferCtxtBuilder {
|
||||
tcx: self,
|
||||
defining_use_anchor: DefiningAnchor::Bind(ty::List::empty()),
|
||||
defining_opaque_types: ty::List::empty(),
|
||||
considering_regions: true,
|
||||
skip_leak_check: false,
|
||||
intercrate: false,
|
||||
|
@ -646,8 +640,16 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
|||
/// It is only meant to be called in two places, for typeck
|
||||
/// (via `Inherited::build`) and for the inference context used
|
||||
/// in mir borrowck.
|
||||
pub fn with_opaque_type_inference(mut self, defining_use_anchor: DefiningAnchor<'tcx>) -> Self {
|
||||
self.defining_use_anchor = defining_use_anchor;
|
||||
pub fn with_opaque_type_inference(mut self, defining_anchor: LocalDefId) -> Self {
|
||||
self.defining_opaque_types = self.tcx.opaque_types_defined_by(defining_anchor);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_defining_opaque_types(
|
||||
mut self,
|
||||
defining_opaque_types: &'tcx ty::List<LocalDefId>,
|
||||
) -> Self {
|
||||
self.defining_opaque_types = defining_opaque_types;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -679,14 +681,14 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
|||
/// the bound values in `C` to their instantiated values in `V`
|
||||
/// (in other words, `S(C) = V`).
|
||||
pub fn build_with_canonical<T>(
|
||||
&mut self,
|
||||
self,
|
||||
span: Span,
|
||||
canonical: &Canonical<'tcx, T>,
|
||||
) -> (InferCtxt<'tcx>, T, CanonicalVarValues<'tcx>)
|
||||
where
|
||||
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
let infcx = self.build();
|
||||
let infcx = self.with_defining_opaque_types(canonical.defining_opaque_types).build();
|
||||
let (value, args) = infcx.instantiate_canonical(span, canonical);
|
||||
(infcx, value, args)
|
||||
}
|
||||
|
@ -694,7 +696,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
|||
pub fn build(&mut self) -> InferCtxt<'tcx> {
|
||||
let InferCtxtBuilder {
|
||||
tcx,
|
||||
defining_use_anchor,
|
||||
defining_opaque_types,
|
||||
considering_regions,
|
||||
skip_leak_check,
|
||||
intercrate,
|
||||
|
@ -702,7 +704,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
|||
} = *self;
|
||||
InferCtxt {
|
||||
tcx,
|
||||
defining_use_anchor,
|
||||
defining_opaque_types,
|
||||
considering_regions,
|
||||
skip_leak_check,
|
||||
inner: RefCell::new(InferCtxtInner::new()),
|
||||
|
@ -1230,6 +1232,12 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
self.inner.borrow().opaque_type_storage.opaque_types.clone()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool {
|
||||
let Some(id) = id.into().as_local() else { return false };
|
||||
self.defining_opaque_types.contains(&id)
|
||||
}
|
||||
|
||||
pub fn ty_to_string(&self, t: Ty<'tcx>) -> String {
|
||||
self.resolve_vars_if_possible(t).to_string()
|
||||
}
|
||||
|
|
|
@ -4,11 +4,10 @@ use crate::errors::OpaqueHiddenTypeDiag;
|
|||
use crate::infer::{InferCtxt, InferOk};
|
||||
use crate::traits::{self, PredicateObligation};
|
||||
use hir::def_id::{DefId, LocalDefId};
|
||||
use hir::OpaqueTyOrigin;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::traits::{DefiningAnchor, ObligationCause};
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::fold::BottomUpFolder;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
|
@ -54,16 +53,13 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}
|
||||
|
||||
let mut obligations = vec![];
|
||||
let replace_opaque_type = |def_id: DefId| {
|
||||
def_id.as_local().is_some_and(|def_id| self.opaque_type_origin(def_id).is_some())
|
||||
};
|
||||
let value = value.fold_with(&mut BottomUpFolder {
|
||||
tcx: self.tcx,
|
||||
lt_op: |lt| lt,
|
||||
ct_op: |ct| ct,
|
||||
ty_op: |ty| match *ty.kind() {
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })
|
||||
if replace_opaque_type(def_id) && !ty.has_escaping_bound_vars() =>
|
||||
if self.can_define_opaque_ty(def_id) && !ty.has_escaping_bound_vars() =>
|
||||
{
|
||||
let def_span = self.tcx.def_span(def_id);
|
||||
let span = if span.contains(def_span) { def_span } else { span };
|
||||
|
@ -106,56 +102,52 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
b,
|
||||
));
|
||||
}
|
||||
match self.defining_use_anchor {
|
||||
DefiningAnchor::Bind(_) => {
|
||||
// Check that this is `impl Trait` type is
|
||||
// declared by `parent_def_id` -- i.e., one whose
|
||||
// value we are inferring. At present, this is
|
||||
// always true during the first phase of
|
||||
// type-check, but not always true later on during
|
||||
// NLL. Once we support named opaque types more fully,
|
||||
// this same scenario will be able to arise during all phases.
|
||||
//
|
||||
// Here is an example using type alias `impl Trait`
|
||||
// that indicates the distinction we are checking for:
|
||||
//
|
||||
// ```rust
|
||||
// mod a {
|
||||
// pub type Foo = impl Iterator;
|
||||
// pub fn make_foo() -> Foo { .. }
|
||||
// }
|
||||
//
|
||||
// mod b {
|
||||
// fn foo() -> a::Foo { a::make_foo() }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Here, the return type of `foo` references an
|
||||
// `Opaque` indeed, but not one whose value is
|
||||
// presently being inferred. You can get into a
|
||||
// similar situation with closure return types
|
||||
// today:
|
||||
//
|
||||
// ```rust
|
||||
// fn foo() -> impl Iterator { .. }
|
||||
// fn bar() {
|
||||
// let x = || foo(); // returns the Opaque assoc with `foo`
|
||||
// }
|
||||
// ```
|
||||
if self.opaque_type_origin(def_id).is_none() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
DefiningAnchor::Bubble => {}
|
||||
// Check that this is `impl Trait` type is
|
||||
// declared by `parent_def_id` -- i.e., one whose
|
||||
// value we are inferring. At present, this is
|
||||
// always true during the first phase of
|
||||
// type-check, but not always true later on during
|
||||
// NLL. Once we support named opaque types more fully,
|
||||
// this same scenario will be able to arise during all phases.
|
||||
//
|
||||
// Here is an example using type alias `impl Trait`
|
||||
// that indicates the distinction we are checking for:
|
||||
//
|
||||
// ```rust
|
||||
// mod a {
|
||||
// pub type Foo = impl Iterator;
|
||||
// pub fn make_foo() -> Foo { .. }
|
||||
// }
|
||||
//
|
||||
// mod b {
|
||||
// fn foo() -> a::Foo { a::make_foo() }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Here, the return type of `foo` references an
|
||||
// `Opaque` indeed, but not one whose value is
|
||||
// presently being inferred. You can get into a
|
||||
// similar situation with closure return types
|
||||
// today:
|
||||
//
|
||||
// ```rust
|
||||
// fn foo() -> impl Iterator { .. }
|
||||
// fn bar() {
|
||||
// let x = || foo(); // returns the Opaque assoc with `foo`
|
||||
// }
|
||||
// ```
|
||||
if !self.can_define_opaque_ty(def_id) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() {
|
||||
// We could accept this, but there are various ways to handle this situation, and we don't
|
||||
// want to make a decision on it right now. Likely this case is so super rare anyway, that
|
||||
// no one encounters it in practice.
|
||||
// It does occur however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`,
|
||||
// where it is of no concern, so we only check for TAITs.
|
||||
if let Some(OpaqueTyOrigin::TyAlias { .. }) =
|
||||
b_def_id.as_local().and_then(|b_def_id| self.opaque_type_origin(b_def_id))
|
||||
if self.can_define_opaque_ty(b_def_id)
|
||||
&& self.tcx.is_type_alias_impl_trait(b_def_id)
|
||||
{
|
||||
self.tcx.dcx().emit_err(OpaqueHiddenTypeDiag {
|
||||
span: cause.span,
|
||||
|
@ -367,20 +359,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
op: |r| self.member_constraint(opaque_type_key, span, concrete_ty, r, &choice_regions),
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns the origin of the opaque type `def_id` if we're currently
|
||||
/// in its defining scope.
|
||||
#[instrument(skip(self), level = "trace", ret)]
|
||||
pub fn opaque_type_origin(&self, def_id: LocalDefId) -> Option<OpaqueTyOrigin> {
|
||||
let defined_opaque_types = match self.defining_use_anchor {
|
||||
DefiningAnchor::Bubble => return None,
|
||||
DefiningAnchor::Bind(bind) => bind,
|
||||
};
|
||||
|
||||
let origin = self.tcx.opaque_type_origin(def_id);
|
||||
|
||||
defined_opaque_types.contains(&def_id).then_some(origin)
|
||||
}
|
||||
}
|
||||
|
||||
/// Visitor that requires that (almost) all regions in the type visited outlive
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue