Skip to content

loop_match: suggest extracting to a const item #143585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

folkertdev
Copy link
Contributor

@folkertdev folkertdev commented Jul 7, 2025

tracking issue: #132306
fixes #143310

@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 Jul 7, 2025
@RalfJung
Copy link
Member

RalfJung commented Jul 7, 2025

Sorry, I won't be able to review this.
r? @oli-obk

@rustbot rustbot assigned oli-obk and unassigned RalfJung Jul 7, 2025
@folkertdev folkertdev force-pushed the loop-match-suggest-const-block branch from 38091b2 to aafc04c Compare July 7, 2025 13:38
Comment on lines 7 to 10
help: try wrapping the expression in a `const` block
|
LL | break 'blk const { const_fn() };
| +++++++ +
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As additional context, #143310 shows that this suggestion doesn't work (but it should). We get

https://godbolt.org/z/zvs9ePzdj

error[E0391]: cycle detected when building MIR for `break_on_constant_evalution`
  --> <source>:20:32
   |
20 |                     break 'blk const{ const_fn() };
   |                                ^^^^^^^^^^^^^^^^^^^
   |
note: ...which requires evaluating type-level constant...
  --> <source>:20:37
   |
20 |                     break 'blk const{ const_fn() };
   |                                     ^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `break_on_constant_evalution::{constant#0}`...
  --> <source>:20:37
   |
20 |                     break 'blk const{ const_fn() };
   |                                     ^^^^^^^^^^^^^^
note: ...which requires caching mir of `break_on_constant_evalution::{constant#0}` for CTFE...
  --> <source>:20:37
   |
20 |                     break 'blk const{ const_fn() };
   |                                     ^^^^^^^^^^^^^^
note: ...which requires elaborating drops for `break_on_constant_evalution::{constant#0}`...

And I'm not sure why. We must be evaluating the constant in some sort of unexpected way, but I really don't know enough about const eval to troubleshoot it.

Copy link
Contributor

Choose a reason for hiding this comment

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

const blocks are nested within their parent and borrowchecked together with them, so in order to drop elaborate the constant we need to borrowck the parent, and that depends on MIR building of the parent again (which is when you're evaluating the const). To avoid this you need to put the const into a separate item, not a const block. This means you lose access to generic params, but that's probably a good thing similar to how const blocks in patterns being affected by generic params is fishy

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, but what confuses me is why the example does work when I comment out the loop-match attributes, like here https://godbolt.org/z/66q98PeW9.

Copy link
Member

@RalfJung RalfJung Jul 7, 2025

Choose a reason for hiding this comment

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

Why is that surprising? If you don't use the attributes, then your code doesn't run, so it can't ICE.

It is fundamentally not possible to evaluate constants during MIR building. The constant could be generic, and MIR building happens pre-monomorphization, so the value of generics is not known yet. For the same reason, you cannot use constants that use generic in patterns.

Copy link
Member

Choose a reason for hiding this comment

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

So this has to enforce the same restrictions as patterns: only named constants without any generics in their path. It shouldn't ICE, of course. But unfortunately const blocks do not work.

After all, the entire point if this construct is to hard-code where this jumps to; if that could depend on generics, then it couldn't be hard-coded into the MIR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, you mean that loop-match evaluates the const { ... } too early? It must evaluate it at compile-time at some point right, so whether the code runs doesn't seem relevant. Also this const { ... } block doesn´t use generics in practice so I think that added to my confusion (but it's probably hard to figure that out at that point).

Anyway, that means we should suggest using a const item, not a const expression.

Copy link
Member

@RalfJung RalfJung Jul 7, 2025

Choose a reason for hiding this comment

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

Hmm, you mean that loop-match evaluates the const { ... } too early?

Yes. In general, it can only be evaluated during monomorphization.

It must evaluate it at compile-time at some point right,

Sure, but there's many different stages of compile-time and it makes a big difference when (i.e., in which stage) code runs. In particular, there's the key distinction of "are generics still present or have they been monomorphized away already".

So, this is very relevant. It seems you are fundamentally confused about how generic functions work in rustc; please let's take any further questions you may have here to Zulip.

this const { ... } block doesn´t use generics

Yeah, for this const block the ICE comes from the borrow checker as Oli explained.
But we also don't actually detect whether a const block needs some generic or not, we just give all the surrounding generics to const blocks. Surely it would be very confusing if const { ... } worked in loop_match but only in non-generic functions.

I missed this problem with generics when I wrote my comment in #143310, sorry for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just hadn't considered generics given that the concrete example didn't have them. But it makes sense that they must be taken into account now. Thanks for the background!

@folkertdev folkertdev changed the title loop_match: suggest wrapping in a const block loop_match: suggest extracting to a const item Jul 7, 2025
@folkertdev folkertdev force-pushed the loop-match-suggest-const-block branch from aafc04c to 37706c5 Compare July 7, 2025 15:18
@folkertdev
Copy link
Contributor Author

Allright, the error now suggests to extract to a const item, and gives some specific extra information when using const parameters or const blocks, because it seems like those should work, but for good technical reasons they cannot.

if the expression cannot be evaluated in a straightforward way
@folkertdev folkertdev force-pushed the loop-match-suggest-const-block branch from 37706c5 to 069f5cb Compare July 7, 2025 15:21
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.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ICEs on #[loop_match] with constant evaluation
4 participants