Skip to content

Commit 974ccc1

Browse files
committed
Auto merge of rust-lang#129514 - estebank:default-field-values, r=compiler-errors
Introduce `default_field_values` feature 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. ```rust #[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); } ```
2 parents a224f38 + fa331f4 commit 974ccc1

File tree

77 files changed

+1569
-394
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+1569
-394
lines changed

compiler/rustc_ast/src/ast.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3119,6 +3119,7 @@ pub struct FieldDef {
31193119
pub ident: Option<Ident>,
31203120

31213121
pub ty: P<Ty>,
3122+
pub default: Option<AnonConst>,
31223123
pub is_placeholder: bool,
31233124
}
31243125

compiler/rustc_ast/src/mut_visit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1120,13 +1120,14 @@ fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) {
11201120
}
11211121

11221122
pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) {
1123-
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety } = fd;
1123+
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety, default } = fd;
11241124
visitor.visit_id(id);
11251125
visit_attrs(visitor, attrs);
11261126
visitor.visit_vis(vis);
11271127
visit_safety(visitor, safety);
11281128
visit_opt(ident, |ident| visitor.visit_ident(ident));
11291129
visitor.visit_ty(ty);
1130+
visit_opt(default, |default| visitor.visit_anon_const(default));
11301131
visitor.visit_span(span);
11311132
}
11321133

compiler/rustc_ast/src/visit.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -975,11 +975,13 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>(
975975
}
976976

977977
pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result {
978-
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _ } = field;
978+
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _, default } =
979+
field;
979980
walk_list!(visitor, visit_attribute, attrs);
980981
try_visit!(visitor.visit_vis(vis));
981982
visit_opt!(visitor, visit_ident, ident);
982983
try_visit!(visitor.visit_ty(ty));
984+
visit_opt!(visitor, visit_anon_const, &*default);
983985
V::Result::output()
984986
}
985987

compiler/rustc_ast_lowering/messages.ftl

+3-4
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,6 @@ ast_lowering_bad_return_type_notation_output =
4545
4646
ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet
4747
48-
ast_lowering_base_expression_double_dot =
49-
base expression required after `..`
50-
.suggestion = add a base expression here
51-
5248
ast_lowering_clobber_abi_not_supported =
5349
`clobber_abi` is not supported on this target
5450
@@ -57,6 +53,9 @@ ast_lowering_closure_cannot_be_static = closures cannot be static
5753
ast_lowering_coroutine_too_many_parameters =
5854
too many parameters for a coroutine (expected 0 or 1 parameters)
5955
56+
ast_lowering_default_field_in_tuple = default fields are not supported in tuple structs
57+
.label = default fields are only supported on structs
58+
6059
ast_lowering_does_not_support_modifiers =
6160
the `{$class_name}` register class does not support template modifiers
6261

compiler/rustc_ast_lowering/src/errors.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ pub(crate) struct InvalidAbi {
3838
pub suggestion: Option<InvalidAbiSuggestion>,
3939
}
4040

41+
#[derive(Diagnostic)]
42+
#[diag(ast_lowering_default_field_in_tuple)]
43+
pub(crate) struct TupleStructWithDefault {
44+
#[primary_span]
45+
#[label]
46+
pub span: Span,
47+
}
48+
4149
pub(crate) struct InvalidAbiReason(pub &'static str);
4250

4351
impl Subdiagnostic for InvalidAbiReason {
@@ -114,14 +122,6 @@ pub(crate) struct UnderscoreExprLhsAssign {
114122
pub span: Span,
115123
}
116124

117-
#[derive(Diagnostic)]
118-
#[diag(ast_lowering_base_expression_double_dot, code = E0797)]
119-
pub(crate) struct BaseExpressionDoubleDot {
120-
#[primary_span]
121-
#[suggestion(code = "/* expr */", applicability = "has-placeholders", style = "verbose")]
122-
pub span: Span,
123-
}
124-
125125
#[derive(Diagnostic)]
126126
#[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)]
127127
pub(crate) struct AwaitOnlyInAsyncFnAndBlocks {

compiler/rustc_ast_lowering/src/expr.rs

+8-11
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ use thin_vec::{ThinVec, thin_vec};
1919
use visit::{Visitor, walk_expr};
2020

2121
use super::errors::{
22-
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot,
23-
ClosureCannotBeStatic, CoroutineTooManyParameters,
24-
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
25-
NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign,
22+
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, ClosureCannotBeStatic,
23+
CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment,
24+
InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard,
25+
UnderscoreExprLhsAssign,
2626
};
2727
use super::{
2828
GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
@@ -357,12 +357,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
357357
),
358358
ExprKind::Struct(se) => {
359359
let rest = match &se.rest {
360-
StructRest::Base(e) => Some(self.lower_expr(e)),
361-
StructRest::Rest(sp) => {
362-
let guar = self.dcx().emit_err(BaseExpressionDoubleDot { span: *sp });
363-
Some(&*self.arena.alloc(self.expr_err(*sp, guar)))
364-
}
365-
StructRest::None => None,
360+
StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)),
361+
StructRest::Rest(sp) => hir::StructTailExpr::DefaultFields(*sp),
362+
StructRest::None => hir::StructTailExpr::None,
366363
};
367364
hir::ExprKind::Struct(
368365
self.arena.alloc(self.lower_qpath(
@@ -1526,7 +1523,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
15261523
hir::ExprKind::Struct(
15271524
self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))),
15281525
fields,
1529-
None,
1526+
hir::StructTailExpr::None,
15301527
)
15311528
}
15321529

compiler/rustc_ast_lowering/src/item.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ use smallvec::{SmallVec, smallvec};
1717
use thin_vec::ThinVec;
1818
use tracing::instrument;
1919

20-
use super::errors::{InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound};
20+
use super::errors::{
21+
InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound,
22+
TupleStructWithDefault,
23+
};
2124
use super::{
2225
AstOwner, FnDeclKind, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode,
2326
ResolverAstLoweringExt,
@@ -690,13 +693,27 @@ impl<'hir> LoweringContext<'_, 'hir> {
690693
VariantData::Tuple(fields, id) => {
691694
let ctor_id = self.lower_node_id(*id);
692695
self.alias_attrs(ctor_id, parent_id);
693-
hir::VariantData::Tuple(
694-
self.arena.alloc_from_iter(
695-
fields.iter().enumerate().map(|f| self.lower_field_def(f)),
696-
),
697-
ctor_id,
698-
self.local_def_id(*id),
699-
)
696+
let fields = self
697+
.arena
698+
.alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f)));
699+
for field in &fields[..] {
700+
if let Some(default) = field.default {
701+
// Default values in tuple struct and tuple variants are not allowed by the
702+
// RFC due to concerns about the syntax, both in the item definition and the
703+
// expression. We could in the future allow `struct S(i32 = 0);` and force
704+
// users to construct the value with `let _ = S { .. };`.
705+
if self.tcx.features().default_field_values() {
706+
self.dcx().emit_err(TupleStructWithDefault { span: default.span });
707+
} else {
708+
let _ = self.dcx().span_delayed_bug(
709+
default.span,
710+
"expected `default values on `struct` fields aren't supported` \
711+
feature-gate error but none was produced",
712+
);
713+
}
714+
}
715+
}
716+
hir::VariantData::Tuple(fields, ctor_id, self.local_def_id(*id))
700717
}
701718
VariantData::Unit(id) => {
702719
let ctor_id = self.lower_node_id(*id);
@@ -723,6 +740,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
723740
None => Ident::new(sym::integer(index), self.lower_span(f.span)),
724741
},
725742
vis_span: self.lower_span(f.vis.span),
743+
default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)),
726744
ty,
727745
safety: self.lower_safety(f.safety, hir::Safety::Safe),
728746
}

compiler/rustc_ast_passes/src/feature_gate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
557557
gate_all!(explicit_tail_calls, "`become` expression is experimental");
558558
gate_all!(generic_const_items, "generic const items are experimental");
559559
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
560+
gate_all!(default_field_values, "default values on fields are experimental");
560561
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
561562
gate_all!(postfix_match, "postfix match is experimental");
562563
gate_all!(mut_ref, "mutable by-reference bindings are experimental");

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1151,7 +1151,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
11511151
expr: &hir::Expr<'_>,
11521152
) {
11531153
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
1154-
let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return };
1154+
let hir::ExprKind::Struct(struct_qpath, fields, hir::StructTailExpr::Base(base)) =
1155+
expr.kind
1156+
else {
1157+
return;
1158+
};
11551159
let hir::QPath::Resolved(_, path) = struct_qpath else { return };
11561160
let hir::def::Res::Def(_, def_id) = path.res else { return };
11571161
let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return };
@@ -1239,7 +1243,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
12391243
expr: &'tcx hir::Expr<'tcx>,
12401244
use_spans: Option<UseSpans<'tcx>>,
12411245
) {
1242-
if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind {
1246+
if let hir::ExprKind::Struct(_, _, hir::StructTailExpr::Base(_)) = expr.kind {
12431247
// We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single
12441248
// `Location` that covers both the `S { ... }` literal, all of its fields and the
12451249
// `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr`

compiler/rustc_builtin_macros/messages.ftl

+1-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive
261261
262262
builtin_macros_non_generic_pointee = the `#[pointee]` attribute may only be used on generic parameters
263263
264-
builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants
264+
builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants{$post}
265265
.help = consider a manual implementation of `Default`
266266
267267
builtin_macros_only_one_argument = {$name} takes 1 argument

compiler/rustc_builtin_macros/src/deriving/decodable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ where
205205
let fields = fields
206206
.iter()
207207
.enumerate()
208-
.map(|(i, &(ident, span))| {
208+
.map(|(i, &(ident, span, _))| {
209209
let arg = getarg(cx, span, ident.name, i);
210210
cx.field_imm(span, ident, arg)
211211
})

compiler/rustc_builtin_macros/src/deriving/default.rs

+70-13
Original file line numberDiff line numberDiff line change
@@ -54,26 +54,38 @@ pub(crate) fn expand_deriving_default(
5454
trait_def.expand(cx, mitem, item, push)
5555
}
5656

57+
fn default_call(cx: &ExtCtxt<'_>, span: Span) -> ast::ptr::P<ast::Expr> {
58+
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
59+
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
60+
cx.expr_call_global(span, default_ident, ThinVec::new())
61+
}
62+
5763
fn default_struct_substructure(
5864
cx: &ExtCtxt<'_>,
5965
trait_span: Span,
6066
substr: &Substructure<'_>,
6167
summary: &StaticFields,
6268
) -> BlockOrExpr {
63-
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
64-
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
65-
let default_call = |span| cx.expr_call_global(span, default_ident.clone(), ThinVec::new());
66-
6769
let expr = match summary {
6870
Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident),
6971
Unnamed(fields, IsTuple::Yes) => {
70-
let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
72+
let exprs = fields.iter().map(|sp| default_call(cx, *sp)).collect();
7173
cx.expr_call_ident(trait_span, substr.type_ident, exprs)
7274
}
7375
Named(fields) => {
7476
let default_fields = fields
7577
.iter()
76-
.map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
78+
.map(|(ident, span, default_val)| {
79+
let value = match default_val {
80+
// We use `Default::default()`.
81+
None => default_call(cx, *span),
82+
// We use the field default const expression.
83+
Some(val) => {
84+
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
85+
}
86+
};
87+
cx.field_imm(*span, *ident, value)
88+
})
7789
.collect();
7890
cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
7991
}
@@ -93,10 +105,38 @@ fn default_enum_substructure(
93105
} {
94106
Ok(default_variant) => {
95107
// We now know there is exactly one unit variant with exactly one `#[default]` attribute.
96-
cx.expr_path(cx.path(default_variant.span, vec![
97-
Ident::new(kw::SelfUpper, default_variant.span),
98-
default_variant.ident,
99-
]))
108+
match &default_variant.data {
109+
VariantData::Unit(_) => cx.expr_path(cx.path(default_variant.span, vec![
110+
Ident::new(kw::SelfUpper, default_variant.span),
111+
default_variant.ident,
112+
])),
113+
VariantData::Struct { fields, .. } => {
114+
// This only happens if `#![feature(default_field_values)]`. We have validated
115+
// all fields have default values in the definition.
116+
let default_fields = fields
117+
.iter()
118+
.map(|field| {
119+
cx.field_imm(field.span, field.ident.unwrap(), match &field.default {
120+
// We use `Default::default()`.
121+
None => default_call(cx, field.span),
122+
// We use the field default const expression.
123+
Some(val) => {
124+
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
125+
}
126+
})
127+
})
128+
.collect();
129+
let path = cx.path(default_variant.span, vec![
130+
Ident::new(kw::SelfUpper, default_variant.span),
131+
default_variant.ident,
132+
]);
133+
cx.expr_struct(default_variant.span, path, default_fields)
134+
}
135+
// Logic error in `extract_default_variant`.
136+
VariantData::Tuple(..) => {
137+
cx.dcx().bug("encountered tuple variant annotated with `#[default]`")
138+
}
139+
}
100140
}
101141
Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
102142
};
@@ -156,8 +196,20 @@ fn extract_default_variant<'a>(
156196
}
157197
};
158198

159-
if !matches!(variant.data, VariantData::Unit(..)) {
160-
let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span });
199+
if cx.ecfg.features.default_field_values()
200+
&& let VariantData::Struct { fields, .. } = &variant.data
201+
&& fields.iter().all(|f| f.default.is_some())
202+
// Disallow `#[default] Variant {}`
203+
&& !fields.is_empty()
204+
{
205+
// Allowed
206+
} else if !matches!(variant.data, VariantData::Unit(..)) {
207+
let post = if cx.ecfg.features.default_field_values() {
208+
" or variants where every field has a default value"
209+
} else {
210+
""
211+
};
212+
let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span, post });
161213
return Err(guar);
162214
}
163215

@@ -216,7 +268,12 @@ struct DetectNonVariantDefaultAttr<'a, 'b> {
216268
impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
217269
fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
218270
if attr.has_name(kw::Default) {
219-
self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span });
271+
let post = if self.cx.ecfg.features.default_field_values() {
272+
" or variants where every field has a default value"
273+
} else {
274+
""
275+
};
276+
self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span, post });
220277
}
221278

222279
rustc_ast::visit::walk_attribute(self, attr);

compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ pub(crate) use StaticFields::*;
182182
pub(crate) use SubstructureFields::*;
183183
use rustc_ast::ptr::P;
184184
use rustc_ast::{
185-
self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics,
186-
Mutability, PatKind, VariantData,
185+
self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
186+
Generics, Mutability, PatKind, VariantData,
187187
};
188188
use rustc_attr as attr;
189189
use rustc_expand::base::{Annotatable, ExtCtxt};
@@ -296,7 +296,7 @@ pub(crate) enum StaticFields {
296296
/// Tuple and unit structs/enum variants like this.
297297
Unnamed(Vec<Span>, IsTuple),
298298
/// Normal structs/struct variants.
299-
Named(Vec<(Ident, Span)>),
299+
Named(Vec<(Ident, Span, Option<AnonConst>)>),
300300
}
301301

302302
/// A summary of the possible sets of fields.
@@ -1435,7 +1435,7 @@ impl<'a> TraitDef<'a> {
14351435
for field in struct_def.fields() {
14361436
let sp = field.span.with_ctxt(self.span.ctxt());
14371437
match field.ident {
1438-
Some(ident) => named_idents.push((ident, sp)),
1438+
Some(ident) => named_idents.push((ident, sp, field.default.clone())),
14391439
_ => just_spans.push(sp),
14401440
}
14411441
}

compiler/rustc_builtin_macros/src/errors.rs

+1
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ pub(crate) struct MultipleDefaultsSugg {
424424
pub(crate) struct NonUnitDefault {
425425
#[primary_span]
426426
pub(crate) span: Span,
427+
pub(crate) post: &'static str,
427428
}
428429

429430
#[derive(Diagnostic)]

0 commit comments

Comments
 (0)