Skip to content

Reject union default field values #142570

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

Merged
merged 2 commits into from
Jun 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_ast_lowering/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_ast_lowering/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
42 changes: 32 additions & 10 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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)),
)
},
);
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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),
Expand All @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/feature-gates/feature-gate-default-field-values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
54 changes: 37 additions & 17 deletions tests/ui/feature-gates/feature-gate-default-field-values.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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 { .. };
| ^
Expand All @@ -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, .. };
| ^
Expand All @@ -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 { .. };
| ^
Expand All @@ -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, .. };
| ^
Expand All @@ -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> { .. };
| ^
Expand All @@ -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));
| ^
Expand All @@ -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, .. };
| ^
Expand All @@ -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));
| ^
Expand All @@ -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));
| ^
Expand All @@ -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));
| ^
Expand All @@ -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));
| ^
Expand All @@ -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, .. };
| ^
Expand All @@ -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));
| ^
Expand All @@ -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));
| ^
Expand All @@ -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));
| ^
Expand All @@ -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));
| ^
Expand All @@ -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`.
6 changes: 6 additions & 0 deletions tests/ui/structs/default-field-values/failures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading