Skip to content

Conversation

@BoxyUwU
Copy link
Member

@BoxyUwU BoxyUwU commented Nov 19, 2025

r? oli-obk

tracking issue: #132980

based on #149136

High level goal

Under feature(min_generic_const_args) this PR adds another kind of const argument. A struct/variant construction const arg kind. We represent the values of the fields as themselves being const arguments which allows for uses of generic parameters subject to the existing restrictions present in min_generic_const_args:

fn foo<const N: Option<u32>>() {}

trait Trait {
    #[type_const]
    const ASSOC: usize;
}

fn bar<T: Trait, const N: u32>() {
    // the initializer of `_0` is a `N` which is a legal const argument
    // so this is ok.
    foo::<{ Some::<u32> { 0: N } }>();

    // this is allowed as mgca supports uses of assoc consts in the
    // type system. ie `<T as Trait>::ASSOC` is a legal const argument
    foo::<{ Some::<u32> { 0: <T as Trait>::ASSOC } }>();

    // this on the other hand is not allowed as `N + 1` is not a legal
    // const argument
    foo::<{ Some::<u32> { 0: N + 1 } }>();
}

This PR does not support uses of const ctors, e.g. None. And also does not support tuple constructors, e.g. Some(N). I believe that it would not be difficult to add support for such functionality after this PR lands so have left it out deliberately.

We currently require that all generic parameters on the type being constructed be explicitly specified. I haven't really looked into why that is but it doesn't seem desirable to me as it should be legal to write Some { ... } in a const argument inside of a body and have that desugar to Some::<_> { ... }. Regardless this can definitely be a follow-up PR and I assume this is some underlying consistency with the way that elided args are handled with type paths elsewhere.

This PRs implementation of supporting struct expressions is quite buggy/ICEy/incomplete. Just looking at the ty lowering logic there's immediately a bunch of panic spots where we should emit proper errors. We also don't handle Foo { ..expr } correctly (ie at all). The printing of ConstArgKind::Struct HIR nodes doesn't really exist either :')

I've tried to keep the implementation here somewhat deliberately incomplete as I think a number of these issues are actually quite small and self contained after this PR lands and I'm hoping it could be a good set of issues to mentor newer contributors on 🤔 I just wanted the "bare minimum" required to actually demonstrate that the previous changes are "necessary".

ValTree now recurse through ty::Const

In order to actually represent struct/variant construction in ty::Const without going through an anon const we would need to introduce some new ConstKind variant. Let's say some hypothetical ConstKind::ADT(Ty<'tcx>, List<Const<'tcx>>).

This variant would represent things the same way that ValTree does with the first element representing the VariantIdx of the enum (if its an enum), and then followed by a list of field values in definition order.

This could work but there are a few reasons why it's suboptimal.

First it would mean we have a second kind of Const that can be normalized. Right now we only have ConstKind::Unevaluated which possibly needs normalization. Similarly with TyKind we only have TyKind::Alias. If we introduced ConstKind::ADT it would need to be normalized to a ConstKind::Value eventually. This feels to me like it has the potential to cause bugs in the long run where only ConstKind::Unevaluated is handled by some code paths.

Secondly it would make type equality/inference be kind of... weird... It's desirable for Some { 0: ?x } eq Some { 0: 1_u32 } to result in ?x=1_u32. I can't see a way for this to work with this ConstKind::ADT design under the current architecture for how we represent types/consts and generally do equality operations.

We would need to wholly special case these two variants in type equality and have a custom recursive walker separate from the existing architecture for doing type equality. It would also be somewhat unique in that it's a non-rigid ty::Const (it can be normalized more later on in type inference) while also having somewhat "structural" equality behaviour.

Lastly, it's worth noting that its not actually ConstKind::ADT that we want. It's desirable to extend this setup to also support tuples and arrays, or even references if we wind up supporting those in const generics. Therefore this isn't really ConstKind::ADT but a more general ConstKind::ShallowValue or something to that effect. It represents at least one "layer" of a types value :')

Instead of doing this implementation choice we instead change ValTree::Branch:

enum ValTree<'tcx> {
    Leaf(ScalarInt),
    // Before this PR:
    Branch(Box<[ValTree<'tcx>]>),
    // After this PR
    Branch(Box<[Const<'tcx>]>),
}

The representation for so called "shallow values" is now the same as the representation for the entire full value. The desired inference/type equality behaviour just falls right out of this. We also don't wind up with these shallow values actually being non-rigid. And ValTree already supports references/tuples/arrays so we can handle those just fine.

I think in the future it might be worth considering inlining ValTree into ty::ConstKind. E.g:

enum ConstKind {
    Scalar(Ty<'tcx>, ScalarInt),
    ShallowValue(Ty<'tcx>, List<Const<'tcx>>),
    Unevaluated(UnevaluatedConst<'tcx>),
    ...
}

This would imply that the usage of ValTrees in patterns would now be using ty::Const but they already kind of are anyway and I think that's probably okay in the long run. It also would mean that the set of things we could represent in const patterns is greater which may be desirable in the long run for supporting things such as const patterns of const generic parameters.

Regardless, this PR doesn't actually inline ValTree into ty::ConstKind, it only changes Branch to recurse through Const. This change could be split out of this PR if desired.

I'm not sure if there'll be a perf impact from this change. It's somewhat plausible as now all const pattern values that have nesting will be interning a lot more Tys. We shall see :>

Forbidding generic parameters under mgca

Under mgca we now allow all const arguments to resolve paths to generic parameters. We then later actually validate that the const arg should be allowed to access generic parameters if it did wind up resolving to any.

This winds up just being a lot simpler to implement than trying to make name resolution "keep track" of whether we're inside of a non-anon-const const arg and then encounter a const { ... } indicating we should now stop allowing resolving to generic parameters.

It's also somewhat in line with what we'll need for a feature(generic_const_args) where we'll want to decide whether an anon const should have any generic parameters based off syntactically whether any generic parameters were used. Though that design is entirely hypothetical at this point :)

@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Nov 19, 2025
@rustbot
Copy link
Collaborator

rustbot commented Nov 19, 2025

oli-obk is not on the review rotation at the moment.
They may take a while to respond.

@rust-log-analyzer

This comment has been minimized.

@rustbot rustbot added the T-clippy Relevant to the Clippy team. label Nov 19, 2025
@rust-log-analyzer

This comment has been minimized.

@rustbot rustbot added the T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output. label Nov 20, 2025
@BoxyUwU
Copy link
Member Author

BoxyUwU commented Nov 20, 2025

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

rust-bors bot added a commit that referenced this pull request Nov 20, 2025
MGCA: Support struct expressions without intermediary anon consts
@rust-bors

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Nov 20, 2025
let (variant_idx, branches) = if def.is_enum() {
let (head, rest) = branches.split_first().unwrap();
(VariantIdx::from_u32(head.unwrap_leaf().to_u32()), rest)
(VariantIdx::from_u32(head.to_value().valtree.unwrap_leaf().to_u32()), rest)
Copy link
Member Author

Choose a reason for hiding this comment

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

the addition of all the .to_value().valtree everywhere is kind of unfortunate

// We need a branch for each "level" of the data structure.
let bytes = ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str());
ty::ValTree::from_branches(tcx, [bytes])
ty::ValTree::from_branches(tcx, [ty::Const::new_value(tcx, bytes, *inner_ty)])
Copy link
Member Author

Choose a reason for hiding this comment

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

same with all the extra calls to ty::Const::new_x


let valtree =
ty::ValTree::from_branches(tcx, opt_discr_const.into_iter().chain(fields));
ty::Const::new_value(tcx, valtree, ty)
Copy link
Member Author

Choose a reason for hiding this comment

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

need to add some tests into this commit, pretty much any of the example code ive written for showing the PR to people 😆

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-bors
Copy link

rust-bors bot commented Nov 20, 2025

☀️ Try build successful (CI)
Build commit: eeb4682 (eeb4682d365158169a665813e65ad6eabb62448b, parent: d2f887349fe3ea079a4f89b020ce6df1993e1e06)

@rust-timer

This comment has been minimized.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (eeb4682): comparison URL.

Overall result: ❌✅ regressions and improvements - please read the text below

Benchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf.

Next Steps: If you can justify the regressions found in this try perf run, please do so in sufficient writing along with @rustbot label: +perf-regression-triaged. If not, please fix the regressions and do another perf run. If its results are neutral or positive, the label will be automatically removed.

@bors rollup=never
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
0.3% [0.1%, 0.7%] 18
Regressions ❌
(secondary)
0.4% [0.1%, 0.7%] 8
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.3% [-0.5%, -0.1%] 9
All ❌✅ (primary) 0.3% [0.1%, 0.7%] 18

Max RSS (memory usage)

Results (primary 1.2%, secondary 1.0%)

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

mean range count
Regressions ❌
(primary)
1.2% [1.0%, 1.4%] 2
Regressions ❌
(secondary)
1.0% [1.0%, 1.0%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 1.2% [1.0%, 1.4%] 2

Cycles

Results (secondary -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
Regressions ❌
(secondary)
3.0% [3.0%, 3.0%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-3.2% [-3.2%, -3.2%] 1
All ❌✅ (primary) - - 0

Binary size

Results (secondary 0.0%)

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)
0.0% [0.0%, 0.0%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Bootstrap: 474.035s -> 472.627s (-0.30%)
Artifact size: 388.78 MiB -> 388.92 MiB (0.04%)

@rustbot rustbot added perf-regression Performance regression. and removed S-waiting-on-perf Status: Waiting on a perf run to be completed. labels Nov 20, 2025
@BoxyUwU
Copy link
Member Author

BoxyUwU commented Nov 20, 2025

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rust-bors

This comment has been minimized.

rust-bors bot added a commit that referenced this pull request Nov 20, 2025
MGCA: Support struct expressions without intermediary anon consts
@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Nov 20, 2025
@rust-log-analyzer

This comment has been minimized.

@BoxyUwU
Copy link
Member Author

BoxyUwU commented Nov 20, 2025

@bors try cancel

@rust-bors
Copy link

rust-bors bot commented Nov 20, 2025

Try build cancelled. Cancelled workflows:

@BoxyUwU
Copy link
Member Author

BoxyUwU commented Nov 20, 2025

@bors try

@rust-bors

This comment has been minimized.

rust-bors bot added a commit that referenced this pull request Nov 20, 2025
MGCA: Support struct expressions without intermediary anon consts
@rust-log-analyzer

This comment has been minimized.

@rust-bors
Copy link

rust-bors bot commented Nov 20, 2025

☀️ Try build successful (CI)
Build commit: 5d5bf97 (5d5bf97bb90dcbb0a21af42d4606cf425202cc06, parent: d2f887349fe3ea079a4f89b020ce6df1993e1e06)

@rust-timer

This comment has been minimized.

@rust-log-analyzer
Copy link
Collaborator

The job x86_64-gnu-tools failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
tests/ui/size_of_in_element_count/functions.rs ... ok
tests/ui/crashes/third-party/conf_allowlisted.rs ... ok

FAILED TEST: tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs
command: CLIPPY_CONF_DIR="tests" RUSTC_ICE="0" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/release/clippy-driver" "--error-format=json" "--emit=metadata" "-Aunused" "-Ainternal_features" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-Dwarnings" "-Ldependency=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/release/deps" "--sysroot=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2" "--crate-type=lib" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/ui_test/0/tests/ui" "tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs" "--extern" "futures=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libfutures-4c1a4b0248711c45.rlib" "--extern" "futures=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libfutures-4c1a4b0248711c45.rmeta" "--extern" "itertools=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libitertools-24a47e8fd97ebd3e.rlib" "--extern" "itertools=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libitertools-24a47e8fd97ebd3e.rmeta" "--extern" "libc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/liblibc-a58ad119292836ad.rlib" "--extern" "libc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/liblibc-a58ad119292836ad.rmeta" "--extern" "parking_lot=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libparking_lot-d619026ba519bcc0.rlib" "--extern" "parking_lot=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libparking_lot-d619026ba519bcc0.rmeta" "--extern" "quote=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libquote-aa085e8b6475c909.rlib" "--extern" "quote=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libquote-aa085e8b6475c909.rmeta" "--extern" "regex=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libregex-242103a92527adbd.rlib" "--extern" "regex=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libregex-242103a92527adbd.rmeta" "--extern" "serde=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libserde-78f0ab379e8b82ce.rlib" "--extern" "serde=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libserde-78f0ab379e8b82ce.rmeta" "--extern" "syn=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libsyn-87445bd6858a522a.rlib" "--extern" "syn=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libsyn-87445bd6858a522a.rmeta" "--extern" "tokio=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libtokio-f9436399da823a44.rlib" "--extern" "tokio=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libtokio-f9436399da823a44.rmeta" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/debug/deps" "--edition" "2024"

error: actual output differed from expected
Execute `./x test src/tools/clippy --bless` to update `tests/ui/trait_duplication_in_bounds_assoc_const_eq.stderr` to the actual output
--- tests/ui/trait_duplication_in_bounds_assoc_const_eq.stderr
+++ <stderr output>
-error: these where clauses contain repeated elements
+error: complex const arguments must be placed inside of a `const` block
-  --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:11:8
+  --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:11:32
    |
 LL |     T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `AssocConstTrait<ASSOC = 0>`
+   |                                ^
+
+error: complex const arguments must be placed inside of a `const` block
+  --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:11:61
    |
-note: the lint level is defined here
-  --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:1:9
-   |
-LL | #![deny(clippy::trait_duplication_in_bounds)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
+   |                                                             ^
 
-error: aborting due to 1 previous error
+error: aborting due to 2 previous errors
 

Full unnormalized output:
error: complex const arguments must be placed inside of a `const` block
##[error]  --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:11:32
   |
LL |     T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
   |                                ^

error: complex const arguments must be placed inside of a `const` block
##[error]  --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:11:61
   |
LL |     T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
   |                                                             ^

error: aborting due to 2 previous errors


---

error: there were 2 unmatched diagnostics
##[error]  --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:11:32
   |
11 |     T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
   |                                ^                            ^ Error: complex const arguments must be placed inside of a `const` block
   |                                |
   |                                Error: complex const arguments must be placed inside of a `const` block
   |

full stderr:
error: complex const arguments must be placed inside of a `const` block
##[error]  --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:11:32
   |
LL |     T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
   |                                ^

error: complex const arguments must be placed inside of a `const` block
##[error]  --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:11:61
   |
LL |     T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
   |                                                             ^

error: aborting due to 2 previous errors


@rust-timer
Copy link
Collaborator

Finished benchmarking commit (5d5bf97): comparison URL.

Overall result: ❌✅ regressions and improvements - please read the text below

Benchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf.

Next Steps: If you can justify the regressions found in this try perf run, please do so in sufficient writing along with @rustbot label: +perf-regression-triaged. If not, please fix the regressions and do another perf run. If its results are neutral or positive, the label will be automatically removed.

@bors rollup=never
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
0.3% [0.1%, 0.5%] 12
Regressions ❌
(secondary)
0.7% [0.7%, 0.7%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.3% [-0.6%, -0.1%] 11
All ❌✅ (primary) 0.3% [0.1%, 0.5%] 12

Max RSS (memory usage)

Results (primary -1.0%, secondary -0.8%)

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

mean range count
Regressions ❌
(primary)
1.0% [0.8%, 1.2%] 2
Regressions ❌
(secondary)
0.9% [0.9%, 0.9%] 1
Improvements ✅
(primary)
-5.1% [-5.1%, -5.1%] 1
Improvements ✅
(secondary)
-1.6% [-2.3%, -0.9%] 2
All ❌✅ (primary) -1.0% [-5.1%, 1.2%] 3

Cycles

Results (secondary 2.8%)

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)
2.8% [2.4%, 3.3%] 2
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Binary size

Results (secondary 0.0%)

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)
0.0% [0.0%, 0.0%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Bootstrap: 474.035s -> 474.013s (-0.00%)
Artifact size: 388.78 MiB -> 388.92 MiB (0.04%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Nov 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. perf-regression Performance regression. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants