Skip to content
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

Introduce default_field_values feature #129514

Merged
merged 7 commits into from
Dec 10, 2024

Conversation

estebank
Copy link
Contributor

Initial implementation of #[feature(default_field_values], proposed in rust-lang/rfcs#3681.

We now parse const expressions after a = in a field definition, to specify a struct field default value.

We now allow Struct { field, .. } where there's no base after ...

#[derive(Default)] now uses the default value if present, continuing to use Default::default() if not.

#[derive(Debug)]
pub struct S;

#[derive(Debug, Default)]
pub struct Foo {
    pub bar: S = S,
    pub baz: i32 = 42 + 3,
}

fn main () {
    let x = Foo { .. };
    let y = Foo::default();
    let z = Foo { baz: 1, .. };

    assert_eq!(45, x.baz);
    assert_eq!(45, y.baz);
    assert_eq!(1, z.baz);
}

@rustbot
Copy link
Collaborator

rustbot commented Aug 24, 2024

r? @cjgillot

rustbot has assigned @cjgillot.
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

@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-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. labels Aug 24, 2024
@rust-log-analyzer

This comment has been minimized.

@estebank
Copy link
Contributor Author

It just occurred to me to try how the following fails, and it Just Works™️ 😄:

pub struct Foo {
    pub baz: i32 = Self::X,
}

impl Foo {
    const X: i32 = 1;
}

@rust-log-analyzer

This comment has been minimized.

Copy link
Contributor

@cjgillot cjgillot left a comment

Choose a reason for hiding this comment

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

I made a first pass. I don't have anything substantial to say, that's a great pr.

Just an avalanche of nits. An additional one: could you prefer matching on Rest now there are >2 cases?

compiler/rustc_ast/src/ast.rs Outdated Show resolved Hide resolved
compiler/rustc_builtin_macros/src/deriving/default.rs Outdated Show resolved Hide resolved
compiler/rustc_hir/src/intravisit.rs Outdated Show resolved Hide resolved
compiler/rustc_hir/src/hir.rs Outdated Show resolved Hide resolved
compiler/rustc_hir/src/intravisit.rs Outdated Show resolved Hide resolved
compiler/rustc_middle/src/ty/mod.rs Outdated Show resolved Hide resolved
compiler/rustc_mir_build/src/build/expr/into.rs Outdated Show resolved Hide resolved
compiler/rustc_mir_build/src/build/expr/into.rs Outdated Show resolved Hide resolved
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@cjgillot cjgillot 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 Aug 24, 2024
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

Comment on lines 123 to 124
// We use `Default::default()`.
None => default_call(cx, field.span),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Given this, I filed rust-lang/rfcs#3683 as this bit is independent of the default values RFC.

@rust-log-analyzer

This comment has been minimized.

@bors

This comment was marked as resolved.

@estebank estebank force-pushed the default-field-values branch 2 times, most recently from 0f49c94 to 3a64e38 Compare September 24, 2024 00:21
@rust-log-analyzer

This comment has been minimized.

@estebank estebank force-pushed the default-field-values branch 2 times, most recently from d59696d to 27549fb Compare September 24, 2024 00:53
@bors

This comment was marked as outdated.

@estebank estebank force-pushed the default-field-values branch from 27549fb to e9c76a3 Compare September 27, 2024 01:14
@bors

This comment was marked as outdated.

@@ -1104,6 +1104,23 @@ fn check_type_defn<'tcx>(
for variant in variants.iter() {
// All field types must be well-formed.
for field in &variant.fields {
if let Some(def_id) = field.value
Copy link
Member

Choose a reason for hiding this comment

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

For the record, erroring eagerly even if we don't instantiate the default is consistent with how defaults in struct const generics works:

struct Foo<const N: u8 = {130 + 130}> {}

which errors with:

error[E0080]: evaluation of constant value failed
 --> src/lib.rs:1:27
  |
1 | struct Foo<const N: u8 = {130 + 130}> {}
  |                           ^^^^^^^^^ attempt to compute `130_u8 + 130_u8`, which would overflow

For more information about this error, try `rustc --explain E0080`.
error: could not compile `playground` (lib) due to 1 previous error

Not totally certain why the RFC specified that it would only be linted against, but this seems to me to be the correct behavior.

@compiler-errors
Copy link
Member

compiler-errors commented Dec 9, 2024

Please address nits and then please clean up the history a bit (that zip_eq commit could be squashed into whatever commit, and bbdd6ba too -- have you ever heard of git absorb? that might be useful to automate this type of cleanup).

Then r=me

Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```

Add lint for default fields that will always fail const-eval

We *allow* this to happen for API writers that might want to rely on users'
getting a compile error when using the default field, different to the error
that they would get when the field isn't default. We could change this to
*always* error instead of being a lint, if we wanted.

This will *not* catch errors for partially evaluated consts, like when the
expression relies on a const parameter.

Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`:

 - Suggest adding a base expression if there are missing fields.
 - Suggest enabling the feature if all the missing fields have optional values.
 - Suggest removing `..` if there are no missing fields.
Emit a specific error for unsupported default field value syntax in tuple structs.
People might extrapolate from `Struct { .. }` that `Struct(..)` would work, but it doesn't.
…errors

Emit E0080 always on struct definition with default fields that have unconditional const errors and remove `default_field_always_invalid_const` lint.
@estebank estebank force-pushed the default-field-values branch from e37271d to fa331f4 Compare December 9, 2024 21:58
@compiler-errors
Copy link
Member

@bors r+ rollup=never

@bors
Copy link
Contributor

bors commented Dec 9, 2024

📌 Commit fa331f4 has been approved by compiler-errors

It is now in the queue for this repository.

@bors bors 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 Dec 9, 2024
@fmease
Copy link
Member

fmease commented Dec 9, 2024

@bors p=10 bitrotty

@bors
Copy link
Contributor

bors commented Dec 10, 2024

⌛ Testing commit fa331f4 with merge 974ccc1...

@bors
Copy link
Contributor

bors commented Dec 10, 2024

☀️ Test successful - checks-actions
Approved by: compiler-errors
Pushing 974ccc1 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Dec 10, 2024
@bors bors merged commit 974ccc1 into rust-lang:master Dec 10, 2024
7 checks passed
@rustbot rustbot added this to the 1.85.0 milestone Dec 10, 2024
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (974ccc1): comparison URL.

Overall result: ❌✅ regressions and improvements - no action needed

@rustbot label: -perf-regression

Instruction count

This is the most reliable metric that we have; it was used to determine the overall result at the top of this comment. However, even this metric can sometimes exhibit noise.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
0.7% [0.3%, 2.3%] 7
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.7% [-0.7%, -0.7%] 1
All ❌✅ (primary) - - 0

Max RSS (memory usage)

Results (primary -0.0%, secondary 2.4%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
1.3% [1.1%, 1.5%] 2
Regressions ❌
(secondary)
2.4% [2.4%, 2.4%] 1
Improvements ✅
(primary)
-1.4% [-2.2%, -0.7%] 2
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -0.0% [-2.2%, 1.5%] 4

Cycles

Results (secondary 2.2%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

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

Binary size

Results (primary 0.1%, secondary 0.0%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
0.1% [0.0%, 0.2%] 53
Regressions ❌
(secondary)
0.0% [0.0%, 0.1%] 13
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.1% [0.0%, 0.2%] 53

Bootstrap: 767.262s -> 767.421s (0.02%)
Artifact size: 330.86 MiB -> 331.00 MiB (0.04%)

@ytmimi
Copy link
Contributor

ytmimi commented Dec 22, 2024

@estebank @compiler-errors I'm not sure how, but this change broken rustfmt macro formatting. rust-lang/rustfmt#6424 was recently reported and I bisected the change in behavior back to this PR.

@compiler-errors
Copy link
Member

I'll look into it. Probably has to do with changes to the compiler representation of that default value.

tgross35 added a commit to tgross35/rust that referenced this pull request Dec 23, 2024
…ue-fmt, r=ytmimi

Make sure we don't lose default struct value when formatting struct

The reason why rust-lang/rustfmt#6424 broke when rust-lang#129514 landed is because the parser now *successfully* parses default struct values. That means that where we used to fail in `rewrite_macro_inner`:

https://github.com/rust-lang/rust/blob/e108481f74ff123ad98a63bd107a18d13035b275/src/tools/rustfmt/src/macros.rs#L263-L267

... we now succeed, and we now proceed to format the inner struct as a macro arg. Since formatting was never implemented for default struct values, this means that we’ll always rewrite the struct to exclude the default value.

This PR makes it so that we simply don’t rewrite struct fields if they have a default value.

r? `@ytmimi`
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Dec 23, 2024
Rollup merge of rust-lang#134668 - compiler-errors:default-struct-value-fmt, r=ytmimi

Make sure we don't lose default struct value when formatting struct

The reason why rust-lang/rustfmt#6424 broke when rust-lang#129514 landed is because the parser now *successfully* parses default struct values. That means that where we used to fail in `rewrite_macro_inner`:

https://github.com/rust-lang/rust/blob/e108481f74ff123ad98a63bd107a18d13035b275/src/tools/rustfmt/src/macros.rs#L263-L267

... we now succeed, and we now proceed to format the inner struct as a macro arg. Since formatting was never implemented for default struct values, this means that we’ll always rewrite the struct to exclude the default value.

This PR makes it so that we simply don’t rewrite struct fields if they have a default value.

r? `@ytmimi`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
F-default_field_values `#![feature(default_field_values)]` merged-by-bors This PR was explicitly merged by bors. 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. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants