diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 12124f14a821f..c6947bc16cfcd 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -843,7 +843,7 @@ fn test_unstable_options_tracking_hash() { tracked!(thir_unsafeck, true); tracked!(tiny_const_eval_limit, true); tracked!(tls_model, Some(TlsModel::GeneralDynamic)); - tracked!(trait_solver, TraitSolver::NextCoherence); + tracked!(trait_solver, TraitSolver::Next); tracked!(translate_remapped_path_to_local_path, false); tracked!(trap_unreachable, Some(false)); tracked!(treat_err_as_bug, NonZeroUsize::new(1)); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index ff433fdf16dc4..47cc45c590989 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -989,7 +989,7 @@ mod parse { Some("next") => *slot = TraitSolver::Next, Some("next-coherence") => *slot = TraitSolver::NextCoherence, // default trait solver is subject to change.. - Some("default") => *slot = TraitSolver::Classic, + Some("default") => *slot = TraitSolver::NextCoherence, _ => return false, } true @@ -1817,8 +1817,8 @@ written to standard error output)"), "for every macro invocation, print its name and arguments (default: no)"), track_diagnostics: bool = (false, parse_bool, [UNTRACKED], "tracks where in rustc a diagnostic was emitted"), - trait_solver: TraitSolver = (TraitSolver::Classic, parse_trait_solver, [TRACKED], - "specify the trait solver mode used by rustc (default: classic)"), + trait_solver: TraitSolver = (TraitSolver::NextCoherence, parse_trait_solver, [TRACKED], + "specify the trait solver mode used by rustc (default: next-coherence)"), // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved // alongside query results and changes to translation options can affect diagnostics - so // translation options should be tracked. diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 6920e790e71ae..3e13d93ac6916 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -889,20 +889,48 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { SolverMode::Normal => return, SolverMode::Coherence => { let trait_ref = goal.predicate.trait_ref(self.tcx()); - match coherence::trait_ref_is_knowable(self.tcx(), trait_ref) { - Ok(()) => {} - Err(_) => match self - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - { - Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), - result, - }), - // FIXME: This will be reachable at some point if we're in - // `assemble_candidates_after_normalizing_self_ty` and we get a - // universe error. We'll deal with it at this point. - Err(NoSolution) => bug!("coherence candidate resulted in NoSolution"), - }, + // will assemble in `assemble_candidates_after_normalizing_self_ty` + if matches!(trait_ref.self_ty().kind(), ty::Alias(..)) { + return; + } + let result = self.probe_candidate("unknowable candidate").enter(|ecx| { + let mut args = trait_ref.args.to_vec(); + for arg in args.iter_mut().skip(1) { + let Some(ty) = arg.as_type() else { + continue; + }; + let Some(normalized_ty) = ecx.normalize_non_self_ty(ty, goal.param_env)? + else { + return Ok(ecx + .evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ) + .unwrap()); + }; + *arg = normalized_ty.into(); + } + match coherence::trait_ref_is_knowable(ecx.tcx(), trait_ref) { + Ok(()) => Err(NoSolution), + Err(_) => { + match ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ) { + Ok(result) => Ok(result), + // FIXME: This will be reachable at some point if we're in + // `assemble_candidates_after_normalizing_self_ty` and we get a + // universe error. We'll deal with it at this point. + Err(NoSolution) => { + bug!("coherence candidate resulted in NoSolution") + } + } + } + } + }); + if let Ok(result) = result { + candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + result, + }); } } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 6e0aa08c307bd..11bc6b45f68a1 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -373,6 +373,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { && has_changed && is_normalizes_to_hack == IsNormalizesToHack::No && !self.search_graph.in_cycle() + && false { debug!("rerunning goal to check result is stable"); let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index f1d3091225c0f..0da9ee2294d37 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -89,7 +89,12 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let mut errors = Vec::new(); for i in 0.. { if !infcx.tcx.recursion_limit().value_within_limit(i) { - unimplemented!("overflowed on pending obligations: {:?}", self.obligations); + let obligation = self.obligations.first().cloned().unwrap(); + return vec![FulfillmentError { + root_obligation: obligation.clone(), + obligation, + code: FulfillmentErrorCode::CodeAmbiguity { overflow: true }, + }]; } let mut has_changed = false; diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index 98c8a74752c00..601752b95d463 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -9,9 +9,8 @@ use cache::ProvisionalCache; use overflow::OverflowData; use rustc_index::IndexVec; use rustc_middle::dep_graph::DepKind; -use rustc_middle::traits::solve::{ - CanonicalInput, Certainty, EvaluationCache, MaybeCause, QueryResult, -}; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; use rustc_middle::ty::TyCtxt; use std::{collections::hash_map::Entry, mem}; @@ -146,11 +145,7 @@ impl<'tcx> SearchGraph<'tcx> { { Err(cache.provisional_result(entry_index)) } else { - Err(super::response_no_constraints( - tcx, - input, - Certainty::Maybe(MaybeCause::Overflow), - )) + Err(Err(NoSolution)) } } } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 930e62d638831..a66900c842a11 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,6 +1,7 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. use super::assembly::{self, structural_traits}; +use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; @@ -750,4 +751,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_candidates(candidates) } + + /// Normalize a non-self type when it is structually matched on when solving + /// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty` + /// for the self type, but for other goals, additional normalization of other + /// arguments may be needed to completely implement the semantics of the trait. + /// + /// This is required when structurally matching on any trait argument that is + /// not the self type. + pub(super) fn normalize_non_self_ty( + &mut self, + mut ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Result>, NoSolution> { + if !matches!(ty.kind(), ty::Alias(..)) { + return Ok(Some(ty)); + } + + self.repeat_while_none( + |_| Ok(None), + |ecx| { + let ty::Alias(_, projection_ty) = *ty.kind() else { + return Some(Ok(Some(ty))); + }; + + let normalized_ty = ecx.next_ty_infer(); + let normalizes_to_goal = Goal::new( + ecx.tcx(), + param_env, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty, + term: normalized_ty.into(), + }), + ); + ecx.add_goal(normalizes_to_goal); + if let Err(err) = ecx.try_evaluate_added_goals() { + return Some(Err(err)); + } + + ty = ecx.resolve_vars_if_possible(normalized_ty); + None + }, + ) + } }