mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-07 09:13:07 +03:00
Auto merge of #155443 - jdonszelmann:canonical, r=lcnr
Improve caching by introducing `TypingMode::ErasedNotCoherence` r? @lcnr This introduces `TypingMode::ErasedNotCoherence`. Most typing modes contain a list of opaque types, which are quite often unused during canonicalization. With this change, any time we try canonicalization, we replace whichever typing mode we're currently in with `ErasedNotcoherence`, attempt to canonicalize, and if that fails *retry* in the original typing mode. If erased mode succeeds, this is beneficial because that way the opaque types don't end up in the cache key, allowing more cache reuse. This seems to have a small (0.5%) slowdown on most programs, but a dramatic (>60%) speedup in specific cases like the rustc-perf `wg-grammar` benchmark. Some more improvements are expected with "eager normalization", which is work that's under way right now.
This commit is contained in:
@@ -374,7 +374,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
||||
assert!(key.value.promoted.is_some() || !tcx.is_static(key.value.instance.def_id()));
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
match key.typing_env.typing_mode() {
|
||||
match key.typing_env.typing_mode().assert_not_erased() {
|
||||
ty::TypingMode::PostAnalysis => {}
|
||||
ty::TypingMode::Coherence
|
||||
| ty::TypingMode::Analysis { .. }
|
||||
|
||||
@@ -237,7 +237,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
|
||||
cid: GlobalId<'tcx>,
|
||||
) -> EvalToValTreeResult<'tcx> {
|
||||
if cfg!(debug_assertions) {
|
||||
match typing_env.typing_mode() {
|
||||
match typing_env.typing_mode().assert_not_erased() {
|
||||
ty::TypingMode::PostAnalysis => {}
|
||||
ty::TypingMode::Coherence
|
||||
| ty::TypingMode::Analysis { .. }
|
||||
|
||||
@@ -243,7 +243,7 @@ pub fn new(
|
||||
// types that are not specified in the opaque type. We also use MIR bodies whose opaque types have
|
||||
// already been revealed, so we'd be able to at least partially observe the hidden types anyways.
|
||||
if cfg!(debug_assertions) {
|
||||
match typing_env.typing_mode() {
|
||||
match typing_env.typing_mode().assert_not_erased() {
|
||||
TypingMode::PostAnalysis => {}
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
|
||||
@@ -62,6 +62,7 @@ enum CallStep<'tcx> {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub(crate) fn check_expr_call(
|
||||
&self,
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
};
|
||||
use rustc_infer::infer::{self, RegionVariableOrigin};
|
||||
use rustc_infer::traits::{DynCompatibilityViolation, Obligation};
|
||||
use rustc_middle::ty::{self, Const, Flags, Ty, TyCtxt, TypeVisitableExt, Unnormalized};
|
||||
use rustc_middle::ty::{
|
||||
self, CantBeErased, Const, Flags, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized,
|
||||
};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span};
|
||||
use rustc_trait_selection::error_reporting::TypeErrCtxt;
|
||||
@@ -161,6 +163,12 @@ pub(crate) fn new(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn typing_mode(&self) -> TypingMode<'tcx, CantBeErased> {
|
||||
// `FnCtxt` is never constructed in the trait solver, so we can safely use
|
||||
// `assert_not_erased`.
|
||||
self.infcx.typing_mode_raw().assert_not_erased()
|
||||
}
|
||||
|
||||
pub(crate) fn dcx(&self) -> DiagCtxtHandle<'a> {
|
||||
self.root_ctxt.infcx.dcx()
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ pub fn canonicalize_query<V>(
|
||||
query_state,
|
||||
)
|
||||
.unchecked_map(|(param_env, value)| param_env.and(value));
|
||||
CanonicalQueryInput { canonical, typing_mode: TypingModeEqWrapper(self.typing_mode()) }
|
||||
CanonicalQueryInput { canonical, typing_mode: TypingModeEqWrapper(self.typing_mode_raw()) }
|
||||
}
|
||||
|
||||
/// Canonicalizes a query *response* `V`. When we canonicalize a
|
||||
|
||||
@@ -26,8 +26,8 @@ fn disable_trait_solver_fast_paths(&self) -> bool {
|
||||
self.disable_trait_solver_fast_paths()
|
||||
}
|
||||
|
||||
fn typing_mode(&self) -> ty::TypingMode<'tcx> {
|
||||
self.typing_mode()
|
||||
fn typing_mode_raw(&self) -> ty::TypingMode<'tcx> {
|
||||
self.typing_mode_raw()
|
||||
}
|
||||
|
||||
fn universe(&self) -> ty::UniverseIndex {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypingEnv, TypingMode, fold_regions,
|
||||
};
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol};
|
||||
use rustc_type_ir::MayBeErased;
|
||||
use snapshot::undo_log::InferCtxtUndoLogs;
|
||||
use tracing::{debug, instrument};
|
||||
use type_variable::TypeVariableOrigin;
|
||||
@@ -640,14 +641,34 @@ pub fn next_trait_solver(&self) -> bool {
|
||||
self.next_trait_solver
|
||||
}
|
||||
|
||||
/// This method is deliberately called `..._raw`,
|
||||
/// since the output may possibly include [`TypingMode::ErasedNotCoherence`](TypingMode::ErasedNotCoherence).
|
||||
/// `ErasedNotCoherence` is an implementation detail of the next trait solver, see its docs for
|
||||
/// more information.
|
||||
///
|
||||
/// `InferCtxt` has two uses: the trait solver calls some methods on it, because the `InferCtxt`
|
||||
/// works as a kind of store for for example type unification information.
|
||||
/// `InferCtxt` is also often used outside the trait solver during typeck.
|
||||
/// There, we don't care about the `ErasedNotCoherence` case and should never encounter it.
|
||||
/// To make sure these two uses are never confused, we want to statically encode this information.
|
||||
///
|
||||
/// The `FnCtxt`, for example, is only used in the outside-trait-solver case. It has a non-raw
|
||||
/// version of the `typing_mode` method available that asserts `ErasedNotCoherence` is
|
||||
/// impossible, and returns a `TypingMode` where `ErasedNotCoherence` is made uninhabited using
|
||||
/// the [`CantBeErased`](rustc_type_ir::CantBeErased) enum. That way you don't even have to
|
||||
/// match on the variant and can safely ignore it.
|
||||
///
|
||||
/// Prefer non-raw apis if available. e.g.,
|
||||
/// - On the `FnCtxt`
|
||||
/// - on the `SelectionCtxt`
|
||||
#[inline(always)]
|
||||
pub fn disable_trait_solver_fast_paths(&self) -> bool {
|
||||
self.tcx.disable_trait_solver_fast_paths()
|
||||
pub fn typing_mode_raw(&self) -> TypingMode<'tcx> {
|
||||
self.typing_mode
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn typing_mode(&self) -> TypingMode<'tcx> {
|
||||
self.typing_mode
|
||||
pub fn disable_trait_solver_fast_paths(&self) -> bool {
|
||||
self.tcx.disable_trait_solver_fast_paths()
|
||||
}
|
||||
|
||||
/// Returns the origin of the type variable identified by `vid`.
|
||||
@@ -1071,7 +1092,7 @@ pub fn opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> Vec<ty::Ali
|
||||
#[inline(always)]
|
||||
pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool {
|
||||
debug_assert!(!self.next_trait_solver());
|
||||
match self.typing_mode() {
|
||||
match self.typing_mode_raw().assert_not_erased() {
|
||||
TypingMode::Analysis {
|
||||
defining_opaque_types_and_generators: defining_opaque_types,
|
||||
}
|
||||
@@ -1402,7 +1423,7 @@ pub fn create_next_universe(&self) -> ty::UniverseIndex {
|
||||
/// which contains the necessary information to use the trait system without
|
||||
/// using canonicalization or carrying this inference context around.
|
||||
pub fn typing_env(&self, param_env: ty::ParamEnv<'tcx>) -> ty::TypingEnv<'tcx> {
|
||||
let typing_mode = match self.typing_mode() {
|
||||
let typing_mode = match self.typing_mode_raw() {
|
||||
// FIXME(#132279): This erases the `defining_opaque_types` as it isn't possible
|
||||
// to handle them without proper canonicalization. This means we may cause cycle
|
||||
// errors and fail to reveal opaques while inside of bodies. We should rename this
|
||||
@@ -1414,6 +1435,7 @@ pub fn typing_env(&self, param_env: ty::ParamEnv<'tcx>) -> ty::TypingEnv<'tcx> {
|
||||
mode @ (ty::TypingMode::Coherence
|
||||
| ty::TypingMode::PostBorrowckAnalysis { .. }
|
||||
| ty::TypingMode::PostAnalysis) => mode,
|
||||
ty::TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(),
|
||||
};
|
||||
ty::TypingEnv::new(param_env, typing_mode)
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ pub fn handle_opaque_type(
|
||||
if def_id.is_local() =>
|
||||
{
|
||||
let def_id = def_id.expect_local();
|
||||
if self.typing_mode().is_coherence() {
|
||||
if self.typing_mode_raw().is_coherence() {
|
||||
// See comment on `insert_hidden_type` for why this is sufficient in coherence
|
||||
return Some(self.register_hidden_type(
|
||||
OpaqueTypeKey { def_id, args },
|
||||
@@ -229,7 +229,9 @@ pub fn insert_hidden_type(
|
||||
// value being folded. In simple cases like `-> impl Foo`,
|
||||
// these are the same span, but not in cases like `-> (impl
|
||||
// Foo, impl Bar)`.
|
||||
match self.typing_mode() {
|
||||
//
|
||||
// Note: we don't use this function in the next solver so we can safely call `assert_not_erased`
|
||||
match self.typing_mode_raw().assert_not_erased() {
|
||||
ty::TypingMode::Coherence => {
|
||||
// During intercrate we do not define opaque types but instead always
|
||||
// force ambiguity unless the hidden type is known to not implement
|
||||
|
||||
@@ -604,7 +604,7 @@ fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
|
||||
//
|
||||
// cc trait-system-refactor-initiative#108
|
||||
if self.infcx.next_trait_solver()
|
||||
&& !self.infcx.typing_mode().is_coherence()
|
||||
&& !self.infcx.typing_mode_raw().is_coherence()
|
||||
&& self.in_alias
|
||||
{
|
||||
inner.type_variables().equate(vid, new_var_id);
|
||||
@@ -736,7 +736,7 @@ fn consts(
|
||||
// See the comment for type inference variables
|
||||
// for more details.
|
||||
if self.infcx.next_trait_solver()
|
||||
&& !self.infcx.typing_mode().is_coherence()
|
||||
&& !self.infcx.typing_mode_raw().is_coherence()
|
||||
&& self.in_alias
|
||||
{
|
||||
variable_table.union(vid, new_var_id);
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
pub type CandidateSource<'tcx> = ir::solve::CandidateSource<TyCtxt<'tcx>>;
|
||||
pub type CanonicalInput<'tcx, P = ty::Predicate<'tcx>> = ir::solve::CanonicalInput<TyCtxt<'tcx>, P>;
|
||||
pub type CanonicalResponse<'tcx> = ir::solve::CanonicalResponse<TyCtxt<'tcx>>;
|
||||
pub type FetchEligibleAssocItemResponse<'tcx> =
|
||||
ir::solve::FetchEligibleAssocItemResponse<TyCtxt<'tcx>>;
|
||||
|
||||
pub type PredefinedOpaques<'tcx> = &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>;
|
||||
|
||||
|
||||
@@ -1063,7 +1063,7 @@ pub fn non_body_analysis(
|
||||
def_id: impl IntoQueryKey<DefId>,
|
||||
) -> TypingEnv<'tcx> {
|
||||
let def_id = def_id.into_query_key();
|
||||
Self::new(tcx.param_env(def_id), TypingMode::non_body_analysis())
|
||||
Self::new(tcx.param_env(def_id), TypingMode::non_body_analysis().into())
|
||||
}
|
||||
|
||||
pub fn post_analysis(tcx: TyCtxt<'tcx>, def_id: impl IntoQueryKey<DefId>) -> TypingEnv<'tcx> {
|
||||
@@ -1075,19 +1075,20 @@ pub fn post_analysis(tcx: TyCtxt<'tcx>, def_id: impl IntoQueryKey<DefId>) -> Typ
|
||||
/// opaque types in the `param_env`.
|
||||
pub fn with_post_analysis_normalized(self, tcx: TyCtxt<'tcx>) -> TypingEnv<'tcx> {
|
||||
let TypingEnv { typing_mode, param_env } = self;
|
||||
match typing_mode.0 {
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. } => {}
|
||||
TypingMode::PostAnalysis => return self,
|
||||
}
|
||||
|
||||
// No need to reveal opaques with the new solver enabled,
|
||||
// since we have lazy norm.
|
||||
let param_env = if tcx.next_trait_solver_globally() {
|
||||
param_env
|
||||
} else {
|
||||
match typing_mode.0.assert_not_erased() {
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. } => {}
|
||||
TypingMode::PostAnalysis => return self,
|
||||
}
|
||||
|
||||
ParamEnv::new(tcx.reveal_opaque_types_in_bounds(param_env.caller_bounds()))
|
||||
};
|
||||
TypingEnv { typing_mode: TypingModeEqWrapper(TypingMode::PostAnalysis), param_env }
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
use rustc_type_ir::TyKind::*;
|
||||
use rustc_type_ir::solve::SizedTraitKind;
|
||||
use rustc_type_ir::walk::TypeWalker;
|
||||
use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, TypeVisitableExt, elaborate};
|
||||
use rustc_type_ir::{
|
||||
self as ir, BoundVar, CollectAndApply, MayBeErased, TypeVisitableExt, elaborate,
|
||||
};
|
||||
use tracing::instrument;
|
||||
use ty::util::IntTypeExt;
|
||||
|
||||
@@ -41,7 +43,7 @@
|
||||
pub type Binder<'tcx, T> = ir::Binder<TyCtxt<'tcx>, T>;
|
||||
pub type EarlyBinder<'tcx, T> = ir::EarlyBinder<TyCtxt<'tcx>, T>;
|
||||
pub type Unnormalized<'tcx, T> = ir::Unnormalized<TyCtxt<'tcx>, T>;
|
||||
pub type TypingMode<'tcx> = ir::TypingMode<TyCtxt<'tcx>>;
|
||||
pub type TypingMode<'tcx, S = MayBeErased> = ir::TypingMode<TyCtxt<'tcx>, S>;
|
||||
pub type TypingModeEqWrapper<'tcx> = ir::TypingModeEqWrapper<TyCtxt<'tcx>>;
|
||||
pub type Placeholder<'tcx, T> = ir::Placeholder<TyCtxt<'tcx>, T>;
|
||||
pub type PlaceholderRegion<'tcx> = ir::PlaceholderRegion<TyCtxt<'tcx>>;
|
||||
|
||||
@@ -547,7 +547,7 @@ fn move_paths_for_fields(
|
||||
let subpath = self.elaborator.field_subpath(variant_path, field_idx);
|
||||
let tcx = self.tcx();
|
||||
|
||||
match self.elaborator.typing_env().typing_mode() {
|
||||
match self.elaborator.typing_env().typing_mode().assert_not_erased() {
|
||||
ty::TypingMode::PostAnalysis => {}
|
||||
ty::TypingMode::Coherence
|
||||
| ty::TypingMode::Analysis { .. }
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
use rustc_type_ir::relate::solver_relating::RelateExt;
|
||||
use rustc_type_ir::{
|
||||
self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner,
|
||||
TypeFoldable, TypingModeEqWrapper,
|
||||
TypeFoldable, TypingMode, TypingModeEqWrapper,
|
||||
};
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -54,6 +54,7 @@ pub(super) fn canonicalize_goal<D, I>(
|
||||
delegate: &D,
|
||||
goal: Goal<I, I::Predicate>,
|
||||
opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)],
|
||||
typing_mode: TypingMode<I>,
|
||||
) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>)
|
||||
where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
@@ -66,10 +67,9 @@ pub(super) fn canonicalize_goal<D, I>(
|
||||
predefined_opaques_in_body: delegate.cx().mk_predefined_opaques_in_body(opaque_types),
|
||||
},
|
||||
);
|
||||
let query_input = ty::CanonicalQueryInput {
|
||||
canonical,
|
||||
typing_mode: TypingModeEqWrapper(delegate.typing_mode()),
|
||||
};
|
||||
|
||||
let query_input =
|
||||
ty::CanonicalQueryInput { canonical, typing_mode: TypingModeEqWrapper(typing_mode) };
|
||||
(orig_values, query_input)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use rustc_type_ir::solve::{Certainty, Goal, NoSolution, VisibleForLeakCheck};
|
||||
use rustc_type_ir::solve::{
|
||||
Certainty, FetchEligibleAssocItemResponse, Goal, NoSolution, VisibleForLeakCheck,
|
||||
};
|
||||
use rustc_type_ir::{self as ty, InferCtxtLike, Interner, TypeFoldable};
|
||||
|
||||
pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
|
||||
@@ -79,10 +81,7 @@ fn fetch_eligible_assoc_item(
|
||||
goal_trait_ref: ty::TraitRef<Self::Interner>,
|
||||
trait_assoc_def_id: <Self::Interner as Interner>::DefId,
|
||||
impl_def_id: <Self::Interner as Interner>::ImplId,
|
||||
) -> Result<
|
||||
Option<<Self::Interner as Interner>::DefId>,
|
||||
<Self::Interner as Interner>::ErrorGuaranteed,
|
||||
>;
|
||||
) -> FetchEligibleAssocItemResponse<Self::Interner>;
|
||||
|
||||
fn is_transmutable(
|
||||
&self,
|
||||
|
||||
@@ -9,11 +9,14 @@
|
||||
use rustc_type_ir::inherent::*;
|
||||
use rustc_type_ir::lang_items::SolverTraitLangItem;
|
||||
use rustc_type_ir::search_graph::CandidateHeadUsages;
|
||||
use rustc_type_ir::solve::{AliasBoundKind, MaybeInfo, SizedTraitKind, StalledOnCoroutines};
|
||||
use rustc_type_ir::solve::{
|
||||
AliasBoundKind, MaybeInfo, NoSolutionOrOpaquesAccessed, RerunReason, SizedTraitKind,
|
||||
StalledOnCoroutines,
|
||||
};
|
||||
use rustc_type_ir::{
|
||||
self as ty, AliasTy, Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable,
|
||||
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Unnormalized,
|
||||
Upcast, elaborate,
|
||||
self as ty, AliasTy, Interner, MayBeErased, TypeFlags, TypeFoldable, TypeFolder,
|
||||
TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
TypingMode, Unnormalized, Upcast, elaborate,
|
||||
};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
@@ -161,7 +164,7 @@ fn probe_and_consider_param_env_candidate(
|
||||
})
|
||||
});
|
||||
|
||||
match result {
|
||||
match result.map_err(Into::into) {
|
||||
Ok(result) => Ok(Candidate { source: source.get(), result, head_usages }),
|
||||
Err(NoSolution) => Err(head_usages),
|
||||
}
|
||||
@@ -415,6 +418,9 @@ impl<D, I> EvalCtxt<'_, D>
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
// FIXME(#155443): This function should only ever return an error
|
||||
// as we want to force a rerun when accessing opaques. We should change
|
||||
// this file to revert all the newly added places which return `NoSolution`.
|
||||
pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<D>>(
|
||||
&mut self,
|
||||
goal: Goal<I, G>,
|
||||
@@ -471,7 +477,8 @@ pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<D>>(
|
||||
TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis => !candidates.iter().any(|c| {
|
||||
| TypingMode::PostAnalysis
|
||||
| TypingMode::ErasedNotCoherence(MayBeErased) => !candidates.iter().any(|c| {
|
||||
matches!(
|
||||
c.source,
|
||||
CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)
|
||||
@@ -682,14 +689,23 @@ fn assemble_alias_bound_candidates<G: GoalKind<D>>(
|
||||
goal: Goal<I, G>,
|
||||
candidates: &mut Vec<Candidate<I>>,
|
||||
) {
|
||||
let () = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
|
||||
let res = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
|
||||
ecx.assemble_alias_bound_candidates_recur(
|
||||
goal.predicate.self_ty(),
|
||||
goal,
|
||||
candidates,
|
||||
AliasBoundKind::SelfBounds,
|
||||
);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// always returns Ok
|
||||
match res {
|
||||
Ok(_) | Err(NoSolutionOrOpaquesAccessed::OpaquesAccessed) => {}
|
||||
Err(NoSolutionOrOpaquesAccessed::NoSolution(NoSolution)) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For some deeply nested `<T>::A::B::C::D` rigid associated type,
|
||||
@@ -959,12 +975,8 @@ pub(super) fn filter_specialized_impls(
|
||||
allow_inference_constraints: AllowInferenceConstraints,
|
||||
candidates: &mut Vec<Candidate<I>>,
|
||||
) {
|
||||
match self.typing_mode() {
|
||||
TypingMode::Coherence => return,
|
||||
TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis => {}
|
||||
if self.typing_mode().is_coherence() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
@@ -1014,6 +1026,7 @@ pub(super) fn filter_specialized_impls(
|
||||
///
|
||||
/// See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/182>
|
||||
/// for why this is necessary.
|
||||
#[tracing::instrument(skip(self, assemble_from))]
|
||||
fn try_assemble_bounds_via_registered_opaques<G: GoalKind<D>>(
|
||||
&mut self,
|
||||
goal: Goal<I, G>,
|
||||
@@ -1028,6 +1041,11 @@ fn try_assemble_bounds_via_registered_opaques<G: GoalKind<D>>(
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis => vec![],
|
||||
TypingMode::ErasedNotCoherence(MayBeErased) => {
|
||||
self.opaque_accesses
|
||||
.rerun_if_any_opaque_has_infer_as_hidden_type(RerunReason::SelfTyInfer);
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
|
||||
if opaque_types.is_empty() {
|
||||
@@ -1059,7 +1077,7 @@ fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
|
||||
|
||||
// We look at all item-bounds of the opaque, replacing the
|
||||
// opaque with the current self type before considering
|
||||
// them as a candidate. Imagine e've got `?x: Trait<?y>`
|
||||
// them as a candidate. Imagine we've got `?x: Trait<?y>`
|
||||
// and `?x` has been sub-unified with the hidden type of
|
||||
// `impl Trait<u32>`, We take the item bound `opaque: Trait<u32>`
|
||||
// and replace all occurrences of `opaque` with `?x`. This results
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use rustc_type_ir::lang_items::SolverTraitLangItem;
|
||||
use rustc_type_ir::solve::inspect::ProbeKind;
|
||||
use rustc_type_ir::solve::{AliasBoundKind, SizedTraitKind};
|
||||
use rustc_type_ir::{self as ty, Interner, TypingMode, Unnormalized, elaborate};
|
||||
use rustc_type_ir::{self as ty, Interner, Unnormalized, elaborate};
|
||||
use tracing::instrument;
|
||||
|
||||
use super::assembly::{Candidate, structural_traits};
|
||||
@@ -142,13 +142,13 @@ fn consider_impl_candidate(
|
||||
let impl_polarity = cx.impl_polarity(impl_def_id);
|
||||
let certainty = match impl_polarity {
|
||||
ty::ImplPolarity::Negative => return Err(NoSolution),
|
||||
ty::ImplPolarity::Reservation => match ecx.typing_mode() {
|
||||
TypingMode::Coherence => Certainty::AMBIGUOUS,
|
||||
TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis => return Err(NoSolution),
|
||||
},
|
||||
ty::ImplPolarity::Reservation => {
|
||||
if ecx.typing_mode().is_coherence() {
|
||||
Certainty::AMBIGUOUS
|
||||
} else {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
}
|
||||
ty::ImplPolarity::Positive => Certainty::Yes,
|
||||
};
|
||||
|
||||
|
||||
@@ -8,13 +8,16 @@
|
||||
use rustc_type_ir::relate::Relate;
|
||||
use rustc_type_ir::relate::solver_relating::RelateExt;
|
||||
use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind};
|
||||
use rustc_type_ir::solve::{MaybeInfo, OpaqueTypesJank};
|
||||
use rustc_type_ir::{
|
||||
self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder,
|
||||
TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
TypingMode,
|
||||
use rustc_type_ir::solve::{
|
||||
AccessedOpaques, FetchEligibleAssocItemResponse, MaybeInfo, OpaqueTypesJank, RerunCondition,
|
||||
RerunReason, SmallCopyList,
|
||||
};
|
||||
use tracing::{debug, instrument, trace};
|
||||
use rustc_type_ir::{
|
||||
self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, MayBeErased,
|
||||
OpaqueTypeKey, PredicateKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable,
|
||||
TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
|
||||
};
|
||||
use tracing::{Level, debug, instrument, trace, warn};
|
||||
|
||||
use super::has_only_region_constraints;
|
||||
use crate::canonical::{
|
||||
@@ -74,6 +77,12 @@ fn from_query_input<I: Interner>(cx: I, input: QueryInput<I, I::Predicate>) -> C
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum RerunDecision {
|
||||
Yes,
|
||||
No,
|
||||
EagerlyPropagateToParent,
|
||||
}
|
||||
pub struct EvalCtxt<'a, D, I = <D as SolverDelegate>::Interner>
|
||||
where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
@@ -134,6 +143,9 @@ pub struct EvalCtxt<'a, D, I = <D as SolverDelegate>::Interner>
|
||||
// evaluation code.
|
||||
tainted: Result<(), NoSolution>,
|
||||
|
||||
/// Tracks accesses of opaque types while in [`TypingMode::ErasedNotCoherence`].
|
||||
pub(super) opaque_accesses: AccessedOpaques<I>,
|
||||
|
||||
pub(super) inspect: inspect::EvaluationStepBuilder<D>,
|
||||
}
|
||||
|
||||
@@ -259,7 +271,7 @@ impl<'a, D, I> EvalCtxt<'a, D>
|
||||
I: Interner,
|
||||
{
|
||||
pub(super) fn typing_mode(&self) -> TypingMode<I> {
|
||||
self.delegate.typing_mode()
|
||||
self.delegate.typing_mode_raw()
|
||||
}
|
||||
|
||||
/// Computes the `PathKind` for the step from the current goal to the
|
||||
@@ -335,12 +347,14 @@ pub(super) fn enter_root<R>(
|
||||
current_goal_kind: CurrentGoalKind::Misc,
|
||||
origin_span,
|
||||
tainted: Ok(()),
|
||||
opaque_accesses: AccessedOpaques::default(),
|
||||
};
|
||||
let result = f(&mut ecx);
|
||||
assert!(
|
||||
ecx.nested_goals.is_empty(),
|
||||
"root `EvalCtxt` should not have any goals added to it"
|
||||
);
|
||||
assert!(!ecx.opaque_accesses.might_rerun());
|
||||
assert!(search_graph.is_empty());
|
||||
result
|
||||
}
|
||||
@@ -352,13 +366,13 @@ pub(super) fn enter_root<R>(
|
||||
///
|
||||
/// This function takes care of setting up the inference context, setting the anchor,
|
||||
/// and registering opaques from the canonicalized input.
|
||||
pub(super) fn enter_canonical<R>(
|
||||
pub(super) fn enter_canonical<T>(
|
||||
cx: I,
|
||||
search_graph: &'a mut SearchGraph<D>,
|
||||
canonical_input: CanonicalInput<I>,
|
||||
proof_tree_builder: &mut inspect::ProofTreeBuilder<D>,
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal<I, I::Predicate>) -> R,
|
||||
) -> R {
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal<I, I::Predicate>) -> Result<T, NoSolution>,
|
||||
) -> (Result<T, NoSolution>, AccessedOpaques<I>) {
|
||||
let (ref delegate, input, var_values) = D::build_with_canonical(cx, &canonical_input);
|
||||
for (key, ty) in input.predefined_opaques_in_body.iter() {
|
||||
let prev = delegate.register_hidden_type_in_storage(key, ty, I::Span::dummy());
|
||||
@@ -379,6 +393,10 @@ pub(super) fn enter_canonical<R>(
|
||||
}
|
||||
|
||||
let initial_opaque_types_storage_num_entries = delegate.opaque_types_storage_num_entries();
|
||||
if cfg!(debug_assertions) && delegate.typing_mode_raw().is_erased_not_coherence() {
|
||||
assert!(delegate.clone_opaque_types_lookup_table().is_empty());
|
||||
}
|
||||
|
||||
let mut ecx = EvalCtxt {
|
||||
delegate,
|
||||
var_kinds: canonical_input.canonical.var_kinds,
|
||||
@@ -391,12 +409,17 @@ pub(super) fn enter_canonical<R>(
|
||||
origin_span: I::Span::dummy(),
|
||||
tainted: Ok(()),
|
||||
inspect: proof_tree_builder.new_evaluation_step(var_values),
|
||||
opaque_accesses: AccessedOpaques::default(),
|
||||
};
|
||||
|
||||
let result = f(&mut ecx, input.goal);
|
||||
ecx.inspect.probe_final_state(ecx.delegate, ecx.max_input_universe);
|
||||
proof_tree_builder.finish_evaluation_step(ecx.inspect);
|
||||
|
||||
if canonical_input.typing_mode.0.is_erased_not_coherence() {
|
||||
debug_assert!(delegate.clone_opaque_types_lookup_table().is_empty());
|
||||
}
|
||||
|
||||
// When creating a query response we clone the opaque type constraints
|
||||
// instead of taking them. This would cause an ICE here, since we have
|
||||
// assertions against dropping an `InferCtxt` without taking opaques.
|
||||
@@ -404,7 +427,7 @@ pub(super) fn enter_canonical<R>(
|
||||
// FIXME: Could we make `build_with_canonical` into `enter_with_canonical` and call this at the end?
|
||||
delegate.reset_opaque_types();
|
||||
|
||||
result
|
||||
(result, ecx.opaque_accesses)
|
||||
}
|
||||
|
||||
pub(super) fn ignore_candidate_head_usages(&mut self, usages: CandidateHeadUsages) {
|
||||
@@ -470,19 +493,107 @@ pub(super) fn evaluate_goal_raw(
|
||||
// duplicate entries.
|
||||
let opaque_types = self.delegate.clone_opaque_types_lookup_table();
|
||||
let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types));
|
||||
let typing_mode = self.typing_mode();
|
||||
let step_kind = self.step_kind_for_source(source);
|
||||
|
||||
let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, &opaque_types);
|
||||
let canonical_result = self.search_graph.evaluate_goal(
|
||||
self.cx(),
|
||||
canonical_goal,
|
||||
self.step_kind_for_source(source),
|
||||
&mut inspect::ProofTreeBuilder::new_noop(),
|
||||
);
|
||||
let response = match canonical_result {
|
||||
Err(e) => return Err(e),
|
||||
Ok(response) => response,
|
||||
let tracing_span = tracing::span!(
|
||||
Level::DEBUG,
|
||||
"evaluate_goal_raw in typing mode",
|
||||
"{:?} opaques={:?}",
|
||||
typing_mode,
|
||||
opaque_types
|
||||
)
|
||||
.entered();
|
||||
|
||||
let (result, orig_values, canonical_goal) = 'retry_canonicalize: {
|
||||
let skip_erased_attempt = if typing_mode.is_coherence() {
|
||||
true
|
||||
} else {
|
||||
let mut skip = false;
|
||||
if opaque_types.iter().any(|(_, ty)| ty.is_ty_var())
|
||||
&& let PredicateKind::Clause(ClauseKind::Trait(..)) =
|
||||
goal.predicate.kind().skip_binder()
|
||||
{
|
||||
skip = true;
|
||||
}
|
||||
|
||||
if let PredicateKind::Clause(ClauseKind::Trait(tr)) =
|
||||
goal.predicate.kind().skip_binder()
|
||||
&& tr.self_ty().has_coroutines()
|
||||
&& self.cx().trait_is_auto(tr.trait_ref.def_id)
|
||||
{
|
||||
// FIXME(#155443): this doesn't make a difference now, but with eager normalization
|
||||
// it likely will.
|
||||
// skip_erased_attempt = true;
|
||||
}
|
||||
|
||||
skip
|
||||
};
|
||||
|
||||
if skip_erased_attempt {
|
||||
if typing_mode.is_erased_not_coherence() {
|
||||
self.opaque_accesses.rerun_always(RerunReason::SkipErasedAttempt);
|
||||
// FIXME(#155443): We should differentiate between `NoSolution` and force rerun here.
|
||||
return Err(NoSolution);
|
||||
} else {
|
||||
debug!("running in original typing mode");
|
||||
}
|
||||
} else {
|
||||
debug!("trying without opaques: {goal:?}");
|
||||
|
||||
let (orig_values, canonical_goal) = canonicalize_goal(
|
||||
self.delegate,
|
||||
goal,
|
||||
&[],
|
||||
TypingMode::ErasedNotCoherence(MayBeErased),
|
||||
);
|
||||
|
||||
let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal(
|
||||
self.cx(),
|
||||
canonical_goal,
|
||||
step_kind,
|
||||
&mut inspect::ProofTreeBuilder::new_noop(),
|
||||
);
|
||||
|
||||
let should_rerun = self.should_rerun_after_erased_canonicalization(
|
||||
accessed_opaques,
|
||||
self.typing_mode(),
|
||||
&opaque_types,
|
||||
);
|
||||
match should_rerun {
|
||||
RerunDecision::Yes => debug!("rerunning in original typing mode"),
|
||||
RerunDecision::No => {
|
||||
break 'retry_canonicalize (canonical_result, orig_values, canonical_goal);
|
||||
}
|
||||
RerunDecision::EagerlyPropagateToParent => {
|
||||
self.opaque_accesses.update(accessed_opaques);
|
||||
break 'retry_canonicalize (canonical_result, orig_values, canonical_goal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (orig_values, canonical_goal) =
|
||||
canonicalize_goal(self.delegate, goal, &opaque_types, typing_mode);
|
||||
|
||||
let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal(
|
||||
self.cx(),
|
||||
canonical_goal,
|
||||
step_kind,
|
||||
&mut inspect::ProofTreeBuilder::new_noop(),
|
||||
);
|
||||
assert!(
|
||||
!accessed_opaques.might_rerun(),
|
||||
"we run without TypingMode::ErasedNotCoherence, so opaques are available, and we don't retry if the outer typing mode is ErasedNotCoherence: {accessed_opaques:?} after {goal:?}"
|
||||
);
|
||||
|
||||
(canonical_result, orig_values, canonical_goal)
|
||||
};
|
||||
|
||||
debug!(?result);
|
||||
let response = result?;
|
||||
|
||||
drop(tracing_span);
|
||||
|
||||
let has_changed =
|
||||
if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No };
|
||||
|
||||
@@ -585,6 +696,96 @@ pub(super) fn evaluate_goal_raw(
|
||||
))
|
||||
}
|
||||
|
||||
fn should_rerun_after_erased_canonicalization(
|
||||
&self,
|
||||
AccessedOpaques { reason: _, rerun }: AccessedOpaques<I>,
|
||||
original_typing_mode: TypingMode<I>,
|
||||
parent_opaque_types: &[(OpaqueTypeKey<I>, I::Ty)],
|
||||
) -> RerunDecision {
|
||||
let parent_opaque_defids = parent_opaque_types.iter().map(|(key, _)| key.def_id);
|
||||
let opaque_in_storage = |opaques: I::LocalDefIds, defids: SmallCopyList<_>| {
|
||||
if defids.as_ref().is_empty() {
|
||||
RerunDecision::No
|
||||
} else if opaques
|
||||
.iter()
|
||||
.chain(parent_opaque_defids)
|
||||
.any(|opaque| defids.as_ref().contains(&opaque))
|
||||
{
|
||||
RerunDecision::Yes
|
||||
} else {
|
||||
RerunDecision::No
|
||||
}
|
||||
};
|
||||
let any_opaque_has_infer_as_hidden = || {
|
||||
if parent_opaque_types.iter().any(|(_, ty)| ty.is_ty_var()) {
|
||||
RerunDecision::Yes
|
||||
} else {
|
||||
RerunDecision::No
|
||||
}
|
||||
};
|
||||
|
||||
let res = match (rerun, original_typing_mode) {
|
||||
// =============================
|
||||
(RerunCondition::Never, _) => RerunDecision::No,
|
||||
// =============================
|
||||
(_, TypingMode::ErasedNotCoherence(MayBeErased)) => {
|
||||
RerunDecision::EagerlyPropagateToParent
|
||||
}
|
||||
// =============================
|
||||
// In coherence, we never switch to erased mode, so we will never register anything
|
||||
// in the rerun state, so we should've taken the first branch of this match
|
||||
(_, TypingMode::Coherence) => unreachable!(),
|
||||
// =============================
|
||||
(RerunCondition::Always, _) => RerunDecision::Yes,
|
||||
// =============================
|
||||
(RerunCondition::OpaqueInStorage(..), TypingMode::PostAnalysis) => RerunDecision::Yes,
|
||||
(
|
||||
RerunCondition::OpaqueInStorage(defids),
|
||||
TypingMode::PostBorrowckAnalysis { defined_opaque_types: opaques }
|
||||
| TypingMode::Analysis { defining_opaque_types_and_generators: opaques }
|
||||
| TypingMode::Borrowck { defining_opaque_types: opaques },
|
||||
) => opaque_in_storage(opaques, defids),
|
||||
// =============================
|
||||
(RerunCondition::AnyOpaqueHasInferAsHidden, TypingMode::Analysis { .. }) => {
|
||||
any_opaque_has_infer_as_hidden()
|
||||
}
|
||||
(
|
||||
RerunCondition::AnyOpaqueHasInferAsHidden,
|
||||
TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis
|
||||
| TypingMode::Borrowck { .. },
|
||||
) => RerunDecision::No,
|
||||
// =============================
|
||||
(
|
||||
RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(_),
|
||||
TypingMode::PostAnalysis,
|
||||
) => RerunDecision::No,
|
||||
(
|
||||
RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(defids),
|
||||
TypingMode::Analysis { defining_opaque_types_and_generators: opaques },
|
||||
) => {
|
||||
if let RerunDecision::Yes = any_opaque_has_infer_as_hidden() {
|
||||
RerunDecision::Yes
|
||||
} else if let RerunDecision::Yes = opaque_in_storage(opaques, defids) {
|
||||
RerunDecision::Yes
|
||||
} else {
|
||||
RerunDecision::No
|
||||
}
|
||||
}
|
||||
(
|
||||
RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(defids),
|
||||
TypingMode::PostBorrowckAnalysis { defined_opaque_types: opaques }
|
||||
| TypingMode::Borrowck { defining_opaque_types: opaques },
|
||||
) => opaque_in_storage(opaques, defids),
|
||||
};
|
||||
|
||||
debug!(
|
||||
"checking whether to rerun {rerun:?} in outer typing mode {original_typing_mode:?} and opaques {parent_opaque_types:?}: {res:?}"
|
||||
);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub(super) fn compute_goal(&mut self, goal: Goal<I, I::Predicate>) -> QueryResult<I> {
|
||||
let Goal { param_env, predicate } = goal;
|
||||
let kind = predicate.kind();
|
||||
@@ -1158,7 +1359,7 @@ pub(super) fn fetch_eligible_assoc_item(
|
||||
goal_trait_ref: ty::TraitRef<I>,
|
||||
trait_assoc_def_id: I::DefId,
|
||||
impl_def_id: I::ImplId,
|
||||
) -> Result<Option<I::DefId>, I::ErrorGuaranteed> {
|
||||
) -> FetchEligibleAssocItemResponse<I> {
|
||||
self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id)
|
||||
}
|
||||
|
||||
@@ -1193,10 +1394,15 @@ pub(super) fn add_item_bounds_for_hidden_type(
|
||||
// This doesn't mean the const isn't evaluatable, though, and should be treated
|
||||
// as an ambiguity rather than no-solution.
|
||||
pub(super) fn evaluate_const(
|
||||
&self,
|
||||
&mut self,
|
||||
param_env: I::ParamEnv,
|
||||
uv: ty::UnevaluatedConst<I>,
|
||||
) -> Option<I::Const> {
|
||||
if self.typing_mode().is_erased_not_coherence() {
|
||||
self.opaque_accesses.rerun_always(RerunReason::EvaluateConst);
|
||||
return None;
|
||||
}
|
||||
|
||||
self.delegate.evaluate_const(param_env, uv)
|
||||
}
|
||||
|
||||
@@ -1256,10 +1462,15 @@ pub(super) fn replace_bound_vars<T: TypeFoldable<I>>(
|
||||
}
|
||||
|
||||
pub(super) fn may_use_unstable_feature(
|
||||
&self,
|
||||
&mut self,
|
||||
param_env: I::ParamEnv,
|
||||
symbol: I::Symbol,
|
||||
) -> bool {
|
||||
if self.typing_mode().is_erased_not_coherence() {
|
||||
self.opaque_accesses.rerun_always(RerunReason::MayUseUnstableFeature);
|
||||
return false;
|
||||
}
|
||||
|
||||
may_use_unstable_feature(&**self.delegate, param_env, symbol)
|
||||
}
|
||||
|
||||
@@ -1435,6 +1646,10 @@ fn compute_external_query_constraints(
|
||||
.delegate
|
||||
.clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries);
|
||||
|
||||
if self.typing_mode().is_erased_not_coherence() {
|
||||
assert!(opaque_types.is_empty());
|
||||
}
|
||||
|
||||
ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals }
|
||||
}
|
||||
}
|
||||
@@ -1555,13 +1770,15 @@ pub fn evaluate_root_goal_for_proof_tree_raw_provider<
|
||||
canonical_goal: CanonicalInput<I>,
|
||||
) -> (QueryResult<I>, I::Probe) {
|
||||
let mut inspect = inspect::ProofTreeBuilder::new();
|
||||
let canonical_result = SearchGraph::<D>::evaluate_root_goal_for_proof_tree(
|
||||
let (canonical_result, accessed_opaques) = SearchGraph::<D>::evaluate_root_goal_for_proof_tree(
|
||||
cx,
|
||||
cx.recursion_limit(),
|
||||
canonical_goal,
|
||||
&mut inspect,
|
||||
);
|
||||
let final_revision = inspect.unwrap();
|
||||
|
||||
assert!(!accessed_opaques.might_rerun());
|
||||
(canonical_result, cx.mk_probe(final_revision))
|
||||
}
|
||||
|
||||
@@ -1576,8 +1793,10 @@ pub(super) fn evaluate_root_goal_for_proof_tree<D: SolverDelegate<Interner = I>,
|
||||
) -> (Result<NestedNormalizationGoals<I>, NoSolution>, inspect::GoalEvaluation<I>) {
|
||||
let opaque_types = delegate.clone_opaque_types_lookup_table();
|
||||
let (goal, opaque_types) = eager_resolve_vars(delegate, (goal, opaque_types));
|
||||
let typing_mode = delegate.typing_mode_raw().assert_not_erased();
|
||||
|
||||
let (orig_values, canonical_goal) = canonicalize_goal(delegate, goal, &opaque_types);
|
||||
let (orig_values, canonical_goal) =
|
||||
canonicalize_goal(delegate, goal, &opaque_types, typing_mode.into());
|
||||
|
||||
let (canonical_result, final_revision) =
|
||||
delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rustc_type_ir::search_graph::CandidateHeadUsages;
|
||||
use rustc_type_ir::solve::{AccessedOpaques, CanonicalResponse, NoSolutionOrOpaquesAccessed};
|
||||
use rustc_type_ir::{InferCtxtLike, Interner};
|
||||
use tracing::instrument;
|
||||
use tracing::{instrument, warn};
|
||||
|
||||
use crate::delegate::SolverDelegate;
|
||||
use crate::solve::assembly::Candidate;
|
||||
@@ -23,16 +24,21 @@ pub(in crate::solve) struct ProbeCtxt<'me, 'a, D, I, F, T>
|
||||
|
||||
impl<D, I, F, T> ProbeCtxt<'_, '_, D, I, F, T>
|
||||
where
|
||||
F: FnOnce(&T) -> inspect::ProbeKind<I>,
|
||||
F: FnOnce(&Result<T, NoSolution>) -> inspect::ProbeKind<I>,
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
pub(in crate::solve) fn enter_single_candidate(
|
||||
self,
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T,
|
||||
) -> (T, CandidateHeadUsages) {
|
||||
self.ecx.search_graph.enter_single_candidate();
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result<T, NoSolution>,
|
||||
) -> (Result<T, NoSolutionOrOpaquesAccessed>, CandidateHeadUsages) {
|
||||
let mut candidate_usages = CandidateHeadUsages::default();
|
||||
|
||||
if self.ecx.opaque_accesses.should_bail() {
|
||||
return (Err(NoSolutionOrOpaquesAccessed::OpaquesAccessed), candidate_usages);
|
||||
}
|
||||
|
||||
self.ecx.search_graph.enter_single_candidate();
|
||||
let result = self.enter(|ecx| {
|
||||
let result = f(ecx);
|
||||
candidate_usages = ecx.search_graph.finish_single_candidate();
|
||||
@@ -41,25 +47,32 @@ pub(in crate::solve) fn enter_single_candidate(
|
||||
(result, candidate_usages)
|
||||
}
|
||||
|
||||
pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T) -> T {
|
||||
pub(in crate::solve) fn enter(
|
||||
self,
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result<T, NoSolution>,
|
||||
) -> Result<T, NoSolutionOrOpaquesAccessed> {
|
||||
let nested_goals = self.ecx.nested_goals.clone();
|
||||
self.enter_inner(f, nested_goals)
|
||||
}
|
||||
|
||||
pub(in crate::solve) fn enter_without_propagated_nested_goals(
|
||||
self,
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T,
|
||||
) -> T {
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result<T, NoSolution>,
|
||||
) -> Result<T, NoSolutionOrOpaquesAccessed> {
|
||||
self.enter_inner(f, Default::default())
|
||||
}
|
||||
|
||||
fn enter_inner(
|
||||
pub(in crate::solve) fn enter_inner(
|
||||
self,
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T,
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result<T, NoSolution>,
|
||||
propagated_nested_goals: Vec<(GoalSource, Goal<I, I::Predicate>, Option<GoalStalledOn<I>>)>,
|
||||
) -> T {
|
||||
) -> Result<T, NoSolutionOrOpaquesAccessed> {
|
||||
let ProbeCtxt { ecx: outer, probe_kind, _result } = self;
|
||||
|
||||
if outer.opaque_accesses.should_bail() {
|
||||
return Err(NoSolutionOrOpaquesAccessed::OpaquesAccessed);
|
||||
}
|
||||
|
||||
let delegate = outer.delegate;
|
||||
let max_input_universe = outer.max_input_universe;
|
||||
let mut nested = EvalCtxt {
|
||||
@@ -75,6 +88,7 @@ fn enter_inner(
|
||||
origin_span: outer.origin_span,
|
||||
tainted: outer.tainted,
|
||||
inspect: outer.inspect.take_and_enter_probe(),
|
||||
opaque_accesses: AccessedOpaques::default(),
|
||||
};
|
||||
let r = nested.delegate.probe(|| {
|
||||
let r = f(&mut nested);
|
||||
@@ -86,7 +100,10 @@ fn enter_inner(
|
||||
nested.inspect.probe_kind(probe_kind);
|
||||
outer.inspect = nested.inspect.finish_probe();
|
||||
}
|
||||
r
|
||||
|
||||
outer.opaque_accesses.update(nested.opaque_accesses);
|
||||
|
||||
r.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +112,7 @@ pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, D, I, F>
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
cx: ProbeCtxt<'me, 'a, D, I, F, QueryResult<I>>,
|
||||
cx: ProbeCtxt<'me, 'a, D, I, F, CanonicalResponse<I>>,
|
||||
source: CandidateSource<I>,
|
||||
}
|
||||
|
||||
@@ -111,7 +128,7 @@ pub(in crate::solve) fn enter(
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let (result, head_usages) = self.cx.enter_single_candidate(f);
|
||||
result.map(|result| Candidate { source: self.source, result, head_usages })
|
||||
Ok(Candidate { source: self.source, result: result?, head_usages })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +141,7 @@ impl<'a, D, I> EvalCtxt<'a, D, I>
|
||||
/// as expensive as necessary to output the desired information.
|
||||
pub(in crate::solve) fn probe<F, T>(&mut self, probe_kind: F) -> ProbeCtxt<'_, 'a, D, I, F, T>
|
||||
where
|
||||
F: FnOnce(&T) -> inspect::ProbeKind<I>,
|
||||
F: FnOnce(&Result<T, NoSolution>) -> inspect::ProbeKind<I>,
|
||||
{
|
||||
ProbeCtxt { ecx: self, probe_kind, _result: PhantomData }
|
||||
}
|
||||
|
||||
@@ -358,7 +358,11 @@ fn structurally_normalize_term(
|
||||
}
|
||||
|
||||
fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool {
|
||||
match self.typing_mode() {
|
||||
match self
|
||||
.typing_mode()
|
||||
// Caller should handle erased mode
|
||||
.assert_not_erased()
|
||||
{
|
||||
// Opaques are never rigid outside of analysis mode.
|
||||
TypingMode::Coherence | TypingMode::PostAnalysis => false,
|
||||
// During analysis, opaques are rigid unless they may be defined by
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
use rustc_type_ir::fast_reject::DeepRejectCtxt;
|
||||
use rustc_type_ir::inherent::*;
|
||||
use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem};
|
||||
use rustc_type_ir::solve::{FetchEligibleAssocItemResponse, RerunReason};
|
||||
use rustc_type_ir::{
|
||||
self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Unnormalized, Upcast as _,
|
||||
};
|
||||
@@ -74,13 +75,17 @@ pub(super) fn compute_normalizes_to_goal(
|
||||
None
|
||||
},
|
||||
|ecx| {
|
||||
ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| {
|
||||
this.structurally_instantiate_normalizes_to_term(
|
||||
goal,
|
||||
goal.predicate.alias,
|
||||
);
|
||||
this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
})
|
||||
ecx.probe(|&result| ProbeKind::RigidAlias { result })
|
||||
.enter(|this| {
|
||||
this.structurally_instantiate_normalizes_to_term(
|
||||
goal,
|
||||
goal.predicate.alias,
|
||||
);
|
||||
this.evaluate_added_goals_and_make_canonical_response(
|
||||
Certainty::Yes,
|
||||
)
|
||||
})
|
||||
.map_err(Into::into)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -280,9 +285,9 @@ fn consider_impl_candidate(
|
||||
goal.predicate.def_id(),
|
||||
impl_def_id,
|
||||
) {
|
||||
Ok(Some(target_item_def_id)) => target_item_def_id,
|
||||
Ok(None) => {
|
||||
match ecx.typing_mode() {
|
||||
FetchEligibleAssocItemResponse::Found(target_item_def_id) => target_item_def_id,
|
||||
FetchEligibleAssocItemResponse::NotFound(tm) => {
|
||||
match tm {
|
||||
// In case the associated item is hidden due to specialization,
|
||||
// normalizing this associated item is always ambiguous. Treating
|
||||
// the associated item as rigid would be incomplete and allow for
|
||||
@@ -313,7 +318,11 @@ fn consider_impl_candidate(
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(guar) => return error_response(ecx, guar),
|
||||
FetchEligibleAssocItemResponse::Err(guar) => return error_response(ecx, guar),
|
||||
FetchEligibleAssocItemResponse::NotFoundBecauseErased => {
|
||||
ecx.opaque_accesses.rerun_always(RerunReason::FetchEligibleAssocItem);
|
||||
return Err(NoSolution);
|
||||
}
|
||||
};
|
||||
|
||||
if !cx.has_item_definition(target_item_def_id) {
|
||||
@@ -323,7 +332,7 @@ fn consider_impl_candidate(
|
||||
// delay a bug because we can have trivially false where clauses, so we
|
||||
// treat it as rigid.
|
||||
if cx.impl_self_is_guaranteed_unsized(impl_def_id) {
|
||||
match ecx.typing_mode() {
|
||||
if ecx.typing_mode().is_coherence() {
|
||||
// Trying to normalize such associated items is always ambiguous
|
||||
// during coherence to avoid cyclic reasoning. See the example in
|
||||
// tests/ui/traits/trivial-unsized-projection-in-coherence.rs.
|
||||
@@ -334,20 +343,11 @@ fn consider_impl_candidate(
|
||||
// would be relevant if any of the nested goals refer to the `term`.
|
||||
// This is not the case here and we only prefer adding an ambiguous
|
||||
// nested goal for consistency.
|
||||
ty::TypingMode::Coherence => {
|
||||
ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous));
|
||||
return then(ecx, Certainty::Yes);
|
||||
}
|
||||
ty::TypingMode::Analysis { .. }
|
||||
| ty::TypingMode::Borrowck { .. }
|
||||
| ty::TypingMode::PostBorrowckAnalysis { .. }
|
||||
| ty::TypingMode::PostAnalysis => {
|
||||
ecx.structurally_instantiate_normalizes_to_term(
|
||||
goal,
|
||||
goal.predicate.alias,
|
||||
);
|
||||
return then(ecx, Certainty::Yes);
|
||||
}
|
||||
ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous));
|
||||
return then(ecx, Certainty::Yes);
|
||||
} else {
|
||||
ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
|
||||
return then(ecx, Certainty::Yes);
|
||||
}
|
||||
} else {
|
||||
return error_response(ecx, cx.delay_bug("missing item"));
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
//! behaves differently depending on the current `TypingMode`.
|
||||
|
||||
use rustc_type_ir::inherent::*;
|
||||
use rustc_type_ir::solve::GoalSource;
|
||||
use rustc_type_ir::{self as ty, Interner, TypingMode, fold_regions};
|
||||
use rustc_type_ir::solve::{GoalSource, NoSolution, RerunReason};
|
||||
use rustc_type_ir::{self as ty, Interner, MayBeErased, TypingMode, fold_regions};
|
||||
|
||||
use crate::delegate::SolverDelegate;
|
||||
use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
|
||||
@@ -13,6 +13,7 @@ impl<D, I> EvalCtxt<'_, D>
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub(super) fn normalize_opaque_type(
|
||||
&mut self,
|
||||
goal: Goal<I, ty::NormalizesTo<I>>,
|
||||
@@ -81,7 +82,7 @@ pub(super) fn normalize_opaque_type(
|
||||
// During HIR typeck, opaque types start out as unconstrained
|
||||
// inference variables. In borrowck we instead use the type
|
||||
// computed in HIR typeck as the initial value.
|
||||
match self.typing_mode() {
|
||||
match self.typing_mode().assert_not_erased() {
|
||||
TypingMode::Analysis { .. } => {}
|
||||
TypingMode::Borrowck { .. } => {
|
||||
let actual = cx
|
||||
@@ -137,6 +138,34 @@ pub(super) fn normalize_opaque_type(
|
||||
self.eq(goal.param_env, expected, actual)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
TypingMode::ErasedNotCoherence(MayBeErased) => {
|
||||
let def_id = opaque_ty.def_id().as_local();
|
||||
|
||||
// If we have a local defid, in other typing modes we check whether
|
||||
// this is the defining scope, and otherwise treat it as rigid.
|
||||
// However, in `ErasedNotcoherence` we *always* treat it as rigid.
|
||||
// This is the same as other modes if def_id is None, but wrong if we do have a DefId.
|
||||
// So, if we have one, we register in the EvalCtxt that we may need that defid.
|
||||
// We might then decide to rerun in the correct typing mode.
|
||||
if let Some(def_id) = def_id {
|
||||
self.opaque_accesses.rerun_if_opaque_in_opaque_type_storage(
|
||||
RerunReason::NormalizeOpaqueType,
|
||||
def_id,
|
||||
);
|
||||
} else {
|
||||
self.opaque_accesses
|
||||
.rerun_if_in_post_analysis(RerunReason::NormalizeOpaqueTypeRemoteCrate);
|
||||
}
|
||||
if self.opaque_accesses.should_bail() {
|
||||
// If we already accessed opaque types once, bail.
|
||||
// We can't make it more precise
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// Always treat the opaque type as rigid.
|
||||
self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
use rustc_type_ir::data_structures::ensure_sufficient_stack;
|
||||
use rustc_type_ir::search_graph::{self, PathKind};
|
||||
use rustc_type_ir::solve::{CanonicalInput, Certainty, NoSolution, QueryResult};
|
||||
use rustc_type_ir::{Interner, TypingMode};
|
||||
use rustc_type_ir::solve::{AccessedOpaques, CanonicalInput, Certainty, NoSolution, QueryResult};
|
||||
use rustc_type_ir::{Interner, MayBeErased, TypingMode};
|
||||
|
||||
use crate::canonical::response_no_constraints_raw;
|
||||
use crate::delegate::SolverDelegate;
|
||||
@@ -47,7 +47,7 @@ fn initial_provisional_result(
|
||||
cx: I,
|
||||
kind: PathKind,
|
||||
input: CanonicalInput<I>,
|
||||
) -> QueryResult<I> {
|
||||
) -> (QueryResult<I>, AccessedOpaques<I>) {
|
||||
match kind {
|
||||
PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes),
|
||||
PathKind::Unknown | PathKind::ForcedAmbiguity => {
|
||||
@@ -69,13 +69,18 @@ fn initial_provisional_result(
|
||||
TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis => Err(NoSolution),
|
||||
| TypingMode::PostAnalysis
|
||||
| TypingMode::ErasedNotCoherence(MayBeErased) => {
|
||||
(Err(NoSolution), AccessedOpaques::default())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_initial_provisional_result(result: QueryResult<I>) -> Option<PathKind> {
|
||||
match result {
|
||||
fn is_initial_provisional_result(
|
||||
result: (QueryResult<I>, AccessedOpaques<I>),
|
||||
) -> Option<PathKind> {
|
||||
match result.0 {
|
||||
Ok(response) => {
|
||||
if has_no_inference_or_external_constraints(response) {
|
||||
if response.value.certainty == Certainty::Yes {
|
||||
@@ -91,16 +96,22 @@ fn is_initial_provisional_result(result: QueryResult<I>) -> Option<PathKind> {
|
||||
}
|
||||
}
|
||||
|
||||
fn stack_overflow_result(cx: I, input: CanonicalInput<I>) -> QueryResult<I> {
|
||||
fn stack_overflow_result(
|
||||
cx: I,
|
||||
input: CanonicalInput<I>,
|
||||
) -> (QueryResult<I>, AccessedOpaques<I>) {
|
||||
response_no_constraints(cx, input, Certainty::overflow(true))
|
||||
}
|
||||
|
||||
fn fixpoint_overflow_result(cx: I, input: CanonicalInput<I>) -> QueryResult<I> {
|
||||
fn fixpoint_overflow_result(
|
||||
cx: I,
|
||||
input: CanonicalInput<I>,
|
||||
) -> (QueryResult<I>, AccessedOpaques<I>) {
|
||||
response_no_constraints(cx, input, Certainty::overflow(false))
|
||||
}
|
||||
|
||||
fn is_ambiguous_result(result: QueryResult<I>) -> Option<Certainty> {
|
||||
result.ok().and_then(|response| {
|
||||
fn is_ambiguous_result(result: (QueryResult<I>, AccessedOpaques<I>)) -> Option<Certainty> {
|
||||
result.0.ok().and_then(|response| {
|
||||
if has_no_inference_or_external_constraints(response)
|
||||
&& matches!(response.value.certainty, Certainty::Maybe { .. })
|
||||
{
|
||||
@@ -115,7 +126,7 @@ fn propagate_ambiguity(
|
||||
cx: I,
|
||||
for_input: CanonicalInput<I>,
|
||||
certainty: Certainty,
|
||||
) -> QueryResult<I> {
|
||||
) -> (QueryResult<I>, AccessedOpaques<I>) {
|
||||
response_no_constraints(cx, for_input, certainty)
|
||||
}
|
||||
|
||||
@@ -124,7 +135,7 @@ fn compute_goal(
|
||||
cx: I,
|
||||
input: CanonicalInput<I>,
|
||||
inspect: &mut Self::ProofTreeBuilder,
|
||||
) -> QueryResult<I> {
|
||||
) -> (QueryResult<I>, AccessedOpaques<I>) {
|
||||
ensure_sufficient_stack(|| {
|
||||
EvalCtxt::enter_canonical(cx, search_graph, input, inspect, |ecx, goal| {
|
||||
let result = ecx.compute_goal(goal);
|
||||
@@ -139,11 +150,14 @@ fn response_no_constraints<I: Interner>(
|
||||
cx: I,
|
||||
input: CanonicalInput<I>,
|
||||
certainty: Certainty,
|
||||
) -> QueryResult<I> {
|
||||
Ok(response_no_constraints_raw(
|
||||
cx,
|
||||
input.canonical.max_universe,
|
||||
input.canonical.var_kinds,
|
||||
certainty,
|
||||
))
|
||||
) -> (QueryResult<I>, AccessedOpaques<I>) {
|
||||
(
|
||||
Ok(response_no_constraints_raw(
|
||||
cx,
|
||||
input.canonical.max_universe,
|
||||
input.canonical.var_kinds,
|
||||
certainty,
|
||||
)),
|
||||
AccessedOpaques::default(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
use rustc_type_ir::lang_items::SolverTraitLangItem;
|
||||
use rustc_type_ir::solve::{
|
||||
AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, MaybeInfo, OpaqueTypesJank,
|
||||
SizedTraitKind,
|
||||
RerunReason, SizedTraitKind,
|
||||
};
|
||||
use rustc_type_ir::{
|
||||
self as ty, FieldInfo, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef,
|
||||
TypeVisitableExt as _, TypingMode, Unnormalized, Upcast as _, elaborate,
|
||||
self as ty, FieldInfo, Interner, MayBeErased, Movability, PredicatePolarity, TraitPredicate,
|
||||
TraitRef, TypeVisitableExt as _, TypingMode, Unnormalized, Upcast as _, elaborate,
|
||||
};
|
||||
use tracing::{debug, instrument, trace};
|
||||
use tracing::{debug, instrument, trace, warn};
|
||||
|
||||
use crate::delegate::SolverDelegate;
|
||||
use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
|
||||
@@ -74,15 +74,14 @@ fn consider_impl_candidate(
|
||||
// of reservation impl to ambiguous during coherence.
|
||||
let impl_polarity = cx.impl_polarity(impl_def_id);
|
||||
let maximal_certainty = match (impl_polarity, goal.predicate.polarity) {
|
||||
// In intercrate mode, this is ambiguous. But outside of intercrate,
|
||||
// it's not a real impl.
|
||||
(ty::ImplPolarity::Reservation, _) => match ecx.typing_mode() {
|
||||
TypingMode::Coherence => Certainty::AMBIGUOUS,
|
||||
TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis => return Err(NoSolution),
|
||||
},
|
||||
// In coherence mode, this is ambiguous. But outside of coherence, it's not a real impl.
|
||||
(ty::ImplPolarity::Reservation, _) => {
|
||||
if ecx.typing_mode().is_coherence() {
|
||||
Certainty::AMBIGUOUS
|
||||
} else {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
}
|
||||
|
||||
// Impl matches polarity
|
||||
(ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive)
|
||||
@@ -237,6 +236,11 @@ fn consider_auto_trait_candidate(
|
||||
if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }) =
|
||||
goal.predicate.self_ty().kind()
|
||||
{
|
||||
if ecx.opaque_accesses.might_rerun() {
|
||||
ecx.opaque_accesses.rerun_always(RerunReason::AutoTraitLeakage);
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
debug_assert!(ecx.opaque_type_is_rigid(def_id));
|
||||
for item_bound in cx.item_self_bounds(def_id).skip_binder() {
|
||||
if item_bound
|
||||
@@ -804,58 +808,56 @@ fn consider_structural_builtin_unsize_candidates(
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let result_to_single = |result| match result {
|
||||
Ok(resp) => vec![resp],
|
||||
let result = ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(
|
||||
|ecx| -> Result<Vec<Candidate<I>>, NoSolution> {
|
||||
let a_ty = goal.predicate.self_ty();
|
||||
// We need to normalize the b_ty since it's matched structurally
|
||||
// in the other functions below.
|
||||
let b_ty = ecx.structurally_normalize_ty(
|
||||
goal.param_env,
|
||||
goal.predicate.trait_ref.args.type_at(1),
|
||||
)?;
|
||||
|
||||
let goal = goal.with(ecx.cx(), (a_ty, b_ty));
|
||||
match (a_ty.kind(), b_ty.kind()) {
|
||||
(ty::Infer(ty::TyVar(..)), ..) => panic!("unexpected infer {a_ty:?} {b_ty:?}"),
|
||||
|
||||
(_, ty::Infer(ty::TyVar(..))) => {
|
||||
Ok(vec![ecx.forced_ambiguity(MaybeInfo::AMBIGUOUS)?])
|
||||
}
|
||||
|
||||
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`.
|
||||
(ty::Dynamic(a_data, a_region), ty::Dynamic(b_data, b_region)) => Ok(ecx
|
||||
.consider_builtin_dyn_upcast_candidates(
|
||||
goal, a_data, a_region, b_data, b_region,
|
||||
)),
|
||||
|
||||
// `T` -> `dyn Trait` unsizing.
|
||||
(_, ty::Dynamic(b_region, b_data)) => Ok(vec![
|
||||
ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data)?,
|
||||
]),
|
||||
|
||||
// `[T; N]` -> `[T]` unsizing
|
||||
(ty::Array(a_elem_ty, ..), ty::Slice(b_elem_ty)) => {
|
||||
Ok(vec![ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty)?])
|
||||
}
|
||||
|
||||
// `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
|
||||
(ty::Adt(a_def, a_args), ty::Adt(b_def, b_args))
|
||||
if a_def.is_struct() && a_def == b_def =>
|
||||
{
|
||||
Ok(vec![ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args)?])
|
||||
}
|
||||
|
||||
_ => Err(NoSolution),
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
match result.map_err(Into::into) {
|
||||
Ok(resp) => resp,
|
||||
Err(NoSolution) => vec![],
|
||||
};
|
||||
|
||||
ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| {
|
||||
let a_ty = goal.predicate.self_ty();
|
||||
// We need to normalize the b_ty since it's matched structurally
|
||||
// in the other functions below.
|
||||
let Ok(b_ty) = ecx.structurally_normalize_ty(
|
||||
goal.param_env,
|
||||
goal.predicate.trait_ref.args.type_at(1),
|
||||
) else {
|
||||
return vec![];
|
||||
};
|
||||
|
||||
let goal = goal.with(ecx.cx(), (a_ty, b_ty));
|
||||
match (a_ty.kind(), b_ty.kind()) {
|
||||
(ty::Infer(ty::TyVar(..)), ..) => panic!("unexpected infer {a_ty:?} {b_ty:?}"),
|
||||
|
||||
(_, ty::Infer(ty::TyVar(..))) => {
|
||||
result_to_single(ecx.forced_ambiguity(MaybeInfo::AMBIGUOUS))
|
||||
}
|
||||
|
||||
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`.
|
||||
(ty::Dynamic(a_data, a_region), ty::Dynamic(b_data, b_region)) => ecx
|
||||
.consider_builtin_dyn_upcast_candidates(
|
||||
goal, a_data, a_region, b_data, b_region,
|
||||
),
|
||||
|
||||
// `T` -> `dyn Trait` unsizing.
|
||||
(_, ty::Dynamic(b_region, b_data)) => result_to_single(
|
||||
ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data),
|
||||
),
|
||||
|
||||
// `[T; N]` -> `[T]` unsizing
|
||||
(ty::Array(a_elem_ty, ..), ty::Slice(b_elem_ty)) => {
|
||||
result_to_single(ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty))
|
||||
}
|
||||
|
||||
// `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
|
||||
(ty::Adt(a_def, a_args), ty::Adt(b_def, b_args))
|
||||
if a_def.is_struct() && a_def == b_def =>
|
||||
{
|
||||
result_to_single(
|
||||
ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args),
|
||||
)
|
||||
}
|
||||
|
||||
_ => vec![],
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_builtin_field_candidate(
|
||||
@@ -1393,12 +1395,8 @@ impl<D, I> EvalCtxt<'_, D>
|
||||
/// normalization. This means the only case where this special-case results in exploitable
|
||||
/// unsoundness should be lifetime dependent user-written impls.
|
||||
pub(super) fn unsound_prefer_builtin_dyn_impl(&mut self, candidates: &mut Vec<Candidate<I>>) {
|
||||
match self.typing_mode() {
|
||||
TypingMode::Coherence => return,
|
||||
TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis => {}
|
||||
if self.typing_mode().is_coherence() {
|
||||
return;
|
||||
}
|
||||
|
||||
if candidates
|
||||
@@ -1573,6 +1571,11 @@ fn try_stall_coroutine(&mut self, self_ty: I::Ty) -> Option<Result<Candidate<I>,
|
||||
}));
|
||||
}
|
||||
}
|
||||
TypingMode::ErasedNotCoherence(MayBeErased) => {
|
||||
// Trying to continue here isn't worth it.
|
||||
self.opaque_accesses.rerun_always(RerunReason::TryStallCoroutine);
|
||||
return Some(Err(NoSolution));
|
||||
}
|
||||
TypingMode::Coherence
|
||||
| TypingMode::PostAnalysis
|
||||
| TypingMode::Borrowck { defining_opaque_types: _ }
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues,
|
||||
};
|
||||
use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt};
|
||||
use rustc_infer::traits::solve::Goal;
|
||||
use rustc_infer::traits::solve::{FetchEligibleAssocItemResponse, Goal};
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::traits::solve::Certainty;
|
||||
use rustc_middle::ty::{
|
||||
self, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt as _, TypingMode,
|
||||
self, MayBeErased, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt as _, TypingMode,
|
||||
};
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
|
||||
use crate::traits::{EvaluateConstErr, ObligationCause, sizedness_fast_path, specialization_graph};
|
||||
|
||||
@@ -37,6 +37,15 @@ fn deref(&self) -> &Self::Target {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> SolverDelegate<'tcx> {
|
||||
fn known_no_opaque_types_in_storage(&self) -> bool {
|
||||
self.inner.borrow_mut().opaque_types().is_empty()
|
||||
// in erased mode, observing that opaques are empty aren't enough to give a result
|
||||
// here, so let's try the slow path instead.
|
||||
&& !self.typing_mode_raw().is_erased_not_coherence()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<'tcx> {
|
||||
type Infcx = InferCtxt<'tcx>;
|
||||
type Interner = TyCtxt<'tcx>;
|
||||
@@ -70,7 +79,7 @@ fn compute_goal_fast_path(
|
||||
// eventually use opaques to incompletely guide inference via ty var
|
||||
// self types.
|
||||
// FIXME: Properly consider opaques here.
|
||||
&& self.inner.borrow_mut().opaque_types().is_empty()
|
||||
&& self.known_no_opaque_types_in_storage()
|
||||
{
|
||||
return Some(Certainty::AMBIGUOUS);
|
||||
}
|
||||
@@ -277,8 +286,14 @@ fn fetch_eligible_assoc_item(
|
||||
goal_trait_ref: ty::TraitRef<'tcx>,
|
||||
trait_assoc_def_id: DefId,
|
||||
impl_def_id: DefId,
|
||||
) -> Result<Option<DefId>, ErrorGuaranteed> {
|
||||
let node_item = specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id)?;
|
||||
) -> FetchEligibleAssocItemResponse<'tcx> {
|
||||
let node_item =
|
||||
match specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id) {
|
||||
Ok(i) => i,
|
||||
Err(guar) => return FetchEligibleAssocItemResponse::Err(guar),
|
||||
};
|
||||
|
||||
let typing_mode = self.typing_mode_raw();
|
||||
|
||||
let eligible = if node_item.is_final() {
|
||||
// Non-specializable items are always projectable.
|
||||
@@ -288,7 +303,7 @@ fn fetch_eligible_assoc_item(
|
||||
// and the obligation is monomorphic, otherwise passes such as
|
||||
// transmute checking and polymorphic MIR optimizations could
|
||||
// get a result which isn't correct for all monomorphizations.
|
||||
match self.typing_mode() {
|
||||
match typing_mode {
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
@@ -297,11 +312,20 @@ fn fetch_eligible_assoc_item(
|
||||
let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref);
|
||||
!poly_trait_ref.still_further_specializable()
|
||||
}
|
||||
TypingMode::ErasedNotCoherence(MayBeErased) => {
|
||||
return FetchEligibleAssocItemResponse::NotFoundBecauseErased;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: Check for defaultness here may cause diagnostics problems.
|
||||
if eligible { Ok(Some(node_item.item.def_id)) } else { Ok(None) }
|
||||
if eligible {
|
||||
FetchEligibleAssocItemResponse::Found(node_item.item.def_id)
|
||||
} else {
|
||||
// We know it's not erased since then we'd have returned in the match above,
|
||||
// or node_item.final() was true and eligible is always true.
|
||||
FetchEligibleAssocItemResponse::NotFound(typing_mode.assert_not_erased())
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This actually should destructure the `Result` we get from transmutability and
|
||||
|
||||
@@ -276,7 +276,7 @@ fn drain_stalled_obligations_for_coroutines(
|
||||
&mut self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
) -> PredicateObligations<'tcx> {
|
||||
let stalled_coroutines = match infcx.typing_mode() {
|
||||
let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
|
||||
TypingMode::Analysis { defining_opaque_types_and_generators } => {
|
||||
defining_opaque_types_and_generators
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ pub fn evaluate_host_effect_obligation<'tcx>(
|
||||
selcx: &mut SelectionContext<'_, 'tcx>,
|
||||
obligation: &HostEffectObligation<'tcx>,
|
||||
) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
|
||||
if selcx.infcx.typing_mode().is_coherence() {
|
||||
if selcx.typing_mode().is_coherence() {
|
||||
span_bug!(
|
||||
obligation.cause.span,
|
||||
"should not select host obligation in old solver in intercrate mode"
|
||||
|
||||
@@ -171,7 +171,7 @@ fn drain_stalled_obligations_for_coroutines(
|
||||
&mut self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
) -> PredicateObligations<'tcx> {
|
||||
let stalled_coroutines = match infcx.typing_mode() {
|
||||
let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
|
||||
TypingMode::Analysis { defining_opaque_types_and_generators } => {
|
||||
defining_opaque_types_and_generators
|
||||
}
|
||||
@@ -878,7 +878,7 @@ fn process_trait_obligation(
|
||||
stalled_on: &mut Vec<TyOrConstInferVar>,
|
||||
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
|
||||
let infcx = self.selcx.infcx;
|
||||
if obligation.predicate.is_global() && !infcx.typing_mode().is_coherence() {
|
||||
if obligation.predicate.is_global() && !self.selcx.typing_mode().is_coherence() {
|
||||
// no type variables present, can use evaluation for better caching.
|
||||
// FIXME: consider caching errors too.
|
||||
if infcx.predicate_must_hold_considering_regions(obligation) {
|
||||
@@ -932,7 +932,7 @@ fn process_projection_obligation(
|
||||
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
|
||||
let tcx = self.selcx.tcx();
|
||||
let infcx = self.selcx.infcx;
|
||||
if obligation.predicate.is_global() && !infcx.typing_mode().is_coherence() {
|
||||
if obligation.predicate.is_global() && !self.selcx.typing_mode().is_coherence() {
|
||||
// no type variables present, can use evaluation for better caching.
|
||||
// FIXME: consider caching errors too.
|
||||
if infcx.predicate_must_hold_considering_regions(obligation) {
|
||||
|
||||
@@ -138,7 +138,7 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
|
||||
|
||||
// Opaques are treated as rigid outside of `TypingMode::PostAnalysis`,
|
||||
// so we can ignore those.
|
||||
match infcx.typing_mode() {
|
||||
match infcx.typing_mode_raw().assert_not_erased() {
|
||||
// FIXME(#132279): We likely want to reveal opaques during post borrowck analysis
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
@@ -410,7 +410,7 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
match data.kind {
|
||||
ty::Opaque { def_id } => {
|
||||
// Only normalize `impl Trait` outside of type inference, usually in codegen.
|
||||
match self.selcx.infcx.typing_mode() {
|
||||
match self.selcx.typing_mode() {
|
||||
// FIXME(#132279): We likely want to reveal opaques during post borrowck analysis
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
|
||||
@@ -950,7 +950,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
|
||||
// and the obligation is monomorphic, otherwise passes such as
|
||||
// transmute checking and polymorphic MIR optimizations could
|
||||
// get a result which isn't correct for all monomorphizations.
|
||||
match selcx.infcx.typing_mode() {
|
||||
match selcx.typing_mode() {
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
|
||||
@@ -215,7 +215,7 @@ fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
|
||||
let res = match data.kind {
|
||||
ty::Opaque { def_id } => {
|
||||
// Only normalize `impl Trait` outside of type inference, usually in codegen.
|
||||
match self.infcx.typing_mode() {
|
||||
match self.infcx.typing_mode_raw().assert_not_erased() {
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
|
||||
@@ -849,7 +849,7 @@ fn assemble_candidates_from_auto_impls(
|
||||
//
|
||||
// Note that this is only sound as projection candidates of opaque types
|
||||
// are always applicable for auto traits.
|
||||
} else if self.infcx.typing_mode().is_coherence() {
|
||||
} else if self.typing_mode().is_coherence() {
|
||||
// We do not emit auto trait candidates for opaque types in coherence.
|
||||
// Doing so can result in weird dependency cycles.
|
||||
candidates.ambiguous = true;
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
use rustc_middle::ty::error::TypeErrorToStringExt;
|
||||
use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
|
||||
use rustc_middle::ty::{
|
||||
self, CandidatePreferenceMode, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate,
|
||||
SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Unnormalized, Upcast,
|
||||
elaborate, may_use_unstable_feature,
|
||||
self, CandidatePreferenceMode, CantBeErased, DeepRejectCtxt, GenericArgsRef,
|
||||
PolyProjectionPredicate, SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt,
|
||||
TypingMode, Unnormalized, Upcast, elaborate, may_use_unstable_feature,
|
||||
};
|
||||
use rustc_next_trait_solver::solve::AliasBoundKind;
|
||||
use rustc_span::Symbol;
|
||||
@@ -199,6 +199,10 @@ pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn typing_mode(&self) -> TypingMode<'tcx, CantBeErased> {
|
||||
self.infcx.typing_mode_raw().assert_not_erased()
|
||||
}
|
||||
|
||||
pub fn with_query_mode(
|
||||
infcx: &'cx InferCtxt<'tcx>,
|
||||
query_mode: TraitQueryMode,
|
||||
@@ -210,7 +214,7 @@ pub fn with_query_mode(
|
||||
/// Enables tracking of intercrate ambiguity causes. See
|
||||
/// the documentation of [`Self::intercrate_ambiguity_causes`] for more.
|
||||
pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) {
|
||||
assert!(self.infcx.typing_mode().is_coherence());
|
||||
assert!(self.typing_mode().is_coherence());
|
||||
assert!(self.intercrate_ambiguity_causes.is_none());
|
||||
|
||||
self.intercrate_ambiguity_causes = Some(FxIndexSet::default());
|
||||
@@ -223,7 +227,7 @@ pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) {
|
||||
pub fn take_intercrate_ambiguity_causes(
|
||||
&mut self,
|
||||
) -> FxIndexSet<IntercrateAmbiguityCause<'tcx>> {
|
||||
assert!(self.infcx.typing_mode().is_coherence());
|
||||
assert!(self.typing_mode().is_coherence());
|
||||
|
||||
self.intercrate_ambiguity_causes.take().unwrap_or_default()
|
||||
}
|
||||
@@ -1022,7 +1026,7 @@ fn evaluate_trait_predicate_recursively<'o>(
|
||||
previous_stack: TraitObligationStackList<'o, 'tcx>,
|
||||
mut obligation: PolyTraitObligation<'tcx>,
|
||||
) -> Result<EvaluationResult, OverflowError> {
|
||||
if !self.infcx.typing_mode().is_coherence()
|
||||
if !self.typing_mode().is_coherence()
|
||||
&& obligation.is_global()
|
||||
&& obligation.param_env.caller_bounds().iter().all(|bound| bound.has_param())
|
||||
{
|
||||
@@ -1479,7 +1483,7 @@ fn filter_reservation_impls(
|
||||
|
||||
fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Result<(), Conflict> {
|
||||
let obligation = &stack.obligation;
|
||||
match self.infcx.typing_mode() {
|
||||
match self.typing_mode() {
|
||||
TypingMode::Coherence => {}
|
||||
TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
@@ -1512,7 +1516,7 @@ fn can_use_global_caches(
|
||||
return false;
|
||||
}
|
||||
|
||||
match self.infcx.typing_mode() {
|
||||
match self.typing_mode() {
|
||||
// Avoid using the global cache during coherence and just rely
|
||||
// on the local cache. It is really just a simplification to
|
||||
// avoid us having to fear that coherence results "pollute"
|
||||
@@ -2565,7 +2569,7 @@ fn match_impl(
|
||||
nested_obligations.extend(obligations);
|
||||
|
||||
if impl_trait_header.polarity == ty::ImplPolarity::Reservation
|
||||
&& !self.infcx.typing_mode().is_coherence()
|
||||
&& !self.typing_mode().is_coherence()
|
||||
{
|
||||
debug!("reservation impls only apply in intercrate mode");
|
||||
return Err(());
|
||||
@@ -2904,7 +2908,7 @@ fn impl_or_trait_obligations(
|
||||
}
|
||||
|
||||
pub(super) fn should_stall_coroutine(&self, def_id: DefId) -> bool {
|
||||
match self.infcx.typing_mode() {
|
||||
match self.typing_mode() {
|
||||
TypingMode::Analysis { defining_opaque_types_and_generators: stalled_generators } => {
|
||||
def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id))
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ fn resolve_associated_item<'tcx>(
|
||||
// and the obligation is monomorphic, otherwise passes such as
|
||||
// transmute checking and polymorphic MIR optimizations could
|
||||
// get a result which isn't correct for all monomorphizations.
|
||||
match typing_env.typing_mode() {
|
||||
match typing_env.typing_mode().assert_not_erased() {
|
||||
ty::TypingMode::Coherence
|
||||
| ty::TypingMode::Analysis { .. }
|
||||
| ty::TypingMode::Borrowck { .. }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use core::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use derive_where::derive_where;
|
||||
@@ -11,6 +12,26 @@
|
||||
use crate::solve::VisibleForLeakCheck;
|
||||
use crate::{self as ty, Interner, TyVid};
|
||||
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
|
||||
impl Sealed for super::CantBeErased {}
|
||||
impl Sealed for super::MayBeErased {}
|
||||
}
|
||||
pub trait TypingModeErasedStatus: private::Sealed + Clone + Copy + Hash + fmt::Debug {}
|
||||
|
||||
#[derive(Clone, Copy, Hash, Debug)]
|
||||
pub enum CantBeErased {}
|
||||
#[derive(Clone, Copy, Hash, Debug)]
|
||||
#[cfg_attr(
|
||||
feature = "nightly",
|
||||
derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext)
|
||||
)]
|
||||
pub struct MayBeErased;
|
||||
|
||||
impl TypingModeErasedStatus for CantBeErased {}
|
||||
impl TypingModeErasedStatus for MayBeErased {}
|
||||
|
||||
/// The current typing mode of an inference context. We unfortunately have some
|
||||
/// slightly different typing rules depending on the current context. See the
|
||||
/// doc comment for each variant for how and why they are used.
|
||||
@@ -42,8 +63,7 @@
|
||||
feature = "nightly",
|
||||
derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext)
|
||||
)]
|
||||
#[cfg_attr(feature = "nightly", rustc_must_match_exhaustively)]
|
||||
pub enum TypingMode<I: Interner> {
|
||||
pub enum TypingMode<I: Interner, S: TypingModeErasedStatus = MayBeErased> {
|
||||
/// When checking whether impls overlap, we check whether any obligations
|
||||
/// are guaranteed to never hold when unifying the impls. This requires us
|
||||
/// to be complete: we must never fail to prove something which may actually
|
||||
@@ -108,6 +128,20 @@ pub enum TypingMode<I: Interner> {
|
||||
/// always run in `PostAnalysis` mode, even when used during analysis. This exposes
|
||||
/// some information about the underlying type to users, but not the type itself.
|
||||
PostAnalysis,
|
||||
|
||||
/// The typing modes above (except coherence) only differ in how they handle
|
||||
///
|
||||
/// - Generators
|
||||
/// - Opaque types
|
||||
/// - Specialization (in `PostAnalysis`)
|
||||
///
|
||||
/// We replace all of them with this `TypingMode` in the first attempt at canonicalization.
|
||||
/// If, during that attempt, we try to access information about opaques or generators
|
||||
/// we bail out, setting a field on `EvalCtxt` that indicates the canonicalization must be
|
||||
/// rerun in the original typing mode.
|
||||
///
|
||||
/// `TypingMode::Coherence` is not replaced by this and is always kept as-is.
|
||||
ErasedNotCoherence(S),
|
||||
}
|
||||
|
||||
/// We want to highly discourage using equality checks on typing modes.
|
||||
@@ -120,7 +154,7 @@ pub enum TypingMode<I: Interner> {
|
||||
feature = "nightly",
|
||||
derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext)
|
||||
)]
|
||||
pub struct TypingModeEqWrapper<I: Interner>(pub TypingMode<I>);
|
||||
pub struct TypingModeEqWrapper<I: Interner>(pub TypingMode<I, MayBeErased>);
|
||||
|
||||
impl<I: Interner> Hash for TypingModeEqWrapper<I> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
@@ -145,12 +179,17 @@ fn eq(&self, other: &Self) -> bool {
|
||||
TypingMode::PostBorrowckAnalysis { defined_opaque_types: r },
|
||||
) => l == r,
|
||||
(TypingMode::PostAnalysis, TypingMode::PostAnalysis) => true,
|
||||
(
|
||||
TypingMode::ErasedNotCoherence(MayBeErased),
|
||||
TypingMode::ErasedNotCoherence(MayBeErased),
|
||||
) => true,
|
||||
(
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis,
|
||||
| TypingMode::PostAnalysis
|
||||
| TypingMode::ErasedNotCoherence(MayBeErased),
|
||||
_,
|
||||
) => false,
|
||||
}
|
||||
@@ -159,7 +198,7 @@ fn eq(&self, other: &Self) -> bool {
|
||||
|
||||
impl<I: Interner> Eq for TypingModeEqWrapper<I> {}
|
||||
|
||||
impl<I: Interner> TypingMode<I> {
|
||||
impl<I: Interner, S: TypingModeErasedStatus> TypingMode<I, S> {
|
||||
/// There are a bunch of places in the compiler where we single out `Coherence`,
|
||||
/// and alter behavior. We'd like to *always* match on `TypingMode` exhaustively,
|
||||
/// but not having this method leads to a bunch of noisy code.
|
||||
@@ -171,10 +210,54 @@ pub fn is_coherence(&self) -> bool {
|
||||
TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis => false,
|
||||
| TypingMode::PostAnalysis
|
||||
| TypingMode::ErasedNotCoherence(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// There are a bunch of places in the trait solver where we single out `Coherence`,
|
||||
/// and alter behavior. We'd like to *always* match on `TypingMode` exhaustively,
|
||||
/// but not having this method leads to a bunch of noisy code.
|
||||
///
|
||||
/// See also the documentation on [`TypingMode`] about exhaustive matching.
|
||||
pub fn is_erased_not_coherence(&self) -> bool {
|
||||
match self {
|
||||
TypingMode::ErasedNotCoherence(_) => true,
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner> TypingMode<I, MayBeErased> {
|
||||
/// Only call this when you're sure you're outside the next trait solver!
|
||||
/// That means either not in the trait solver, or in code that is old-solver only.
|
||||
///
|
||||
/// See the comment on `InferCtxt::typing_mode_raw`
|
||||
pub fn assert_not_erased(self) -> TypingMode<I, CantBeErased> {
|
||||
match self {
|
||||
TypingMode::Coherence => TypingMode::Coherence,
|
||||
TypingMode::Analysis { defining_opaque_types_and_generators } => {
|
||||
TypingMode::Analysis { defining_opaque_types_and_generators }
|
||||
}
|
||||
TypingMode::Borrowck { defining_opaque_types } => {
|
||||
TypingMode::Borrowck { defining_opaque_types }
|
||||
}
|
||||
TypingMode::PostBorrowckAnalysis { defined_opaque_types } => {
|
||||
TypingMode::PostBorrowckAnalysis { defined_opaque_types }
|
||||
}
|
||||
TypingMode::PostAnalysis => TypingMode::PostAnalysis,
|
||||
TypingMode::ErasedNotCoherence(MayBeErased) => panic!(
|
||||
"Called `assert_not_erased` from a place that can be called by the trait solver in `TypingMode::ErasedNotCoherence`. `TypingMode` is `ErasedNotCoherence` in a place where that should be impossible"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner> TypingMode<I, CantBeErased> {
|
||||
/// Analysis outside of a body does not define any opaque types.
|
||||
pub fn non_body_analysis() -> TypingMode<I> {
|
||||
TypingMode::Analysis { defining_opaque_types_and_generators: Default::default() }
|
||||
@@ -217,6 +300,24 @@ pub fn post_borrowck_analysis(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner> From<TypingMode<I, CantBeErased>> for TypingMode<I, MayBeErased> {
|
||||
fn from(value: TypingMode<I, CantBeErased>) -> Self {
|
||||
match value {
|
||||
TypingMode::Coherence => TypingMode::Coherence,
|
||||
TypingMode::Analysis { defining_opaque_types_and_generators } => {
|
||||
TypingMode::Analysis { defining_opaque_types_and_generators }
|
||||
}
|
||||
TypingMode::Borrowck { defining_opaque_types } => {
|
||||
TypingMode::Borrowck { defining_opaque_types }
|
||||
}
|
||||
TypingMode::PostBorrowckAnalysis { defined_opaque_types } => {
|
||||
TypingMode::PostBorrowckAnalysis { defined_opaque_types }
|
||||
}
|
||||
TypingMode::PostAnalysis => TypingMode::PostAnalysis,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_infer_ctxt_like")]
|
||||
pub trait InferCtxtLike: Sized {
|
||||
type Interner: Interner;
|
||||
@@ -232,7 +333,7 @@ fn next_trait_solver(&self) -> bool {
|
||||
|
||||
fn disable_trait_solver_fast_paths(&self) -> bool;
|
||||
|
||||
fn typing_mode(&self) -> TypingMode<Self::Interner>;
|
||||
fn typing_mode_raw(&self) -> TypingMode<Self::Interner>;
|
||||
|
||||
fn universe(&self) -> ty::UniverseIndex;
|
||||
fn create_next_universe(&self) -> ty::UniverseIndex;
|
||||
@@ -408,7 +509,7 @@ pub fn may_use_unstable_feature<'a, I: Interner, Infcx>(
|
||||
// Note: `feature_bound_holds_in_crate` does not consider a feature to be enabled
|
||||
// if we are in std/core even if there is a corresponding `feature` attribute on the crate.
|
||||
|
||||
match infcx.typing_mode() {
|
||||
match infcx.typing_mode_raw().assert_not_erased() {
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
use crate::ir_print::IrPrint;
|
||||
use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem};
|
||||
use crate::relate::Relate;
|
||||
use crate::solve::{CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect};
|
||||
use crate::solve::{
|
||||
AccessedOpaques, CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect,
|
||||
};
|
||||
use crate::visit::{Flags, TypeVisitable};
|
||||
use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph};
|
||||
|
||||
@@ -558,7 +560,7 @@ fn collect_and_apply<I, F>(mut iter: I, f: F) -> Result<R, E>
|
||||
|
||||
impl<I: Interner> search_graph::Cx for I {
|
||||
type Input = CanonicalInput<I>;
|
||||
type Result = QueryResult<I>;
|
||||
type Result = (QueryResult<I>, AccessedOpaques<I>);
|
||||
type AmbiguityInfo = Certainty;
|
||||
|
||||
type DepNodeIndex = I::DepNodeIndex;
|
||||
|
||||
@@ -131,7 +131,7 @@ pub fn super_combine_tys<Infcx, I, R>(
|
||||
(ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }), _)
|
||||
| (_, ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. })) => {
|
||||
assert!(!infcx.next_trait_solver());
|
||||
match infcx.typing_mode() {
|
||||
match infcx.typing_mode_raw().assert_not_erased() {
|
||||
// During coherence, opaque types should be treated as *possibly*
|
||||
// equal to any other type. This is an
|
||||
// extremely heavy hammer, but can be relaxed in a forwards-compatible
|
||||
|
||||
@@ -68,7 +68,7 @@ pub(super) fn insert(
|
||||
let prev = entry.success.replace(Success { required_depth, nested_goals, result });
|
||||
if let Some(prev) = &prev {
|
||||
cx.assert_evaluation_is_concurrent();
|
||||
assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result);
|
||||
assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result, "{input:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,7 +851,7 @@ pub fn evaluate_goal(
|
||||
if let Some((_scope, expected)) = validate_cache {
|
||||
// Do not try to move a goal into the cache again if we're testing
|
||||
// the global cache.
|
||||
assert_eq!(expected, evaluation_result.result, "input={input:?}");
|
||||
assert_eq!(expected, result, "input={input:?}");
|
||||
} else if D::inspect_is_noop(inspect) {
|
||||
self.insert_global_cache(cx, input, evaluation_result, dep_node)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod inspect;
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
use derive_where::derive_where;
|
||||
@@ -8,10 +9,13 @@
|
||||
use rustc_type_ir_macros::{
|
||||
GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::lang_items::SolverTraitLangItem;
|
||||
use crate::search_graph::PathKind;
|
||||
use crate::{self as ty, Canonical, CanonicalVarValues, Interner, Upcast};
|
||||
use crate::{
|
||||
self as ty, Canonical, CanonicalVarValues, CantBeErased, Interner, TypingMode, Upcast,
|
||||
};
|
||||
|
||||
pub type CanonicalInput<I, T = <I as Interner>::Predicate> =
|
||||
ty::CanonicalQueryInput<I, QueryInput<I, T>>;
|
||||
@@ -28,6 +32,301 @@
|
||||
#[cfg_attr(feature = "nightly", derive(StableHash))]
|
||||
pub struct NoSolution;
|
||||
|
||||
pub enum NoSolutionOrOpaquesAccessed {
|
||||
NoSolution(NoSolution),
|
||||
/// A bit like [`NoSolution`], but for functions that normally cannot fail *unless* they accessed
|
||||
/// opaues. (See [`TypingMode::ErasedNotCoherence`]). Getting `OpaquesAccessed` doesn't mean there
|
||||
/// truly is no solution. It just means that we want to bail out of the current query as fast as
|
||||
/// possible, possibly by returning `NoSolution` if that's fastest. This is okay because when you get
|
||||
/// `OpaquesAccessed` we're guaranteed that we're going to retry this query in the original typing
|
||||
/// mode to get the correct answer.
|
||||
OpaquesAccessed,
|
||||
}
|
||||
|
||||
/// This conversion is sound, because even in we're in `OpaquesAccessed`,
|
||||
/// we're going to retry so `NoSolution` is a valid response to give..
|
||||
impl From<NoSolutionOrOpaquesAccessed> for NoSolution {
|
||||
fn from(
|
||||
(NoSolutionOrOpaquesAccessed::NoSolution(_) | NoSolutionOrOpaquesAccessed::OpaquesAccessed): NoSolutionOrOpaquesAccessed,
|
||||
) -> Self {
|
||||
NoSolution
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NoSolution> for NoSolutionOrOpaquesAccessed {
|
||||
fn from(value: NoSolution) -> Self {
|
||||
Self::NoSolution(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)]
|
||||
#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))]
|
||||
pub enum SmallCopyList<T: Copy + Debug + Hash + Eq> {
|
||||
Empty,
|
||||
One([T; 1]),
|
||||
Two([T; 2]),
|
||||
Three([T; 3]),
|
||||
}
|
||||
|
||||
impl<T: Copy + Debug + Hash + Eq> SmallCopyList<T> {
|
||||
fn empty() -> Self {
|
||||
Self::Empty
|
||||
}
|
||||
|
||||
fn new(first: T) -> Self {
|
||||
Self::One([first])
|
||||
}
|
||||
|
||||
/// Computes the union of two lists. Duplicates are removed.
|
||||
fn union(self, other: Self) -> Option<Self> {
|
||||
match (self, other) {
|
||||
(Self::Empty, other) | (other, Self::Empty) => Some(other),
|
||||
|
||||
(Self::One([a]), Self::One([b])) if a == b => Some(Self::One([a])),
|
||||
(Self::One([a]), Self::One([b])) => Some(Self::Two([a, b])),
|
||||
(Self::One([a]), Self::Two([b, c])) | (Self::Two([a, b]), Self::One([c]))
|
||||
if a == b && b == c =>
|
||||
{
|
||||
Some(Self::One([a]))
|
||||
}
|
||||
(Self::One([a]), Self::Two([b, c])) | (Self::Two([a, b]), Self::One([c])) if a == b => {
|
||||
Some(Self::Two([a, c]))
|
||||
}
|
||||
(Self::One([a]), Self::Two([b, c])) | (Self::Two([a, b]), Self::One([c])) if a == c => {
|
||||
Some(Self::Two([a, b]))
|
||||
}
|
||||
(Self::One([a]), Self::Two([b, c])) | (Self::Two([a, b]), Self::One([c])) if b == c => {
|
||||
Some(Self::Two([a, b]))
|
||||
}
|
||||
(Self::One([a]), Self::Two([b, c])) | (Self::Two([a, b]), Self::One([c])) => {
|
||||
Some(Self::Three([a, b, c]))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Debug + Hash + Eq> AsRef<[T]> for SmallCopyList<T> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
match self {
|
||||
Self::Empty => &[],
|
||||
Self::One(l) => l,
|
||||
Self::Two(l) => l,
|
||||
Self::Three(l) => l,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about how we accessed opaque types
|
||||
/// This is what the trait solver does when each states is encountered:
|
||||
///
|
||||
/// | | bail? | rerun goal? |
|
||||
/// | ----------------------- | ----- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
/// | never | no | no |
|
||||
/// | always | yes | yes |
|
||||
/// | [defid in storage] | no | only if any of the defids in the list is in the opaque type storage OR if TypingMode::PostAnalysis |
|
||||
/// | opaque with hidden type | no | only if any of the the opaques in the opaque type storage has a hidden type in this list AND if TypingMode::Analysis |
|
||||
///
|
||||
/// - "bail" is implemented with [`should_bail`](Self::should_bail).
|
||||
/// If true, we're abandoning our attempt to canonicalize in [`TypingMode::ErasedNotCoherence`],
|
||||
/// and should try to return as soon as possible to waste as little time as possible.
|
||||
/// A rerun will be attempted in the original typing mode.
|
||||
///
|
||||
/// - Rerun goal is implemented with `should_rerun_after_erased_canonicalization`, on the `EvalCtxt`.
|
||||
///
|
||||
/// Some variant names contain an `Or` here. They rerun when any of the two conditions applies
|
||||
#[derive_where(Copy, Clone, Debug, Hash, PartialEq, Eq; I: Interner)]
|
||||
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)]
|
||||
#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))]
|
||||
pub enum RerunCondition<I: Interner> {
|
||||
Never,
|
||||
|
||||
/// Note that this only reruns according to the condition *if* we are in [`TypingMode::Analysis`].
|
||||
AnyOpaqueHasInferAsHidden,
|
||||
/// Note: unconditionally reruns in postanalysis
|
||||
OpaqueInStorage(SmallCopyList<I::LocalDefId>),
|
||||
|
||||
/// Merges [`Self::AnyOpaqueHasInferAsHidden`] and [`Self::OpaqueInStorage`].
|
||||
/// Note that just like the unmerged [`Self::OpaqueInStorage`], that part of the
|
||||
/// condition only matters in [`TypingMode::Analysis`]
|
||||
OpaqueInStorageOrAnyOpaqueHasInferAsHidden(SmallCopyList<I::LocalDefId>),
|
||||
|
||||
Always,
|
||||
}
|
||||
|
||||
impl<I: Interner> RerunCondition<I> {
|
||||
/// Merge two rerun states according to the following transition diagram
|
||||
/// (some cells are empty because the table is symmetric, i.e. `a.merge(b)` == `b.merge(a)`).
|
||||
///
|
||||
/// - "self" here means the current state, i.e. the state of the current column
|
||||
/// - square brackets represents that this is a list of things. Even if the state doesn't
|
||||
/// change, we might grow the list to effectively end up in a different state anyway
|
||||
/// - `[o. in s.]` abbreviates "opaque in storage"
|
||||
///
|
||||
///
|
||||
/// | | never | always | [opaque in storage] | opaque has infer as hidden | [o. in s.] or i. as hidden |
|
||||
/// | ------------------------------- | ------ | ------ | ------------------- | -------------------------- | -------------------------- |
|
||||
/// | never | self | self | self | self | self |
|
||||
/// | always | | always | always | always | always |
|
||||
/// | [opaque in storage] | | | concat self | [o. in s.] or i. as hidden | concat to self |
|
||||
/// | opaque has infer as hidden type | | | | self | to self |
|
||||
///
|
||||
fn merge(self, other: Self) -> Self {
|
||||
let merged = match (self, other) {
|
||||
(Self::Never, other) | (other, Self::Never) => other,
|
||||
(Self::Always, _) | (_, Self::Always) => Self::Always,
|
||||
|
||||
(Self::OpaqueInStorage(a), Self::OpaqueInStorage(b)) => {
|
||||
a.union(b).map(Self::OpaqueInStorage).unwrap_or(Self::Always)
|
||||
}
|
||||
(Self::AnyOpaqueHasInferAsHidden, Self::AnyOpaqueHasInferAsHidden) => {
|
||||
Self::AnyOpaqueHasInferAsHidden
|
||||
}
|
||||
(
|
||||
Self::AnyOpaqueHasInferAsHidden,
|
||||
Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(a),
|
||||
)
|
||||
| (
|
||||
Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(a),
|
||||
Self::AnyOpaqueHasInferAsHidden,
|
||||
) => Self::OpaqueInStorage(a),
|
||||
|
||||
(
|
||||
Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(a),
|
||||
Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(b),
|
||||
) => a
|
||||
.union(b)
|
||||
.map(Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden)
|
||||
.unwrap_or(Self::Always),
|
||||
|
||||
(Self::OpaqueInStorage(a), Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(b))
|
||||
| (Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(b), Self::OpaqueInStorage(a)) => a
|
||||
.union(b)
|
||||
.map(Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden)
|
||||
.unwrap_or(Self::Always),
|
||||
|
||||
(Self::OpaqueInStorage(a), Self::AnyOpaqueHasInferAsHidden)
|
||||
| (Self::AnyOpaqueHasInferAsHidden, Self::OpaqueInStorage(a)) => {
|
||||
Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(a)
|
||||
}
|
||||
};
|
||||
debug!("merging rerun state {self:?} + {other:?} => {merged:?}");
|
||||
merged
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn should_bail(&self) -> bool {
|
||||
match self {
|
||||
Self::Always => true,
|
||||
Self::Never
|
||||
| Self::OpaqueInStorage(_)
|
||||
| Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(_)
|
||||
| Self::AnyOpaqueHasInferAsHidden => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true when any access of opaques was attempted.
|
||||
/// i.e. when `self != Self::Never`
|
||||
#[must_use]
|
||||
fn might_rerun(&self) -> bool {
|
||||
match self {
|
||||
Self::Never => false,
|
||||
Self::Always
|
||||
| Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(_)
|
||||
| Self::OpaqueInStorage(_)
|
||||
| Self::AnyOpaqueHasInferAsHidden => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Mainly for debugging, to keep track of the source of the rerunning
|
||||
/// in [`TypingMode::ErasedNotCoherence`].
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
#[derive(TypeVisitable_Generic, GenericTypeVisitable)]
|
||||
#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))]
|
||||
pub enum RerunReason {
|
||||
NormalizeOpaqueTypeRemoteCrate,
|
||||
NormalizeOpaqueType,
|
||||
MayUseUnstableFeature,
|
||||
EvaluateConst,
|
||||
SkipErasedAttempt,
|
||||
SelfTyInfer,
|
||||
FetchEligibleAssocItem,
|
||||
AutoTraitLeakage,
|
||||
TryStallCoroutine,
|
||||
}
|
||||
|
||||
#[derive_where(Copy, Clone, Debug, Hash, PartialEq, Eq; I: Interner)]
|
||||
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)]
|
||||
#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))]
|
||||
pub struct AccessedOpaques<I: Interner> {
|
||||
#[cfg_attr(feature = "nightly", type_visitable(ignore))]
|
||||
#[type_foldable(identity)]
|
||||
pub reason: Option<RerunReason>,
|
||||
pub rerun: RerunCondition<I>,
|
||||
}
|
||||
|
||||
impl<I: Interner> Default for AccessedOpaques<I> {
|
||||
fn default() -> Self {
|
||||
Self { reason: None, rerun: RerunCondition::Never }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner> AccessedOpaques<I> {
|
||||
pub fn update(&mut self, other: Self) {
|
||||
*self = Self {
|
||||
// prefer the newest reason
|
||||
reason: other.reason.or(self.reason),
|
||||
// merging accessed states can only result in MultipleOrUnknown
|
||||
rerun: self.rerun.merge(other.rerun),
|
||||
};
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn might_rerun(&self) -> bool {
|
||||
self.rerun.might_rerun()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn should_bail(&self) -> bool {
|
||||
self.rerun.should_bail()
|
||||
}
|
||||
|
||||
pub fn rerun_always(&mut self, reason: RerunReason) {
|
||||
debug!("set rerun always");
|
||||
self.update(AccessedOpaques { reason: Some(reason), rerun: RerunCondition::Always });
|
||||
}
|
||||
|
||||
pub fn rerun_if_in_post_analysis(&mut self, reason: RerunReason) {
|
||||
debug!("set rerun if post analysis");
|
||||
self.update(AccessedOpaques {
|
||||
reason: Some(reason),
|
||||
rerun: RerunCondition::OpaqueInStorage(SmallCopyList::empty()),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn rerun_if_opaque_in_opaque_type_storage(
|
||||
&mut self,
|
||||
reason: RerunReason,
|
||||
defid: I::LocalDefId,
|
||||
) {
|
||||
debug!("set rerun if opaque type {defid:?} in storage");
|
||||
self.update(AccessedOpaques {
|
||||
reason: Some(reason),
|
||||
rerun: RerunCondition::OpaqueInStorage(SmallCopyList::new(defid)),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn rerun_if_any_opaque_has_infer_as_hidden_type(&mut self, reason: RerunReason) {
|
||||
debug!("set rerun if any opaque in the storage has a hidden type that is an infer var");
|
||||
self.update(AccessedOpaques {
|
||||
reason: Some(reason),
|
||||
rerun: RerunCondition::AnyOpaqueHasInferAsHidden,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// A goal is a statement, i.e. `predicate`, we want to prove
|
||||
/// given some assumptions, i.e. `param_env`.
|
||||
///
|
||||
@@ -35,7 +334,7 @@
|
||||
/// we're currently typechecking while the `predicate` is some trait bound.
|
||||
#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, P)]
|
||||
#[derive_where(Copy; I: Interner, P: Copy)]
|
||||
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)]
|
||||
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic, GenericTypeVisitable)]
|
||||
#[cfg_attr(
|
||||
feature = "nightly",
|
||||
derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext)
|
||||
@@ -97,7 +396,7 @@ pub enum GoalSource {
|
||||
|
||||
#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, Goal<I, P>)]
|
||||
#[derive_where(Copy; I: Interner, Goal<I, P>: Copy)]
|
||||
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)]
|
||||
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)]
|
||||
#[cfg_attr(
|
||||
feature = "nightly",
|
||||
derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext)
|
||||
@@ -233,6 +532,14 @@ pub enum BuiltinImplSource {
|
||||
TraitUpcasting(usize),
|
||||
}
|
||||
|
||||
#[derive_where(Copy, Clone, Debug; I: Interner)]
|
||||
pub enum FetchEligibleAssocItemResponse<I: Interner> {
|
||||
Err(I::ErrorGuaranteed),
|
||||
Found(I::DefId),
|
||||
NotFound(TypingMode<I, CantBeErased>),
|
||||
NotFoundBecauseErased,
|
||||
}
|
||||
|
||||
#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)]
|
||||
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)]
|
||||
#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))]
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
//@ compile-flags: -Znext-solver
|
||||
//@ build-pass
|
||||
//@ edition: 2021
|
||||
//@ compile-flags: -C debuginfo=1 --crate-type=lib
|
||||
// This test explores a funny case when handling opaque types:
|
||||
// We involve a couple of iterator types from the standard library, which
|
||||
// importantly is another crate from this test's perspective.
|
||||
// When we check whether we can normalize something, if the DefId is from
|
||||
// another crate we refuse to.
|
||||
//
|
||||
// However, in `TypingMode::PostAnalysis`, all opaques become normalizable,
|
||||
// and so also these iteraators from `std`. In the next solver, when we
|
||||
// canonicalize, we always do a first attempt in `TypingMode::ErasedNotCoherence`,
|
||||
// deleting all opaque types in scope.
|
||||
//
|
||||
// If we then end up accessing opaques, we rerun the canonicalization in the
|
||||
// original typing modes *with* opaques. This relies on us properly tracking
|
||||
// whether opaques were used. And, even if opaque types have a non-local defid,
|
||||
// we *can* end up being able to normalize said opaque if the original `TypingMode`
|
||||
// was `PostAnalysis`.
|
||||
//
|
||||
// This test makes sure that we indeed track these opaque type accesses properly.
|
||||
|
||||
pub(crate) struct Foo;
|
||||
|
||||
impl From<()> for Foo {
|
||||
fn from(_: ()) -> Foo {
|
||||
String::new().extend('a'.to_uppercase());
|
||||
Foo
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user