change definitely non-productive cycles to error

This commit is contained in:
lcnr 2025-02-27 11:57:05 +01:00
parent ac951d3799
commit 01795b14f0
19 changed files with 235 additions and 206 deletions

View File

@ -271,12 +271,39 @@ where
/// and will need to clearly document it in the rustc-dev-guide before
/// stabilization.
pub(super) fn step_kind_for_source(&self, source: GoalSource) -> PathKind {
match (self.current_goal_kind, source) {
(_, GoalSource::NormalizeGoal(step_kind)) => step_kind,
(CurrentGoalKind::CoinductiveTrait, GoalSource::ImplWhereBound) => {
PathKind::Coinductive
match source {
// We treat these goals as unknown for now. It is likely that most miscellaneous
// nested goals will be converted to an inductive variant in the future.
//
// Having unknown cycles is always the safer option, as changing that to either
// succeed or hard error is backwards compatible. If we incorrectly treat a cycle
// 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::ImplWhereBound => {
// We currently only consider a cycle coinductive if it steps
// into a where-clause of a coinductive trait.
//
// We probably want to make all traits coinductive in the future,
// so we treat cycles involving their where-clauses as ambiguous.
if let CurrentGoalKind::CoinductiveTrait = self.current_goal_kind {
PathKind::Coinductive
} else {
PathKind::Unknown
}
}
_ => PathKind::Inductive,
// Relating types is always unproductive. If we were to map proof trees to
// corecursive functions as explained in #136824, relating types never
// introduces a constructor which could cause the recursion to be guarded.
GoalSource::TypeRelating => PathKind::Inductive,
// Instantiating a higher ranked goal can never cause the recursion to be
// guarded and is therefore unproductive.
GoalSource::InstantiateHigherRanked => PathKind::Inductive,
// These goal sources are likely unproductive and can be changed to
// `PathKind::Inductive`. Keeping them as unknown until we're confident
// about this and have an example where it is necessary.
GoalSource::AliasBoundConstCondition | GoalSource::AliasWellFormed => PathKind::Unknown,
}
}
@ -606,7 +633,7 @@ where
let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw(
GoalEvaluationKind::Nested,
GoalSource::Misc,
GoalSource::TypeRelating,
unconstrained_goal,
)?;
// Add the nested goals from normalization to our own nested goals.
@ -683,7 +710,7 @@ where
pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal<I, ty::NormalizesTo<I>>) {
goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(
self,
GoalSource::Misc,
GoalSource::TypeRelating,
goal.param_env,
));
self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal);
@ -939,7 +966,15 @@ where
rhs: T,
) -> Result<(), NoSolution> {
let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?;
self.add_goals(GoalSource::Misc, goals);
if cfg!(debug_assertions) {
for g in goals.iter() {
match g.predicate.kind().skip_binder() {
ty::PredicateKind::Subtype { .. } | ty::PredicateKind::AliasRelate(..) => {}
p => unreachable!("unexpected nested goal in `relate`: {p:?}"),
}
}
}
self.add_goals(GoalSource::TypeRelating, goals);
Ok(())
}

View File

@ -421,7 +421,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
self.add_goal(
delegate,
max_input_universe,
GoalSource::Misc,
GoalSource::TypeRelating,
goal.with(delegate.cx(), goal.predicate),
);
}

View File

@ -313,7 +313,9 @@ where
ty::AliasRelationDirection::Equate,
),
);
self.add_goal(GoalSource::Misc, alias_relate_goal);
// We normalize the self type to be able to relate it with
// types from candidates.
self.add_goal(GoalSource::TypeRelating, alias_relate_goal);
self.try_evaluate_added_goals()?;
Ok(self.resolve_vars_if_possible(normalized_term))
} else {

View File

@ -24,7 +24,8 @@ where
ty::AliasRelationDirection::Equate,
),
);
self.add_goal(GoalSource::Misc, goal);
// A projection goal holds if the alias is equal to the expected term.
self.add_goal(GoalSource::TypeRelating, goal);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}

View File

@ -3,7 +3,7 @@ use std::marker::PhantomData;
use rustc_type_ir::Interner;
use rustc_type_ir::search_graph::{self, PathKind};
use rustc_type_ir::solve::{CanonicalInput, Certainty, QueryResult};
use rustc_type_ir::solve::{CanonicalInput, Certainty, NoSolution, QueryResult};
use super::inspect::ProofTreeBuilder;
use super::{FIXPOINT_STEP_LIMIT, has_no_inference_or_external_constraints};
@ -47,7 +47,8 @@ where
) -> QueryResult<I> {
match kind {
PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes),
PathKind::Inductive => response_no_constraints(cx, input, Certainty::overflow(false)),
PathKind::Unknown => response_no_constraints(cx, input, Certainty::overflow(false)),
PathKind::Inductive => Err(NoSolution),
}
}
@ -57,12 +58,7 @@ where
input: CanonicalInput<I>,
result: QueryResult<I>,
) -> bool {
match kind {
PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes) == result,
PathKind::Inductive => {
response_no_constraints(cx, input, Certainty::overflow(false)) == result
}
}
Self::initial_provisional_result(cx, kind, input) == result
}
fn on_stack_overflow(

View File

@ -440,7 +440,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
match (child_mode, nested_goal.source()) {
(
ChildMode::Trait(_) | ChildMode::Host(_),
GoalSource::Misc | GoalSource::NormalizeGoal(_),
GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_),
) => {
continue;
}

View File

@ -13,6 +13,7 @@
/// behavior as long as the resulting behavior is still correct.
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::collections::hash_map::Entry;
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
@ -20,7 +21,7 @@ use std::marker::PhantomData;
use derive_where::derive_where;
use rustc_index::{Idx, IndexVec};
#[cfg(feature = "nightly")]
use rustc_macros::HashStable_NoContext;
use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
use tracing::debug;
use crate::data_structures::HashMap;
@ -111,21 +112,32 @@ pub trait Delegate {
/// In the initial iteration of a cycle, we do not yet have a provisional
/// result. In the case we return an initial provisional result depending
/// on the kind of cycle.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
pub enum PathKind {
Coinductive,
/// A path consisting of only inductive/unproductive steps.
Inductive,
/// A path which is not be 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,
/// A path with at least one coinductive step. Such cycles hold.
Coinductive,
}
impl PathKind {
/// Returns the path kind when merging `self` with `rest`.
///
/// Given an inductive path `self` and a coinductive path `rest`,
/// the path `self -> rest` would be coinductive.
///
/// This operation represents an ordering and would be equivalent
/// to `max(self, rest)`.
fn extend(self, rest: PathKind) -> PathKind {
match self {
PathKind::Coinductive => PathKind::Coinductive,
PathKind::Inductive => rest,
match (self, rest) {
(PathKind::Coinductive, _) | (_, PathKind::Coinductive) => PathKind::Coinductive,
(PathKind::Unknown, _) | (_, PathKind::Unknown) => PathKind::Unknown,
(PathKind::Inductive, PathKind::Inductive) => PathKind::Inductive,
}
}
}
@ -159,9 +171,6 @@ impl UsageKind {
}
}
}
fn and_merge(&mut self, other: impl Into<Self>) {
*self = self.merge(other);
}
}
/// For each goal we track whether the paths from this goal
@ -297,7 +306,7 @@ impl CycleHeads {
let path_from_entry = match step_kind {
PathKind::Coinductive => AllPathsToHeadCoinductive::Yes,
PathKind::Inductive => path_from_entry,
PathKind::Unknown | PathKind::Inductive => path_from_entry,
};
self.insert(head, path_from_entry);
@ -305,6 +314,63 @@ impl CycleHeads {
}
}
bitflags::bitflags! {
/// Tracks how nested goals have been accessed. This is necessary to disable
/// global cache entries if computing them would otherwise result in a cycle or
/// access a provisional cache entry.
#[derive(Debug, Clone, Copy)]
pub struct PathsToNested: u8 {
/// The initial value when adding a goal to its own nested goals.
const EMPTY = 1 << 0;
const INDUCTIVE = 1 << 1;
const UNKNOWN = 1 << 2;
const COINDUCTIVE = 1 << 3;
}
}
impl From<PathKind> for PathsToNested {
fn from(path: PathKind) -> PathsToNested {
match path {
PathKind::Inductive => PathsToNested::INDUCTIVE,
PathKind::Unknown => PathsToNested::UNKNOWN,
PathKind::Coinductive => PathsToNested::COINDUCTIVE,
}
}
}
impl PathsToNested {
/// The implementation of this function is kind of ugly. We check whether
/// there currently exist 'weaker' paths in the set, if so we upgrade these
/// paths to at least `path`.
#[must_use]
fn extend_with(mut self, path: PathKind) -> Self {
match path {
PathKind::Inductive => {
if self.intersects(PathsToNested::EMPTY) {
self.remove(PathsToNested::EMPTY);
self.insert(PathsToNested::INDUCTIVE);
}
}
PathKind::Unknown => {
if self.intersects(PathsToNested::EMPTY | PathsToNested::INDUCTIVE) {
self.remove(PathsToNested::EMPTY | PathsToNested::INDUCTIVE);
self.insert(PathsToNested::UNKNOWN);
}
}
PathKind::Coinductive => {
if self.intersects(
PathsToNested::EMPTY | PathsToNested::INDUCTIVE | PathsToNested::UNKNOWN,
) {
self.remove(
PathsToNested::EMPTY | PathsToNested::INDUCTIVE | PathsToNested::UNKNOWN,
);
self.insert(PathsToNested::COINDUCTIVE);
}
}
}
self
}
}
/// The nested goals of each stack entry and the path from the
/// stack entry to that nested goal.
///
@ -322,15 +388,18 @@ impl CycleHeads {
/// results from a the cycle BAB depending on the cycle root.
#[derive_where(Debug, Default, Clone; X: Cx)]
struct NestedGoals<X: Cx> {
nested_goals: HashMap<X::Input, UsageKind>,
nested_goals: HashMap<X::Input, PathsToNested>,
}
impl<X: Cx> NestedGoals<X> {
fn is_empty(&self) -> bool {
self.nested_goals.is_empty()
}
fn insert(&mut self, input: X::Input, path_from_entry: UsageKind) {
self.nested_goals.entry(input).or_insert(path_from_entry).and_merge(path_from_entry);
fn insert(&mut self, input: X::Input, paths_to_nested: PathsToNested) {
match self.nested_goals.entry(input) {
Entry::Occupied(mut entry) => *entry.get_mut() |= paths_to_nested,
Entry::Vacant(entry) => drop(entry.insert(paths_to_nested)),
}
}
/// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal
@ -341,18 +410,15 @@ impl<X: Cx> NestedGoals<X> {
/// the same as for the child.
fn extend_from_child(&mut self, step_kind: PathKind, nested_goals: &NestedGoals<X>) {
#[allow(rustc::potential_query_instability)]
for (input, path_from_entry) in nested_goals.iter() {
let path_from_entry = match step_kind {
PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive),
PathKind::Inductive => path_from_entry,
};
self.insert(input, path_from_entry);
for (input, paths_to_nested) in nested_goals.iter() {
let paths_to_nested = paths_to_nested.extend_with(step_kind);
self.insert(input, paths_to_nested);
}
}
#[cfg_attr(feature = "nightly", rustc_lint_query_instability)]
#[allow(rustc::potential_query_instability)]
fn iter(&self) -> impl Iterator<Item = (X::Input, UsageKind)> {
fn iter(&self) -> impl Iterator<Item = (X::Input, PathsToNested)> + '_ {
self.nested_goals.iter().map(|(i, p)| (*i, *p))
}
@ -490,7 +556,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
// goals as this change may cause them to now depend on additional
// goals, resulting in new cycles. See the dev-guide for examples.
if parent_depends_on_cycle {
parent.nested_goals.insert(parent.input, UsageKind::Single(PathKind::Inductive))
parent.nested_goals.insert(parent.input, PathsToNested::EMPTY);
}
}
}
@ -666,7 +732,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
//
// We must therefore not use the global cache entry for `B` in that case.
// See tests/ui/traits/next-solver/cycles/hidden-by-overflow.rs
last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Inductive));
last.nested_goals.insert(last.input, PathsToNested::EMPTY);
}
debug!("encountered stack overflow");
@ -749,16 +815,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
// We now care about the path from the next highest cycle head to the
// provisional cache entry.
match path_from_head {
PathKind::Coinductive => {}
PathKind::Inductive => {
*path_from_head = Self::cycle_path_kind(
&self.stack,
stack_entry.step_kind_from_parent,
head,
)
}
}
*path_from_head = path_from_head.extend(Self::cycle_path_kind(
&self.stack,
stack_entry.step_kind_from_parent,
head,
));
// Mutate the result of the provisional cache entry in case we did
// not reach a fixpoint.
*result = mutate_result(input, *result);
@ -858,7 +919,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
for &ProvisionalCacheEntry {
encountered_overflow,
ref heads,
path_from_head,
path_from_head: head_to_provisional,
result: _,
} in entries.iter()
{
@ -870,24 +931,19 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
// A provisional cache entry only applies if the path from its highest head
// matches the path when encountering the goal.
//
// We check if any of the paths taken while computing the global goal
// would end up with an applicable provisional cache entry.
let head = heads.highest_cycle_head();
let full_path = match Self::cycle_path_kind(stack, step_kind_from_parent, head) {
PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive),
PathKind::Inductive => path_from_global_entry,
};
match (full_path, path_from_head) {
(UsageKind::Mixed, _)
| (UsageKind::Single(PathKind::Coinductive), PathKind::Coinductive)
| (UsageKind::Single(PathKind::Inductive), PathKind::Inductive) => {
debug!(
?full_path,
?path_from_head,
"cache entry not applicable due to matching paths"
);
return false;
}
_ => debug!(?full_path, ?path_from_head, "paths don't match"),
let head_to_curr = Self::cycle_path_kind(stack, step_kind_from_parent, head);
let full_paths = path_from_global_entry.extend_with(head_to_curr);
if full_paths.contains(head_to_provisional.into()) {
debug!(
?full_paths,
?head_to_provisional,
"cache entry not applicable due to matching paths"
);
return false;
}
}
}
@ -986,8 +1042,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
let last = &mut self.stack[last_index];
last.reached_depth = last.reached_depth.max(next_index);
last.nested_goals.insert(input, UsageKind::Single(step_kind_from_parent));
last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Inductive));
last.nested_goals.insert(input, step_kind_from_parent.into());
last.nested_goals.insert(last.input, PathsToNested::EMPTY);
if last_index != head {
last.heads.insert(head, step_kind_from_parent);
}

View File

@ -58,20 +58,24 @@ impl<I: Interner, P> Goal<I, P> {
/// Why a specific goal has to be proven.
///
/// This is necessary as we treat nested goals different depending on
/// their source. This is currently mostly used by proof tree visitors
/// but will be used by cycle handling in the future.
/// their source. This is used to decide whether a cycle is coinductive.
/// See the documentation of `EvalCtxt::step_kind_for_source` for more details
/// about this.
///
/// It is also used by proof tree visitors, e.g. for diagnostics purposes.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
pub enum GoalSource {
Misc,
/// We're proving a where-bound of an impl.
/// A nested goal required to prove that types are equal/subtypes.
/// This is always an unproductive step.
///
/// FIXME(-Znext-solver=coinductive): Explain how and why this
/// changes whether cycles are coinductive.
/// This is also used for all `NormalizesTo` goals as we they are used
/// to relate types in `AliasRelate`.
TypeRelating,
/// We're proving a where-bound of an impl.
ImplWhereBound,
/// Const conditions that need to hold for `~const` alias bounds to hold.
///
/// FIXME(-Znext-solver=coinductive): Are these even coinductive?
AliasBoundConstCondition,
/// Instantiating a higher-ranked goal and re-proving it.
InstantiateHigherRanked,
@ -79,7 +83,6 @@ pub enum GoalSource {
/// This is used in two places: projecting to an opaque whose hidden type
/// is already registered in the opaque type storage, and for rigid projections.
AliasWellFormed,
/// 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

View File

@ -1,4 +1,7 @@
//@ check-pass
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)
trait A<'a> {}
trait B<'b> {}

View File

@ -1,4 +1,7 @@
//@ check-pass
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)
// Make sure that we don't look into associated type bounds when looking for
// supertraits that define an associated type. Fixes #76593.

View File

@ -1,8 +1,8 @@
error[E0275]: overflow evaluating the requirement `Loop == _`
error[E0271]: type mismatch resolving `Loop normalizes-to _`
--> $DIR/inherent-impls-overflow.rs:10:6
|
LL | impl Loop {}
| ^^^^
| ^^^^ types differ
error: type parameter `T` is only used recursively
--> $DIR/inherent-impls-overflow.rs:14:24
@ -36,4 +36,5 @@ LL | impl Poly0<()> {}
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0275`.
Some errors have detailed explanations: E0271, E0275.
For more information about an error, try `rustc --explain E0271`.

View File

@ -9,7 +9,7 @@ type Loop = Loop; //[current]~ ERROR overflow normalizing the type alias `Loop`
impl Loop {}
//[current]~^ ERROR overflow normalizing the type alias `Loop`
//[next]~^^ ERROR overflow evaluating the requirement `Loop == _`
//[next]~^^ ERROR type mismatch resolving `Loop normalizes-to _`
type Poly0<T> = Poly1<(T,)>;
//[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>`

View File

@ -6,9 +6,11 @@
trait Overflow {
type Assoc;
}
impl<T> Overflow for T {
type Assoc = <T as Overflow>::Assoc;
//~^ ERROR: overflow
impl<T> Overflow for T
where
(T,): Overflow
{
type Assoc = <(T,) as Overflow>::Assoc;
}

View File

@ -1,19 +1,15 @@
error[E0275]: overflow evaluating the requirement `<T as Overflow>::Assoc == _`
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:10:18
|
LL | type Assoc = <T as Overflow>::Assoc;
| ^^^^^^^^^^^^^^^^^^^^^^
error[E0119]: conflicting implementations of trait `Trait`
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:18:1
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:20:1
|
LL | impl<T: Copy> Trait for T {}
| ------------------------- first implementation here
LL | struct LocalTy;
LL | impl Trait for <LocalTy as Overflow>::Assoc {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
|
= note: overflow evaluating the requirement `_ == <LocalTy as Overflow>::Assoc`
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`trait_ref_is_knowable_norm_overflow`)
error: aborting due to 2 previous errors
error: aborting due to 1 previous error
Some errors have detailed explanations: E0119, E0275.
For more information about an error, try `rustc --explain E0119`.
For more information about this error, try `rustc --explain E0119`.

View File

@ -0,0 +1,21 @@
// Regression test for trait-system-refactor-initiative#114.
//
// We previously treated the cycle when trying to use the
// `<R as DimMin<C>>::Output: DimMin` where-bound when
// normalizing `<R as DimMin<C>>::Output` as ambiguous, causing
// this to error.
//@ check-pass
//@ compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver
pub trait DimMin<D> {
type Output;
}
pub fn repro<R: DimMin<C>, C>()
where
<R as DimMin<C>>::Output: DimMin<C, Output = <R as DimMin<C>>::Output>,
{
}
fn main() {}

View File

@ -13,12 +13,7 @@ fn needs_bar<S: Bar>() {}
fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() {
needs_bar::<T::Assoc1>();
//~^ ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
//~| ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
//~| ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
//~| ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
//~| ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1: Sized`
//~| ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1: Bar`
//~^ ERROR the trait bound `<T as Foo1>::Assoc1: Bar` is not satisfied
}
fn main() {}

View File

@ -1,59 +1,19 @@
error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
error[E0277]: the trait bound `<T as Foo1>::Assoc1: Bar` is not satisfied
--> $DIR/recursive-self-normalization-2.rs:15:17
|
LL | needs_bar::<T::Assoc1>();
| ^^^^^^^^^
error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1: Bar`
--> $DIR/recursive-self-normalization-2.rs:15:17
|
LL | needs_bar::<T::Assoc1>();
| ^^^^^^^^^
| ^^^^^^^^^ the trait `Bar` is not implemented for `<T as Foo1>::Assoc1`
|
note: required by a bound in `needs_bar`
--> $DIR/recursive-self-normalization-2.rs:12:17
|
LL | fn needs_bar<S: Bar>() {}
| ^^^ required by this bound in `needs_bar`
help: consider further restricting the associated type
|
LL | fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() where <T as Foo1>::Assoc1: Bar {
| ++++++++++++++++++++++++++++++
error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1: Sized`
--> $DIR/recursive-self-normalization-2.rs:15:17
|
LL | needs_bar::<T::Assoc1>();
| ^^^^^^^^^
|
note: required by an implicit `Sized` bound in `needs_bar`
--> $DIR/recursive-self-normalization-2.rs:12:14
|
LL | fn needs_bar<S: Bar>() {}
| ^ required by the implicit `Sized` requirement on this type parameter in `needs_bar`
help: consider relaxing the implicit `Sized` restriction
|
LL | fn needs_bar<S: Bar + ?Sized>() {}
| ++++++++
error: aborting due to 1 previous error
error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
--> $DIR/recursive-self-normalization-2.rs:15:5
|
LL | needs_bar::<T::Assoc1>();
| ^^^^^^^^^^^^^^^^^^^^^^
error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
--> $DIR/recursive-self-normalization-2.rs:15:5
|
LL | needs_bar::<T::Assoc1>();
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
--> $DIR/recursive-self-normalization-2.rs:15:17
|
LL | needs_bar::<T::Assoc1>();
| ^^^^^^^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0275`.
For more information about this error, try `rustc --explain E0277`.

View File

@ -9,12 +9,7 @@ fn needs_bar<S: Bar>() {}
fn test<T: Foo<Assoc = <T as Foo>::Assoc>>() {
needs_bar::<T::Assoc>();
//~^ ERROR overflow evaluating the requirement `<T as Foo>::Assoc == _`
//~| ERROR overflow evaluating the requirement `<T as Foo>::Assoc == _`
//~| ERROR overflow evaluating the requirement `<T as Foo>::Assoc == _`
//~| ERROR overflow evaluating the requirement `<T as Foo>::Assoc == _`
//~| ERROR overflow evaluating the requirement `<T as Foo>::Assoc: Sized`
//~| ERROR overflow evaluating the requirement `<T as Foo>::Assoc: Bar`
//~^ ERROR the trait bound `<T as Foo>::Assoc: Bar` is not satisfied
}
fn main() {}

View File

@ -1,59 +1,19 @@
error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _`
error[E0277]: the trait bound `<T as Foo>::Assoc: Bar` is not satisfied
--> $DIR/recursive-self-normalization.rs:11:17
|
LL | needs_bar::<T::Assoc>();
| ^^^^^^^^
error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc: Bar`
--> $DIR/recursive-self-normalization.rs:11:17
|
LL | needs_bar::<T::Assoc>();
| ^^^^^^^^
| ^^^^^^^^ the trait `Bar` is not implemented for `<T as Foo>::Assoc`
|
note: required by a bound in `needs_bar`
--> $DIR/recursive-self-normalization.rs:8:17
|
LL | fn needs_bar<S: Bar>() {}
| ^^^ required by this bound in `needs_bar`
help: consider further restricting the associated type
|
LL | fn test<T: Foo<Assoc = <T as Foo>::Assoc>>() where <T as Foo>::Assoc: Bar {
| ++++++++++++++++++++++++++++
error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc: Sized`
--> $DIR/recursive-self-normalization.rs:11:17
|
LL | needs_bar::<T::Assoc>();
| ^^^^^^^^
|
note: required by an implicit `Sized` bound in `needs_bar`
--> $DIR/recursive-self-normalization.rs:8:14
|
LL | fn needs_bar<S: Bar>() {}
| ^ required by the implicit `Sized` requirement on this type parameter in `needs_bar`
help: consider relaxing the implicit `Sized` restriction
|
LL | fn needs_bar<S: Bar + ?Sized>() {}
| ++++++++
error: aborting due to 1 previous error
error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _`
--> $DIR/recursive-self-normalization.rs:11:5
|
LL | needs_bar::<T::Assoc>();
| ^^^^^^^^^^^^^^^^^^^^^
error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _`
--> $DIR/recursive-self-normalization.rs:11:5
|
LL | needs_bar::<T::Assoc>();
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _`
--> $DIR/recursive-self-normalization.rs:11:17
|
LL | needs_bar::<T::Assoc>();
| ^^^^^^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0275`.
For more information about this error, try `rustc --explain E0277`.