Skip to content

teach Const::Unevaluated to handle direct consts - #164982

Closed
khyperia wants to merge 1 commit into
rust-lang:mainfrom
khyperia:direct-Const_Unevaluated
Closed

khyperia wants to merge 1 commit into
rust-lang:mainfrom
khyperia:direct-Const_Unevaluated

Conversation

@khyperia

Copy link
Copy Markdown
Member

Lots of theorizing and discussion has happened here. Here's some words: One such writeup of the issue was done by me in this comment, for some background context: #162336 (comment) - one solution is to lower references to direct-rhs const items in MIR to Const::Unevaluated rather than Const::Ty (as they are fundamentally sourced from regular values rather than the type system, which is what Const::Unevaluated vs Const::Ty means). One particular way to do that is to intercept const eval at a very particular point: where we have just resolved the instance, but still have access to the unresolved instance, due to adding support for evaluating resolved projected items being vaguely annoying (certainly possible to do so, just difficult and annoying to maintain from a tech debt point of view). An example of an alternative implementation would be to add support to the trait solver for resolved projections, and instead intercept const eval within e.g. eval_to_const_value_raw or eval_to_allocation_raw

This PR explicitly only addresses direct consts in ExprKind::NamedConst. It does NOT address direct consts in patterns, in simd_swizzle, or #[const_continue], all of which use const_eval_resolve_for_typeck.

r? @BoxyUwU

fyi @lcnr @oli-obk this is the change we discussed in person

@rustbot

rustbot commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

Some changes occurred to constck

cc @fee1-dead

@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. labels Oct 26, 2026
@rustbot

rustbot commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

BoxyUwU is currently at their maximum review capacity.
They may take a while to respond.

@bit-aloo bit-aloo left a comment •

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.

Changes look good! Left a few nits. 🚀

Question: Why do we want to handle rhs consts through the CTFE machinery? The typesystem machinery (valtrees) already works for this, though it's not as expressive as CTFE. Are we planning to eventually use the CTFE machinery for everything, including typesystemy stuff, since valtrees can't handle everything and can ICE on expressions that are valid for CTFE but not supported by the typesystem machinery? is the idea to gradually deprecate valtrees?

View changes since this review

};
if self.const_of_item(def_id).is_none() {
return None;
};

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.

nit:

Suggested change
};
}

@@ -374,7 +375,11 @@ where
assert!(promoted.is_none() || Q::ALLOW_PROMOTED);

// Don't peak inside trait associated constants.

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.

nit:

Suggested change
// Don't peak inside trait associated constants.
// Don't peek inside trait associated constants.

Comment on lines +111 to +113
if self.const_of_item(def_id).is_none() {
return None;
};

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
if self.const_of_item(def_id).is_none() {
return None;
};
self.const_of_item(def_id)?

@RalfJung

RalfJung commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

What is a "direct const"?
You talk about "references to (direct) consts", do you mean &CONST or are you using a different meaning of "reference"?

All that new logic in compiler/rustc_middle/src/mir/interpret/queries.rs makes me very uncomfortable. How is this supposed to be maintainable going forward if we need so much special-casing?

From the issue:

CTFE machinery knows how to evaluate regular consts. It ICEs on direct consts.

That sounds very concerning and fragile. I think we may have to have a design session that includes wg-const-eval. It seems you are building a second const-eval system but it uses the same underlying compiler types as the old system so every part of the compiler needs to treat every const like a raw egg to avoid passing it to the wrong system.

@khyperia

Copy link
Copy Markdown
Member Author

What is a "direct const"?

A direct const is a concept from the generic const args family of features. It's unfortunately quite an extensive explanation of what it is and why it is needed, I would love to properly sync at some point and get folks up to speed on what "direct consts" are. (e.g. had a lovely talk with Niko a few days ago about them). The long story short is that they are consts (both const items, and generic const arguments) that are represented "directly" via ty::Const rather than a MIR body.

You talk about "references to (direct) consts", do you mean &CONST or are you using a different meaning of "reference"?

Apologies, I mean reference as like, "refer to", let x = CONST; in a body. Perhaps I should have said uses or something.

All that new logic in compiler/rustc_middle/src/mir/interpret/queries.rs makes me very uncomfortable. How is this supposed to be maintainable going forward if we need so much special-casing?

Generic const args is indeed quite a large feature and it took us many years to figure out a workable approach for const generics. I'm not sure what to say here, other than that this PR is reducing the amount of special-casing and jank, and pushing for a more cohesively designed GCA without as much special-casing.

From the issue:

CTFE machinery knows how to evaluate regular consts. It ICEs on direct consts.

That sounds very concerning and fragile.

Indeed, I agree it is very concerning and fragile, and this PR is working towards making things less ICEy and fragile. There are many very difficult questions here (e.g. how to handle initialized padding bytes in the type system) that are being worked on, and we have some good design progress on how to handle them.

I think we may have to have a design session that includes wg-const-eval. It seems you are building a second const-eval system but it uses the same underlying compiler types as the old system so every part of the compiler needs to treat every const like a raw egg to avoid passing it to the wrong system.

Yeah, syncing wg-const-eval and project-const-generics is important to do at some point probably soon! This PR was conceptualized from design discussions with me, @BoxyUwU, @oli-obk, and @lcnr which is a handful of wg-const-eval folks but yeah, would be good to get everyone involved!

@khyperia

Copy link
Copy Markdown
Member Author

Question: Why do we want to handle rhs consts through the CTFE machinery? The typesystem machinery (valtrees) already works for this, though it's not as expressive as CTFE. Are we planning to eventually use the CTFE machinery for everything, including typesystemy stuff, since valtrees can't handle everything and can ICE on expressions that are valid for CTFE but not supported by the typesystem machinery? is the idea to gradually deprecate valtrees?

We are still using valtrees here! Definitely not deprecating valtrees in any way.

It's a very long story, but I suppose a bit of explanation can be gathered by considering one of the motivating cases of this PR: when a projection const is used in a body, we do not know whether it will resolve to a regular const (a const with a MIR body) or a direct const (a const with a "directly represented" ty::Const rhs). Before this, we had a hack that lowered uses of consts that were statically known to be direct consts to Const::Ty, to pretend that they're from the type system, and use type system normalization to resolve them, but this breaks down for projection consts (and so we just ICEd a bunch for projection consts). This isn't conceptually correct because they really ought to be Const::Unevaluated because the uses of these consts are not from the type system, they're just regular let x = CONST;. Hence this PR.

@RalfJung

RalfJung commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

other than that this PR is reducing the amount of special-casing and jank, and pushing for a more cohesively designed GCA without as much special-casing.

For the part of the compiler I keep an eye on, it is a significant increase in what looks like ad-hoc special cases and non-trivial global invariants that are not encoded via separate types. I assume it reduces complexity elsewhere, but moving complexity around doesn't make it a clear win in my book. (The total line diff also confirms that impression.)

If we really need two kinds of consts, they should be represented entirely separately in the compiler IMO. It should not be possible to get a mir::UnevaluatedConst that can't actually be evaluated as a MIR const by the MIR interpreter. If we have two "kinds" of conts, we should design their compiler representation in a way that it becomes impossible to accidentally mix them up.

(As I told Boxy before, I continue to feel like the amount of compiler and language complexity we are piling up here is entirely out of proportion to the benefit, and we should accept post-mono errors instead. I know that's not easy either due to trait solver interaction, but it avoids having to introduce a "kinding" system for consts and re-introducing an AST-based const-eval, as this proposal seems to be doing. But I don't have the capacity to actually help work out such a design so, well, this is neither here nor there. But I hope this helps you understand why I am so skeptical about this. :)

@BoxyUwU

BoxyUwU commented Oct 27, 2026

Copy link
Copy Markdown
Member

r? oli-obk

@rustbot rustbot assigned oli-obk and unassigned BoxyUwU Oct 27, 2026
@BoxyUwU

BoxyUwU commented Oct 27, 2026

Copy link
Copy Markdown
Member

I think there's a lot of context missing here which is partially due to it not being in the PR description but also it's just not feasible to put it all in the PR description. I would like it if you and @oli-obk could speak about this given we (const generics folks) already spent time talking with wg-const-eval folks and building context there.

I think it would be useful to keep this PR just to "how to make the impl maintainable" moreso than building context from scratch about how gca works which is probably more suited to a zulip convo or something.

@@ -74,8 +74,14 @@ impl<'tcx> TyCtxt<'tcx> {
// FIXME: maybe have a separate version for resolving mir::UnevaluatedConst?
match ty::Instance::try_resolve(self, typing_env, ct.def, ct.args) {

@BoxyUwU BoxyUwU Oct 27, 2026 •

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.

might it make sense to have instance resolution return a new variant/thing saying "needs you to do direct shenanigans" so that when you resolve stuff you're forced to handle it?

I guess I'm curious about two things:

  • Do we resolve instances elsewhere that can encounter assoc consts and has wrong behaviour under gca
  • Do we invoke any things like const_eval_global_id directly elsewhere incorrectly

View changes since the review

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 guess more generally it's not clear to me whether or not we want a more involved change for the API of how we enter const eval

@RalfJung

Copy link
Copy Markdown
Member

I would like it if you and @oli-obk could speak about this given we (const generics folks) already spent time talking with wg-const-eval folks and building context there.

As a wg-const-eval member I wasn't involved. It'd probably help to have you around for such a discussion.

I think it would be useful to keep this PR just to "how to make the impl maintainable" moreso than building context from scratch about how gca works which is probably more suited to a zulip convo or something.

Well I can't really discuss that with my current lack of context. I'd be rather unhappy about landing the PR in its current state, it basically turns interpret/queries.rs into a file where I understand approximately nothing and that's not great. That file has slowly been getting worse over the years but this is pushing it over the edge.

@khyperia

khyperia commented Nov 2, 2026

Copy link
Copy Markdown
Member Author

We are going with the alternative approach as posted in #165070 and as such this PR is no longer necessary, closing

@khyperia khyperia closed this Nov 2, 2026
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Nov 2, 2026
@khyperia
khyperia deleted the direct-Const_Unevaluated branch November 2, 2026 14:39
@RalfJung

RalfJung commented Nov 2, 2026

Copy link
Copy Markdown
Member

Thanks!

JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Nov 2, 2026
…oxyUwU

create mir for direct-rhs consts

Alternative to rust-lang#164982 - see that PR for context.

When referring to a direct constant via a `mir::Const::Unevaluated`, synthesize a MIR body, whose contents are simply returning a `mir::Const::Ty` of itself. This effectively provides both a MIR view, as well as a typesystem view, of the same constant, without risking representing the same constant in different ways in the two different representations, because one is just referring to the other.

r? @BoxyUwU

fyi @oli-obk @lcnr
rust-bors Bot pushed a commit that referenced this pull request Nov 2, 2026
Rollup merge of #165070 - khyperia:mir_for_direct_const, r=BoxyUwU

create mir for direct-rhs consts

Alternative to #164982 - see that PR for context.

When referring to a direct constant via a `mir::Const::Unevaluated`, synthesize a MIR body, whose contents are simply returning a `mir::Const::Ty` of itself. This effectively provides both a MIR view, as well as a typesystem view, of the same constant, without risking representing the same constant in different ways in the two different representations, because one is just referring to the other.

r? @BoxyUwU

fyi @oli-obk @lcnr
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants