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..ab859be5d50fa 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 @@ -47,6 +47,10 @@ enum CurrentGoalKind { /// These are currently the only goals whose impl where-clauses are considered to be /// productive steps. CoinductiveTrait, + /// `AliasRelate` goals are used for normalization and their inference constraints are + /// necessary to guide inference for other goals. This means that even on overflow, we don't + /// discard inference constraints from `AliasRelate` and return them to the caller. + AliasRelate, /// Unlike other goals, `NormalizesTo` goals act like functions with the expected term /// always being fully unconstrained. This would weaken inference however, as the nested /// goals never get the inference constraints from the actual normalized-to type. @@ -67,6 +71,7 @@ impl CurrentGoalKind { CurrentGoalKind::Misc } } + ty::PredicateKind::AliasRelate(..) => CurrentGoalKind::AliasRelate, ty::PredicateKind::NormalizesTo(_) => CurrentGoalKind::NormalizesTo, _ => CurrentGoalKind::Misc, } @@ -275,7 +280,8 @@ where // as inductive even though it should not be, it may be unsound during coherence and // fixing it may cause inference breakage or introduce ambiguity. GoalSource::Misc => PathKind::Unknown, - GoalSource::NormalizeGoal(path_kind) => path_kind, + GoalSource::NormalizeGoal(path_kind) + | GoalSource::NestedNormalizationGoal(path_kind) => path_kind, GoalSource::ImplWhereBound => match self.current_goal_kind { // We currently only consider a cycle coinductive if it steps // into a where-clause of a coinductive trait. @@ -292,6 +298,7 @@ where // so we treat cycles involving where-clauses of not-yet coinductive // traits as ambiguous for now. CurrentGoalKind::Misc => PathKind::Unknown, + CurrentGoalKind::AliasRelate => unreachable!(), }, // Relating types is always unproductive. If we were to map proof trees to // corecursive functions as explained in #136824, relating types never @@ -679,7 +686,11 @@ where ) = self.evaluate_goal_raw(source, unconstrained_goal, stalled_on)?; // Add the nested goals from normalization to our own nested goals. trace!(?nested_goals); - self.nested_goals.extend(nested_goals.into_iter().map(|(s, g)| (s, g, None))); + self.nested_goals.extend( + nested_goals + .into_iter() + .map(|(s, g)| (GoalSource::NestedNormalizationGoal(s), g, None)), + ); // Finally, equate the goal's RHS with the unconstrained var. // @@ -1258,7 +1269,10 @@ where ( Certainty::Yes, NestedNormalizationGoals( - goals.into_iter().map(|(s, g, _)| (s, g)).collect(), + goals + .into_iter() + .map(|(s, g, _)| (self.step_kind_for_source(s), g)) + .collect(), ), ) } @@ -1268,23 +1282,30 @@ where } }; - if let Certainty::Maybe { - cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, - opaque_types_jank, - } = certainty - { - // If we have overflow, it's probable that we're substituting a type - // into itself infinitely and any partial substitutions in the query - // response are probably not useful anyways, so just return an empty - // query response. - // - // This may prevent us from potentially useful inference, e.g. - // 2 candidates, one ambiguous and one overflow, which both - // have the same inference constraints. - // - // Changing this to retain some constraints in the future - // won't be a breaking change, so this is good enough for now. - return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); + match self.current_goal_kind { + CurrentGoalKind::AliasRelate | CurrentGoalKind::NormalizesTo => {} + CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { + if let Certainty::Maybe { + cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, + opaque_types_jank, + } = certainty + { + // If we have overflow, it's probable that we're substituting a type + // into itself infinitely and any partial substitutions in the query + // response are probably not useful anyways, so just return an empty + // query response. + // + // This may prevent us from potentially useful inference, e.g. + // 2 candidates, one ambiguous and one overflow, which both + // have the same inference constraints. + // + // Changing this to retain some constraints in the future + // won't be a breaking change, so this is good enough for now. + return Ok( + self.make_ambiguous_response_no_constraints(cause, opaque_types_jank) + ); + } + } } let external_constraints = diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 8482c8a2972dc..179f6dfc46868 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -495,7 +495,10 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { match (child_mode, nested_goal.source()) { ( ChildMode::Trait(_) | ChildMode::Host(_), - GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_), + GoalSource::Misc + | GoalSource::TypeRelating + | GoalSource::NormalizeGoal(_) + | GoalSource::NestedNormalizationGoal(_), ) => { continue; } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 4f3f140af67d1..f8db83ecdd876 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -22,7 +22,9 @@ use std::marker::PhantomData; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; +use rustc_type_ir::Interner; use rustc_type_ir::data_structures::HashMap; +use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::{debug, instrument}; mod stack; @@ -122,13 +124,14 @@ pub trait Delegate: Sized { feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) )] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] pub enum PathKind { /// A path consisting of only inductive/unproductive steps. Their initial /// provisional result is `Err(NoSolution)`. We currently treat them as /// `PathKind::Unknown` during coherence until we're fully confident in /// our approach. Inductive, - /// A path which is not be coinductive right now but we may want + /// A path which is not coinductive right now but we may want /// to change of them to be so in the future. We return an ambiguous /// result in this case to prevent people from relying on this. Unknown, diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index ede23d5998327..3771be5b12a02 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -71,7 +71,7 @@ pub enum GoalSource { /// A nested goal required to prove that types are equal/subtypes. /// This is always an unproductive step. /// - /// This is also used for all `NormalizesTo` goals as we they are used + /// This is also used for all `NormalizesTo` goals as those are used /// to relate types in `AliasRelate`. TypeRelating, /// We're proving a where-bound of an impl. @@ -85,6 +85,8 @@ pub enum GoalSource { /// 2. for rigid projections's trait goal, /// 3. for GAT where clauses. AliasWellFormed, + /// A goal from a nested `NormalizesTo` goal which has been propagated to its caller. + NestedNormalizationGoal(PathKind), /// In case normalizing aliases in nested goals cycles, eagerly normalizing these /// aliases in the context of the parent may incorrectly change the cycle kind. /// Normalizing aliases in goals therefore tracks the original path kind for this @@ -269,7 +271,7 @@ impl ExternalConstraintsData { #[derive_where(Clone, Hash, PartialEq, Debug, Default; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] -pub struct NestedNormalizationGoals(pub Vec<(GoalSource, Goal)>); +pub struct NestedNormalizationGoals(pub Vec<(PathKind, Goal)>); impl Eq for NestedNormalizationGoals {} diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.rs b/tests/ui/traits/next-solver/alias-bound-unsound.rs index 57fc88d87cf65..400fab743e41d 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.rs +++ b/tests/ui/traits/next-solver/alias-bound-unsound.rs @@ -26,12 +26,8 @@ impl Foo for () { fn main() { let x = String::from("hello, world"); let _ = identity(<() as Foo>::copy_me(&x)); - //~^ ERROR overflow evaluating the requirement `String <: <() as Foo>::Item` - //~| ERROR overflow evaluating the requirement `<() as Foo>::Item well-formed` - //~| ERROR overflow evaluating the requirement `&<() as Foo>::Item well-formed` - //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _` - //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _` - //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _` - //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _` + //~^ ERROR type mismatch resolving `<() as Foo>::Item normalizes-to String` + //~| ERROR type mismatch resolving `<() as Foo>::Item normalizes-to String` + //~| ERROR mismatched types println!("{x}"); } diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.stderr b/tests/ui/traits/next-solver/alias-bound-unsound.stderr index 1079c27fa815f..e91ebf6300162 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.stderr +++ b/tests/ui/traits/next-solver/alias-bound-unsound.stderr @@ -12,52 +12,53 @@ LL | trait Foo { LL | type Item: Copy | ^^^^ this trait's associated type doesn't have the requirement `String: Copy` -error[E0275]: overflow evaluating the requirement `String <: <() as Foo>::Item` +error[E0271]: type mismatch resolving `<() as Foo>::Item normalizes-to String` --> $DIR/alias-bound-unsound.rs:28:43 | LL | let _ = identity(<() as Foo>::copy_me(&x)); - | ^^ - -error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:28:22 - | -LL | let _ = identity(<() as Foo>::copy_me(&x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:28:22 + | ^^ types differ | -LL | let _ = identity(<() as Foo>::copy_me(&x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `Foo::Item` + --> $DIR/alias-bound-unsound.rs:14:30 | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +LL | type Item: Copy + | ---- required by a bound in this associated type +LL | where +LL | ::Item: Copy; + | ^^^^ required by this bound in `Foo::Item` -error[E0275]: overflow evaluating the requirement `&<() as Foo>::Item well-formed` +error[E0308]: mismatched types --> $DIR/alias-bound-unsound.rs:28:43 | LL | let _ = identity(<() as Foo>::copy_me(&x)); - | ^^ - -error[E0275]: overflow evaluating the requirement `<() as Foo>::Item well-formed` - --> $DIR/alias-bound-unsound.rs:28:22 + | -------------------- ^^ types differ + | | + | arguments to this function are incorrect | -LL | let _ = identity(<() as Foo>::copy_me(&x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected reference `&<() as Foo>::Item` + found reference `&String` +note: associated function defined here + --> $DIR/alias-bound-unsound.rs:16:8 + | +LL | fn copy_me(x: &Self::Item) -> Self::Item { + | ^^^^^^^ -------------- -error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` +error[E0271]: type mismatch resolving `<() as Foo>::Item normalizes-to String` --> $DIR/alias-bound-unsound.rs:28:22 | LL | let _ = identity(<() as Foo>::copy_me(&x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ types differ | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:28:43 +note: required by a bound in `Foo::Item` + --> $DIR/alias-bound-unsound.rs:14:30 | -LL | let _ = identity(<() as Foo>::copy_me(&x)); - | ^^ +LL | type Item: Copy + | ---- required by a bound in this associated type +LL | where +LL | ::Item: Copy; + | ^^^^ required by this bound in `Foo::Item` -error: aborting due to 8 previous errors +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0275`. +Some errors have detailed explanations: E0271, E0275, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/alias-relate/no-discard-constraints-on-ambiguity.rs b/tests/ui/traits/next-solver/alias-relate/no-discard-constraints-on-ambiguity.rs new file mode 100644 index 0000000000000..e7d644321b369 --- /dev/null +++ b/tests/ui/traits/next-solver/alias-relate/no-discard-constraints-on-ambiguity.rs @@ -0,0 +1,49 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#246. + +struct MarkedStruct; +pub trait MarkerTrait {} +impl MarkerTrait for MarkedStruct {} + +// Necessary indirection to get a cycle with `PathKind::Unknown` +struct ChainStruct; +trait ChainTrait {} +impl ChainTrait for ChainStruct where MarkedStruct: MarkerTrait {} + +pub struct FooStruct; +pub trait FooTrait { + type Output; +} +pub struct FooOut; +impl FooTrait for FooStruct +where + ChainStruct: ChainTrait, +{ + type Output = FooOut; +} + +pub trait Trait { + type Output; +} + +// prove <::Output as Trait>::Output: MarkerTrait +// normalize <::Output as Trait>::Output +// `>::Output` remains ambiguous, so this alias is never rigid +// alias-relate ::Output ?fresh_infer +// does not constrain `?fresh_infer` as `keep_constraints` is `false` +// normalize ::Output +// result `FooOut` with overflow +// prove ChainStruct: ChainTrait +// prove MarkedStruct: MarkerTrait +// impl trivial (ignored) +// where-clause requires normalize <::Output as Trait>::Output overflow +pub fn foo() +where + FooOut: Trait, + <::Output as Trait>::Output: MarkerTrait, +{ +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.next.stderr b/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.next.stderr index 451c1442ed294..68fe74c4fdf2a 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.next.stderr +++ b/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.next.stderr @@ -10,12 +10,6 @@ note: required by a bound in `transmute` LL | fn transmute, R>(r: L) -> >::Proof { r } | ^^^^^^^^ required by this bound in `transmute` -error[E0275]: overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == _` - --> $DIR/item-bound-via-impl-where-clause.rs:31:21 - | -LL | let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0275]: overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == String` --> $DIR/item-bound-via-impl-where-clause.rs:31:21 | @@ -36,14 +30,6 @@ error[E0275]: overflow evaluating the requirement `< as Trait>:: LL | let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0275]: overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == _` - --> $DIR/item-bound-via-impl-where-clause.rs:31:21 - | -LL | let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.rs b/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.rs index 39381d17f7afd..4c84bd1da0ce5 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.rs +++ b/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.rs @@ -30,10 +30,8 @@ fn transmute, R>(r: L) -> >::Proof { r } fn main() { let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); //~^ ERROR overflow evaluating the requirement `Vec: Trait` - //[next]~| ERROR overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == _` //[next]~| ERROR overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == String` //[next]~| ERROR overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof: Sized` //[next]~| ERROR overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof well-formed` - //[next]~| ERROR overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == _` println!("{}", s); // ABC }