Skip to content
Draft
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
10 changes: 10 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.type_matches_expected_vid(expected_vid, data.self_ty())
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
if !matches!(
data.projection_term.kind(self.tcx),
ty::AliasTermKind::ProjectionTy
| ty::AliasTermKind::ProjectionConst
| ty::AliasTermKind::InherentTy
| ty::AliasTermKind::InherentConst
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the NormalizesTo is going to be replaced by Projection, this function can be called upon some non-projection aliases

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an exhaustive match please (and comment)

) {
return false;
}

self.type_matches_expected_vid(expected_vid, data.projection_term.self_ty())
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
Expand Down
21 changes: 16 additions & 5 deletions compiler/rustc_next_trait_solver/src/canonical/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,19 @@ pub(super) fn instantiate_and_apply_query_response<D, I>(
original_values: &[I::GenericArg],
response: CanonicalResponse<I>,
span: I::Span,
prev_universe: ty::UniverseIndex,
) -> (NestedNormalizationGoals<I>, Certainty)
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
let instantiation =
compute_query_response_instantiation_values(delegate, &original_values, &response, span);
let instantiation = compute_query_response_instantiation_values(
delegate,
&original_values,
&response,
span,
prev_universe,
);

let Response { var_values, external_constraints, certainty } =
delegate.instantiate_canonical(response, instantiation);
Expand All @@ -127,6 +133,7 @@ fn compute_query_response_instantiation_values<D, I, T>(
original_values: &[I::GenericArg],
response: &Canonical<I, T>,
span: I::Span,
prev_universe: ty::UniverseIndex,
) -> CanonicalVarValues<I>
where
D: SolverDelegate<Interner = I>,
Expand All @@ -136,7 +143,6 @@ where
// FIXME: Longterm canonical queries should deal with all placeholders
// created inside of the query directly instead of returning them to the
// caller.
let prev_universe = delegate.universe();
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change here is

  • normal instantiation: unaffected.
  • instantiation inside the very same query: use the max_input_universe instead of the delegate's universe.

Currently, we don't instantiate the canonicalized response within the very same query, but as per rust-lang/trait-system-refactor-initiative#223 (comment) (5. the root of the Projection goal then instantiates this query result inside of the same query using the var_values with the added entry for the new infer var) we should do.

In such cases, the current instantiation is not so idempotent modulo universes.

Suppose that the current query's max_input_universe is U(N) and we have the max universe U(N + 1) when computing the projection goal by here, because we have some higher-kinded binder in the goal,

pub(super) fn compute_goal(&mut self, goal: Goal<I, I::Predicate>) -> QueryResult<I> {
let Goal { param_env, predicate } = goal;
let kind = predicate.kind();
self.enter_forall(kind, |ecx, kind| match kind {

let prev_universe = delegate.universe();
let universes_created_in_query = response.max_universe.index();
for _ in 0..universes_created_in_query {
delegate.create_next_universe();
}

And we have some max universe U(M) in the probe's canonicalized response, instantiating it increases the current query's universe index by M, so it becomes U(N + 1 + M) and the prev_universe is U(N + 1).

Suppose that we have a region var with universe U(i) in the probe's canonical response.
We instantiate it into the universe U(i + N + 1) in the following lines

CanonicalVarValues::instantiate(delegate.cx(), response.var_kinds, |var_values, kind| {
if kind.universe() != ty::UniverseIndex::ROOT {
// A variable from inside a binder of the query. While ideally these shouldn't
// exist at all (see the FIXME at the start of this method), we have to deal with
// them for now.
delegate.instantiate_canonical_var(kind, span, &var_values, |idx| {
prev_universe + idx.index()
})

Then when we finally finishing the query, we canonicalize it as a response, but since our max_input_universe is U(N)

ty::ReVar(vid) => {
debug_assert_eq!(
self.delegate.opportunistic_resolve_lt_var(vid),
r,
"region vid should have been resolved fully before canonicalization"
);
match self.canonicalize_mode {
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
CanonicalizeMode::Response { .. } => {
CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
}

CanonicalizeMode::Response { max_input_universe } => {
for var in var_kinds.iter_mut() {
let uv = var.universe();
let new_uv = ty::UniverseIndex::from(
uv.index().saturating_sub(max_input_universe.index()),
);
*var = var.with_updated_universe(new_uv);
}
var_kinds
.iter()
.map(|kind| kind.universe())
.max()
.unwrap_or(ty::UniverseIndex::ROOT)
}

the resulting universe of that region becomes U(i + N + 1 - N) = U(i + 1), increased by 1 from the probe's response.

This might result into a leak check failure or other higher-kinded error. So, we should either do use max_input_universe to instantiate the canonical response from the probe or use the delegate's universe when canonicalizing the final response. I chose the former as it feels more hard to mistake with. (Sorry for the messy language since I'm very tired rn 😅 )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i agree, annoying issue. I think we shouldn't canonicalize an input while inside of a binder, so not immediately clear how to handle higher-kinded Projection goals 🤔 I think this is definitely an issue, not happy with your current fix for it though 😅

Copy link
Copy Markdown
Member Author

@ShoyuVanilla ShoyuVanilla Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll think about the proper fix 😅
Edit) Looks like the intermediate step you've suggested would be the road to it, right?

let universes_created_in_query = response.max_universe.index();
for _ in 0..universes_created_in_query {
delegate.create_next_universe();
Expand Down Expand Up @@ -328,8 +334,13 @@ where
.map(|&arg| delegate.fresh_var_for_kind_with_span(arg, span)),
);

let instantiation =
compute_query_response_instantiation_values(delegate, orig_values, &state, span);
let instantiation = compute_query_response_instantiation_values(
delegate,
orig_values,
&state,
span,
delegate.universe(),
);

let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation);

Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_next_trait_solver/src/solve/alias_relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,13 @@ where
let term = self.next_term_infer_of_kind(lhs);
self.add_goal(
GoalSource::TypeRelating,
goal.with(cx, ty::NormalizesTo { alias, term }),
goal.with(
cx,
ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_term: alias,
term,
}),
),
);
term
} else {
Expand All @@ -64,7 +70,13 @@ where
let term = self.next_term_infer_of_kind(rhs);
self.add_goal(
GoalSource::TypeRelating,
goal.with(cx, ty::NormalizesTo { alias, term }),
goal.with(
cx,
ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_term: alias,
term,
}),
),
);
term
} else {
Expand Down
48 changes: 26 additions & 22 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ mod probe;
/// This has effects on cycle handling handling and on how we compute
/// query responses, see the variant descriptions for more info.
#[derive(Debug, Copy, Clone)]
enum CurrentGoalKind {
pub(super) enum CurrentGoalKind {
Misc,
/// We're proving an trait goal for a coinductive trait, either an auto trait or `Sized`.
///
Expand Down Expand Up @@ -93,15 +93,15 @@ where
/// If some `InferCtxt` method is missing, please first think defensively about
/// the method's compatibility with this solver, or if an existing one does
/// the job already.
delegate: &'a D,
pub(super) delegate: &'a D,

/// The variable info for the `var_values`, only used to make an ambiguous response
/// with no constraints.
var_kinds: I::CanonicalVarKinds,
pub(super) var_kinds: I::CanonicalVarKinds,

/// What kind of goal we're currently computing, see the enum definition
/// for more info.
current_goal_kind: CurrentGoalKind,
pub(super) current_goal_kind: CurrentGoalKind,
pub(super) var_values: CanonicalVarValues<I>,

/// The highest universe index nameable by the caller.
Expand Down Expand Up @@ -486,6 +486,7 @@ where
&orig_values,
response,
self.origin_span,
self.delegate.universe(),
);

// FIXME: We previously had an assert here that checked that recomputing
Expand Down Expand Up @@ -564,7 +565,7 @@ where
pub(super) fn compute_goal(&mut self, goal: Goal<I, I::Predicate>) -> QueryResult<I> {
let Goal { param_env, predicate } = goal;
let kind = predicate.kind();
self.enter_forall(kind, |ecx, kind| match kind {
let resp = self.enter_forall(kind, |ecx, kind| match kind {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
ecx.compute_trait_goal(Goal { param_env, predicate }).map(|(r, _via)| r)
}
Expand Down Expand Up @@ -604,16 +605,18 @@ where
ty::PredicateKind::ConstEquate(_, _) => {
panic!("ConstEquate should not be emitted when `-Znext-solver` is active")
}
ty::PredicateKind::NormalizesTo(predicate) => {
ecx.compute_normalizes_to_goal(Goal { param_env, predicate })
ty::PredicateKind::NormalizesTo(_) => {
unreachable!()
}
ty::PredicateKind::AliasRelate(lhs, rhs, direction) => {
ecx.compute_alias_relate_goal(Goal { param_env, predicate: (lhs, rhs, direction) })
}
ty::PredicateKind::Ambiguous => {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
})
})?;
debug_assert!(resp.value.external_constraints.normalization_nested_goals.is_empty());
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok(resp)
}

// Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning
Expand Down Expand Up @@ -806,6 +809,7 @@ where
///
/// This is the case if the `term` does not occur in any other part of the predicate
/// and is able to name all other placeholder and inference variables.
#[allow(unused)]
#[instrument(level = "trace", skip(self), ret)]
pub(super) fn term_is_fully_unconstrained(&self, goal: Goal<I, ty::NormalizesTo<I>>) -> bool {
let universe_of_term = match goal.predicate.term.kind() {
Expand All @@ -832,6 +836,7 @@ where
cache: HashSet<I::Ty>,
}

#[allow(unused)]
impl<D: SolverDelegate<Interner = I>, I: Interner> ContainsTermOrNotNameable<'_, D, I> {
fn check_nameable(&self, universe: ty::UniverseIndex) -> ControlFlow<()> {
if self.universe_of_term.can_name(universe) {
Expand Down Expand Up @@ -1423,16 +1428,15 @@ where

fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
match ty.kind() {
ty::Alias(..) if !ty.has_escaping_bound_vars() => {
ty::Alias(_, alias) if !ty.has_escaping_bound_vars() => {
let infer_ty = self.ecx.next_ty_infer();
let normalizes_to = ty::PredicateKind::AliasRelate(
ty.into(),
infer_ty.into(),
ty::AliasRelationDirection::Equate,
);
let projection = ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_term: alias.into(),
term: infer_ty.into(),
});
self.ecx.add_goal(
self.normalization_goal_source,
Goal::new(self.cx(), self.param_env, normalizes_to),
Goal::new(self.cx(), self.param_env, projection),
);
infer_ty
}
Expand All @@ -1452,16 +1456,15 @@ where

fn fold_const(&mut self, ct: I::Const) -> I::Const {
match ct.kind() {
ty::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => {
ty::ConstKind::Unevaluated(uc) if !ct.has_escaping_bound_vars() => {
let infer_ct = self.ecx.next_const_infer();
let normalizes_to = ty::PredicateKind::AliasRelate(
ct.into(),
infer_ct.into(),
ty::AliasRelationDirection::Equate,
);
let projection = ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_term: uc.into(),
term: infer_ct.into(),
});
self.ecx.add_goal(
self.normalization_goal_source,
Goal::new(self.cx(), self.param_env, normalizes_to),
Goal::new(self.cx(), self.param_env, projection),
);
infer_ct
}
Expand Down Expand Up @@ -1528,6 +1531,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree<D: SolverDelegate<Interner = I>,
&proof_tree.orig_values,
response,
origin_span,
delegate.universe(),
);

(Ok(normalization_nested_goals), proof_tree)
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ mod assembly;
mod effect_goals;
mod eval_ctxt;
pub mod inspect;
mod normalizes_to;
mod project_goals;
mod search_graph;
mod trait_goals;
Expand Down
31 changes: 0 additions & 31 deletions compiler/rustc_next_trait_solver/src/solve/project_goals.rs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ where
#[instrument(level = "trace", skip(self), ret)]
pub(super) fn normalize_anon_const(
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
goal: Goal<I, ty::ProjectionPredicate<I>>,
) -> QueryResult<I> {
if self.typing_mode() == TypingMode::Coherence
&& self.cx().anon_const_kind(goal.predicate.alias.def_id) == ty::AnonConstKind::OGCA
&& self.cx().anon_const_kind(goal.predicate.projection_term.def_id)
== ty::AnonConstKind::OGCA
{
// During coherence, OGCA consts should be normalized ambiguously
// because they are opaque but eventually resolved to a real value.
Expand All @@ -28,11 +29,11 @@ where
} else if let Some(normalized_const) = self.evaluate_const(
goal.param_env,
ty::UnevaluatedConst::new(
goal.predicate.alias.def_id.try_into().unwrap(),
goal.predicate.alias.args,
goal.predicate.projection_term.def_id.try_into().unwrap(),
goal.predicate.projection_term.args,
),
) {
self.instantiate_normalizes_to_term(goal, normalized_const.into());
self.instantiate_projection_term(goal, normalized_const.into())?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ where
{
pub(super) fn normalize_free_alias(
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
goal: Goal<I, ty::ProjectionPredicate<I>>,
) -> QueryResult<I> {
let cx = self.cx();
let free_alias = goal.predicate.alias;
let free_alias = goal.predicate.projection_term;

// Check where clauses
self.add_goals(
Expand All @@ -35,7 +35,7 @@ where
cx.const_of_item(free_alias.def_id).instantiate(cx, free_alias.args).into()
};

self.instantiate_normalizes_to_term(goal, actual);
self.instantiate_projection_term(goal, actual)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ where
{
pub(super) fn normalize_inherent_associated_term(
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
goal: Goal<I, ty::ProjectionPredicate<I>>,
) -> QueryResult<I> {
let cx = self.cx();
let inherent = goal.predicate.alias;
let inherent = goal.predicate.projection_term;

let impl_def_id = cx.parent(inherent.def_id);
let impl_args = self.fresh_args_for_item(impl_def_id);
Expand Down Expand Up @@ -56,7 +56,7 @@ where
} else {
cx.const_of_item(inherent.def_id).instantiate(cx, inherent_args).into()
};
self.instantiate_normalizes_to_term(goal, normalized);
self.instantiate_projection_term(goal, normalized)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
Loading
Loading