1
Fork 0

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:
Esteban Küber 2024-08-24 17:22:48 +00:00
parent f6cb952dc1
commit 9ac95c10c0
70 changed files with 1469 additions and 392 deletions

View file

@ -3119,6 +3119,7 @@ pub struct FieldDef {
pub ident: Option<Ident>,
pub ty: P<Ty>,
pub default: Option<AnonConst>,
pub is_placeholder: bool,
}

View file

@ -1120,13 +1120,14 @@ fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) {
}
pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) {
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety } = fd;
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety, default } = fd;
visitor.visit_id(id);
visit_attrs(visitor, attrs);
visitor.visit_vis(vis);
visit_safety(visitor, safety);
visit_opt(ident, |ident| visitor.visit_ident(ident));
visitor.visit_ty(ty);
visit_opt(default, |default| visitor.visit_anon_const(default));
visitor.visit_span(span);
}

View file

@ -975,11 +975,13 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>(
}
pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result {
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _ } = field;
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _, default } =
field;
walk_list!(visitor, visit_attribute, attrs);
try_visit!(visitor.visit_vis(vis));
visit_opt!(visitor, visit_ident, ident);
try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_anon_const, &*default);
V::Result::output()
}

View file

@ -45,10 +45,6 @@ ast_lowering_bad_return_type_notation_output =
ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet
ast_lowering_base_expression_double_dot =
base expression required after `..`
.suggestion = add a base expression here
ast_lowering_clobber_abi_not_supported =
`clobber_abi` is not supported on this target

View file

@ -114,14 +114,6 @@ pub(crate) struct UnderscoreExprLhsAssign {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_base_expression_double_dot, code = E0797)]
pub(crate) struct BaseExpressionDoubleDot {
#[primary_span]
#[suggestion(code = "/* expr */", applicability = "has-placeholders", style = "verbose")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)]
pub(crate) struct AwaitOnlyInAsyncFnAndBlocks {

View file

@ -19,10 +19,10 @@ use thin_vec::{ThinVec, thin_vec};
use visit::{Visitor, walk_expr};
use super::errors::{
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot,
ClosureCannotBeStatic, CoroutineTooManyParameters,
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign,
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, ClosureCannotBeStatic,
CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment,
InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard,
UnderscoreExprLhsAssign,
};
use super::{
GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
@ -357,12 +357,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
),
ExprKind::Struct(se) => {
let rest = match &se.rest {
StructRest::Base(e) => Some(self.lower_expr(e)),
StructRest::Rest(sp) => {
let guar = self.dcx().emit_err(BaseExpressionDoubleDot { span: *sp });
Some(&*self.arena.alloc(self.expr_err(*sp, guar)))
}
StructRest::None => None,
StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)),
StructRest::Rest(sp) => hir::StructTailExpr::DefaultFields(*sp),
StructRest::None => hir::StructTailExpr::None,
};
hir::ExprKind::Struct(
self.arena.alloc(self.lower_qpath(
@ -1526,7 +1523,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Struct(
self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))),
fields,
None,
hir::StructTailExpr::None,
)
}

View file

@ -723,6 +723,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
None => Ident::new(sym::integer(index), self.lower_span(f.span)),
},
vis_span: self.lower_span(f.vis.span),
default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)),
ty,
safety: self.lower_safety(f.safety, hir::Safety::Safe),
}

View file

@ -557,6 +557,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(explicit_tail_calls, "`become` expression is experimental");
gate_all!(generic_const_items, "generic const items are experimental");
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
gate_all!(default_field_values, "default values on `struct` fields aren't supported");
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(postfix_match, "postfix match is experimental");
gate_all!(mut_ref, "mutable by-reference bindings are experimental");

View file

@ -1151,7 +1151,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
expr: &hir::Expr<'_>,
) {
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return };
let hir::ExprKind::Struct(struct_qpath, fields, hir::StructTailExpr::Base(base)) =
expr.kind
else {
return;
};
let hir::QPath::Resolved(_, path) = struct_qpath else { return };
let hir::def::Res::Def(_, def_id) = path.res else { return };
let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return };
@ -1239,7 +1243,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
expr: &'tcx hir::Expr<'tcx>,
use_spans: Option<UseSpans<'tcx>>,
) {
if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind {
if let hir::ExprKind::Struct(_, _, hir::StructTailExpr::Base(_)) = expr.kind {
// We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single
// `Location` that covers both the `S { ... }` literal, all of its fields and the
// `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr`

View file

@ -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)
})

View file

@ -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);
}

View file

@ -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),
}
}

View file

@ -174,6 +174,7 @@ pub(crate) fn placeholder(
vis,
is_placeholder: true,
safety: Safety::Default,
default: None,
}]),
AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant {
attrs: Default::default(),

View file

@ -455,6 +455,9 @@ declare_features! (
(unstable, custom_test_frameworks, "1.30.0", Some(50297)),
/// Allows declarative macros 2.0 (`macro`).
(unstable, decl_macro, "1.17.0", Some(39412)),
/// Allows the use of default values on struct definitions and the construction of struct
/// literals with the functional update syntax without a base.
(unstable, default_field_values, "CURRENT_RUSTC_VERSION", Some(132162)),
/// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait
(unstable, deprecated_safe, "1.61.0", Some(94978)),
/// Allows having using `suggestion` in the `#[deprecated]` attribute.

View file

@ -1857,7 +1857,12 @@ impl Expr<'_> {
base.can_have_side_effects()
}
ExprKind::Struct(_, fields, init) => {
fields.iter().map(|field| field.expr).chain(init).any(|e| e.can_have_side_effects())
let init_side_effects = match init {
StructTailExpr::Base(init) => init.can_have_side_effects(),
StructTailExpr::DefaultFields(_) | StructTailExpr::None => false,
};
fields.iter().map(|field| field.expr).any(|e| e.can_have_side_effects())
|| init_side_effects
}
ExprKind::Array(args)
@ -1926,20 +1931,52 @@ impl Expr<'_> {
ExprKind::Path(QPath::Resolved(None, path2)),
) => path1.res == path2.res,
(
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val1], None),
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val2], None),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeTo, _),
[val1],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeTo, _),
[val2],
StructTailExpr::None,
),
)
| (
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val1], None),
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val2], None),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeToInclusive, _),
[val1],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeToInclusive, _),
[val2],
StructTailExpr::None,
),
)
| (
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val1], None),
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val2], None),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeFrom, _),
[val1],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeFrom, _),
[val2],
StructTailExpr::None,
),
) => val1.expr.equivalent_for_indexing(val2.expr),
(
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val1, val3], None),
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val2, val4], None),
ExprKind::Struct(
QPath::LangItem(LangItem::Range, _),
[val1, val3],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::Range, _),
[val2, val4],
StructTailExpr::None,
),
) => {
val1.expr.equivalent_for_indexing(val2.expr)
&& val3.expr.equivalent_for_indexing(val4.expr)
@ -2096,7 +2133,7 @@ pub enum ExprKind<'hir> {
///
/// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`,
/// where `base` is the `Option<Expr>`.
Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], Option<&'hir Expr<'hir>>),
Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], StructTailExpr<'hir>),
/// An array literal constructed from one repeated element.
///
@ -2111,6 +2148,19 @@ pub enum ExprKind<'hir> {
Err(rustc_span::ErrorGuaranteed),
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum StructTailExpr<'hir> {
/// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`.
None,
/// A struct expression with a "base", an expression of the same type as the outer struct that
/// will be used to populate any fields not explicitly mentioned: `Foo { ..base }`
Base(&'hir Expr<'hir>),
/// A struct expression with a `..` tail but no "base" expression. The values from the struct
/// fields' default values will be used to populate any fields not explicitly mentioned:
/// `Foo { .. }`.
DefaultFields(Span),
}
/// Represents an optionally `Self`-qualified value/type path or associated extension.
///
/// To resolve the path to a `DefId`, call [`qpath_res`].
@ -3172,6 +3222,7 @@ pub struct FieldDef<'hir> {
pub def_id: LocalDefId,
pub ty: &'hir Ty<'hir>,
pub safety: Safety,
pub default: Option<&'hir AnonConst>,
}
impl FieldDef<'_> {

View file

@ -748,7 +748,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
ExprKind::Struct(ref qpath, fields, ref optional_base) => {
try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span));
walk_list!(visitor, visit_expr_field, fields);
visit_opt!(visitor, visit_expr, optional_base);
match optional_base {
StructTailExpr::Base(base) => try_visit!(visitor.visit_expr(base)),
StructTailExpr::None | StructTailExpr::DefaultFields(_) => {}
}
}
ExprKind::Tup(subexpressions) => {
walk_list!(visitor, visit_expr, subexpressions);
@ -1190,10 +1193,14 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>(
V::Result::output()
}
pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) -> V::Result {
try_visit!(visitor.visit_id(field.hir_id));
try_visit!(visitor.visit_ident(field.ident));
visitor.visit_ty(field.ty)
pub fn walk_field_def<'v, V: Visitor<'v>>(
visitor: &mut V,
FieldDef { hir_id, ident, ty, default, span: _, vis_span: _, def_id: _, safety: _ }: &'v FieldDef<'v>,
) -> V::Result {
try_visit!(visitor.visit_id(*hir_id));
try_visit!(visitor.visit_ident(*ident));
visit_opt!(visitor, visit_anon_const, default);
visitor.visit_ty(*ty)
}
pub fn walk_enum_def<'v, V: Visitor<'v>>(

View file

@ -1021,12 +1021,12 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> {
}
}
fn lower_variant(
tcx: TyCtxt<'_>,
fn lower_variant<'tcx>(
tcx: TyCtxt<'tcx>,
variant_did: Option<LocalDefId>,
ident: Ident,
discr: ty::VariantDiscr,
def: &hir::VariantData<'_>,
def: &hir::VariantData<'tcx>,
adt_kind: ty::AdtKind,
parent_did: LocalDefId,
) -> ty::VariantDef {
@ -1042,6 +1042,7 @@ fn lower_variant(
name: f.ident.name,
vis: tcx.visibility(f.def_id),
safety: f.safety,
value: f.default.map(|v| v.def_id.to_def_id()),
})
.collect();
let recovered = match def {

View file

@ -125,6 +125,12 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
return ty;
}
Node::Field(&hir::FieldDef { default: Some(c), def_id: field_def_id, .. })
if c.hir_id == hir_id =>
{
tcx.type_of(field_def_id).instantiate_identity()
}
_ => Ty::new_error_with_message(
tcx,
span,

View file

@ -1080,22 +1080,36 @@ impl<'a> State<'a> {
&mut self,
qpath: &hir::QPath<'_>,
fields: &[hir::ExprField<'_>],
wth: Option<&hir::Expr<'_>>,
wth: hir::StructTailExpr<'_>,
) {
self.print_qpath(qpath, true);
self.word("{");
self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span);
if let Some(expr) = wth {
self.ibox(INDENT_UNIT);
if !fields.is_empty() {
self.word(",");
self.space();
match wth {
hir::StructTailExpr::Base(expr) => {
self.ibox(INDENT_UNIT);
if !fields.is_empty() {
self.word(",");
self.space();
}
self.word("..");
self.print_expr(expr);
self.end();
}
hir::StructTailExpr::DefaultFields(_) => {
self.ibox(INDENT_UNIT);
if !fields.is_empty() {
self.word(",");
self.space();
}
self.word("..");
self.end();
}
hir::StructTailExpr::None => {
if !fields.is_empty() {
self.word(",");
}
}
self.word("..");
self.print_expr(expr);
self.end();
} else if !fields.is_empty() {
self.word(",");
}
self.word("}");

View file

@ -10,6 +10,12 @@ hir_typeck_address_of_temporary_taken = cannot take address of a temporary
hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but rustc had trouble determining where
.note = we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new
hir_typeck_base_expression_double_dot = base expression required after `..`
hir_typeck_base_expression_double_dot_add_expr = add a base expression here
hir_typeck_base_expression_double_dot_enable_default_field_values =
add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
hir_typeck_base_expression_double_dot_remove = remove the `..` as all the fields are already present
hir_typeck_candidate_trait_note = `{$trait_name}` defines an item `{$item_name}`{$action_or_ty ->
[NONE] {""}
[implement] , perhaps you need to implement it

View file

@ -15,6 +15,47 @@ use rustc_span::{Span, Symbol};
use crate::fluent_generated as fluent;
#[derive(Diagnostic)]
#[diag(hir_typeck_base_expression_double_dot, code = E0797)]
pub(crate) struct BaseExpressionDoubleDot {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub default_field_values: Option<BaseExpressionDoubleDotEnableDefaultFieldValues>,
#[subdiagnostic]
pub add_expr: Option<BaseExpressionDoubleDotAddExpr>,
#[subdiagnostic]
pub remove_dots: Option<BaseExpressionDoubleDotRemove>,
}
#[derive(Subdiagnostic)]
#[suggestion(
hir_typeck_base_expression_double_dot_remove,
code = "",
applicability = "machine-applicable",
style = "verbose"
)]
pub(crate) struct BaseExpressionDoubleDotRemove {
#[primary_span]
pub span: Span,
}
#[derive(Subdiagnostic)]
#[suggestion(
hir_typeck_base_expression_double_dot_add_expr,
code = "/* expr */",
applicability = "has-placeholders",
style = "verbose"
)]
pub(crate) struct BaseExpressionDoubleDotAddExpr {
#[primary_span]
pub span: Span,
}
#[derive(Subdiagnostic)]
#[help(hir_typeck_base_expression_double_dot_enable_default_field_values)]
pub(crate) struct BaseExpressionDoubleDotEnableDefaultFieldValues;
#[derive(Diagnostic)]
#[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)]
pub(crate) struct FieldMultiplySpecifiedInInitializer {

View file

@ -44,10 +44,11 @@ use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectatio
use crate::TupleArgumentsFlag::DontTupleArguments;
use crate::coercion::{CoerceMany, DynamicCoerceMany};
use crate::errors::{
AddressOfTemporaryTaken, FieldMultiplySpecifiedInInitializer,
FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, ReturnLikeStatementKind,
ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, TypeMismatchFruTypo,
YieldExprOutsideOfCoroutine,
AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr,
BaseExpressionDoubleDotEnableDefaultFieldValues, BaseExpressionDoubleDotRemove,
FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition,
ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
};
use crate::{
BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, Needs, cast, fatally_break_rust,
@ -723,7 +724,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.suggest_assoc_method_call(segs);
let e =
self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted");
self.set_tainted_by_errors(e);
Ty::new_error(tcx, e)
}
Res::Def(DefKind::Variant, _) => {
@ -1855,11 +1855,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_expr_struct(
&self,
expr: &hir::Expr<'_>,
expr: &hir::Expr<'tcx>,
expected: Expectation<'tcx>,
qpath: &QPath<'tcx>,
qpath: &'tcx QPath<'tcx>,
fields: &'tcx [hir::ExprField<'tcx>],
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
base_expr: &'tcx hir::StructTailExpr<'tcx>,
) -> Ty<'tcx> {
// Find the relevant variant
let (variant, adt_ty) = match self.check_struct_path(qpath, expr.hir_id) {
@ -1899,7 +1899,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
variant: &'tcx ty::VariantDef,
hir_fields: &'tcx [hir::ExprField<'tcx>],
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
base_expr: &'tcx hir::StructTailExpr<'tcx>,
) {
let tcx = self.tcx;
@ -2023,13 +2023,90 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// the fields with the base_expr. This could cause us to hit errors later
// when certain fields are assumed to exist that in fact do not.
if error_happened {
if let Some(base_expr) = base_expr {
if let hir::StructTailExpr::Base(base_expr) = base_expr {
self.check_expr(base_expr);
}
return;
}
if let Some(base_expr) = base_expr {
if let hir::StructTailExpr::DefaultFields(span) = *base_expr {
let mut missing_mandatory_fields = Vec::new();
let mut missing_optional_fields = Vec::new();
for f in &variant.fields {
let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id);
if let Some(_) = remaining_fields.remove(&ident) {
if f.value.is_none() {
missing_mandatory_fields.push(ident);
} else {
missing_optional_fields.push(ident);
}
}
}
if !self.tcx.features().default_field_values() {
self.dcx().emit_err(BaseExpressionDoubleDot {
span: span.shrink_to_hi(),
// We only mention enabling the feature if this is a nightly rustc *and* the
// expression would make sense with the feature enabled.
default_field_values: if self.tcx.sess.is_nightly_build()
&& missing_mandatory_fields.is_empty()
&& !missing_optional_fields.is_empty()
{
Some(BaseExpressionDoubleDotEnableDefaultFieldValues)
} else {
None
},
add_expr: if !missing_mandatory_fields.is_empty()
|| !missing_optional_fields.is_empty()
{
Some(BaseExpressionDoubleDotAddExpr { span: span.shrink_to_hi() })
} else {
None
},
remove_dots: if missing_mandatory_fields.is_empty()
&& missing_optional_fields.is_empty()
{
Some(BaseExpressionDoubleDotRemove { span })
} else {
None
},
});
return;
}
if !missing_mandatory_fields.is_empty() {
let s = pluralize!(missing_mandatory_fields.len());
let fields: Vec<_> =
missing_mandatory_fields.iter().map(|f| format!("`{f}`")).collect();
let fields = match &fields[..] {
[] => unreachable!(),
[only] => only.to_string(),
[start @ .., last] => format!("{} and {last}", start.join(", ")),
};
self.dcx()
.struct_span_err(
span.shrink_to_hi(),
format!("missing mandatory field{s} {fields}"),
)
.emit();
return;
}
let fru_tys = match adt_ty.kind() {
ty::Adt(adt, args) if adt.is_struct() => variant
.fields
.iter()
.map(|f| self.normalize(span, f.ty(self.tcx, args)))
.collect(),
ty::Adt(adt, args) if adt.is_enum() => variant
.fields
.iter()
.map(|f| self.normalize(span, f.ty(self.tcx, args)))
.collect(),
_ => {
self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { span });
return;
}
};
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys);
} else if let hir::StructTailExpr::Base(base_expr) = base_expr {
// FIXME: We are currently creating two branches here in order to maintain
// consistency. But they should be merged as much as possible.
let fru_tys = if self.tcx.features().type_changing_struct_update() {
@ -2161,12 +2238,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_struct_fields_on_error(
&self,
fields: &'tcx [hir::ExprField<'tcx>],
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
base_expr: &'tcx hir::StructTailExpr<'tcx>,
) {
for field in fields {
self.check_expr(field.expr);
}
if let Some(base) = *base_expr {
if let hir::StructTailExpr::Base(base) = *base_expr {
self.check_expr(base);
}
}

View file

@ -686,7 +686,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
fn walk_struct_expr<'hir>(
&self,
fields: &[hir::ExprField<'_>],
opt_with: &Option<&'hir hir::Expr<'_>>,
opt_with: &hir::StructTailExpr<'hir>,
) -> Result<(), Cx::Error> {
// Consume the expressions supplying values for each field.
for field in fields {
@ -702,8 +702,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
}
let with_expr = match *opt_with {
Some(w) => &*w,
None => {
hir::StructTailExpr::Base(w) => &*w,
hir::StructTailExpr::DefaultFields(_) | hir::StructTailExpr::None => {
return Ok(());
}
};

View file

@ -211,6 +211,10 @@ lint_dangling_pointers_from_temporaries = a dangling pointer will be produced be
.note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
.help = for more information, see <https://doc.rust-lang.org/reference/destructors.html>
lint_default_field_always_invalid_const = default field fails const-evaluation
.label = this field's constant fails const-evaluation, as seen in the previous error
.help = you can skip const-evaluation of default fields by enabling this lint
lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance
.note = a `use rustc_data_structures::fx::{$preferred}` may be necessary

View file

@ -0,0 +1,91 @@
use rustc_hir as hir;
use rustc_middle::lint::LintLevelSource;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_session::lint::Level;
use rustc_session::{declare_lint, declare_lint_pass};
use crate::lints::DefaultFieldAlwaysInvalidConst;
use crate::{LateContext, LateLintPass};
declare_lint! {
/// The `default_field_always_invalid_const` lint checks for structs with
/// default fields const values that will *always* fail to be created.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![feature(default_field_values)]
/// #[deny(default_field_always_invalid_const)]
/// struct Foo {
/// bar: u8 = 130 + 130, // `260` doesn't fit in `u8`
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Without this lint, the error would only happen only during construction
/// of the affected type. For example, given the type above, `Foo { .. }`
/// would always fail to build, but `Foo { bar: 0 }` would be accepted. This
/// lint will catch accidental cases of const values that would fail to
/// compile, but won't detect cases that are only partially evaluated.
pub DEFAULT_FIELD_ALWAYS_INVALID_CONST,
Deny,
"using this default field will always fail to compile"
}
declare_lint_pass!(DefaultFieldAlwaysInvalid => [DEFAULT_FIELD_ALWAYS_INVALID_CONST]);
impl<'tcx> LateLintPass<'tcx> for DefaultFieldAlwaysInvalid {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
let data = match item.kind {
hir::ItemKind::Struct(data, _generics) => data,
_ => return,
};
let hir::VariantData::Struct { fields, recovered: _ } = data else {
return;
};
let (level, source) =
cx.tcx.lint_level_at_node(DEFAULT_FIELD_ALWAYS_INVALID_CONST, item.hir_id());
match level {
Level::Deny | Level::Forbid => {}
Level::Warn | Level::ForceWarn(_) | Level::Expect(_) => {
// We *can't* turn the const eval error into a warning, so we make it a
// warning to not use `#[warn(default_field_always_invalid_const)]`.
let invalid_msg = "lint `default_field_always_invalid_const` can't be warned on";
#[allow(rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic)]
if let LintLevelSource::Node { span, .. } = source {
let mut err = cx.tcx.sess.dcx().struct_span_warn(span, invalid_msg);
err.span_label(
span,
"either `deny` or `allow`, no other lint level is supported for this lint",
);
err.emit();
} else {
cx.tcx.sess.dcx().warn(invalid_msg);
}
}
Level::Allow => {
// We don't even look at the fields.
return;
}
}
for field in fields {
if let Some(c) = field.default
&& let Some(_ty) = cx.tcx.type_of(c.def_id).no_bound_vars()
&& let Err(ErrorHandled::Reported(_, _)) = cx.tcx.const_eval_poly(c.def_id.into())
{
// We use the item's hir id because the const's hir id might resolve inside of a
// foreign macro, meaning the lint won't trigger.
cx.tcx.emit_node_span_lint(
DEFAULT_FIELD_ALWAYS_INVALID_CONST,
item.hir_id(),
field.span,
DefaultFieldAlwaysInvalidConst { span: field.span, help: () },
);
}
}
}
}

View file

@ -41,6 +41,7 @@ mod async_fn_in_trait;
pub mod builtin;
mod context;
mod dangling;
mod default_field_always_invalid;
mod deref_into_dyn_supertrait;
mod drop_forget_useless;
mod early;
@ -85,6 +86,7 @@ use async_closures::AsyncClosureUsage;
use async_fn_in_trait::AsyncFnInTrait;
use builtin::*;
use dangling::*;
use default_field_always_invalid::*;
use deref_into_dyn_supertrait::*;
use drop_forget_useless::*;
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
@ -193,6 +195,7 @@ late_lint_methods!(
DropForgetUseless: DropForgetUseless,
ImproperCTypesDeclarations: ImproperCTypesDeclarations,
ImproperCTypesDefinitions: ImproperCTypesDefinitions,
DefaultFieldAlwaysInvalid: DefaultFieldAlwaysInvalid,
InvalidFromUtf8: InvalidFromUtf8,
VariantSizeDifferences: VariantSizeDifferences,
PathStatements: PathStatements,

View file

@ -730,6 +730,15 @@ pub(crate) struct UndroppedManuallyDropsSuggestion {
pub end_span: Span,
}
#[derive(LintDiagnostic)]
#[diag(lint_default_field_always_invalid_const)]
pub(crate) struct DefaultFieldAlwaysInvalidConst {
#[label]
pub span: Span,
#[help]
pub help: (),
}
// invalid_from_utf8.rs
#[derive(LintDiagnostic)]
pub(crate) enum InvalidFromUtf8Diag {

View file

@ -1104,6 +1104,7 @@ impl<'a> CrateMetadataRef<'a> {
name: self.item_name(did.index),
vis: self.get_visibility(did.index),
safety: self.get_safety(did.index),
value: None,
})
.collect(),
adt_kind,

View file

@ -158,8 +158,21 @@ pub struct AdtExpr<'tcx> {
pub user_ty: UserTy<'tcx>,
pub fields: Box<[FieldExpr]>,
/// The base, e.g. `Foo {x: 1, .. base}`.
pub base: Option<FruInfo<'tcx>>,
/// The base, e.g. `Foo {x: 1, ..base}`.
pub base: AdtExprBase<'tcx>,
}
#[derive(Clone, Debug, HashStable)]
pub enum AdtExprBase<'tcx> {
/// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`.
None,
/// A struct expression with a "base", an expression of the same type as the outer struct that
/// will be used to populate any fields not explicitly mentioned: `Foo { ..base }`
Base(FruInfo<'tcx>),
/// A struct expression with a `..` tail but no "base" expression. The values from the struct
/// fields' default values will be used to populate any fields not explicitly mentioned:
/// `Foo { .. }`.
DefaultFields(Box<[Ty<'tcx>]>),
}
#[derive(Clone, Debug, HashStable)]

View file

@ -2,6 +2,7 @@ use super::{
AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat,
PatKind, Stmt, StmtKind, Thir,
};
use crate::thir::AdtExprBase;
pub trait Visitor<'thir, 'tcx: 'thir>: Sized {
fn thir(&self) -> &'thir Thir<'tcx>;
@ -127,7 +128,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
for field in &**fields {
visitor.visit_expr(&visitor.thir()[field.expr]);
}
if let Some(base) = base {
if let AdtExprBase::Base(base) = base {
visitor.visit_expr(&visitor.thir()[base.base]);
}
}

View file

@ -259,10 +259,10 @@ impl Into<DataTypeKind> for AdtKind {
}
}
impl AdtDefData {
impl<'tcx> AdtDefData {
/// Creates a new `AdtDefData`.
pub(super) fn new(
tcx: TyCtxt<'_>,
tcx: TyCtxt<'tcx>,
did: DefId,
kind: AdtKind,
variants: IndexVec<VariantIdx, VariantDef>,

View file

@ -1364,6 +1364,7 @@ pub struct FieldDef {
pub name: Symbol,
pub vis: Visibility<DefId>,
pub safety: hir::Safety,
pub value: Option<DefId>,
}
impl PartialEq for FieldDef {
@ -1376,9 +1377,9 @@ impl PartialEq for FieldDef {
// of `FieldDef` changes, a compile-error will be produced, reminding
// us to revisit this assumption.
let Self { did: lhs_did, name: _, vis: _, safety: _ } = &self;
let Self { did: lhs_did, name: _, vis: _, safety: _, value: _ } = &self;
let Self { did: rhs_did, name: _, vis: _, safety: _ } = other;
let Self { did: rhs_did, name: _, vis: _, safety: _, value: _ } = other;
let res = lhs_did == rhs_did;
@ -1405,7 +1406,7 @@ impl Hash for FieldDef {
// of `FieldDef` changes, a compile-error will be produced, reminding
// us to revisit this assumption.
let Self { did, name: _, vis: _, safety: _ } = &self;
let Self { did, name: _, vis: _, safety: _, value: _ } = &self;
did.hash(s)
}

View file

@ -283,7 +283,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
fields.iter().map(|e| self.parse_operand(*e)).collect::<Result<_, _>>()?
))
},
ExprKind::Adt(box AdtExpr{ adt_def, variant_index, args, fields, .. }) => {
ExprKind::Adt(box AdtExpr { adt_def, variant_index, args, fields, .. }) => {
let is_union = adt_def.is_union();
let active_field_index = is_union.then(|| fields[0].name);

View file

@ -1,7 +1,5 @@
//! See docs in build/expr/mod.rs
use std::iter;
use rustc_ast::{AsmMacro, InlineAsmOptions};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
@ -344,25 +342,51 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
})
.collect();
let field_names = adt_def.variant(variant_index).fields.indices();
let variant = adt_def.variant(variant_index);
let field_names = variant.fields.indices();
let fields = if let Some(FruInfo { base, field_types }) = base {
let place_builder = unpack!(block = this.as_place_builder(block, *base));
let fields = match base {
AdtExprBase::None => {
field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
}
AdtExprBase::Base(FruInfo { base, field_types }) => {
let place_builder = unpack!(block = this.as_place_builder(block, *base));
// MIR does not natively support FRU, so for each
// base-supplied field, generate an operand that
// reads it from the base.
iter::zip(field_names, &**field_types)
.map(|(n, ty)| match fields_map.get(&n) {
Some(v) => v.clone(),
None => {
let place = place_builder.clone_project(PlaceElem::Field(n, *ty));
this.consume_by_copy_or_move(place.to_place(this))
}
})
.collect()
} else {
field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
// MIR does not natively support FRU, so for each
// base-supplied field, generate an operand that
// reads it from the base.
itertools::zip_eq(field_names, &**field_types)
.map(|(n, ty)| match fields_map.get(&n) {
Some(v) => v.clone(),
None => {
let place =
place_builder.clone_project(PlaceElem::Field(n, *ty));
this.consume_by_copy_or_move(place.to_place(this))
}
})
.collect()
}
AdtExprBase::DefaultFields(field_types) => {
itertools::zip_eq(field_names, &**field_types)
.map(|(n, ty)| match fields_map.get(&n) {
Some(v) => v.clone(),
None => match variant.fields[n].value {
Some(def) => {
let value = Const::from_unevaluated(this.tcx, def)
.instantiate(this.tcx, args);
this.literal_operand(expr_span, value)
}
None => {
let name = variant.fields[n].name;
span_bug!(
expr_span,
"missing mandatory field `{name}` of type `{ty}`",
);
}
},
})
.collect()
}
};
let inferred_ty = expr.ty;

View file

@ -222,7 +222,7 @@ impl<'tcx> Cx<'tcx> {
args,
fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]),
user_ty: None,
base: None,
base: AdtExprBase::None,
}));
debug!(?kind);
@ -464,7 +464,7 @@ impl<'tcx> Cx<'tcx> {
variant_index: index,
fields: field_refs,
user_ty,
base: None,
base: AdtExprBase::None,
}))
} else {
ExprKind::Call {
@ -594,20 +594,36 @@ impl<'tcx> Cx<'tcx> {
args,
user_ty,
fields: self.field_refs(fields),
base: base.map(|base| FruInfo {
base: self.mirror_expr(base),
field_types: self.typeck_results().fru_field_types()[expr.hir_id]
.iter()
.copied()
.collect(),
}),
base: match base {
hir::StructTailExpr::Base(base) => AdtExprBase::Base(FruInfo {
base: self.mirror_expr(base),
field_types: self.typeck_results().fru_field_types()
[expr.hir_id]
.iter()
.copied()
.collect(),
}),
hir::StructTailExpr::DefaultFields(_) => {
AdtExprBase::DefaultFields(
self.typeck_results().fru_field_types()[expr.hir_id]
.iter()
.copied()
.collect(),
)
}
hir::StructTailExpr::None => AdtExprBase::None,
},
}))
}
AdtKind::Enum => {
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
match res {
Res::Def(DefKind::Variant, variant_id) => {
assert!(base.is_none());
assert!(matches!(
base,
hir::StructTailExpr::None
| hir::StructTailExpr::DefaultFields(_)
));
let index = adt.variant_index_with_id(variant_id);
let user_provided_types =
@ -621,7 +637,21 @@ impl<'tcx> Cx<'tcx> {
args,
user_ty,
fields: self.field_refs(fields),
base: None,
base: match base {
hir::StructTailExpr::DefaultFields(_) => {
AdtExprBase::DefaultFields(
self.typeck_results().fru_field_types()
[expr.hir_id]
.iter()
.copied()
.collect(),
)
}
hir::StructTailExpr::Base(base) => {
span_bug!(base.span, "unexpected res: {:?}", res);
}
hir::StructTailExpr::None => AdtExprBase::None,
},
}))
}
_ => {
@ -1029,7 +1059,7 @@ impl<'tcx> Cx<'tcx> {
args,
user_ty,
fields: Box::new([]),
base: None,
base: AdtExprBase::None,
})),
_ => bug!("unexpected ty: {:?}", ty),
}

View file

@ -566,11 +566,17 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
self.print_expr(field_expr.expr, depth_lvl + 2);
}
if let Some(ref base) = adt_expr.base {
print_indented!(self, "base:", depth_lvl + 1);
self.print_fru_info(base, depth_lvl + 2);
} else {
print_indented!(self, "base: None", depth_lvl + 1);
match adt_expr.base {
AdtExprBase::Base(ref base) => {
print_indented!(self, "base:", depth_lvl + 1);
self.print_fru_info(base, depth_lvl + 2);
}
AdtExprBase::DefaultFields(_) => {
print_indented!(self, "base: {{ defaulted fields }}", depth_lvl + 1);
}
AdtExprBase::None => {
print_indented!(self, "base: None", depth_lvl + 1);
}
}
}

View file

@ -169,9 +169,6 @@ parse_enum_struct_mutually_exclusive = `enum` and `struct` are mutually exclusiv
parse_eq_field_init = expected `:`, found `=`
.suggestion = replace equals symbol with a colon
parse_equals_struct_default = default values on `struct` fields aren't supported
.suggestion = remove this unsupported default value
parse_escape_only_char = {$byte ->
[true] byte
*[false] character

View file

@ -3066,14 +3066,6 @@ pub(crate) struct SingleColonStructType {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_equals_struct_default)]
pub(crate) struct EqualsStructDefault {
#[primary_span]
#[suggestion(code = "", applicability = "machine-applicable", style = "verbose")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_macro_rules_missing_bang)]
pub(crate) struct MacroRulesMissingBang {

View file

@ -3533,7 +3533,7 @@ impl<'a> Parser<'a> {
let exp_span = self.prev_token.span;
// We permit `.. }` on the left-hand side of a destructuring assignment.
if self.check(&token::CloseDelim(close_delim)) {
base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi());
base = ast::StructRest::Rest(self.prev_token.span);
break;
}
match self.parse_expr() {

View file

@ -1845,6 +1845,7 @@ impl<'a> Parser<'a> {
ident: None,
id: DUMMY_NODE_ID,
ty,
default: None,
attrs,
is_placeholder: false,
},
@ -2024,12 +2025,15 @@ impl<'a> Parser<'a> {
if self.token == token::Colon && self.look_ahead(1, |t| *t != token::Colon) {
self.dcx().emit_err(errors::SingleColonStructType { span: self.token.span });
}
if self.token == token::Eq {
let default = if self.token == token::Eq {
self.bump();
let const_expr = self.parse_expr_anon_const()?;
let sp = ty.span.shrink_to_hi().to(const_expr.value.span);
self.dcx().emit_err(errors::EqualsStructDefault { span: sp });
}
self.psess.gated_spans.gate(sym::default_field_values, sp);
Some(const_expr)
} else {
None
};
Ok(FieldDef {
span: lo.to(self.prev_token.span),
ident: Some(name),
@ -2037,6 +2041,7 @@ impl<'a> Parser<'a> {
safety,
id: DUMMY_NODE_ID,
ty,
default,
attrs,
is_placeholder: false,
})

View file

@ -1007,7 +1007,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
hir::ExprKind::Array(exprs) => self.propagate_through_exprs(exprs, succ),
hir::ExprKind::Struct(_, fields, ref with_expr) => {
let succ = self.propagate_through_opt_expr(with_expr.as_deref(), succ);
let succ = match with_expr {
hir::StructTailExpr::Base(base) => {
self.propagate_through_opt_expr(Some(base), succ)
}
hir::StructTailExpr::None | hir::StructTailExpr::DefaultFields(_) => succ,
};
fields
.iter()
.rev()

View file

@ -947,6 +947,25 @@ impl<'tcx> NamePrivacyVisitor<'tcx> {
});
}
}
fn check_expanded_fields(
&mut self,
adt: ty::AdtDef<'tcx>,
variant: &'tcx ty::VariantDef,
fields: &[hir::ExprField<'tcx>],
hir_id: hir::HirId,
span: Span,
) {
for (vf_index, variant_field) in variant.fields.iter_enumerated() {
let field =
fields.iter().find(|f| self.typeck_results().field_index(f.hir_id) == vf_index);
let (hir_id, use_ctxt, span) = match field {
Some(field) => (field.hir_id, field.ident.span, field.span),
None => (hir_id, span, span),
};
self.check_field(hir_id, use_ctxt, span, adt, variant_field, true);
}
}
}
impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
@ -966,25 +985,29 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap();
let variant = adt.variant_of_res(res);
if let Some(base) = *base {
// If the expression uses FRU we need to make sure all the unmentioned fields
// are checked for privacy (RFC 736). Rather than computing the set of
// unmentioned fields, just check them all.
for (vf_index, variant_field) in variant.fields.iter_enumerated() {
let field = fields
.iter()
.find(|f| self.typeck_results().field_index(f.hir_id) == vf_index);
let (hir_id, use_ctxt, span) = match field {
Some(field) => (field.hir_id, field.ident.span, field.span),
None => (base.hir_id, base.span, base.span),
};
self.check_field(hir_id, use_ctxt, span, adt, variant_field, true);
match *base {
hir::StructTailExpr::Base(base) => {
// If the expression uses FRU we need to make sure all the unmentioned fields
// are checked for privacy (RFC 736). Rather than computing the set of
// unmentioned fields, just check them all.
self.check_expanded_fields(adt, variant, fields, base.hir_id, base.span);
}
} else {
for field in fields {
let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span);
let index = self.typeck_results().field_index(field.hir_id);
self.check_field(hir_id, use_ctxt, span, adt, &variant.fields[index], false);
hir::StructTailExpr::DefaultFields(span) => {
self.check_expanded_fields(adt, variant, fields, expr.hir_id, span);
}
hir::StructTailExpr::None => {
for field in fields {
let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span);
let index = self.typeck_results().field_index(field.hir_id);
self.check_field(
hir_id,
use_ctxt,
span,
adt,
&variant.fields[index],
false,
);
}
}
}
}

View file

@ -13,7 +13,9 @@ use std::collections::hash_map::Entry;
use std::mem::{replace, swap, take};
use rustc_ast::ptr::P;
use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, visit_opt, walk_list};
use rustc_ast::visit::{
AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list,
};
use rustc_ast::*;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_errors::codes::*;
@ -749,8 +751,8 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
self.resolve_block(block);
self.parent_scope.macro_rules = old_macro_rules;
}
fn visit_anon_const(&mut self, _constant: &'ast AnonConst) {
bug!("encountered anon const without a manual call to `resolve_anon_const`");
fn visit_anon_const(&mut self, constant: &'ast AnonConst) {
bug!("encountered anon const without a manual call to `resolve_anon_const` {constant:#?}");
}
fn visit_expr(&mut self, expr: &'ast Expr) {
self.resolve_expr(expr, None);
@ -1346,7 +1348,24 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
fn visit_field_def(&mut self, f: &'ast FieldDef) {
self.resolve_doc_links(&f.attrs, MaybeExported::Ok(f.id));
visit::walk_field_def(self, f)
let FieldDef {
attrs,
id: _,
span: _,
vis,
ident,
ty,
is_placeholder: _,
default,
safety: _,
} = f;
walk_list!(self, visit_attribute, attrs);
try_visit!(self.visit_vis(vis));
visit_opt!(self, visit_ident, ident);
try_visit!(self.visit_ty(ty));
if let Some(v) = &default {
self.resolve_anon_const(v, AnonConstKind::ConstArg(IsRepeatExpr::No));
}
}
}

View file

@ -728,6 +728,7 @@ symbols! {
declare_lint_pass,
decode,
default_alloc_error_handler,
default_field_values,
default_fn,
default_lib_allocator,
default_method_body_is_const,

View file

@ -5,7 +5,7 @@ use clippy_utils::{contains_name, get_parent_expr, in_automatically_derived, is_
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_middle::ty::print::with_forced_trimmed_paths;
@ -285,7 +285,7 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
/// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }`
fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
if let Some(parent) = get_parent_expr(cx, expr)
&& let ExprKind::Struct(_, _, Some(base)) = parent.kind
&& let ExprKind::Struct(_, _, StructTailExpr::Base(base)) = parent.kind
{
base.hir_id == expr.hir_id
} else {

View file

@ -4,7 +4,7 @@ use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt};
use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind};
use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
@ -197,7 +197,7 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> {
}
// Visit base with no bound.
if let Some(base) = base {
if let StructTailExpr::Base(base) = base {
self.ty_bounds.push(ExplicitTyBound(false));
self.visit_expr(base);
self.ty_bounds.pop();

View file

@ -3,7 +3,7 @@ use clippy_utils::fulfill_or_allowed;
use clippy_utils::source::snippet;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::{self as hir, ExprKind};
use rustc_hir::{self as hir, ExprKind, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::Symbol;
@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
}
fields_snippet.push_str(&last_ident.to_string());
let base_snippet = if let Some(base) = base {
let base_snippet = if let StructTailExpr::Base(base) = base {
format!(", ..{}", snippet(cx, base.span, ".."))
} else {
String::new()

View file

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, ExprKind};
use rustc_hir::{Expr, ExprKind, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::SyntaxContext;
@ -43,7 +43,7 @@ declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
impl<'tcx> LateLintPass<'tcx> for NumberedFields {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if let ExprKind::Struct(path, fields @ [field, ..], None) = e.kind
if let ExprKind::Struct(path, fields @ [field, ..], StructTailExpr::None) = e.kind
// If the first character of any field is a digit it has to be a tuple.
&& field.ident.as_str().as_bytes().first().is_some_and(u8::is_ascii_digit)
// Type aliases can't be used as functions.

View file

@ -5,7 +5,7 @@ use clippy_utils::higher::ForLoop;
use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind, StructTailExpr};
use rustc_lint::LateContext;
use rustc_span::{Span, sym};
use std::iter::once;
@ -164,7 +164,7 @@ fn never_loop_expr<'tcx>(
},
ExprKind::Struct(_, fields, base) => {
let fields = never_loop_expr_all(cx, fields.iter().map(|f| f.expr), local_labels, main_loop_id);
if let Some(base) = base {
if let StructTailExpr::Base(base) = base {
combine_seq(fields, || never_loop_expr(cx, base, local_labels, main_loop_id))
} else {
fields

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint;
use rustc_hir::{Expr, ExprKind};
use rustc_hir::{Expr, ExprKind, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::declare_lint_pass;
@ -51,7 +51,7 @@ declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]);
impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Struct(_, fields, Some(base)) = expr.kind {
if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind {
let ty = cx.typeck_results().expr_ty(expr);
if let ty::Adt(def, _) = ty.kind() {
if fields.len() == def.non_enum_variant().fields.len()

View file

@ -8,7 +8,7 @@ use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{
BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, Stmt, StmtKind,
UnsafeSource, is_range_literal,
UnsafeSource, StructTailExpr, is_range_literal,
};
use rustc_infer::infer::TyCtxtInferExt as _;
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -238,7 +238,10 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
ExprKind::Struct(_, fields, ref base) => {
!has_drop(cx, cx.typeck_results().expr_ty(expr))
&& fields.iter().all(|field| has_no_effect(cx, field.expr))
&& base.as_ref().is_none_or(|base| has_no_effect(cx, base))
&& match &base {
StructTailExpr::None | StructTailExpr::DefaultFields(_) => true,
StructTailExpr::Base(base) => has_no_effect(cx, base),
}
},
ExprKind::Call(callee, args) => {
if let ExprKind::Path(ref qpath) = callee.kind {
@ -342,6 +345,10 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
None
} else {
let base = match base {
StructTailExpr::Base(base) => Some(base),
StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
};
Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
}
},

View file

@ -6,7 +6,7 @@ use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::implements_trait;
use rustc_ast::{LitIntType, LitKind, UintTy};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, QPath};
use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use std::fmt::{self, Display, Formatter};
@ -86,7 +86,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
return;
};
let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], None) = inner_expr.kind else {
let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], StructTailExpr::None) = inner_expr.kind else {
return;
};

View file

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_copy;
use clippy_utils::{get_parent_expr, path_to_local};
use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp};
use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
@ -59,15 +59,15 @@ impl LateLintPass<'_> for UnnecessaryStruct {
let field_path = same_path_in_all_fields(cx, expr, fields);
let sugg = match (field_path, base) {
(Some(&path), None) => {
(Some(&path), StructTailExpr::None | StructTailExpr::DefaultFields(_)) => {
// all fields match, no base given
path.span
},
(Some(path), Some(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => {
(Some(path), StructTailExpr::Base(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => {
// all fields match, has base: ensure that the path of the base matches
base.span
},
(None, Some(base)) if fields.is_empty() && base_is_suitable(cx, expr, base) => {
(None, StructTailExpr::Base(base)) if fields.is_empty() && base_is_suitable(cx, expr, base) => {
// just the base, no explicit fields
base.span
},

View file

@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{
self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind,
ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind,
ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, StructTailExpr,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::declare_lint_pass;
@ -598,7 +598,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
},
ExprKind::Struct(qpath, fields, base) => {
bind!(self, qpath, fields);
opt_bind!(self, base);
let base = OptionPat::new(match base {
StructTailExpr::Base(base) => Some(self.bind("base", base)),
StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
});
kind!("Struct({qpath}, {fields}, {base})");
self.qpath(qpath);
self.slice(fields, |field| {

View file

@ -8,7 +8,7 @@ use crate::ty::is_type_diagnostic_item;
use rustc_ast::ast;
use rustc_hir as hir;
use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath};
use rustc_hir::{Arm, Block, Expr, ExprKind, StructTailExpr, HirId, LoopSource, MatchSource, Node, Pat, QPath};
use rustc_lint::LateContext;
use rustc_span::{Span, sym, symbol};
@ -236,7 +236,7 @@ impl<'a> Range<'a> {
limits: ast::RangeLimits::Closed,
})
},
ExprKind::Struct(path, fields, None) => match (path, fields) {
ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) {
(QPath::LangItem(hir::LangItem::RangeFull, ..), []) => Some(Range {
start: None,
end: None,

View file

@ -10,7 +10,7 @@ use rustc_hir::{
AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr,
ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitBoundModifiers, Ty,
TyKind,
TyKind, StructTailExpr,
};
use rustc_lexer::{TokenKind, tokenize};
use rustc_lint::LateContext;
@ -380,7 +380,12 @@ impl HirEqInterExpr<'_, '_, '_> {
(ExprKind::Ret(l), ExprKind::Ret(r)) => both(l.as_ref(), r.as_ref(), |l, r| self.eq_expr(l, r)),
(&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
self.eq_qpath(l_path, r_path)
&& both(lo.as_ref(), ro.as_ref(), |l, r| self.eq_expr(l, r))
&& match (lo, ro) {
(StructTailExpr::Base(l),StructTailExpr::Base(r)) => self.eq_expr(l, r),
(StructTailExpr::None, StructTailExpr::None) => true,
(StructTailExpr::DefaultFields(_), StructTailExpr::DefaultFields(_)) => true,
_ => false,
}
&& over(lf, rf, |l, r| self.eq_expr_field(l, r))
},
(&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
@ -1017,7 +1022,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(f.expr);
}
if let Some(e) = *expr {
if let StructTailExpr::Base(e) = *expr {
self.hash_expr(e);
}
},

View file

@ -7,7 +7,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr};
use rustc_hir::{
AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath,
Safety, Stmt, UnOp, UnsafeSource,
Safety, Stmt, UnOp, UnsafeSource, StructTailExpr,
};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
@ -663,7 +663,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
for field in fields {
helper(typeck, true, field.expr, f)?;
}
if let Some(default) = default {
if let StructTailExpr::Base(default) = default {
helper(typeck, false, default, f)?;
}
},

View file

@ -12,17 +12,6 @@ error: functional record updates are not allowed in destructuring assignments
LL | Struct { a, ..d } = Struct { a: 1, b: 2 };
| ^ help: consider removing the trailing pattern
error[E0797]: base expression required after `..`
--> $DIR/struct_destructure_fail.rs:15:19
|
LL | Struct { a, .. };
| ^
|
help: add a base expression here
|
LL | Struct { a, ../* expr */ };
| ++++++++++
error[E0026]: struct `Struct` does not have a field named `c`
--> $DIR/struct_destructure_fail.rs:10:20
|
@ -48,6 +37,17 @@ help: or always ignore missing fields here
LL | Struct { a, .. } = Struct { a: 1, b: 2 };
| ~~~~~~
error[E0797]: base expression required after `..`
--> $DIR/struct_destructure_fail.rs:15:19
|
LL | Struct { a, .. };
| ^
|
help: add a base expression here
|
LL | Struct { a, ../* expr */ };
| ++++++++++
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0026, E0027, E0797.

View file

@ -0,0 +1,106 @@
#![feature(generic_const_exprs)]
#![allow(unused_variables, dead_code, incomplete_features)]
pub struct S;
#[derive(Default)]
pub struct Foo {
pub bar: S = S, //~ ERROR default values on `struct` fields aren't supported
pub baz: i32 = 42 + 3, //~ ERROR default values on `struct` fields aren't supported
}
#[derive(Default)]
pub enum Bar {
#[default]
Foo { //~ ERROR the `#[default]` attribute may only be used on unit enum variants
bar: S = S, //~ ERROR default values on `struct` fields aren't supported
baz: i32 = 42 + 3, //~ ERROR default values on `struct` fields aren't supported
}
}
#[derive(Default)]
pub struct Qux<A, const C: i32> {
bar: S = Qux::<A, C>::S, //~ ERROR default values on `struct` fields aren't supported
baz: i32 = foo(), //~ ERROR default values on `struct` fields aren't supported
bat: i32 = <Qux<A, C> as T>::K, //~ ERROR default values on `struct` fields aren't supported
bay: i32 = C, //~ ERROR default values on `struct` fields aren't supported
bak: Vec<A> = Vec::new(), //~ ERROR default values on `struct` fields aren't supported
}
impl<A, const C: i32> Qux<A, C> {
const S: S = S;
}
trait T {
const K: i32;
}
impl<A, const C: i32> T for Qux<A, C> {
const K: i32 = 2;
}
const fn foo() -> i32 {
42
}
#[derive(Default)]
pub struct Opt {
mandatory: Option<()>,
optional: () = (), //~ ERROR default values on `struct` fields aren't supported
}
#[derive(Default)]
pub enum OptEnum {
#[default]
Variant { //~ ERROR the `#[default]` attribute may only be used on unit enum variants
mandatory: Option<()>,
optional: () = (), //~ ERROR default values on `struct` fields aren't supported
}
}
fn main () {
let x = Foo { .. }; //~ ERROR base expression required after `..`
let y = Foo::default();
let z = Foo { baz: 1, .. }; //~ ERROR base expression required after `..`
assert_eq!(45, x.baz);
assert_eq!(45, y.baz);
assert_eq!(1, z.baz);
let x = Bar::Foo { .. }; //~ ERROR base expression required after `..`
let y = Bar::default();
let z = Bar::Foo { baz: 1, .. }; //~ ERROR base expression required after `..`
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, x));
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, y));
assert!(matches!(Bar::Foo { bar: S, baz: 1 }, z));
let x = Qux::<i32, 4> { .. }; //~ ERROR base expression required after `..`
assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, bay: 4, .. }, x));
//~^ ERROR base expression required after `..`
assert!(x.bak.is_empty());
let y = Opt { mandatory: None, .. };
//~^ ERROR base expression required after `..`
assert!(matches!(Opt::default(), y));
let z = Opt::default();
assert!(matches!(Opt { mandatory: None, .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(Opt { .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(Opt { optional: (), .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(Opt { optional: (), mandatory: None, .. }, z));
//~^ ERROR base expression required after `..`
let y = OptEnum::Variant { mandatory: None, .. };
//~^ ERROR base expression required after `..`
assert!(matches!(OptEnum::default(), y));
let z = OptEnum::default();
assert!(matches!(OptEnum::Variant { mandatory: None, .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(OptEnum::Variant { .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(OptEnum::Variant { optional: (), .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }, z));
//~^ ERROR base expression required after `..`
}

View file

@ -0,0 +1,318 @@
error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/feature-gate-default-field-values.rs:15:5
|
LL | Foo {
| ^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/feature-gate-default-field-values.rs:55:5
|
LL | Variant {
| ^^^^^^^
|
= help: consider a manual implementation of `Default`
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:8:15
|
LL | pub bar: S = S,
| ^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:9:17
|
LL | pub baz: i32 = 42 + 3,
| ^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:16:15
|
LL | bar: S = S,
| ^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:17:17
|
LL | baz: i32 = 42 + 3,
| ^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:23:11
|
LL | bar: S = Qux::<A, C>::S,
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:24:13
|
LL | baz: i32 = foo(),
| ^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:25:13
|
LL | bat: i32 = <Qux<A, C> as T>::K,
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:26:13
|
LL | bay: i32 = C,
| ^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:27:16
|
LL | bak: Vec<A> = Vec::new(),
| ^^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:49:17
|
LL | optional: () = (),
| ^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:57:21
|
LL | optional: () = (),
| ^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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
|
LL | let x = Foo { .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let x = Foo { ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:64:29
|
LL | let z = Foo { baz: 1, .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let z = Foo { baz: 1, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:70:26
|
LL | let x = Bar::Foo { .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let x = Bar::Foo { ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:72:34
|
LL | let z = Bar::Foo { baz: 1, .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let z = Bar::Foo { baz: 1, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:78:31
|
LL | let x = Qux::<i32, 4> { .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let x = Qux::<i32, 4> { ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:79:73
|
LL | assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, bay: 4, .. }, x));
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, bay: 4, ../* expr */ }, x));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:82:38
|
LL | let y = Opt { mandatory: None, .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let y = Opt { mandatory: None, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:86:47
|
LL | assert!(matches!(Opt { mandatory: None, .. }, z));
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | assert!(matches!(Opt { mandatory: None, ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:88:30
|
LL | assert!(matches!(Opt { .. }, z));
| ^
|
help: add a base expression here
|
LL | assert!(matches!(Opt { ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:90:44
|
LL | assert!(matches!(Opt { optional: (), .. }, z));
| ^
|
help: add a base expression here
|
LL | assert!(matches!(Opt { optional: (), ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:92:61
|
LL | assert!(matches!(Opt { optional: (), mandatory: None, .. }, z));
| ^
|
help: remove the `..` as all the fields are already present
|
LL - assert!(matches!(Opt { optional: (), mandatory: None, .. }, z));
LL + assert!(matches!(Opt { optional: (), mandatory: None, }, z));
|
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:94:51
|
LL | let y = OptEnum::Variant { mandatory: None, .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let y = OptEnum::Variant { mandatory: None, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:98:60
|
LL | assert!(matches!(OptEnum::Variant { mandatory: None, .. }, z));
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | assert!(matches!(OptEnum::Variant { mandatory: None, ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:100:43
|
LL | assert!(matches!(OptEnum::Variant { .. }, z));
| ^
|
help: add a base expression here
|
LL | assert!(matches!(OptEnum::Variant { ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:102:57
|
LL | assert!(matches!(OptEnum::Variant { optional: (), .. }, z));
| ^
|
help: add a base expression here
|
LL | assert!(matches!(OptEnum::Variant { optional: (), ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:104:74
|
LL | assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }, z));
| ^
|
help: remove the `..` as all the fields are already present
|
LL - assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }, z));
LL + assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, }, z));
|
error: aborting due to 29 previous errors
Some errors have detailed explanations: E0658, E0797.
For more information about an error, try `rustc --explain E0658`.

View file

@ -1,35 +0,0 @@
//@ run-rustfix
#![allow(dead_code)]
enum E {
A,
}
struct S {
field1: i32, //~ ERROR default values on `struct` fields aren't supported
field2: E, //~ ERROR default values on `struct` fields aren't supported
field3: i32, //~ ERROR default values on `struct` fields aren't supported
field4: i32, //~ ERROR default values on `struct` fields aren't supported
field5: E, //~ ERROR default values on `struct` fields aren't supported
field6: E, //~ ERROR default values on `struct` fields aren't supported
}
struct S1 {
field1: i32, //~ ERROR expected `,`, or `}`, found `field2`
field2: E, //~ ERROR expected `,`, or `}`, found `field3`
field3: i32, //~ ERROR default values on `struct` fields aren't supported
field4: i32, //~ ERROR default values on `struct` fields aren't supported
field5: E, //~ ERROR default values on `struct` fields aren't supported
field6: E, //~ ERROR default values on `struct` fields aren't supported
}
struct S2 {
field1 : i32, //~ ERROR expected `:`, found `=`
field2: E, //~ ERROR expected `:`, found `;`
}
const fn foo(_: i32) -> E {
E::A
}
fn main() {}

View file

@ -1,4 +1,3 @@
//@ run-rustfix
#![allow(dead_code)]
enum E {

View file

@ -1,137 +1,17 @@
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:9:16
|
LL | field1: i32 = 42,
| ^^^^^
|
help: remove this unsupported default value
|
LL - field1: i32 = 42,
LL + field1: i32,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:10:14
|
LL | field2: E = E::A,
| ^^^^^^^
|
help: remove this unsupported default value
|
LL - field2: E = E::A,
LL + field2: E,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:11:16
|
LL | field3: i32 = 1 + 2,
| ^^^^^^^^
|
help: remove this unsupported default value
|
LL - field3: i32 = 1 + 2,
LL + field3: i32,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:12:16
|
LL | field4: i32 = { 1 + 2 },
| ^^^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field4: i32 = { 1 + 2 },
LL + field4: i32,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:13:14
|
LL | field5: E = foo(42),
| ^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field5: E = foo(42),
LL + field5: E,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:14:14
|
LL | field6: E = { foo(42) },
| ^^^^^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field6: E = { foo(42) },
LL + field6: E,
|
error: expected `,`, or `}`, found `field2`
--> $DIR/struct-default-values-and-missing-field-separator.rs:18:16
--> $DIR/struct-default-values-and-missing-field-separator.rs:17:16
|
LL | field1: i32
| ^ help: try adding a comma: `,`
error: expected `,`, or `}`, found `field3`
--> $DIR/struct-default-values-and-missing-field-separator.rs:19:14
--> $DIR/struct-default-values-and-missing-field-separator.rs:18:14
|
LL | field2: E
| ^ help: try adding a comma: `,`
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:20:16
|
LL | field3: i32 = 1 + 2,
| ^^^^^^^^
|
help: remove this unsupported default value
|
LL - field3: i32 = 1 + 2,
LL + field3: i32,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:21:16
|
LL | field4: i32 = { 1 + 2 },
| ^^^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field4: i32 = { 1 + 2 },
LL + field4: i32,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:22:14
|
LL | field5: E = foo(42),
| ^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field5: E = foo(42),
LL + field5: E,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:23:14
|
LL | field6: E = { foo(42) },
| ^^^^^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field6: E = { foo(42) },
LL + field6: E,
|
error: expected `:`, found `=`
--> $DIR/struct-default-values-and-missing-field-separator.rs:27:12
--> $DIR/struct-default-values-and-missing-field-separator.rs:26:12
|
LL | field1 = i32,
| ^
@ -140,7 +20,7 @@ LL | field1 = i32,
| help: field names and their types are separated with `:`
error: expected `:`, found `;`
--> $DIR/struct-default-values-and-missing-field-separator.rs:28:11
--> $DIR/struct-default-values-and-missing-field-separator.rs:27:11
|
LL | field2; E,
| ^
@ -148,5 +28,106 @@ LL | field2; E,
| expected `:`
| help: field names and their types are separated with `:`
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:8:16
|
LL | field1: i32 = 42,
| ^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:9:14
|
LL | field2: E = E::A,
| ^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:10:16
|
LL | field3: i32 = 1 + 2,
| ^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:11:16
|
LL | field4: i32 = { 1 + 2 },
| ^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:12:14
|
LL | field5: E = foo(42),
| ^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:13:14
|
LL | field6: E = { foo(42) },
| ^^^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:19:16
|
LL | field3: i32 = 1 + 2,
| ^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:20:16
|
LL | field4: i32 = { 1 + 2 },
| ^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:21:14
|
LL | field5: E = foo(42),
| ^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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 `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:22:14
|
LL | field6: E = { foo(42) },
| ^^^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/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: aborting due to 14 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -20,8 +20,8 @@ ast-stats-1 Stmt 160 ( 2.4%) 5 32
ast-stats-1 - Let 32 ( 0.5%) 1
ast-stats-1 - MacCall 32 ( 0.5%) 1
ast-stats-1 - Expr 96 ( 1.4%) 3
ast-stats-1 FieldDef 176 ( 2.6%) 2 88
ast-stats-1 Block 192 ( 2.9%) 6 32
ast-stats-1 FieldDef 208 ( 3.1%) 2 104
ast-stats-1 Variant 208 ( 3.1%) 2 104
ast-stats-1 AssocItem 352 ( 5.3%) 4 88
ast-stats-1 - Type 176 ( 2.6%) 2
@ -29,7 +29,7 @@ ast-stats-1 - Fn 176 ( 2.6%) 2
ast-stats-1 GenericBound 352 ( 5.3%) 4 88
ast-stats-1 - Trait 352 ( 5.3%) 4
ast-stats-1 GenericParam 480 ( 7.2%) 5 96
ast-stats-1 Pat 504 ( 7.6%) 7 72
ast-stats-1 Pat 504 ( 7.5%) 7 72
ast-stats-1 - Struct 72 ( 1.1%) 1
ast-stats-1 - Wild 72 ( 1.1%) 1
ast-stats-1 - Ident 360 ( 5.4%) 5
@ -39,13 +39,13 @@ ast-stats-1 - Match 72 ( 1.1%) 1
ast-stats-1 - Struct 72 ( 1.1%) 1
ast-stats-1 - Lit 144 ( 2.2%) 2
ast-stats-1 - Block 216 ( 3.2%) 3
ast-stats-1 PathSegment 744 (11.2%) 31 24
ast-stats-1 PathSegment 744 (11.1%) 31 24
ast-stats-1 Ty 896 (13.4%) 14 64
ast-stats-1 - Ref 64 ( 1.0%) 1
ast-stats-1 - Ptr 64 ( 1.0%) 1
ast-stats-1 - ImplicitSelf 128 ( 1.9%) 2
ast-stats-1 - Path 640 ( 9.6%) 10
ast-stats-1 Item 1_224 (18.4%) 9 136
ast-stats-1 Item 1_224 (18.3%) 9 136
ast-stats-1 - ForeignMod 136 ( 2.0%) 1
ast-stats-1 - Trait 136 ( 2.0%) 1
ast-stats-1 - Impl 136 ( 2.0%) 1
@ -53,7 +53,7 @@ ast-stats-1 - Enum 136 ( 2.0%) 1
ast-stats-1 - Fn 272 ( 4.1%) 2
ast-stats-1 - Use 408 ( 6.1%) 3
ast-stats-1 ----------------------------------------------------------------
ast-stats-1 Total 6_664 116
ast-stats-1 Total 6_696 116
ast-stats-1
ast-stats-2 POST EXPANSION AST STATS
ast-stats-2 Name Accumulated Size Count Item Size
@ -70,7 +70,7 @@ ast-stats-2 - Fn 88 ( 1.2%) 1
ast-stats-2 Arm 96 ( 1.3%) 2 48
ast-stats-2 FnDecl 120 ( 1.6%) 5 24
ast-stats-2 InlineAsm 120 ( 1.6%) 1 120
ast-stats-2 Attribute 128 ( 1.8%) 4 32
ast-stats-2 Attribute 128 ( 1.7%) 4 32
ast-stats-2 - DocComment 32 ( 0.4%) 1
ast-stats-2 - Normal 96 ( 1.3%) 3
ast-stats-2 Param 160 ( 2.2%) 4 40
@ -78,33 +78,33 @@ ast-stats-2 Stmt 160 ( 2.2%) 5 32
ast-stats-2 - Let 32 ( 0.4%) 1
ast-stats-2 - Semi 32 ( 0.4%) 1
ast-stats-2 - Expr 96 ( 1.3%) 3
ast-stats-2 FieldDef 176 ( 2.4%) 2 88
ast-stats-2 Block 192 ( 2.6%) 6 32
ast-stats-2 FieldDef 208 ( 2.8%) 2 104
ast-stats-2 Variant 208 ( 2.8%) 2 104
ast-stats-2 AssocItem 352 ( 4.8%) 4 88
ast-stats-2 - Type 176 ( 2.4%) 2
ast-stats-2 - Fn 176 ( 2.4%) 2
ast-stats-2 GenericBound 352 ( 4.8%) 4 88
ast-stats-2 - Trait 352 ( 4.8%) 4
ast-stats-2 GenericParam 480 ( 6.6%) 5 96
ast-stats-2 GenericParam 480 ( 6.5%) 5 96
ast-stats-2 Pat 504 ( 6.9%) 7 72
ast-stats-2 - Struct 72 ( 1.0%) 1
ast-stats-2 - Wild 72 ( 1.0%) 1
ast-stats-2 - Ident 360 ( 4.9%) 5
ast-stats-2 Expr 648 ( 8.9%) 9 72
ast-stats-2 Expr 648 ( 8.8%) 9 72
ast-stats-2 - Path 72 ( 1.0%) 1
ast-stats-2 - Match 72 ( 1.0%) 1
ast-stats-2 - Struct 72 ( 1.0%) 1
ast-stats-2 - InlineAsm 72 ( 1.0%) 1
ast-stats-2 - Lit 144 ( 2.0%) 2
ast-stats-2 - Block 216 ( 3.0%) 3
ast-stats-2 - Block 216 ( 2.9%) 3
ast-stats-2 PathSegment 864 (11.8%) 36 24
ast-stats-2 Ty 896 (12.3%) 14 64
ast-stats-2 Ty 896 (12.2%) 14 64
ast-stats-2 - Ref 64 ( 0.9%) 1
ast-stats-2 - Ptr 64 ( 0.9%) 1
ast-stats-2 - ImplicitSelf 128 ( 1.8%) 2
ast-stats-2 - Path 640 ( 8.8%) 10
ast-stats-2 Item 1_496 (20.5%) 11 136
ast-stats-2 - ImplicitSelf 128 ( 1.7%) 2
ast-stats-2 - Path 640 ( 8.7%) 10
ast-stats-2 Item 1_496 (20.4%) 11 136
ast-stats-2 - Enum 136 ( 1.9%) 1
ast-stats-2 - Trait 136 ( 1.9%) 1
ast-stats-2 - Impl 136 ( 1.9%) 1
@ -113,7 +113,7 @@ ast-stats-2 - ForeignMod 136 ( 1.9%) 1
ast-stats-2 - Fn 272 ( 3.7%) 2
ast-stats-2 - Use 544 ( 7.4%) 4
ast-stats-2 ----------------------------------------------------------------
ast-stats-2 Total 7_312 127
ast-stats-2 Total 7_344 127
ast-stats-2
hir-stats HIR STATS
hir-stats Name Accumulated Size Count Item Size
@ -138,9 +138,9 @@ hir-stats Stmt 96 ( 1.1%) 3 32
hir-stats - Let 32 ( 0.4%) 1
hir-stats - Semi 32 ( 0.4%) 1
hir-stats - Expr 32 ( 0.4%) 1
hir-stats FieldDef 112 ( 1.3%) 2 56
hir-stats FnDecl 120 ( 1.3%) 3 40
hir-stats Attribute 128 ( 1.4%) 4 32
hir-stats FieldDef 128 ( 1.4%) 2 64
hir-stats GenericArgs 144 ( 1.6%) 3 48
hir-stats Variant 144 ( 1.6%) 2 72
hir-stats GenericBound 256 ( 2.9%) 4 64
@ -163,7 +163,7 @@ hir-stats - Struct 64 ( 0.7%) 1
hir-stats - InlineAsm 64 ( 0.7%) 1
hir-stats - Lit 128 ( 1.4%) 2
hir-stats - Block 384 ( 4.3%) 6
hir-stats Item 968 (10.9%) 11 88
hir-stats Item 968 (10.8%) 11 88
hir-stats - Enum 88 ( 1.0%) 1
hir-stats - Trait 88 ( 1.0%) 1
hir-stats - Impl 88 ( 1.0%) 1
@ -174,5 +174,5 @@ hir-stats - Use 352 ( 3.9%) 4
hir-stats Path 1_240 (13.9%) 31 40
hir-stats PathSegment 1_920 (21.5%) 40 48
hir-stats ----------------------------------------------------------------
hir-stats Total 8_920 180
hir-stats Total 8_936 180
hir-stats

View file

@ -0,0 +1,49 @@
#![feature(default_field_values)]
#[derive(Debug)]
pub struct S;
#[derive(Debug, Default)]
pub struct Foo {
pub bar: S = S,
pub baz: i32 = 42 + 3,
}
#[derive(Debug, Default)]
pub struct Bar {
pub bar: S, //~ ERROR the trait bound `S: Default` is not satisfied
pub baz: i32 = 42 + 3,
}
#[derive(Default)]
pub struct Qux<const C: i32> {
bar: S = Self::S, //~ ERROR generic `Self` types are currently not permitted in anonymous constants
baz: i32 = foo(),
bat: i32 = <Qux<{ C }> as T>::K, //~ ERROR generic parameters may not be used in const operations
bay: i32 = C,
}
impl<const C: i32> Qux<C> {
const S: S = S;
}
trait T {
const K: i32;
}
impl<const C: i32> T for Qux<C> {
const K: i32 = 2;
}
const fn foo() -> i32 {
42
}
fn main () {
let _ = Foo { .. }; // ok
let _ = Foo::default(); // ok
let _ = Bar { .. }; //~ ERROR mandatory field
let _ = Bar::default(); // silenced
let _ = Bar { bar: S, .. }; // ok
let _ = Qux::<4> { .. };
}

View file

@ -0,0 +1,40 @@
error: generic parameters may not be used in const operations
--> $DIR/default-field-values-failures.rs:22:23
|
LL | bat: i32 = <Qux<{ C }> as T>::K,
| ^ cannot perform const operation using `C`
|
= help: const parameters may only be used as standalone arguments, i.e. `C`
= help: add `#![feature(generic_const_exprs)]` to allow generic const expressions
error[E0277]: the trait bound `S: Default` is not satisfied
--> $DIR/default-field-values-failures.rs:14:5
|
LL | #[derive(Debug, Default)]
| ------- in this derive macro expansion
LL | pub struct Bar {
LL | pub bar: S,
| ^^^^^^^^^^ the trait `Default` is not implemented for `S`
|
= note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `S` with `#[derive(Default)]`
|
LL + #[derive(Default)]
LL | pub struct S;
|
error: missing mandatory field `bar`
--> $DIR/default-field-values-failures.rs:45:21
|
LL | let _ = Bar { .. };
| ^
error: generic `Self` types are currently not permitted in anonymous constants
--> $DIR/default-field-values-failures.rs:20:14
|
LL | bar: S = Self::S,
| ^^^^
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,19 @@
#![feature(default_field_values, generic_const_exprs)]
#![allow(incomplete_features)]
#[warn(default_field_always_invalid_const)] //~ WARN lint `default_field_always_invalid_const` can't be warned on
pub struct Bat {
pub bax: u8 = panic!("asdf"),
//~^ ERROR evaluation of constant value failed
//~| WARN default field fails const-evaluation
}
pub struct Baz<const C: u8> {
pub bax: u8 = 130 + C, // ok
pub bat: u8 = 130 + 130,
//~^ ERROR evaluation of `Baz::<C>::bat::{constant#0}` failed
//~| ERROR default field fails const-evaluation
pub bay: u8 = 1, // ok
}
fn main() {}

View file

@ -0,0 +1,45 @@
warning: lint `default_field_always_invalid_const` can't be warned on
--> $DIR/default-field-values-invalid-const.rs:4:8
|
LL | #[warn(default_field_always_invalid_const)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ either `deny` or `allow`, no other lint level is supported for this lint
error[E0080]: evaluation of constant value failed
--> $DIR/default-field-values-invalid-const.rs:6:19
|
LL | pub bax: u8 = panic!("asdf"),
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'asdf', $DIR/default-field-values-invalid-const.rs:6:19
|
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: default field fails const-evaluation
--> $DIR/default-field-values-invalid-const.rs:6:5
|
LL | pub bax: u8 = panic!("asdf"),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this field's constant fails const-evaluation, as seen in the previous error
|
= help: you can skip const-evaluation of default fields by enabling this lint
note: the lint level is defined here
--> $DIR/default-field-values-invalid-const.rs:4:8
|
LL | #[warn(default_field_always_invalid_const)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of `Baz::<C>::bat::{constant#0}` failed
--> $DIR/default-field-values-invalid-const.rs:13:19
|
LL | pub bat: u8 = 130 + 130,
| ^^^^^^^^^ attempt to compute `130_u8 + 130_u8`, which would overflow
error: default field fails const-evaluation
--> $DIR/default-field-values-invalid-const.rs:13:5
|
LL | pub bat: u8 = 130 + 130,
| ^^^^^^^^^^^^^^^^^^^^^^^ this field's constant fails const-evaluation, as seen in the previous error
|
= help: you can skip const-evaluation of default fields by enabling this lint
= note: `#[deny(default_field_always_invalid_const)]` on by default
error: aborting due to 3 previous errors; 2 warnings emitted
For more information about this error, try `rustc --explain E0080`.

View file

@ -0,0 +1,68 @@
//@ run-pass
#![feature(default_field_values, generic_const_exprs)]
#![allow(unused_variables, dead_code, incomplete_features)]
pub struct S;
#[derive(Default)]
pub struct Foo {
pub bar: S = S,
pub baz: i32 = 42 + 3,
}
#[derive(Default)]
pub enum Bar {
#[default]
Foo {
bar: S = S,
baz: i32 = 42 + 3,
}
}
#[derive(Default)]
pub struct Qux<A, const C: i32> {
bar: S = Qux::<A, C>::S,
baz: i32 = foo(),
bat: i32 = <Qux<A, C> as T>::K,
baq: i32 = Self::K,
bay: i32 = C,
bak: Vec<A> = Vec::new(),
}
impl<A, const C: i32> Qux<A, C> {
const S: S = S;
}
trait T {
const K: i32;
}
impl<A, const C: i32> T for Qux<A, C> {
const K: i32 = 2;
}
const fn foo() -> i32 {
42
}
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);
let x = Bar::Foo { .. };
let y = Bar::default();
let z = Bar::Foo { baz: 1, .. };
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, x));
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, y));
assert!(matches!(Bar::Foo { bar: S, baz: 1 }, z));
let x = Qux::<i32, 4> { .. };
assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, baq: 2, bay: 4, .. }, x));
assert!(x.bak.is_empty());
}

View file

@ -92,7 +92,7 @@ body:
adt_def:
AdtDef {
did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo)
variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }]
variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe, value: None }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }]
flags: IS_ENUM
repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 }
args: []
@ -154,7 +154,7 @@ body:
adt_def:
AdtDef {
did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo)
variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }]
variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe, value: None }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }]
flags: IS_ENUM
repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 }
args: []
@ -206,7 +206,7 @@ body:
adt_def:
AdtDef {
did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo)
variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }]
variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe, value: None }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }]
flags: IS_ENUM
repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 }
args: []