From 32c0cb0f5a18ae1cb1966944d287e15ff9225b7d Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 16 Jun 2025 16:17:32 +0800 Subject: [PATCH 1/2] Add union with default field values case test In particular, there should be no additional errors (default field values for `union` fields are currently erroneously accepted). --- .../ui/structs/default-field-values/failures.rs | 6 ++++++ .../structs/default-field-values/failures.stderr | 16 ++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/tests/ui/structs/default-field-values/failures.rs b/tests/ui/structs/default-field-values/failures.rs index dee6566bd0ea1..77640d4f461b7 100644 --- a/tests/ui/structs/default-field-values/failures.rs +++ b/tests/ui/structs/default-field-values/failures.rs @@ -49,6 +49,12 @@ enum E { Variant {} //~ ERROR the `#[default]` attribute may only be used on unit enum variants } +union U +{ + x: i32 = 1, + y: f32 = 2., +} + fn main () { let _ = Foo { .. }; // ok let _ = Foo::default(); // ok diff --git a/tests/ui/structs/default-field-values/failures.stderr b/tests/ui/structs/default-field-values/failures.stderr index aaa75fd3180c5..4f9aedff93e41 100644 --- a/tests/ui/structs/default-field-values/failures.stderr +++ b/tests/ui/structs/default-field-values/failures.stderr @@ -28,19 +28,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 +53,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 +81,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); | ^^ From 2dd9cc113067113453efcff722e3a5c0893a6244 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 16 Jun 2025 17:53:44 +0800 Subject: [PATCH 2/2] Reject union default field values --- compiler/rustc_ast_lowering/messages.ftl | 2 + compiler/rustc_ast_lowering/src/errors.rs | 7 +++ compiler/rustc_ast_lowering/src/item.rs | 42 +++++++++++---- .../feature-gate-default-field-values.rs | 10 ++++ .../feature-gate-default-field-values.stderr | 54 +++++++++++++------ .../structs/default-field-values/failures.rs | 4 +- .../default-field-values/failures.stderr | 14 ++++- 7 files changed, 103 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index 5ef76fb64aaf2..50eb7c7ae99bd 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -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 diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 576fa9731e906..b444324ef9143 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -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, +} diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 49110c93954ad..ef27d0ef69b14 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -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); diff --git a/tests/ui/feature-gates/feature-gate-default-field-values.rs b/tests/ui/feature-gates/feature-gate-default-field-values.rs index d2e41a7160259..4631f51b9d840 100644 --- a/tests/ui/feature-gates/feature-gate-default-field-values.rs +++ b/tests/ui/feature-gates/feature-gate-default-field-values.rs @@ -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(); diff --git a/tests/ui/feature-gates/feature-gate-default-field-values.stderr b/tests/ui/feature-gates/feature-gate-default-field-values.stderr index 104d72a39861d..292c38990726e 100644 --- a/tests/ui/feature-gates/feature-gate-default-field-values.stderr +++ b/tests/ui/feature-gates/feature-gate-default-field-values.stderr @@ -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 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 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:: { .. }; | ^ @@ -200,7 +220,7 @@ LL | let x = Qux:: { ../* 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:: { bar: S, baz: 42, bat: 2, bay: 4, .. }, x)); | ^ @@ -215,7 +235,7 @@ LL | assert!(matches!(Qux:: { 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`. diff --git a/tests/ui/structs/default-field-values/failures.rs b/tests/ui/structs/default-field-values/failures.rs index 77640d4f461b7..9c5b7172929c6 100644 --- a/tests/ui/structs/default-field-values/failures.rs +++ b/tests/ui/structs/default-field-values/failures.rs @@ -51,8 +51,8 @@ enum E { union U { - x: i32 = 1, - y: f32 = 2., + x: i32 = 1, //~ ERROR unions cannot have default field values + y: f32 = 2., //~ ERROR unions cannot have default field values } fn main () { diff --git a/tests/ui/structs/default-field-values/failures.stderr b/tests/ui/structs/default-field-values/failures.stderr index 4f9aedff93e41..5e3d4c89c2a3c 100644 --- a/tests/ui/structs/default-field-values/failures.stderr +++ b/tests/ui/structs/default-field-values/failures.stderr @@ -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 | @@ -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`.