Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 35 additions & 52 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ enum CurrentGoalKind {
/// These are currently the only goals whose impl where-clauses are considered to be
/// productive steps.
CoinductiveTrait,
/// A `ProjectionGoal`. These are temporarily special wrt to cycle handling.
Projection,
// FIXME: Consider renaming `PredicateKind::NormalizesTo` to match with this
/// Unlike other goals, `NormalizesTo` goals aren't independent goals but just implementation
/// details for handling projections of associated terms. When we encounter a `Projection` goal
Expand All @@ -89,6 +91,7 @@ impl CurrentGoalKind {
CurrentGoalKind::Misc
}
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => CurrentGoalKind::Projection,
ty::PredicateKind::NormalizesTo(_) => {
CurrentGoalKind::ProjectionComputeAssocTermCandidate
}
Expand Down Expand Up @@ -428,46 +431,29 @@ where
}

/// Computes the `PathKind` for the step from the current goal to the
/// nested goal required due to `source`.
/// nested goal. See #136824 for a more detailed reasoning for this why we care about
/// the step from a goal to its nested goals.
///
/// See #136824 for a more detailed reasoning for this behavior. We
/// consider cycles to be coinductive if they 'step into' a where-clause
/// of a coinductive trait. We will likely extend this function in the future
/// 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 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 => match self.current_goal_kind {
// We currently only consider a cycle coinductive if it steps
// into a where-clause of a coinductive trait.
CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive,
// 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 | 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
// introduces a constructor which could cause the recursion to be guarded.
//
// FIXME(-Znext-solver=coinductive): For now we treat all inductive cycles as
// `Unknown`. See the comment in `fn initial_provisional_result`.
GoalSource::TypeRelating => PathKind::Unknown,
// 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,
/// For now we're entirely ignoring the reason why the current goal depends on the
/// nested goal and instead only depend on the current goal itself. This closely
/// matches the old solver. This is something we'll likely improve as we go forward
/// afterwards.
// FIXME(-Znext-solver=coinductive): Nothing interesting going on here right now
// and we're ignoring the goal source. We should change this going forward.
pub(super) fn step_kind_to_nested(&self, _source: GoalSource) -> PathKind {
match self.current_goal_kind {
// We currently consider a cycle involving a trait goal for a coinductive
// trait as coinductive as long as it otherwise only includes normalization.
CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive,
// We do need to treat cycles involving only coinductive trait goals
// and normalization as coinductive due to trait-system-refactor-initiative#10.
CurrentGoalKind::ProjectionComputeAssocTermCandidate | CurrentGoalKind::Projection => {
PathKind::Unknown
}
// 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::ForcedAmbiguity,
}
}

Expand Down Expand Up @@ -771,7 +757,7 @@ where
let (goal, opaque_types) =
self.delegate.deeply_resolve_via_unification_table((goal, opaque_types));
let typing_mode = self.typing_mode();
let step_kind = self.step_kind_for_source(source);
let step_kind = self.step_kind_to_nested(source);

let tracing_span = tracing::span!(
Level::DEBUG,
Expand Down Expand Up @@ -1109,11 +1095,8 @@ where
source: GoalSource,
mut goal: Goal<I, I::Predicate>,
) -> Result<(), NoSolutionOrRerunNonErased> {
goal.predicate = self.normalize(
GoalSource::NormalizeGoal(self.step_kind_for_source(source)),
goal.param_env,
ty::Unnormalized::new_wip(goal.predicate),
)?;
goal.predicate =
self.normalize(goal.param_env, ty::Unnormalized::new_wip(goal.predicate))?;
self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal);

if let Some(GoalEvaluation { goal, certainty, has_changed: _, stalled_on }) =
Expand Down Expand Up @@ -1339,10 +1322,12 @@ where
let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?;
for &goal in goals.iter() {
let source = match goal.predicate.kind().skip_binder() {
ty::PredicateKind::Subtype { .. }
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => {
GoalSource::TypeRelating
ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => {
GoalSource::Normalization
}
// FIXME(-Znext-solver=coinductive): subtyping goals should
// likely be unproductive
ty::PredicateKind::Subtype { .. } => GoalSource::Misc,
// FIXME(-Znext-solver=coinductive): should these WF goals also be unproductive?
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => GoalSource::Misc,
p => unreachable!("unexpected nested goal in `relate`: {p:?}"),
Expand Down Expand Up @@ -1519,9 +1504,7 @@ where
match self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)? {}
}

self.delegate.evaluate_const(param_env, alias_const, |ty| {
self.normalize(GoalSource::Misc, param_env, ty)
})
self.delegate.evaluate_const(param_env, alias_const, |ty| self.normalize(param_env, ty))
}

pub(super) fn evaluate_const_and_instantiate_projection_term(
Expand Down Expand Up @@ -1803,7 +1786,6 @@ where

pub(super) fn normalize<T: TypeFoldable<I>>(
&mut self,
source: GoalSource,
param_env: I::ParamEnv,
value: ty::Unnormalized<I, T>,
) -> Result<T, NoSolutionOrRerunNonErased> {
Expand All @@ -1819,6 +1801,7 @@ where
let infer_term = self.next_term_infer_of_alias_kind(alias_term);
let pred = ty::ProjectionClause { projection_term: alias_term, term: infer_term };
let goal = Goal::new(self.cx(), param_env, pred);
let source = GoalSource::Normalization;
self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal);
let GoalEvaluation { goal, certainty, has_changed: _, stalled_on } =
self.evaluate_goal(source, goal, None)?;
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ where
goal: Goal<I, ty::OutlivesClause<I, I::Ty>>,
) -> QueryResultOrRerunNonErased<I> {
let ty::OutlivesClause(ty, lt) = goal.predicate;
let ty = self.normalize(GoalSource::Misc, goal.param_env, ty::Unnormalized::new_wip(ty))?;
let ty = self.normalize(goal.param_env, ty::Unnormalized::new_wip(ty))?;

// The normalized type can still contain non-rigid higher ranked aliases if their
// normalization ends up with ambiguity. Or we have non-rigid aliases inside rigid ones.
Expand Down Expand Up @@ -405,7 +405,7 @@ where
);
// We normalize the self type to be able to relate it with
// types from candidates.
self.add_goal(GoalSource::TypeRelating, projection_goal)?;
self.add_goal(GoalSource::Normalization, projection_goal)?;
self.try_evaluate_added_goals()?;
Ok(self.deeply_resolve_ignoring_regions(normalized_term))
} else {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,15 +439,15 @@ where
let term = match target_item_kind {
ty::AliasTermKind::ProjectionTy { .. } => {
let t = cx.type_of(target_item_def_id).instantiate(cx, target_args);
let t = ecx.normalize(GoalSource::Misc, goal.param_env, t)?;
let t = ecx.normalize(goal.param_env, t)?;
t.into()
}
ty::AliasTermKind::ProjectionConst { def_id }
if let Some(c) =
cx.const_of_item(ty::AliasConstKind::Projection { def_id }) =>
{
let c = c.instantiate(cx, target_args);
let c = ecx.normalize(GoalSource::Misc, goal.param_env, c)?;
let c = ecx.normalize(goal.param_env, c)?;
c.into()
}
ty::AliasTermKind::ProjectionConst { .. } => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ where
let actual = match free_alias.kind {
ty::AliasTermKind::FreeTy { def_id } => {
let free = cx.type_of(def_id.into()).instantiate(cx, free_alias.args);
let free = self.normalize(GoalSource::Misc, goal.param_env, free)?;
let free = self.normalize(goal.param_env, free)?;
free.into()
}
ty::AliasTermKind::FreeConst { def_id }
if let Some(free) = cx.const_of_item(ty::AliasConstKind::Free { def_id }) =>
{
let free = free.instantiate(cx, free_alias.args);
let free = self.normalize(GoalSource::Misc, goal.param_env, free)?;
let free = self.normalize(goal.param_env, free)?;

free.into()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ where
let normalized: I::Term = match inherent_kind {
ty::AliasTermKind::InherentTy { def_id } => {
let inherent = cx.type_of(def_id.into()).instantiate(cx, inherent_args);
let inherent = self.normalize(GoalSource::Misc, goal.param_env, inherent)?;
let inherent = self.normalize(goal.param_env, inherent)?;
inherent.into()
}
ty::AliasTermKind::InherentConstImpl { def_id }
if let Some(inherent) =
cx.const_of_item(ty::AliasConstKind::InherentImpl { def_id }) =>
{
let inherent = inherent.instantiate(cx, inherent_args);
let normalized_ct = self.normalize(GoalSource::Misc, goal.param_env, inherent)?;
let normalized_ct = self.normalize(goal.param_env, inherent)?;
let normalized = normalized_ct.into();
let term = ty::AliasTerm::new_from_args(cx, inherent_kind, inherent_args);
self.push_const_arg_has_type_goal(goal.param_env, term, normalized)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ where
let (
NestedNormalizationGoals(nested_goals),
GoalEvaluation { goal: _, certainty, stalled_on: _, has_changed: _ },
) = self.evaluate_goal_raw(GoalSource::TypeRelating, normalizes_to)?;
) = self.evaluate_goal_raw(GoalSource::Normalization, normalizes_to)?;

trace!(?nested_goals);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ where
_ => re,
})
});
let actual =
self.normalize(GoalSource::Misc, goal.param_env, actual)?;
let actual = self.normalize(goal.param_env, actual)?;
self.eq(goal.param_env, expected, actual)?;
}
TypingMode::Coherence
Expand Down Expand Up @@ -145,7 +144,7 @@ where
_ => re,
})
});
let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?;
let actual = self.normalize(goal.param_env, actual)?;
self.eq(goal.param_env, expected, actual)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
.map_err(Into::into)
Expand All @@ -154,7 +153,7 @@ where
TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => {
// FIXME: Add an assertion that opaque type storage is empty.
let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args);
let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?;
let actual = self.normalize(goal.param_env, actual)?;
self.eq(goal.param_env, expected, actual)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
.map_err(Into::into)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ 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::Normalization,
) => {
continue;
}
Expand Down
24 changes: 6 additions & 18 deletions compiler/rustc_type_ir/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use tracing::debug;
use crate::inherent::*;
use crate::lang_items::SolverTraitLangItem;
use crate::region_constraint::RegionConstraint;
use crate::search_graph::PathKind;
use crate::{
self as ty, Canonical, CanonicalVarValues, CantBeErased, Const, ConstVid, FloatVid,
GenericArgKind, InferConst, IntVid, Interner, TermKind, TyVid, TypingMode, Upcast,
Expand Down Expand Up @@ -411,22 +410,15 @@ 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 used to decide whether a cycle is coinductive.
/// See the documentation of `EvalCtxt::step_kind_for_source` for more details
/// about this.
/// This is used by proof tree visitors, especially for diagnostics purposes.
///
/// It is also used by proof tree visitors, e.g. for diagnostics purposes.
/// FIXME(-Znext-solver=coinductive): This will also matter in the future when
/// deciding whether a step in a cycle is coinductive. We're currently still
/// matching the old solver behavior here for now, so the `GoalSource` is ignored.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub enum GoalSource {
Misc,
/// 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
/// 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.
Expand All @@ -438,12 +430,8 @@ pub enum GoalSource {
/// 2. for rigid projections's trait goal,
/// 3. for GAT where clauses.
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
/// nested goal. See the comment of the `ReplaceAliasWithInfer` visitor for more
/// details.
NormalizeGoal(PathKind),
/// Normalizing happens in the current context and is unproductive by itself.
Normalization,
}

#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, Goal<I, P>)]
Expand Down
12 changes: 6 additions & 6 deletions tests/ui/sized-hierarchy/overflow.current.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error[E0275]: overflow evaluating the requirement `Element: MetaSized`
--> $DIR/overflow.rs:17:16
--> $DIR/overflow.rs:15:16
|
LL | struct Element(<Box<Box<Element>> as ParseTokens>::Output);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: required for `Box<Element>` to implement `ParseTokens`
--> $DIR/overflow.rs:13:31
--> $DIR/overflow.rs:11:31
|
LL | impl<T: ParseTokens + ?Sized> ParseTokens for Box<T> {
| ------ ^^^^^^^^^^^ ^^^^^^
Expand All @@ -15,25 +15,25 @@ LL | impl<T: ParseTokens + ?Sized> ParseTokens for Box<T> {
= note: required for `Box<Box<Element>>` to implement `ParseTokens`

error[E0275]: overflow evaluating the requirement `Box<Element>: ParseTokens`
--> $DIR/overflow.rs:19:22
--> $DIR/overflow.rs:18:22
|
LL | impl ParseTokens for Element {
| ^^^^^^^
|
note: required for `Box<Box<Element>>` to implement `ParseTokens`
--> $DIR/overflow.rs:13:31
--> $DIR/overflow.rs:11:31
|
LL | impl<T: ParseTokens + ?Sized> ParseTokens for Box<T> {
| ----------- ^^^^^^^^^^^ ^^^^^^
| |
| unsatisfied trait bound introduced here
note: required because it appears within the type `Element`
--> $DIR/overflow.rs:17:8
--> $DIR/overflow.rs:15:8
|
LL | struct Element(<Box<Box<Element>> as ParseTokens>::Output);
| ^^^^^^^
note: required by a bound in `ParseTokens`
--> $DIR/overflow.rs:10:1
--> $DIR/overflow.rs:8:1
|
LL | / trait ParseTokens {
LL | | type Output;
Expand Down
29 changes: 29 additions & 0 deletions tests/ui/sized-hierarchy/overflow.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
error[E0275]: overflow evaluating the requirement `<Box<Box<Element>> as ParseTokens>::Output == _`
--> $DIR/overflow.rs:15:16
|
LL | struct Element(<Box<Box<Element>> as ParseTokens>::Output);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0275]: overflow evaluating whether `<Box<Box<Element>> as ParseTokens>::Output` is well-formed
--> $DIR/overflow.rs:15:16
|
LL | struct Element(<Box<Box<Element>> as ParseTokens>::Output);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0275]: overflow evaluating the requirement `Element: MetaSized`
--> $DIR/overflow.rs:18:22
|
LL | impl ParseTokens for Element {
| ^^^^^^^
|
note: required by a bound in `ParseTokens`
--> $DIR/overflow.rs:8:1
|
LL | / trait ParseTokens {
LL | | type Output;
LL | | }
| |_^ required by this bound in `ParseTokens`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0275`.
Loading
Loading