Rollup merge of #142570 - jieyouxu:disunion, r=estebank

Reject union default field values

Fixes rust-lang/rust#142555.

The [`default_field_values` RFC][rfc] does not specify that default field values may be used on `union`s, and it's not clear how default field values may be used with `union`s without an design extension to the RFC. So, for now, reject trying to use default field values with `union`s.

### Review notes

- The first commit adds the `union` with default field values test case to `tests/ui/structs/default-field-values/failures.rs`, where `union`s with default field values are currently accepted.
- The second commit rejects trying to supply default field values to `union` definitions.
- When `default_field_values` feature gate is disabled, we show the feature gate error when the user tries to write `union`s with default field values. When the feature gate is enabled, we reject this usage with
   > unions cannot have default field values

``@rustbot`` label: +F-default_field_values

[rfc]: https://rust-lang.github.io/rfcs/3681-default-field-values.html
This commit is contained in:
Jubilee 2025-06-17 00:28:17 -07:00 committed by GitHub
commit d4f23cdc91
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 115 additions and 36 deletions

View file

@ -179,6 +179,8 @@ ast_lowering_underscore_expr_lhs_assign =
in expressions, `_` can only be used on the left-hand side of an assignment
.label = `_` not allowed here
ast_lowering_union_default_field_values = unions cannot have default field values
ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on this architecture
ast_lowering_unstable_inline_assembly_label_operand_with_outputs =
using both label and output operands for inline assembly is unstable

View file

@ -475,3 +475,10 @@ pub(crate) struct UseConstGenericArg {
#[suggestion_part(code = "{other_args}")]
pub call_args: Span,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_union_default_field_values)]
pub(crate) struct UnionWithDefault {
#[primary_span]
pub span: Span,
}

View file

@ -17,6 +17,7 @@ use tracing::instrument;
use super::errors::{
InvalidAbi, InvalidAbiSuggestion, MisplacedRelaxTraitBound, TupleStructWithDefault,
UnionWithDefault,
};
use super::stability::{enabled_names, gate_unstable_abi};
use super::{
@ -316,7 +317,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
this.arena.alloc_from_iter(
enum_definition.variants.iter().map(|x| this.lower_variant(x)),
enum_definition.variants.iter().map(|x| this.lower_variant(i, x)),
)
},
);
@ -328,7 +329,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
generics,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_variant_data(hir_id, struct_def),
|this| this.lower_variant_data(hir_id, i, struct_def),
);
hir::ItemKind::Struct(ident, generics, struct_def)
}
@ -338,7 +339,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
generics,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_variant_data(hir_id, vdata),
|this| this.lower_variant_data(hir_id, i, vdata),
);
hir::ItemKind::Union(ident, generics, vdata)
}
@ -714,13 +715,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> {
fn lower_variant(&mut self, item_kind: &ItemKind, v: &Variant) -> hir::Variant<'hir> {
let hir_id = self.lower_node_id(v.id);
self.lower_attrs(hir_id, &v.attrs, v.span);
hir::Variant {
hir_id,
def_id: self.local_def_id(v.id),
data: self.lower_variant_data(hir_id, &v.data),
data: self.lower_variant_data(hir_id, item_kind, &v.data),
disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const_to_anon_const(e)),
ident: self.lower_ident(v.ident),
span: self.lower_span(v.span),
@ -730,15 +731,36 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_variant_data(
&mut self,
parent_id: hir::HirId,
item_kind: &ItemKind,
vdata: &VariantData,
) -> hir::VariantData<'hir> {
match vdata {
VariantData::Struct { fields, recovered } => hir::VariantData::Struct {
fields: self
VariantData::Struct { fields, recovered } => {
let fields = self
.arena
.alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))),
recovered: *recovered,
},
.alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f)));
if let ItemKind::Union(..) = item_kind {
for field in &fields[..] {
if let Some(default) = field.default {
// Unions cannot derive `Default`, and it's not clear how to use default
// field values of unions if that was supported. Therefore, blanket reject
// trying to use field values with unions.
if self.tcx.features().default_field_values() {
self.dcx().emit_err(UnionWithDefault { span: default.span });
} else {
let _ = self.dcx().span_delayed_bug(
default.span,
"expected union default field values feature gate error but none \
was produced",
);
}
}
}
}
hir::VariantData::Struct { fields, recovered: *recovered }
}
VariantData::Tuple(fields, id) => {
let ctor_id = self.lower_node_id(*id);
self.alias_attrs(ctor_id, parent_id);

View file

@ -58,6 +58,16 @@ pub enum OptEnum {
}
}
// Default field values may not be used on `union`s (at least, this is not described in the accepted
// RFC, and it's not currently clear how to extend the design to do so). We emit a feature gate
// error when the feature is not enabled, but syntactically reject default field values when used
// with unions when the feature is enabled. This can be adjusted if there's an acceptable design
// extension, or just unconditionally reject always.
union U {
x: i32 = 0, //~ ERROR default values on fields are experimental
y: f32 = 0.0, //~ ERROR default values on fields are experimental
}
fn main () {
let x = Foo { .. }; //~ ERROR base expression required after `..`
let y = Foo::default();

View file

@ -124,8 +124,28 @@ LL | optional: () = (),
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on fields are experimental
--> $DIR/feature-gate-default-field-values.rs:67:11
|
LL | x: i32 = 0,
| ^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on fields are experimental
--> $DIR/feature-gate-default-field-values.rs:68:11
|
LL | y: f32 = 0.0,
| ^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:62:21
--> $DIR/feature-gate-default-field-values.rs:72:21
|
LL | let x = Foo { .. };
| ^
@ -140,7 +160,7 @@ LL | let x = Foo { ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:64:29
--> $DIR/feature-gate-default-field-values.rs:74:29
|
LL | let z = Foo { baz: 1, .. };
| ^
@ -155,7 +175,7 @@ LL | let z = Foo { baz: 1, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:70:26
--> $DIR/feature-gate-default-field-values.rs:80:26
|
LL | let x = Bar::Foo { .. };
| ^
@ -170,7 +190,7 @@ LL | let x = Bar::Foo { ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:72:34
--> $DIR/feature-gate-default-field-values.rs:82:34
|
LL | let z = Bar::Foo { baz: 1, .. };
| ^
@ -185,7 +205,7 @@ LL | let z = Bar::Foo { baz: 1, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:78:31
--> $DIR/feature-gate-default-field-values.rs:88:31
|
LL | let x = Qux::<i32, 4> { .. };
| ^
@ -200,7 +220,7 @@ LL | let x = Qux::<i32, 4> { ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:79:73
--> $DIR/feature-gate-default-field-values.rs:89:73
|
LL | assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, bay: 4, .. }, x));
| ^
@ -215,7 +235,7 @@ LL | assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, bay: 4, ../*
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:82:38
--> $DIR/feature-gate-default-field-values.rs:92:38
|
LL | let y = Opt { mandatory: None, .. };
| ^
@ -230,7 +250,7 @@ LL | let y = Opt { mandatory: None, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:86:47
--> $DIR/feature-gate-default-field-values.rs:96:47
|
LL | assert!(matches!(Opt { mandatory: None, .. }, z));
| ^
@ -245,7 +265,7 @@ LL | assert!(matches!(Opt { mandatory: None, ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:88:30
--> $DIR/feature-gate-default-field-values.rs:98:30
|
LL | assert!(matches!(Opt { .. }, z));
| ^
@ -256,7 +276,7 @@ LL | assert!(matches!(Opt { ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:90:44
--> $DIR/feature-gate-default-field-values.rs:100:44
|
LL | assert!(matches!(Opt { optional: (), .. }, z));
| ^
@ -267,7 +287,7 @@ LL | assert!(matches!(Opt { optional: (), ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:92:61
--> $DIR/feature-gate-default-field-values.rs:102:61
|
LL | assert!(matches!(Opt { optional: (), mandatory: None, .. }, z));
| ^
@ -279,7 +299,7 @@ LL + assert!(matches!(Opt { optional: (), mandatory: None, }, z));
|
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:94:51
--> $DIR/feature-gate-default-field-values.rs:104:51
|
LL | let y = OptEnum::Variant { mandatory: None, .. };
| ^
@ -294,7 +314,7 @@ LL | let y = OptEnum::Variant { mandatory: None, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:98:60
--> $DIR/feature-gate-default-field-values.rs:108:60
|
LL | assert!(matches!(OptEnum::Variant { mandatory: None, .. }, z));
| ^
@ -309,7 +329,7 @@ LL | assert!(matches!(OptEnum::Variant { mandatory: None, ../* expr */ }, z)
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:100:43
--> $DIR/feature-gate-default-field-values.rs:110:43
|
LL | assert!(matches!(OptEnum::Variant { .. }, z));
| ^
@ -320,7 +340,7 @@ LL | assert!(matches!(OptEnum::Variant { ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:102:57
--> $DIR/feature-gate-default-field-values.rs:112:57
|
LL | assert!(matches!(OptEnum::Variant { optional: (), .. }, z));
| ^
@ -331,7 +351,7 @@ LL | assert!(matches!(OptEnum::Variant { optional: (), ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:104:74
--> $DIR/feature-gate-default-field-values.rs:114:74
|
LL | assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }, z));
| ^
@ -342,7 +362,7 @@ LL - assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }
LL + assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, }, z));
|
error: aborting due to 29 previous errors
error: aborting due to 31 previous errors
Some errors have detailed explanations: E0658, E0797.
For more information about an error, try `rustc --explain E0658`.

View file

@ -49,6 +49,12 @@ enum E {
Variant {} //~ ERROR the `#[default]` attribute may only be used on unit enum variants
}
union U
{
x: i32 = 1, //~ ERROR unions cannot have default field values
y: f32 = 2., //~ ERROR unions cannot have default field values
}
fn main () {
let _ = Foo { .. }; // ok
let _ = Foo::default(); // ok

View file

@ -12,6 +12,18 @@ error: default fields are not supported in tuple structs
LL | pub struct Rak(i32 = 42);
| ^^ default fields are only supported on structs
error: unions cannot have default field values
--> $DIR/failures.rs:54:14
|
LL | x: i32 = 1,
| ^
error: unions cannot have default field values
--> $DIR/failures.rs:55:14
|
LL | y: f32 = 2.,
| ^^
error[E0277]: the trait bound `S: Default` is not satisfied
--> $DIR/failures.rs:16:5
|
@ -28,19 +40,19 @@ LL | pub struct S;
|
error: missing field `bar` in initializer
--> $DIR/failures.rs:55:19
--> $DIR/failures.rs:61:19
|
LL | let _ = Bar { .. };
| ^ fields that do not have a defaulted value must be provided explicitly
error: missing field `bar` in initializer
--> $DIR/failures.rs:56:27
--> $DIR/failures.rs:62:27
|
LL | let _ = Bar { baz: 0, .. };
| ^ fields that do not have a defaulted value must be provided explicitly
error[E0308]: mismatched types
--> $DIR/failures.rs:60:17
--> $DIR/failures.rs:66:17
|
LL | let _ = Rak(..);
| --- ^^ expected `i32`, found `RangeFull`
@ -53,19 +65,19 @@ note: tuple struct defined here
LL | pub struct Rak(i32 = 42);
| ^^^
help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal
--> $DIR/failures.rs:60:17
--> $DIR/failures.rs:66:17
|
LL | let _ = Rak(..);
| ^^
error[E0061]: this struct takes 1 argument but 2 arguments were supplied
--> $DIR/failures.rs:62:13
--> $DIR/failures.rs:68:13
|
LL | let _ = Rak(0, ..);
| ^^^ -- unexpected argument #2 of type `RangeFull`
|
help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal
--> $DIR/failures.rs:62:20
--> $DIR/failures.rs:68:20
|
LL | let _ = Rak(0, ..);
| ^^
@ -81,13 +93,13 @@ LL + let _ = Rak(0);
|
error[E0061]: this struct takes 1 argument but 2 arguments were supplied
--> $DIR/failures.rs:64:13
--> $DIR/failures.rs:70:13
|
LL | let _ = Rak(.., 0);
| ^^^ -- unexpected argument #1 of type `RangeFull`
|
help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal
--> $DIR/failures.rs:64:17
--> $DIR/failures.rs:70:17
|
LL | let _ = Rak(.., 0);
| ^^
@ -102,7 +114,7 @@ LL - let _ = Rak(.., 0);
LL + let _ = Rak(0);
|
error: aborting due to 8 previous errors
error: aborting due to 10 previous errors
Some errors have detailed explanations: E0061, E0277, E0308.
For more information about an error, try `rustc --explain E0061`.