mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-06 12:18:33 +00:00
Rollup merge of #137314 - lcnr:cycles-with-unknown-kind, r=compiler-errors
change definitely unproductive cycles to error builds on top of #136824 by adding a third variant to `PathKind` for paths which may change to be coinductive in the future but must not be so right now. Most notably, impl where-clauses of not yet coinductive traits. With this, we can change cycles which are definitely unproductive to a proper error. This fixes https://github.com/rust-lang/trait-system-refactor-initiative/issues/114. This does not affect stable as we keep these cycles as ambiguous during coherence. r? ````````@compiler-errors```````` ````````@nikomatsakis````````
This commit is contained in:
commit
d55e2e4333
@ -271,12 +271,39 @@ where
|
|||||||
/// and will need to clearly document it in the rustc-dev-guide before
|
/// and will need to clearly document it in the rustc-dev-guide before
|
||||||
/// stabilization.
|
/// stabilization.
|
||||||
pub(super) fn step_kind_for_source(&self, source: GoalSource) -> PathKind {
|
pub(super) fn step_kind_for_source(&self, source: GoalSource) -> PathKind {
|
||||||
match (self.current_goal_kind, source) {
|
match source {
|
||||||
(_, GoalSource::NormalizeGoal(step_kind)) => step_kind,
|
// We treat these goals as unknown for now. It is likely that most miscellaneous
|
||||||
(CurrentGoalKind::CoinductiveTrait, GoalSource::ImplWhereBound) => {
|
// nested goals will be converted to an inductive variant in the future.
|
||||||
PathKind::Coinductive
|
//
|
||||||
|
// 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(
|
let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw(
|
||||||
GoalEvaluationKind::Nested,
|
GoalEvaluationKind::Nested,
|
||||||
GoalSource::Misc,
|
GoalSource::TypeRelating,
|
||||||
unconstrained_goal,
|
unconstrained_goal,
|
||||||
)?;
|
)?;
|
||||||
// Add the nested goals from normalization to our own nested goals.
|
// 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>>) {
|
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(
|
goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(
|
||||||
self,
|
self,
|
||||||
GoalSource::Misc,
|
GoalSource::TypeRelating,
|
||||||
goal.param_env,
|
goal.param_env,
|
||||||
));
|
));
|
||||||
self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal);
|
self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal);
|
||||||
@ -939,7 +966,15 @@ where
|
|||||||
rhs: T,
|
rhs: T,
|
||||||
) -> Result<(), NoSolution> {
|
) -> Result<(), NoSolution> {
|
||||||
let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,7 +421,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
|
|||||||
self.add_goal(
|
self.add_goal(
|
||||||
delegate,
|
delegate,
|
||||||
max_input_universe,
|
max_input_universe,
|
||||||
GoalSource::Misc,
|
GoalSource::TypeRelating,
|
||||||
goal.with(delegate.cx(), goal.predicate),
|
goal.with(delegate.cx(), goal.predicate),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -313,7 +313,9 @@ where
|
|||||||
ty::AliasRelationDirection::Equate,
|
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()?;
|
self.try_evaluate_added_goals()?;
|
||||||
Ok(self.resolve_vars_if_possible(normalized_term))
|
Ok(self.resolve_vars_if_possible(normalized_term))
|
||||||
} else {
|
} else {
|
||||||
|
@ -24,7 +24,8 @@ where
|
|||||||
ty::AliasRelationDirection::Equate,
|
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)
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use rustc_type_ir::Interner;
|
|
||||||
use rustc_type_ir::search_graph::{self, PathKind};
|
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 rustc_type_ir::{Interner, TypingMode};
|
||||||
|
|
||||||
use super::inspect::ProofTreeBuilder;
|
use super::inspect::ProofTreeBuilder;
|
||||||
use super::{FIXPOINT_STEP_LIMIT, has_no_inference_or_external_constraints};
|
use super::{FIXPOINT_STEP_LIMIT, has_no_inference_or_external_constraints};
|
||||||
@ -47,7 +47,24 @@ where
|
|||||||
) -> QueryResult<I> {
|
) -> QueryResult<I> {
|
||||||
match kind {
|
match kind {
|
||||||
PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes),
|
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)),
|
||||||
|
// Even though we know these cycles to be unproductive, we still return
|
||||||
|
// overflow during coherence. This is both as we are not 100% confident in
|
||||||
|
// the implementation yet and any incorrect errors would be unsound there.
|
||||||
|
// The affected cases are also fairly artificial and not necessarily desirable
|
||||||
|
// so keeping this as ambiguity is fine for now.
|
||||||
|
//
|
||||||
|
// See `tests/ui/traits/next-solver/cycles/unproductive-in-coherence.rs` for an
|
||||||
|
// example where this would matter. We likely should change these cycles to `NoSolution`
|
||||||
|
// even in coherence once this is a bit more settled.
|
||||||
|
PathKind::Inductive => match input.typing_mode {
|
||||||
|
TypingMode::Coherence => {
|
||||||
|
response_no_constraints(cx, input, Certainty::overflow(false))
|
||||||
|
}
|
||||||
|
TypingMode::Analysis { .. }
|
||||||
|
| TypingMode::PostBorrowckAnalysis { .. }
|
||||||
|
| TypingMode::PostAnalysis => Err(NoSolution),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,12 +74,7 @@ where
|
|||||||
input: CanonicalInput<I>,
|
input: CanonicalInput<I>,
|
||||||
result: QueryResult<I>,
|
result: QueryResult<I>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match kind {
|
Self::initial_provisional_result(cx, kind, input) == result
|
||||||
PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes) == result,
|
|
||||||
PathKind::Inductive => {
|
|
||||||
response_no_constraints(cx, input, Certainty::overflow(false)) == result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_stack_overflow(
|
fn on_stack_overflow(
|
||||||
|
@ -440,7 +440,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
|
|||||||
match (child_mode, nested_goal.source()) {
|
match (child_mode, nested_goal.source()) {
|
||||||
(
|
(
|
||||||
ChildMode::Trait(_) | ChildMode::Host(_),
|
ChildMode::Trait(_) | ChildMode::Host(_),
|
||||||
GoalSource::Misc | GoalSource::NormalizeGoal(_),
|
GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_),
|
||||||
) => {
|
) => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
/// behavior as long as the resulting behavior is still correct.
|
/// behavior as long as the resulting behavior is still correct.
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@ -20,7 +21,7 @@ use std::marker::PhantomData;
|
|||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use rustc_index::{Idx, IndexVec};
|
use rustc_index::{Idx, IndexVec};
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
use rustc_macros::HashStable_NoContext;
|
use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::data_structures::HashMap;
|
use crate::data_structures::HashMap;
|
||||||
@ -111,21 +112,35 @@ pub trait Delegate {
|
|||||||
/// In the initial iteration of a cycle, we do not yet have a provisional
|
/// 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
|
/// result. In the case we return an initial provisional result depending
|
||||||
/// on the kind of cycle.
|
/// on the kind of cycle.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
|
#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
|
||||||
pub enum PathKind {
|
pub enum PathKind {
|
||||||
Coinductive,
|
/// 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,
|
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 {
|
impl PathKind {
|
||||||
/// Returns the path kind when merging `self` with `rest`.
|
/// Returns the path kind when merging `self` with `rest`.
|
||||||
///
|
///
|
||||||
/// Given an inductive path `self` and a coinductive path `rest`,
|
/// Given an inductive path `self` and a coinductive path `rest`,
|
||||||
/// the path `self -> rest` would be coinductive.
|
/// 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 {
|
fn extend(self, rest: PathKind) -> PathKind {
|
||||||
match self {
|
match (self, rest) {
|
||||||
PathKind::Coinductive => PathKind::Coinductive,
|
(PathKind::Coinductive, _) | (_, PathKind::Coinductive) => PathKind::Coinductive,
|
||||||
PathKind::Inductive => rest,
|
(PathKind::Unknown, _) | (_, PathKind::Unknown) => PathKind::Unknown,
|
||||||
|
(PathKind::Inductive, PathKind::Inductive) => PathKind::Inductive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,9 +174,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
|
/// For each goal we track whether the paths from this goal
|
||||||
@ -297,7 +309,7 @@ impl CycleHeads {
|
|||||||
|
|
||||||
let path_from_entry = match step_kind {
|
let path_from_entry = match step_kind {
|
||||||
PathKind::Coinductive => AllPathsToHeadCoinductive::Yes,
|
PathKind::Coinductive => AllPathsToHeadCoinductive::Yes,
|
||||||
PathKind::Inductive => path_from_entry,
|
PathKind::Unknown | PathKind::Inductive => path_from_entry,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.insert(head, path_from_entry);
|
self.insert(head, path_from_entry);
|
||||||
@ -305,6 +317,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
|
/// The nested goals of each stack entry and the path from the
|
||||||
/// stack entry to that nested goal.
|
/// stack entry to that nested goal.
|
||||||
///
|
///
|
||||||
@ -322,15 +391,18 @@ impl CycleHeads {
|
|||||||
/// results from a the cycle BAB depending on the cycle root.
|
/// results from a the cycle BAB depending on the cycle root.
|
||||||
#[derive_where(Debug, Default, Clone; X: Cx)]
|
#[derive_where(Debug, Default, Clone; X: Cx)]
|
||||||
struct NestedGoals<X: Cx> {
|
struct NestedGoals<X: Cx> {
|
||||||
nested_goals: HashMap<X::Input, UsageKind>,
|
nested_goals: HashMap<X::Input, PathsToNested>,
|
||||||
}
|
}
|
||||||
impl<X: Cx> NestedGoals<X> {
|
impl<X: Cx> NestedGoals<X> {
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
self.nested_goals.is_empty()
|
self.nested_goals.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, input: X::Input, path_from_entry: UsageKind) {
|
fn insert(&mut self, input: X::Input, paths_to_nested: PathsToNested) {
|
||||||
self.nested_goals.entry(input).or_insert(path_from_entry).and_merge(path_from_entry);
|
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
|
/// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal
|
||||||
@ -341,18 +413,15 @@ impl<X: Cx> NestedGoals<X> {
|
|||||||
/// the same as for the child.
|
/// the same as for the child.
|
||||||
fn extend_from_child(&mut self, step_kind: PathKind, nested_goals: &NestedGoals<X>) {
|
fn extend_from_child(&mut self, step_kind: PathKind, nested_goals: &NestedGoals<X>) {
|
||||||
#[allow(rustc::potential_query_instability)]
|
#[allow(rustc::potential_query_instability)]
|
||||||
for (input, path_from_entry) in nested_goals.iter() {
|
for (input, paths_to_nested) in nested_goals.iter() {
|
||||||
let path_from_entry = match step_kind {
|
let paths_to_nested = paths_to_nested.extend_with(step_kind);
|
||||||
PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive),
|
self.insert(input, paths_to_nested);
|
||||||
PathKind::Inductive => path_from_entry,
|
|
||||||
};
|
|
||||||
self.insert(input, path_from_entry);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "nightly", rustc_lint_query_instability)]
|
#[cfg_attr(feature = "nightly", rustc_lint_query_instability)]
|
||||||
#[allow(rustc::potential_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))
|
self.nested_goals.iter().map(|(i, p)| (*i, *p))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,7 +559,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
|
|||||||
// goals as this change may cause them to now depend on additional
|
// goals as this change may cause them to now depend on additional
|
||||||
// goals, resulting in new cycles. See the dev-guide for examples.
|
// goals, resulting in new cycles. See the dev-guide for examples.
|
||||||
if parent_depends_on_cycle {
|
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 +735,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
|
|||||||
//
|
//
|
||||||
// We must therefore not use the global cache entry for `B` in that case.
|
// 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
|
// 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");
|
debug!("encountered stack overflow");
|
||||||
@ -749,16 +818,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
|
|||||||
|
|
||||||
// We now care about the path from the next highest cycle head to the
|
// We now care about the path from the next highest cycle head to the
|
||||||
// provisional cache entry.
|
// provisional cache entry.
|
||||||
match path_from_head {
|
*path_from_head = path_from_head.extend(Self::cycle_path_kind(
|
||||||
PathKind::Coinductive => {}
|
&self.stack,
|
||||||
PathKind::Inductive => {
|
stack_entry.step_kind_from_parent,
|
||||||
*path_from_head = Self::cycle_path_kind(
|
head,
|
||||||
&self.stack,
|
));
|
||||||
stack_entry.step_kind_from_parent,
|
|
||||||
head,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Mutate the result of the provisional cache entry in case we did
|
// Mutate the result of the provisional cache entry in case we did
|
||||||
// not reach a fixpoint.
|
// not reach a fixpoint.
|
||||||
*result = mutate_result(input, *result);
|
*result = mutate_result(input, *result);
|
||||||
@ -858,7 +922,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
|
|||||||
for &ProvisionalCacheEntry {
|
for &ProvisionalCacheEntry {
|
||||||
encountered_overflow,
|
encountered_overflow,
|
||||||
ref heads,
|
ref heads,
|
||||||
path_from_head,
|
path_from_head: head_to_provisional,
|
||||||
result: _,
|
result: _,
|
||||||
} in entries.iter()
|
} in entries.iter()
|
||||||
{
|
{
|
||||||
@ -870,24 +934,19 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
|
|||||||
|
|
||||||
// A provisional cache entry only applies if the path from its highest head
|
// A provisional cache entry only applies if the path from its highest head
|
||||||
// matches the path when encountering the goal.
|
// 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 head = heads.highest_cycle_head();
|
||||||
let full_path = match Self::cycle_path_kind(stack, step_kind_from_parent, head) {
|
let head_to_curr = Self::cycle_path_kind(stack, step_kind_from_parent, head);
|
||||||
PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive),
|
let full_paths = path_from_global_entry.extend_with(head_to_curr);
|
||||||
PathKind::Inductive => path_from_global_entry,
|
if full_paths.contains(head_to_provisional.into()) {
|
||||||
};
|
debug!(
|
||||||
|
?full_paths,
|
||||||
match (full_path, path_from_head) {
|
?head_to_provisional,
|
||||||
(UsageKind::Mixed, _)
|
"cache entry not applicable due to matching paths"
|
||||||
| (UsageKind::Single(PathKind::Coinductive), PathKind::Coinductive)
|
);
|
||||||
| (UsageKind::Single(PathKind::Inductive), PathKind::Inductive) => {
|
return false;
|
||||||
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"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -986,8 +1045,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
|
|||||||
let last = &mut self.stack[last_index];
|
let last = &mut self.stack[last_index];
|
||||||
last.reached_depth = last.reached_depth.max(next_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(input, step_kind_from_parent.into());
|
||||||
last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Inductive));
|
last.nested_goals.insert(last.input, PathsToNested::EMPTY);
|
||||||
if last_index != head {
|
if last_index != head {
|
||||||
last.heads.insert(head, step_kind_from_parent);
|
last.heads.insert(head, step_kind_from_parent);
|
||||||
}
|
}
|
||||||
|
@ -58,20 +58,24 @@ impl<I: Interner, P> Goal<I, P> {
|
|||||||
/// Why a specific goal has to be proven.
|
/// Why a specific goal has to be proven.
|
||||||
///
|
///
|
||||||
/// This is necessary as we treat nested goals different depending on
|
/// This is necessary as we treat nested goals different depending on
|
||||||
/// their source. This is currently mostly used by proof tree visitors
|
/// their source. This is used to decide whether a cycle is coinductive.
|
||||||
/// but will be used by cycle handling in the future.
|
/// 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)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
|
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
|
||||||
pub enum GoalSource {
|
pub enum GoalSource {
|
||||||
Misc,
|
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
|
/// This is also used for all `NormalizesTo` goals as we they are used
|
||||||
/// changes whether cycles are coinductive.
|
/// to relate types in `AliasRelate`.
|
||||||
|
TypeRelating,
|
||||||
|
/// We're proving a where-bound of an impl.
|
||||||
ImplWhereBound,
|
ImplWhereBound,
|
||||||
/// Const conditions that need to hold for `~const` alias bounds to hold.
|
/// Const conditions that need to hold for `~const` alias bounds to hold.
|
||||||
///
|
|
||||||
/// FIXME(-Znext-solver=coinductive): Are these even coinductive?
|
|
||||||
AliasBoundConstCondition,
|
AliasBoundConstCondition,
|
||||||
/// Instantiating a higher-ranked goal and re-proving it.
|
/// Instantiating a higher-ranked goal and re-proving it.
|
||||||
InstantiateHigherRanked,
|
InstantiateHigherRanked,
|
||||||
@ -79,7 +83,6 @@ pub enum GoalSource {
|
|||||||
/// This is used in two places: projecting to an opaque whose hidden type
|
/// 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.
|
/// is already registered in the opaque type storage, and for rigid projections.
|
||||||
AliasWellFormed,
|
AliasWellFormed,
|
||||||
|
|
||||||
/// In case normalizing aliases in nested goals cycles, eagerly normalizing these
|
/// In case normalizing aliases in nested goals cycles, eagerly normalizing these
|
||||||
/// aliases in the context of the parent may incorrectly change the cycle kind.
|
/// 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
|
/// Normalizing aliases in goals therefore tracks the original path kind for this
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
//@ check-pass
|
//@ check-pass
|
||||||
|
//@ revisions: current next
|
||||||
|
//@[next] compile-flags: -Znext-solver
|
||||||
|
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||||
|
|
||||||
trait A<'a> {}
|
trait A<'a> {}
|
||||||
trait B<'b> {}
|
trait B<'b> {}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
//@ check-pass
|
//@ 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
|
// Make sure that we don't look into associated type bounds when looking for
|
||||||
// supertraits that define an associated type. Fixes #76593.
|
// supertraits that define an associated type. Fixes #76593.
|
||||||
|
@ -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
|
--> $DIR/inherent-impls-overflow.rs:10:6
|
||||||
|
|
|
|
||||||
LL | impl Loop {}
|
LL | impl Loop {}
|
||||||
| ^^^^
|
| ^^^^ types differ
|
||||||
|
|
||||||
error: type parameter `T` is only used recursively
|
error: type parameter `T` is only used recursively
|
||||||
--> $DIR/inherent-impls-overflow.rs:14:24
|
--> $DIR/inherent-impls-overflow.rs:14:24
|
||||||
@ -36,4 +36,5 @@ LL | impl Poly0<()> {}
|
|||||||
|
|
||||||
error: aborting due to 4 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.
|
||||||
|
For more information about an error, try `rustc --explain E0271`.
|
||||||
|
@ -9,7 +9,7 @@ type Loop = Loop; //[current]~ ERROR overflow normalizing the type alias `Loop`
|
|||||||
|
|
||||||
impl Loop {}
|
impl Loop {}
|
||||||
//[current]~^ ERROR overflow normalizing the type alias `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,)>;
|
type Poly0<T> = Poly1<(T,)>;
|
||||||
//[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>`
|
//[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>`
|
||||||
|
@ -6,9 +6,11 @@
|
|||||||
trait Overflow {
|
trait Overflow {
|
||||||
type Assoc;
|
type Assoc;
|
||||||
}
|
}
|
||||||
impl<T> Overflow for T {
|
impl<T> Overflow for T
|
||||||
type Assoc = <T as Overflow>::Assoc;
|
where
|
||||||
//~^ ERROR: overflow
|
(T,): Overflow
|
||||||
|
{
|
||||||
|
type Assoc = <(T,) as Overflow>::Assoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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`
|
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 {}
|
LL | impl<T: Copy> Trait for T {}
|
||||||
| ------------------------- first implementation here
|
| ------------------------- first implementation here
|
||||||
LL | struct LocalTy;
|
LL | struct LocalTy;
|
||||||
LL | impl Trait for <LocalTy as Overflow>::Assoc {}
|
LL | impl Trait for <LocalTy as Overflow>::Assoc {}
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 this error, try `rustc --explain E0119`.
|
||||||
For more information about an error, try `rustc --explain E0119`.
|
|
||||||
|
@ -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() {}
|
@ -0,0 +1,21 @@
|
|||||||
|
// If we treat known inductive cycles as errors, this test compiles
|
||||||
|
// as normalizing `Overflow::Assoc<Overflow>` fails.
|
||||||
|
//
|
||||||
|
// As coherence already uses the new solver on stable, this change
|
||||||
|
// would require an FCP.
|
||||||
|
|
||||||
|
trait Trait {
|
||||||
|
type Assoc<T: Trait>;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Overflow;
|
||||||
|
impl Trait for Overflow {
|
||||||
|
type Assoc<T: Trait> = <T as Trait>::Assoc<Overflow>;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Overlap<T, WfHack> {}
|
||||||
|
impl<T: Trait, U: Copy> Overlap<T::Assoc<T>, U> for T {}
|
||||||
|
impl<U> Overlap<u32, U> for Overflow {}
|
||||||
|
//~^ ERROR conflicting implementations of trait `Overlap<<Overflow as Trait>::Assoc<Overflow>, _>` for type `Overflow`
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,11 @@
|
|||||||
|
error[E0119]: conflicting implementations of trait `Overlap<<Overflow as Trait>::Assoc<Overflow>, _>` for type `Overflow`
|
||||||
|
--> $DIR/unproductive-in-coherence.rs:18:1
|
||||||
|
|
|
||||||
|
LL | impl<T: Trait, U: Copy> Overlap<T::Assoc<T>, U> for T {}
|
||||||
|
| ----------------------------------------------------- first implementation here
|
||||||
|
LL | impl<U> Overlap<u32, U> for Overflow {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Overflow`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
@ -13,12 +13,7 @@ fn needs_bar<S: Bar>() {}
|
|||||||
|
|
||||||
fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() {
|
fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() {
|
||||||
needs_bar::<T::Assoc1>();
|
needs_bar::<T::Assoc1>();
|
||||||
//~^ ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
|
//~^ ERROR the trait bound `<T as Foo1>::Assoc1: Bar` is not satisfied
|
||||||
//~| 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`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -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
|
--> $DIR/recursive-self-normalization-2.rs:15:17
|
||||||
|
|
|
|
||||||
LL | needs_bar::<T::Assoc1>();
|
LL | needs_bar::<T::Assoc1>();
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^ the trait `Bar` is not implemented for `<T as Foo1>::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>();
|
|
||||||
| ^^^^^^^^^
|
|
||||||
|
|
|
|
||||||
note: required by a bound in `needs_bar`
|
note: required by a bound in `needs_bar`
|
||||||
--> $DIR/recursive-self-normalization-2.rs:12:17
|
--> $DIR/recursive-self-normalization-2.rs:12:17
|
||||||
|
|
|
|
||||||
LL | fn needs_bar<S: Bar>() {}
|
LL | fn needs_bar<S: Bar>() {}
|
||||||
| ^^^ required by this bound in `needs_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`
|
error: aborting due to 1 previous error
|
||||||
--> $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[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
|
For more information about this error, try `rustc --explain E0277`.
|
||||||
--> $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`.
|
|
||||||
|
@ -9,12 +9,7 @@ fn needs_bar<S: Bar>() {}
|
|||||||
|
|
||||||
fn test<T: Foo<Assoc = <T as Foo>::Assoc>>() {
|
fn test<T: Foo<Assoc = <T as Foo>::Assoc>>() {
|
||||||
needs_bar::<T::Assoc>();
|
needs_bar::<T::Assoc>();
|
||||||
//~^ ERROR overflow evaluating the requirement `<T as Foo>::Assoc == _`
|
//~^ ERROR the trait bound `<T as Foo>::Assoc: Bar` is not satisfied
|
||||||
//~| 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`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -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
|
--> $DIR/recursive-self-normalization.rs:11:17
|
||||||
|
|
|
|
||||||
LL | needs_bar::<T::Assoc>();
|
LL | needs_bar::<T::Assoc>();
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^ the trait `Bar` is not implemented for `<T as Foo>::Assoc`
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc: Bar`
|
|
||||||
--> $DIR/recursive-self-normalization.rs:11:17
|
|
||||||
|
|
|
||||||
LL | needs_bar::<T::Assoc>();
|
|
||||||
| ^^^^^^^^
|
|
||||||
|
|
|
|
||||||
note: required by a bound in `needs_bar`
|
note: required by a bound in `needs_bar`
|
||||||
--> $DIR/recursive-self-normalization.rs:8:17
|
--> $DIR/recursive-self-normalization.rs:8:17
|
||||||
|
|
|
|
||||||
LL | fn needs_bar<S: Bar>() {}
|
LL | fn needs_bar<S: Bar>() {}
|
||||||
| ^^^ required by this bound in `needs_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`
|
error: aborting due to 1 previous error
|
||||||
--> $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[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _`
|
For more information about this error, try `rustc --explain E0277`.
|
||||||
--> $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`.
|
|
||||||
|
Loading…
Reference in New Issue
Block a user