From 3b0a2589181ed09204c2ac2628274adf082c5199 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 01/15] 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 | 112 ++++++++++++++++++ 3 files changed, 131 insertions(+), 2 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 b9e1758be0fd4..3226533992397 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::{MaybeInfo, OpaqueTypesJank}; +use rustc_type_ir::solve::{AccessedOpaques, AccessedOpaquesInfo, MaybeInfo, 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::{ @@ -134,6 +134,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, } @@ -335,6 +348,7 @@ where current_goal_kind: CurrentGoalKind::Misc, origin_span, tainted: Ok(()), + canonicalize_accessed_opaques: AccessedOpaques::default(), }; let result = f(&mut ecx); assert!( @@ -391,6 +405,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 893d36f45c99d..aaa46f1a6046e 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -8,6 +8,7 @@ use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash, StableH use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; +use tracing::warn; use crate::lang_items::SolverTraitLangItem; use crate::search_graph::PathKind; @@ -28,6 +29,117 @@ pub type QueryResult = Result, NoSolution>; #[cfg_attr(feature = "nightly", derive(StableHash))] 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 6173314a500f68354461517830e6603585765806 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 02/15] 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 +- .../rustc_type_ir/src/search_graph/mod.rs | 2 +- 7 files changed, 124 insertions(+), 87 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 5a73c99616815..cc6588c868117 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 3226533992397..cb46a0c14786b 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 @@ -366,13 +366,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()); @@ -419,7 +419,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) { @@ -487,7 +487,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), @@ -1532,13 +1532,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 b97d895bec15f..e822c47129c87 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -804,58 +804,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(MaybeInfo::AMBIGUOUS)?]) + } - 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(MaybeInfo::AMBIGUOUS)) - } + // `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 fba43b9cffbe2..effcc12e8c08f 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}; @@ -557,7 +559,7 @@ impl CollectAndApply for Result { impl search_graph::Cx for I { type Input = CanonicalInput; - type Result = QueryResult; + type Result = (QueryResult, AccessedOpaques); type AmbiguityInfo = Certainty; type DepNodeIndex = I::DepNodeIndex; diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index fbb521452ddd5..3a962b3832aa3 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -851,7 +851,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) } From a93f8c0579b99d7ff7cf02d2286704f272646b64 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 03/15] Introduce `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 | 14 +++---- .../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 | 19 +++++----- .../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, 105 insertions(+), 27 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 aa5697dfc9ced..8d618485c8eeb 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -384,6 +384,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 104a4e4d1ec4b..6174927df6ecc 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1084,6 +1084,7 @@ impl<'tcx> InferCtxt<'tcx> { TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => false, + TypingMode::ErasedNotCoherence => todo!(), } } @@ -1414,6 +1415,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 ad6dd5c3cad52..a3926a0dd23d6 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -284,6 +284,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 93da73e1e4505..4cf8aa091ca16 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1081,6 +1081,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 042b937a76a0c..69c1e676466f5 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -555,6 +555,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 7ecb7466d623c..cd65cd0cc1862 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 cc6588c868117..910c943dfec04 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); @@ -969,6 +970,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} + TypingMode::ErasedNotCoherence => todo!(), } let mut i = 0; @@ -1032,6 +1034,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 aa49997253dc2..b7fbdb70864bf 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -142,13 +142,13 @@ where let impl_polarity = cx.impl_polarity(impl_def_id); let certainty = match impl_polarity { ty::ImplPolarity::Negative => return Err(NoSolution), - ty::ImplPolarity::Reservation => match ecx.typing_mode() { - TypingMode::Coherence => Certainty::AMBIGUOUS, - TypingMode::Analysis { .. } - | TypingMode::Borrowck { .. } - | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => return Err(NoSolution), - }, + ty::ImplPolarity::Reservation => { + if ecx.typing_mode().is_coherence() { + Certainty::AMBIGUOUS + } else { + return Err(NoSolution); + } + } ty::ImplPolarity::Positive => Certainty::Yes, }; 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 cb46a0c14786b..03b3e34f43e65 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; @@ -486,7 +486,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, @@ -1556,7 +1557,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 6f05df707ad84..82189e53fcd43 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -368,6 +368,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 d93b7843b2251..35c0910ae6775 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 @@ -311,6 +311,7 @@ where return ecx .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } + ty::TypingMode::ErasedNotCoherence => todo!(), }; } Err(guar) => return error_response(ecx, guar), @@ -348,6 +349,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 6e21366955d82..a1e7246eee6cb 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 @@ -97,6 +97,7 @@ where TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => unreachable!(), + TypingMode::ErasedNotCoherence => todo!(), } } @@ -137,6 +138,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 e822c47129c87..01954bc744e53 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -74,15 +74,14 @@ 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, - // it's not a real impl. - (ty::ImplPolarity::Reservation, _) => match ecx.typing_mode() { - TypingMode::Coherence => Certainty::AMBIGUOUS, - TypingMode::Analysis { .. } - | TypingMode::Borrowck { .. } - | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => return Err(NoSolution), - }, + // In coherence mode, this is ambiguous. But outside of coherence, it's not a real impl. + (ty::ImplPolarity::Reservation, _) => { + if ecx.typing_mode().is_coherence() { + Certainty::AMBIGUOUS + } else { + return Err(NoSolution); + } + } // Impl matches polarity (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive) @@ -1397,6 +1396,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} + TypingMode::ErasedNotCoherence => todo!(), } if candidates @@ -1575,6 +1575,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 c61702a10f6e0..382158901e29c 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -297,6 +297,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 d8b9c400b4d7d..0c6e091235b3d 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -284,6 +284,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 b4b54cd16bd5f..f34500c5dd2ea 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -179,6 +179,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 2946d8d1d487d..5f866af0f7748 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -145,6 +145,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) @@ -435,6 +436,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 5423e394119c4..91713c2108457 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -969,6 +969,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 bd9fee90d8903..d373cd772b57c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -253,6 +253,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 3a8e2429069f5..f980f7ee00a23 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1485,6 +1485,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Ok(()), + TypingMode::ErasedNotCoherence => todo!(), } debug!("is_knowable()"); @@ -1544,6 +1545,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!(), } } @@ -2912,6 +2914,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 c6e8ae49e4c9d..0ac2899165f2e 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -161,6 +161,7 @@ fn resolve_associated_item<'tcx>( | ty::TypingMode::Borrowck { .. } | ty::TypingMode::PostBorrowckAnalysis { .. } => false, ty::TypingMode::PostAnalysis => !trait_ref.still_further_specializable(), + ty::TypingMode::ErasedNotCoherence => todo!(), } }; if !eligible { diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 99588cb20aed0..5d4326d22eb9e 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -108,6 +108,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`) + /// + /// We replace all of them with this `TypingMode` in the first attempt at canonicalization. + /// If, during that attempt, we try to access information about opaques or generators + /// we bail out, setting a field on `EvalCtxt` that indicates the canonicalization must be + /// rerun in the original typing mode. + /// + /// `TypingMode::Coherence` is not replaced by this and is always kept as-is. + ErasedNotCoherence, } /// We want to highly discourage using equality checks on typing modes. @@ -145,12 +159,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, } @@ -171,7 +187,8 @@ impl TypingMode { TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => false, + | TypingMode::PostAnalysis + | TypingMode::ErasedNotCoherence => false, } } @@ -416,5 +433,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 630f0d2bbbdb18bce35169a135a0654a1e6a9bcc 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 04/15] 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 | 10 ++- .../src/infer/canonical/canonicalizer.rs | 2 +- compiler/rustc_infer/src/infer/context.rs | 4 +- compiler/rustc_infer/src/infer/mod.rs | 37 ++++++-- .../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 | 6 +- .../rustc_mir_transform/src/elaborate_drop.rs | 4 +- .../src/canonical/mod.rs | 10 +-- .../src/solve/assembly/mod.rs | 12 +-- .../src/solve/effect_goals.rs | 2 +- .../src/solve/eval_ctxt/mod.rs | 6 +- .../rustc_next_trait_solver/src/solve/mod.rs | 4 +- .../src/solve/normalizes_to/mod.rs | 7 +- .../src/solve/normalizes_to/opaque_types.rs | 6 +- .../src/solve/search_graph.rs | 8 +- .../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 | 6 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 89 ++++++++++++++++--- compiler/rustc_type_ir/src/relate/combine.rs | 4 +- 33 files changed, 206 insertions(+), 112 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 8d618485c8eeb..7f0d6c8e87343 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; @@ -384,7 +384,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..3858a3b7047d3 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) => unreachable!(), } } 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..06410df7e1f16 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) => unreachable!(), } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 63bd49f080309..5fea7454e57ea 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -17,7 +17,9 @@ 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, Flags, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; +use rustc_middle::ty::{ + self, CantBeErased, Const, Flags, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, +}; use rustc_session::Session; use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span}; use rustc_trait_selection::error_reporting::TypeErrCtxt; @@ -161,6 +163,12 @@ 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 a575ba79df45c..e0adcfc06a0ed 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -72,7 +72,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 0dc1f5e5eec58..8107d91ba4109 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -26,8 +26,8 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.disable_trait_solver_fast_paths() } - fn typing_mode(&self) -> ty::TypingMode<'tcx> { - self.typing_mode() + fn typing_mode_raw(&self) -> ty::TypingMode<'tcx> { + self.typing_mode_raw() } fn universe(&self) -> ty::UniverseIndex { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 6174927df6ecc..2ccdd317f05c9 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -34,6 +34,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; @@ -640,14 +641,34 @@ 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 disable_trait_solver_fast_paths(&self) -> bool { - self.tcx.disable_trait_solver_fast_paths() + pub fn typing_mode_raw(&self) -> TypingMode<'tcx> { + self.typing_mode } #[inline(always)] - pub fn typing_mode(&self) -> TypingMode<'tcx> { - self.typing_mode + pub fn disable_trait_solver_fast_paths(&self) -> bool { + self.tcx.disable_trait_solver_fast_paths() } /// Returns the origin of the type variable identified by `vid`. @@ -1071,7 +1092,7 @@ 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().assert_not_erased() { TypingMode::Analysis { defining_opaque_types_and_generators: defining_opaque_types, } @@ -1084,7 +1105,7 @@ impl<'tcx> InferCtxt<'tcx> { TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => false, - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } @@ -1403,7 +1424,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 @@ -1415,7 +1436,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 a3926a0dd23d6..6b051f42421b0 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 @@ -284,7 +286,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 5ec56b3e9dc4a..3d2d617cfdb29 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -604,7 +604,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); @@ -736,7 +736,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 4cf8aa091ca16..bf35668950b50 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1063,7 +1063,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> { @@ -1081,7 +1081,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 5360a123a4199..112ad65e19bff 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -17,7 +17,9 @@ 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::{ + self as ir, BoundVar, CollectAndApply, MayBeErased, TypeVisitableExt, elaborate, +}; use tracing::instrument; use ty::util::IntTypeExt; @@ -41,7 +43,7 @@ pub type FnSigKind<'tcx> = ir::FnSigKind>; pub type Binder<'tcx, T> = ir::Binder, T>; pub type EarlyBinder<'tcx, T> = ir::EarlyBinder, T>; pub type Unnormalized<'tcx, T> = ir::Unnormalized, 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 69c1e676466f5..48006bace447c 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, Unnormalized}; +use rustc_middle::ty::{self, GenericArg, GenericArgsRef, MayBeErased, Ty, TyCtxt, Unnormalized}; use rustc_middle::{bug, span_bug, traits}; use rustc_span::{DUMMY_SP, Spanned, dummy_spanned}; use tracing::{debug, instrument}; @@ -555,7 +555,7 @@ where | ty::TypingMode::PostBorrowckAnalysis { .. } => { bug!() } - ty::TypingMode::ErasedNotCoherence => todo!(), + ty::TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), } 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 cd65cd0cc1862..8de84247c8ca9 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, TypingMode, TypingModeEqWrapper, + MayBeErased, TypeFoldable, TypingMode, TypingModeEqWrapper, }; use tracing::instrument; @@ -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 910c943dfec04..b5e5109c6b79a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -11,9 +11,9 @@ use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::search_graph::CandidateHeadUsages; use rustc_type_ir::solve::{AliasBoundKind, MaybeInfo, SizedTraitKind, StalledOnCoroutines}; use rustc_type_ir::{ - self as ty, AliasTy, Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Unnormalized, - Upcast, elaborate, + self as ty, AliasTy, Interner, MayBeErased, TypeFlags, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + TypingMode, Unnormalized, Upcast, elaborate, }; use tracing::{debug, instrument}; @@ -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); @@ -970,7 +970,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } let mut i = 0; @@ -1034,7 +1034,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 b7fbdb70864bf..508287a4dfee6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -6,7 +6,7 @@ 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, Unnormalized, elaborate}; +use rustc_type_ir::{self as ty, Interner, Unnormalized, elaborate}; use tracing::instrument; use super::assembly::{Candidate, structural_traits}; 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 03b3e34f43e65..2813f23d489f9 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::{AccessedOpaques, AccessedOpaquesInfo, MaybeInfo, OpaqueTypesJank}; +use rustc_type_ir::solve::{AccessedOpaques, MaybeInfo, OpaqueTypesJank}; use rustc_type_ir::{ self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; -use tracing::{Level, debug, instrument, trace, warn}; +use tracing::{debug, instrument, trace, warn}; use super::has_only_region_constraints; use crate::canonical::{ @@ -272,7 +272,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 82189e53fcd43..b50e686606e04 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -24,7 +24,7 @@ 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::{self as ty, Interner, MayBeErased, TyVid, TypingMode}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -368,7 +368,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 35c0910ae6775..d6383cb48fa95 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 @@ -7,7 +7,8 @@ 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::{ - self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Unnormalized, Upcast as _, + self as ty, FieldInfo, Interner, MayBeErased, NormalizesTo, PredicateKind, Unnormalized, + Upcast as _, }; use tracing::instrument; @@ -311,7 +312,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), @@ -349,7 +350,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 a1e7246eee6cb..98c0c7eadb5b2 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 @@ -3,7 +3,7 @@ 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::{self as ty, Interner, MayBeErased, TypingMode, fold_regions}; use crate::delegate::SolverDelegate; use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; @@ -97,7 +97,7 @@ where TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => unreachable!(), - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } @@ -138,7 +138,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..9e09d3cd019a0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -3,10 +3,8 @@ use std::marker::PhantomData; use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::search_graph::{self, PathKind}; -use rustc_type_ir::solve::{ - AccessedOpaques, AccessedOpaquesInfo, CanonicalInput, Certainty, NoSolution, QueryResult, -}; -use rustc_type_ir::{Interner, TypingMode}; +use rustc_type_ir::solve::{AccessedOpaques, CanonicalInput, Certainty, NoSolution, QueryResult}; +use rustc_type_ir::{Interner, MayBeErased, TypingMode}; use crate::canonical::response_no_constraints_raw; use crate::delegate::SolverDelegate; @@ -72,7 +70,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 01954bc744e53..5934b01f96e13 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -9,8 +9,8 @@ use rustc_type_ir::solve::{ SizedTraitKind, }; use rustc_type_ir::{ - self as ty, FieldInfo, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef, - TypeVisitableExt as _, TypingMode, Unnormalized, Upcast as _, elaborate, + self as ty, FieldInfo, Interner, MayBeErased, Movability, PredicatePolarity, TraitPredicate, + TraitRef, TypeVisitableExt as _, TypingMode, Unnormalized, Upcast as _, elaborate, }; use tracing::{debug, instrument, trace}; @@ -1396,7 +1396,7 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} - TypingMode::ErasedNotCoherence => todo!(), + TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } if candidates @@ -1575,7 +1575,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 382158901e29c..4bea605211785 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -13,7 +13,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}; @@ -288,7 +288,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 { .. } @@ -297,7 +297,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 0c6e091235b3d..ed345d98b5392 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -276,7 +276,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 } @@ -284,7 +284,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 0f36edcdd830e..a4691c6aa4386 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 f34500c5dd2ea..ce8f05637f9a6 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -171,7 +171,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 } @@ -179,7 +179,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() { @@ -879,7 +878,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) { @@ -933,7 +932,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 5f866af0f7748..6e526e8dc548c 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, Unnormalized, + self, AliasTerm, MayBeErased, Term, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitable, TypeVisitableExt, TypingMode, Unnormalized, }; use tracing::{debug, instrument}; @@ -138,14 +138,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) => unreachable!(), } value.has_type_flags(flags) @@ -411,7 +411,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 { .. } @@ -436,7 +436,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 91713c2108457..e2458a2392f5f 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -950,7 +950,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // and the obligation is monomorphic, otherwise passes such as // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. - match selcx.infcx.typing_mode() { + match selcx.typing_mode() { TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } @@ -969,7 +969,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 d373cd772b57c..e8c5d10c78ce3 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -215,7 +215,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { let res = match data.kind { ty::Opaque { def_id } => { // Only normalize `impl Trait` outside of type inference, usually in codegen. - match self.infcx.typing_mode() { + match self.infcx.typing_mode_raw().assert_not_erased() { TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } @@ -253,7 +253,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 f980f7ee00a23..fc208ceaf609d 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, Unnormalized, Upcast, - elaborate, may_use_unstable_feature, + self, CandidatePreferenceMode, CantBeErased, DeepRejectCtxt, GenericArgsRef, + PolyProjectionPredicate, SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, + TypingMode, Unnormalized, Upcast, elaborate, may_use_unstable_feature, }; use rustc_next_trait_solver::solve::AliasBoundKind; use rustc_span::Symbol; @@ -199,6 +199,10 @@ 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() } @@ -1022,7 +1026,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()) { @@ -1479,13 +1483,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()"); @@ -1513,7 +1516,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" @@ -1545,7 +1548,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!(), } } @@ -2567,7 +2569,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(()); @@ -2906,7 +2908,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)) } @@ -2914,7 +2916,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 0ac2899165f2e..178533fb231a0 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -6,8 +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, - Unnormalized, + self, ClosureKind, GenericArgsRef, Instance, MayBeErased, PseudoCanonicalInput, TyCtxt, + TypeVisitableExt, Unnormalized, }; use rustc_span::sym; use rustc_trait_selection::traits; @@ -161,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) => unreachable!(), } }; if !eligible { diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 5d4326d22eb9e..c9533623591a1 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; @@ -11,6 +12,26 @@ use crate::relate::combine::PredicateEmittingRelation; use crate::solve::VisibleForLeakCheck; use crate::{self as ty, Interner, TyVid}; +mod private { + pub trait Sealed {} + + impl Sealed for super::CantBeErased {} + impl Sealed for super::MayBeErased {} +} +pub trait TypingModeErasedStatus: private::Sealed + Clone + Copy + Hash + fmt::Debug {} + +#[derive(Clone, Copy, Hash, Debug)] +pub enum CantBeErased {} +#[derive(Clone, Copy, Hash, Debug)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, 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,8 +63,7 @@ use crate::{self as ty, Interner, TyVid}; feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) )] -#[cfg_attr(feature = "nightly", 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 @@ -121,7 +141,7 @@ pub enum TypingMode { /// rerun 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. @@ -134,7 +154,7 @@ pub enum TypingMode { feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) )] -pub struct TypingModeEqWrapper(pub TypingMode); +pub struct TypingModeEqWrapper(pub TypingMode); impl Hash for TypingModeEqWrapper { fn hash(&self, state: &mut H) { @@ -159,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, } @@ -175,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. @@ -188,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 outside the next trait solver! + /// That means either not in the trait solver, or in code that is old-solver only. + /// + /// See the comment on `InferCtxt::typing_mode_raw` + pub fn assert_not_erased(self) -> TypingMode { + 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() } @@ -234,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; @@ -249,7 +317,7 @@ pub trait InferCtxtLike: Sized { fn disable_trait_solver_fast_paths(&self) -> bool; - fn typing_mode(&self) -> TypingMode; + fn typing_mode_raw(&self) -> TypingMode; fn universe(&self) -> ty::UniverseIndex; fn create_next_universe(&self) -> ty::UniverseIndex; @@ -425,7 +493,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().assert_not_erased() { TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } @@ -433,6 +501,5 @@ 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 3f7b8f9e6c848..7e4b95580a8e4 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -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().assert_not_erased() { // During coherence, opaque types should be treated as *possibly* // equal to any other type. This is an // extremely heavy hammer, but can be relaxed in a forwards-compatible @@ -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 a299302d5c5d4e42e5def4d2085feefc92b68377 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 05/15] Add logging in various places --- .../src/solve/normalizes_to/opaque_types.rs | 1 + compiler/rustc_type_ir/src/infer_ctxt.rs | 2 +- compiler/rustc_type_ir/src/search_graph/global_cache.rs | 2 +- compiler/rustc_type_ir/src/solve/mod.rs | 6 +++--- 4 files changed, 6 insertions(+), 5 deletions(-) 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 98c0c7eadb5b2..babcd79d83348 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_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index c9533623591a1..f4c61da45ce24 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -25,7 +25,7 @@ pub enum CantBeErased {} #[derive(Clone, Copy, Hash, Debug)] #[cfg_attr( feature = "nightly", - derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) + derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) )] pub struct MayBeErased; 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 3e0f8004182ad..fcbc8b281d132 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -68,7 +68,7 @@ impl GlobalCache { let prev = entry.success.replace(Success { required_depth, nested_goals, result }); if let Some(prev) = &prev { cx.assert_evaluation_is_concurrent(); - assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result); + assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result, "{input:?}"); } } } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index aaa46f1a6046e..4dc7b9ccf366f 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -31,7 +31,7 @@ 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))] +#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] pub enum AccessedState { Known1([I::LocalDefId; 1]), Known2([I::LocalDefId; 2]), @@ -41,7 +41,7 @@ pub enum AccessedState { #[derive_where(Copy, Clone, Debug, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] pub struct AccessedOpaquesInfo { #[type_visitable(ignore)] #[type_foldable(identity)] @@ -82,7 +82,7 @@ impl AccessedOpaquesInfo { #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] pub enum AccessedOpaques { Yes(AccessedOpaquesInfo), No, From 7ff71be015fb954489cfb626b857881235f1ae96 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 06/15] 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 | 3 +- compiler/rustc_middle/src/ty/mod.rs | 16 +- .../src/canonical/mod.rs | 6 +- .../src/solve/assembly/mod.rs | 43 +-- .../src/solve/eval_ctxt/mod.rs | 262 ++++++++++++++--- .../src/solve/eval_ctxt/probe.rs | 11 +- .../rustc_next_trait_solver/src/solve/mod.rs | 3 +- .../src/solve/normalizes_to/mod.rs | 26 +- .../src/solve/normalizes_to/opaque_types.rs | 29 +- .../src/solve/search_graph.rs | 8 +- .../src/solve/trait_goals.rs | 24 +- .../src/solve/delegate.rs | 13 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 16 + compiler/rustc_type_ir/src/relate/combine.rs | 1 - compiler/rustc_type_ir/src/solve/mod.rs | 277 +++++++++++++----- .../next-solver/canonical/erased-opaques.rs | 31 ++ 17 files changed, 591 insertions(+), 179 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 9730e08c90c37..d4394c10d485a 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 2ccdd317f05c9..b802c3b2e8d40 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1105,7 +1105,6 @@ impl<'tcx> InferCtxt<'tcx> { TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => false, - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } @@ -1436,7 +1435,7 @@ impl<'tcx> InferCtxt<'tcx> { mode @ (ty::TypingMode::Coherence | ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => mode, - ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + ty::TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), }; 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 bf35668950b50..f68526724135c 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1075,20 +1075,20 @@ impl<'tcx> TypingEnv<'tcx> { /// opaque types in the `param_env`. pub fn with_post_analysis_normalized(self, tcx: TyCtxt<'tcx>) -> TypingEnv<'tcx> { let TypingEnv { typing_mode, param_env } = self; - match typing_mode.0 { - TypingMode::Coherence - | TypingMode::Analysis { .. } - | TypingMode::Borrowck { .. } - | TypingMode::PostBorrowckAnalysis { .. } => {} - TypingMode::PostAnalysis => return self, - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), - } // No need to reveal opaques with the new solver enabled, // since we have lazy norm. let param_env = if tcx.next_trait_solver_globally() { param_env } else { + match typing_mode.0.assert_not_erased() { + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } => {} + TypingMode::PostAnalysis => return self, + } + ParamEnv::new(tcx.reveal_opaque_types_in_bounds(param_env.caller_bounds())) }; TypingEnv { typing_mode: TypingModeEqWrapper(TypingMode::PostAnalysis), param_env } diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 8de84247c8ca9..30d9bc55ea11a 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 b5e5109c6b79a..b81c357a8061e 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( @@ -964,13 +964,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; @@ -1020,12 +1015,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() { @@ -1034,12 +1030,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(MaybeInfo::AMBIGUOUS)); - return; + return Ok(()); } for &alias_ty in &opaque_types { @@ -1066,7 +1065,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 @@ -1139,6 +1138,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 @@ -1195,7 +1196,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 @@ -1224,7 +1225,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/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 2813f23d489f9..99a37b3f1ea4e 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::{AccessedOpaques, MaybeInfo, OpaqueTypesJank}; +use rustc_type_ir::solve::{AccessedOpaques, MaybeInfo, 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, MayBeErased, + OpaqueTypeKey, PredicateKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; -use tracing::{debug, instrument, trace, warn}; +use tracing::{Level, debug, instrument, trace, warn}; use super::has_only_region_constraints; use crate::canonical::{ @@ -74,6 +74,13 @@ impl CurrentGoalKind { } } +#[derive(Debug)] +enum RerunDecision { + Yes, + No, + EagerlyPropagateToParent, +} + pub struct EvalCtxt<'a, D, I = ::Interner> where D: SolverDelegate, @@ -134,18 +141,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, } @@ -348,13 +345,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 } @@ -393,6 +391,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, @@ -405,13 +407,17 @@ 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); ecx.inspect.probe_final_state(ecx.delegate, ecx.max_input_universe); proof_tree_builder.finish_evaluation_step(ecx.inspect); + if canonical_input.typing_mode.0.is_erased_not_coherence() { + debug_assert!(delegate.clone_opaque_types_lookup_table().is_empty()); + } + // When creating a query response we clone the opaque type constraints // instead of taking them. This would cause an ICE here, since we have // assertions against dropping an `InferCtxt` without taking opaques. @@ -419,7 +425,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) { @@ -485,20 +491,80 @@ 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)); + 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; + } - 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, + 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::EagerlyPropagateToParent => { + 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 }; @@ -601,6 +667,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::EagerlyPropagateToParent + } + // ============================= + // In coherence, we never switch to erased mode, so we will never register anything + // in the rerun state, so we should've taken the first branch of this match + (_, TypingMode::Coherence) => unreachable!(), + // ============================= + (RerunCondition::Always, _) => RerunDecision::Yes, + // ============================= + (RerunCondition::OpaqueInStorage(..), TypingMode::PostAnalysis) => RerunDecision::Yes, + ( + RerunCondition::OpaqueInStorage(defids), + TypingMode::PostBorrowckAnalysis { defined_opaque_types: opaques } + | TypingMode::Analysis { defining_opaque_types_and_generators: opaques } + | TypingMode::Borrowck { defining_opaque_types: opaques }, + ) => opaque_in_storage(opaques, defids), + // ============================= + (RerunCondition::AnyOpaqueHasInferAsHidden, TypingMode::Analysis { .. }) => { + any_opaque_has_infer_as_hidden() + } + ( + RerunCondition::AnyOpaqueHasInferAsHidden, + TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis + | TypingMode::Borrowck { .. }, + ) => RerunDecision::No, + // ============================= + ( + RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(_), + TypingMode::PostAnalysis, + ) => RerunDecision::No, + ( + RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(defids), + TypingMode::Analysis { defining_opaque_types_and_generators: opaques }, + ) => { + if let RerunDecision::Yes = any_opaque_has_infer_as_hidden() { + RerunDecision::Yes + } else if let RerunDecision::Yes = opaque_in_storage(opaques, defids) { + RerunDecision::Yes + } else { + RerunDecision::No + } + } + ( + RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(defids), + TypingMode::PostBorrowckAnalysis { defined_opaque_types: opaques } + | TypingMode::Borrowck { defining_opaque_types: opaques }, + ) => opaque_in_storage(opaques, defids), + }; + + debug!( + "checking whether to rerun {rerun:?} in outer typing mode {original_typing_mode:?} and opaques {parent_opaque_types:?}: {res:?}" + ); + + res + } + pub(super) fn compute_goal(&mut self, goal: Goal) -> QueryResult { let Goal { param_env, predicate } = goal; let kind = predicate.kind(); @@ -1209,10 +1385,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) } @@ -1234,10 +1415,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) } @@ -1413,6 +1599,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 } } } @@ -1541,7 +1731,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)) } @@ -1557,6 +1747,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 b50e686606e04..678cf2f9abe6a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -368,7 +368,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 this variant + TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index d6383cb48fa95..47126cd6beec4 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 @@ -304,7 +304,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, @@ -312,7 +313,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), @@ -325,7 +325,7 @@ where // delay a bug because we can have trivially false where clauses, so we // treat it as rigid. if cx.impl_self_is_guaranteed_unsized(impl_def_id) { - match ecx.typing_mode() { + if ecx.typing_mode().is_coherence() { // Trying to normalize such associated items is always ambiguous // during coherence to avoid cyclic reasoning. See the example in // tests/ui/traits/trivial-unsized-projection-in-coherence.rs. @@ -336,21 +336,11 @@ where // would be relevant if any of the nested goals refer to the `term`. // This is not the case here and we only prefer adding an ambiguous // nested goal for consistency. - ty::TypingMode::Coherence => { - ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); - return then(ecx, Certainty::Yes); - } - ty::TypingMode::Analysis { .. } - | ty::TypingMode::Borrowck { .. } - | ty::TypingMode::PostBorrowckAnalysis { .. } - | ty::TypingMode::PostAnalysis => { - ecx.structurally_instantiate_normalizes_to_term( - goal, - goal.predicate.alias, - ); - return then(ecx, Certainty::Yes); - } - ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), + ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); + return then(ecx, Certainty::Yes); + } else { + ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + return then(ecx, Certainty::Yes); } } else { return error_response(ecx, cx.delay_bug("missing item")); 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 babcd79d83348..61bc0cdcda100 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 @@ -2,7 +2,7 @@ //! behaves differently depending on the current `TypingMode`. use rustc_type_ir::inherent::*; -use rustc_type_ir::solve::GoalSource; +use rustc_type_ir::solve::{GoalSource, NoSolution}; use rustc_type_ir::{self as ty, Interner, MayBeErased, TypingMode, fold_regions}; use crate::delegate::SolverDelegate; @@ -139,7 +139,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 defining scope, and otherwise treat it as rigid. + // However, in `ErasedNotcoherence` we *always* treat it as rigid. + // This is the same as other modes if def_id is None, but wrong if we do have a DefId. + // So, if we have one, we register in the EvalCtxt that we may need that defid. + // We might then decide to rerun in the correct typing mode. + if let Some(def_id) = def_id { + self.opaque_accesses + .rerun_if_opaque_in_opaque_type_storage("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 9e09d3cd019a0..45a32e51ed3f5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -69,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()) + } }, } } @@ -156,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 5934b01f96e13..807b313805e8e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -12,7 +12,7 @@ use rustc_type_ir::{ self as ty, FieldInfo, Interner, MayBeErased, Movability, PredicatePolarity, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode, Unnormalized, Upcast as _, elaborate, }; -use tracing::{debug, instrument, trace}; +use tracing::{debug, instrument, trace, warn}; use crate::delegate::SolverDelegate; use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; @@ -236,6 +236,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 @@ -1390,13 +1395,8 @@ where /// normalization. This means the only case where this special-case results in exploitable /// unsoundness should be lifetime dependent user-written impls. pub(super) fn unsound_prefer_builtin_dyn_impl(&mut self, candidates: &mut Vec>) { - 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; } if candidates @@ -1550,7 +1550,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) @@ -1571,11 +1571,15 @@ where })); } } + 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 4bea605211785..acf22321e70e8 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -72,7 +72,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 { @@ -297,7 +303,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 f4c61da45ce24..093969704e54e 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 7e4b95580a8e4..77dfed9c3313a 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -144,7 +144,6 @@ where | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => structurally_relate_tys(relation, a, b), - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 4dc7b9ccf366f..c5be0d09b9750 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 derive_where::derive_where; @@ -8,7 +9,7 @@ use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash, StableH use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; -use tracing::warn; +use tracing::debug; use crate::lang_items::SolverTraitLangItem; use crate::search_graph::PathKind; @@ -29,114 +30,254 @@ pub type QueryResult = Result, NoSolution>; #[cfg_attr(feature = "nightly", derive(StableHash))] 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(StableHash_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::ErasedNotCoherence`], +/// and should try to return as soon as possible to waste as little time as possible. +/// A rerun will be attempted in the original typing mode. +/// +/// - Rerun goal is implemented with `should_rerun_after_erased_canonicalization`, on the `EvalCtxt`. +/// +/// Some variant names contain an `Or` here. They rerun when any of the two conditions applies #[derive_where(Copy, Clone, Debug, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] -pub struct AccessedOpaquesInfo { - #[type_visitable(ignore)] - #[type_foldable(identity)] - pub reason: &'static str, - pub defids_accessed: AccessedState, +pub enum RerunCondition { + Never, + + /// Note that this only reruns according to the condition *if* we are in [`TypingMode::Analysis`]. + AnyOpaqueHasInferAsHidden, + /// Note: unconditionally reruns in postanalysis + OpaqueInStorage(SmallCopyList), + + /// Merges [`Self::AnyOpaqueHasInferAsHidden`] and [`Self::OpaqueInStorage`]. + /// Note that just like the unmerged [`Self::OpaqueInStorage`], that part of the + /// condition only matters in [`TypingMode::Analysis`] + OpaqueInStorageOrAnyOpaqueHasInferAsHidden(SmallCopyList), + + 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)`). + /// + /// - "self" here means the current state, i.e. the state of the current column + /// - square brackets represents that this is a list of things. Even if the state doesn't + /// change, we might grow the list to effectively end up in a different state anyway + /// - `[o. in s.]` abbreviates "opaque in storage" + /// + /// + /// | | never | always | [opaque in storage] | opaque has infer as hidden | [o. in s.] or i. as hidden | + /// | ------------------------------- | ------ | ------ | ------------------- | -------------------------- | -------------------------- | + /// | never | self | self | self | self | self | + /// | always | | always | always | always | always | + /// | [opaque in storage] | | | concat self | [o. in s.] or i. as hidden | concat to self | + /// | opaque has infer as hidden type | | | | self | to self | + /// + fn merge(self, other: Self) -> Self { + let merged = match (self, other) { + (Self::Never, other) | (other, Self::Never) => other, + (Self::Always, _) | (_, Self::Always) => Self::Always, + + (Self::OpaqueInStorage(a), Self::OpaqueInStorage(b)) => { + a.union(b).map(Self::OpaqueInStorage).unwrap_or(Self::Always) } - (AccessedState::Known2([one, two]), AccessedState::Known1([three])) - | (AccessedState::Known1([one]), AccessedState::Known2([two, three])) => { - AccessedState::Known3([one, two, three]) + (Self::AnyOpaqueHasInferAsHidden, Self::AnyOpaqueHasInferAsHidden) => { + Self::AnyOpaqueHasInferAsHidden + } + ( + Self::AnyOpaqueHasInferAsHidden, + Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(a), + ) + | ( + Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(a), + Self::AnyOpaqueHasInferAsHidden, + ) => Self::OpaqueInStorage(a), + + ( + Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(a), + Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(b), + ) => a + .union(b) + .map(Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden) + .unwrap_or(Self::Always), + + (Self::OpaqueInStorage(a), Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(b)) + | (Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(b), Self::OpaqueInStorage(a)) => a + .union(b) + .map(Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden) + .unwrap_or(Self::Always), + + (Self::OpaqueInStorage(a), Self::AnyOpaqueHasInferAsHidden) + | (Self::AnyOpaqueHasInferAsHidden, Self::OpaqueInStorage(a)) => { + Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(a) } - _ => 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(StableHash_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 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..c76e1b288e497 --- /dev/null +++ b/tests/ui/traits/next-solver/canonical/erased-opaques.rs @@ -0,0 +1,31 @@ +//@ compile-flags: -Znext-solver +//@ build-pass +//@ edition: 2021 +//@ compile-flags: -C debuginfo=1 --crate-type=lib +// This test explores a funny case when handling opaque types: +// We involve a couple of iterator types from the standard library, which +// importantly is another crate from this test's perspective. +// When we check whether we can normalize something, if the DefId is from +// another crate we refuse to. +// +// However, in `TypingMode::PostAnalysis`, all opaques become normalizable, +// and so also these iteraators from `std`. In the next solver, when we +// canonicalize, we always do a first attempt in `TypingMode::ErasedNotCoherence`, +// deleting all opaque types in scope. +// +// If we then end up accessing opaques, we rerun the canonicalization in the +// original typing modes *with* opaques. This relies on us properly tracking +// whether opaques were used. And, even if opaque types have a non-local defid, +// we *can* end up being able to normalize said opaque if the original `TypingMode` +// was `PostAnalysis`. +// +// This test makes sure that we indeed track these opaque type accesses properly. + +pub(crate) struct Foo; + +impl From<()> for Foo { + fn from(_: ()) -> Foo { + String::new().extend('a'.to_uppercase()); + Foo + } +} From 9fda7e0c9792b06cbbf33532ea62c09f25bbb3ec 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 07/15] Refactor `fetch_eligible_assoc_item` output --- compiler/rustc_middle/src/traits/solve.rs | 2 ++ .../rustc_next_trait_solver/src/delegate.rs | 9 +++---- .../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 | 24 ++++++++++++----- 6 files changed, 57 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index e1946598ab0d8..f61088d2c5bf9 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 541276f5925da..429206a93dfee 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -1,6 +1,8 @@ use std::ops::Deref; -use rustc_type_ir::solve::{Certainty, Goal, NoSolution, VisibleForLeakCheck}; +use rustc_type_ir::solve::{ + Certainty, FetchEligibleAssocItemResponse, Goal, NoSolution, VisibleForLeakCheck, +}; use rustc_type_ir::{self as ty, InferCtxtLike, Interner, TypeFoldable}; pub trait SolverDelegate: Deref + Sized { @@ -79,10 +81,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 99a37b3f1ea4e..299b075a64523 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,7 +8,9 @@ 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::{AccessedOpaques, MaybeInfo, OpaqueTypesJank, RerunCondition, SmallCopyList}; +use rustc_type_ir::solve::{ + AccessedOpaques, MaybeInfo, FetchEligibleAssocItemResponse, OpaqueTypesJank, RerunCondition, SmallCopyList, +}; use rustc_type_ir::{ self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, MayBeErased, OpaqueTypeKey, PredicateKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, @@ -1350,7 +1352,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 47126cd6beec4..6ff1c5153e575 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 @@ -6,6 +6,8 @@ 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::FetchEligibleAssocItemResponse; use rustc_type_ir::{ self as ty, FieldInfo, Interner, MayBeErased, NormalizesTo, PredicateKind, Unnormalized, Upcast as _, @@ -281,9 +283,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 @@ -304,8 +306,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, @@ -315,7 +316,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 acf22321e70e8..b0bcf82e87c06 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -9,13 +9,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}; @@ -283,8 +283,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. @@ -294,7 +300,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 { .. } @@ -304,14 +310,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 c5be0d09b9750..5a3722e3beaca 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -13,7 +13,9 @@ use tracing::debug; use crate::lang_items::SolverTraitLangItem; use crate::search_graph::PathKind; -use crate::{self as ty, Canonical, CanonicalVarValues, Interner, Upcast}; +use crate::{ + self as ty, Canonical, CanonicalVarValues, CantBeErased, Interner, TypingMode, Upcast, +}; pub type CanonicalInput::Predicate> = ty::CanonicalQueryInput>; @@ -31,8 +33,8 @@ pub type QueryResult = Result, NoSolution>; pub struct NoSolution; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(StableHash_NoContext, GenericTypeVisitable))] pub enum SmallCopyList { Empty, One([T; 1]), @@ -108,8 +110,8 @@ impl AsRef<[T]> for SmallCopyList { /// /// 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(StableHash_NoContext))] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(StableHash_NoContext, GenericTypeVisitable))] pub enum RerunCondition { Never, @@ -212,8 +214,8 @@ impl RerunCondition { } #[derive_where(Copy, Clone, Debug, Hash, PartialEq, Eq; I: Interner)] -#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(StableHash_NoContext, GenericTypeVisitable))] pub struct AccessedOpaques { #[type_visitable(ignore)] #[type_foldable(identity)] @@ -486,6 +488,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(StableHash_NoContext))] From e8b74eec74769328da193d0dd9ac7f3838724376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 28 Apr 2026 09:53:07 +0200 Subject: [PATCH 08/15] consistently return OpaquesAccessed from probes to signal why they error --- .../src/solve/assembly/mod.rs | 15 +++++++---- .../src/solve/eval_ctxt/probe.rs | 18 ++++++------- .../src/solve/normalizes_to/mod.rs | 18 ++++++++----- .../src/solve/trait_goals.rs | 2 +- compiler/rustc_type_ir/src/solve/mod.rs | 27 +++++++++++++++++++ 5 files changed, 58 insertions(+), 22 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 b81c357a8061e..d1233773e00a9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -9,7 +9,7 @@ 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, MaybeInfo, SizedTraitKind, StalledOnCoroutines}; +use rustc_type_ir::solve::{AliasBoundKind, MaybeInfo, NoSolutionOrOpaquesAccessed, SizedTraitKind, StalledOnCoroutines}; use rustc_type_ir::{ self as ty, AliasTy, Interner, MayBeErased, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -161,7 +161,7 @@ where }) }); - match result { + match result.map_err(Into::into) { Ok(result) => Ok(Candidate { source: source.get(), result, head_usages }), Err(NoSolution) => Err(head_usages), } @@ -683,7 +683,7 @@ where goal: Goal, candidates: &mut Vec>, ) { - let _res = 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, @@ -692,9 +692,14 @@ where ); Ok(()) }); + // always returns Ok - // TODO: separate path for probes erroring because of accessing opaques - // assert!(res.is_ok()); + match res { + Ok(_) | Err(NoSolutionOrOpaquesAccessed::OpaquesAccessed) => {} + Err(NoSolutionOrOpaquesAccessed::NoSolution(NoSolution)) => { + unreachable!() + } + } } /// For some deeply nested `::A::B::C::D` rigid associated type, 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 b9b251ca9baf1..3660b95e464bc 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,7 +1,7 @@ use std::marker::PhantomData; use rustc_type_ir::search_graph::CandidateHeadUsages; -use rustc_type_ir::solve::{AccessedOpaques, CanonicalResponse}; +use rustc_type_ir::solve::{AccessedOpaques, CanonicalResponse, NoSolutionOrOpaquesAccessed}; use rustc_type_ir::{InferCtxtLike, Interner}; use tracing::{instrument, warn}; @@ -31,11 +31,11 @@ where pub(in crate::solve) fn enter_single_candidate( self, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result, - ) -> (Result, CandidateHeadUsages) { + ) -> (Result, CandidateHeadUsages) { let mut candidate_usages = CandidateHeadUsages::default(); if self.ecx.opaque_accesses.should_bail() { - return (Err(NoSolution), candidate_usages); + return (Err(NoSolutionOrOpaquesAccessed::OpaquesAccessed), candidate_usages); } self.ecx.search_graph.enter_single_candidate(); @@ -50,7 +50,7 @@ where pub(in crate::solve) fn enter( self, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result, - ) -> Result { + ) -> Result { let nested_goals = self.ecx.nested_goals.clone(); self.enter_inner(f, nested_goals) } @@ -58,7 +58,7 @@ where pub(in crate::solve) fn enter_without_propagated_nested_goals( self, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result, - ) -> Result { + ) -> Result { self.enter_inner(f, Default::default()) } @@ -66,11 +66,11 @@ where self, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result, propagated_nested_goals: Vec<(GoalSource, Goal, Option>)>, - ) -> Result { + ) -> Result { let ProbeCtxt { ecx: outer, probe_kind, _result } = self; if outer.opaque_accesses.should_bail() { - return Err(NoSolution); + return Err(NoSolutionOrOpaquesAccessed::OpaquesAccessed); } let delegate = outer.delegate; @@ -103,7 +103,7 @@ where outer.opaque_accesses.update(nested.opaque_accesses); - r + r.map_err(Into::into) } } @@ -128,7 +128,7 @@ where f: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, ) -> Result, NoSolution> { let (result, head_usages) = self.cx.enter_single_candidate(f); - result.map(|result| Candidate { source: self.source, result, head_usages }) + Ok(Candidate { source: self.source, result: result?, head_usages }) } } 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 6ff1c5153e575..d72af517b17b9 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 @@ -77,13 +77,17 @@ where None }, |ecx| { - ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| { - this.structurally_instantiate_normalizes_to_term( - goal, - goal.predicate.alias, - ); - this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) + ecx.probe(|&result| ProbeKind::RigidAlias { result }) + .enter(|this| { + this.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + this.evaluate_added_goals_and_make_canonical_response( + Certainty::Yes, + ) + }) + .map_err(Into::into) }, ) } 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 807b313805e8e..8f0a14a0f571c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -854,7 +854,7 @@ where }, ); - match result { + match result.map_err(Into::into) { Ok(resp) => resp, Err(NoSolution) => vec![], } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 5a3722e3beaca..faea4bb145d12 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -32,6 +32,33 @@ pub type QueryResult = Result, NoSolution>; #[cfg_attr(feature = "nightly", derive(StableHash))] pub struct NoSolution; +pub enum NoSolutionOrOpaquesAccessed { + NoSolution(NoSolution), + /// A bit like [`NoSolution`], but for functions that normally cannot fail *unless* they accessed + /// opaues. (See [`TypingMode::ErasedNotCoherence`]). Getting `OpaquesAccessed` doesn't mean there + /// truly is no solution. It just means that we want to bail out of the current query as fast as + /// possible, possibly by returning `NoSolution` if that's fastest. This is okay because when you get + /// `OpaquesAccessed` we're guaranteed that we're going to retry this query in the original typing + /// mode to get the correct answer. + OpaquesAccessed, +} + +/// This conversion is sound, because even in we're in `OpaquesAccessed`, +/// we're going to retry so `NoSolution` is a valid response to give.. +impl From for NoSolution { + fn from( + (NoSolutionOrOpaquesAccessed::NoSolution(_) | NoSolutionOrOpaquesAccessed::OpaquesAccessed): NoSolutionOrOpaquesAccessed, + ) -> Self { + NoSolution + } +} + +impl From for NoSolutionOrOpaquesAccessed { + fn from(value: NoSolution) -> Self { + Self::NoSolution(value) + } +} + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(StableHash_NoContext, GenericTypeVisitable))] From 13904094c56368138c2ed1b6f5b26177589136de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 28 Apr 2026 11:06:05 +0200 Subject: [PATCH 09/15] some attempts at improving coroutines resulting in FIXME --- .../src/solve/assembly/mod.rs | 4 +++- .../src/solve/eval_ctxt/mod.rs | 19 +++++++++++++++++-- .../src/solve/normalizes_to/mod.rs | 1 - compiler/rustc_type_ir/src/solve/mod.rs | 6 +++--- 4 files changed, 23 insertions(+), 7 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 d1233773e00a9..3e2d02fc99d01 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -9,7 +9,9 @@ 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, MaybeInfo, NoSolutionOrOpaquesAccessed, SizedTraitKind, StalledOnCoroutines}; +use rustc_type_ir::solve::{ + AliasBoundKind, MaybeInfo, NoSolutionOrOpaquesAccessed, SizedTraitKind, StalledOnCoroutines, +}; use rustc_type_ir::{ self as ty, AliasTy, Interner, MayBeErased, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, 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 299b075a64523..3173d034adc85 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 @@ -9,7 +9,8 @@ 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, MaybeInfo, FetchEligibleAssocItemResponse, OpaqueTypesJank, RerunCondition, SmallCopyList, + AccessedOpaques, FetchEligibleAssocItemResponse, MaybeInfo, OpaqueTypesJank, RerunCondition, + SmallCopyList, }; use rustc_type_ir::{ self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, MayBeErased, @@ -514,6 +515,20 @@ where skip_erased_attempt = true; } + if let PredicateKind::Clause(ClauseKind::Trait(tr)) = + goal.predicate.kind().skip_binder() + && tr.self_ty().has_coroutines() + && self.cx().trait_is_auto(tr.trait_ref.def_id) + { + // FIXME: this doesn't make a difference now, but with eager normalization it likely will + // skip_erased_attempt = true; + } + + if skip_erased_attempt && self.typing_mode().is_erased_not_coherence() { + self.opaque_accesses.rerun_always("skip erased attempt"); + return Err(NoSolution); + } + if !skip_erased_attempt { debug!("trying without opaques: {goal:?}"); @@ -548,7 +563,7 @@ where ); 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" + "we run without TypingMode::ErasedNotCoherence, so opaques are available, and we don't retry if the outer typing mode is ErasedNotCoherence: {accessed_opaques:?} after {goal:?}" ); data 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 d72af517b17b9..19e74a7c708de 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 @@ -6,7 +6,6 @@ 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::FetchEligibleAssocItemResponse; use rustc_type_ir::{ self as ty, FieldInfo, Interner, MayBeErased, NormalizesTo, PredicateKind, Unnormalized, diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index faea4bb145d12..a0413f1345f10 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -6,9 +6,9 @@ use std::hash::Hash; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash, StableHash_NoContext}; -use rustc_type_ir_macros::{ - GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, -}; +#[cfg(feature = "nightly")] +use rustc_type_ir_macros::GenericTypeVisitable; +use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::debug; use crate::lang_items::SolverTraitLangItem; From d543ed8344e1d3ca3d8caecf0a39f76e96582510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 29 Apr 2026 12:05:23 +0200 Subject: [PATCH 10/15] make RerunReason an enum --- .../src/solve/assembly/mod.rs | 6 +++-- .../src/solve/eval_ctxt/mod.rs | 8 +++--- .../src/solve/normalizes_to/mod.rs | 4 +-- .../src/solve/normalizes_to/opaque_types.rs | 10 ++++--- .../src/solve/trait_goals.rs | 6 ++--- compiler/rustc_type_ir/src/solve/mod.rs | 26 +++++++++++++++---- 6 files changed, 40 insertions(+), 20 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 3e2d02fc99d01..9a4be7909ee74 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -10,7 +10,8 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::search_graph::CandidateHeadUsages; use rustc_type_ir::solve::{ - AliasBoundKind, MaybeInfo, NoSolutionOrOpaquesAccessed, SizedTraitKind, StalledOnCoroutines, + AliasBoundKind, MaybeInfo, NoSolutionOrOpaquesAccessed, RerunReason, SizedTraitKind, + StalledOnCoroutines, }; use rustc_type_ir::{ self as ty, AliasTy, Interner, MayBeErased, TypeFlags, TypeFoldable, TypeFolder, @@ -1038,7 +1039,8 @@ where | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => vec![], TypingMode::ErasedNotCoherence(MayBeErased) => { - self.opaque_accesses.rerun_if_any_opaque_has_infer_as_hidden_type("self ty infer"); + self.opaque_accesses + .rerun_if_any_opaque_has_infer_as_hidden_type(RerunReason::SelfTyInfer); Vec::new() } }; 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 3173d034adc85..8cf7127664c23 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 @@ -10,7 +10,7 @@ use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind}; use rustc_type_ir::solve::{ AccessedOpaques, FetchEligibleAssocItemResponse, MaybeInfo, OpaqueTypesJank, RerunCondition, - SmallCopyList, + RerunReason, SmallCopyList, }; use rustc_type_ir::{ self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, MayBeErased, @@ -525,7 +525,7 @@ where } if skip_erased_attempt && self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always("skip erased attempt"); + self.opaque_accesses.rerun_always(RerunReason::SkipErasedAttempt); return Err(NoSolution); } @@ -1407,7 +1407,7 @@ where uv: ty::UnevaluatedConst, ) -> Option { if self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always("evaluate const"); + self.opaque_accesses.rerun_always(RerunReason::EvaluateConst); return None; } @@ -1437,7 +1437,7 @@ where symbol: I::Symbol, ) -> bool { if self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always("may use unstable feature"); + self.opaque_accesses.rerun_always(RerunReason::MayUseUnstableFeature); return false; } 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 19e74a7c708de..7996e9a448789 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 @@ -6,7 +6,7 @@ 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::FetchEligibleAssocItemResponse; +use rustc_type_ir::solve::{FetchEligibleAssocItemResponse, RerunReason}; use rustc_type_ir::{ self as ty, FieldInfo, Interner, MayBeErased, NormalizesTo, PredicateKind, Unnormalized, Upcast as _, @@ -321,7 +321,7 @@ where } FetchEligibleAssocItemResponse::Err(guar) => return error_response(ecx, guar), FetchEligibleAssocItemResponse::NotFoundBecauseErased => { - ecx.opaque_accesses.rerun_always("fetch eligible assoc item"); + ecx.opaque_accesses.rerun_always(RerunReason::FetchEligibleAssocItem); return Err(NoSolution); } }; 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 61bc0cdcda100..83d078c5d8735 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 @@ -2,7 +2,7 @@ //! behaves differently depending on the current `TypingMode`. use rustc_type_ir::inherent::*; -use rustc_type_ir::solve::{GoalSource, NoSolution}; +use rustc_type_ir::solve::{GoalSource, NoSolution, RerunReason}; use rustc_type_ir::{self as ty, Interner, MayBeErased, TypingMode, fold_regions}; use crate::delegate::SolverDelegate; @@ -149,11 +149,13 @@ where // 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); + self.opaque_accesses.rerun_if_opaque_in_opaque_type_storage( + RerunReason::NormalizeOpaqueType, + def_id, + ); } else { self.opaque_accesses - .rerun_if_in_post_analysis("normalize opaque type non local"); + .rerun_if_in_post_analysis(RerunReason::NormalizeOpaqueTypeRemoteCrate); } if self.opaque_accesses.should_bail() { // If we already accessed opaque types once, bail. 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 8f0a14a0f571c..123027cad8c12 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -6,7 +6,7 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::solve::{ AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, MaybeInfo, OpaqueTypesJank, - SizedTraitKind, + RerunReason, SizedTraitKind, }; use rustc_type_ir::{ self as ty, FieldInfo, Interner, MayBeErased, Movability, PredicatePolarity, TraitPredicate, @@ -237,7 +237,7 @@ where goal.predicate.self_ty().kind() { if ecx.opaque_accesses.might_rerun() { - ecx.opaque_accesses.rerun_always("auto trait leakage"); + ecx.opaque_accesses.rerun_always(RerunReason::AutoTraitLeakage); return Err(NoSolution); } @@ -1573,7 +1573,7 @@ where } TypingMode::ErasedNotCoherence(MayBeErased) => { // Trying to continue here isn't worth it. - self.opaque_accesses.rerun_always("try stall coroutine"); + self.opaque_accesses.rerun_always(RerunReason::TryStallCoroutine); return Some(Err(NoSolution)); } TypingMode::Coherence diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index a0413f1345f10..c7f785454b233 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -240,13 +240,29 @@ impl RerunCondition { } } +/// Mainly for debugging, to keep track of the source of the rerunning +/// in [`TypingMode::ErasedNotCoherence`]. +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] +pub enum RerunReason { + NormalizeOpaqueTypeRemoteCrate, + NormalizeOpaqueType, + MayUseUnstableFeature, + EvaluateConst, + SkipErasedAttempt, + SelfTyInfer, + FetchEligibleAssocItem, + AutoTraitLeakage, + TryStallCoroutine, +} + #[derive_where(Copy, Clone, Debug, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(StableHash_NoContext, GenericTypeVisitable))] pub struct AccessedOpaques { #[type_visitable(ignore)] #[type_foldable(identity)] - pub reason: Option<&'static str>, + pub reason: Option, pub rerun: RerunCondition, } @@ -276,12 +292,12 @@ impl AccessedOpaques { self.rerun.should_bail() } - pub fn rerun_always(&mut self, reason: &'static str) { + pub fn rerun_always(&mut self, reason: RerunReason) { debug!("set rerun always"); self.update(AccessedOpaques { reason: Some(reason), rerun: RerunCondition::Always }); } - pub fn rerun_if_in_post_analysis(&mut self, reason: &'static str) { + pub fn rerun_if_in_post_analysis(&mut self, reason: RerunReason) { debug!("set rerun if post analysis"); self.update(AccessedOpaques { reason: Some(reason), @@ -291,7 +307,7 @@ impl AccessedOpaques { pub fn rerun_if_opaque_in_opaque_type_storage( &mut self, - reason: &'static str, + reason: RerunReason, defid: I::LocalDefId, ) { debug!("set rerun if opaque type {defid:?} in storage"); @@ -301,7 +317,7 @@ impl AccessedOpaques { }); } - pub fn rerun_if_any_opaque_has_infer_as_hidden_type(&mut self, reason: &'static str) { + pub fn rerun_if_any_opaque_has_infer_as_hidden_type(&mut self, reason: RerunReason) { debug!("set rerun if any opaque in the storage has a hidden type that is an infer var"); self.update(AccessedOpaques { reason: Some(reason), From 8ca4e431efee4cc6f2802ac6076705585bf38154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 29 Apr 2026 16:11:26 +0200 Subject: [PATCH 11/15] return ambiguous in fast path, factor out method to check if no opaques in scope --- .../src/solve/delegate.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index b0bcf82e87c06..15762652da141 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -37,6 +37,15 @@ impl<'tcx> Deref for SolverDelegate<'tcx> { } } +impl<'tcx> SolverDelegate<'tcx> { + fn known_no_opaque_types_in_storage(&self) -> bool { + self.inner.borrow_mut().opaque_types().is_empty() + // in erased mode, observing that opaques are empty aren't enough to give a result + // here, so let's try the slow path instead. + && !self.typing_mode_raw().is_erased_not_coherence() + } +} + impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<'tcx> { type Infcx = InferCtxt<'tcx>; type Interner = TyCtxt<'tcx>; @@ -70,15 +79,9 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< // eventually use opaques to incompletely guide inference via ty var // self types. // FIXME: Properly consider opaques here. - && self.inner.borrow_mut().opaque_types().is_empty() + && self.known_no_opaque_types_in_storage() { - // 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); - } + return Some(Certainty::AMBIGUOUS); } if trait_pred.polarity() == ty::PredicatePolarity::Positive { From f0c1798a3a14a8e2cd9cafbbe96ecdcd57746688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 4 May 2026 15:37:16 +0200 Subject: [PATCH 12/15] prefer assert_not_erased over matching+unreachable --- .../rustc_const_eval/src/const_eval/eval_queries.rs | 5 ++--- compiler/rustc_const_eval/src/const_eval/valtrees.rs | 5 ++--- .../rustc_const_eval/src/interpret/eval_context.rs | 7 +++---- compiler/rustc_mir_transform/src/elaborate_drop.rs | 5 ++--- compiler/rustc_next_trait_solver/src/solve/mod.rs | 10 ++++++---- .../src/solve/normalizes_to/mod.rs | 3 +-- .../src/solve/normalizes_to/opaque_types.rs | 3 +-- compiler/rustc_trait_selection/src/traits/normalize.rs | 7 +++---- compiler/rustc_ty_utils/src/instance.rs | 7 +++---- 9 files changed, 23 insertions(+), 29 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 7f0d6c8e87343..62ec0d2ef8bf4 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, MayBeErased, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, throw_inval}; use rustc_span::Span; use rustc_span::def_id::LocalDefId; @@ -374,7 +374,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( assert!(key.value.promoted.is_some() || !tcx.is_static(key.value.instance.def_id())); if cfg!(debug_assertions) { - match key.typing_env.typing_mode() { + match key.typing_env.typing_mode().assert_not_erased() { ty::TypingMode::PostAnalysis => {} ty::TypingMode::Coherence | ty::TypingMode::Analysis { .. } @@ -384,7 +384,6 @@ pub fn eval_to_allocation_raw_provider<'tcx>( "Const eval should always happens in PostAnalysis mode. See the comment in `InterpCx::new` for more details." ) } - ty::TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } // Make sure we format the instance even if we do not print it. diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 3858a3b7047d3..0169dc7ed99df 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, MayBeErased, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::DUMMY_SP; use tracing::{debug, instrument, trace}; @@ -237,7 +237,7 @@ pub(crate) fn eval_to_valtree<'tcx>( cid: GlobalId<'tcx>, ) -> EvalToValTreeResult<'tcx> { if cfg!(debug_assertions) { - match typing_env.typing_mode() { + match typing_env.typing_mode().assert_not_erased() { ty::TypingMode::PostAnalysis => {} ty::TypingMode::Coherence | ty::TypingMode::Analysis { .. } @@ -247,7 +247,6 @@ pub(crate) fn eval_to_valtree<'tcx>( "Const eval should always happens in PostAnalysis mode. See the comment in `InterpCx::new` for more details." ) } - ty::TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), } } 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 06410df7e1f16..7dc6d292a94af 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, MayBeErased, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingEnv, - TypingMode, Variance, + self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingEnv, TypingMode, + Variance, }; use rustc_middle::{bug, mir, span_bug}; use rustc_span::Span; @@ -243,7 +243,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // types that are not specified in the opaque type. We also use MIR bodies whose opaque types have // already been revealed, so we'd be able to at least partially observe the hidden types anyways. if cfg!(debug_assertions) { - match typing_env.typing_mode() { + match typing_env.typing_mode().assert_not_erased() { TypingMode::PostAnalysis => {} TypingMode::Coherence | TypingMode::Analysis { .. } @@ -251,7 +251,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | TypingMode::PostBorrowckAnalysis { .. } => { bug!("Const eval should always happens in PostAnalysis mode."); } - TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), } } diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 48006bace447c..6cd08cafbcc17 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, MayBeErased, Ty, TyCtxt, Unnormalized}; +use rustc_middle::ty::{self, GenericArg, GenericArgsRef, Ty, TyCtxt, Unnormalized}; use rustc_middle::{bug, span_bug, traits}; use rustc_span::{DUMMY_SP, Spanned, dummy_spanned}; use tracing::{debug, instrument}; @@ -547,7 +547,7 @@ where let subpath = self.elaborator.field_subpath(variant_path, field_idx); let tcx = self.tcx(); - match self.elaborator.typing_env().typing_mode() { + match self.elaborator.typing_env().typing_mode().assert_not_erased() { ty::TypingMode::PostAnalysis => {} ty::TypingMode::Coherence | ty::TypingMode::Analysis { .. } @@ -555,7 +555,6 @@ where | ty::TypingMode::PostBorrowckAnalysis { .. } => { bug!() } - ty::TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), } let field_ty = field.ty(tcx, args); diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 678cf2f9abe6a..178a192a66300 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -24,7 +24,7 @@ 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, MayBeErased, TyVid, TypingMode}; +use rustc_type_ir::{self as ty, Interner, TyVid, TypingMode}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -358,7 +358,11 @@ where } fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool { - match self.typing_mode() { + match self + .typing_mode() + // Caller should handle erased mode + .assert_not_erased() + { // Opaques are never rigid outside of analysis mode. TypingMode::Coherence | TypingMode::PostAnalysis => false, // During analysis, opaques are rigid unless they may be defined by @@ -368,8 +372,6 @@ where | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => { !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id)) } - // Caller should handle this variant - TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 7996e9a448789..632311ebab1b3 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,8 +8,7 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::{FetchEligibleAssocItemResponse, RerunReason}; use rustc_type_ir::{ - self as ty, FieldInfo, Interner, MayBeErased, NormalizesTo, PredicateKind, Unnormalized, - Upcast as _, + self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Unnormalized, Upcast as _, }; use tracing::instrument; 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 83d078c5d8735..45dd2f25abd78 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 @@ -82,7 +82,7 @@ where // During HIR typeck, opaque types start out as unconstrained // inference variables. In borrowck we instead use the type // computed in HIR typeck as the initial value. - match self.typing_mode() { + match self.typing_mode().assert_not_erased() { TypingMode::Analysis { .. } => {} TypingMode::Borrowck { .. } => { let actual = cx @@ -98,7 +98,6 @@ where TypingMode::Coherence | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => unreachable!(), - TypingMode::ErasedNotCoherence(MayBeErased) => todo!(), } } diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 6e526e8dc548c..84dbd53de83f2 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, MayBeErased, Term, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitable, TypeVisitableExt, TypingMode, Unnormalized, + self, AliasTerm, Term, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, + TypeVisitableExt, TypingMode, Unnormalized, }; use tracing::{debug, instrument}; @@ -138,14 +138,13 @@ 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_raw() { + match infcx.typing_mode_raw().assert_not_erased() { // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(ty::TypeFlags::HAS_TY_OPAQUE), TypingMode::PostAnalysis => {} - TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), } value.has_type_flags(flags) diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 178533fb231a0..c423f9925c6bd 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -6,8 +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, MayBeErased, PseudoCanonicalInput, TyCtxt, - TypeVisitableExt, Unnormalized, + self, ClosureKind, GenericArgsRef, Instance, PseudoCanonicalInput, TyCtxt, TypeVisitableExt, + Unnormalized, }; use rustc_span::sym; use rustc_trait_selection::traits; @@ -155,13 +155,12 @@ fn resolve_associated_item<'tcx>( // and the obligation is monomorphic, otherwise passes such as // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. - match typing_env.typing_mode() { + match typing_env.typing_mode().assert_not_erased() { ty::TypingMode::Coherence | ty::TypingMode::Analysis { .. } | ty::TypingMode::Borrowck { .. } | ty::TypingMode::PostBorrowckAnalysis { .. } => false, ty::TypingMode::PostAnalysis => !trait_ref.still_further_specializable(), - ty::TypingMode::ErasedNotCoherence(MayBeErased) => unreachable!(), } }; if !eligible { From 52bd834020638ff331544bd0eb018e74f32c9536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 5 May 2026 11:00:29 +0200 Subject: [PATCH 13/15] move typing mode determination out of canonicalize_goal --- .../src/canonical/mod.rs | 34 +------------ .../src/solve/eval_ctxt/mod.rs | 48 +++++++++++++++++-- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 30d9bc55ea11a..4eea0f2c2198d 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, - MayBeErased, TypeFoldable, TypingMode, TypingModeEqWrapper, + TypeFoldable, TypingMode, TypingModeEqWrapper, }; use tracing::instrument; @@ -46,13 +46,6 @@ impl ResponseT for inspect::State { } } -pub(super) enum EraseOpaqueTypes { - /// This setting erases opaque types, unless we're in coherence. - /// In `TypingMode::Coherence` we never erase opaque types - IfNotCoherence, - No, -} - /// Canonicalizes the goal remembering the original values /// for each bound variable. /// @@ -61,35 +54,12 @@ pub(super) fn canonicalize_goal( delegate: &D, goal: Goal, opaque_types: &[(ty::OpaqueTypeKey, I::Ty)], - erase_opaque_types: EraseOpaqueTypes, + typing_mode: TypingMode, ) -> (Vec, CanonicalInput) where D: SolverDelegate, I: Interner, { - let (opaque_types, typing_mode) = match (erase_opaque_types, delegate.typing_mode_raw()) { - // In `TypingMode::Coherence` there should not be any opaques, and we also don't change typing mode. - (_, TypingMode::Coherence) => { - assert!(opaque_types.is_empty()); - (&[][..], TypingMode::Coherence) - } - // Make sure we're not recursively in `ErasedNotCoherence`. - (_, TypingMode::ErasedNotCoherence(MayBeErased)) => { - assert!(opaque_types.is_empty()); - (&[][..], TypingMode::ErasedNotCoherence(MayBeErased)) - } - // If we're supposed to erase opaque types, and we're in any typing mode other than coherence, - // do the erasing and change typing mode. - ( - EraseOpaqueTypes::IfNotCoherence, - TypingMode::Analysis { .. } - | TypingMode::Borrowck { .. } - | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis, - ) => (&[][..], TypingMode::ErasedNotCoherence(MayBeErased)), - (EraseOpaqueTypes::No, typing_mode) => (opaque_types, typing_mode), - }; - let (orig_values, canonical) = Canonicalizer::canonicalize_input( delegate, QueryInput { 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 8cf7127664c23..79a1fe72bbf54 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 @@ -21,8 +21,8 @@ use tracing::{Level, debug, instrument, trace, warn}; use super::has_only_region_constraints; use crate::canonical::{ - EraseOpaqueTypes, canonicalize_goal, canonicalize_response, - instantiate_and_apply_query_response, response_no_constraints_raw, + canonicalize_goal, canonicalize_response, instantiate_and_apply_query_response, + response_no_constraints_raw, }; use crate::coherence; use crate::delegate::SolverDelegate; @@ -84,6 +84,42 @@ enum RerunDecision { EagerlyPropagateToParent, } +enum EraseOpaqueTypes { + /// This setting erases opaque types, unless we're in coherence. + /// In `TypingMode::Coherence` we never erase opaque types + IfNotCoherence, + No, +} + +fn maybe_erase_opaque_types( + erase_opaque_types: EraseOpaqueTypes, + opaque_types: &[(OpaqueTypeKey, I::Ty)], + typing_mode: TypingMode, +) -> (&[(OpaqueTypeKey, I::Ty)], TypingMode) { + match (erase_opaque_types, 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(MayBeErased)) => { + assert!(opaque_types.is_empty()); + (&[][..], TypingMode::ErasedNotCoherence(MayBeErased)) + } + // If we're supposed to erase opaque types, and we're in any typing mode other than coherence, + // do the erasing and change typing mode. + ( + EraseOpaqueTypes::IfNotCoherence, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis, + ) => (&[][..], TypingMode::ErasedNotCoherence(MayBeErased)), + (EraseOpaqueTypes::No, typing_mode) => (opaque_types, typing_mode), + } +} + pub struct EvalCtxt<'a, D, I = ::Interner> where D: SolverDelegate, @@ -691,8 +727,10 @@ where step_kind: PathKind, erase_opaque_types: EraseOpaqueTypes, ) -> (AccessedOpaques, (QueryResult, Vec, CanonicalInput)) { + let (opaque_types, typing_mode) = + maybe_erase_opaque_types(erase_opaque_types, opaque_types, self.typing_mode()); let (orig_values, canonical_goal) = - canonicalize_goal(self.delegate, goal, &opaque_types, erase_opaque_types); + canonicalize_goal(self.delegate, goal, opaque_types, typing_mode); let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( self.cx(), @@ -1768,8 +1806,10 @@ pub(super) fn evaluate_root_goal_for_proof_tree, assert!(opaque_types.is_empty()); } + let (opaque_types, typing_mode) = + maybe_erase_opaque_types(EraseOpaqueTypes::No, &opaque_types, delegate.typing_mode_raw()); let (orig_values, canonical_goal) = - canonicalize_goal(delegate, goal, &opaque_types, EraseOpaqueTypes::No); + canonicalize_goal(delegate, goal, &opaque_types, typing_mode); let (canonical_result, final_revision) = delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal); From 1c34c395538733a62b87dcce382f28b5d02d3746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 5 May 2026 13:38:29 +0200 Subject: [PATCH 14/15] some cleanup --- .../src/solve/assembly/mod.rs | 25 +-- .../src/solve/eval_ctxt/mod.rs | 171 ++++++------------ .../src/solve/trait_goals.rs | 2 +- compiler/rustc_type_ir/src/solve/mod.rs | 4 +- 4 files changed, 76 insertions(+), 126 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 9a4be7909ee74..eb7e3b93c0bcc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -418,18 +418,21 @@ where D: SolverDelegate, I: Interner, { + // FIXME(#155443): This function should only ever return an error + // as we want to force a rerun when accessing opaques. We should change + // this file to revert all the newly added places which return `NoSolution`. pub(super) fn assemble_and_evaluate_candidates>( &mut self, goal: Goal, assemble_from: AssembleCandidatesFrom, - ) -> Result<(Vec>, FailedCandidateInfo), NoSolution> { + ) -> (Vec>, FailedCandidateInfo) { 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 Ok((candidates, failed_candidate_info)); + return (candidates, failed_candidate_info); }; let goal: Goal = goal @@ -437,8 +440,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 Ok((candidates, failed_candidate_info)); + self.try_assemble_bounds_via_registered_opaques(goal, assemble_from, &mut candidates); + return (candidates, failed_candidate_info); } // Vars that show up in the rest of the goal substs may have been constrained by @@ -449,7 +452,7 @@ where && let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal) { candidates.push(candidate); - return Ok((candidates, failed_candidate_info)); + return (candidates, failed_candidate_info); } self.assemble_alias_bound_candidates(goal, &mut candidates); @@ -500,7 +503,7 @@ where } } - Ok((candidates, failed_candidate_info)) + (candidates, failed_candidate_info) } pub(super) fn forced_ambiguity( @@ -1029,7 +1032,7 @@ where 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() { @@ -1047,7 +1050,7 @@ where if opaque_types.is_empty() { candidates.extend(self.forced_ambiguity(MaybeInfo::AMBIGUOUS)); - return Ok(()); + return; } for &alias_ty in &opaque_types { @@ -1147,8 +1150,6 @@ where this.evaluate_added_goals_and_make_canonical_response(certainty) })); } - - Ok(()) } /// Assemble and merge candidates for goals which are related to an underlying trait @@ -1205,7 +1206,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 @@ -1234,7 +1235,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/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 79a1fe72bbf54..a3f3a6f87d9bd 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 @@ -83,43 +83,6 @@ enum RerunDecision { No, EagerlyPropagateToParent, } - -enum EraseOpaqueTypes { - /// This setting erases opaque types, unless we're in coherence. - /// In `TypingMode::Coherence` we never erase opaque types - IfNotCoherence, - No, -} - -fn maybe_erase_opaque_types( - erase_opaque_types: EraseOpaqueTypes, - opaque_types: &[(OpaqueTypeKey, I::Ty)], - typing_mode: TypingMode, -) -> (&[(OpaqueTypeKey, I::Ty)], TypingMode) { - match (erase_opaque_types, 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(MayBeErased)) => { - assert!(opaque_types.is_empty()); - (&[][..], TypingMode::ErasedNotCoherence(MayBeErased)) - } - // If we're supposed to erase opaque types, and we're in any typing mode other than coherence, - // do the erasing and change typing mode. - ( - EraseOpaqueTypes::IfNotCoherence, - TypingMode::Analysis { .. } - | TypingMode::Borrowck { .. } - | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis, - ) => (&[][..], TypingMode::ErasedNotCoherence(MayBeErased)), - (EraseOpaqueTypes::No, typing_mode) => (opaque_types, typing_mode), - } -} - pub struct EvalCtxt<'a, D, I = ::Interner> where D: SolverDelegate, @@ -530,49 +493,66 @@ 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)); + let typing_mode = self.typing_mode(); let step_kind = self.step_kind_for_source(source); let tracing_span = tracing::span!( Level::DEBUG, - "evaluate goal raw in typing mode", + "evaluate_goal_raw in typing mode", "{:?} opaques={:?}", - self.typing_mode(), + 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; - } + let skip_erased_attempt = if typing_mode.is_coherence() { + true + } else { + let mut skip = false; + if opaque_types.iter().any(|(_, ty)| ty.is_ty_var()) + && let PredicateKind::Clause(ClauseKind::Trait(..)) = + goal.predicate.kind().skip_binder() + { + skip = true; + } - if let PredicateKind::Clause(ClauseKind::Trait(tr)) = - goal.predicate.kind().skip_binder() - && tr.self_ty().has_coroutines() - && self.cx().trait_is_auto(tr.trait_ref.def_id) - { - // FIXME: this doesn't make a difference now, but with eager normalization it likely will - // skip_erased_attempt = true; - } + if let PredicateKind::Clause(ClauseKind::Trait(tr)) = + goal.predicate.kind().skip_binder() + && tr.self_ty().has_coroutines() + && self.cx().trait_is_auto(tr.trait_ref.def_id) + { + // FIXME(#155443): this doesn't make a difference now, but with eager normalization + // it likely will. + // skip_erased_attempt = true; + } - if skip_erased_attempt && self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always(RerunReason::SkipErasedAttempt); - return Err(NoSolution); - } + skip + }; - if !skip_erased_attempt { + if skip_erased_attempt { + if typing_mode.is_erased_not_coherence() { + self.opaque_accesses.rerun_always(RerunReason::SkipErasedAttempt); + // FIXME(#155443): We should differentiate between `NoSolution` and force rerun here. + return Err(NoSolution); + } else { + debug!("running in original typing mode"); + } + } else { debug!("trying without opaques: {goal:?}"); - let (accessed_opaques, data) = self.canonicalize_goal_maybe_erased( + let (orig_values, canonical_goal) = canonicalize_goal( + self.delegate, goal, - &opaque_types, + &[], + TypingMode::ErasedNotCoherence(MayBeErased), + ); + + let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( + self.cx(), + canonical_goal, step_kind, - EraseOpaqueTypes::IfNotCoherence, + &mut inspect::ProofTreeBuilder::new_noop(), ); let should_rerun = self.should_rerun_after_erased_canonicalization( @@ -581,40 +561,36 @@ where &opaque_types, ); match should_rerun { - RerunDecision::Yes => {} - RerunDecision::No => break 'retry_canonicalize data, + RerunDecision::Yes => debug!("rerunning in original typing mode"), + RerunDecision::No => { + break 'retry_canonicalize (canonical_result, orig_values, canonical_goal); + } RerunDecision::EagerlyPropagateToParent => { self.opaque_accesses.update(accessed_opaques); - break 'retry_canonicalize data; + break 'retry_canonicalize (canonical_result, orig_values, canonical_goal); } } } - debug!("rerunning with opaques"); - let (accessed_opaques, data) = self.canonicalize_goal_maybe_erased( - goal, - &opaque_types, + let (orig_values, canonical_goal) = + canonicalize_goal(self.delegate, goal, &opaque_types, typing_mode); + + let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( + self.cx(), + canonical_goal, step_kind, - EraseOpaqueTypes::No, + &mut inspect::ProofTreeBuilder::new_noop(), ); assert!( !accessed_opaques.might_rerun(), "we run without TypingMode::ErasedNotCoherence, so opaques are available, and we don't retry if the outer typing mode is ErasedNotCoherence: {accessed_opaques:?} after {goal:?}" ); - data + (canonical_result, orig_values, canonical_goal) }; - let response = match result { - Ok(response) => { - debug!("success"); - response - } - Err(NoSolution) => { - debug!("normal failure"); - return Err(NoSolution); - } - }; + debug!(?result); + let response = result?; drop(tracing_span); @@ -720,28 +696,6 @@ 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 (opaque_types, typing_mode) = - maybe_erase_opaque_types(erase_opaque_types, opaque_types, self.typing_mode()); - let (orig_values, canonical_goal) = - canonicalize_goal(self.delegate, goal, opaque_types, typing_mode); - - let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( - self.cx(), - canonical_goal, - step_kind, - &mut inspect::ProofTreeBuilder::new_noop(), - ); - - (accessed_opaques, (canonical_result, orig_values, canonical_goal)) - } - fn should_rerun_after_erased_canonicalization( &self, AccessedOpaques { reason: _, rerun }: AccessedOpaques, @@ -1801,15 +1755,10 @@ pub(super) fn evaluate_root_goal_for_proof_tree, ) -> (Result, NoSolution>, inspect::GoalEvaluation) { let opaque_types = delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(delegate, (goal, opaque_types)); + let typing_mode = delegate.typing_mode_raw().assert_not_erased(); - if delegate.typing_mode_raw().is_erased_not_coherence() { - assert!(opaque_types.is_empty()); - } - - let (opaque_types, typing_mode) = - maybe_erase_opaque_types(EraseOpaqueTypes::No, &opaque_types, delegate.typing_mode_raw()); let (orig_values, canonical_goal) = - canonicalize_goal(delegate, goal, &opaque_types, typing_mode); + canonicalize_goal(delegate, goal, &opaque_types, typing_mode.into()); let (canonical_result, final_revision) = delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal); 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 123027cad8c12..4d5fe3bf69502 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1550,7 +1550,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) diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index c7f785454b233..4fb14a14fab4b 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -333,10 +333,10 @@ impl AccessedOpaques { /// we're currently typechecking while the `predicate` is some trait bound. #[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, P)] #[derive_where(Copy; I: Interner, P: Copy)] -#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) + derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext, GenericTypeVisitable,) )] pub struct Goal { pub param_env: I::ParamEnv, From 36b6eeb66e46b6bc20105546ea4a363c7743c6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 6 May 2026 10:51:06 +0200 Subject: [PATCH 15/15] fixup GenericTypeVisitable --- compiler/rustc_type_ir/src/solve/mod.rs | 27 +++++++++++++------------ 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 4fb14a14fab4b..0634c9b741d38 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -6,9 +6,9 @@ use std::hash::Hash; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash, StableHash_NoContext}; -#[cfg(feature = "nightly")] -use rustc_type_ir_macros::GenericTypeVisitable; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{ + GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, +}; use tracing::debug; use crate::lang_items::SolverTraitLangItem; @@ -60,8 +60,8 @@ impl From for NoSolutionOrOpaquesAccessed { } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(StableHash_NoContext, GenericTypeVisitable))] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)] +#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] pub enum SmallCopyList { Empty, One([T; 1]), @@ -137,8 +137,8 @@ impl AsRef<[T]> for SmallCopyList { /// /// Some variant names contain an `Or` here. They rerun when any of the two conditions applies #[derive_where(Copy, Clone, Debug, Hash, PartialEq, Eq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(StableHash_NoContext, GenericTypeVisitable))] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)] +#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] pub enum RerunCondition { Never, @@ -243,6 +243,7 @@ impl RerunCondition { /// Mainly for debugging, to keep track of the source of the rerunning /// in [`TypingMode::ErasedNotCoherence`]. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable)] #[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] pub enum RerunReason { NormalizeOpaqueTypeRemoteCrate, @@ -257,10 +258,10 @@ pub enum RerunReason { } #[derive_where(Copy, Clone, Debug, Hash, PartialEq, Eq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(StableHash_NoContext, GenericTypeVisitable))] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)] +#[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] pub struct AccessedOpaques { - #[type_visitable(ignore)] + #[cfg_attr(feature = "nightly", type_visitable(ignore))] #[type_foldable(identity)] pub reason: Option, pub rerun: RerunCondition, @@ -333,10 +334,10 @@ impl AccessedOpaques { /// we're currently typechecking while the `predicate` is some trait bound. #[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, P)] #[derive_where(Copy; I: Interner, P: Copy)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic, GenericTypeVisitable)] #[cfg_attr( feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext, GenericTypeVisitable,) + derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) )] pub struct Goal { pub param_env: I::ParamEnv, @@ -395,7 +396,7 @@ pub enum GoalSource { #[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, Goal)] #[derive_where(Copy; I: Interner, Goal: Copy)] -#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext)