From 687f839266e41ad2c14d5967cc7de1805b34e4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 6 May 2026 16:13:17 +0200 Subject: [PATCH 1/2] return faster from internal iterators --- compiler/rustc_ast_ir/src/visit.rs | 23 ++++++++++ .../src/ty/context/impl_interner.rs | 43 ++++++++++++++----- .../src/solve/assembly/mod.rs | 2 +- compiler/rustc_type_ir/src/interner.rs | 13 ++++-- 4 files changed, 66 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_ast_ir/src/visit.rs b/compiler/rustc_ast_ir/src/visit.rs index f6d6bf3a3e309..8315c080dfa86 100644 --- a/compiler/rustc_ast_ir/src/visit.rs +++ b/compiler/rustc_ast_ir/src/visit.rs @@ -41,6 +41,29 @@ impl VisitorResult for ControlFlow { } } +impl VisitorResult for Result<(), E> { + type Residual = E; + + fn output() -> Self { + Ok(()) + } + fn from_residual(residual: Self::Residual) -> Self { + Err(residual) + } + fn from_branch(b: ControlFlow) -> Self { + match b { + ControlFlow::Continue(()) => Ok(()), + ControlFlow::Break(e) => Err(e), + } + } + fn branch(self) -> ControlFlow { + match self { + Ok(()) => ControlFlow::Continue(()), + Err(e) => ControlFlow::Break(e), + } + } +} + #[macro_export] macro_rules! try_visit { ($e:expr) => { diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 1ab8aa7027528..19668a4c0f20e 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -1,5 +1,6 @@ //! Implementation of [`rustc_type_ir::Interner`] for [`TyCtxt`]. +use std::ops::ControlFlow; use std::{debug_assert_matches, fmt}; use rustc_errors::ErrorGuaranteed; @@ -9,7 +10,9 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; -use rustc_type_ir::{CollectAndApply, Interner, TypeFoldable, Unnormalized, search_graph}; +use rustc_type_ir::{ + CollectAndApply, Interner, TypeFoldable, Unnormalized, VisitorResult, search_graph, +}; use crate::dep_graph::{DepKind, DepNodeIndex}; use crate::infer::canonical::CanonicalVarKinds; @@ -522,20 +525,31 @@ impl<'tcx> Interner for TyCtxt<'tcx> { // This implementation is a bit different from `TyCtxt::for_each_relevant_impl`, // since we want to skip over blanket impls for non-rigid aliases, and also we // only want to consider types that *actually* unify with float/int vars. - fn for_each_relevant_impl( + fn for_each_relevant_impl( self, trait_def_id: DefId, self_ty: Ty<'tcx>, - mut f: impl FnMut(DefId), - ) { + mut f: impl FnMut(DefId) -> R, + ) -> R { + macro_rules! ret { + ($e: expr) => { + match $e.branch() { + ControlFlow::Break(b) => return R::from_residual(b), + ControlFlow::Continue(()) => {} + } + }; + } + let tcx = self; let trait_impls = tcx.trait_impls_of(trait_def_id); let mut consider_impls_for_simplified_type = |simp| { if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { for &impl_def_id in impls_for_type { - f(impl_def_id); + ret!(f(impl_def_id)) } } + + R::output() }; match self_ty.kind() { @@ -566,7 +580,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self_ty, ty::fast_reject::TreatParams::AsRigid, ) { - consider_impls_for_simplified_type(simp); + ret!(consider_impls_for_simplified_type(simp)); } } @@ -595,7 +609,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ty::SimplifiedType::Uint(Usize), ]; for simp in possible_integers { - consider_impls_for_simplified_type(simp); + ret!(consider_impls_for_simplified_type(simp)); } } @@ -610,7 +624,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ]; for simp in possible_floats { - consider_impls_for_simplified_type(simp); + ret!(consider_impls_for_simplified_type(simp)); } } @@ -634,11 +648,20 @@ impl<'tcx> Interner for TyCtxt<'tcx> { #[allow(rustc::usage_of_type_ir_traits)] self.for_each_blanket_impl(trait_def_id, f) } - fn for_each_blanket_impl(self, trait_def_id: DefId, mut f: impl FnMut(DefId)) { + fn for_each_blanket_impl( + self, + trait_def_id: DefId, + mut f: impl FnMut(DefId) -> R, + ) -> R { let trait_impls = self.trait_impls_of(trait_def_id); for &impl_def_id in trait_impls.blanket_impls() { - f(impl_def_id); + match f(impl_def_id).branch() { + ControlFlow::Break(b) => return R::from_residual(b), + ControlFlow::Continue(()) => {} + } } + + R::output() } fn has_item_definition(self, def_id: DefId) -> bool { 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 eb7e3b93c0bcc..c7e33e1f94f27 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -1129,7 +1129,7 @@ where // FIXME(trait-system-refactor-initiative#229): This isn't // perfect yet as it still allows us to incorrectly constrain // other inference variables. - Err(NoSolution) + Err(NoSolution.into()) } }) { Ok(candidate) => candidates.push(candidate), diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index f25bc5fc4e3c9..7cdd82fe1a807 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -4,6 +4,7 @@ use std::hash::Hash; use std::ops::Deref; use rustc_ast_ir::Movability; +use rustc_ast_ir::visit::VisitorResult; use rustc_index::bit_set::DenseBitSet; use crate::fold::TypeFoldable; @@ -401,13 +402,17 @@ pub trait Interner: def_id: Self::TraitId, ) -> impl IntoIterator; - fn for_each_relevant_impl( + fn for_each_relevant_impl( self, trait_def_id: Self::TraitId, self_ty: Self::Ty, - f: impl FnMut(Self::ImplId), - ); - fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, f: impl FnMut(Self::ImplId)); + f: impl FnMut(Self::ImplId) -> R, + ) -> R; + fn for_each_blanket_impl( + self, + trait_def_id: Self::TraitId, + f: impl FnMut(Self::ImplId) -> R, + ) -> R; fn has_item_definition(self, def_id: Self::ImplOrTraitAssocTermId) -> bool; From 180725cff61d00dd1b9c35fc720a8befaec5d46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 4 May 2026 15:59:21 +0200 Subject: [PATCH 2/2] consistently encode failure reason (nosolution or rerun) in the error type --- .../src/solve/alias_relate.rs | 6 +- .../src/solve/assembly/mod.rs | 213 ++++++++------- .../src/solve/assembly/structural_traits.rs | 2 +- .../src/solve/effect_goals.rs | 82 +++--- .../src/solve/eval_ctxt/mod.rs | 198 ++++++++------ .../src/solve/eval_ctxt/probe.rs | 42 +-- .../rustc_next_trait_solver/src/solve/mod.rs | 52 ++-- .../src/solve/normalizes_to/anon_const.rs | 5 +- .../src/solve/normalizes_to/free_alias.rs | 5 +- .../src/solve/normalizes_to/inherent.rs | 5 +- .../src/solve/normalizes_to/mod.rs | 137 +++++----- .../src/solve/normalizes_to/opaque_types.rs | 28 +- .../src/solve/project_goals.rs | 5 +- .../src/solve/search_graph.rs | 17 +- .../src/solve/trait_goals.rs | 242 ++++++++++-------- compiler/rustc_type_ir/src/solve/mod.rs | 86 ++++--- .../rustc-dev-remap.only-remap.stderr | 3 + .../rustc-dev-remap.remap-unremap.stderr | 3 + 18 files changed, 661 insertions(+), 470 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs index 7c79dba61a6d1..47d33985f9d36 100644 --- a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs +++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs @@ -16,12 +16,12 @@ //! relate them structurally. use rustc_type_ir::inherent::*; -use rustc_type_ir::solve::GoalSource; +use rustc_type_ir::solve::{GoalSource, QueryResultOrRerunNonErased}; use rustc_type_ir::{self as ty, Interner}; use tracing::{instrument, trace}; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; +use crate::solve::{Certainty, EvalCtxt, Goal}; impl EvalCtxt<'_, D> where @@ -32,7 +32,7 @@ where pub(super) fn compute_alias_relate_goal( &mut self, goal: Goal, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { let cx = self.cx(); let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; 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 c7e33e1f94f27..7f3627c6db54e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -10,8 +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, RerunReason, SizedTraitKind, - StalledOnCoroutines, + AliasBoundKind, MaybeInfo, NoSolutionOrRerunNonErased, QueryResultOrRerunNonErased, + RerunNonErased, RerunReason, RerunResultExt, SizedTraitKind, StalledOnCoroutines, }; use rustc_type_ir::{ self as ty, AliasTy, Interner, MayBeErased, TypeFlags, TypeFoldable, TypeFolder, @@ -65,7 +65,7 @@ where goal: Goal, assumption: I::Clause, requirements: impl IntoIterator)>, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { Self::probe_and_match_goal_against_assumption(ecx, parent_source, goal, assumption, |ecx| { for (nested_source, goal) in requirements { ecx.add_goal(nested_source, goal); @@ -84,7 +84,7 @@ where source: CandidateSource, goal: Goal, assumption: I::Clause, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| { let cx = ecx.cx(); let ty::Dynamic(bounds, _) = goal.predicate.self_ty().kind() else { @@ -136,10 +136,10 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - ) -> Result, CandidateHeadUsages> { + ) -> Result, CandidateHeadUsages>, RerunNonErased> { match Self::fast_reject_assumption(ecx, goal, assumption) { Ok(()) => {} - Err(NoSolution) => return Err(CandidateHeadUsages::default()), + Err(NoSolution) => return Ok(Err(CandidateHeadUsages::default())), } // Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily @@ -155,19 +155,25 @@ where result: *result, }) .enter_single_candidate(|ecx| { - Self::match_assumption(ecx, goal, assumption, |ecx| { - ecx.try_evaluate_added_goals()?; - let (src, certainty) = - ecx.characterize_param_env_assumption(goal.param_env, assumption)?; - source.set(src); - ecx.evaluate_added_goals_and_make_canonical_response(certainty) - }) + Self::match_assumption( + ecx, + goal, + assumption, + |ecx| -> Result<_, NoSolutionOrRerunNonErased> { + ecx.try_evaluate_added_goals()?; + let (src, certainty) = + ecx.characterize_param_env_assumption(goal.param_env, assumption)?; + source.set(src); + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + }, + ) + .map_err(Into::into) }); - match result.map_err(Into::into) { + Ok(match result.map_err_to_rerun()? { Ok(result) => Ok(Candidate { source: source.get(), result, head_usages }), Err(NoSolution) => Err(head_usages), - } + }) } /// Try equating an assumption predicate against a goal's predicate. If it @@ -179,8 +185,8 @@ where source: CandidateSource, goal: Goal, assumption: I::Clause, - then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> Result, NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResultOrRerunNonErased, + ) -> Result, NoSolutionOrRerunNonErased> { Self::fast_reject_assumption(ecx, goal, assumption)?; ecx.probe_trait_candidate(source) @@ -200,15 +206,15 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> QueryResult; + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResultOrRerunNonErased, + ) -> QueryResultOrRerunNonErased; fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, impl_def_id: I::ImplId, - then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult, - ) -> Result, NoSolution>; + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResultOrRerunNonErased, + ) -> Result, NoSolutionOrRerunNonErased>; /// If the predicate contained an error, we want to avoid emitting unnecessary trait /// errors but still want to emit errors for other trait goals. We have some special @@ -219,7 +225,7 @@ where fn consider_error_guaranteed_candidate( ecx: &mut EvalCtxt<'_, D>, guar: I::ErrorGuaranteed, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// A type implements an `auto trait` if its components do as well. /// @@ -228,13 +234,13 @@ where fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// A trait alias holds if the RHS traits and `where` clauses hold. fn consider_trait_alias_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// A type is `Sized` if its tail component is `Sized` and a type is `MetaSized` if its tail /// component is `MetaSized`. @@ -245,7 +251,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, sizedness: SizedTraitKind, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. /// @@ -254,13 +260,13 @@ where fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// A type is a `FnPtr` if it is of `FnPtr` type. fn consider_builtin_fn_ptr_trait_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn` /// family of traits where `A` is given by the signature of the type. @@ -268,7 +274,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, kind: ty::ClosureKind, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// An async closure is known to implement the `AsyncFn` family of traits /// where `A` is given by the signature of the type. @@ -276,7 +282,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, kind: ty::ClosureKind, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// Compute the built-in logic of the `AsyncFnKindHelper` helper trait, which /// is used internally to delay computation for async closures until after @@ -284,13 +290,13 @@ where fn consider_builtin_async_fn_kind_helper_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// `Tuple` is implemented if the `Self` type is a tuple. fn consider_builtin_tuple_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// `Pointee` is always implemented. /// @@ -300,7 +306,7 @@ where fn consider_builtin_pointee_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// A coroutine (that comes from an `async` desugaring) is known to implement /// `Future`, where `O` is given by the coroutine's return type @@ -308,7 +314,7 @@ where fn consider_builtin_future_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// A coroutine (that comes from a `gen` desugaring) is known to implement /// `Iterator`, where `O` is given by the generator's yield type @@ -316,19 +322,19 @@ where fn consider_builtin_iterator_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// A coroutine (that comes from a `gen` desugaring) is known to implement /// `FusedIterator` fn consider_builtin_fused_iterator_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// A coroutine (that doesn't come from an `async` or `gen` desugaring) is known to /// implement `Coroutine`, given the resume, yield, @@ -336,27 +342,27 @@ where fn consider_builtin_coroutine_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; fn consider_builtin_transmute_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; /// Consider (possibly several) candidates to upcast or unsize a type to another /// type, excluding the coercion of a sized type into a `dyn Trait`. @@ -368,12 +374,12 @@ where fn consider_structural_builtin_unsize_candidates( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Vec>; + ) -> Result>, RerunNonErased>; fn consider_builtin_field_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution>; + ) -> Result, NoSolutionOrRerunNonErased>; } /// Allows callers of `assemble_and_evaluate_candidates` to choose whether to limit @@ -425,14 +431,14 @@ where &mut self, goal: Goal, assemble_from: AssembleCandidatesFrom, - ) -> (Vec>, FailedCandidateInfo) { + ) -> Result<(Vec>, FailedCandidateInfo), RerunNonErased> { 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 @@ -440,8 +446,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 @@ -452,15 +458,15 @@ 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); - self.assemble_param_env_candidates(goal, &mut candidates, &mut failed_candidate_info); + self.assemble_alias_bound_candidates(goal, &mut candidates)?; + self.assemble_param_env_candidates(goal, &mut candidates, &mut failed_candidate_info)?; match assemble_from { AssembleCandidatesFrom::All => { - self.assemble_builtin_impl_candidates(goal, &mut candidates); + self.assemble_builtin_impl_candidates(goal, &mut candidates)?; // For performance we only assemble impls if there are no candidates // which would shadow them. This is necessary to avoid hangs in rayon, // see trait-system-refactor-initiative#109 for more details. @@ -487,7 +493,7 @@ where }), }; if assemble_impls { - self.assemble_impl_candidates(goal, &mut candidates); + self.assemble_impl_candidates(goal, &mut candidates)?; self.assemble_object_bound_candidates(goal, &mut candidates); } } @@ -503,13 +509,13 @@ where } } - (candidates, failed_candidate_info) + Ok((candidates, failed_candidate_info)) } pub(super) fn forced_ambiguity( &mut self, maybe: MaybeInfo, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { // This may fail if `try_evaluate_added_goals` overflows because it // fails to reach a fixpoint but ends up getting an error after // running for some additional step. @@ -529,26 +535,30 @@ where &mut self, goal: Goal, candidates: &mut Vec>, - ) { + ) -> Result<(), RerunNonErased> { let cx = self.cx(); cx.for_each_relevant_impl( goal.predicate.trait_def_id(cx), goal.predicate.self_ty(), - |impl_def_id| { + |impl_def_id| -> Result<_, _> { // For every `default impl`, there's always a non-default `impl` // that will *also* apply. There's no reason to register a candidate // for this impl, since it is *not* proof that the trait goal holds. if cx.impl_is_default(impl_def_id) { - return; + return Ok(()); } match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { ecx.evaluate_added_goals_and_make_canonical_response(certainty) - }) { + }) + .map_err_to_rerun()? + { Ok(candidate) => candidates.push(candidate), - Err(NoSolution) => (), + Err(NoSolution) => {} } + + Ok(()) }, - ); + ) } #[instrument(level = "trace", skip_all)] @@ -556,7 +566,7 @@ where &mut self, goal: Goal, candidates: &mut Vec>, - ) { + ) -> Result<(), RerunNonErased> { let cx = self.cx(); let trait_def_id = goal.predicate.trait_def_id(cx); @@ -653,7 +663,7 @@ where G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal) } Some(SolverTraitLangItem::Field) => G::consider_builtin_field_candidate(self, goal), - _ => Err(NoSolution), + _ => Err(NoSolution.into()), } }; @@ -662,8 +672,10 @@ where // There may be multiple unsize candidates for a trait with several supertraits: // `trait Foo: Bar + Bar` and `dyn Foo: Unsize>` if cx.is_trait_lang_item(trait_def_id, SolverTraitLangItem::Unsize) { - candidates.extend(G::consider_structural_builtin_unsize_candidates(self, goal)); + candidates.extend(G::consider_structural_builtin_unsize_candidates(self, goal)?); } + + Ok(()) } #[instrument(level = "trace", skip_all)] @@ -672,15 +684,17 @@ where goal: Goal, candidates: &mut Vec>, failed_candidate_info: &mut FailedCandidateInfo, - ) { + ) -> Result<(), RerunNonErased> { for assumption in goal.param_env.caller_bounds().iter() { - match G::probe_and_consider_param_env_candidate(self, goal, assumption) { + match G::probe_and_consider_param_env_candidate(self, goal, assumption)? { Ok(candidate) => candidates.push(candidate), Err(head_usages) => { failed_candidate_info.param_env_head_usages.merge_usages(head_usages) } } } + + Ok(()) } #[instrument(level = "trace", skip_all)] @@ -688,21 +702,22 @@ where &mut self, goal: Goal, candidates: &mut Vec>, - ) { + ) -> Result<(), RerunNonErased> { let res = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { ecx.assemble_alias_bound_candidates_recur( goal.predicate.self_ty(), goal, candidates, AliasBoundKind::SelfBounds, - ); + )?; Ok(()) }); // always returns Ok match res { - Ok(_) | Err(NoSolutionOrOpaquesAccessed::OpaquesAccessed) => {} - Err(NoSolutionOrOpaquesAccessed::NoSolution(NoSolution)) => { + Ok(_) => Ok(()), + Err(NoSolutionOrRerunNonErased::RerunNonErased(e)) => Err(e), + Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => { unreachable!() } } @@ -723,7 +738,7 @@ where goal: Goal, candidates: &mut Vec>, consider_self_bounds: AliasBoundKind, - ) { + ) -> Result<(), RerunNonErased> { let alias_ty = match self_ty.kind() { ty::Bool | ty::Char @@ -751,7 +766,7 @@ where | ty::Param(_) | ty::Placeholder(..) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Error(_) => return, + | ty::Error(_) => return Ok(()), ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) | ty::Bound(..) => { panic!("unexpected self type for `{goal:?}`") } @@ -769,7 +784,7 @@ where head_usages: CandidateHeadUsages::default(), }); } - return; + return Ok(()); } ty::Alias( @@ -777,7 +792,7 @@ where ) => alias_ty, ty::Alias(AliasTy { kind: ty::Inherent { .. } | ty::Free { .. }, .. }) => { self.cx().delay_bug(format!("could not normalize {self_ty:?}, it is not WF")); - return; + return Ok(()); } }; @@ -819,7 +834,7 @@ where candidates.extend(G::consider_additional_alias_assumptions(self, goal, alias_ty)); if !matches!(alias_ty.kind, ty::Projection { .. }) { - return; + return Ok(()); } // Recurse on the self type of the projection. @@ -830,7 +845,8 @@ where candidates, AliasBoundKind::NonSelfBounds, ), - Err(NoSolution) => {} + Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Ok(()), + Err(NoSolutionOrRerunNonErased::RerunNonErased(e)) => Err(e), } } @@ -932,12 +948,12 @@ where fn consider_coherence_unknowable_candidate>( &mut self, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter(|ecx| { let cx = ecx.cx(); let trait_ref = goal.predicate.trait_ref(cx); if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? { - Err(NoSolution) + Err(NoSolution.into()) } else { // While the trait bound itself may be unknowable, we may be able to // prove that a super trait is not implemented. For this, we recursively @@ -1032,7 +1048,7 @@ where goal: Goal, assemble_from: AssembleCandidatesFrom, candidates: &mut Vec>, - ) { + ) -> Result<(), RerunNonErased> { let self_ty = goal.predicate.self_ty(); // We only use this hack during HIR typeck. let opaque_types = match self.typing_mode() { @@ -1043,14 +1059,14 @@ where | TypingMode::PostAnalysis => vec![], TypingMode::ErasedNotCoherence(MayBeErased) => { self.opaque_accesses - .rerun_if_any_opaque_has_infer_as_hidden_type(RerunReason::SelfTyInfer); + .rerun_if_any_opaque_has_infer_as_hidden_type(RerunReason::SelfTyInfer)?; Vec::new() } }; if opaque_types.is_empty() { candidates.extend(self.forced_ambiguity(MaybeInfo::AMBIGUOUS)); - return; + return Ok(()); } for &alias_ty in &opaque_types { @@ -1115,7 +1131,7 @@ where // that will *also* apply. There's no reason to register a candidate // for this impl, since it is *not* proof that the trait goal holds. if cx.impl_is_default(impl_def_id) { - return; + return Ok(()); } match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { @@ -1131,11 +1147,15 @@ where // other inference variables. Err(NoSolution.into()) } - }) { + }) + .map_err_to_rerun()? + { Ok(candidate) => candidates.push(candidate), - Err(NoSolution) => (), + Err(NoSolution) => {} } - }); + + Ok(()) + })?; } if candidates.is_empty() { @@ -1150,6 +1170,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 @@ -1187,9 +1209,18 @@ where &mut self, proven_via: Option, goal: Goal, - inject_forced_ambiguity_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> Option>, - inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> QueryResult { + inject_forced_ambiguity_candidate: impl FnOnce( + &mut EvalCtxt<'_, D>, + ) -> Option< + Result, NoSolutionOrRerunNonErased>, + >, + inject_normalize_to_rigid_candidate: impl FnOnce( + &mut EvalCtxt<'_, D>, + ) -> Result< + CanonicalResponse, + NoSolutionOrRerunNonErased, + >, + ) -> QueryResultOrRerunNonErased { let Some(proven_via) = proven_via else { // We don't care about overflow. If proving the trait goal overflowed, then // it's enough to report an overflow error for that, we don't also have to @@ -1206,7 +1237,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 @@ -1230,12 +1261,12 @@ where if let Some((response, _)) = self.try_merge_candidates(&candidates) { Ok(response) } else { - self.flounder(&candidates) + self.flounder(&candidates).map_err(Into::into) } } 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. @@ -1252,7 +1283,7 @@ where if let Some((response, _)) = self.try_merge_candidates(&candidates) { Ok(response) } else { - self.flounder(&candidates) + self.flounder(&candidates).map_err(Into::into) } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 0053e25e6365a..660f1ec706aa8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -970,7 +970,7 @@ where && self .ecx .probe(|_| ProbeKind::ProjectionCompatibility) - .enter_without_propagated_nested_goals(|ecx| -> Result<_, NoSolution> { + .enter_without_propagated_nested_goals(|ecx| { let source_projection = ecx.instantiate_binder_with_infer(source_projection); ecx.eq(self.param_env, source_projection.projection_term, target_projection)?; ecx.try_evaluate_added_goals() 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 508287a4dfee6..ccd5c649d4c9e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -5,15 +5,17 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::solve::inspect::ProbeKind; -use rustc_type_ir::solve::{AliasBoundKind, SizedTraitKind}; +use rustc_type_ir::solve::{ + AliasBoundKind, NoSolutionOrRerunNonErased, QueryResultOrRerunNonErased, RerunNonErased, + SizedTraitKind, +}; use rustc_type_ir::{self as ty, Interner, Unnormalized, elaborate}; use tracing::instrument; use super::assembly::{Candidate, structural_traits}; use crate::delegate::SolverDelegate; use crate::solve::{ - BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution, - QueryResult, assembly, + BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution, assembly, }; impl assembly::GoalKind for ty::HostEffectPredicate @@ -60,8 +62,8 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> QueryResult { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResultOrRerunNonErased, + ) -> QueryResultOrRerunNonErased { let host_clause = assumption.as_host_effect_clause().unwrap(); let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause); @@ -128,32 +130,32 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, impl_def_id: I::ImplId, - then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult, - ) -> Result, NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResultOrRerunNonErased, + ) -> Result, NoSolutionOrRerunNonErased> { let cx = ecx.cx(); let impl_trait_ref = cx.impl_trait_ref(impl_def_id); if !DeepRejectCtxt::relate_rigid_infer(ecx.cx()) .args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) { - return Err(NoSolution); + return Err(NoSolution.into()); } let impl_polarity = cx.impl_polarity(impl_def_id); let certainty = match impl_polarity { - ty::ImplPolarity::Negative => return Err(NoSolution), + ty::ImplPolarity::Negative => return Err(NoSolution.into()), ty::ImplPolarity::Reservation => { if ecx.typing_mode().is_coherence() { Certainty::AMBIGUOUS } else { - return Err(NoSolution); + return Err(NoSolution.into()); } } ty::ImplPolarity::Positive => Certainty::Yes, }; if !cx.impl_is_const(impl_def_id) { - return Err(NoSolution); + return Err(NoSolution.into()); } ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { @@ -190,7 +192,7 @@ where fn consider_error_guaranteed_candidate( ecx: &mut EvalCtxt<'_, D>, _guar: I::ErrorGuaranteed, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } @@ -198,15 +200,15 @@ where fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { ecx.cx().delay_bug("auto traits are never const"); - Err(NoSolution) + Err(NoSolution.into()) } fn consider_trait_alias_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let cx = ecx.cx(); ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { @@ -242,14 +244,14 @@ where _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, _sizedness: SizedTraitKind, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("Sized/MetaSized is never const") } fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let cx = ecx.cx(); let self_ty = goal.predicate.self_ty(); @@ -278,7 +280,7 @@ where fn consider_builtin_fn_ptr_trait_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { todo!("Fn* are not yet const") } @@ -287,7 +289,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, _kind: rustc_type_ir::ClosureKind, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let cx = ecx.cx(); let self_ty = goal.predicate.self_ty(); @@ -329,83 +331,84 @@ where pred, requirements, ) + .map_err(Into::into) } fn consider_builtin_async_fn_trait_candidates( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, _kind: rustc_type_ir::ClosureKind, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { todo!("AsyncFn* are not yet const") } fn consider_builtin_async_fn_kind_helper_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("AsyncFnKindHelper is not const") } fn consider_builtin_tuple_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("Tuple trait is not const") } fn consider_builtin_pointee_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("Pointee is not const") } fn consider_builtin_future_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("Future is not const") } fn consider_builtin_iterator_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { todo!("Iterator is not yet const") } fn consider_builtin_fused_iterator_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("FusedIterator is not const") } fn consider_builtin_async_iterator_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("AsyncIterator is not const") } fn consider_builtin_coroutine_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("Coroutine is not const") } fn consider_builtin_discriminant_kind_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("DiscriminantKind is not const") } fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let cx = ecx.cx(); let self_ty = goal.predicate.self_ty(); @@ -429,28 +432,28 @@ where fn consider_builtin_transmute_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("TransmuteFrom is not const") } fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("BikeshedGuaranteedNoDrop is not const"); } fn consider_structural_builtin_unsize_candidates( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Vec> { + ) -> Result>, RerunNonErased> { unreachable!("Unsize is not const") } fn consider_builtin_field_candidate( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal<::Interner, Self>, - ) -> Result::Interner>, NoSolution> { + ) -> Result::Interner>, NoSolutionOrRerunNonErased> { unreachable!("Field is not const") } } @@ -464,12 +467,17 @@ where pub(super) fn compute_host_effect_goal( &mut self, goal: Goal>, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { let trait_goal: Goal> = goal.with(ecx.cx(), goal.predicate.trait_ref); - ecx.compute_trait_goal(trait_goal) + ecx.compute_trait_goal(trait_goal).map_err(Into::into) })?; - self.assemble_and_merge_candidates(proven_via, goal, |_ecx| None, |_ecx| Err(NoSolution)) + self.assemble_and_merge_candidates( + proven_via, + goal, + |_ecx| None, + |_ecx| Err(NoSolution.into()), + ) } } 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 a03d3182b9fed..227d091b5c2f7 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,8 +9,9 @@ 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, FetchEligibleAssocItemResponse, MaybeInfo, OpaqueTypesJank, RerunCondition, - RerunReason, SmallCopyList, + AccessedOpaques, FetchEligibleAssocItemResponse, MaybeInfo, NoSolutionOrRerunNonErased, + OpaqueTypesJank, QueryResultOrRerunNonErased, RerunCondition, RerunNonErased, RerunReason, + RerunResultExt, SmallCopyList, }; use rustc_type_ir::{ self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, MayBeErased, @@ -214,9 +215,17 @@ where span: I::Span, stalled_on: Option>, ) -> Result, NoSolution> { - EvalCtxt::enter_root(self, self.cx().recursion_limit(), span, |ecx| { + let result = EvalCtxt::enter_root(self, self.cx().recursion_limit(), span, |ecx| { ecx.evaluate_goal(GoalSource::Misc, goal, stalled_on) - }) + }); + + match result { + Ok(i) => Ok(i), + Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution), + Err(NoSolutionOrRerunNonErased::RerunNonErased(_)) => { + unreachable!("this never happens at the root, we're never in erased mode here"); + } + } } #[instrument(level = "debug", skip(self), ret)] @@ -371,7 +380,10 @@ where search_graph: &'a mut SearchGraph, canonical_input: CanonicalInput, proof_tree_builder: &mut inspect::ProofTreeBuilder, - f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal) -> Result, + 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() { @@ -427,7 +439,19 @@ where // FIXME: Could we make `build_with_canonical` into `enter_with_canonical` and call this at the end? delegate.reset_opaque_types(); - (result, ecx.opaque_accesses) + let opaque_accesses = ecx.opaque_accesses; + ( + match result { + Ok(i) => Ok(i), + Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution), + Err(NoSolutionOrRerunNonErased::RerunNonErased(_)) => { + // check th t the opaque_accesses state mirrors the result we got. + assert!(opaque_accesses.should_bail().is_err()); + Err(NoSolution) + } + }, + opaque_accesses, + ) } pub(super) fn ignore_candidate_head_usages(&mut self, usages: CandidateHeadUsages) { @@ -441,7 +465,7 @@ where source: GoalSource, goal: Goal, stalled_on: Option>, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let (normalization_nested_goals, goal_evaluation) = self.evaluate_goal_raw(source, goal, stalled_on)?; assert!(normalization_nested_goals.is_empty()); @@ -460,7 +484,7 @@ where source: GoalSource, goal: Goal, stalled_on: Option>, - ) -> Result<(NestedNormalizationGoals, GoalEvaluation), NoSolution> { + ) -> Result<(NestedNormalizationGoals, GoalEvaluation), NoSolutionOrRerunNonErased> { // If we have run this goal before, and it was stalled, check that any of the goal's // args have changed. Otherwise, we don't need to re-run the goal because it'll remain // stalled, since it'll canonicalize the same way and evaluation is pure. @@ -532,9 +556,7 @@ where 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); + match self.opaque_accesses.rerun_always(RerunReason::SkipErasedAttempt)? {} } else { debug!("running in original typing mode"); } @@ -566,7 +588,7 @@ where break 'retry_canonicalize (canonical_result, orig_values, canonical_goal); } RerunDecision::EagerlyPropagateToParent => { - self.opaque_accesses.update(accessed_opaques); + self.opaque_accesses.update(accessed_opaques)?; break 'retry_canonicalize (canonical_result, orig_values, canonical_goal); } } @@ -590,7 +612,16 @@ where }; debug!(?result); - let response = result?; + let response = match result { + Ok(response) => { + debug!("success"); + response + } + Err(NoSolution) => { + debug!("normal failure"); + return Err(NoSolution.into()); + } + }; drop(tracing_span); @@ -786,72 +817,81 @@ where res } - pub(super) fn compute_goal(&mut self, goal: Goal) -> QueryResult { + pub(super) fn compute_goal( + &mut self, + goal: Goal, + ) -> QueryResultOrRerunNonErased { let Goal { param_env, predicate } = goal; let kind = predicate.kind(); - self.enter_forall(kind, |ecx, kind| match kind { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { - ecx.compute_trait_goal(Goal { param_env, predicate }).map(|(r, _via)| r) - } - ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { - ecx.compute_host_effect_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => { - ecx.compute_projection_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => { - ecx.compute_type_outlives_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => { - ecx.compute_region_outlives_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { - ecx.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) }) - } - ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { - ecx.compute_unstable_feature_goal(param_env, symbol) - } - ty::PredicateKind::Subtype(predicate) => { - ecx.compute_subtype_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Coerce(predicate) => { - ecx.compute_coerce_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::DynCompatible(trait_def_id) => { - ecx.compute_dyn_compatible_goal(trait_def_id) - } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { - ecx.compute_well_formed_goal(Goal { param_env, predicate: term }) - } - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { - ecx.compute_const_evaluatable_goal(Goal { param_env, predicate: ct }) - } - ty::PredicateKind::ConstEquate(_, _) => { - panic!("ConstEquate should not be emitted when `-Znext-solver` is active") - } - ty::PredicateKind::NormalizesTo(predicate) => { - ecx.compute_normalizes_to_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::AliasRelate(lhs, rhs, direction) => { - ecx.compute_alias_relate_goal(Goal { param_env, predicate: (lhs, rhs, direction) }) - } - ty::PredicateKind::Ambiguous => { - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } + self.enter_forall(kind, |ecx, kind| { + Ok(match kind { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { + ecx.compute_trait_goal(Goal { param_env, predicate }).map(|(r, _via)| r)? + } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { + ecx.compute_host_effect_goal(Goal { param_env, predicate })? + } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => { + ecx.compute_projection_goal(Goal { param_env, predicate })? + } + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => { + ecx.compute_type_outlives_goal(Goal { param_env, predicate })? + } + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => { + ecx.compute_region_outlives_goal(Goal { param_env, predicate })? + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { + ecx.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })? + } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { + ecx.compute_unstable_feature_goal(param_env, symbol)? + } + ty::PredicateKind::Subtype(predicate) => { + ecx.compute_subtype_goal(Goal { param_env, predicate })? + } + ty::PredicateKind::Coerce(predicate) => { + ecx.compute_coerce_goal(Goal { param_env, predicate })? + } + ty::PredicateKind::DynCompatible(trait_def_id) => { + ecx.compute_dyn_compatible_goal(trait_def_id)? + } + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + ecx.compute_well_formed_goal(Goal { param_env, predicate: term })? + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { + ecx.compute_const_evaluatable_goal(Goal { param_env, predicate: ct })? + } + ty::PredicateKind::ConstEquate(_, _) => { + panic!("ConstEquate should not be emitted when `-Znext-solver` is active") + } + ty::PredicateKind::NormalizesTo(predicate) => { + ecx.compute_normalizes_to_goal(Goal { param_env, predicate })? + } + ty::PredicateKind::AliasRelate(lhs, rhs, direction) => ecx + .compute_alias_relate_goal(Goal { + param_env, + predicate: (lhs, rhs, direction), + })?, + ty::PredicateKind::Ambiguous => { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)? + } + }) }) } // Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning // the certainty of all the goals. #[instrument(level = "trace", skip(self))] - pub(super) fn try_evaluate_added_goals(&mut self) -> Result { + pub(super) fn try_evaluate_added_goals( + &mut self, + ) -> Result { for _ in 0..FIXPOINT_STEP_LIMIT { - match self.evaluate_added_goals_step() { + match self.evaluate_added_goals_step().map_err_to_rerun()? { Ok(None) => {} Ok(Some(cert)) => return Ok(cert), Err(NoSolution) => { self.tainted = Err(NoSolution); - return Err(NoSolution); + return Err(NoSolution.into()); } } } @@ -863,7 +903,9 @@ where /// Iterate over all added goals: returning `Ok(Some(_))` in case we can stop rerunning. /// /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`. - fn evaluate_added_goals_step(&mut self) -> Result, NoSolution> { + fn evaluate_added_goals_step( + &mut self, + ) -> Result, NoSolutionOrRerunNonErased> { let cx = self.cx(); // If this loop did not result in any progress, what's our final certainty. let mut unchanged_certainty = Some(Certainty::Yes); @@ -1347,7 +1389,7 @@ where &mut self, param_env: I::ParamEnv, trait_ref: ty::TraitRef, - ) -> Result { + ) -> Result { let delegate = self.delegate; let lazily_normalize_ty = |ty| self.structurally_normalize_ty(param_env, ty); coherence::trait_ref_is_knowable(&**delegate, trait_ref, lazily_normalize_ty) @@ -1397,21 +1439,20 @@ where &mut self, param_env: I::ParamEnv, uv: ty::UnevaluatedConst, - ) -> Option { + ) -> Result, RerunNonErased> { if self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always(RerunReason::EvaluateConst); - return None; + self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)?; } - self.delegate.evaluate_const(param_env, uv) + Ok(self.delegate.evaluate_const(param_env, uv)) } pub(super) fn evaluate_const_and_instantiate_normalizes_to_term( &mut self, goal: Goal>, uv: ty::UnevaluatedConst, - ) -> QueryResult { - match self.evaluate_const(goal.param_env, uv) { + ) -> QueryResultOrRerunNonErased { + match self.evaluate_const(goal.param_env, uv)? { Some(evaluated) => { self.instantiate_normalizes_to_term(goal, evaluated.into()); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -1465,13 +1506,12 @@ where &mut self, param_env: I::ParamEnv, symbol: I::Symbol, - ) -> bool { + ) -> Result { if self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always(RerunReason::MayUseUnstableFeature); - return false; + self.opaque_accesses.rerun_always(RerunReason::MayUseUnstableFeature)?; } - may_use_unstable_feature(&**self.delegate, param_env, symbol) + Ok(may_use_unstable_feature(&**self.delegate, param_env, symbol)) } pub(crate) fn opaques_with_sub_unified_hidden_type( @@ -1502,7 +1542,7 @@ where pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( &mut self, shallow_certainty: Certainty, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { self.inspect.make_canonical_response(shallow_certainty); let goals_certainty = self.try_evaluate_added_goals()?; 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 3660b95e464bc..1c5d6e0b14c65 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,9 @@ use std::marker::PhantomData; use rustc_type_ir::search_graph::CandidateHeadUsages; -use rustc_type_ir::solve::{AccessedOpaques, CanonicalResponse, NoSolutionOrOpaquesAccessed}; +use rustc_type_ir::solve::{ + AccessedOpaques, CanonicalResponse, NoSolutionOrRerunNonErased, RerunResultExt, +}; use rustc_type_ir::{InferCtxtLike, Interner}; use tracing::{instrument, warn}; @@ -30,12 +32,12 @@ where { pub(in crate::solve) fn enter_single_candidate( self, - f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result, - ) -> (Result, CandidateHeadUsages) { + f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result, + ) -> (Result, CandidateHeadUsages) { let mut candidate_usages = CandidateHeadUsages::default(); - if self.ecx.opaque_accesses.should_bail() { - return (Err(NoSolutionOrOpaquesAccessed::OpaquesAccessed), candidate_usages); + if let Err(e) = self.ecx.opaque_accesses.should_bail() { + return (Err(e.into()), candidate_usages); } self.ecx.search_graph.enter_single_candidate(); @@ -49,29 +51,27 @@ where pub(in crate::solve) fn enter( self, - f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result, - ) -> Result { + 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>) -> Result, - ) -> Result { + f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result, + ) -> Result { self.enter_inner(f, Default::default()) } pub(in crate::solve) fn enter_inner( self, - f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result, + 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(NoSolutionOrOpaquesAccessed::OpaquesAccessed); - } + outer.opaque_accesses.should_bail()?; let delegate = outer.delegate; let max_input_universe = outer.max_input_universe; @@ -95,14 +95,20 @@ where nested.inspect.probe_final_state(delegate, max_input_universe); r }); + + outer.opaque_accesses.update(nested.opaque_accesses)?; + + let r = match r.map_err_to_rerun()? { + Ok(i) => Ok(i), + Err(NoSolution) => Err(NoSolution), + }; + if !nested.inspect.is_noop() { let probe_kind = probe_kind(&r); nested.inspect.probe_kind(probe_kind); outer.inspect = nested.inspect.finish_probe(); } - outer.opaque_accesses.update(nested.opaque_accesses); - r.map_err(Into::into) } } @@ -125,8 +131,8 @@ where #[instrument(level = "debug", skip_all, fields(source = ?self.source))] pub(in crate::solve) fn enter( self, - f: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> Result, NoSolution> { + f: impl FnOnce(&mut EvalCtxt<'_, D>) -> Result, NoSolutionOrRerunNonErased>, + ) -> Result, NoSolutionOrRerunNonErased> { let (result, head_usages) = self.cx.enter_single_candidate(f); Ok(Candidate { source: self.source, result: result?, head_usages }) } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 5dc3162fbd2a7..be2c44cdca89f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -89,7 +89,7 @@ where fn compute_type_outlives_goal( &mut self, goal: Goal>, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { let ty::OutlivesPredicate(ty, lt) = goal.predicate; self.register_ty_outlives(ty, lt); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -99,14 +99,17 @@ where fn compute_region_outlives_goal( &mut self, goal: Goal>, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { let ty::OutlivesPredicate(a, b) = goal.predicate; self.register_region_outlives(a, b, VisibleForLeakCheck::Yes); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } #[instrument(level = "trace", skip(self))] - fn compute_coerce_goal(&mut self, goal: Goal>) -> QueryResult { + fn compute_coerce_goal( + &mut self, + goal: Goal>, + ) -> QueryResultOrRerunNonErased { self.compute_subtype_goal(Goal { param_env: goal.param_env, predicate: ty::SubtypePredicate { @@ -118,7 +121,10 @@ where } #[instrument(level = "trace", skip(self))] - fn compute_subtype_goal(&mut self, goal: Goal>) -> QueryResult { + fn compute_subtype_goal( + &mut self, + goal: Goal>, + ) -> QueryResultOrRerunNonErased { match (goal.predicate.a.kind(), goal.predicate.b.kind()) { (ty::Infer(ty::TyVar(a_vid)), ty::Infer(ty::TyVar(b_vid))) => { self.sub_unify_ty_vids_raw(a_vid, b_vid); @@ -131,16 +137,22 @@ where } } - fn compute_dyn_compatible_goal(&mut self, trait_def_id: I::TraitId) -> QueryResult { + fn compute_dyn_compatible_goal( + &mut self, + trait_def_id: I::TraitId, + ) -> QueryResultOrRerunNonErased { if self.cx().trait_is_dyn_compatible(trait_def_id) { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { - Err(NoSolution) + Err(NoSolution.into()) } } #[instrument(level = "trace", skip(self))] - fn compute_well_formed_goal(&mut self, goal: Goal) -> QueryResult { + fn compute_well_formed_goal( + &mut self, + goal: Goal, + ) -> QueryResultOrRerunNonErased { match self.well_formed_goals(goal.param_env, goal.predicate) { Some(goals) => { self.add_goals(GoalSource::Misc, goals); @@ -154,8 +166,8 @@ where &mut self, param_env: ::ParamEnv, symbol: ::Symbol, - ) -> QueryResult { - if self.may_use_unstable_feature(param_env, symbol) { + ) -> QueryResultOrRerunNonErased { + if self.may_use_unstable_feature(param_env, symbol)? { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) @@ -166,7 +178,7 @@ where fn compute_const_evaluatable_goal( &mut self, Goal { param_env, predicate: ct }: Goal, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { match ct.kind() { ty::ConstKind::Unevaluated(uv) => { // We never return `NoSolution` here as `evaluate_const` emits an @@ -177,7 +189,7 @@ where // FIXME(generic_const_exprs): Implement handling for generic // const expressions here. - if let Some(_normalized) = self.evaluate_const(param_env, uv) { + if let Some(_normalized) = self.evaluate_const(param_env, uv)? { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) @@ -203,16 +215,20 @@ where fn compute_const_arg_has_type_goal( &mut self, goal: Goal, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { let (ct, ty) = goal.predicate; let ct = self.structurally_normalize_const(goal.param_env, ct)?; let ct_ty = match ct.kind() { ty::ConstKind::Infer(_) => { - return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + .map_err(Into::into); } ty::ConstKind::Error(_) => { - return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + .map_err(Into::into); } ty::ConstKind::Unevaluated(uv) => { self.cx().type_of(uv.def.into()).instantiate(self.cx(), uv.args).skip_norm_wip() @@ -231,7 +247,7 @@ where }; self.eq(goal.param_env, ct_ty, ty)?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into) } } @@ -308,7 +324,7 @@ where &mut self, param_env: I::ParamEnv, ty: I::Ty, - ) -> Result { + ) -> Result { self.structurally_normalize_term(param_env, ty.into()).map(|term| term.expect_ty()) } @@ -323,7 +339,7 @@ where &mut self, param_env: I::ParamEnv, ct: I::Const, - ) -> Result { + ) -> Result { self.structurally_normalize_term(param_env, ct.into()).map(|term| term.expect_const()) } @@ -335,7 +351,7 @@ where &mut self, param_env: I::ParamEnv, term: I::Term, - ) -> Result { + ) -> Result { if let Some(_) = term.to_alias_term(self.cx()) { let normalized_term = self.next_term_infer_of_kind(term); let alias_relate_goal = Goal::new( diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs index 44b8929c45f43..b76f38b46a3df 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs @@ -1,8 +1,9 @@ +use rustc_type_ir::solve::QueryResultOrRerunNonErased; use rustc_type_ir::{self as ty, Interner}; use tracing::instrument; use crate::delegate::SolverDelegate; -use crate::solve::{EvalCtxt, Goal, QueryResult}; +use crate::solve::{EvalCtxt, Goal}; impl EvalCtxt<'_, D> where @@ -14,7 +15,7 @@ where &mut self, goal: Goal>, def_id: I::UnevaluatedConstId, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { let uv = goal.predicate.alias.expect_ct(self.cx()); self.evaluate_const_and_instantiate_normalizes_to_term(goal, uv) } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs index f70f4bede33db..d68c7dd11d1d9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -4,10 +4,11 @@ //! Since a free alias is never ambiguous, this just computes the `type_of` of //! the alias and registers the where-clauses of the type alias. +use rustc_type_ir::solve::QueryResultOrRerunNonErased; use rustc_type_ir::{self as ty, Interner, Unnormalized}; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; +use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource}; impl EvalCtxt<'_, D> where @@ -17,7 +18,7 @@ where pub(super) fn normalize_free_alias( &mut self, goal: Goal>, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { let cx = self.cx(); let free_alias = goal.predicate.alias; diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index d9eeb3e90f95f..2f44cc42a5d71 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -5,10 +5,11 @@ //! 2. equate the self type, and //! 3. instantiate and register where clauses. +use rustc_type_ir::solve::QueryResultOrRerunNonErased; use rustc_type_ir::{self as ty, Interner, Unnormalized}; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; +use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource}; impl EvalCtxt<'_, D> where @@ -19,7 +20,7 @@ where &mut self, goal: Goal>, def_id: I::InherentAssocTermId, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { let cx = self.cx(); let inherent = goal.predicate.alias; diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index ce9663666ed90..e9a4d7e5919ad 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,10 @@ mod opaque_types; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; -use rustc_type_ir::solve::{FetchEligibleAssocItemResponse, RerunReason}; +use rustc_type_ir::solve::{ + FetchEligibleAssocItemResponse, NoSolutionOrRerunNonErased, QueryResultOrRerunNonErased, + RerunNonErased, RerunReason, RerunResultExt, +}; use rustc_type_ir::{ self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Unnormalized, Upcast as _, }; @@ -18,7 +21,7 @@ use crate::solve::assembly::{self, Candidate}; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeInfo, - NoSolution, QueryResult, SizedTraitKind, + NoSolution, SizedTraitKind, }; impl EvalCtxt<'_, D> @@ -30,7 +33,7 @@ where pub(super) fn compute_normalizes_to_goal( &mut self, goal: Goal>, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { debug_assert!(self.term_is_fully_unconstrained(goal)); match goal.predicate.alias.kind { ty::AliasTermKind::ProjectionTy { .. } | ty::AliasTermKind::ProjectionConst { .. } => { @@ -44,15 +47,18 @@ where } ty::AliasTermKind::OpaqueTy { def_id } => self.normalize_opaque_type(goal, def_id), ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } => { - self.normalize_free_alias(goal) + self.normalize_free_alias(goal).map_err(Into::into) } ty::AliasTermKind::UnevaluatedConst { def_id } => { - self.normalize_anon_const(goal, def_id) + self.normalize_anon_const(goal, def_id).map_err(Into::into) } } } - fn normalize_associated_term(&mut self, goal: Goal>) -> QueryResult { + fn normalize_associated_term( + &mut self, + goal: Goal>, + ) -> QueryResultOrRerunNonErased { let cx = self.cx(); let trait_ref = goal.predicate.alias.trait_ref(cx); @@ -85,7 +91,12 @@ where )); } } - Err(NoSolution) => return Some(Err(NoSolution)), + Err( + e @ (NoSolutionOrRerunNonErased::NoSolution(NoSolution) + | NoSolutionOrRerunNonErased::RerunNonErased(_)), + ) => { + return Some(Err(e)); + } } } @@ -174,8 +185,8 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> QueryResult { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResultOrRerunNonErased, + ) -> QueryResultOrRerunNonErased { let cx = ecx.cx(); let projection_pred = assumption.as_projection_clause().unwrap(); let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); @@ -204,7 +215,7 @@ where source: CandidateSource, goal: Goal, assumption: I::Clause, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) @@ -222,8 +233,8 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal>, impl_def_id: I::ImplId, - then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult, - ) -> Result, NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResultOrRerunNonErased, + ) -> Result, NoSolutionOrRerunNonErased> { let cx = ecx.cx(); let goal_trait_ref = goal.predicate.alias.trait_ref(cx); @@ -232,13 +243,13 @@ where goal.predicate.alias.trait_ref(cx).args, impl_trait_ref.skip_binder().args, ) { - return Err(NoSolution); + return Err(NoSolution.into()); } // We have to ignore negative impls when projecting. let impl_polarity = cx.impl_polarity(impl_def_id); match impl_polarity { - ty::ImplPolarity::Negative => return Err(NoSolution), + ty::ImplPolarity::Negative => return Err(NoSolution.into()), ty::ImplPolarity::Reservation => { unimplemented!("reservation impl for trait with assoc item: {:?}", goal) } @@ -307,7 +318,8 @@ where ty::TypingMode::Coherence => { ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + .map_err(Into::into); } // Outside of coherence, we treat the associated item as rigid instead. ty::TypingMode::Analysis { .. } @@ -319,14 +331,15 @@ where goal.predicate.alias, ); return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + .map_err(Into::into); } }; } FetchEligibleAssocItemResponse::Err(guar) => return error_response(ecx, guar), FetchEligibleAssocItemResponse::NotFoundBecauseErased => { - ecx.opaque_accesses.rerun_always(RerunReason::FetchEligibleAssocItem); - return Err(NoSolution); + ecx.opaque_accesses.rerun_always(RerunReason::FetchEligibleAssocItem)?; + return Err(NoSolution.into()); } }; @@ -349,10 +362,10 @@ where // This is not the case here and we only prefer adding an ambiguous // nested goal for consistency. ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); - return then(ecx, Certainty::Yes); + return then(ecx, Certainty::Yes).map_err(Into::into); } else { ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); - return then(ecx, Certainty::Yes); + return then(ecx, Certainty::Yes).map_err(Into::into); } } else { return error_response(ecx, cx.delay_bug("missing item")); @@ -412,7 +425,7 @@ where }; ecx.instantiate_normalizes_to_term(goal, term); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into) }) } @@ -421,22 +434,22 @@ where fn consider_error_guaranteed_candidate( _ecx: &mut EvalCtxt<'_, D>, _guar: I::ErrorGuaranteed, - ) -> Result, NoSolution> { - Err(NoSolution) + ) -> Result, NoSolutionOrRerunNonErased> { + Err(NoSolution.into()) } fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, D>, _goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { ecx.cx().delay_bug("associated types not allowed on auto traits"); - Err(NoSolution) + Err(NoSolution.into()) } fn consider_trait_alias_candidate( _ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { panic!("trait aliases do not have associated types: {:?}", goal); } @@ -444,21 +457,21 @@ where _ecx: &mut EvalCtxt<'_, D>, goal: Goal, _sizedness: SizedTraitKind, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { panic!("`Sized`/`MetaSized` does not have an associated type: {:?}", goal); } fn consider_builtin_copy_clone_candidate( _ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { panic!("`Copy`/`Clone` does not have an associated type: {:?}", goal); } fn consider_builtin_fn_ptr_trait_candidate( _ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { panic!("`FnPtr` does not have an associated type: {:?}", goal); } @@ -466,7 +479,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, goal_kind: ty::ClosureKind, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let cx = ecx.cx(); let Some(tupled_inputs_and_output) = structural_traits::extract_tupled_inputs_and_output_from_callable( @@ -501,13 +514,14 @@ where pred, [(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))], ) + .map_err(Into::into) } fn consider_builtin_async_fn_trait_candidates( ecx: &mut EvalCtxt<'_, D>, goal: Goal, goal_kind: ty::ClosureKind, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let cx = ecx.cx(); let def_id = goal.predicate.def_id().try_into().unwrap(); @@ -590,7 +604,7 @@ where fn consider_builtin_async_fn_kind_helper_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let [ closure_fn_kind_ty, goal_kind_ty, @@ -610,13 +624,13 @@ where let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else { // We don't need to worry about the self type being an infer var. - return Err(NoSolution); + return Err(NoSolution.into()); }; let Some(goal_kind) = goal_kind_ty.expect_ty().to_opt_closure_kind() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; if !closure_kind.extends(goal_kind) { - return Err(NoSolution); + return Err(NoSolution.into()); } let upvars_ty = ty::CoroutineClosureSignature::tupled_upvars_by_closure_kind( @@ -637,14 +651,14 @@ where fn consider_builtin_tuple_candidate( _ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { panic!("`Tuple` does not have an associated type: {:?}", goal); } fn consider_builtin_pointee_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let cx = ecx.cx(); let metadata_def_id = cx.require_projection_lang_item(SolverProjectionLangItem::Metadata); assert_eq!(Into::::into(metadata_def_id), goal.predicate.def_id()); @@ -695,6 +709,12 @@ where ecx.instantiate_normalizes_to_term(goal, Ty::new_unit(cx).into()); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }); + + let alias_bound_result = match alias_bound_result.map_err_to_rerun()? { + Ok(i) => Ok(i), + Err(NoSolution) => Err(NoSolution), + }; + // In case the dummy alias-bound candidate does not apply, we instead treat this projection // as rigid. return alias_bound_result.or_else(|NoSolution| { @@ -744,16 +764,16 @@ where fn consider_builtin_future_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let self_ty = goal.predicate.self_ty(); let ty::Coroutine(def_id, args) = self_ty.kind() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; // Coroutines are not futures unless they come from `async` desugaring let cx = ecx.cx(); if !cx.coroutine_is_async(def_id) { - return Err(NoSolution); + return Err(NoSolution.into()); } let term = args.as_coroutine().return_ty().into(); @@ -780,16 +800,16 @@ where fn consider_builtin_iterator_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let self_ty = goal.predicate.self_ty(); let ty::Coroutine(def_id, args) = self_ty.kind() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; // Coroutines are not Iterators unless they come from `gen` desugaring let cx = ecx.cx(); if !cx.coroutine_is_gen(def_id) { - return Err(NoSolution); + return Err(NoSolution.into()); } let term = args.as_coroutine().yield_ty().into(); @@ -811,28 +831,29 @@ where // but that's already proven by the generator being WF. [], ) + .map_err(Into::into) } fn consider_builtin_fused_iterator_candidate( _ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { panic!("`FusedIterator` does not have an associated type: {:?}", goal); } fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let self_ty = goal.predicate.self_ty(); let ty::Coroutine(def_id, args) = self_ty.kind() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; // Coroutines are not AsyncIterators unless they come from `gen` desugaring let cx = ecx.cx(); if !cx.coroutine_is_async_gen(def_id) { - return Err(NoSolution); + return Err(NoSolution.into()); } ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { @@ -859,16 +880,16 @@ where fn consider_builtin_coroutine_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let self_ty = goal.predicate.self_ty(); let ty::Coroutine(def_id, args) = self_ty.kind() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; // `async`-desugared coroutines do not implement the coroutine trait let cx = ecx.cx(); if !cx.is_general_coroutine(def_id) { - return Err(NoSolution); + return Err(NoSolution.into()); } let coroutine = args.as_coroutine(); @@ -905,14 +926,14 @@ where fn consider_structural_builtin_unsize_candidates( _ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Vec> { + ) -> Result>, RerunNonErased> { panic!("`Unsize` does not have an associated type: {:?}", goal); } fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let self_ty = goal.predicate.self_ty(); let discriminant_ty = match self_ty.kind() { ty::Bool @@ -971,35 +992,35 @@ where fn consider_builtin_destruct_candidate( _ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { panic!("`Destruct` does not have an associated type: {:?}", goal); } fn consider_builtin_transmute_candidate( _ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { panic!("`TransmuteFrom` does not have an associated type: {:?}", goal) } fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( _ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal) } fn consider_builtin_field_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let self_ty = goal.predicate.self_ty(); let ty::Adt(def, args) = self_ty.kind() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; let Some(FieldInfo { base, ty, .. }) = def.field_representing_type_info(ecx.cx(), args) else { - return Err(NoSolution); + return Err(NoSolution.into()); }; let ty = match ecx.cx().as_projection_lang_item(goal.predicate.def_id().try_into().unwrap()) { 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 f25ac21f4a62b..57d10b4ac1fe1 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,11 +2,11 @@ //! behaves differently depending on the current `TypingMode`. use rustc_type_ir::inherent::*; -use rustc_type_ir::solve::{GoalSource, NoSolution, RerunReason}; +use rustc_type_ir::solve::{GoalSource, QueryResultOrRerunNonErased, RerunReason}; use rustc_type_ir::{self as ty, Interner, MayBeErased, TypingMode, fold_regions}; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; +use crate::solve::{Certainty, EvalCtxt, Goal}; impl EvalCtxt<'_, D> where @@ -18,7 +18,7 @@ where &mut self, goal: Goal>, def_id: I::OpaqueTyId, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { let cx = self.cx(); let opaque_ty = goal.predicate.alias; let expected = goal.predicate.term.as_type().expect("no such thing as an opaque const"); @@ -39,6 +39,7 @@ where // This can then allow nested goals to fail after we've constrained the `term`. self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous)); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + .map_err(Into::into) } TypingMode::Analysis { defining_opaque_types_and_generators: defining_opaque_types, @@ -50,7 +51,9 @@ where else { // If we're not in the defining scope, treat the alias as rigid. self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); - return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + .map_err(Into::into); }; // We structurally normalize the args so that we're able to detect defining uses @@ -108,6 +111,7 @@ where expected, ); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + .map_err(Into::into) } TypingMode::PostBorrowckAnalysis { defined_opaque_types } => { let Some(def_id) = opaque_ty @@ -116,7 +120,9 @@ where .filter(|&def_id| defined_opaque_types.contains(&def_id)) else { self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); - return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + .map_err(Into::into); }; let actual = @@ -130,6 +136,7 @@ where }); self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + .map_err(Into::into) } TypingMode::PostAnalysis => { // FIXME: Add an assertion that opaque type storage is empty. @@ -137,6 +144,7 @@ where cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args).skip_norm_wip(); self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + .map_err(Into::into) } TypingMode::ErasedNotCoherence(MayBeErased) => { let def_id = opaque_ty.def_id().as_local(); @@ -151,20 +159,16 @@ where self.opaque_accesses.rerun_if_opaque_in_opaque_type_storage( RerunReason::NormalizeOpaqueType, def_id, - ); + )?; } else { self.opaque_accesses - .rerun_if_in_post_analysis(RerunReason::NormalizeOpaqueTypeRemoteCrate); - } - if self.opaque_accesses.should_bail() { - // If we already accessed opaque types once, bail. - // We can't make it more precise - return Err(NoSolution); + .rerun_if_in_post_analysis(RerunReason::NormalizeOpaqueTypeRemoteCrate)?; } // 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) + .map_err(Into::into) } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs index 944d5f0e042d7..af6d0aad25597 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs @@ -1,8 +1,9 @@ +use rustc_type_ir::solve::QueryResultOrRerunNonErased; use rustc_type_ir::{self as ty, Interner, ProjectionPredicate}; use tracing::instrument; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; +use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource}; impl EvalCtxt<'_, D> where @@ -13,7 +14,7 @@ where pub(super) fn compute_projection_goal( &mut self, goal: Goal>, - ) -> QueryResult { + ) -> QueryResultOrRerunNonErased { let cx = self.cx(); let projection_term = goal.predicate.projection_term.to_term(cx); let goal = goal.with( 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 45a32e51ed3f5..a46261fcf7271 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::{AccessedOpaques, CanonicalInput, Certainty, NoSolution, QueryResult}; +use rustc_type_ir::solve::{ + AccessedOpaques, CanonicalInput, Certainty, NoSolution, NoSolutionOrRerunNonErased, QueryResult, +}; use rustc_type_ir::{Interner, MayBeErased, TypingMode}; use crate::canonical::response_no_constraints_raw; @@ -139,8 +141,19 @@ where ensure_sufficient_stack(|| { EvalCtxt::enter_canonical(cx, search_graph, input, inspect, |ecx, goal| { let result = ecx.compute_goal(goal); + + // if we're in `RerunNonErased`, don't even bother with inspect, + // and immediately return + let result = match result { + Ok(i) => Ok(i), + Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution), + Err(NoSolutionOrRerunNonErased::RerunNonErased(e)) => { + return Err(e.into()); + } + }; + ecx.inspect.query_result(result); - result + result.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 a4f35f763e8e2..7e56dcb02591d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -5,8 +5,9 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::solve::{ - AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, MaybeInfo, OpaqueTypesJank, - RerunReason, SizedTraitKind, + AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, MaybeInfo, + NoSolutionOrRerunNonErased, OpaqueTypesJank, QueryResultOrRerunNonErased, RerunNonErased, + RerunReason, RerunResultExt, SizedTraitKind, }; use rustc_type_ir::{ self as ty, FieldInfo, Interner, MayBeErased, Movability, PredicatePolarity, TraitPredicate, @@ -22,7 +23,7 @@ use crate::solve::assembly::{ use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, - MergeCandidateInfo, NoSolution, ParamEnvSource, QueryResult, StalledOnCoroutines, + MergeCandidateInfo, NoSolution, ParamEnvSource, StalledOnCoroutines, has_only_region_constraints, }; @@ -59,15 +60,15 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal>, impl_def_id: I::ImplId, - then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult, - ) -> Result, NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResultOrRerunNonErased, + ) -> Result, NoSolutionOrRerunNonErased> { let cx = ecx.cx(); let impl_trait_ref = cx.impl_trait_ref(impl_def_id); if !DeepRejectCtxt::relate_rigid_infer(ecx.cx()) .args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) { - return Err(NoSolution); + return Err(NoSolution.into()); } // An upper bound of the certainty of this goal, used to lower the certainty @@ -79,7 +80,7 @@ where if ecx.typing_mode().is_coherence() { Certainty::AMBIGUOUS } else { - return Err(NoSolution); + return Err(NoSolution.into()); } } @@ -90,7 +91,7 @@ where // Impl doesn't match polarity (ty::ImplPolarity::Positive, ty::PredicatePolarity::Negative) | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Positive) => { - return Err(NoSolution); + return Err(NoSolution.into()); } }; @@ -118,14 +119,14 @@ where .map(|pred| goal.with(cx, pred)), ); - then(ecx, maximal_certainty) + then(ecx, maximal_certainty).map_err(Into::into) }) } fn consider_error_guaranteed_candidate( ecx: &mut EvalCtxt<'_, D>, _guar: I::ErrorGuaranteed, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } @@ -174,8 +175,8 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> QueryResult { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResultOrRerunNonErased, + ) -> QueryResultOrRerunNonErased { let trait_clause = assumption.as_trait_clause().unwrap(); // PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so @@ -200,10 +201,10 @@ where fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let cx = ecx.cx(); if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) { @@ -215,7 +216,7 @@ where if cx.trait_is_unsafe(goal.predicate.def_id()) && goal.predicate.self_ty().has_unsafe_fields() { - return Err(NoSolution); + return Err(NoSolution.into()); } // We leak the implemented auto traits of opaques outside of their defining scope. @@ -237,8 +238,8 @@ where goal.predicate.self_ty().kind() { if ecx.opaque_accesses.might_rerun() { - ecx.opaque_accesses.rerun_always(RerunReason::AutoTraitLeakage); - return Err(NoSolution); + ecx.opaque_accesses.rerun_always(RerunReason::AutoTraitLeakage)?; + return Err(NoSolution.into()); } debug_assert!(ecx.opaque_type_is_rigid(def_id)); @@ -247,7 +248,7 @@ where .as_trait_clause() .is_some_and(|b| b.def_id() == goal.predicate.def_id()) { - return Err(NoSolution); + return Err(NoSolution.into()); } } } @@ -267,9 +268,9 @@ where fn consider_trait_alias_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } let cx = ecx.cx(); @@ -294,9 +295,9 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, sizedness: SizedTraitKind, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } ecx.probe_and_evaluate_goal_for_constituent_tys( @@ -313,9 +314,9 @@ where fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } // We need to make sure to stall any coroutines we are inferring to avoid query cycles. @@ -333,7 +334,7 @@ where fn consider_builtin_fn_ptr_trait_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let self_ty = goal.predicate.self_ty(); match goal.predicate.polarity { // impl FnPtr for FnPtr {} @@ -343,7 +344,7 @@ where ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } else { - Err(NoSolution) + Err(NoSolution.into()) } } // impl !FnPtr for T where T != FnPtr && T is rigid {} @@ -355,7 +356,7 @@ where ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } else { - Err(NoSolution) + Err(NoSolution.into()) } } } @@ -365,9 +366,9 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, goal_kind: ty::ClosureKind, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } let cx = ecx.cx(); @@ -397,15 +398,16 @@ where pred, [(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))], ) + .map_err(Into::into) } fn consider_builtin_async_fn_trait_candidates( ecx: &mut EvalCtxt<'_, D>, goal: Goal, goal_kind: ty::ClosureKind, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } let cx = ecx.cx(); @@ -447,26 +449,27 @@ where .chain(nested_preds.into_iter().map(|pred| goal.with(cx, pred))) .map(|goal| (GoalSource::ImplWhereBound, goal)), ) + .map_err(Into::into) } fn consider_builtin_async_fn_kind_helper_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let [closure_fn_kind_ty, goal_kind_ty] = *goal.predicate.trait_ref.args.as_slice() else { panic!(); }; let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else { // We don't need to worry about the self type being an infer var. - return Err(NoSolution); + return Err(NoSolution.into()); }; let goal_kind = goal_kind_ty.expect_ty().to_opt_closure_kind().unwrap(); if closure_kind.extends(goal_kind) { ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } else { - Err(NoSolution) + Err(NoSolution.into()) } } @@ -479,25 +482,25 @@ where fn consider_builtin_tuple_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } if let ty::Tuple(..) = goal.predicate.self_ty().kind() { ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } else { - Err(NoSolution) + Err(NoSolution.into()) } } fn consider_builtin_pointee_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) @@ -507,19 +510,19 @@ where fn consider_builtin_future_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; // Coroutines are not futures unless they come from `async` desugaring let cx = ecx.cx(); if !cx.coroutine_is_async(def_id) { - return Err(NoSolution); + return Err(NoSolution.into()); } // Async coroutine unconditionally implement `Future` @@ -532,19 +535,19 @@ where fn consider_builtin_iterator_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; // Coroutines are not iterators unless they come from `gen` desugaring let cx = ecx.cx(); if !cx.coroutine_is_gen(def_id) { - return Err(NoSolution); + return Err(NoSolution.into()); } // Gen coroutines unconditionally implement `Iterator` @@ -557,19 +560,19 @@ where fn consider_builtin_fused_iterator_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; // Coroutines are not iterators unless they come from `gen` desugaring let cx = ecx.cx(); if !cx.coroutine_is_gen(def_id) { - return Err(NoSolution); + return Err(NoSolution.into()); } // Gen coroutines unconditionally implement `FusedIterator`. @@ -580,19 +583,19 @@ where fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; // Coroutines are not iterators unless they come from `gen` desugaring let cx = ecx.cx(); if !cx.coroutine_is_async_gen(def_id) { - return Err(NoSolution); + return Err(NoSolution.into()); } // Gen coroutines unconditionally implement `Iterator` @@ -605,20 +608,20 @@ where fn consider_builtin_coroutine_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } let self_ty = goal.predicate.self_ty(); let ty::Coroutine(def_id, args) = self_ty.kind() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; // `async`-desugared coroutines do not implement the coroutine trait let cx = ecx.cx(); if !cx.is_general_coroutine(def_id) { - return Err(NoSolution); + return Err(NoSolution.into()); } let coroutine = args.as_coroutine(); @@ -637,9 +640,9 @@ where fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } // `DiscriminantKind` is automatically implemented for every type. @@ -650,9 +653,9 @@ where fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } // `Destruct` is automatically implemented for every type in @@ -664,14 +667,14 @@ where fn consider_builtin_transmute_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } // `rustc_transmute` does not have support for type or const params if goal.predicate.has_non_region_placeholders() { - return Err(NoSolution); + return Err(NoSolution.into()); } // Match the old solver by treating unresolved inference variables as @@ -680,19 +683,21 @@ where return ecx.forced_ambiguity(MaybeInfo::AMBIGUOUS); } - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - let assume = ecx.structurally_normalize_const( - goal.param_env, - goal.predicate.trait_ref.args.const_at(2), - )?; + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter( + |ecx| -> Result<_, NoSolutionOrRerunNonErased> { + let assume = ecx.structurally_normalize_const( + goal.param_env, + goal.predicate.trait_ref.args.const_at(2), + )?; - let certainty = ecx.is_transmutable( - goal.predicate.trait_ref.args.type_at(0), - goal.predicate.trait_ref.args.type_at(1), - assume, - )?; - ecx.evaluate_added_goals_and_make_canonical_response(certainty) - }) + let certainty = ecx.is_transmutable( + goal.predicate.trait_ref.args.type_at(0), + goal.predicate.trait_ref.args.type_at(1), + assume, + )?; + ecx.evaluate_added_goals_and_make_canonical_response(certainty).map_err(Into::into) + }, + ) } /// NOTE: This is implemented as a built-in goal and not a set of impls like: @@ -710,9 +715,9 @@ where fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } let cx = ecx.cx(); @@ -803,13 +808,13 @@ where fn consider_structural_builtin_unsize_candidates( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Vec> { + ) -> Result>, RerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return vec![]; + return Ok(vec![]); } let result = ecx.probe(|_| ProbeKind::UnsizeAssembly).enter( - |ecx| -> Result>, NoSolution> { + |ecx| -> Result>, NoSolutionOrRerunNonErased> { let a_ty = goal.predicate.self_ty(); // We need to normalize the b_ty since it's matched structurally // in the other functions below. @@ -849,23 +854,23 @@ where Ok(vec![ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args)?]) } - _ => Err(NoSolution), + _ => Err(NoSolution.into()), } }, ); - match result.map_err(Into::into) { - Ok(resp) => resp, - Err(NoSolution) => vec![], + match result.map_err_to_rerun()? { + Ok(resp) => Ok(resp), + Err(NoSolution) => Ok(vec![]), } } fn consider_builtin_field_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); + return Err(NoSolution.into()); } if let ty::Adt(def, args) = goal.predicate.self_ty().kind() && let Some(FieldInfo { base, ty, .. }) = @@ -904,7 +909,7 @@ where ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } else { - Err(NoSolution) + Err(NoSolution.into()) } } } @@ -995,13 +1000,13 @@ where goal: Goal, b_data: I::BoundExistentialPredicates, b_region: I::Region, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let cx = self.cx(); let Goal { predicate: (a_ty, _), .. } = goal; // Can only unsize to an dyn-compatible trait. if b_data.principal_def_id().is_some_and(|def_id| !cx.trait_is_dyn_compatible(def_id)) { - return Err(NoSolution); + return Err(NoSolution.into()); } self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { @@ -1040,7 +1045,7 @@ where b_data: I::BoundExistentialPredicates, b_region: I::Region, upcast_principal: Option>>, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let param_env = goal.param_env; // We may upcast to auto traits that are either explicitly listed in @@ -1066,13 +1071,14 @@ where source_projection.item_def_id() == target_projection.item_def_id() && ecx .probe(|_| ProbeKind::ProjectionCompatibility) - .enter(|ecx| -> Result<_, NoSolution> { + .enter(|ecx| { ecx.enter_forall(target_projection, |ecx, target_projection| { let source_projection = ecx.instantiate_binder_with_infer(source_projection); ecx.eq(param_env, source_projection, target_projection)?; ecx.try_evaluate_added_goals() }) + .map_err(Into::into) }) .is_ok() }; @@ -1104,12 +1110,14 @@ where projection_may_match(ecx, *source_projection, target_projection) }); let Some(source_projection) = matching_projections.next() else { - return Err(NoSolution); + return Err(NoSolution.into()); }; if matching_projections.next().is_some() { - return ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ); + return ecx + .evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ) + .map_err(Into::into); } ecx.enter_forall(target_projection, |ecx, target_projection| { let source_projection = @@ -1121,7 +1129,7 @@ where // Check that b_ty's auto traits are present in a_ty's bounds. ty::ExistentialPredicate::AutoTrait(def_id) => { if !a_auto_traits.contains(&def_id) { - return Err(NoSolution); + return Err(NoSolution.into()); } } } @@ -1133,7 +1141,7 @@ where Goal::new(ecx.cx(), param_env, ty::OutlivesPredicate(a_region, b_region)), ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into) }) } @@ -1150,7 +1158,7 @@ where goal: Goal, a_elem_ty: I::Ty, b_elem_ty: I::Ty, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { self.eq(goal.param_env, a_elem_ty, b_elem_ty)?; self.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) @@ -1175,7 +1183,7 @@ where def: I::AdtDef, a_args: I::GenericArgs, b_args: I::GenericArgs, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { let cx = self.cx(); let Goal { predicate: (_a_ty, b_ty), .. } = goal; @@ -1183,7 +1191,7 @@ where // We must be unsizing some type parameters. This also implies // that the struct has a tail field. if unsizing_params.is_empty() { - return Err(NoSolution); + return Err(NoSolution.into()); } let tail_field_ty = def.struct_tail_ty(cx).unwrap(); @@ -1224,7 +1232,7 @@ where fn disqualify_auto_trait_candidate_due_to_possible_impl( &mut self, goal: Goal>, - ) -> Option, NoSolution>> { + ) -> Option, NoSolutionOrRerunNonErased>> { let self_ty = goal.predicate.self_ty(); let check_impls = || { let mut disqualifying_impl = None; @@ -1239,7 +1247,7 @@ where trace!(?def_id, ?goal, "disqualified auto-trait implementation"); // No need to actually consider the candidate here, // since we do that in `consider_impl_candidate`. - return Some(Err(NoSolution)); + return Some(Err(NoSolution.into())); } else { None } @@ -1268,7 +1276,7 @@ where kind: ty::Projection { .. } | ty::Free { .. } | ty::Inherent { .. }, .. }) - | ty::Placeholder(..) => Some(Err(NoSolution)), + | ty::Placeholder(..) => Some(Err(NoSolution.into())), ty::Infer(_) | ty::Bound(_, _) => panic!("unexpected type `{self_ty:?}`"), @@ -1281,7 +1289,7 @@ where .is_trait_lang_item(goal.predicate.def_id(), SolverTraitLangItem::Unpin) => { match self.cx().coroutine_movability(def_id) { - Movability::Static => Some(Err(NoSolution)), + Movability::Static => Some(Err(NoSolution.into())), Movability::Movable => Some( self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -1339,7 +1347,7 @@ where &EvalCtxt<'_, D>, I::Ty, ) -> Result>, NoSolution>, - ) -> Result, NoSolution> { + ) -> Result, NoSolutionOrRerunNonErased> { self.probe_trait_candidate(source).enter(|ecx| { let goals = ecx.enter_forall(constituent_tys(ecx, goal.predicate.self_ty())?, |ecx, tys| { @@ -1548,15 +1556,20 @@ where pub(super) fn compute_trait_goal( &mut self, goal: Goal>, - ) -> Result<(CanonicalResponse, Option), NoSolution> { + ) -> Result<(CanonicalResponse, Option), NoSolutionOrRerunNonErased> + { 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) + .map_err(Into::into) } - fn try_stall_coroutine(&mut self, self_ty: I::Ty) -> Option, NoSolution>> { + fn try_stall_coroutine( + &mut self, + self_ty: I::Ty, + ) -> Option, NoSolutionOrRerunNonErased>> { if let ty::Coroutine(def_id, _) = self_ty.kind() { match self.typing_mode() { TypingMode::Analysis { @@ -1573,8 +1586,11 @@ where } TypingMode::ErasedNotCoherence(MayBeErased) => { // Trying to continue here isn't worth it. - self.opaque_accesses.rerun_always(RerunReason::TryStallCoroutine); - return Some(Err(NoSolution)); + return Some( + match self.opaque_accesses.rerun_always(RerunReason::TryStallCoroutine) { + Err(e) => Err(e.into()), + }, + ); } TypingMode::Coherence | TypingMode::PostAnalysis diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index cf9530378dc7c..b136dec792f6a 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::convert::Infallible; use std::fmt::Debug; use std::hash::Hash; @@ -27,38 +28,55 @@ pub type CanonicalResponse = Canonical>; /// having to worry about changes to currently used code. Once we've made progress on this /// solver, merge the two responses again. pub type QueryResult = Result, NoSolution>; +pub type QueryResultOrRerunNonErased = Result, NoSolutionOrRerunNonErased>; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[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, +pub trait RerunResultExt { + fn map_err_to_rerun(self) -> Result, RerunNonErased>; } -/// 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 RerunResultExt for Result { + fn map_err_to_rerun(self) -> Result, RerunNonErased> { + match self { + Ok(i) => Ok(Ok(i)), + Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Ok(Err(NoSolution)), + Err(NoSolutionOrRerunNonErased::RerunNonErased(e)) => Err(e), + } } } -impl From for NoSolutionOrOpaquesAccessed { +/// 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. +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "nightly", derive(StableHash))] +pub struct RerunNonErased(()); + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "nightly", derive(StableHash))] +pub enum NoSolutionOrRerunNonErased { + NoSolution(NoSolution), + RerunNonErased(RerunNonErased), +} + +impl From for NoSolutionOrRerunNonErased { fn from(value: NoSolution) -> Self { Self::NoSolution(value) } } +impl From for NoSolutionOrRerunNonErased { + fn from(value: RerunNonErased) -> Self { + Self::RerunNonErased(value) + } +} + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)] #[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] @@ -216,13 +234,13 @@ impl RerunCondition { } #[must_use] - fn should_bail(&self) -> bool { + fn should_bail(&self) -> Result<(), RerunNonErased> { match self { - Self::Always => true, + Self::Always => Err(RerunNonErased(())), Self::Never | Self::OpaqueInStorage(_) | Self::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(_) - | Self::AnyOpaqueHasInferAsHidden => false, + | Self::AnyOpaqueHasInferAsHidden => Ok(()), } } @@ -274,13 +292,15 @@ impl Default for AccessedOpaques { } impl AccessedOpaques { - pub fn update(&mut self, other: Self) { + pub fn update(&mut self, other: Self) -> Result<(), RerunNonErased> { *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), }; + + self.should_bail() } #[must_use] @@ -289,41 +309,47 @@ impl AccessedOpaques { } #[must_use] - pub fn should_bail(&self) -> bool { + pub fn should_bail(&self) -> Result<(), RerunNonErased> { self.rerun.should_bail() } - pub fn rerun_always(&mut self, reason: RerunReason) { + pub fn rerun_always(&mut self, reason: RerunReason) -> Result { debug!("set rerun always"); - self.update(AccessedOpaques { reason: Some(reason), rerun: RerunCondition::Always }); + match self.update(AccessedOpaques { reason: Some(reason), rerun: RerunCondition::Always }) { + Ok(_) => unreachable!(), + Err(e) => Err(e), + } } - pub fn rerun_if_in_post_analysis(&mut self, reason: RerunReason) { + pub fn rerun_if_in_post_analysis(&mut self, reason: RerunReason) -> Result<(), RerunNonErased> { debug!("set rerun if post analysis"); self.update(AccessedOpaques { reason: Some(reason), rerun: RerunCondition::OpaqueInStorage(SmallCopyList::empty()), - }); + }) } pub fn rerun_if_opaque_in_opaque_type_storage( &mut self, reason: RerunReason, defid: I::LocalDefId, - ) { + ) -> Result<(), RerunNonErased> { debug!("set rerun if opaque type {defid:?} in storage"); self.update(AccessedOpaques { reason: Some(reason), rerun: RerunCondition::OpaqueInStorage(SmallCopyList::new(defid)), - }); + }) } - pub fn rerun_if_any_opaque_has_infer_as_hidden_type(&mut self, reason: RerunReason) { + pub fn rerun_if_any_opaque_has_infer_as_hidden_type( + &mut self, + reason: RerunReason, + ) -> Result<(), RerunNonErased> { 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-fulldeps/rustc-dev-remap.only-remap.stderr b/tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr index 4a6ca56bf3293..da83d2038bb73 100644 --- a/tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr +++ b/tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr @@ -16,6 +16,9 @@ help: the following other types implement trait `VisitorResult` ::: /rustc-dev/xyz/compiler/rustc_ast_ir/src/visit.rs:LL:COL | = note: `ControlFlow` + ::: /rustc-dev/xyz/compiler/rustc_ast_ir/src/visit.rs:LL:COL + | + = note: `Result<(), E>` note: required by a bound in `rustc_ast::visit::Visitor::Result` --> /rustc-dev/xyz/compiler/rustc_ast/src/visit.rs:LL:COL ::: /rustc-dev/xyz/compiler/rustc_ast/src/visit.rs:LL:COL diff --git a/tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr b/tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr index 18dffdb2cf795..f9bcc4ef4d8a6 100644 --- a/tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr +++ b/tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr @@ -17,6 +17,9 @@ LL | impl VisitorResult for () { ... LL | impl VisitorResult for ControlFlow { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ControlFlow` +... +LL | impl VisitorResult for Result<(), E> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Result<(), E>` note: required by a bound in `rustc_ast::visit::Visitor::Result` --> $COMPILER_DIR_REAL/rustc_ast/src/visit.rs:LL:COL |