diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index dbbd3906b5258..3b6ef9b6ad1db 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -653,10 +653,13 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) { .map(|feat| feat.attr_sp) { #[allow(rustc::symbol_intern_string_literal)] - sess.dcx().emit_err(errors::IncompatibleFeatures { - spans: vec![gce_span], - f1: Symbol::intern("-Znext-solver=globally"), - f2: sym::generic_const_exprs, - }); + sess.dcx() + .create_fatal(errors::IncompatibleFeatures { + spans: vec![gce_span], + f1: Symbol::intern("-Znext-solver=globally"), + f2: sym::generic_const_exprs, + }) + .with_code(rustc_errors::E0001) + .emit(); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index d04133ccee976..1bc09af96d402 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -235,6 +235,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_place_expr_if_unsized(fn_input_ty, arg_expr); } + let formal_input_tys_ns; + let formal_input_tys = if self.next_trait_solver() { + // In the new solver, the normalizations are done lazily. + // Because of this, if we encounter unnormalized alias types inside this + // fudge scope, we might lose the relationships between them and other vars + // when fudging inference variables created here. + // So, we utilize generalization to normalize aliases by adding a new + // inference var and equating it with the type we want to pull out of the + // fudge scope. + formal_input_tys_ns = formal_input_tys + .iter() + .map(|&ty| { + // If we replace a (unresolved) inference var with a new inference + // var, it will be eventually resolved to itself and this will + // weaken type inferences as the new inference var will be fudged + // out and lose all relationships with other vars while the former + // will not be fudged. + if ty.is_ty_var() { + return ty; + } + + let generalized_ty = self.next_ty_var(call_span); + self.demand_eqtype(call_span, ty, generalized_ty); + generalized_ty + }) + .collect_vec(); + + formal_input_tys_ns.as_slice() + } else { + formal_input_tys + }; + // First, let's unify the formal method signature with the expectation eagerly. // We use this to guide coercion inference; it's output is "fudged" which means // any remaining type variables are assigned to new, unrelated variables. This @@ -257,39 +289,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // No argument expectations are produced if unification fails. let origin = self.misc(call_span); ocx.sup(&origin, self.param_env, expected_output, formal_output)?; - - let formal_input_tys_ns; - let formal_input_tys = if self.next_trait_solver() { - // In the new solver, the normalizations are done lazily. - // Because of this, if we encounter unnormalized alias types inside this - // fudge scope, we might lose the relationships between them and other vars - // when fudging inference variables created here. - // So, we utilize generalization to normalize aliases by adding a new - // inference var and equating it with the type we want to pull out of the - // fudge scope. - formal_input_tys_ns = formal_input_tys - .iter() - .map(|&ty| { - // If we replace a (unresolved) inference var with a new inference - // var, it will be eventually resolved to itself and this will - // weaken type inferences as the new inference var will be fudged - // out and lose all relationships with other vars while the former - // will not be fudged. - if ty.is_ty_var() { - return ty; - } - - let generalized_ty = self.next_ty_var(call_span); - ocx.eq(&origin, self.param_env, ty, generalized_ty).unwrap(); - generalized_ty - }) - .collect_vec(); - - formal_input_tys_ns.as_slice() - } else { - formal_input_tys - }; - if !ocx.try_evaluate_obligations().is_empty() { return Err(TypeError::Mismatch); } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 471bd1d937e94..8161db282a141 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -3475,7 +3475,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn next_trait_solver_globally(self) -> bool { - self.sess.opts.unstable_opts.next_solver.globally + self.sess.opts.unstable_opts.next_solver.globally && !self.features().generic_const_exprs() } pub fn next_trait_solver_in_coherence(self) -> bool { 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 8d0a3ac94d5a3..5c79518d49e9a 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 @@ -201,7 +201,7 @@ where span: I::Span, stalled_on: Option>, ) -> Result, NoSolution> { - EvalCtxt::enter_root(self, self.cx().recursion_limit(), span, |ecx| { + EvalCtxt::enter_root(self, self.cx().recursion_limit() * 2, span, |ecx| { ecx.evaluate_goal(GoalSource::Misc, goal, stalled_on) }) } @@ -1485,7 +1485,7 @@ pub fn evaluate_root_goal_for_proof_tree_raw_provider< let mut inspect = inspect::ProofTreeBuilder::new(); let canonical_result = SearchGraph::::evaluate_root_goal_for_proof_tree( cx, - cx.recursion_limit(), + cx.recursion_limit() * 2, canonical_goal, &mut inspect, ); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index be4b36e3b926c..c9e47f20847ce 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -991,7 +991,7 @@ pub struct NextSolverConfig { pub coherence: bool = true, /// Whether the new trait solver should be enabled everywhere. /// This is only `true` if `coherence` is also enabled. - pub globally: bool = false, + pub globally: bool = true, } #[derive(Clone)] diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 8e6376b22ce61..7c58cd7303ebc 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -916,10 +916,9 @@ impl, X: Cx> SearchGraph { /// heads from the stack. This may not necessarily mean that we've actually /// reached a fixpoint for that cycle head, which impacts the way we rebase /// provisional cache entries. -#[derive_where(Debug; X: Cx)] -enum RebaseReason { +#[derive(Debug)] +enum RebaseReason { NoCycleUsages, - Ambiguity(X::AmbiguityInfo), Overflow, /// We've actually reached a fixpoint. /// @@ -956,7 +955,7 @@ impl, X: Cx> SearchGraph { &mut self, cx: X, stack_entry: &StackEntry, - rebase_reason: RebaseReason, + rebase_reason: RebaseReason, ) { let popped_head_index = self.stack.next_index(); #[allow(rustc::potential_query_instability)] @@ -1035,9 +1034,6 @@ impl, X: Cx> SearchGraph { // is not actually equal to the final provisional result. We // need to discard the provisional cache entry in this case. RebaseReason::NoCycleUsages => return false, - RebaseReason::Ambiguity(info) => { - *result = D::propagate_ambiguity(cx, input, info); - } RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input), RebaseReason::ReachedFixpoint(None) => {} RebaseReason::ReachedFixpoint(Some(path_kind)) => { @@ -1352,27 +1348,6 @@ impl, X: Cx> SearchGraph { return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } - // If computing this goal results in ambiguity with no constraints, - // we do not rerun it. It's incredibly difficult to get a different - // response in the next iteration in this case. These changes would - // likely either be caused by incompleteness or can change the maybe - // cause from ambiguity to overflow. Returning ambiguity always - // preserves soundness and completeness even if the goal is be known - // to succeed or fail. - // - // This prevents exponential blowup affecting multiple major crates. - // As we only get to this branch if we haven't yet reached a fixpoint, - // we also taint all provisional cache entries which depend on the - // current goal. - if let Some(info) = D::is_ambiguous_result(result) { - self.rebase_provisional_cache_entries( - cx, - &stack_entry, - RebaseReason::Ambiguity(info), - ); - return EvaluationResult::finalize(stack_entry, encountered_overflow, result); - }; - // If we've reached the fixpoint step limit, we bail with overflow and taint all // provisional cache entries which depend on the current goal. i += 1; diff --git a/tests/ui/traits/next-solver/global-where-bound-normalization.rs b/tests/ui/traits/next-solver/global-where-bound-normalization.rs new file mode 100644 index 0000000000000..e57fbf378a0d2 --- /dev/null +++ b/tests/ui/traits/next-solver/global-where-bound-normalization.rs @@ -0,0 +1,45 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Regression test for https://github.com/rust-lang/trait-system-refactor-initiative/issues/257. + +#![feature(rustc_attrs)] +#![expect(internal_features)] +#![rustc_no_implicit_bounds] + +pub trait Bound {} +impl Bound for u8 {} + +pub trait Proj { + type Assoc; +} +impl Proj for U { + type Assoc = U; +} +impl Proj for MyField { + type Assoc = u8; +} + +// While wf-checking the global bounds of `fn foo`, elaborating this outlives predicate triggered a +// cycle in the search graph along a particular probe path, which was not an actual solution. +// That cycle then resulted in a forced false-positive ambiguity due to a performance hack in the +// search graph and then ended up floundering the root goal evaluation. +pub trait Field: Proj {} + +struct MyField; +impl Field for MyField {} + +trait IdReqField { + type This; +} +impl IdReqField for F { + type This = F; +} + +fn foo() +where + ::This: Field, +{ +} + +fn main() {}