Skip to content

Commit 5edc88c

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> { .. } ``` Add lint for default fields that will always fail const-eval We *allow* this to happen for API writers that might want to rely on users' getting a compile error when using the default field, different to the error that they would get when the field isn't default. We could change this to *always* error instead of being a lint, if we wanted. This will *not* catch errors for partially evaluated consts, like when the expression relies on a const parameter. Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`: - Suggest adding a base expression if there are missing fields. - Suggest enabling the feature if all the missing fields have optional values. - Suggest removing `..` if there are no missing fields.
1 parent f6cb952 commit 5edc88c

File tree

70 files changed

+1459
-368
lines changed

Some content is hidden

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

70 files changed

+1459
-368
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 1 addition & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 0 additions & 4 deletions
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

compiler/rustc_ast_lowering/src/errors.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,6 @@ pub(crate) struct UnderscoreExprLhsAssign {
114114
pub span: Span,
115115
}
116116

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-
125117
#[derive(Diagnostic)]
126118
#[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)]
127119
pub(crate) struct AwaitOnlyInAsyncFnAndBlocks {

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 8 additions & 11 deletions
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
723723
None => Ident::new(sym::integer(index), self.lower_span(f.span)),
724724
},
725725
vis_span: self.lower_span(f.vis.span),
726+
default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)),
726727
ty,
727728
safety: self.lower_safety(f.safety, hir::Safety::Safe),
728729
}

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 1 addition & 0 deletions
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 `struct` fields aren't supported");
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

Lines changed: 6 additions & 2 deletions
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/src/deriving/decodable.rs

Lines changed: 1 addition & 1 deletion
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
})

0 commit comments

Comments
 (0)