Skip to content

Stabilize feature(generic_arg_infer) #141610

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

BoxyUwU
Copy link
Member

@BoxyUwU BoxyUwU commented May 26, 2025

Fixes #85077

r? lcnr

cc @rust-lang/project-const-generics

@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. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels May 26, 2025
@rustbot
Copy link
Collaborator

rustbot commented May 26, 2025

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

HIR ty lowering was modified

cc @fmease

@BoxyUwU BoxyUwU added A-const-generics Area: const generics (parameters and arguments) F-generic_arg_infer Using `_` as a const argument: #![feature(generic_arg_infer)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels May 26, 2025
@rust-log-analyzer

This comment has been minimized.

@BoxyUwU BoxyUwU force-pushed the stabilize_generic_arg_infer branch from 077c8ba to 081d24a Compare May 26, 2025 18:46
@rustbot
Copy link
Collaborator

rustbot commented May 26, 2025

This PR changes a file inside tests/crashes. If a crash was fixed, please move into the corresponding ui subdir and add 'Fixes #' to the PR description to autoclose the issue upon merge.

@rust-log-analyzer

This comment has been minimized.

@BoxyUwU
Copy link
Member Author

BoxyUwU commented May 26, 2025

Hi @rust-lang/lang I'm proposing we stabilize the generic_arg_infer feature. If someone could start an FCP that would be appreciated :3. @rust-lang/types I don't believe this needs a types FCP as we already FCP'd the behaviour of repeat expression inference and I don't believe there to be any other type system interactions here (but if anyone on the team feels strongly they'd like to FCP this then feel free to start one).

Explicitly inferred const arguments: Stabilization report

General design

We now allow users to write _ as an argument to const generic parameters. This is only allowed outside of type signatures which is consistent with how we handle inferred type arguments.

fn foo() -> [u8; 2] {
  // Previously `_` here was unstable, now its stable
  [1; _]
}

fn make_array<const N: usize>() -> [u8; N] {
  loop {}
}

fn bar() {
  // Previously `_` here was unstable, now its stable
  let _: [u8; 5] = make_array::<_>();
}

Inferring const arguments implicitly has always been possible since the original stabilization of const generics. What is being proposed for stabilization is the ability to explicitly infer a generic argument, instead of implicitly inferring all generic arguments by eliding the entire set.

Inference of explicitly inferred const arguments is handled the same way as elided generic arguments and generally the same as how inference of type arguments is handled. Defaulted const generic parameters are not taken into account for fallback of inferred const arguments, this is the same as how defaulted type parameters work.

Array repeat expressions ([10_u8; 2]) with inferred repeat counts are more subtle. As it is not possible to elide the count of a repeat expression, these were not possible to write and so are a new capability for type inference.

The types team believes that the current implementation of type inference for repeat expresions with inferred counts is maintainable and sound. Information about the exact details of how type inference handles repeat expressions with inferred counts can be found here.

We allow parenthesised inferred const arguments as well as bare underscores. For example: let arr: [u8; (((_)))] = [10; 2];. It's not clear to me if this is desirable, it's consistent with type arguments where we support [(((_))); 2], but inconsistent with const generics not yet supporting Foo<((((N))))>.

Const Generics was originally RFC'd in RFC #2000 Const Generics. I believe that this feature is covered by this RFC even though the RFC does not explicitly talk about inferred const arguments.

It seems natural that in adding a new kind of generic parameter we would also be supporting a way to explicitly infer generic arguments of that kind. Additionally, the only reason this wasnt part of the min_const_generics stabilization was for implementation reason, not design/RFC reasons.

In summary:

  • Support _ and (((((_))))) as arguments to const generic parameters
  • Inferred const arguments are not supported in type signatures
  • Inference behaviour of const arguments is the same as type arguments
    • e.g. defaulted const generics do not aide inference
  • Types team is confident in the inference behaviour of repeat expressions

Implementation Quality

High level implementation

The HIR is now able to represent _ generic arguments without knowing whether it is an argument to a type or a const parameter. This is accomplished via an Infer variant on the hir::GenericArg enum.

A naive implementation would cause maintainability issues as inferred arguments could be represented as either GenericArg::Ty(TyKind::Infer or GenericArg::Infer which could result in the compiler failing to handle inferred arguments in some positions.

To solve this we split HIR's Ty and ConstArg types into two versions, one for when the type/const is in a position where its unambigously a type/const, and another for when its in a position where it is not clear which kind of argument it is. More information can be found in the PR: Forbid usage of hir Infer const/ty variants in ambiguous contexts.

Finally, there are also a fair amount of subtleties around how we could handle repeat expressions with inferred counts. The types team is confident that the current implemention is correct, maintainable and also more flexible enough. More information can be found here.

Test coverage/Correctness assurance

Tests for this feature can generally be found in tests/ui/const-generics/generic_arg_infer or tests/ui/repeat-expr:

On stable we internally use the new GenericArg::Infer representation for inferred arguments and have done so for a couple releases now. This gives us more confidence that the implementation is working correctly as there have been no bugs filed when we made this change. We also did a crater run before switching over to the new representation which helped be confident that the new representation did not introduce any regressions (e.g. ICEs).

Finally, there was a call for testing for this feature 3 months ago and nobody has reported any bugs following the post: Inferred const generic arguments: Call for Testing!

Tooling support

I don't know how r-a handles explicitly inferred const arguments, but at the very least it doesn't seem to give a bunch of errors on passing code. cc @rust-lang/rust-analyzer.

Rustfmt doesn't delete explicitly inferred const args so what more can you ask for.

Reference PR: rust-lang/reference#1835

Type system interactions

This feature is almost entirely syntactic- the type system has been (soundly) working with inferred const arguments since the initial stabilization of Const Generics. Apart from array repeat expressions, this feature does not strengthen the type system in any way or expose new parts of it.

Array repeat expressions ([10; _]) are handled specially by type checking, requiring the element type implements Copy if the length is >=2. I believe the only soundness relevent part of this feature is ensuring we still enforce this in the presence of inferred const arguments.

test: Copy requirement when inferred count is later inferred

N/A Headers

  • Does this feature introduce new expressions and can they produce temporaries? What are the lifetimes of those temporaries?
    • No temporaries
  • What other unstable features may be exposed by this feature?
    • None
  • What outstanding bugs in the issue tracker involve this feature? Are they stabilization-blocking?
    • No open issues
  • What FIXMEs are still in the code for that feature and why is it ok to leave them there?
    • No FIXME's
  • Can users use this feature to break the abstraction of Rust and expose the underlying assembly-level implementation?
    • No, its simple type inference which the compiler already supports
  • Are there extensions to this feature that remain unstable? How do we know that we are not accidentally committing to those?
    • There are no extensions to this feature
  • What behavior are we committing to that has been controversial? Summarize the major arguments pro/con.
    • This has been a relatively uncontroversial feature

Implementation History

feature(generic_arg_infer) was originally implemented by @JulianKnodt and @lcnr back in 2021 (#83484, #91907). This was the core of the implementation but it was brittle as it was easy to not handle all places that inferred generic arguments could appear in the HIR.

@voidc and @camelid have refactored some parts of how we represent const arguments in the HIR (#133589, #125915). This made it possible for me to refactor the HIR to make things less brittle/error prone (#135272), the design for how to accomplish this was a joint effort of @lcnr and myself.

In #139635 I updated our handling of repeat expression checks in HIR typechecking to properly handle inferred repeat counts and inference side effects of the Copy requirements.

Finally thanks to @lcnr @compiler-errors and @oli-obk who have reviewed a lot of the work (be it PRs or otherwise) in this area over the past years.

Sorry if I missed anyone's contributions here, 4 years is a long time and we don't seem to have written any of it down along the way ^^'

@BoxyUwU BoxyUwU added I-lang-nominated Nominated for discussion during a lang team meeting. I-lang-easy-decision Issue: The decision needed by the team is conjectured to be easy; this does not imply nomination labels May 26, 2025
@BoxyUwU
Copy link
Member Author

BoxyUwU commented May 26, 2025

(marking as an easy decision because it ought to be an easy decision to start an FCP not necessarily to actually commit to things)

@BoxyUwU BoxyUwU force-pushed the stabilize_generic_arg_infer branch from 081d24a to f9132af Compare May 26, 2025 19:30
@traviscross
Copy link
Contributor

traviscross commented May 26, 2025

@rustbot labels -I-lang-easy-decision +P-lang-drag-1

Thanks for putting up this stabilization and for the obvious amount of work that went in ahead of it. Will have a careful look.

(Regarding the label, I like your optimism, but probably this isn't going to be what we mean by an easy decision. Even to propose FCP, one of us has to be ready to commit to it unless we concurrently file a concern.)

@rustbot rustbot added P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang and removed I-lang-easy-decision Issue: The decision needed by the team is conjectured to be easy; this does not imply nomination labels May 26, 2025
@traviscross
Copy link
Contributor

cc @ehuss

(Heads-up this one is actually likely to move along quickly.)

@traviscross
Copy link
Contributor

traviscross commented May 26, 2025

(Just for our notes as I work through this; not a concern...)

This stabilization puts weight on the following bit of inference breakage that will ship with Rust 1.89:

struct W<T>(Option<T>);

impl Clone for W<u8> {
    fn clone(&self) -> Self {
        W(None)
    }
}
impl Copy for W<u8> {}

fn extract<T: Copy>(x: [W<T>; 2]) -> T {
    x[0].0.unwrap()
}

fn main() {
    let x = [W(None); 2];
    //[1.89]~^ ERROR type annotations needed
    //[1.88]~^ OK
    _ = extract(x).max(2);
}

@traviscross
Copy link
Contributor

We're also putting some weight on this inference choice:

#![feature(generic_arg_infer)]

struct W<T>(Option<T>);

impl Clone for W<u32> {
    fn clone(&self) -> Self {
        W(None)
    }
}
impl Copy for W<u32> {}

trait Trait {}
impl Trait for i32 {}
impl Trait for u32 {}

fn make_goal<T: Trait>(_: &T) {}
fn tie<T>(_: &T, _: &[W<T>; 2]) {}

fn main() {
    let a = 1;
    make_goal(&a);
    let b: [W<_>; 2] = [W(None); _];
    //        ~
    //~^ Do we fall back to `i32` here (and give an error since
    // `W<i32>` is not `Copy`), or do we infer `u32` given `W<T>:
    // Copy`?  We pick `u32`.
    tie(&a, &b);
}

@traviscross
Copy link
Contributor

traviscross commented May 26, 2025

@rustbot labels +S-waiting-on-documentation

Having reviewed #139635, the tests, the stabilization report, and the Reference PR (though I have not yet reviewed it an editorial sense) this all seems in order to me. So I propose we accept this stabilization.

@rfcbot fcp merge

@rfcbot
Copy link
Collaborator

rfcbot commented May 26, 2025

Team member @traviscross has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
See this document for info about what commands tagged team members can give me.

@rustbot rustbot added the S-waiting-on-documentation Status: Waiting on approved PRs to documentation before merging label May 26, 2025
@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels May 26, 2025
@tmandry
Copy link
Member

tmandry commented May 28, 2025

This looks straightforward to me. Thanks @BoxyUwU for the thorough report.

The parenthesized consts thing seems like: We can't be fully consistent with the language because it's not consistent with itself, but my understanding from @BoxyUwU is that there's no good reason we can't make it consistent eventually. Specifically, we don't support [expr; (N)] where N is a const parameter, but we should be able to support it later. So let's go ahead and support [expr; (_)] for now.

@rfcbot reviewed

@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels May 28, 2025
@rfcbot
Copy link
Collaborator

rfcbot commented May 28, 2025

🔔 This is now entering its final comment period, as per the review above. 🔔

@traviscross traviscross added I-lang-radar Items that are on lang's radar and will need eventual work or consideration. and removed I-lang-nominated Nominated for discussion during a lang team meeting. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang labels May 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-generics Area: const generics (parameters and arguments) disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. F-generic_arg_infer Using `_` as a const argument: #![feature(generic_arg_infer)]` final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. S-waiting-on-documentation Status: Waiting on approved PRs to documentation before merging S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Tracking Issue for _ as a const argument: feature(generic_arg_infer)
7 participants