diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 9411a34f85e6d..05de90b8ba5b7 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -731,11 +731,14 @@ 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(diagnostics::IncompatibleFeatures { - spans: vec![gce_span], - f1: Symbol::intern("-Znext-solver=globally"), - f2: sym::generic_const_exprs, - }); + sess.dcx() + .create_fatal(diagnostics::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_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 146662676acc0..6957c0e688e4f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2681,7 +2681,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 48a421bb5dd9b..0f93415f46887 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 @@ -246,7 +246,7 @@ where return Ok(res); } - let result = EvalCtxt::enter_root(self, self.cx().recursion_limit(), span, |ecx| { + let result = EvalCtxt::enter_root(self, self.cx().recursion_limit() * 2, span, |ecx| { // Fast paths handled above ecx.evaluate_goal_no_fast_paths(GoalSource::Misc, goal) }); @@ -338,18 +338,12 @@ where // We currently only consider a cycle coinductive if it steps // into a where-clause of a coinductive trait. CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive, - // While normalizing via an impl does step into a where-clause of - // an impl, accessing the associated item immediately steps out of - // it again. This means cycles/recursive calls are not guarded - // by impls used for normalization. - // - // See tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs - // for how this can go wrong. - CurrentGoalKind::ProjectionComputeAssocTermCandidate => PathKind::Inductive, // We probably want to make all traits coinductive in the future, // so we treat cycles involving where-clauses of not-yet coinductive // traits as ambiguous for now. - CurrentGoalKind::Misc => PathKind::Unknown, + CurrentGoalKind::Misc | CurrentGoalKind::ProjectionComputeAssocTermCandidate => { + PathKind::Unknown + } }, // Relating types is always unproductive. If we were to map proof trees to // corecursive functions as explained in #136824, relating types never @@ -1705,7 +1699,7 @@ pub fn evaluate_root_goal_for_proof_tree_raw_provider< let mut inspect = inspect::ProofTreeBuilder::new(); let (canonical_result, accessed_opaques) = SearchGraph::::evaluate_root_goal_for_proof_tree( cx, - cx.recursion_limit(), + cx.recursion_limit() * 2, canonical_goal, &mut inspect, ); 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 778826ba60aee..097d6da2ec51f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -106,11 +106,12 @@ where response_no_constraints(cx, input, Certainty::overflow(true)) } + const FIXPOINT_OVERFLOW_AMBIGUITY_KIND: Certainty = Certainty::overflow(false); fn fixpoint_overflow_result( cx: I, input: CanonicalInput, ) -> (QueryResult, AccessedOpaques) { - response_no_constraints(cx, input, Certainty::overflow(false)) + response_no_constraints(cx, input, Self::FIXPOINT_OVERFLOW_AMBIGUITY_KIND) } fn is_ambiguous_result(result: (QueryResult, AccessedOpaques)) -> Option { @@ -125,14 +126,6 @@ where }) } - fn propagate_ambiguity( - cx: I, - for_input: CanonicalInput, - certainty: Certainty, - ) -> (QueryResult, AccessedOpaques) { - response_no_constraints(cx, for_input, certainty) - } - fn compute_goal( search_graph: &mut SearchGraph, cx: I, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 8ed01315ade53..15521ebb658d9 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1005,7 +1005,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_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 003a950095e4d..d3262bd6a6f96 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -297,7 +297,7 @@ fn do_normalize_predicates<'tcx>( let elaborated_env = if tcx.next_trait_solver_globally() && !tcx.disable_param_env_normalization_hack() { - let elaborated_env = ty::set_aliases_to_rigid(tcx, elaborated_env); + let elaborated_env = ty::set_type_aliases_to_rigid(tcx, elaborated_env); let elaborated_env = set_projection_term_to_non_rigid(tcx, elaborated_env.caller_bounds()); ty::ParamEnv::new(tcx.mk_clauses_from_iter(elaborated_env)) } else { diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 14fde5e399994..6a13997ceb0d9 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -584,6 +584,13 @@ where set_aliases_rigidness_with_mode(cx, value, RigidnessFoldMode::AllToRigid) } +pub fn set_type_aliases_to_rigid(cx: I, value: T) -> T +where + T: TypeFoldable, +{ + set_aliases_rigidness_with_mode(cx, value, RigidnessFoldMode::TypeToRigid) +} + fn set_aliases_rigidness_with_mode(cx: I, value: T, mode: RigidnessFoldMode) -> T where T: TypeFoldable, @@ -597,8 +604,9 @@ where } enum RigidnessFoldMode { - AllToNonRigid, AllToRigid, + AllToNonRigid, + TypeToRigid, OpaqueToNonRigid, } @@ -607,6 +615,10 @@ impl RigidnessFoldMode { match self { RigidnessFoldMode::AllToRigid => v.has_non_rigid_aliases(), RigidnessFoldMode::AllToNonRigid => v.has_rigid_aliases(), + RigidnessFoldMode::TypeToRigid => { + v.has_non_rigid_aliases() + && v.has_type_flags(ty::TypeFlags::HAS_ALIAS - ty::TypeFlags::HAS_CONST_ALIAS) + } RigidnessFoldMode::OpaqueToNonRigid => v.has_rigid_aliases() && v.has_opaque_types(), } } @@ -637,7 +649,7 @@ impl TypeFolder for RigidnessFolder { ty::Alias(is_rigid, alias_ty) => { let alias_ty = alias_ty.fold_with(self); match self.mode { - RigidnessFoldMode::AllToRigid => { + RigidnessFoldMode::AllToRigid | RigidnessFoldMode::TypeToRigid => { I::Ty::new_alias(self.cx(), ty::IsRigid::Yes, alias_ty) } RigidnessFoldMode::AllToNonRigid => { @@ -671,7 +683,7 @@ impl TypeFolder for RigidnessFolder { RigidnessFoldMode::AllToNonRigid => { I::Const::new_alias(self.cx(), ty::IsRigid::No, alias_const) } - RigidnessFoldMode::OpaqueToNonRigid => { + RigidnessFoldMode::OpaqueToNonRigid | RigidnessFoldMode::TypeToRigid => { I::Const::new_alias(self.cx(), is_rigid, alias_const) } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 815890c989d93..5314de87d92c4 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -654,7 +654,7 @@ impl CollectAndApply for Result { impl search_graph::Cx for I { type Input = CanonicalInput; type Result = (QueryResult, AccessedOpaques); - type AmbiguityInfo = Certainty; + type AmbiguityKind = Certainty; type DepNodeIndex = I::DepNodeIndex; type Tracked = I::Tracked; diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 711e7ae1c8945..b1635e7e4097c 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -40,7 +40,7 @@ pub use global_cache::GlobalCache; pub trait Cx: Copy { type Input: Debug + Eq + Hash + Copy; type Result: Debug + Eq + Hash + Copy; - type AmbiguityInfo: Debug + Eq + Hash + Copy; + type AmbiguityKind: Debug + Eq + Hash + Copy; type DepNodeIndex; type Tracked: Debug; @@ -92,6 +92,8 @@ pub trait Delegate: Sized { cx: Self::Cx, input: ::Input, ) -> ::Result; + + const FIXPOINT_OVERFLOW_AMBIGUITY_KIND: ::AmbiguityKind; fn fixpoint_overflow_result( cx: Self::Cx, input: ::Input, @@ -99,12 +101,7 @@ pub trait Delegate: Sized { fn is_ambiguous_result( result: ::Result, - ) -> Option<::AmbiguityInfo>; - fn propagate_ambiguity( - cx: Self::Cx, - for_input: ::Input, - ambiguity_info: ::AmbiguityInfo, - ) -> ::Result; + ) -> Option<::AmbiguityKind>; fn compute_goal( search_graph: &mut SearchGraph, @@ -955,8 +952,7 @@ impl, X: Cx> SearchGraph { #[derive_where(Debug; X: Cx)] enum RebaseReason { NoCycleUsages, - Ambiguity(X::AmbiguityInfo), - Overflow, + Ambiguity(X::AmbiguityKind), /// We've actually reached a fixpoint. /// /// This either happens in the first evaluation step for the cycle head. @@ -987,10 +983,9 @@ impl, X: Cx> SearchGraph { /// cache entries to also be ambiguous. This causes some undesirable ambiguity for nested /// goals whose result doesn't actually depend on this cycle head, but that's acceptable /// to me. - #[instrument(level = "trace", skip(self, cx))] + #[instrument(level = "trace", skip(self))] fn rebase_provisional_cache_entries( &mut self, - cx: X, stack_entry: &StackEntry, rebase_reason: RebaseReason, ) { @@ -1065,18 +1060,22 @@ impl, X: Cx> SearchGraph { } // The provisional cache entry does depend on the provisional result - // of the popped cycle head. We need to mutate the result of our - // provisional cache entry in case we did not reach a fixpoint. + // of the popped cycle head. In case we didn't actually reach a fixpoint, + // we must not keep potentially incorrect provisional cache entries around. match rebase_reason { // If the cycle head does not actually depend on itself, then // the provisional result used by the provisional cache entry // 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); + // If we avoid rerunning a goal due to ambiguity, we only keep provisional + // results which depend on that cycle head if these are already ambiguous + // themselves. + RebaseReason::Ambiguity(kind) => { + if !D::is_ambiguous_result(*result).is_some_and(|k| k == kind) { + return false; + } } - RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input), RebaseReason::ReachedFixpoint(None) => {} RebaseReason::ReachedFixpoint(Some(path_kind)) => { if !popped_head.usages.is_single(path_kind) { @@ -1380,17 +1379,12 @@ impl, X: Cx> SearchGraph { // final result is equal to the initial response for that case. if let Ok(fixpoint) = self.reached_fixpoint(&stack_entry, usages, result) { self.rebase_provisional_cache_entries( - cx, &stack_entry, RebaseReason::ReachedFixpoint(fixpoint), ); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } else if usages.is_empty() { - self.rebase_provisional_cache_entries( - cx, - &stack_entry, - RebaseReason::NoCycleUsages, - ); + self.rebase_provisional_cache_entries(&stack_entry, RebaseReason::NoCycleUsages); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } @@ -1399,19 +1393,15 @@ impl, X: Cx> SearchGraph { // 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. + // preserves soundness and completeness even if the goal could + // otherwise 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), - ); + if let Some(kind) = D::is_ambiguous_result(result) { + self.rebase_provisional_cache_entries(&stack_entry, RebaseReason::Ambiguity(kind)); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; @@ -1421,7 +1411,10 @@ impl, X: Cx> SearchGraph { if i >= D::FIXPOINT_STEP_LIMIT { debug!("canonical cycle overflow"); let result = D::fixpoint_overflow_result(cx, input); - self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Overflow); + self.rebase_provisional_cache_entries( + &stack_entry, + RebaseReason::Ambiguity(D::FIXPOINT_OVERFLOW_AMBIGUITY_KIND), + ); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } diff --git a/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs b/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs index 16c0f80b1c8dd..4bf8a21756b02 100644 --- a/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs +++ b/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs @@ -9,7 +9,8 @@ trait Foo { trait Baz {} -impl<'a, T> Foo for &'a T +impl<'a, T> Foo for &'a T //~ ERROR not all trait items implemented, missing: `Item` +//~| ERROR type annotations needed: cannot satisfy `&'a T: Foo` where Self::Item: 'a, { diff --git a/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr b/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr index 4c2dc5e8e71fa..8bfd8b773b790 100644 --- a/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr +++ b/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr @@ -1,22 +1,58 @@ -error[E0391]: cycle detected when computing normalized predicates of `` - --> $DIR/next-solver-region-resolution.rs:18:1 +error[E0046]: not all trait items implemented, missing: `Item` + --> $DIR/next-solver-region-resolution.rs:12:1 | -LL | / impl<'a, T> Foo for &T +LL | type Item; + | --------- `Item` from trait +... +LL | / impl<'a, T> Foo for &'a T LL | | LL | | where -LL | | Self::Item: Baz, - | |____________________^ +LL | | Self::Item: 'a, + | |___________________^ missing `Item` in implementation + +error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo` + --> $DIR/next-solver-region-resolution.rs:12:21 | - = note: ...which immediately requires computing normalized predicates of `` again -note: cycle used when computing whether impls specialize one another +LL | impl<'a, T> Foo for &'a T + | ^^^^^ + | +note: multiple `impl`s satisfying `&'a T: Foo` found --> $DIR/next-solver-region-resolution.rs:12:1 | LL | / impl<'a, T> Foo for &'a T +LL | | LL | | where LL | | Self::Item: 'a, | |___________________^ - = note: for more information, see and +... +LL | / impl<'a, T> Foo for &T +LL | | +LL | | where +LL | | Self::Item: Baz, + | |____________________^ + +error[E0046]: not all trait items implemented, missing: `Item` + --> $DIR/next-solver-region-resolution.rs:19:1 + | +LL | type Item; + | --------- `Item` from trait +... +LL | / impl<'a, T> Foo for &T +LL | | +LL | | where +LL | | Self::Item: Baz, + | |____________________^ missing `Item` in implementation + +error: cannot normalize `<&_ as Foo>::Item: '_` + --> $DIR/next-solver-region-resolution.rs:19:1 + | +LL | / impl<'a, T> Foo for &T +LL | | +LL | | where +LL | | Self::Item: Baz, + | |____________________^ -error: aborting due to 1 previous error +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0391`. +Some errors have detailed explanations: E0046, E0283. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive-2.next.stderr b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive-2.next.stderr new file mode 100644 index 0000000000000..2d70ef51e7f1e --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive-2.next.stderr @@ -0,0 +1,11 @@ +error[E0283]: type annotations needed: cannot satisfy ` as IntoIterator>::IntoIter: Iterator` + --> $DIR/normalizes-to-is-not-productive-2.rs:21:41 + | +LL | as IntoIterator>::IntoIter: Iterator, + | ^^^^^^^^ + | + = note: cannot satisfy ` as IntoIterator>::IntoIter: Iterator` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive-2.rs b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive-2.rs index bb3540f9a214f..2032fc7ec73f5 100644 --- a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive-2.rs +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive-2.rs @@ -1,9 +1,9 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@ check-pass +//@[current] check-pass -// Regression test for trait-system-refactor-initiative#176. +// Test from trait-system-refactor-initiative#176. // // Normalizing ` as IntoIterator>::IntoIter` has two candidates // inside of the function: @@ -13,11 +13,13 @@ // - where-clause requires ` as IntoIterator>::IntoIter eq Vec` // - normalize ` as IntoIterator>::IntoIter` again, cycle // -// We need to treat this cycle as an error to be able to use the actual impl. +// The blanket impl is unfortunately also a productive cycle, so we have to +// break this code, see trait-system-refactor-initiative#273 fn test() where as IntoIterator>::IntoIter: Iterator, + //[next]~^ ERROR type annotations needed: cannot satisfy ` as IntoIterator>::IntoIter: Iterator` { } diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs index 83e300b077428..cb37d2c457a42 100644 --- a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs @@ -1,9 +1,10 @@ //@ ignore-compare-mode-next-solver (explicit) //@ compile-flags: -Znext-solver -// Make sure that stepping into impl where-clauses of `NormalizesTo` -// goals is unproductive. This must not compile, see the inline -// comments. +// A test for the cycle handling when stepping into where-clauses of `NormalizesTo`. +// Whether stepping into where-clauses is productive depends on how they are used. +// +// In this concrete test, the cycle must not be productive. trait Bound { fn method(); @@ -30,7 +31,7 @@ fn impls_bound() { // The where-clause requires `Foo: Trait` to hold to be wf. // If stepping into where-clauses during normalization is considered -// to be productive, this would be the case: +// to be productive here, this would be the case: // // - `Foo: Trait` // - via blanket impls, requires `Foo: Bound` @@ -41,12 +42,13 @@ fn generic() //~^ ERROR the trait bound `Foo: Bound` is not satisfied where >::Assoc: Bound, - //~^ ERROR the trait bound `Foo: Bound` is not satisfied + //~^ ERROR overflow evaluating the requirement `>::Assoc: Bound` + //~| ERROR overflow evaluating whether `>::Assoc` is well-formed { // Requires proving `Foo: Bound` by normalizing // `>::Assoc` to `Foo`. impls_bound::(); - //~^ ERROR the trait bound `Foo: Bound` is not satisfied + //~^ ERROR overflow evaluating the requirement `Foo: Bound` } fn main() { // Requires proving `>::Assoc: Bound`. diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr index e18265190278a..9d726b482b50b 100644 --- a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr @@ -1,83 +1,27 @@ -error[E0277]: the trait bound `Foo: Bound` is not satisfied - --> $DIR/normalizes-to-is-not-productive.rs:40:1 - | -LL | / fn generic() -LL | | -LL | | where -LL | | >::Assoc: Bound, - | |____________________________________^ unsatisfied trait bound - | -help: the trait `Bound` is not implemented for `Foo` - --> $DIR/normalizes-to-is-not-productive.rs:18:1 - | -LL | struct Foo; - | ^^^^^^^^^^ -help: the trait `Bound` is implemented for `u32` - --> $DIR/normalizes-to-is-not-productive.rs:11:1 - | -LL | impl Bound for u32 { - | ^^^^^^^^^^^^^^^^^^ -note: required for `Foo` to implement `Trait` - --> $DIR/normalizes-to-is-not-productive.rs:23:19 +error[E0275]: overflow evaluating the requirement `>::Assoc: Bound` + --> $DIR/normalizes-to-is-not-productive.rs:43:31 | -LL | impl Trait for T { - | ----- ^^^^^^^^ ^ - | | - | unsatisfied trait bound introduced here +LL | >::Assoc: Bound, + | ^^^^^ -error[E0277]: the trait bound `Foo: Bound` is not satisfied +error[E0275]: overflow evaluating whether `>::Assoc` is well-formed --> $DIR/normalizes-to-is-not-productive.rs:43:31 | LL | >::Assoc: Bound, - | ^^^^^ unsatisfied trait bound - | -help: the trait `Bound` is not implemented for `Foo` - --> $DIR/normalizes-to-is-not-productive.rs:18:1 - | -LL | struct Foo; - | ^^^^^^^^^^ -help: the trait `Bound` is implemented for `u32` - --> $DIR/normalizes-to-is-not-productive.rs:11:1 - | -LL | impl Bound for u32 { - | ^^^^^^^^^^^^^^^^^^ -note: required for `Foo` to implement `Trait` - --> $DIR/normalizes-to-is-not-productive.rs:23:19 - | -LL | impl Trait for T { - | ----- ^^^^^^^^ ^ - | | - | unsatisfied trait bound introduced here -note: required by a bound in `Bound` - --> $DIR/normalizes-to-is-not-productive.rs:8:1 - | -LL | / trait Bound { -LL | | fn method(); -LL | | } - | |_^ required by this bound in `Bound` + | ^^^^^ -error[E0277]: the trait bound `Foo: Bound` is not satisfied - --> $DIR/normalizes-to-is-not-productive.rs:48:19 +error[E0275]: overflow evaluating the requirement `Foo: Bound` + --> $DIR/normalizes-to-is-not-productive.rs:49:19 | LL | impls_bound::(); - | ^^^ unsatisfied trait bound - | -help: the trait `Bound` is not implemented for `Foo` - --> $DIR/normalizes-to-is-not-productive.rs:18:1 - | -LL | struct Foo; - | ^^^^^^^^^^ -help: the trait `Bound` is implemented for `u32` - --> $DIR/normalizes-to-is-not-productive.rs:11:1 + | ^^^ | -LL | impl Bound for u32 { - | ^^^^^^^^^^^^^^^^^^ note: required by a bound in `impls_bound` - --> $DIR/normalizes-to-is-not-productive.rs:27:19 + --> $DIR/normalizes-to-is-not-productive.rs:28:19 | LL | fn impls_bound() { | ^^^^^ required by this bound in `impls_bound` error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0275`. 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() {} diff --git a/tests/ui/traits/next-solver/normalize/dont-mark-const-aliases-as-rigid-in-param-env.rs b/tests/ui/traits/next-solver/normalize/dont-mark-const-aliases-as-rigid-in-param-env.rs new file mode 100644 index 0000000000000..0c3fb9fcbea3c --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/dont-mark-const-aliases-as-rigid-in-param-env.rs @@ -0,0 +1,24 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#279. +// The old solver treats unnormalized param env as rigid when normalizing param +// env. However, it eagerly evaluates const aliases in param env before doing +// full normalization. +// We previously treated unevaluated const aliases as rigid in the next solver. + +trait Trait { + type Assoc; +} + +fn foo() +where + [T; 1 + 1]: Trait, + // When normalizing this associated type, we couldn't prove the trait + // predicate. Because the unnormalized trait clause is marked as rigid + // and can't be used as a matching param env candidate. + <[T; 2] as Trait>::Assoc: Copy, +{} + +fn main() {}