Skip to content

hir_ty_lowering: fix anon const type recovery - #158874

Merged
rust-bors[bot] merged 10 commits into
rust-lang:mainfrom
Dnreikronos:hir_ty_lowering/anon_const_type_of_race
Aug 25, 2026
Merged

hir_ty_lowering: fix anon const type recovery#158874
rust-bors[bot] merged 10 commits into
rust-lang:mainfrom
Dnreikronos:hir_ty_lowering/anon_const_type_of_race

Conversation

@Dnreikronos

@Dnreikronos Dnreikronos commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

View all comments

fixes #158818

check_crate used to check whether coroutine closures need a by-move body in the same parallel pass that type-checks body owners.

That ordering is the bug. With an async closure inside a const argument, the by-move check asks for type_of on the nested coroutine. This can type-check the anon const before the enclosing body lowers the const argument and feeds its expected type. The query caches {type error}, then normal lowering later feeds the const parameter's actual type for the same key, so rustc sees two different values and ICEs.

I moved the by-move work into a second pass. The first pass gets through normal body type checking and feeds the anon const types. The second pass only starts after that is done, so it cannot ask for the nested coroutine type too early.

I think this is a much better place to fix it than the HIR-path recovery I tried earlier. The race starts in check_crate, and fixing the ordering there covers path expressions, type positions, and the generic argument cases without keeping a second, incomplete version of generic arg lowering in type_of.

The UI tests cover the original repro and the nearby shapes that came up while working on it. They run with -Zthreads=0 and now produce the normal type mismatch instead of an ICE.

@rustbot

rustbot commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

HIR ty lowering was modified

cc @fmease

@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 6, 2026
@rustbot

rustbot commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

r? @davidtwco

rustbot has assigned @davidtwco.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler, types
  • compiler, types expanded to 75 candidates
  • Random selection from 20 candidates

@rust-log-analyzer

This comment has been minimized.

@Dnreikronos
Dnreikronos force-pushed the hir_ty_lowering/anon_const_type_of_race branch from 0ce9ac1 to 2b086ad Compare July 6, 2026 18:18
Comment thread compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs Outdated
Comment thread compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs Outdated
@rust-log-analyzer

This comment has been minimized.

@Dnreikronos
Dnreikronos force-pushed the hir_ty_lowering/anon_const_type_of_race branch from 2b086ad to 7eb6962 Compare July 6, 2026 23:02
@Dnreikronos

Copy link
Copy Markdown
Contributor Author

@fmease i pushed a much narrower version.

i dropped the type-relative recovery path completely, so there is no separate iat/inherent assoc selection anymore. i also removed the generic-arg lowering helper from generics.rs. the remaining type_of code only accepts the exact simple shape from the repro: one resolved segment, one explicit const arg, one own const param, no parent generics, no self, no inferred args, no constraints, no late-bound regions.

imo this is aligned with the review feedback while still fixing the bug. there is still a tiny guard in type_of to make sure the anon const maps to that one const param, but idk how to remove even that without either reusing an existing lowering hook or changing the shape of the fix more deeply. ltm if you want zero matching in type_of. in that case i think the next step is finding a proper existing hook, or maybe backing out of this query-side recovery approach.

@rust-log-analyzer

This comment has been minimized.

@Dnreikronos
Dnreikronos force-pushed the hir_ty_lowering/anon_const_type_of_race branch from 7eb6962 to 73d0e43 Compare July 6, 2026 23:48
@Dnreikronos
Dnreikronos requested a review from fmease July 7, 2026 19:03

@davidtwco davidtwco 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.

I'm going to re-roll this because I'm not too confident in it, the implementation strikes me as overfitting to the specific test case rather than the general problem.

View changes since this review

Comment thread compiler/rustc_hir_analysis/src/collect/type_of.rs Outdated
Comment thread compiler/rustc_hir_analysis/src/collect/type_of.rs Outdated
@davidtwco

Copy link
Copy Markdown
Member

r? compiler

@rustbot rustbot assigned TaKO8Ki and unassigned davidtwco Jul 8, 2026
@Dnreikronos

Copy link
Copy Markdown
Contributor Author

I'm going to re-roll this because I'm not too confident in it, the implementation strikes me as overfitting to the specific test case rather than the general problem.

View changes since this review

yeah, fair.
the reason i went this way is that the bug looks like a query consistency issue to me: type_of for the anon const can run before normal generic arg lowering gets to feed the expected const type. then later the normal path feeds the real type, and we can end up with the inconsistency that caused the ice.

my first attempt tried to recover more cases, but after fmease's comments i think that was the wrong direction. it was starting to duplicate too much of generic arg lowering and type-relative/iat selection in type_of, and i agree that code shouldn't live there.

but yeah, requiring exactly one generic arg and exactly one own param is probably overcorrecting. i'll keep this limited to direct resolved function paths, but make it handle simple multi-arg cases where this const arg's hir id can be matched to the corresponding own const param without doing real lowering. i'll still bail out for stuff that needs real lowering, like parent generics, self, late-bound regions, constraints, inferred args, defaults, type-relative paths, and const param types that depend on other generics.

ltm if that still feels like the wrong place for this. i'm not completely sure, but imo that seems like the least bad middle ground between the first version and this overfit one.

@Dnreikronos
Dnreikronos requested a review from davidtwco July 8, 2026 12:38
Comment thread compiler/rustc_hir_analysis/src/collect/type_of.rs Outdated
@rust-log-analyzer

This comment has been minimized.

Comment thread compiler/rustc_hir_analysis/src/collect/type_of.rs Outdated
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 30, 2026
@rustbot

rustbot commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@TaKO8Ki

TaKO8Ki commented Jul 30, 2026

Copy link
Copy Markdown
Member

If there isn’t a strong reason to restrict this to a single generic argument, could you support multiple arguments as well? The current restriction still leaves similar ICEs for valid code such as:

fn f<T, const N: u8>() {
    f::<u8, { async || {} }>();
}

@Dnreikronos

Dnreikronos commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

If there isn't a strong reason to restrict this to a single generic argument, could you support multiple arguments as well?

Yeah, no strong reason to keep it restricted to one arg, so I lifted that. Pushed in 1d2d34dfb06, tests in 2d620d4acc1.

Your exact repro is a regression test now and gives a clean expected u8, found {async closure} instead of the ICE:

fn f<T, const N: u8>() {
    f::<u8, { async || {} }>();
}

The recovery matches args positionally against the generic params and only bails when there are more args than can line up, so multiple args just work.

btw while I was in there I ran into one more flavor of the same bug: trailing type params with defaults also drop out of the turbofish, so fn f<const N: u8, T = ()>() called as f::<{ ... }>() still fed {type error} and then u8 for the same key and double-fed. folded that in too (swap the exact-count != for >) with its own test. fyi both only repro under -Zthreads=0.

imo positional matching is the right call here. once you drop the exact-count requirement, the omittable-param cases (synthetic impl Trait, elided lifetimes, defaults) all fall out for free. idk if you'd prefer defaults handled more explicitly, ltm if so.

verified locally: your repro plus the three existing tests are green, and the whole const-generics anon-const suite passes (840). no rush on my end, nothing asap.

(the async-closure-in-a-const trick is a weirdly clean way to trip this race irl, kinda enjoyed chasing it.)

@fmease

fmease commented Aug 3, 2026

Copy link
Copy Markdown
Member

cc @BoxyUwU

@Dnreikronos

Copy link
Copy Markdown
Contributor Author

@rustbot ready

Recovering the type from the HIR path meant re-deriving what the generic
argument lowering already works out, and only covered the cases the
partial classifier recognized: free function paths resolved to
`DefKind::Fn`, with no late-bound lifetimes.

Anything else still cached an error type and tripped the double-feed
ICE, including associated functions, tuple struct constructors, inherent
type-relative paths, and const arguments in type annotations, which are
not path expressions at all and so cannot be classified this way.

With the by-move bodies generated after typeck the anon const's type is
always fed before anything asks for it, so drop the recovery along with
the tests that only exercised its argument counting. The shapes they
covered are folded into the main regression test.
@Dnreikronos
Dnreikronos force-pushed the hir_ty_lowering/anon_const_type_of_race branch from febafdd to 023a327 Compare August 21, 2026 17:47
@rustbot

rustbot commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

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.

@BoxyUwU

BoxyUwU commented Aug 21, 2026

Copy link
Copy Markdown
Member

This makes sense to me 🤔 This is basically just reverting #141950 though I think?

@bors try @rust-timer queue

though I'm not sure what alternative there is if this is perf sensitive. we can't just move this logic into type checking of closure expressions 🤔

@rust-timer

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 21, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 21, 2026
…f_race, r=<try>

hir_ty_lowering: fix anon const type recovery
@BoxyUwU BoxyUwU removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 21, 2026
@rust-bors

rust-bors Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 8d05ea8 (8d05ea813c6b5434ca0a42c4ed59359853e7c45f)
Base parent: a872286 (a872286d0a1873caec0291ed4304de3170adbe16)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (8d05ea8): comparison URL.

Overall result: no relevant changes - no action needed

Benchmarking means the PR may be perf-sensitive. Consider adding rollup=never if this change is not fit for rolling up.

@rustbot label: -S-waiting-on-perf -perf-regression

Instruction count

This perf run didn't have relevant results for this metric.

Max RSS (memory usage)

Results (primary 5.0%, secondary -5.8%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
5.0% [5.0%, 5.0%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-5.8% [-5.8%, -5.8%] 1
All ❌✅ (primary) 5.0% [5.0%, 5.0%] 1

Cycles

Results (secondary 1.4%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
3.7% [2.4%, 4.9%] 2
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-3.0% [-3.0%, -3.0%] 1
All ❌✅ (primary) - - 0

Binary size

Results (primary 0.1%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
0.1% [0.1%, 0.1%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.1% [0.1%, 0.1%] 1

Bootstrap: 468.499s -> 468.404s (-0.02%)
Artifact size: 400.07 MiB -> 400.88 MiB (0.20%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 21, 2026
@Dnreikronos

Copy link
Copy Markdown
Contributor Author

This makes sense to me 🤔 This is basically just reverting #141950 though I think?

@bors try @rust-timer queue

though I'm not sure what alternative there is if this is perf sensitive. we can't just move this logic into type checking of closure expressions 🤔

sup, Boxy :)
yeah, for the scheduling part, pretty much. #141950 moved this work into the main body-owner loop. This puts it back in its own pass, though it still lives in check_crate instead of going back to run_required_analyses.

what bites us here is that the loop is parallel. Doing typeck and then the by-move check for one owner doesn't tell us whether its enclosing owner has finished. For the coroutine inside the const arg, needs_coroutine_by_move_body_def_id can ask for its type_of before the enclosing body has lowered that arg and fed the anon const type. That first lookup ends up caching the error type. Later, normal typeck reaches the const arg and feeds u8 for the same query, and that's the ICE.

I tried fixing it around HIR lowering first, but that turned into chasing each place a const arg can show up, and it duplicated some generic arg matching along the way. Didn't feel like the right layer. Putting the by-move work after body typeck gives us the ordering we need and handles those cases in one place.

I don't see a clean way to tie this to closure expression type checking either...the thing we need is for the enclosing body to have already lowered the const arg, and a closure-local hook doesn't give us that ordering. imo the second pass is a little annoying after #141950, but it's also the least surprising fix to me. perf came back with no relevant changes, so I'd keep it like this so far.

But if you really want me to change something ltm

@rust-timer

Copy link
Copy Markdown
Collaborator

Insufficient permissions to issue commands to rust-timer.

@Dnreikronos

Copy link
Copy Markdown
Contributor Author

@rustbot ready

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 24, 2026
@BoxyUwU

BoxyUwU commented Aug 25, 2026

Copy link
Copy Markdown
Member

@bors r+

@rust-bors

rust-bors Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

📌 Commit 023a327 has been approved by BoxyUwU

It is now in the queue for this repository.

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 25, 2026
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Aug 25, 2026
…onst_type_of_race, r=BoxyUwU

hir_ty_lowering: fix anon const type recovery

fixes rust-lang#158818

`check_crate` used to check whether coroutine closures need a by-move body in the same parallel pass that type-checks body owners.

That ordering is the bug. With an async closure inside a const argument, the by-move check asks for `type_of` on the nested coroutine. This can type-check the anon const before the enclosing body lowers the const argument and feeds its expected type. The query caches `{type error}`, then normal lowering later feeds the const parameter's actual type for the same key, so rustc sees two different values and ICEs.

I moved the by-move work into a second pass. The first pass gets through normal body type checking and feeds the anon const types. The second pass only starts after that is done, so it cannot ask for the nested coroutine type too early.

I think this is a much better place to fix it than the HIR-path recovery I tried earlier. The race starts in `check_crate`, and fixing the ordering there covers path expressions, type positions, and the generic argument cases without keeping a second, incomplete version of generic arg lowering in `type_of`.

The UI tests cover the original repro and the nearby shapes that came up while working on it. They run with `-Zthreads=0` and now produce the normal type mismatch instead of an ICE.
rust-bors Bot pushed a commit that referenced this pull request Aug 25, 2026
…uwer

Rollup of 13 pull requests

Successful merges:

 - #158874 (hir_ty_lowering: fix anon const type recovery)
 - #161443 (add internal DSL for testing binders)
 - #161617 (Add custom allocators to `(try_)map` on `Box`, `Rc`, `Arc`)
 - #161726 (Fix debugger visualizer tuple child ordering w/ PDB debug info)
 - #161729 (miri subtree update)
 - #161745 (make trivial ABI check resilient against new repr)
 - #160871 (Remove `#[rustc_reservation_impl]`)
 - #161180 (Detect missing binding available: add a MaybeIncorrect suggestion)
 - #161522 (test `f16::mul_add` not double-rounding the result)
 - #161631 (Add two comments relating to new-solver performance)
 - #161724 (Add codegen test for static table search loop unrolling)
 - #161740 (do not compress debuginfo for Cygwin)
 - #161750 (vector ABI check: reword so it makes more sense for non-obviously-vector types)
@rust-bors
rust-bors Bot merged commit 5fae19c into rust-lang:main Aug 25, 2026
14 checks passed
rust-bors Bot pushed a commit that referenced this pull request Aug 25, 2026
Rollup merge of #158874 - Dnreikronos:hir_ty_lowering/anon_const_type_of_race, r=BoxyUwU

hir_ty_lowering: fix anon const type recovery

fixes #158818

`check_crate` used to check whether coroutine closures need a by-move body in the same parallel pass that type-checks body owners.

That ordering is the bug. With an async closure inside a const argument, the by-move check asks for `type_of` on the nested coroutine. This can type-check the anon const before the enclosing body lowers the const argument and feeds its expected type. The query caches `{type error}`, then normal lowering later feeds the const parameter's actual type for the same key, so rustc sees two different values and ICEs.

I moved the by-move work into a second pass. The first pass gets through normal body type checking and feeds the anon const types. The second pass only starts after that is done, so it cannot ask for the nested coroutine type too early.

I think this is a much better place to fix it than the HIR-path recovery I tried earlier. The race starts in `check_crate`, and fixing the ordering there covers path expressions, type positions, and the generic argument cases without keeping a second, incomplete version of generic arg lowering in `type_of`.

The UI tests cover the original repro and the nearby shapes that came up while working on it. They run with `-Zthreads=0` and now produce the normal type mismatch instead of an ICE.
@rustbot rustbot added this to the 1.100.0 milestone Aug 25, 2026
pull Bot pushed a commit to LeeeeeeM/miri that referenced this pull request Aug 26, 2026
…uwer

Rollup of 13 pull requests

Successful merges:

 - rust-lang/rust#158874 (hir_ty_lowering: fix anon const type recovery)
 - rust-lang/rust#161443 (add internal DSL for testing binders)
 - rust-lang/rust#161617 (Add custom allocators to `(try_)map` on `Box`, `Rc`, `Arc`)
 - rust-lang/rust#161726 (Fix debugger visualizer tuple child ordering w/ PDB debug info)
 - rust-lang/rust#161729 (miri subtree update)
 - rust-lang/rust#161745 (make trivial ABI check resilient against new repr)
 - rust-lang/rust#160871 (Remove `#[rustc_reservation_impl]`)
 - rust-lang/rust#161180 (Detect missing binding available: add a MaybeIncorrect suggestion)
 - rust-lang/rust#161522 (test `f16::mul_add` not double-rounding the result)
 - rust-lang/rust#161631 (Add two comments relating to new-solver performance)
 - rust-lang/rust#161724 (Add codegen test for static table search loop unrolling)
 - rust-lang/rust#161740 (do not compress debuginfo for Cygwin)
 - rust-lang/rust#161750 (vector ABI check: reword so it makes more sense for non-obviously-vector types)
flip1995 pushed a commit to flip1995/rust-clippy that referenced this pull request Aug 28, 2026
…uwer

Rollup of 13 pull requests

Successful merges:

 - rust-lang/rust#158874 (hir_ty_lowering: fix anon const type recovery)
 - rust-lang/rust#161443 (add internal DSL for testing binders)
 - rust-lang/rust#161617 (Add custom allocators to `(try_)map` on `Box`, `Rc`, `Arc`)
 - rust-lang/rust#161726 (Fix debugger visualizer tuple child ordering w/ PDB debug info)
 - rust-lang/rust#161729 (miri subtree update)
 - rust-lang/rust#161745 (make trivial ABI check resilient against new repr)
 - rust-lang/rust#160871 (Remove `#[rustc_reservation_impl]`)
 - rust-lang/rust#161180 (Detect missing binding available: add a MaybeIncorrect suggestion)
 - rust-lang/rust#161522 (test `f16::mul_add` not double-rounding the result)
 - rust-lang/rust#161631 (Add two comments relating to new-solver performance)
 - rust-lang/rust#161724 (Add codegen test for static table search loop unrolling)
 - rust-lang/rust#161740 (do not compress debuginfo for Cygwin)
 - rust-lang/rust#161750 (vector ABI check: reword so it makes more sense for non-obviously-vector types)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. 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.

[ICE]: Computed query value for type_of is inconsistent with fed value / Trying to feed an already recorded value for query type_of

8 participants