Introduce default_field_values
feature
Initial implementation of `#[feature(default_field_values]`, proposed in https://github.com/rust-lang/rfcs/pull/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.
This commit is contained in:
parent
f6cb952dc1
commit
9ac95c10c0
70 changed files with 1469 additions and 392 deletions
|
@ -205,7 +205,7 @@ where
|
|||
let fields = fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, &(ident, span))| {
|
||||
.map(|(i, &(ident, span, _))| {
|
||||
let arg = getarg(cx, span, ident.name, i);
|
||||
cx.field_imm(span, ident, arg)
|
||||
})
|
||||
|
|
|
@ -54,26 +54,38 @@ pub(crate) fn expand_deriving_default(
|
|||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
fn default_call(cx: &ExtCtxt<'_>, span: Span) -> ast::ptr::P<ast::Expr> {
|
||||
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
|
||||
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
|
||||
cx.expr_call_global(span, default_ident, ThinVec::new())
|
||||
}
|
||||
|
||||
fn default_struct_substructure(
|
||||
cx: &ExtCtxt<'_>,
|
||||
trait_span: Span,
|
||||
substr: &Substructure<'_>,
|
||||
summary: &StaticFields,
|
||||
) -> BlockOrExpr {
|
||||
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
|
||||
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
|
||||
let default_call = |span| cx.expr_call_global(span, default_ident.clone(), ThinVec::new());
|
||||
|
||||
let expr = match summary {
|
||||
Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident),
|
||||
Unnamed(fields, IsTuple::Yes) => {
|
||||
let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
|
||||
let exprs = fields.iter().map(|sp| default_call(cx, *sp)).collect();
|
||||
cx.expr_call_ident(trait_span, substr.type_ident, exprs)
|
||||
}
|
||||
Named(fields) => {
|
||||
let default_fields = fields
|
||||
.iter()
|
||||
.map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
|
||||
.map(|(ident, span, default_val)| {
|
||||
let value = match default_val {
|
||||
// We use `Default::default()`.
|
||||
None => default_call(cx, *span),
|
||||
// We use the field default const expression.
|
||||
Some(val) => {
|
||||
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
|
||||
}
|
||||
};
|
||||
cx.field_imm(*span, *ident, value)
|
||||
})
|
||||
.collect();
|
||||
cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
|
||||
}
|
||||
|
@ -93,10 +105,38 @@ fn default_enum_substructure(
|
|||
} {
|
||||
Ok(default_variant) => {
|
||||
// We now know there is exactly one unit variant with exactly one `#[default]` attribute.
|
||||
cx.expr_path(cx.path(default_variant.span, vec![
|
||||
Ident::new(kw::SelfUpper, default_variant.span),
|
||||
default_variant.ident,
|
||||
]))
|
||||
match &default_variant.data {
|
||||
VariantData::Unit(_) => cx.expr_path(cx.path(default_variant.span, vec![
|
||||
Ident::new(kw::SelfUpper, default_variant.span),
|
||||
default_variant.ident,
|
||||
])),
|
||||
VariantData::Struct { fields, .. } => {
|
||||
// This only happens if `#![feature(default_field_values)]`. We have validated
|
||||
// all fields have default values in the definition.
|
||||
let default_fields = fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
cx.field_imm(field.span, field.ident.unwrap(), match &field.default {
|
||||
// We use `Default::default()`.
|
||||
None => default_call(cx, field.span),
|
||||
// We use the field default const expression.
|
||||
Some(val) => {
|
||||
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let path = cx.path(default_variant.span, vec![
|
||||
Ident::new(kw::SelfUpper, default_variant.span),
|
||||
default_variant.ident,
|
||||
]);
|
||||
cx.expr_struct(default_variant.span, path, default_fields)
|
||||
}
|
||||
// Logic error in `extract_default_variant`.
|
||||
VariantData::Tuple(..) => {
|
||||
cx.dcx().bug("encountered tuple variant annotated with `#[default]`")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
|
||||
};
|
||||
|
@ -156,7 +196,12 @@ fn extract_default_variant<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
if !matches!(variant.data, VariantData::Unit(..)) {
|
||||
if cx.ecfg.features.default_field_values()
|
||||
&& let VariantData::Struct { fields, .. } = &variant.data
|
||||
&& fields.iter().all(|f| f.default.is_some())
|
||||
{
|
||||
// Allowed
|
||||
} else if !matches!(variant.data, VariantData::Unit(..)) {
|
||||
let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span });
|
||||
return Err(guar);
|
||||
}
|
||||
|
|
|
@ -182,8 +182,8 @@ pub(crate) use StaticFields::*;
|
|||
pub(crate) use SubstructureFields::*;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::{
|
||||
self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics,
|
||||
Mutability, PatKind, VariantData,
|
||||
self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
|
||||
Generics, Mutability, PatKind, VariantData,
|
||||
};
|
||||
use rustc_attr as attr;
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
|
@ -296,7 +296,7 @@ pub(crate) enum StaticFields {
|
|||
/// Tuple and unit structs/enum variants like this.
|
||||
Unnamed(Vec<Span>, IsTuple),
|
||||
/// Normal structs/struct variants.
|
||||
Named(Vec<(Ident, Span)>),
|
||||
Named(Vec<(Ident, Span, Option<AnonConst>)>),
|
||||
}
|
||||
|
||||
/// A summary of the possible sets of fields.
|
||||
|
@ -1435,7 +1435,7 @@ impl<'a> TraitDef<'a> {
|
|||
for field in struct_def.fields() {
|
||||
let sp = field.span.with_ctxt(self.span.ctxt());
|
||||
match field.ident {
|
||||
Some(ident) => named_idents.push((ident, sp)),
|
||||
Some(ident) => named_idents.push((ident, sp, field.default.clone())),
|
||||
_ => just_spans.push(sp),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue