Skip to content

Commit e9c76a3

Browse files
committed
Introduce default_field_values feature
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> { .. } ```
1 parent 2bd1e89 commit e9c76a3

File tree

60 files changed

+874
-290
lines changed

Some content is hidden

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

60 files changed

+874
-290
lines changed

compiler/rustc_ast/src/ast.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2995,6 +2995,7 @@ pub struct FieldDef {
29952995
pub ident: Option<Ident>,
29962996

29972997
pub ty: P<Ty>,
2998+
pub default: Option<AnonConst>,
29982999
pub is_placeholder: bool,
29993000
}
30003001

compiler/rustc_ast/src/mut_visit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1048,12 +1048,13 @@ pub fn walk_flat_map_field_def<T: MutVisitor>(
10481048
visitor: &mut T,
10491049
mut fd: FieldDef,
10501050
) -> SmallVec<[FieldDef; 1]> {
1051-
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = &mut fd;
1051+
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, default } = &mut fd;
10521052
visitor.visit_id(id);
10531053
visit_attrs(visitor, attrs);
10541054
visitor.visit_vis(vis);
10551055
visit_opt(ident, |ident| visitor.visit_ident(ident));
10561056
visitor.visit_ty(ty);
1057+
visit_opt(default, |default| visitor.visit_anon_const(default));
10571058
visitor.visit_span(span);
10581059
smallvec![fd]
10591060
}

compiler/rustc_ast/src/visit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -934,11 +934,12 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>(
934934
}
935935

936936
pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result {
937-
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _ } = field;
937+
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, default } = field;
938938
walk_list!(visitor, visit_attribute, attrs);
939939
try_visit!(visitor.visit_vis(vis));
940940
visit_opt!(visitor, visit_ident, *ident);
941941
try_visit!(visitor.visit_ty(ty));
942+
visit_opt!(visitor, visit_anon_const, &*default);
942943
V::Result::output()
943944
}
944945

compiler/rustc_ast_lowering/src/expr.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -316,12 +316,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
316316
),
317317
ExprKind::Struct(se) => {
318318
let rest = match &se.rest {
319-
StructRest::Base(e) => Some(self.lower_expr(e)),
319+
StructRest::Base(e) => hir::Rest::Base(self.lower_expr(e)),
320320
StructRest::Rest(sp) => {
321-
let guar = self.dcx().emit_err(BaseExpressionDoubleDot { span: *sp });
322-
Some(&*self.arena.alloc(self.expr_err(*sp, guar)))
321+
if !self.tcx.features().default_field_values {
322+
self.dcx().emit_err(BaseExpressionDoubleDot { span: *sp });
323+
}
324+
hir::Rest::DefaultFields(*sp)
323325
}
324-
StructRest::None => None,
326+
StructRest::None => hir::Rest::None,
325327
};
326328
hir::ExprKind::Struct(
327329
self.arena.alloc(self.lower_qpath(
@@ -1460,7 +1462,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
14601462
hir::ExprKind::Struct(
14611463
self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))),
14621464
fields,
1463-
None,
1465+
hir::Rest::None,
14641466
)
14651467
}
14661468

compiler/rustc_ast_lowering/src/item.rs

+1
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
772772
None => Ident::new(sym::integer(index), self.lower_span(f.span)),
773773
},
774774
vis_span: self.lower_span(f.vis.span),
775+
default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)),
775776
ty,
776777
}
777778
}

compiler/rustc_ast_passes/src/feature_gate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
541541
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
542542
gate_all!(explicit_tail_calls, "`become` expression is experimental");
543543
gate_all!(generic_const_items, "generic const items are experimental");
544+
gate_all!(default_field_values, "default values on `struct` fields aren't supported");
544545
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
545546
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
546547
gate_all!(postfix_match, "postfix match is experimental");

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1146,7 +1146,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
11461146
expr: &hir::Expr<'_>,
11471147
) {
11481148
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
1149-
let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return };
1149+
let hir::ExprKind::Struct(struct_qpath, fields, hir::Rest::Base(base)) = expr.kind else {
1150+
return;
1151+
};
11501152
let hir::QPath::Resolved(_, path) = struct_qpath else { return };
11511153
let hir::def::Res::Def(_, def_id) = path.res else { return };
11521154
let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return };
@@ -1234,7 +1236,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
12341236
expr: &'tcx hir::Expr<'tcx>,
12351237
use_spans: Option<UseSpans<'tcx>>,
12361238
) {
1237-
if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind {
1239+
if let hir::ExprKind::Struct(_, _, hir::Rest::Base(_)) = expr.kind {
12381240
// We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single
12391241
// `Location` that covers both the `S { ... }` literal, all of its fields and the
12401242
// `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr`

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

+56-11
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,7 +196,12 @@ fn extract_default_variant<'a>(
156196
}
157197
};
158198

159-
if !matches!(variant.data, VariantData::Unit(..)) {
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+
{
203+
// Allowed
204+
} else if !matches!(variant.data, VariantData::Unit(..)) {
160205
let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span });
161206
return Err(guar);
162207
}

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.
@@ -1443,7 +1443,7 @@ impl<'a> TraitDef<'a> {
14431443
for field in struct_def.fields() {
14441444
let sp = field.span.with_ctxt(self.span.ctxt());
14451445
match field.ident {
1446-
Some(ident) => named_idents.push((ident, sp)),
1446+
Some(ident) => named_idents.push((ident, sp, field.default.clone())),
14471447
_ => just_spans.push(sp),
14481448
}
14491449
}

compiler/rustc_expand/src/placeholders.rs

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ pub(crate) fn placeholder(
173173
ty: ty(),
174174
vis,
175175
is_placeholder: true,
176+
default: None,
176177
}]),
177178
AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant {
178179
attrs: Default::default(),

compiler/rustc_feature/src/unstable.rs

+5
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,11 @@ declare_features! (
424424
(unstable, custom_test_frameworks, "1.30.0", Some(50297)),
425425
/// Allows declarative macros 2.0 (`macro`).
426426
(unstable, decl_macro, "1.17.0", Some(39412)),
427+
/// Allows the use of default values on struct definitions and the construction of struct
428+
/// literals with the functional update syntax without a base.
429+
// FIXME: change tracking issue number from RFCS repo to rust repo after RFC is accepted an
430+
// tracking ticket is created.
431+
(unstable, default_field_values, "CURRENT_RUSTC_VERSION", Some(3681)),
427432
/// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait
428433
(unstable, deprecated_safe, "1.61.0", Some(94978)),
429434
/// Allows having using `suggestion` in the `#[deprecated]` attribute.

compiler/rustc_hir/src/hir.rs

+31-10
Original file line numberDiff line numberDiff line change
@@ -1873,7 +1873,12 @@ impl Expr<'_> {
18731873
base.can_have_side_effects()
18741874
}
18751875
ExprKind::Struct(_, fields, init) => {
1876-
fields.iter().map(|field| field.expr).chain(init).any(|e| e.can_have_side_effects())
1876+
let init_side_effects = match init {
1877+
Rest::Base(init) => init.can_have_side_effects(),
1878+
Rest::DefaultFields(_) | Rest::None => false,
1879+
};
1880+
fields.iter().map(|field| field.expr).any(|e| e.can_have_side_effects())
1881+
|| init_side_effects
18771882
}
18781883

18791884
ExprKind::Array(args)
@@ -1942,20 +1947,28 @@ impl Expr<'_> {
19421947
ExprKind::Path(QPath::Resolved(None, path2)),
19431948
) => path1.res == path2.res,
19441949
(
1945-
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val1], None),
1946-
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val2], None),
1950+
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val1], Rest::None),
1951+
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val2], Rest::None),
19471952
)
19481953
| (
1949-
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val1], None),
1950-
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val2], None),
1954+
ExprKind::Struct(
1955+
QPath::LangItem(LangItem::RangeToInclusive, _),
1956+
[val1],
1957+
Rest::None,
1958+
),
1959+
ExprKind::Struct(
1960+
QPath::LangItem(LangItem::RangeToInclusive, _),
1961+
[val2],
1962+
Rest::None,
1963+
),
19511964
)
19521965
| (
1953-
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val1], None),
1954-
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val2], None),
1966+
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val1], Rest::None),
1967+
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val2], Rest::None),
19551968
) => val1.expr.equivalent_for_indexing(val2.expr),
19561969
(
1957-
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val1, val3], None),
1958-
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val2, val4], None),
1970+
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val1, val3], Rest::None),
1971+
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val2, val4], Rest::None),
19591972
) => {
19601973
val1.expr.equivalent_for_indexing(val2.expr)
19611974
&& val3.expr.equivalent_for_indexing(val4.expr)
@@ -2112,7 +2125,7 @@ pub enum ExprKind<'hir> {
21122125
///
21132126
/// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`,
21142127
/// where `base` is the `Option<Expr>`.
2115-
Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], Option<&'hir Expr<'hir>>),
2128+
Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], Rest<'hir>),
21162129

21172130
/// An array literal constructed from one repeated element.
21182131
///
@@ -2127,6 +2140,13 @@ pub enum ExprKind<'hir> {
21272140
Err(rustc_span::ErrorGuaranteed),
21282141
}
21292142

2143+
#[derive(Debug, Clone, Copy, HashStable_Generic)]
2144+
pub enum Rest<'hir> {
2145+
Base(&'hir Expr<'hir>),
2146+
DefaultFields(Span),
2147+
None,
2148+
}
2149+
21302150
/// Represents an optionally `Self`-qualified value/type path or associated extension.
21312151
///
21322152
/// To resolve the path to a `DefId`, call [`qpath_res`].
@@ -3181,6 +3201,7 @@ pub struct FieldDef<'hir> {
31813201
pub hir_id: HirId,
31823202
pub def_id: LocalDefId,
31833203
pub ty: &'hir Ty<'hir>,
3204+
pub default: Option<&'hir AnonConst>,
31843205
}
31853206

31863207
impl FieldDef<'_> {

compiler/rustc_hir/src/intravisit.rs

+12-5
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
756756
ExprKind::Struct(ref qpath, fields, ref optional_base) => {
757757
try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span));
758758
walk_list!(visitor, visit_expr_field, fields);
759-
visit_opt!(visitor, visit_expr, optional_base);
759+
match optional_base {
760+
Rest::Base(base) => try_visit!(visitor.visit_expr(base)),
761+
Rest::None | Rest::DefaultFields(_) => {}
762+
}
760763
}
761764
ExprKind::Tup(subexpressions) => {
762765
walk_list!(visitor, visit_expr, subexpressions);
@@ -1194,10 +1197,14 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>(
11941197
V::Result::output()
11951198
}
11961199

1197-
pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) -> V::Result {
1198-
try_visit!(visitor.visit_id(field.hir_id));
1199-
try_visit!(visitor.visit_ident(field.ident));
1200-
visitor.visit_ty(field.ty)
1200+
pub fn walk_field_def<'v, V: Visitor<'v>>(
1201+
visitor: &mut V,
1202+
FieldDef { hir_id, ident, ty, default, span: _, vis_span: _, def_id: _ }: &'v FieldDef<'v>,
1203+
) -> V::Result {
1204+
try_visit!(visitor.visit_id(*hir_id));
1205+
try_visit!(visitor.visit_ident(*ident));
1206+
visit_opt!(visitor, visit_anon_const, default);
1207+
visitor.visit_ty(*ty)
12011208
}
12021209

12031210
pub fn walk_enum_def<'v, V: Visitor<'v>>(

compiler/rustc_hir_analysis/src/collect.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1082,12 +1082,12 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> {
10821082
}
10831083
}
10841084

1085-
fn lower_variant(
1086-
tcx: TyCtxt<'_>,
1085+
fn lower_variant<'tcx>(
1086+
tcx: TyCtxt<'tcx>,
10871087
variant_did: Option<LocalDefId>,
10881088
ident: Ident,
10891089
discr: ty::VariantDiscr,
1090-
def: &hir::VariantData<'_>,
1090+
def: &hir::VariantData<'tcx>,
10911091
adt_kind: ty::AdtKind,
10921092
parent_did: LocalDefId,
10931093
is_anonymous: bool,
@@ -1109,6 +1109,7 @@ fn lower_variant(
11091109
did: f.def_id.to_def_id(),
11101110
name: f.ident.name,
11111111
vis: tcx.visibility(f.def_id),
1112+
value: f.default.map(|v| v.def_id.to_def_id()),
11121113
})
11131114
.collect();
11141115
let recovered = match def {

0 commit comments

Comments
 (0)