diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 316fc9ba5f97d..a6a60b9299919 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -8,7 +8,7 @@ use rustc_middle::mir::{self, ConstAlloc, ConstValue}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, MayBeErased, Ty, TyCtxt}; use rustc_middle::{bug, throw_inval}; use rustc_span::Span; use rustc_span::def_id::LocalDefId; @@ -383,6 +383,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( "Const eval should always happens in PostAnalysis mode. See the comment in `InterpCx::new` for more details." ) } + ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } // Make sure we format the instance even if we do not print it. diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 9e23f56d372b2..de1c547fcf912 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -3,7 +3,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ValTreeCreationError}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::layout::{LayoutCx, TyAndLayout}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, MayBeErased, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::DUMMY_SP; use tracing::{debug, instrument, trace}; @@ -247,6 +247,7 @@ pub(crate) fn eval_to_valtree<'tcx>( "Const eval should always happens in PostAnalysis mode. See the comment in `InterpCx::new` for more details." ) } + ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } let const_alloc = tcx.eval_to_allocation_raw(typing_env.as_query_input(cid))?; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 466dcff359829..77f5a84115db4 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -9,8 +9,8 @@ use rustc_middle::ty::layout::{ LayoutOfHelpers, TyAndLayout, }; use rustc_middle::ty::{ - self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingEnv, TypingMode, - Variance, + self, GenericArgsRef, MayBeErased, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingEnv, + TypingMode, Variance, }; use rustc_middle::{bug, mir, span_bug}; use rustc_span::Span; @@ -251,6 +251,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | TypingMode::PostBorrowckAnalysis { .. } => { bug!("Const eval should always happens in PostAnalysis mode."); } + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 3952d3889bb8f..9e7243c8d64a0 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -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>, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 857713e3295c9..7e2a761f436ba 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -17,7 +17,7 @@ use rustc_hir_analysis::hir_ty_lowering::{ }; use rustc_infer::infer::{self, RegionVariableOrigin}; use rustc_infer::traits::{DynCompatibilityViolation, Obligation}; -use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, CantBeErased, Const, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_session::Session; use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span}; use rustc_trait_selection::error_reporting::TypeErrCtxt; @@ -161,6 +161,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + 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() } diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index c889381586753..593e183637a97 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -73,7 +73,7 @@ impl<'tcx> InferCtxt<'tcx> { 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 diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index fada30ff30633..6e04df7881746 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -22,8 +22,8 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.next_trait_solver } - 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 { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index a38d4e819e298..f5c0226cc73ae 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -33,6 +33,7 @@ use rustc_middle::ty::{ 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; @@ -632,8 +633,28 @@ impl<'tcx> InferCtxt<'tcx> { 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 typing_mode(&self) -> TypingMode<'tcx> { + pub fn typing_mode_raw(&self) -> TypingMode<'tcx> { self.typing_mode } @@ -1036,7 +1057,7 @@ impl<'tcx> InferCtxt<'tcx> { #[inline(always)] pub fn can_define_opaque_ty(&self, id: impl Into) -> bool { debug_assert!(!self.next_trait_solver()); - match self.typing_mode() { + match self.typing_mode_raw() { TypingMode::Analysis { defining_opaque_types_and_generators: defining_opaque_types, } @@ -1048,7 +1069,8 @@ impl<'tcx> InferCtxt<'tcx> { // to support PostBorrowckAnalysis in the old solver as well. TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => false, + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence(MayBeErased) => false, } } @@ -1363,7 +1385,7 @@ impl<'tcx> InferCtxt<'tcx> { /// 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 @@ -1374,7 +1396,8 @@ impl<'tcx> InferCtxt<'tcx> { } mode @ (ty::TypingMode::Coherence | ty::TypingMode::PostBorrowckAnalysis { .. } - | ty::TypingMode::PostAnalysis) => mode, + | ty::TypingMode::PostAnalysis + | ty::TypingMode::ErasedNotCoherence(MayBeErased)) => mode, }; ty::TypingEnv::new(param_env, typing_mode) } diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 0c447f7c69973..3a6ad8efd5aff 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -89,7 +89,7 @@ impl<'tcx> InferCtxt<'tcx> { 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 @@ impl<'tcx> InferCtxt<'tcx> { // 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 diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 0229af53f2a67..ecabf95756de7 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -603,7 +603,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, '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); @@ -735,7 +735,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { // 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); diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 3343f27033301..ae99e5ded9005 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -14,6 +14,8 @@ pub type QueryResult<'tcx> = ir::solve::QueryResult>; pub type CandidateSource<'tcx> = ir::solve::CandidateSource>; pub type CanonicalInput<'tcx, P = ty::Predicate<'tcx>> = ir::solve::CanonicalInput, P>; pub type CanonicalResponse<'tcx> = ir::solve::CanonicalResponse>; +pub type FetchEligibleAssocItemResponse<'tcx> = + ir::solve::FetchEligibleAssocItemResponse>; pub type PredefinedOpaques<'tcx> = &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index bc971d7a43705..beae90b671c98 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1015,7 +1015,7 @@ impl<'tcx> TypingEnv<'tcx> { def_id: impl IntoQueryKey, ) -> 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) -> TypingEnv<'tcx> { @@ -1032,7 +1032,12 @@ impl<'tcx> TypingEnv<'tcx> { | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => {} - TypingMode::PostAnalysis => return self, + TypingMode::PostAnalysis + // If we get here in erased mode, we don't know the original typing mode. + // It could be post analysis, or something else. + // Regardless of what it was, we want to continue in erased mode, + // if we end up accessing any opaque types we bail out anyway. + | TypingMode::ErasedNotCoherence(MayBeErased) => return self, } // No need to reveal opaques with the new solver enabled, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 9164f7b57e648..355c1b51aebe4 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -14,10 +14,10 @@ use rustc_hir::LangItem; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, extension}; use rustc_span::{DUMMY_SP, Span, Symbol, kw, sym}; -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::{MayBeErased, TyKind::*}; use tracing::instrument; use ty::util::IntTypeExt; @@ -39,7 +39,7 @@ pub type AliasTyKind<'tcx> = ir::AliasTyKind>; pub type FnSig<'tcx> = ir::FnSig>; pub type Binder<'tcx, T> = ir::Binder, T>; pub type EarlyBinder<'tcx, T> = ir::EarlyBinder, T>; -pub type TypingMode<'tcx> = ir::TypingMode>; +pub type TypingMode<'tcx, S = MayBeErased> = ir::TypingMode, S>; pub type TypingModeEqWrapper<'tcx> = ir::TypingModeEqWrapper>; pub type Placeholder<'tcx, T> = ir::Placeholder, T>; pub type PlaceholderRegion<'tcx> = ir::PlaceholderRegion>; diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 3da9d6aac9be9..ccc9126ed7fe9 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -7,7 +7,7 @@ use rustc_index::Idx; use rustc_middle::mir::*; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, GenericArg, GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, GenericArg, GenericArgsRef, MayBeErased, Ty, TyCtxt}; use rustc_middle::{bug, span_bug, traits}; use rustc_span::{DUMMY_SP, Spanned, dummy_spanned}; use tracing::{debug, instrument}; @@ -556,6 +556,7 @@ where | ty::TypingMode::PostBorrowckAnalysis { .. } => { bug!() } + ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } let field_ty = field.ty(tcx, args); diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 7fdbfa023af4b..0df430e356474 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -13,12 +13,12 @@ use std::iter; use canonicalizer::Canonicalizer; use rustc_index::IndexVec; -use rustc_type_ir::inherent::*; 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 rustc_type_ir::{MayBeErased, inherent::*}; use tracing::instrument; use crate::delegate::SolverDelegate; @@ -46,6 +46,13 @@ impl ResponseT for inspect::State { } } +pub(super) enum EraseOpaqueTypes { + /// This setting erases opaque types, unless we're in coherence. + /// In `TypingMode::Coherence` we never erase opaque types + IfNotCoherence, + No, +} + /// Canonicalizes the goal remembering the original values /// for each bound variable. /// @@ -54,11 +61,35 @@ pub(super) fn canonicalize_goal( delegate: &D, goal: Goal, opaque_types: &[(ty::OpaqueTypeKey, I::Ty)], + erase_opaque_types: EraseOpaqueTypes, ) -> (Vec, CanonicalInput) where D: SolverDelegate, I: Interner, { + let (opaque_types, typing_mode) = match (erase_opaque_types, delegate.typing_mode_raw()) { + // In `TypingMode::Coherence` there should not be any opaques, and we also don't change typing mode. + (_, TypingMode::Coherence) => { + assert!(opaque_types.is_empty()); + (&[][..], TypingMode::Coherence) + } + // Make sure we're not recursively in `ErasedNotCoherence`. + (_, TypingMode::ErasedNotCoherence(MayBeErased)) => { + assert!(opaque_types.is_empty()); + (&[][..], TypingMode::ErasedNotCoherence(MayBeErased)) + } + // If we're supposed to erase opaque types, and we're in any typing mode other than coherence, + // do the erasing and change typing mode. + ( + EraseOpaqueTypes::IfNotCoherence, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis, + ) => (&[][..], TypingMode::ErasedNotCoherence(MayBeErased)), + (EraseOpaqueTypes::No, typing_mode) => (opaque_types, typing_mode), + }; + let (orig_values, canonical) = Canonicalizer::canonicalize_input( delegate, QueryInput { @@ -66,10 +97,9 @@ where 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) } diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 9d5aa8bc124b6..7b2cc1549efda 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -1,6 +1,6 @@ use std::ops::Deref; -use rustc_type_ir::solve::{Certainty, Goal, NoSolution}; +use rustc_type_ir::solve::{Certainty, FetchEligibleAssocItemResponse, Goal, NoSolution}; use rustc_type_ir::{self as ty, InferCtxtLike, Interner, TypeFoldable}; pub trait SolverDelegate: Deref + Sized { @@ -79,10 +79,7 @@ pub trait SolverDelegate: Deref + Sized { goal_trait_ref: ty::TraitRef, trait_assoc_def_id: ::DefId, impl_def_id: ::ImplId, - ) -> Result< - Option<::DefId>, - ::ErrorGuaranteed, - >; + ) -> FetchEligibleAssocItemResponse; fn is_transmutable( &self, diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 2e17885b4f44d..28eb10276b91a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -6,7 +6,6 @@ use std::cell::Cell; use std::ops::ControlFlow; use derive_where::derive_where; -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, SizedTraitKind}; @@ -15,6 +14,7 @@ use rustc_type_ir::{ TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast, elaborate, }; +use rustc_type_ir::{MayBeErased, inherent::*}; use tracing::{debug, instrument}; use super::trait_goals::TraitGoalProvenVia; @@ -419,14 +419,14 @@ where &mut self, goal: Goal, assemble_from: AssembleCandidatesFrom, - ) -> (Vec>, FailedCandidateInfo) { + ) -> Result<(Vec>, FailedCandidateInfo), NoSolution> { let mut candidates = vec![]; let mut failed_candidate_info = FailedCandidateInfo { param_env_head_usages: CandidateHeadUsages::default() }; let Ok(normalized_self_ty) = self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty()) else { - return (candidates, failed_candidate_info); + return Ok((candidates, failed_candidate_info)); }; let goal: Goal = goal @@ -434,8 +434,8 @@ where if normalized_self_ty.is_ty_var() { debug!("self type has been normalized to infer"); - self.try_assemble_bounds_via_registered_opaques(goal, assemble_from, &mut candidates); - return (candidates, failed_candidate_info); + self.try_assemble_bounds_via_registered_opaques(goal, assemble_from, &mut candidates)?; + return Ok((candidates, failed_candidate_info)); } // Vars that show up in the rest of the goal substs may have been constrained by @@ -446,7 +446,7 @@ where && let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal) { candidates.push(candidate); - return (candidates, failed_candidate_info); + return Ok((candidates, failed_candidate_info)); } self.assemble_alias_bound_candidates(goal, &mut candidates); @@ -471,7 +471,8 @@ where 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) @@ -496,7 +497,7 @@ where } } - (candidates, failed_candidate_info) + Ok((candidates, failed_candidate_info)) } pub(super) fn forced_ambiguity( @@ -682,14 +683,18 @@ where goal: Goal, candidates: &mut Vec>, ) { - 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 + // TODO: separate path for probes erroring because of accessing opaques + // assert!(res.is_ok()); } /// For some deeply nested `::A::B::C::D` rigid associated type, @@ -957,12 +962,8 @@ where allow_inference_constraints: AllowInferenceConstraints, candidates: &mut Vec>, ) { - 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; @@ -1012,12 +1013,13 @@ where /// /// See /// for why this is necessary. + #[tracing::instrument(skip(self, assemble_from))] fn try_assemble_bounds_via_registered_opaques>( &mut self, goal: Goal, assemble_from: AssembleCandidatesFrom, candidates: &mut Vec>, - ) { + ) -> Result<(), NoSolution> { let self_ty = goal.predicate.self_ty(); // We only use this hack during HIR typeck. let opaque_types = match self.typing_mode() { @@ -1026,11 +1028,15 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => vec![], + TypingMode::ErasedNotCoherence(MayBeErased) => { + self.opaque_accesses.rerun_if_any_opaque_has_infer_as_hidden_type("self ty infer"); + Vec::new() + } }; if opaque_types.is_empty() { candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); - return; + return Ok(()); } for &alias_ty in &opaque_types { @@ -1057,7 +1063,7 @@ where // 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` + // them as a candidate. Imagine we've got `?x: Trait` // and `?x` has been sub-unified with the hidden type of // `impl Trait`, We take the item bound `opaque: Trait` // and replace all occurrences of `opaque` with `?x`. This results @@ -1128,6 +1134,8 @@ where this.evaluate_added_goals_and_make_canonical_response(certainty) })); } + + Ok(()) } /// Assemble and merge candidates for goals which are related to an underlying trait @@ -1184,7 +1192,7 @@ where // still need to consider alias-bounds for normalization, see // `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`. let (mut candidates, _) = self - .assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds); + .assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds)?; debug!(?candidates); // If the trait goal has been proven by using the environment, we want to treat @@ -1213,7 +1221,7 @@ where } TraitGoalProvenVia::Misc => { let (mut candidates, _) = - self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); + self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All)?; // Prefer "orphaned" param-env normalization predicates, which are used // (for example, and ideally only) when proving item bounds for an impl. diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 0543143ef8fea..9d523d09a5986 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -981,6 +981,13 @@ where .iter() .filter(|source_projection| self.projection_may_match(**source_projection, alias_term)); let Some(replacement) = matching_projections.next() else { + // In TypingMode::ErasedNotCoherence we might not have access to opaque types, + // causing all matching projections to be filtered out. + // In that case, we should just return ASAP. + if self.ecx.opaque_accesses.might_rerun() { + return Ok(None); + } + // This shouldn't happen. panic!("could not replace {alias_term:?} with term from from {:?}", self.self_ty); }; diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 3e44ba689cd25..7c435cecda21a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -2,11 +2,11 @@ //! `T: const Trait` or `T: [const] Trait`. use rustc_type_ir::fast_reject::DeepRejectCtxt; -use rustc_type_ir::inherent::*; 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, elaborate}; +use rustc_type_ir::{MayBeErased, inherent::*}; use tracing::instrument; use super::assembly::{Candidate, structural_traits}; @@ -143,7 +143,8 @@ where TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => return Err(NoSolution), + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence(MayBeErased) => return Err(NoSolution), }, ty::ImplPolarity::Positive => Certainty::Yes, }; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 6841fe1c5124e..11252e84917ed 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -4,22 +4,24 @@ use std::ops::ControlFlow; #[cfg(feature = "nightly")] use rustc_macros::HashStable_NoContext; use rustc_type_ir::data_structures::{HashMap, HashSet}; -use rustc_type_ir::inherent::*; 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::OpaqueTypesJank; +use rustc_type_ir::solve::{ + AccessedOpaques, FetchEligibleAssocItemResponse, OpaqueTypesJank, RerunCondition, SmallCopyList, +}; use rustc_type_ir::{ - self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, - TypingMode, + self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, OpaqueTypeKey, + PredicateKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, TypingMode, }; -use tracing::{debug, instrument, trace}; +use rustc_type_ir::{MayBeErased, inherent::*}; +use tracing::{Level, debug, instrument, trace}; use super::has_only_region_constraints; use crate::canonical::{ - canonicalize_goal, canonicalize_response, instantiate_and_apply_query_response, - response_no_constraints_raw, + EraseOpaqueTypes, canonicalize_goal, canonicalize_response, + instantiate_and_apply_query_response, response_no_constraints_raw, }; use crate::coherence; use crate::delegate::SolverDelegate; @@ -73,6 +75,13 @@ impl CurrentGoalKind { } } +#[derive(Debug)] +enum RerunDecision { + Yes, + No, + NoAndPropagateToParent, +} + pub struct EvalCtxt<'a, D, I = ::Interner> where D: SolverDelegate, @@ -133,6 +142,9 @@ where // evaluation code. tainted: Result<(), NoSolution>, + /// Tracks accesses of opaque types while in [`TypingMode::ErasedNotCoherence`]. + pub(super) opaque_accesses: AccessedOpaques, + pub(super) inspect: inspect::EvaluationStepBuilder, } @@ -254,7 +266,7 @@ where I: Interner, { pub(super) fn typing_mode(&self) -> TypingMode { - self.delegate.typing_mode() + self.delegate.typing_mode_raw() } /// Computes the `PathKind` for the step from the current goal to the @@ -330,12 +342,14 @@ where 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 } @@ -347,13 +361,13 @@ where /// /// 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( + pub(super) fn enter_canonical( cx: I, search_graph: &'a mut SearchGraph, canonical_input: CanonicalInput, proof_tree_builder: &mut inspect::ProofTreeBuilder, - f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal) -> R, - ) -> R { + f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal) -> Result, + ) -> (Result, AccessedOpaques) { 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()); @@ -374,6 +388,10 @@ where } 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, @@ -386,6 +404,7 @@ where 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); @@ -399,7 +418,7 @@ where // 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) { @@ -464,19 +483,84 @@ where // duplicate entries. let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); + if self.typing_mode().is_erased_not_coherence() { + assert!(opaque_types.is_empty()); + } - 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 step_kind = self.step_kind_for_source(source); + + let tracing_span = tracing::span!( + Level::DEBUG, + "evaluate goal raw in typing mode", + "{:?} opaques={:?}", + self.typing_mode(), + opaque_types + ) + .entered(); + + let (result, orig_values, canonical_goal) = 'retry_canonicalize: { + let mut skip_erased_attempt = false; + if matches!(self.typing_mode(), TypingMode::Analysis { .. }) + && opaque_types.iter().any(|(_, ty)| ty.is_ty_var()) + && let PredicateKind::Clause(ClauseKind::Trait(..)) = + goal.predicate.kind().skip_binder() + { + skip_erased_attempt = true; + } + + if !skip_erased_attempt { + debug!("trying without opaques: {goal:?}"); + + let (accessed_opaques, data) = self.canonicalize_goal_maybe_erased( + goal, + &opaque_types, + step_kind, + EraseOpaqueTypes::IfNotCoherence, + ); + + let should_rerun = self.should_rerun_after_erased_canonicalization( + accessed_opaques, + self.typing_mode(), + &opaque_types, + ); + match should_rerun { + RerunDecision::Yes => {} + RerunDecision::No => break 'retry_canonicalize data, + RerunDecision::NoAndPropagateToParent => { + self.opaque_accesses.update(accessed_opaques); + break 'retry_canonicalize data; + } + } + } + debug!("rerunning with opaques"); + + let (accessed_opaques, data) = self.canonicalize_goal_maybe_erased( + goal, + &opaque_types, + step_kind, + EraseOpaqueTypes::No, + ); + 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" + ); + + data + }; + + let response = match result { + Ok(response) => { + debug!("success"); + response + } + Err(NoSolution) => { + debug!("normal failure"); + return Err(NoSolution); + } }; + drop(tracing_span); + let has_changed = if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; @@ -561,6 +645,116 @@ where )) } + fn canonicalize_goal_maybe_erased( + &mut self, + goal: Goal, + opaque_types: &[(ty::OpaqueTypeKey, I::Ty)], + step_kind: PathKind, + erase_opaque_types: EraseOpaqueTypes, + ) -> (AccessedOpaques, (QueryResult, Vec, CanonicalInput)) { + let (orig_values, canonical_goal) = + canonicalize_goal(self.delegate, goal, &opaque_types, erase_opaque_types); + + let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( + self.cx(), + canonical_goal, + step_kind, + &mut inspect::ProofTreeBuilder::new_noop(), + ); + + (accessed_opaques, (canonical_result, orig_values, canonical_goal)) + } + + fn should_rerun_after_erased_canonicalization( + &self, + AccessedOpaques { reason: _, rerun }: AccessedOpaques, + original_typing_mode: TypingMode, + parent_opaque_types: &[(OpaqueTypeKey, 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::NoAndPropagateToParent + } + // ============================= + // 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) -> QueryResult { let Goal { param_env, predicate } = goal; let kind = predicate.kind(); @@ -1126,7 +1320,7 @@ where goal_trait_ref: ty::TraitRef, trait_assoc_def_id: I::DefId, impl_def_id: I::ImplId, - ) -> Result, I::ErrorGuaranteed> { + ) -> FetchEligibleAssocItemResponse { self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id) } @@ -1161,10 +1355,15 @@ where // 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, ) -> Option { + if self.typing_mode().is_erased_not_coherence() { + self.opaque_accesses.rerun_always("evaluate const"); + return None; + } + self.delegate.evaluate_const(param_env, uv) } @@ -1186,10 +1385,15 @@ where } 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("may use unstable feature"); + return false; + } + may_use_unstable_feature(&**self.delegate, param_env, symbol) } @@ -1363,6 +1567,10 @@ where .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 } } } @@ -1483,13 +1691,15 @@ pub fn evaluate_root_goal_for_proof_tree_raw_provider< canonical_goal: CanonicalInput, ) -> (QueryResult, I::Probe) { let mut inspect = inspect::ProofTreeBuilder::new(); - let canonical_result = SearchGraph::::evaluate_root_goal_for_proof_tree( + let (canonical_result, accessed_opaques) = SearchGraph::::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)) } @@ -1505,7 +1715,12 @@ pub(super) fn evaluate_root_goal_for_proof_tree, let opaque_types = delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = canonicalize_goal(delegate, goal, &opaque_types); + if delegate.typing_mode_raw().is_erased_not_coherence() { + assert!(opaque_types.is_empty()); + } + + let (orig_values, canonical_goal) = + canonicalize_goal(delegate, goal, &opaque_types, EraseOpaqueTypes::No); let (canonical_result, final_revision) = delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal); diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index 8ef2dad78848e..b9b251ca9baf1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -1,8 +1,9 @@ use std::marker::PhantomData; use rustc_type_ir::search_graph::CandidateHeadUsages; +use rustc_type_ir::solve::{AccessedOpaques, CanonicalResponse}; 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 @@ where impl ProbeCtxt<'_, '_, D, I, F, T> where - F: FnOnce(&T) -> inspect::ProbeKind, + F: FnOnce(&Result) -> inspect::ProbeKind, D: SolverDelegate, 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, + ) -> (Result, CandidateHeadUsages) { let mut candidate_usages = CandidateHeadUsages::default(); + + if self.ecx.opaque_accesses.should_bail() { + return (Err(NoSolution), 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 @@ where (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, + ) -> Result { 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, + ) -> Result { 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, propagated_nested_goals: Vec<(GoalSource, Goal, Option>)>, - ) -> T { + ) -> Result { let ProbeCtxt { ecx: outer, probe_kind, _result } = self; + if outer.opaque_accesses.should_bail() { + return Err(NoSolution); + } + let delegate = outer.delegate; let max_input_universe = outer.max_input_universe; let mut nested = EvalCtxt { @@ -75,6 +88,7 @@ where 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,6 +100,9 @@ where nested.inspect.probe_kind(probe_kind); outer.inspect = nested.inspect.finish_probe(); } + + outer.opaque_accesses.update(nested.opaque_accesses); + r } } @@ -95,7 +112,7 @@ where D: SolverDelegate, I: Interner, { - cx: ProbeCtxt<'me, 'a, D, I, F, QueryResult>, + cx: ProbeCtxt<'me, 'a, D, I, F, CanonicalResponse>, source: CandidateSource, } @@ -124,7 +141,7 @@ where /// as expensive as necessary to output the desired information. pub(in crate::solve) fn probe(&mut self, probe_kind: F) -> ProbeCtxt<'_, 'a, D, I, F, T> where - F: FnOnce(&T) -> inspect::ProbeKind, + F: FnOnce(&Result) -> inspect::ProbeKind, { ProbeCtxt { ecx: self, probe_kind, _result: PhantomData } } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 58bd7cf663d98..afadfe50feb93 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -22,9 +22,9 @@ mod search_graph; mod trait_goals; use derive_where::derive_where; -use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; use rustc_type_ir::{self as ty, Interner, TyVid, TypingMode}; +use rustc_type_ir::{MayBeErased, inherent::*}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -376,6 +376,8 @@ where | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => { !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id)) } + // Caller should handle erased not coherence + TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 4deb6ed0bb81f..8700275e0386e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -4,10 +4,10 @@ mod inherent; mod opaque_types; 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::SizedTraitKind; +use rustc_type_ir::solve::{FetchEligibleAssocItemResponse, SizedTraitKind}; use rustc_type_ir::{self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Upcast as _}; +use rustc_type_ir::{MayBeErased, inherent::*}; use tracing::instrument; use crate::delegate::SolverDelegate; @@ -276,9 +276,9 @@ where 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 @@ -309,7 +309,11 @@ where } }; } - Err(guar) => return error_response(ecx, guar), + FetchEligibleAssocItemResponse::Err(guar) => return error_response(ecx, guar), + FetchEligibleAssocItemResponse::NotFoundBecauseErased => { + ecx.opaque_accesses.rerun_always("fetch eligible assoc item"); + return Err(NoSolution); + } }; if !cx.has_item_definition(target_item_def_id) { @@ -337,7 +341,8 @@ where ty::TypingMode::Analysis { .. } | ty::TypingMode::Borrowck { .. } | ty::TypingMode::PostBorrowckAnalysis { .. } - | ty::TypingMode::PostAnalysis => { + | ty::TypingMode::PostAnalysis + | ty::TypingMode::ErasedNotCoherence(MayBeErased) => { ecx.structurally_instantiate_normalizes_to_term( goal, goal.predicate.alias, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index 9b2bd7cb74ff0..d4ab70bcf109f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -1,9 +1,9 @@ //! Computes a normalizes-to (projection) goal for opaque types. This goal //! behaves differently depending on the current `TypingMode`. -use rustc_type_ir::inherent::*; -use rustc_type_ir::solve::GoalSource; +use rustc_type_ir::solve::{GoalSource, NoSolution}; use rustc_type_ir::{self as ty, Interner, TypingMode, fold_regions}; +use rustc_type_ir::{MayBeErased, inherent::*}; use crate::delegate::SolverDelegate; use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; @@ -13,6 +13,7 @@ where D: SolverDelegate, I: Interner, { + #[tracing::instrument(skip(self))] pub(super) fn normalize_opaque_type( &mut self, goal: Goal>, @@ -96,6 +97,7 @@ where TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => unreachable!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } @@ -134,6 +136,32 @@ where 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 definding 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("normalize opaque type", def_id); + } else { + self.opaque_accesses + .rerun_if_in_post_analysis("normalize opaque type non local"); + } + 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) + } } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 0490b285aedf0..45a32e51ed3f5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -3,8 +3,8 @@ use std::marker::PhantomData; 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 @@ where cx: I, kind: PathKind, input: CanonicalInput, - ) -> QueryResult { + ) -> (QueryResult, AccessedOpaques) { match kind { PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes), PathKind::Unknown | PathKind::ForcedAmbiguity => { @@ -69,13 +69,18 @@ where 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) -> Option { - match result { + fn is_initial_provisional_result( + result: (QueryResult, AccessedOpaques), + ) -> Option { + match result.0 { Ok(response) => { if has_no_inference_or_external_constraints(response) { if response.value.certainty == Certainty::Yes { @@ -91,16 +96,22 @@ where } } - fn stack_overflow_result(cx: I, input: CanonicalInput) -> QueryResult { + fn stack_overflow_result( + cx: I, + input: CanonicalInput, + ) -> (QueryResult, AccessedOpaques) { response_no_constraints(cx, input, Certainty::overflow(true)) } - fn fixpoint_overflow_result(cx: I, input: CanonicalInput) -> QueryResult { + fn fixpoint_overflow_result( + cx: I, + input: CanonicalInput, + ) -> (QueryResult, AccessedOpaques) { response_no_constraints(cx, input, Certainty::overflow(false)) } - fn is_ambiguous_result(result: QueryResult) -> Option { - result.ok().and_then(|response| { + fn is_ambiguous_result(result: (QueryResult, AccessedOpaques)) -> Option { + result.0.ok().and_then(|response| { if has_no_inference_or_external_constraints(response) && matches!(response.value.certainty, Certainty::Maybe { .. }) { @@ -115,7 +126,7 @@ where cx: I, for_input: CanonicalInput, certainty: Certainty, - ) -> QueryResult { + ) -> (QueryResult, AccessedOpaques) { response_no_constraints(cx, for_input, certainty) } @@ -124,7 +135,7 @@ where cx: I, input: CanonicalInput, inspect: &mut Self::ProofTreeBuilder, - ) -> QueryResult { + ) -> (QueryResult, AccessedOpaques) { 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( cx: I, input: CanonicalInput, certainty: Certainty, -) -> QueryResult { - Ok(response_no_constraints_raw( - cx, - input.canonical.max_universe, - input.canonical.var_kinds, - certainty, - )) +) -> (QueryResult, AccessedOpaques) { + ( + Ok(response_no_constraints_raw( + cx, + input.canonical.max_universe, + input.canonical.var_kinds, + certainty, + )), + AccessedOpaques::default(), + ) } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 33c165fbea6c4..6200bcc8634e1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -8,10 +8,10 @@ use rustc_type_ir::solve::{ AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, SizedTraitKind, }; use rustc_type_ir::{ - self as ty, FieldInfo, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef, - TypeVisitableExt as _, TypingMode, Upcast as _, elaborate, + self as ty, FieldInfo, Interner, MayBeErased, Movability, PredicatePolarity, TraitPredicate, + TraitRef, TypeVisitableExt as _, TypingMode, 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}; @@ -72,14 +72,15 @@ where // 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, + // In coherence mode, this is ambiguous. But outside of, // 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), + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence(MayBeErased) => return Err(NoSolution), }, // Impl matches polarity @@ -233,6 +234,11 @@ where 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("auto trait leakage"); + 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 @@ -799,58 +805,56 @@ where return vec![]; } - let result_to_single = |result| match result { - Ok(resp) => vec![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:?}"), + let result = ecx.probe(|_| ProbeKind::UnsizeAssembly).enter( + |ecx| -> Result>, 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(MaybeCause::Ambiguity)?]) + } - (_, ty::Infer(ty::TyVar(..))) => { - result_to_single(ecx.forced_ambiguity(MaybeCause::Ambiguity)) - } + // 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, + )), - // 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)) => Ok(vec![ + ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data)?, + ]), - // `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)) => { + Ok(vec![ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty)?]) + } - // `[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` -> `Struct` where `T: Unsize` + (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)?]) + } - // `Struct` -> `Struct` where `T: Unsize` - (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), - ) + _ => Err(NoSolution), } + }, + ); - _ => vec![], - } - }) + match result { + Ok(resp) => resp, + Err(NoSolution) => vec![], + } } fn consider_builtin_field_candidate( @@ -1393,7 +1397,8 @@ where TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => {} + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence(MayBeErased) => {} } if candidates @@ -1547,7 +1552,7 @@ where goal: Goal>, ) -> Result<(CanonicalResponse, Option), NoSolution> { let (candidates, failed_candidate_info) = - self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); + self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All)?; let candidate_preference_mode = CandidatePreferenceMode::compute(self.cx(), goal.predicate.def_id()); self.merge_trait_candidates(candidate_preference_mode, candidates, failed_candidate_info) @@ -1564,6 +1569,11 @@ where return Some(self.forced_ambiguity(MaybeCause::Ambiguity)); } } + TypingMode::ErasedNotCoherence(MayBeErased) => { + // Trying to continue here isn't worth it. + self.opaque_accesses.rerun_always("try stall coroutine"); + return Some(Err(NoSolution)); + } TypingMode::Coherence | TypingMode::PostAnalysis | TypingMode::Borrowck { defining_opaque_types: _ } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 62572694de326..eccd88588d59a 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -8,13 +8,13 @@ use rustc_infer::infer::canonical::{ 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}; @@ -71,7 +71,13 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< // FIXME: Properly consider opaques here. && self.inner.borrow_mut().opaque_types().is_empty() { - return Some(Certainty::AMBIGUOUS); + // in erased mode, observing that opaques are empty aren't enough to giv a result + // here, so let's try the slow path instead. + if self.typing_mode_raw().is_erased_not_coherence() { + return None; + } else { + return Some(Certainty::AMBIGUOUS); + } } if trait_pred.polarity() == ty::PredicatePolarity::Positive { @@ -263,8 +269,14 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< goal_trait_ref: ty::TraitRef<'tcx>, trait_assoc_def_id: DefId, impl_def_id: DefId, - ) -> Result, 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. @@ -274,7 +286,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< // 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 { .. } @@ -283,11 +295,20 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< 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 diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 7b61a653ae31e..08e1913e95575 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -282,7 +282,7 @@ where &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 } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 567f660a59383..0f7ad953ae84c 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -95,6 +95,7 @@ impl<'tcx, E> NormalizationFolder<'_, 'tcx, E> where E: FromSolverError<'tcx, NextSolverError<'tcx>>, { + #[tracing::instrument(skip(self))] fn normalize_alias_term( &mut self, alias_term: ty::Term<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 9959c92a6f897..45d06bbbc9d01 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -25,7 +25,7 @@ pub fn evaluate_host_effect_obligation<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &HostEffectObligation<'tcx>, ) -> Result>, 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" diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 2dbeff4a50508..17dab6281cf7b 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -172,7 +172,7 @@ where &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 } @@ -841,7 +841,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { stalled_on: &mut Vec, ) -> ProcessResult, 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) { @@ -895,7 +895,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { ) -> ProcessResult, 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) { diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 65c4b40d43964..414ee14d51e3c 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -12,8 +12,8 @@ use rustc_macros::extension; use rustc_middle::span_bug; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{ - self, AliasTerm, Term, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, - TypeVisitableExt, TypingMode, + self, AliasTerm, MayBeErased, Term, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitable, TypeVisitableExt, TypingMode, }; use tracing::{debug, instrument}; @@ -133,13 +133,14 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable>>( // Opaques are treated as rigid outside of `TypingMode::PostAnalysis`, // so we can ignore those. - match infcx.typing_mode() { + match infcx.typing_mode_raw() { // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(ty::TypeFlags::HAS_TY_OPAQUE), TypingMode::PostAnalysis => {} + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } value.has_type_flags(flags) @@ -394,7 +395,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, '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 { .. } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 72d3ba9629f4d..16a75273828b1 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -948,7 +948,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 { .. } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index a088384146293..3d625f95c504f 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -213,7 +213,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { let res = match data.kind { ty::Opaque { .. } => { // 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 { .. } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index f7614e7c9730a..3b599db8ff1c2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -849,7 +849,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // // 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; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index a564e8060d930..aec6c77e7f62c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -26,9 +26,9 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable; 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, Upcast, elaborate, - may_use_unstable_feature, + self, CandidatePreferenceMode, CantBeErased, DeepRejectCtxt, GenericArgsRef, + PolyProjectionPredicate, SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, + TypingMode, Upcast, elaborate, may_use_unstable_feature, }; use rustc_next_trait_solver::solve::AliasBoundKind; use rustc_span::Symbol; @@ -199,6 +199,10 @@ impl<'cx, '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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// 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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn take_intercrate_ambiguity_causes( &mut self, ) -> FxIndexSet> { - assert!(self.infcx.typing_mode().is_coherence()); + assert!(self.typing_mode().is_coherence()); self.intercrate_ambiguity_causes.take().unwrap_or_default() } @@ -1018,7 +1022,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { previous_stack: TraitObligationStackList<'o, 'tcx>, mut obligation: PolyTraitObligation<'tcx>, ) -> Result { - 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()) { @@ -1467,7 +1471,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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 { .. } @@ -1500,7 +1504,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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" @@ -2550,7 +2554,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { 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(()); @@ -2885,7 +2889,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } 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)) } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index e48e525e571d0..d126105daeb70 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -6,7 +6,8 @@ use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError}; use rustc_middle::ty::{ - self, ClosureKind, GenericArgsRef, Instance, PseudoCanonicalInput, TyCtxt, TypeVisitableExt, + self, ClosureKind, GenericArgsRef, Instance, MayBeErased, PseudoCanonicalInput, TyCtxt, + TypeVisitableExt, }; use rustc_span::sym; use rustc_trait_selection::traits; @@ -160,6 +161,7 @@ fn resolve_associated_item<'tcx>( | ty::TypingMode::Borrowck { .. } | ty::TypingMode::PostBorrowckAnalysis { .. } => false, ty::TypingMode::PostAnalysis => !trait_ref.still_further_specializable(), + ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } }; if !eligible { diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index ac6e345ec1de1..3a00a483d5dec 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -1,3 +1,4 @@ +use core::fmt; use std::hash::{Hash, Hasher}; use derive_where::derive_where; @@ -10,6 +11,26 @@ use crate::relate::RelateResult; use crate::relate::combine::PredicateEmittingRelation; 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, HashStable_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,7 +63,7 @@ use crate::{self as ty, Interner, TyVid}; derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] #[cfg_attr(feature = "nightly", cfg_attr(not(bootstrap), rustc_must_match_exhaustively))] -pub enum TypingMode { +pub enum TypingMode { /// 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 @@ -107,6 +128,20 @@ pub enum TypingMode { /// 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`) + /// + /// This typing mode replaces all of them in canonicalization on the first attempt. + /// If, during that attempt, we try to access information about opaques or generators + /// we bail out with a response containing [`AccessedOpaques::Yes`], + /// and we re-canonicalize 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. @@ -119,7 +154,7 @@ pub enum TypingMode { feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] -pub struct TypingModeEqWrapper(pub TypingMode); +pub struct TypingModeEqWrapper(pub TypingMode); impl Hash for TypingModeEqWrapper { fn hash(&self, state: &mut H) { @@ -144,12 +179,17 @@ impl PartialEq for TypingModeEqWrapper { 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, } @@ -158,7 +198,7 @@ impl PartialEq for TypingModeEqWrapper { impl Eq for TypingModeEqWrapper {} -impl TypingMode { +impl TypingMode { /// 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. @@ -170,10 +210,54 @@ impl TypingMode { TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } + | 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 TypingMode { + /// Only call this when you're sure you're oustide 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 { + 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 TypingMode { /// Analysis outside of a body does not define any opaque types. pub fn non_body_analysis() -> TypingMode { TypingMode::Analysis { defining_opaque_types_and_generators: Default::default() } @@ -216,6 +300,24 @@ impl TypingMode { } } +impl From> for TypingMode { + fn from(value: TypingMode) -> 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; @@ -229,7 +331,7 @@ pub trait InferCtxtLike: Sized { true } - fn typing_mode(&self) -> TypingMode; + fn typing_mode_raw(&self) -> TypingMode; fn universe(&self) -> ty::UniverseIndex; fn create_next_universe(&self) -> ty::UniverseIndex; @@ -403,7 +505,7 @@ where // 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() { TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } @@ -411,5 +513,6 @@ where infcx.cx().features().feature_bound_holds_in_crate(symbol) } TypingMode::PostAnalysis => true, + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index baae3f2ebe363..f12ff7f443761 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -11,7 +11,9 @@ use crate::inherent::*; 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}; @@ -559,7 +561,7 @@ impl CollectAndApply for Result { impl search_graph::Cx for I { type Input = CanonicalInput; - type Result = QueryResult; + type ResultAndAccessedOpaques = (QueryResult, AccessedOpaques); type AmbiguityInfo = Certainty; type DepNodeIndex = I::DepNodeIndex; diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 3489e1f55bc3f..6493d81e024bd 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -7,11 +7,11 @@ use super::{ structurally_relate_consts, structurally_relate_tys, }; use crate::error::TypeError; -use crate::inherent::*; use crate::relate::VarianceDiagInfo; use crate::solve::Goal; use crate::visit::TypeVisitableExt as _; use crate::{self as ty, InferCtxtLike, Interner, TypingMode, Upcast}; +use crate::{MayBeErased, inherent::*}; pub trait PredicateEmittingRelation::Interner>: TypeRelation @@ -131,7 +131,7 @@ where (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() { // 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 @@ -143,7 +143,10 @@ where TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => structurally_relate_tys(relation, a, b), + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence(MayBeErased) => { + structurally_relate_tys(relation, a, b) + } } } diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index 7e438fefffca0..94ae8a382f12b 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -7,12 +7,12 @@ use crate::search_graph::EvaluationResult; struct Success { required_depth: usize, nested_goals: NestedGoals, - result: X::Tracked, + result: X::Tracked, } struct WithOverflow { nested_goals: NestedGoals, - result: X::Tracked, + result: X::Tracked, } /// The cache entry for a given input. @@ -28,7 +28,7 @@ struct CacheEntry { #[derive_where(Debug; X: Cx)] pub(super) struct CacheData<'a, X: Cx> { - pub(super) result: X::Result, + pub(super) result: X::ResultAndAccessedOpaques, pub(super) required_depth: usize, pub(super) encountered_overflow: bool, pub(super) nested_goals: &'a NestedGoals, @@ -63,7 +63,7 @@ impl GlobalCache { 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:?}"); } } } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 535af2718f3ab..75137bf8e0a31 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -39,7 +39,7 @@ pub use global_cache::GlobalCache; /// of the search graph. pub trait Cx: Copy { type Input: Debug + Eq + Hash + Copy; - type Result: Debug + Eq + Hash + Copy; + type ResultAndAccessedOpaques: Debug + Eq + Hash + Copy; type AmbiguityInfo: Debug + Eq + Hash + Copy; type DepNodeIndex; @@ -86,32 +86,34 @@ pub trait Delegate: Sized { cx: Self::Cx, kind: PathKind, input: ::Input, - ) -> ::Result; - fn is_initial_provisional_result(result: ::Result) -> Option; + ) -> ::ResultAndAccessedOpaques; + fn is_initial_provisional_result( + result: ::ResultAndAccessedOpaques, + ) -> Option; fn stack_overflow_result( cx: Self::Cx, input: ::Input, - ) -> ::Result; + ) -> ::ResultAndAccessedOpaques; fn fixpoint_overflow_result( cx: Self::Cx, input: ::Input, - ) -> ::Result; + ) -> ::ResultAndAccessedOpaques; fn is_ambiguous_result( - result: ::Result, + result: ::ResultAndAccessedOpaques, ) -> Option<::AmbiguityInfo>; fn propagate_ambiguity( cx: Self::Cx, for_input: ::Input, ambiguity_info: ::AmbiguityInfo, - ) -> ::Result; + ) -> ::ResultAndAccessedOpaques; fn compute_goal( search_graph: &mut SearchGraph, cx: Self::Cx, input: ::Input, inspect: &mut Self::ProofTreeBuilder, - ) -> ::Result; + ) -> ::ResultAndAccessedOpaques; } /// In the initial iteration of a cycle, we do not yet have a provisional @@ -538,7 +540,7 @@ struct ProvisionalCacheEntry { /// The path from the highest cycle head to this goal. This differs from /// `heads` which tracks the path to the cycle head *from* this goal. path_from_head: PathKind, - result: X::Result, + result: X::ResultAndAccessedOpaques, } /// The final result of evaluating a goal. @@ -556,14 +558,14 @@ struct EvaluationResult { required_depth: usize, heads: CycleHeads, nested_goals: NestedGoals, - result: X::Result, + result: X::ResultAndAccessedOpaques, } impl EvaluationResult { fn finalize( final_entry: StackEntry, encountered_overflow: bool, - result: X::Result, + result: X::ResultAndAccessedOpaques, ) -> EvaluationResult { EvaluationResult { encountered_overflow, @@ -734,7 +736,7 @@ impl, X: Cx> SearchGraph { root_depth: usize, input: X::Input, inspect: &mut D::ProofTreeBuilder, - ) -> X::Result { + ) -> X::ResultAndAccessedOpaques { let mut this = SearchGraph::::new(root_depth); let available_depth = AvailableDepth(root_depth); let step_kind_from_parent = PathKind::Inductive; // is never used @@ -765,7 +767,7 @@ impl, X: Cx> SearchGraph { input: X::Input, step_kind_from_parent: PathKind, inspect: &mut D::ProofTreeBuilder, - ) -> X::Result { + ) -> X::ResultAndAccessedOpaques { let Some(available_depth) = AvailableDepth::allowed_depth_for_nested::(self.root_depth, &self.stack) else { @@ -854,7 +856,7 @@ impl, X: Cx> SearchGraph { 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) } @@ -884,7 +886,7 @@ impl, X: Cx> SearchGraph { result } - fn handle_overflow(&mut self, cx: X, input: X::Input) -> X::Result { + fn handle_overflow(&mut self, cx: X, input: X::Input) -> X::ResultAndAccessedOpaques { if let Some(last) = self.stack.last_mut() { last.encountered_overflow = true; // If computing a goal `B` depends on another goal `A` and @@ -1084,7 +1086,7 @@ impl, X: Cx> SearchGraph { &mut self, input: X::Input, step_kind_from_parent: PathKind, - ) -> Option { + ) -> Option { if !D::ENABLE_PROVISIONAL_CACHE { return None; } @@ -1210,7 +1212,7 @@ impl, X: Cx> SearchGraph { input: X::Input, step_kind_from_parent: PathKind, available_depth: AvailableDepth, - ) -> Option { + ) -> Option { cx.with_global_cache(|cache| { cache .get(cx, input, available_depth, |nested_goals| { @@ -1229,7 +1231,7 @@ impl, X: Cx> SearchGraph { input: X::Input, step_kind_from_parent: PathKind, available_depth: AvailableDepth, - ) -> Option { + ) -> Option { cx.with_global_cache(|cache| { let CacheData { result, required_depth, encountered_overflow, nested_goals } = cache .get(cx, input, available_depth, |nested_goals| { @@ -1258,7 +1260,7 @@ impl, X: Cx> SearchGraph { cx: X, input: X::Input, step_kind_from_parent: PathKind, - ) -> Option { + ) -> Option { let head_index = self.stack.find(input)?; // We have a nested goal which directly relies on a goal deeper in the stack. // @@ -1295,7 +1297,7 @@ impl, X: Cx> SearchGraph { &mut self, stack_entry: &StackEntry, usages: HeadUsages, - result: X::Result, + result: X::ResultAndAccessedOpaques, ) -> Result, ()> { let provisional_result = stack_entry.provisional_result; if let Some(provisional_result) = provisional_result { diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs index 8348666be412d..a17acc9db4101 100644 --- a/compiler/rustc_type_ir/src/search_graph/stack.rs +++ b/compiler/rustc_type_ir/src/search_graph/stack.rs @@ -33,7 +33,7 @@ pub(super) struct StackEntry { /// Starts out as `None` and gets set when rerunning this /// goal in case we encounter a cycle. - pub provisional_result: Option, + pub provisional_result: Option, /// All cycle heads this goal depends on. Lazily updated and only /// up-to date for the top of the stack. diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 72b7df22b30d5..abc300b31630f 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -1,17 +1,20 @@ pub mod inspect; +use std::fmt::Debug; use std::hash::Hash; +use crate::lang_items::SolverTraitLangItem; +use crate::search_graph::PathKind; +use crate::{ + self as ty, Canonical, CanonicalVarValues, CantBeErased, Interner, TypingMode, Upcast, +}; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; - -use crate::lang_items::SolverTraitLangItem; -use crate::search_graph::PathKind; -use crate::{self as ty, Canonical, CanonicalVarValues, Interner, Upcast}; +use tracing::debug; pub type CanonicalInput::Predicate> = ty::CanonicalQueryInput>; @@ -28,6 +31,249 @@ pub type QueryResult = Result, NoSolution>; #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct NoSolution; +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub enum SmallCopyList { + Empty, + One([T; 1]), + Two([T; 2]), + Three([T; 3]), +} + +impl SmallCopyList { + 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 { + 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 AsRef<[T]> for SmallCopyList { + 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::Erased`], +/// 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, GenericTypeVisitable, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub enum RerunCondition { + Never, + + AnyOpaqueHasInferAsHidden, + /// Note: unconditionally reruns in postanalysis + OpaqueInStorage(SmallCopyList), + OpaqueInStorageOrAnyOpaqueHasInferAsHidden(SmallCopyList), + + Always, +} + +impl RerunCondition { + /// 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)`). + /// + /// | | never | always | postanalysis | [defid in storage] | [opaque with hidden type] | + /// | ------------------------------- | ------ | ------ | ------------ | ------------------------- | ------------------------------- | + /// | never | never | always | postanalysis | [defid in storage] | [opaque with hidden type] | + /// | always | | always | always | always | always | + /// | postanalysis | | | postanalysis | [defid in storage] | always | + /// | [defid in storage] | | | | concat [defid in storage] | always | + /// | opaque has infer as hidden type | | | | | opaque has infer as hidden type | + /// + 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, + } + } +} + +#[derive_where(Copy, Clone, Debug, Hash, PartialEq, Eq; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub struct AccessedOpaques { + #[type_visitable(ignore)] + #[type_foldable(identity)] + pub reason: Option<&'static str>, + pub rerun: RerunCondition, +} + +impl AccessedOpaques {} + +impl Default for AccessedOpaques { + fn default() -> Self { + Self { reason: None, rerun: RerunCondition::Never } + } +} + +impl AccessedOpaques { + 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: &'static str) { + debug!("set rerun always"); + self.update(AccessedOpaques { reason: Some(reason), rerun: RerunCondition::Always }); + } + + pub fn rerun_if_in_post_analysis(&mut self, reason: &'static str) { + 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: &'static str, + 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: &'static str) { + 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`. /// @@ -236,6 +482,14 @@ pub enum BuiltinImplSource { TraitUpcasting(usize), } +#[derive_where(Copy, Clone, Debug; I: Interner)] +pub enum FetchEligibleAssocItemResponse { + Err(I::ErrorGuaranteed), + Found(I::DefId), + NotFound(TypingMode), + NotFoundBecauseErased, +} + #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] diff --git a/tests/ui/traits/next-solver/canonical/erased-opaques.rs b/tests/ui/traits/next-solver/canonical/erased-opaques.rs new file mode 100644 index 0000000000000..c22c832975e63 --- /dev/null +++ b/tests/ui/traits/next-solver/canonical/erased-opaques.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -Znext-solver +//@ build-pass +//@ edition: 2021 +//@ compile-flags: -C debuginfo=1 --crate-type=lib + +pub(crate) struct Foo; + +impl From<()> for Foo { + fn from(_: ()) -> Foo { + String::new().extend('a'.to_uppercase()); + Foo + } +}