From 4f512f9c2b395792ec7e06c106e7d0e5f322ba91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 2 Mar 2026 12:32:01 +0100 Subject: [PATCH 1/7] Add `AccessedOpaques`, tracking opaque access during canonicalization --- .../src/solve/eval_ctxt/mod.rs | 19 ++- .../src/solve/eval_ctxt/probe.rs | 2 + compiler/rustc_type_ir/src/solve/mod.rs | 119 +++++++++++++++++- 3 files changed, 134 insertions(+), 6 deletions(-) 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..c655389572f47 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 @@ -8,13 +8,13 @@ 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, AccessedOpaquesInfo, OpaqueTypesJank}; use rustc_type_ir::{ self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; -use tracing::{debug, instrument, trace}; +use tracing::{Level, debug, instrument, trace, warn}; use super::has_only_region_constraints; use crate::canonical::{ @@ -133,6 +133,19 @@ where // evaluation code. tainted: Result<(), NoSolution>, + /// This method is called any time we canonicalize in [`TypingMode::ErasedNotCoherence`], + /// and some operation attempts to access opaque types. + /// In this typing mode, we do not provide opaque types. + /// Attempting to access them should bail out of canonicalization as fast as possible, + /// so we can retry *with* opaque types. + /// + /// This is an optimization strategy: if we *can* canonicalize in `TypingMode::ErasedNotCoherence`, + /// so without accessing opaque types, we can create a smaller cache key (without opaque types), + /// making it more likely that we can use this cached result in the future. + /// + /// This function returns [`NoSolution`] that must be used, to encourage you to bail out. + pub(super) canonicalize_accessed_opaques: AccessedOpaques, + pub(super) inspect: inspect::EvaluationStepBuilder, } @@ -330,6 +343,7 @@ where current_goal_kind: CurrentGoalKind::Misc, origin_span, tainted: Ok(()), + canonicalize_accessed_opaques: AccessedOpaques::default(), }; let result = f(&mut ecx); assert!( @@ -386,6 +400,7 @@ where origin_span: I::Span::dummy(), tainted: Ok(()), inspect: proof_tree_builder.new_evaluation_step(var_values), + canonicalize_accessed_opaques: AccessedOpaques::default(), }; let result = f(&mut ecx, input.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..1bf42bdfe7f24 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,6 +1,7 @@ use std::marker::PhantomData; use rustc_type_ir::search_graph::CandidateHeadUsages; +use rustc_type_ir::solve::{AccessedOpaques, AccessedOpaquesInfo}; use rustc_type_ir::{InferCtxtLike, Interner}; use tracing::instrument; @@ -75,6 +76,7 @@ where origin_span: outer.origin_span, tainted: outer.tainted, inspect: outer.inspect.take_and_enter_probe(), + canonicalize_accessed_opaques: AccessedOpaques::default(), }; let r = nested.delegate.probe(|| { let r = f(&mut nested); diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 72b7df22b30d5..b4e2509426979 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -2,16 +2,16 @@ pub mod inspect; use std::hash::Hash; +use crate::lang_items::SolverTraitLangItem; +use crate::search_graph::PathKind; +use crate::{self as ty, Canonical, CanonicalVarValues, Interner, 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::warn; pub type CanonicalInput::Predicate> = ty::CanonicalQueryInput>; @@ -28,6 +28,117 @@ pub type QueryResult = Result, NoSolution>; #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct NoSolution; +#[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 AccessedState { + Known1([I::LocalDefId; 1]), + Known2([I::LocalDefId; 2]), + Known3([I::LocalDefId; 3]), + UnknownOrTooManyKnown, +} + +#[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 AccessedOpaquesInfo { + #[type_visitable(ignore)] + #[type_foldable(identity)] + pub reason: &'static str, + pub defids_accessed: AccessedState, +} + +impl AccessedOpaquesInfo { + pub fn merge(&self, new_info: AccessedOpaquesInfo) -> Self { + let defid_accessed = match (self.defids_accessed, new_info.defids_accessed) { + (AccessedState::Known1([one]), AccessedState::Known1([two])) => { + AccessedState::Known2([one, two]) + } + (AccessedState::Known2([one, two]), AccessedState::Known1([three])) + | (AccessedState::Known1([one]), AccessedState::Known2([two, three])) => { + AccessedState::Known3([one, two, three]) + } + _ => AccessedState::UnknownOrTooManyKnown, + }; + + Self { + // choose the newest one + reason: new_info.reason, + // merging accessed states can only result in MultipleOrUnknown + defids_accessed: defid_accessed, + } + } + + pub fn opaques_accessed(&self) -> Option<&[I::LocalDefId]> { + match &self.defids_accessed { + AccessedState::Known1(d) => Some(d), + AccessedState::Known2(d) => Some(d), + AccessedState::Known3(d) => Some(d), + AccessedState::UnknownOrTooManyKnown => None, + } + } +} + +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub enum AccessedOpaques { + Yes(AccessedOpaquesInfo), + No, +} + +impl Default for AccessedOpaques { + fn default() -> Self { + Self::No + } +} + +impl AccessedOpaques { + pub fn merge(&mut self, info: AccessedOpaquesInfo) { + warn!("merging {info:?}"); + *self = match self { + AccessedOpaques::Yes(existing_info) => AccessedOpaques::Yes(existing_info.merge(info)), + AccessedOpaques::No => AccessedOpaques::Yes(info), + }; + } + + #[must_use] + pub fn should_bail_instantly(&self) -> bool { + match self { + AccessedOpaques::Yes(AccessedOpaquesInfo { + reason: _, + defids_accessed: AccessedState::UnknownOrTooManyKnown, + }) => true, + AccessedOpaques::Yes(AccessedOpaquesInfo { + reason: _, + defids_accessed: + AccessedState::Known1(_) | AccessedState::Known2(_) | AccessedState::Known3(_), + }) => false, + AccessedOpaques::No => false, + } + } + + pub fn opaques_accessed(&self) -> Option<&[I::LocalDefId]> { + match self { + AccessedOpaques::Yes(i) => i.opaques_accessed(), + AccessedOpaques::No => Some(&[]), + } + } + + pub fn bail_unrecoverable(&mut self, reason: &'static str) { + warn!("bail unrecoverable {reason:?}"); + self.merge(AccessedOpaquesInfo { + reason, + defids_accessed: AccessedState::UnknownOrTooManyKnown, + }); + } + + pub fn bail_defid(&mut self, reason: &'static str, defid: I::LocalDefId) { + warn!("bail defid {defid:?} {reason:?}"); + self.merge(AccessedOpaquesInfo { reason, defids_accessed: AccessedState::Known1([defid]) }); + } +} + /// A goal is a statement, i.e. `predicate`, we want to prove /// given some assumptions, i.e. `param_env`. /// From e2d403641cd213054086ba092c2f68fc91239b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 2 Mar 2026 12:32:01 +0100 Subject: [PATCH 2/7] Change sg apis to cache opaque accesses --- .../src/solve/assembly/mod.rs | 6 +- .../src/solve/eval_ctxt/mod.rs | 14 +-- .../src/solve/eval_ctxt/probe.rs | 46 +++++++--- .../src/solve/search_graph.rs | 49 +++++++---- .../src/solve/trait_goals.rs | 88 +++++++++---------- compiler/rustc_type_ir/src/interner.rs | 6 +- .../src/search_graph/global_cache.rs | 6 +- .../rustc_type_ir/src/search_graph/mod.rs | 42 ++++----- .../rustc_type_ir/src/search_graph/stack.rs | 2 +- 9 files changed, 149 insertions(+), 110 deletions(-) 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..0c0dc02e1decb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -682,14 +682,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, 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 c655389572f47..c8e6d89afd58a 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 @@ -361,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()); @@ -414,7 +414,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.canonicalize_accessed_opaques) } pub(super) fn ignore_candidate_head_usages(&mut self, usages: CandidateHeadUsages) { @@ -481,7 +481,7 @@ where let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, &opaque_types); - let canonical_result = self.search_graph.evaluate_goal( + let (canonical_result, _accessed_opaques) = self.search_graph.evaluate_goal( self.cx(), canonical_goal, self.step_kind_for_source(source), @@ -1498,13 +1498,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!(!matches!(accessed_opaques, AccessedOpaques::Yes(..))); (canonical_result, cx.mk_probe(final_revision)) } 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 1bf42bdfe7f24..f4f5c21be7f48 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,9 +1,9 @@ use std::marker::PhantomData; use rustc_type_ir::search_graph::CandidateHeadUsages; -use rustc_type_ir::solve::{AccessedOpaques, AccessedOpaquesInfo}; +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; @@ -24,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.canonicalize_accessed_opaques.should_bail_instantly() { + 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(); @@ -42,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.canonicalize_accessed_opaques.should_bail_instantly() { + return Err(NoSolution); + } + let delegate = outer.delegate; let max_input_universe = outer.max_input_universe; let mut nested = EvalCtxt { @@ -88,6 +100,12 @@ where nested.inspect.probe_kind(probe_kind); outer.inspect = nested.inspect.finish_probe(); } + + if let AccessedOpaques::Yes(info) = nested.canonicalize_accessed_opaques { + warn!("forwarding accessed opaques {info:?}"); + outer.canonicalize_accessed_opaques.merge(info); + } + r } } @@ -97,7 +115,7 @@ where D: SolverDelegate, I: Interner, { - cx: ProbeCtxt<'me, 'a, D, I, F, QueryResult>, + cx: ProbeCtxt<'me, 'a, D, I, F, CanonicalResponse>, source: CandidateSource, } @@ -126,7 +144,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/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 0490b285aedf0..b21bddc301286 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -3,7 +3,9 @@ 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::solve::{ + AccessedOpaques, AccessedOpaquesInfo, CanonicalInput, Certainty, NoSolution, QueryResult, +}; use rustc_type_ir::{Interner, TypingMode}; use crate::canonical::response_no_constraints_raw; @@ -47,7 +49,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 +71,15 @@ where TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => Err(NoSolution), + | TypingMode::PostAnalysis => (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 +95,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 +125,7 @@ where cx: I, for_input: CanonicalInput, certainty: Certainty, - ) -> QueryResult { + ) -> (QueryResult, AccessedOpaques) { response_no_constraints(cx, for_input, certainty) } @@ -124,7 +134,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 +149,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::No, + ) } 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..dd98bd446fd8f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -799,58 +799,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 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)?]) + } - 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:?}"), + // 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, + )), - (_, ty::Infer(ty::TyVar(..))) => { - result_to_single(ecx.forced_ambiguity(MaybeCause::Ambiguity)) - } + // `T` -> `dyn Trait` unsizing. + (_, ty::Dynamic(b_region, b_data)) => Ok(vec![ + ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data)?, + ]), - // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`. - (ty::Dynamic(a_data, a_region), ty::Dynamic(b_data, b_region)) => ecx - .consider_builtin_dyn_upcast_candidates( - goal, a_data, a_region, b_data, b_region, - ), - - // `T` -> `dyn Trait` unsizing. - (_, ty::Dynamic(b_region, b_data)) => result_to_single( - ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data), - ), + // `[T; N]` -> `[T]` unsizing + (ty::Array(a_elem_ty, ..), ty::Slice(b_elem_ty)) => { + 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( 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/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index 7e438fefffca0..1a69af24a4b85 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, 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. From 2d0ef82abdb7aa6ed0f3de8146cdb0c0ef40822c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 2 Mar 2026 12:32:01 +0100 Subject: [PATCH 3/7] Introduced `ErasedNotCoherence` typing mode --- .../src/const_eval/eval_queries.rs | 1 + .../src/const_eval/valtrees.rs | 1 + .../src/interpret/eval_context.rs | 1 + compiler/rustc_infer/src/infer/mod.rs | 2 + .../rustc_infer/src/infer/opaque_types/mod.rs | 1 + compiler/rustc_middle/src/ty/mod.rs | 1 + .../rustc_mir_transform/src/elaborate_drop.rs | 1 + .../src/canonical/mod.rs | 38 ++++++++++++++++--- .../src/solve/assembly/mod.rs | 3 ++ .../src/solve/effect_goals.rs | 1 + .../src/solve/eval_ctxt/mod.rs | 10 +++-- .../rustc_next_trait_solver/src/solve/mod.rs | 1 + .../src/solve/normalizes_to/mod.rs | 2 + .../src/solve/normalizes_to/opaque_types.rs | 2 + .../src/solve/search_graph.rs | 1 + .../src/solve/trait_goals.rs | 3 ++ .../src/solve/delegate.rs | 1 + .../src/solve/fulfill.rs | 1 + .../src/traits/fulfill.rs | 1 + .../src/traits/normalize.rs | 2 + .../src/traits/project.rs | 1 + .../src/traits/query/normalize.rs | 1 + .../src/traits/select/mod.rs | 3 ++ compiler/rustc_ty_utils/src/instance.rs | 1 + compiler/rustc_type_ir/src/infer_ctxt.rs | 22 ++++++++++- compiler/rustc_type_ir/src/relate/combine.rs | 1 + 26 files changed, 92 insertions(+), 11 deletions(-) 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..e500d1345d400 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -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 => 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..2bfce2f945ce4 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -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 => 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..53b9be71bc524 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -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 => todo!(), } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index a38d4e819e298..052091c2d2f92 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1049,6 +1049,7 @@ impl<'tcx> InferCtxt<'tcx> { TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => false, + TypingMode::ErasedNotCoherence => todo!(), } } @@ -1375,6 +1376,7 @@ impl<'tcx> InferCtxt<'tcx> { mode @ (ty::TypingMode::Coherence | ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => mode, + ty::TypingMode::ErasedNotCoherence => todo!(), }; 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..5ef6bd06aa7dc 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -283,6 +283,7 @@ impl<'tcx> InferCtxt<'tcx> { mode @ (ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => { bug!("insert hidden type in {mode:?}") } + ty::TypingMode::ErasedNotCoherence => todo!(), } Ok(()) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index bc971d7a43705..dc0be9b724a8e 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1033,6 +1033,7 @@ impl<'tcx> TypingEnv<'tcx> { | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => {} TypingMode::PostAnalysis => return self, + TypingMode::ErasedNotCoherence => todo!(), } // No need to reveal opaques with the new solver enabled, diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 3da9d6aac9be9..874320abb8338 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -556,6 +556,7 @@ where | ty::TypingMode::PostBorrowckAnalysis { .. } => { bug!() } + ty::TypingMode::ErasedNotCoherence => 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..6e203e57ac505 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -17,7 +17,7 @@ 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 tracing::instrument; @@ -46,6 +46,11 @@ impl ResponseT for inspect::State { } } +pub(super) enum EraseOpaqueTypes { + Yes, + No, +} + /// Canonicalizes the goal remembering the original values /// for each bound variable. /// @@ -54,11 +59,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()) { + // 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) => { + assert!(opaque_types.is_empty()); + (&[][..], TypingMode::ErasedNotCoherence) + } + // 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::Yes, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis, + ) => (&[][..], TypingMode::ErasedNotCoherence), + (EraseOpaqueTypes::No, typing_mode) => (opaque_types, typing_mode), + }; + let (orig_values, canonical) = Canonicalizer::canonicalize_input( delegate, QueryInput { @@ -66,10 +95,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/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 0c0dc02e1decb..aafff517e30e4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -478,6 +478,7 @@ where | CandidateSource::AliasBound(_) ) && has_no_inference_or_external_constraints(c.result) }), + TypingMode::ErasedNotCoherence => todo!(), }; if assemble_impls { self.assemble_impl_candidates(goal, &mut candidates); @@ -967,6 +968,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} + TypingMode::ErasedNotCoherence => todo!(), } let mut i = 0; @@ -1030,6 +1032,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => vec![], + TypingMode::ErasedNotCoherence => todo!(), }; if opaque_types.is_empty() { 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..c49b42bc746ee 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -144,6 +144,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Err(NoSolution), + TypingMode::ErasedNotCoherence => todo!(), }, 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 c8e6d89afd58a..a69cc5d3b6d9c 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 @@ -18,8 +18,8 @@ use tracing::{Level, debug, instrument, trace, warn}; 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; @@ -480,7 +480,8 @@ where let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, &opaque_types); + let (orig_values, canonical_goal) = + canonicalize_goal(self.delegate, goal, &opaque_types, EraseOpaqueTypes::No); let (canonical_result, _accessed_opaques) = self.search_graph.evaluate_goal( self.cx(), canonical_goal, @@ -1522,7 +1523,8 @@ 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); + 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/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 58bd7cf663d98..a123dc241c423 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -376,6 +376,7 @@ where | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => { !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id)) } + TypingMode::ErasedNotCoherence => todo!(), } } } 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..fd4f33fe3de32 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 @@ -307,6 +307,7 @@ where return ecx .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } + ty::TypingMode::ErasedNotCoherence => todo!(), }; } Err(guar) => return error_response(ecx, guar), @@ -344,6 +345,7 @@ where ); return then(ecx, Certainty::Yes); } + ty::TypingMode::ErasedNotCoherence => todo!(), } } else { return error_response(ecx, cx.delay_bug("missing item")); 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..ed8ee97d9decf 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 @@ -96,6 +96,7 @@ where TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => unreachable!(), + TypingMode::ErasedNotCoherence => todo!(), } } @@ -134,6 +135,7 @@ where self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + TypingMode::ErasedNotCoherence => todo!(), } } } 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 b21bddc301286..877e3dfaad0bd 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -72,6 +72,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => (Err(NoSolution), AccessedOpaques::default()), + TypingMode::ErasedNotCoherence => todo!(), }, } } 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 dd98bd446fd8f..517f1f5c66b5e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -80,6 +80,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Err(NoSolution), + TypingMode::ErasedNotCoherence => todo!(), }, // Impl matches polarity @@ -1392,6 +1393,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} + TypingMode::ErasedNotCoherence => todo!(), } if candidates @@ -1566,6 +1568,7 @@ where | TypingMode::PostAnalysis | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {} + TypingMode::ErasedNotCoherence => todo!(), } } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 62572694de326..8a322271125b8 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -283,6 +283,7 @@ 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 => todo!(), } }; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 7b61a653ae31e..56fc7d7b7f21f 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -290,6 +290,7 @@ where | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } | TypingMode::PostAnalysis => return Default::default(), + TypingMode::ErasedNotCoherence => todo!(), }; if stalled_coroutines.is_empty() { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 2dbeff4a50508..49a03109262e8 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -180,6 +180,7 @@ where | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } | TypingMode::PostAnalysis => return Default::default(), + TypingMode::ErasedNotCoherence => todo!(), }; if stalled_coroutines.is_empty() { diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 65c4b40d43964..695bdacd743e2 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -140,6 +140,7 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable>>( | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(ty::TypeFlags::HAS_TY_OPAQUE), TypingMode::PostAnalysis => {} + TypingMode::ErasedNotCoherence => todo!(), } value.has_type_flags(flags) @@ -419,6 +420,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx self.depth -= 1; folded_ty } + TypingMode::ErasedNotCoherence => todo!(), } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 72d3ba9629f4d..d0341bce0d9db 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -967,6 +967,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( selcx.infcx.resolve_vars_if_possible(trait_ref); !poly_trait_ref.still_further_specializable() } + TypingMode::ErasedNotCoherence => todo!(), } } } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index a088384146293..c9a2e0179e0c9 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -250,6 +250,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { self.anon_depth -= 1; folded_ty? } + TypingMode::ErasedNotCoherence => todo!(), } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index a564e8060d930..eb7dce43f9fe9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1473,6 +1473,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Ok(()), + TypingMode::ErasedNotCoherence => todo!(), } debug!("is_knowable()"); @@ -1532,6 +1533,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // FIXME(#132279): This is still incorrect as we treat opaque types // and default associated items differently between these two modes. TypingMode::PostAnalysis => true, + TypingMode::ErasedNotCoherence => todo!(), } } @@ -2893,6 +2895,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | TypingMode::PostAnalysis | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => false, + TypingMode::ErasedNotCoherence => todo!(), } } } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index e48e525e571d0..1d7b7b10fa51a 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -160,6 +160,7 @@ fn resolve_associated_item<'tcx>( | ty::TypingMode::Borrowck { .. } | ty::TypingMode::PostBorrowckAnalysis { .. } => false, ty::TypingMode::PostAnalysis => !trait_ref.still_further_specializable(), + ty::TypingMode::ErasedNotCoherence => 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..6ebf061dd5dee 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -107,6 +107,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, } /// We want to highly discourage using equality checks on typing modes. @@ -144,12 +158,14 @@ impl PartialEq for TypingModeEqWrapper { TypingMode::PostBorrowckAnalysis { defined_opaque_types: r }, ) => l == r, (TypingMode::PostAnalysis, TypingMode::PostAnalysis) => true, + (TypingMode::ErasedNotCoherence, TypingMode::ErasedNotCoherence) => true, ( TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis, + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence, _, ) => false, } @@ -170,7 +186,8 @@ impl TypingMode { TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => false, + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence => false, } } @@ -411,5 +428,6 @@ where infcx.cx().features().feature_bound_holds_in_crate(symbol) } TypingMode::PostAnalysis => true, + TypingMode::ErasedNotCoherence => todo!(), } } diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 3489e1f55bc3f..3f7b8f9e6c848 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -144,6 +144,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => structurally_relate_tys(relation, a, b), + TypingMode::ErasedNotCoherence => todo!(), } } From ffc0d5dd5488d724cf5124679399fd2b9c2a62a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 13 Apr 2026 13:37:51 +0200 Subject: [PATCH 4/7] type safety for typing mode outside trait solver which can't be erased-not-coherence --- .../src/const_eval/eval_queries.rs | 4 +- .../src/const_eval/valtrees.rs | 4 +- .../src/interpret/eval_context.rs | 6 +- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 8 +- .../src/infer/canonical/canonicalizer.rs | 2 +- compiler/rustc_infer/src/infer/context.rs | 4 +- compiler/rustc_infer/src/infer/mod.rs | 31 +++++-- .../rustc_infer/src/infer/opaque_types/mod.rs | 7 +- .../src/infer/relate/generalize.rs | 4 +- compiler/rustc_middle/src/ty/mod.rs | 4 +- compiler/rustc_middle/src/ty/sty.rs | 4 +- .../rustc_mir_transform/src/elaborate_drop.rs | 4 +- .../src/canonical/mod.rs | 10 +-- .../src/solve/assembly/mod.rs | 8 +- .../src/solve/effect_goals.rs | 4 +- .../src/solve/eval_ctxt/mod.rs | 4 +- .../rustc_next_trait_solver/src/solve/mod.rs | 4 +- .../src/solve/normalizes_to/mod.rs | 6 +- .../src/solve/normalizes_to/opaque_types.rs | 6 +- .../src/solve/search_graph.rs | 4 +- .../src/solve/trait_goals.rs | 8 +- .../src/solve/delegate.rs | 6 +- .../src/solve/fulfill.rs | 3 +- .../src/traits/effects.rs | 2 +- .../src/traits/fulfill.rs | 7 +- .../src/traits/normalize.rs | 11 ++- .../src/traits/project.rs | 3 +- .../src/traits/query/normalize.rs | 3 +- .../src/traits/select/candidate_assembly.rs | 2 +- .../src/traits/select/mod.rs | 27 +++--- compiler/rustc_ty_utils/src/instance.rs | 5 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 89 ++++++++++++++++--- compiler/rustc_type_ir/src/relate/combine.rs | 6 +- 33 files changed, 197 insertions(+), 103 deletions(-) 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 e500d1345d400..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,7 +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 => todo!(), + 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 2bfce2f945ce4..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,7 +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 => todo!(), + 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 53b9be71bc524..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,7 +251,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | TypingMode::PostBorrowckAnalysis { .. } => { bug!("Const eval should always happens in PostAnalysis mode."); } - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } 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 052091c2d2f92..227c4386f19be 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, } @@ -1049,7 +1070,7 @@ impl<'tcx> InferCtxt<'tcx> { TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => false, - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } @@ -1364,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 @@ -1376,7 +1397,7 @@ impl<'tcx> InferCtxt<'tcx> { mode @ (ty::TypingMode::Coherence | ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => mode, - ty::TypingMode::ErasedNotCoherence => todo!(), + ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), }; 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 5ef6bd06aa7dc..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 @@ -283,7 +285,6 @@ impl<'tcx> InferCtxt<'tcx> { mode @ (ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => { bug!("insert hidden type in {mode:?}") } - ty::TypingMode::ErasedNotCoherence => todo!(), } Ok(()) 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/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index dc0be9b724a8e..de42f50cc4a5d 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> { @@ -1033,7 +1033,7 @@ impl<'tcx> TypingEnv<'tcx> { | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => {} TypingMode::PostAnalysis => return self, - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } // 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 874320abb8338..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,7 +556,7 @@ where | ty::TypingMode::PostBorrowckAnalysis { .. } => { bug!() } - ty::TypingMode::ErasedNotCoherence => todo!(), + 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 6e203e57ac505..8d8c28f88436f 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, TypingMode, TypingModeEqWrapper, }; +use rustc_type_ir::{MayBeErased, inherent::*}; use tracing::instrument; use crate::delegate::SolverDelegate; @@ -65,16 +65,16 @@ where D: SolverDelegate, I: Interner, { - let (opaque_types, typing_mode) = match (erase_opaque_types, delegate.typing_mode()) { + 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) => { + (_, TypingMode::ErasedNotCoherence(MayBeErased)) => { assert!(opaque_types.is_empty()); - (&[][..], TypingMode::ErasedNotCoherence) + (&[][..], 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. @@ -84,7 +84,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis, - ) => (&[][..], TypingMode::ErasedNotCoherence), + ) => (&[][..], TypingMode::ErasedNotCoherence(MayBeErased)), (EraseOpaqueTypes::No, typing_mode) => (opaque_types, typing_mode), }; 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 aafff517e30e4..1e5df6cb9fc86 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; @@ -478,7 +478,7 @@ where | CandidateSource::AliasBound(_) ) && has_no_inference_or_external_constraints(c.result) }), - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), }; if assemble_impls { self.assemble_impl_candidates(goal, &mut candidates); @@ -968,7 +968,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } let mut i = 0; @@ -1032,7 +1032,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => vec![], - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), }; if opaque_types.is_empty() { 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 c49b42bc746ee..a6dadec296122 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}; @@ -144,7 +144,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Err(NoSolution), - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), }, 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 a69cc5d3b6d9c..a48125a9da4d4 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,7 +4,6 @@ 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}; @@ -14,6 +13,7 @@ use rustc_type_ir::{ TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; +use rustc_type_ir::{CantBeErased, inherent::*}; use tracing::{Level, debug, instrument, trace, warn}; use super::has_only_region_constraints; @@ -267,7 +267,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 diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index a123dc241c423..da24cc0ce8057 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,7 +376,7 @@ where | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => { !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id)) } - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } } 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 fd4f33fe3de32..1973efbd20105 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::{self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Upcast as _}; +use rustc_type_ir::{MayBeErased, inherent::*}; use tracing::instrument; use crate::delegate::SolverDelegate; @@ -307,7 +307,7 @@ where return ecx .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } - ty::TypingMode::ErasedNotCoherence => todo!(), + ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), }; } Err(guar) => return error_response(ecx, guar), @@ -345,7 +345,7 @@ where ); return then(ecx, Certainty::Yes); } - ty::TypingMode::ErasedNotCoherence => todo!(), + ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } else { return error_response(ecx, cx.delay_bug("missing item")); 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 ed8ee97d9decf..3fc7dc6af0798 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::{self as ty, Interner, TypingMode, fold_regions}; +use rustc_type_ir::{MayBeErased, inherent::*}; use crate::delegate::SolverDelegate; use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; @@ -96,7 +96,7 @@ where TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => unreachable!(), - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } @@ -135,7 +135,7 @@ where self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } } 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 877e3dfaad0bd..cd9e7739688aa 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -6,7 +6,7 @@ use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{ AccessedOpaques, AccessedOpaquesInfo, CanonicalInput, Certainty, NoSolution, QueryResult, }; -use rustc_type_ir::{Interner, TypingMode}; +use rustc_type_ir::{Interner, MayBeErased, TypingMode}; use crate::canonical::response_no_constraints_raw; use crate::delegate::SolverDelegate; @@ -72,7 +72,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => (Err(NoSolution), AccessedOpaques::default()), - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), }, } } 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 517f1f5c66b5e..c65ee14bda03b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -2,7 +2,6 @@ use rustc_type_ir::data_structures::IndexSet; use rustc_type_ir::fast_reject::DeepRejectCtxt; -use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::solve::{ AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, SizedTraitKind, @@ -11,6 +10,7 @@ use rustc_type_ir::{ self as ty, FieldInfo, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode, Upcast as _, elaborate, }; +use rustc_type_ir::{MayBeErased, inherent::*}; use tracing::{debug, instrument, trace}; use crate::delegate::SolverDelegate; @@ -80,7 +80,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Err(NoSolution), - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), }, // Impl matches polarity @@ -1393,7 +1393,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } if candidates @@ -1568,7 +1568,7 @@ where | TypingMode::PostAnalysis | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {} - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 8a322271125b8..eb50c8bb24deb 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -12,7 +12,7 @@ use rustc_infer::traits::solve::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}; @@ -274,7 +274,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 self.typing_mode_raw() { TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } @@ -283,7 +283,7 @@ 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 => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } }; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 56fc7d7b7f21f..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 } @@ -290,7 +290,6 @@ where | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } | TypingMode::PostAnalysis => return Default::default(), - TypingMode::ErasedNotCoherence => todo!(), }; if stalled_coroutines.is_empty() { 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 49a03109262e8..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 } @@ -180,7 +180,6 @@ where | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } | TypingMode::PostAnalysis => return Default::default(), - TypingMode::ErasedNotCoherence => todo!(), }; if stalled_coroutines.is_empty() { @@ -842,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) { @@ -896,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 695bdacd743e2..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,14 +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 => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } value.has_type_flags(flags) @@ -395,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 { .. } @@ -420,7 +420,6 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx self.depth -= 1; folded_ty } - TypingMode::ErasedNotCoherence => todo!(), } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index d0341bce0d9db..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 { .. } @@ -967,7 +967,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( selcx.infcx.resolve_vars_if_possible(trait_ref); !poly_trait_ref.still_further_specializable() } - TypingMode::ErasedNotCoherence => todo!(), } } } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index c9a2e0179e0c9..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 { .. } @@ -250,7 +250,6 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { self.anon_depth -= 1; folded_ty? } - TypingMode::ErasedNotCoherence => todo!(), } } 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 eb7dce43f9fe9..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,13 +1471,12 @@ 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 { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Ok(()), - TypingMode::ErasedNotCoherence => todo!(), } debug!("is_knowable()"); @@ -1501,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" @@ -1533,7 +1536,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // FIXME(#132279): This is still incorrect as we treat opaque types // and default associated items differently between these two modes. TypingMode::PostAnalysis => true, - TypingMode::ErasedNotCoherence => todo!(), } } @@ -2552,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(()); @@ -2887,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)) } @@ -2895,7 +2897,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | TypingMode::PostAnalysis | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => false, - TypingMode::ErasedNotCoherence => todo!(), } } } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 1d7b7b10fa51a..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,7 +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 => todo!(), + 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 6ebf061dd5dee..25bfda1d2316a 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 @@ -120,7 +141,7 @@ pub enum TypingMode { /// and we re-canonicalize in the original typing mode. /// /// `TypingMode::Coherence` is not replaced by this and is always kept as-is. - ErasedNotCoherence, + ErasedNotCoherence(S), } /// We want to highly discourage using equality checks on typing modes. @@ -133,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) { @@ -158,14 +179,17 @@ impl PartialEq for TypingModeEqWrapper { TypingMode::PostBorrowckAnalysis { defined_opaque_types: r }, ) => l == r, (TypingMode::PostAnalysis, TypingMode::PostAnalysis) => true, - (TypingMode::ErasedNotCoherence, TypingMode::ErasedNotCoherence) => true, + ( + TypingMode::ErasedNotCoherence(MayBeErased), + TypingMode::ErasedNotCoherence(MayBeErased), + ) => true, ( TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis - | TypingMode::ErasedNotCoherence, + | TypingMode::ErasedNotCoherence(MayBeErased), _, ) => false, } @@ -174,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. @@ -187,10 +211,37 @@ impl TypingMode { | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis - | TypingMode::ErasedNotCoherence => false, + | TypingMode::ErasedNotCoherence(_) => 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() } @@ -233,6 +284,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; @@ -246,7 +315,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; @@ -420,7 +489,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 { .. } @@ -428,6 +497,6 @@ where infcx.cx().features().feature_bound_holds_in_crate(symbol) } TypingMode::PostAnalysis => true, - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 3f7b8f9e6c848..727230af3083c 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 @@ -144,7 +144,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => structurally_relate_tys(relation, a, b), - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } From 46a9c587b921fbc7e9f7ba32f164bfb894e62abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 2 Mar 2026 12:32:01 +0100 Subject: [PATCH 5/7] Add logging in various places --- .../src/solve/normalizes_to/free_alias.rs | 2 ++ .../src/solve/normalizes_to/inherent.rs | 2 ++ .../rustc_next_trait_solver/src/solve/normalizes_to/mod.rs | 3 ++- .../src/solve/normalizes_to/opaque_types.rs | 1 + compiler/rustc_trait_selection/src/solve/normalize.rs | 1 + compiler/rustc_type_ir/src/search_graph/global_cache.rs | 2 +- 6 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs index 8777f84957a79..4967f616338a8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -5,6 +5,7 @@ //! the alias and registers the where-clauses of the type alias. use rustc_type_ir::{self as ty, Interner}; +use tracing::warn; use crate::delegate::SolverDelegate; use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; @@ -18,6 +19,7 @@ where &mut self, goal: Goal>, ) -> QueryResult { + warn!("NFA"); let cx = self.cx(); let free_alias = goal.predicate.alias; diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 42aa237762d9e..9413125ce150d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -6,6 +6,7 @@ //! 3. instantiate and register where clauses. use rustc_type_ir::{self as ty, Interner}; +use tracing::warn; use crate::delegate::SolverDelegate; use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; @@ -19,6 +20,7 @@ where &mut self, goal: Goal>, ) -> QueryResult { + warn!("NFA"); let cx = self.cx(); let inherent = goal.predicate.alias; 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 1973efbd20105..005762605ef53 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 @@ -8,7 +8,7 @@ use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLa use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Upcast as _}; use rustc_type_ir::{MayBeErased, inherent::*}; -use tracing::instrument; +use tracing::{instrument, warn}; use crate::delegate::SolverDelegate; use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; @@ -651,6 +651,7 @@ where ty::Dynamic(_, _) => { let dyn_metadata = cx.require_lang_item(SolverLangItem::DynMetadata); + warn!("CBPC"); cx.type_of(dyn_metadata) .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) } 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 3fc7dc6af0798..23ae6250b84a8 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 @@ -13,6 +13,7 @@ where D: SolverDelegate, I: Interner, { + #[tracing::instrument(skip(self))] pub(super) fn normalize_opaque_type( &mut self, goal: Goal>, 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_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index 1a69af24a4b85..94ae8a382f12b 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -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:?}"); } } } From 7ba504240fb43008679943bc0679d7b12736e652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 2 Mar 2026 12:32:01 +0100 Subject: [PATCH 6/7] Try to canonicalize with erased opaques, retry when opaques are used. --- compiler/rustc_hir_typeck/src/callee.rs | 1 + compiler/rustc_infer/src/infer/mod.rs | 8 +- compiler/rustc_middle/src/ty/mod.rs | 8 +- .../src/canonical/mod.rs | 6 +- .../src/solve/assembly/mod.rs | 43 +-- .../src/solve/assembly/structural_traits.rs | 7 + .../src/solve/effect_goals.rs | 4 +- .../src/solve/eval_ctxt/mod.rs | 264 ++++++++++++++--- .../src/solve/eval_ctxt/probe.rs | 11 +- .../rustc_next_trait_solver/src/solve/mod.rs | 3 +- .../src/solve/normalizes_to/free_alias.rs | 2 - .../src/solve/normalizes_to/inherent.rs | 2 - .../src/solve/normalizes_to/mod.rs | 11 +- .../src/solve/normalizes_to/opaque_types.rs | 29 +- .../src/solve/search_graph.rs | 12 +- .../src/solve/trait_goals.rs | 31 +- .../src/solve/delegate.rs | 13 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 16 ++ compiler/rustc_type_ir/src/relate/combine.rs | 6 +- compiler/rustc_type_ir/src/solve/mod.rs | 269 +++++++++++++----- .../next-solver/canonical/erased-opaques.rs | 13 + 21 files changed, 584 insertions(+), 175 deletions(-) create mode 100644 tests/ui/traits/next-solver/canonical/erased-opaques.rs 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_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 227c4386f19be..f5c0226cc73ae 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1069,8 +1069,8 @@ impl<'tcx> InferCtxt<'tcx> { // to support PostBorrowckAnalysis in the old solver as well. TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => false, - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence(MayBeErased) => false, } } @@ -1396,8 +1396,8 @@ impl<'tcx> InferCtxt<'tcx> { } mode @ (ty::TypingMode::Coherence | ty::TypingMode::PostBorrowckAnalysis { .. } - | ty::TypingMode::PostAnalysis) => mode, - ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + | ty::TypingMode::PostAnalysis + | ty::TypingMode::ErasedNotCoherence(MayBeErased)) => mode, }; ty::TypingEnv::new(param_env, typing_mode) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index de42f50cc4a5d..beae90b671c98 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1032,8 +1032,12 @@ impl<'tcx> TypingEnv<'tcx> { | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => {} - TypingMode::PostAnalysis => return self, - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + 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_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 8d8c28f88436f..0df430e356474 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -47,7 +47,9 @@ impl ResponseT for inspect::State { } pub(super) enum EraseOpaqueTypes { - Yes, + /// This setting erases opaque types, unless we're in coherence. + /// In `TypingMode::Coherence` we never erase opaque types + IfNotCoherence, No, } @@ -79,7 +81,7 @@ where // 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::Yes, + EraseOpaqueTypes::IfNotCoherence, TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } 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 1e5df6cb9fc86..28eb10276b91a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -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,14 +471,14 @@ 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) | CandidateSource::AliasBound(_) ) && has_no_inference_or_external_constraints(c.result) }), - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), }; if assemble_impls { self.assemble_impl_candidates(goal, &mut candidates); @@ -497,7 +497,7 @@ where } } - (candidates, failed_candidate_info) + Ok((candidates, failed_candidate_info)) } pub(super) fn forced_ambiguity( @@ -962,13 +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 => {} - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + if self.typing_mode().is_coherence() { + return; } let mut i = 0; @@ -1018,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() { @@ -1032,12 +1028,15 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => vec![], - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + 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 { @@ -1064,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 @@ -1135,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 @@ -1191,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 @@ -1220,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 a6dadec296122..7c435cecda21a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -143,8 +143,8 @@ where TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => return Err(NoSolution), - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + | 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 a48125a9da4d4..2ef95a31f5d6c 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 @@ -7,14 +7,14 @@ use rustc_type_ir::data_structures::{HashMap, HashSet}; 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::{AccessedOpaques, AccessedOpaquesInfo, OpaqueTypesJank}; +use rustc_type_ir::solve::{AccessedOpaques, 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 rustc_type_ir::{CantBeErased, inherent::*}; -use tracing::{Level, debug, instrument, trace, warn}; +use rustc_type_ir::{MayBeErased, inherent::*}; +use tracing::{Level, debug, instrument, trace}; use super::has_only_region_constraints; use crate::canonical::{ @@ -73,6 +73,13 @@ impl CurrentGoalKind { } } +#[derive(Debug)] +enum RerunDecision { + Yes, + No, + NoAndPropagateToParent, +} + pub struct EvalCtxt<'a, D, I = ::Interner> where D: SolverDelegate, @@ -133,18 +140,8 @@ where // evaluation code. tainted: Result<(), NoSolution>, - /// This method is called any time we canonicalize in [`TypingMode::ErasedNotCoherence`], - /// and some operation attempts to access opaque types. - /// In this typing mode, we do not provide opaque types. - /// Attempting to access them should bail out of canonicalization as fast as possible, - /// so we can retry *with* opaque types. - /// - /// This is an optimization strategy: if we *can* canonicalize in `TypingMode::ErasedNotCoherence`, - /// so without accessing opaque types, we can create a smaller cache key (without opaque types), - /// making it more likely that we can use this cached result in the future. - /// - /// This function returns [`NoSolution`] that must be used, to encourage you to bail out. - pub(super) canonicalize_accessed_opaques: AccessedOpaques, + /// Tracks accesses of opaque types while in [`TypingMode::ErasedNotCoherence`]. + pub(super) opaque_accesses: AccessedOpaques, pub(super) inspect: inspect::EvaluationStepBuilder, } @@ -343,13 +340,14 @@ where current_goal_kind: CurrentGoalKind::Misc, origin_span, tainted: Ok(()), - canonicalize_accessed_opaques: AccessedOpaques::default(), + 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 } @@ -388,6 +386,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, @@ -400,7 +402,7 @@ where origin_span: I::Span::dummy(), tainted: Ok(()), inspect: proof_tree_builder.new_evaluation_step(var_values), - canonicalize_accessed_opaques: AccessedOpaques::default(), + opaque_accesses: AccessedOpaques::default(), }; let result = f(&mut ecx, input.goal); @@ -414,7 +416,7 @@ where // FIXME: Could we make `build_with_canonical` into `enter_with_canonical` and call this at the end? delegate.reset_opaque_types(); - (result, ecx.canonicalize_accessed_opaques) + (result, ecx.opaque_accesses) } pub(super) fn ignore_candidate_head_usages(&mut self, usages: CandidateHeadUsages) { @@ -479,20 +481,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, EraseOpaqueTypes::No); - let (canonical_result, _accessed_opaques) = 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 }; @@ -577,6 +643,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(); @@ -1177,10 +1353,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) } @@ -1202,10 +1383,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) } @@ -1379,6 +1565,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 } } } @@ -1507,7 +1697,7 @@ pub fn evaluate_root_goal_for_proof_tree_raw_provider< ); let final_revision = inspect.unwrap(); - assert!(!matches!(accessed_opaques, AccessedOpaques::Yes(..))); + assert!(!accessed_opaques.might_rerun()); (canonical_result, cx.mk_probe(final_revision)) } @@ -1523,6 +1713,10 @@ 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)); + 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); 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 f4f5c21be7f48..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 @@ -34,7 +34,7 @@ where ) -> (Result, CandidateHeadUsages) { let mut candidate_usages = CandidateHeadUsages::default(); - if self.ecx.canonicalize_accessed_opaques.should_bail_instantly() { + if self.ecx.opaque_accesses.should_bail() { return (Err(NoSolution), candidate_usages); } @@ -69,7 +69,7 @@ where ) -> Result { let ProbeCtxt { ecx: outer, probe_kind, _result } = self; - if outer.canonicalize_accessed_opaques.should_bail_instantly() { + if outer.opaque_accesses.should_bail() { return Err(NoSolution); } @@ -88,7 +88,7 @@ where origin_span: outer.origin_span, tainted: outer.tainted, inspect: outer.inspect.take_and_enter_probe(), - canonicalize_accessed_opaques: AccessedOpaques::default(), + opaque_accesses: AccessedOpaques::default(), }; let r = nested.delegate.probe(|| { let r = f(&mut nested); @@ -101,10 +101,7 @@ where outer.inspect = nested.inspect.finish_probe(); } - if let AccessedOpaques::Yes(info) = nested.canonicalize_accessed_opaques { - warn!("forwarding accessed opaques {info:?}"); - outer.canonicalize_accessed_opaques.merge(info); - } + outer.opaque_accesses.update(nested.opaque_accesses); r } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index da24cc0ce8057..afadfe50feb93 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -376,7 +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)) } - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + // Caller should handle erased not coherence + TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs index 4967f616338a8..8777f84957a79 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -5,7 +5,6 @@ //! the alias and registers the where-clauses of the type alias. use rustc_type_ir::{self as ty, Interner}; -use tracing::warn; use crate::delegate::SolverDelegate; use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; @@ -19,7 +18,6 @@ where &mut self, goal: Goal>, ) -> QueryResult { - warn!("NFA"); let cx = self.cx(); let free_alias = goal.predicate.alias; diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 9413125ce150d..42aa237762d9e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -6,7 +6,6 @@ //! 3. instantiate and register where clauses. use rustc_type_ir::{self as ty, Interner}; -use tracing::warn; use crate::delegate::SolverDelegate; use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; @@ -20,7 +19,6 @@ where &mut self, goal: Goal>, ) -> QueryResult { - warn!("NFA"); let cx = self.cx(); let inherent = goal.predicate.alias; 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 005762605ef53..b6731374f5506 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 @@ -8,7 +8,7 @@ use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLa use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Upcast as _}; use rustc_type_ir::{MayBeErased, inherent::*}; -use tracing::{instrument, warn}; +use tracing::instrument; use crate::delegate::SolverDelegate; use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; @@ -299,7 +299,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, @@ -307,7 +308,6 @@ where return ecx .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } - ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), }; } Err(guar) => return error_response(ecx, guar), @@ -338,14 +338,14 @@ 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, ); return then(ecx, Certainty::Yes); } - ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } else { return error_response(ecx, cx.delay_bug("missing item")); @@ -651,7 +651,6 @@ where ty::Dynamic(_, _) => { let dyn_metadata = cx.require_lang_item(SolverLangItem::DynMetadata); - warn!("CBPC"); cx.type_of(dyn_metadata) .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) } 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 23ae6250b84a8..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,7 +1,7 @@ //! Computes a normalizes-to (projection) goal for opaque types. This goal //! behaves differently depending on the current `TypingMode`. -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::*}; @@ -136,7 +136,32 @@ where self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + 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 cd9e7739688aa..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,9 +3,7 @@ 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::{ - AccessedOpaques, AccessedOpaquesInfo, CanonicalInput, Certainty, NoSolution, QueryResult, -}; +use rustc_type_ir::solve::{AccessedOpaques, CanonicalInput, Certainty, NoSolution, QueryResult}; use rustc_type_ir::{Interner, MayBeErased, TypingMode}; use crate::canonical::response_no_constraints_raw; @@ -71,8 +69,10 @@ where TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => (Err(NoSolution), AccessedOpaques::default()), - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence(MayBeErased) => { + (Err(NoSolution), AccessedOpaques::default()) + } }, } } @@ -158,6 +158,6 @@ fn response_no_constraints( input.canonical.var_kinds, certainty, )), - AccessedOpaques::No, + 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 c65ee14bda03b..6200bcc8634e1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -2,16 +2,16 @@ use rustc_type_ir::data_structures::IndexSet; use rustc_type_ir::fast_reject::DeepRejectCtxt; +use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; 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 rustc_type_ir::{MayBeErased, inherent::*}; -use tracing::{debug, instrument, trace}; +use tracing::{debug, instrument, trace, warn}; use crate::delegate::SolverDelegate; use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; @@ -72,15 +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::ErasedNotCoherence(MayBeErased) => todo!(), + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence(MayBeErased) => return Err(NoSolution), }, // Impl matches polarity @@ -234,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 @@ -1392,8 +1397,8 @@ where TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => {} - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + | 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,11 +1569,15 @@ 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: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {} - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index eb50c8bb24deb..e03e089f3040a 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -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 { @@ -283,7 +289,10 @@ 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) => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => { + // TODO: make sure this can't be ignored by callers + return Ok(None); + } } }; diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 25bfda1d2316a..3a00a483d5dec 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -214,6 +214,22 @@ impl TypingMode { | 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 { diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 727230af3083c..6493d81e024bd 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -143,8 +143,10 @@ where TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => structurally_relate_tys(relation, a, b), - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence(MayBeErased) => { + structurally_relate_tys(relation, a, b) + } } } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index b4e2509426979..6eb3a2490709c 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -1,5 +1,6 @@ pub mod inspect; +use std::fmt::Debug; use std::hash::Hash; use crate::lang_items::SolverTraitLangItem; @@ -11,7 +12,7 @@ use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContex use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; -use tracing::warn; +use tracing::debug; pub type CanonicalInput::Predicate> = ty::CanonicalQueryInput>; @@ -28,114 +29,246 @@ pub type QueryResult = Result, NoSolution>; #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct NoSolution; -#[derive_where(Copy, Clone, Debug, Hash, PartialEq, Eq; I: Interner)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] -pub enum AccessedState { - Known1([I::LocalDefId; 1]), - Known2([I::LocalDefId; 2]), - Known3([I::LocalDefId; 3]), - UnknownOrTooManyKnown, +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 struct AccessedOpaquesInfo { - #[type_visitable(ignore)] - #[type_foldable(identity)] - pub reason: &'static str, - pub defids_accessed: AccessedState, +pub enum RerunCondition { + Never, + + AnyOpaqueHasInferAsHidden, + /// Note: unconditionally reruns in postanalysis + OpaqueInStorage(SmallCopyList), + OpaqueInStorageOrAnyOpaqueHasInferAsHidden(SmallCopyList), + + Always, } -impl AccessedOpaquesInfo { - pub fn merge(&self, new_info: AccessedOpaquesInfo) -> Self { - let defid_accessed = match (self.defids_accessed, new_info.defids_accessed) { - (AccessedState::Known1([one]), AccessedState::Known1([two])) => { - AccessedState::Known2([one, two]) +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 } - (AccessedState::Known2([one, two]), AccessedState::Known1([three])) - | (AccessedState::Known1([one]), AccessedState::Known2([two, three])) => { - AccessedState::Known3([one, two, three]) + ( + 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) } - _ => AccessedState::UnknownOrTooManyKnown, }; + debug!("merging rerun state {self:?} + {other:?} => {merged:?}"); + merged + } - Self { - // choose the newest one - reason: new_info.reason, - // merging accessed states can only result in MultipleOrUnknown - defids_accessed: defid_accessed, + #[must_use] + fn should_bail(&self) -> bool { + match self { + Self::Always => true, + Self::Never + | Self::OpaqueInStorage(_) + | Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(_) + | Self::AnyOpaqueHasInferAsHidden => false, } } - pub fn opaques_accessed(&self) -> Option<&[I::LocalDefId]> { - match &self.defids_accessed { - AccessedState::Known1(d) => Some(d), - AccessedState::Known2(d) => Some(d), - AccessedState::Known3(d) => Some(d), - AccessedState::UnknownOrTooManyKnown => None, + /// 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(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] +#[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 AccessedOpaques { - Yes(AccessedOpaquesInfo), - No, +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::No + Self { reason: None, rerun: RerunCondition::Never } } } impl AccessedOpaques { - pub fn merge(&mut self, info: AccessedOpaquesInfo) { - warn!("merging {info:?}"); - *self = match self { - AccessedOpaques::Yes(existing_info) => AccessedOpaques::Yes(existing_info.merge(info)), - AccessedOpaques::No => AccessedOpaques::Yes(info), + 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 should_bail_instantly(&self) -> bool { - match self { - AccessedOpaques::Yes(AccessedOpaquesInfo { - reason: _, - defids_accessed: AccessedState::UnknownOrTooManyKnown, - }) => true, - AccessedOpaques::Yes(AccessedOpaquesInfo { - reason: _, - defids_accessed: - AccessedState::Known1(_) | AccessedState::Known2(_) | AccessedState::Known3(_), - }) => false, - AccessedOpaques::No => false, - } + pub fn might_rerun(&self) -> bool { + self.rerun.might_rerun() } - pub fn opaques_accessed(&self) -> Option<&[I::LocalDefId]> { - match self { - AccessedOpaques::Yes(i) => i.opaques_accessed(), - AccessedOpaques::No => Some(&[]), - } + #[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 bail_unrecoverable(&mut self, reason: &'static str) { - warn!("bail unrecoverable {reason:?}"); - self.merge(AccessedOpaquesInfo { - reason, - defids_accessed: AccessedState::UnknownOrTooManyKnown, + 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 bail_defid(&mut self, reason: &'static str, defid: I::LocalDefId) { - warn!("bail defid {defid:?} {reason:?}"); - self.merge(AccessedOpaquesInfo { reason, defids_accessed: AccessedState::Known1([defid]) }); + 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, + }); } } 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 + } +} From a728352843626dc3edcdb0b981184f2aade1bb98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 15 Apr 2026 11:23:19 +0200 Subject: [PATCH 7/7] Refactor `fetch_eligible_assoc_item` output --- compiler/rustc_middle/src/traits/solve.rs | 2 ++ .../rustc_next_trait_solver/src/delegate.rs | 7 ++--- .../src/solve/eval_ctxt/mod.rs | 6 +++-- .../src/solve/normalizes_to/mod.rs | 17 +++++++----- .../src/solve/delegate.rs | 27 +++++++++++++------ compiler/rustc_type_ir/src/solve/mod.rs | 12 ++++++++- 6 files changed, 48 insertions(+), 23 deletions(-) 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_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/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 2ef95a31f5d6c..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 @@ -7,7 +7,9 @@ use rustc_type_ir::data_structures::{HashMap, HashSet}; 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::{AccessedOpaques, OpaqueTypesJank, RerunCondition, SmallCopyList}; +use rustc_type_ir::solve::{ + AccessedOpaques, FetchEligibleAssocItemResponse, OpaqueTypesJank, RerunCondition, SmallCopyList, +}; use rustc_type_ir::{ self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, OpaqueTypeKey, PredicateKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, @@ -1318,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) } 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 b6731374f5506..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 @@ -5,7 +5,7 @@ mod opaque_types; use rustc_type_ir::fast_reject::DeepRejectCtxt; 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; @@ -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 @@ -299,8 +299,7 @@ where ty::TypingMode::Analysis { .. } | ty::TypingMode::Borrowck { .. } | ty::TypingMode::PostBorrowckAnalysis { .. } - | ty::TypingMode::PostAnalysis - | ty::TypingMode::ErasedNotCoherence(MayBeErased) => { + | ty::TypingMode::PostAnalysis => { ecx.structurally_instantiate_normalizes_to_term( goal, goal.predicate.alias, @@ -310,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) { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index e03e089f3040a..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, 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}; @@ -269,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. @@ -280,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_raw() { + match typing_mode { TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } @@ -290,14 +296,19 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< !poly_trait_ref.still_further_specializable() } TypingMode::ErasedNotCoherence(MayBeErased) => { - // TODO: make sure this can't be ignored by callers - return Ok(None); + 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_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 6eb3a2490709c..abc300b31630f 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -5,7 +5,9 @@ use std::hash::Hash; use crate::lang_items::SolverTraitLangItem; use crate::search_graph::PathKind; -use crate::{self as ty, Canonical, CanonicalVarValues, Interner, Upcast}; +use crate::{ + self as ty, Canonical, CanonicalVarValues, CantBeErased, Interner, TypingMode, Upcast, +}; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; @@ -480,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))]