Skip to content

generic_const_args: allow paths to non type consts#155341

Open
khyperia wants to merge 2 commits intorust-lang:mainfrom
khyperia:non-type-const
Open

generic_const_args: allow paths to non type consts#155341
khyperia wants to merge 2 commits intorust-lang:mainfrom
khyperia:non-type-const

Conversation

@khyperia
Copy link
Copy Markdown
Contributor

@khyperia khyperia commented Apr 15, 2026

tracking issue: #151972

Non type consts should be usable in the type system in feature(generic_const_args). These are directly plugged into the constant evaluator, unlike type consts, which are attempted to be reasoned about by the type system.

Inherent associated constants are not supported at this time, due to complications around how generic arguments are represented for them (it's currently a mess). The mess is being cleaned up (e.g. #154758), so instead of trying to hack support in before the refactoring is done, let's just wait to be able to implement it more cleanly.

r? @BoxyUwU

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 15, 2026

changes to the core type system

cc @lcnr

Some changes occurred to the core trait solver

cc @rust-lang/initiative-trait-system-refactor

Some changes occurred in engine.rs, potentially modifying the public API of ObligationCtxt.

cc @lcnr

Some changes occurred in const_evaluatable.rs

cc @BoxyUwU

HIR ty lowering was modified

cc @fmease

changes to the core type system

cc @lcnr

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver) labels Apr 15, 2026
Copy link
Copy Markdown
Member

@BoxyUwU BoxyUwU left a comment

Choose a reason for hiding this comment

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

cool ✨ two big picture things:

  1. you've changed evaluate_const to take an unevaluated const instead of a ty::Const. iirc we talked about doing this to make IACs work but since we've dropped support for them can those changes be reverted?
  2. if it would be kinda chill for you to do can you make the change of adding fields to AliasTermKind's variants be in a separate commit so it's easier to review just the "meaningful" stuff? If it's non-trivial/difficult it's fine to keep it as is 🤔

View changes since this review


// Skip type consts as mGCA doesn't support evaluatable clauses.
if self.tcx.is_type_const(uv.def) {
if self.tcx.is_type_const(uv.def) || self.tcx.features().generic_const_args() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

do you remember why this was needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not really. Removing it!

  • the similar line of code in wf.rs is needed because
    cannot satisfy `the constant `<T as Trait>::PROJECTED` can be evaluated`
    
    (T is a generic parameter to the function, fn f<T: Trait>(), which blocks const eval, because, free var)
  • this is the other place that ty::ClauseKind::ConstEvaluatable is constructed, and I think I slapped on the same guard to this place in predicates_of.rs without fully understanding how/why this line of code is hit

Comment on lines +3038 to +3041
&& !(matches!(tcx.def_kind(def_id), DefKind::AssocConst { is_type_const: false })
&& matches!(
tcx.def_kind(tcx.parent(def_id)),
DefKind::Impl { of_trait: false }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can you move this out to a let is_iac so it's easier to understand what we're actually conditional on

cx.const_of_item(free_alias.def_id).instantiate(cx, free_alias.args).into()
}
ty::AliasTermKind::FreeConst { is_type_const: false } => {
self.try_evaluate_added_goals()?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

do you remember why this was necessary? 🤔 (and same for the call in the other normalization routines in the new solver)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was originally needed for IACs, because the "gen fresh vars, eq self with impl type, see what vars resolved to" song-and-dance needed to be computed to be able to see the concrete args for const eval.

I left it in since I was scared that some parent caller of this function might pass in some vars that have had uncomputed goals added about them that would resolve said vars, but I suppose that might not happen, I'm not quite sure how the new trait solver works and if we're guaranteed to be in a context without Stuff(tm) happening above it.

I'll remove it!

cx.const_of_item(inherent.def_id).instantiate(cx, inherent_args).into()
}
ty::AliasTermKind::InherentConst { is_type_const: false } => {
// FIXME(gca): This is dead code at the moment.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it's ok to leave the logic here as unreachable!() with a FIXME, same for the equivalent logic in the old solver

Comment thread compiler/rustc_type_ir/src/predicate.rs Outdated
Comment on lines +521 to +525
ProjectionConst { is_type_const: bool },
/// A top level const item not part of a trait or impl.
FreeConst,
FreeConst { is_type_const: bool },
/// An associated const in an inherent `impl`
InherentConst,
InherentConst { is_type_const: bool },
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

cc @WaffleLapkin when AliasTermKind has been updated to store DefIds like you've done for AliasTyKind this can be removed and we can just call tcx.is_type_const(def) everywhere instead

@khyperia
Copy link
Copy Markdown
Contributor Author

khyperia commented Apr 16, 2026

  • you've changed evaluate_const to take an unevaluated const instead of a ty::Const. iirc we talked about doing this to make IACs work but since we've dropped support for them can those changes be reverted?

yeah I guess, it just makes me sad to revert, the change seems nice :c :P - I've stashed the work so can maybe done sometime in the future

  • if it would be kinda chill for you to do can you make the change of adding fields to AliasTermKind's variants be in a separate commit so it's easier to review just the "meaningful" stuff? If it's non-trivial/difficult it's fine to keep it as is 🤔

done!

edit: ack, the split wasn't fully clean, there's a swap from is_type to pattern matching in the first commit that should be in the second commit, in normalize.rs, sowwy

edit: ok, pushed, moving the change to the other commit

cc @WaffleLapkin when AliasTermKind has been updated to store DefIds like you've done for AliasTyKind this can be removed and we can just call tcx.is_type_const(def) everywhere instead

I think the DefId is already available in all the relevant places - I don't think adding a is_type_const field is strictly necessary, I just thought it was cleaner/nicer, especially with the exhaustive pattern matching nice things. Totally reasonable for me to use tcx.is_type_const though, let me know what style you'd prefer!

@rust-bors

This comment has been minimized.

Copy link
Copy Markdown
Member

@BoxyUwU BoxyUwU left a comment

Choose a reason for hiding this comment

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

I'm not super fussy about the is_type_const field I guess. It probably doesn't really change much about how much effort it'll be for one of you or waffle to fix things up between this and #155392.

I think this PR basically just needs some more tests at this point. I'm thinking off the top of my head.

  • generic uses of free consts, e.g. FREE<N> so it can't just be immediately evaluated
  • equality rules of non-type consts, so for both free and associated consts we should have tests that:
    • ALIAS1 and ALIAS2 are equal if they evalaute to the same value (and aren't if they don't).
    • ALIAS<N> is only equal to itself and not other aliases with the same body

then it should be good to go

View changes since this review

@BoxyUwU
Copy link
Copy Markdown
Member

BoxyUwU commented Apr 20, 2026

oh also can you link to the tracking issue for gca in the PR description

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 22, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

// Perhaps we could split EvaluateConstErr::HasGenericsOrInfers into HasGenerics
// and HasInfers or something, and make evaluate_const_and_instantiate change
// its behavior based on that, rather than it checking `has_non_region_infer`.
let target_args = ecx.resolve_vars_if_possible(target_args);
Copy link
Copy Markdown
Contributor Author

@khyperia khyperia Apr 22, 2026

Choose a reason for hiding this comment

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

@BoxyUwU this is annoying and gross (see HACK comment), resolve_vars_if_possible is called twice on target_args. I could maybe clean this up in a follow-up PR? Or I could gut/refactor in this PR. I would slightly prefer doing it in a follow-up, but, let me know!

View changes since the review

Copy link
Copy Markdown
Member

@BoxyUwU BoxyUwU left a comment

Choose a reason for hiding this comment

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

thx this looks good ✨

View changes since this review

let cx = self.cx();
let uv = goal.predicate.alias.expect_ct(self.cx());
// keep legacy behavior for array repeat expressions (return Ambiguous instead of a
// structural relation and Yes)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// structural relation and Yes)
// structural relation and Yes if the constant is too generic to be evaluated)

self.delegate.evaluate_const(param_env, uv)
}

pub(super) fn evaluate_const_and_instantiate(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
pub(super) fn evaluate_const_and_instantiate(
pub(super) fn evaluate_const_and_instantiate_term(

maybe 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

my thought rn is that it's not too clear what the instantiate is about. evaluate_const_and_instantiate_normalizes_to_term is maybe better actually. we tend to have a bit of a bias for long names that just say explicitly what they do in the new solver

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

mm, yeah! I don't like evaluate_const_and_instantiate_term, but I do like evaluate_const_and_instantiate_normalizes_to_term

ty::AliasTermKind::ProjectionConst { .. } => {
let uv = ty::UnevaluatedConst::new(assoc_term.item.def_id, args);
let ct = ty::Const::new_unevaluated(tcx, uv);
match super::try_evaluate_const(selcx.infcx, ct, param_env) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can you add a comment about why we're not just using evaluate_const.

if uv.has_non_region_infer() {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
} else {
self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
Copy link
Copy Markdown
Contributor Author

@khyperia khyperia Apr 24, 2026

Choose a reason for hiding this comment

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

I just realized I should add a comment here pointing out it's intentionally instantiating to goal.predicate.alias rather than the passed in uv (very important distinction, for the same reasons as the old solver reason we're using try_evaluate_const instead of evaluate_const)

View changes since the review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants