From 9ac95c10c09faf50cc22eb97b6e1c59d64053c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 24 Aug 2024 17:22:48 +0000 Subject: [PATCH 1/7] 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: Vec = Vec::new(), } S:: { .. } ``` 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. --- compiler/rustc_ast/src/ast.rs | 1 + compiler/rustc_ast/src/mut_visit.rs | 3 +- compiler/rustc_ast/src/visit.rs | 4 +- compiler/rustc_ast_lowering/messages.ftl | 4 - compiler/rustc_ast_lowering/src/errors.rs | 8 - compiler/rustc_ast_lowering/src/expr.rs | 19 +- compiler/rustc_ast_lowering/src/item.rs | 1 + compiler/rustc_ast_passes/src/feature_gate.rs | 1 + .../src/diagnostics/conflict_errors.rs | 8 +- .../src/deriving/decodable.rs | 2 +- .../src/deriving/default.rs | 67 +++- .../src/deriving/generic/mod.rs | 8 +- compiler/rustc_expand/src/placeholders.rs | 1 + compiler/rustc_feature/src/unstable.rs | 3 + compiler/rustc_hir/src/hir.rs | 71 +++- compiler/rustc_hir/src/intravisit.rs | 17 +- compiler/rustc_hir_analysis/src/collect.rs | 7 +- .../rustc_hir_analysis/src/collect/type_of.rs | 6 + compiler/rustc_hir_pretty/src/lib.rs | 36 +- compiler/rustc_hir_typeck/messages.ftl | 6 + compiler/rustc_hir_typeck/src/errors.rs | 41 +++ compiler/rustc_hir_typeck/src/expr.rs | 103 +++++- .../rustc_hir_typeck/src/expr_use_visitor.rs | 6 +- compiler/rustc_lint/messages.ftl | 4 + .../src/default_field_always_invalid.rs | 91 +++++ compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/lints.rs | 9 + compiler/rustc_metadata/src/rmeta/decoder.rs | 1 + compiler/rustc_middle/src/thir.rs | 17 +- compiler/rustc_middle/src/thir/visit.rs | 3 +- compiler/rustc_middle/src/ty/adt.rs | 4 +- compiler/rustc_middle/src/ty/mod.rs | 7 +- .../src/build/custom/parse/instruction.rs | 2 +- .../rustc_mir_build/src/build/expr/into.rs | 64 ++-- compiler/rustc_mir_build/src/thir/cx/expr.rs | 54 ++- compiler/rustc_mir_build/src/thir/print.rs | 16 +- compiler/rustc_parse/messages.ftl | 3 - compiler/rustc_parse/src/errors.rs | 8 - compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/item.rs | 11 +- compiler/rustc_passes/src/liveness.rs | 7 +- compiler/rustc_privacy/src/lib.rs | 59 +++- compiler/rustc_resolve/src/late.rs | 27 +- compiler/rustc_span/src/symbol.rs | 1 + src/tools/clippy/clippy_lints/src/default.rs | 4 +- .../src/default_numeric_fallback.rs | 4 +- .../src/inconsistent_struct_constructor.rs | 4 +- .../clippy_lints/src/init_numbered_fields.rs | 4 +- .../clippy_lints/src/loops/never_loop.rs | 4 +- .../clippy_lints/src/needless_update.rs | 4 +- .../clippy/clippy_lints/src/no_effect.rs | 11 +- .../src/single_range_in_vec_init.rs | 4 +- .../src/unnecessary_struct_initialization.rs | 8 +- .../clippy/clippy_lints/src/utils/author.rs | 7 +- src/tools/clippy/clippy_utils/src/higher.rs | 4 +- .../clippy/clippy_utils/src/hir_utils.rs | 11 +- src/tools/clippy/clippy_utils/src/visitors.rs | 4 +- .../struct_destructure_fail.stderr | 22 +- .../feature-gate-default-field-values.rs | 106 ++++++ .../feature-gate-default-field-values.stderr | 318 ++++++++++++++++++ ...t-values-and-missing-field-separator.fixed | 35 -- ...ault-values-and-missing-field-separator.rs | 1 - ...-values-and-missing-field-separator.stderr | 181 +++++----- tests/ui/stats/input-stats.stderr | 36 +- .../structs/default-field-values-failures.rs | 49 +++ .../default-field-values-failures.stderr | 40 +++ .../default-field-values-invalid-const.rs | 19 ++ .../default-field-values-invalid-const.stderr | 45 +++ .../structs/default-field-values-support.rs | 68 ++++ tests/ui/thir-print/thir-tree-match.stdout | 6 +- 70 files changed, 1446 insertions(+), 369 deletions(-) create mode 100644 compiler/rustc_lint/src/default_field_always_invalid.rs create mode 100644 tests/ui/feature-gates/feature-gate-default-field-values.rs create mode 100644 tests/ui/feature-gates/feature-gate-default-field-values.stderr delete mode 100644 tests/ui/parser/struct-default-values-and-missing-field-separator.fixed create mode 100644 tests/ui/structs/default-field-values-failures.rs create mode 100644 tests/ui/structs/default-field-values-failures.stderr create mode 100644 tests/ui/structs/default-field-values-invalid-const.rs create mode 100644 tests/ui/structs/default-field-values-invalid-const.stderr create mode 100644 tests/ui/structs/default-field-values-support.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 69ba78282f96f..650525a2f520e 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3119,6 +3119,7 @@ pub struct FieldDef { pub ident: Option, pub ty: P, + pub default: Option, pub is_placeholder: bool, } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 3a4a8ce266e9c..2c09059fe1904 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1120,13 +1120,14 @@ fn walk_poly_trait_ref(vis: &mut T, p: &mut PolyTraitRef) { } pub fn walk_field_def(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); } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 0b000c8cef8a3..a7f7c37693a84 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -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() } diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index a4dbf98111538..b53cb7a3822ad 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -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 diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 447af57354fd6..665da14e86123 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -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 { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 84e648f4923e0..2ad0ff3200e74 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -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, ) } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index fb09f1c7fee1f..1078aeea22b15 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -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), } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 390a575a186ba..688d4d635cf52 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -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"); diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index b42c99e1a6d9d..8dcc5324fdfb0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -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>, ) { - 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` diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 686424770fc58..469092e7b1cf0 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -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) }) diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index d4befd12190bf..12d5587b5db84 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -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 { + // 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); } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index f6eea0b21cad7..846d8784dea52 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -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, IsTuple), /// Normal structs/struct variants. - Named(Vec<(Ident, Span)>), + Named(Vec<(Ident, Span, Option)>), } /// 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), } } diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index f044d964f1358..9e459bd81a11e 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -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(), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index bf26b5d25d2ba..93a605e197ce9 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -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. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a9696627f11b1..365e4cbb55676 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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`. - 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<'_> { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 602aa8be740aa..9abb0870bf0ff 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -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>>( diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index a4636da3f6213..8f9b062603127 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1021,12 +1021,12 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { } } -fn lower_variant( - tcx: TyCtxt<'_>, +fn lower_variant<'tcx>( + tcx: TyCtxt<'tcx>, variant_did: Option, 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 { diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 72d5b3ac4f5e2..5595504c3d9b1 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -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, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 0f3dcebc092ef..a74e36c45c6ef 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -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("}"); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index b27f7215ae4ae..a93da52b2703c 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -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 diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index a2e008593077f..7746a5a7132f8 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -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, + #[subdiagnostic] + pub add_expr: Option, + #[subdiagnostic] + pub remove_dots: Option, +} + +#[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 { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 04c06169d3309..0e079b037691e 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -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); } } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 774d00edea035..27ec2e9e0d482 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -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(()); } }; diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 422629cd11d08..f5d2ebc3e87e0 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -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 +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 diff --git a/compiler/rustc_lint/src/default_field_always_invalid.rs b/compiler/rustc_lint/src/default_field_always_invalid.rs new file mode 100644 index 0000000000000..46cffb53b4b63 --- /dev/null +++ b/compiler/rustc_lint/src/default_field_always_invalid.rs @@ -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: () }, + ); + } + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index a99c94592b302..baf2703511e2e 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -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, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 9fa263799ebf1..07f1c48bafb57 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -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 { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index f3f5af494123a..4a4930fff9d53 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -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, diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 9cf6bc1b77781..86014c34b4584 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -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>, + /// 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)] diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 81202a6eaad6c..64bac12b2666a 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -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]); } } diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 447cbc8932ee5..9678863ee4d8a 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -259,10 +259,10 @@ impl Into 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, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c7a2223ecd78b..70e0568b2025b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1364,6 +1364,7 @@ pub struct FieldDef { pub name: Symbol, pub vis: Visibility, pub safety: hir::Safety, + pub value: Option, } 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) } diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index c3e9bd302deb9..67114efdff52e 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -283,7 +283,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { fields.iter().map(|e| self.parse_operand(*e)).collect::>()? )) }, - 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); diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index bebb44faba614..b31f61a75ffe2 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -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 fields = if let Some(FruInfo { base, field_types }) = base { - let place_builder = unpack!(block = this.as_place_builder(block, *base)); + let variant = adt_def.variant(variant_index); + let field_names = variant.fields.indices(); - // 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() + 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. + 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; diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index ee9bcce104e0b..d75f01dfba09f 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -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), } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 6be0ed5fb3115..2bcdb67c58a98 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -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); + } } } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index e5cd4622dae90..d50bd18a1d7d8 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -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 diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 14f2dd32e9232..4c4e03cdfa394 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -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 { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 3a9e9b480ecf2..eeb83a85e59bb 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -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() { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 475cd09147f5c..f12b4ca249d65 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -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, }) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 9cd95a0b02daa..09cbb648f9b1b 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -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() diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index c845d60ac0715..3057f13e3a75a 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -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, + ); + } } } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index fc92dc8b4ed71..f5e1a5988649c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -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)); + } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 818d4afffc66b..d30b17c9cd8dd 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -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, diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index de775b647952e..ffdd946aadb8b 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -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 { diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index ef6b141920d03..6819ad547f876 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -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(); diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index d386bfca6baa1..4fcd2abb76945 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -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() diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs index 7f183bb601eb4..7a14bbfb9e8b7 100644 --- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs +++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs @@ -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. diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index 1c55e3e22e8c1..ed9879de13b1c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -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 diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs index 6a2893cefbd5b..0cba72bd2c6a9 100644 --- a/src/tools/clippy/clippy_lints/src/needless_update.rs +++ b/src/tools/clippy/clippy_lints/src/needless_update.rs @@ -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() diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 8ecff9c3f9b36..9e44bb02c56fa 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -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 Some(base), + StructTailExpr::None | StructTailExpr::DefaultFields(_) => None, + }; Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect()) } }, diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index 44e585953bfb9..bb11daecc07da 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -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; }; diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs index afdd3505cdd16..0a90d31db7e14 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs @@ -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 }, diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 51001d374b44d..311ed427cb910 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -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| { diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 11bbe734844d3..d216879cbd25e 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -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, diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 7f3e331e7f640..4be4340862d2a 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -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); } }, diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index a79be5ca7d4c4..351e619d7b1e8 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -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)?; } }, diff --git a/tests/ui/destructuring-assignment/struct_destructure_fail.stderr b/tests/ui/destructuring-assignment/struct_destructure_fail.stderr index 4c4f0663eeb43..58f8e97dea04a 100644 --- a/tests/ui/destructuring-assignment/struct_destructure_fail.stderr +++ b/tests/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -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. diff --git a/tests/ui/feature-gates/feature-gate-default-field-values.rs b/tests/ui/feature-gates/feature-gate-default-field-values.rs new file mode 100644 index 0000000000000..01441de67e0a8 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-default-field-values.rs @@ -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 { + bar: S = Qux::::S, //~ ERROR default values on `struct` fields aren't supported + baz: i32 = foo(), //~ ERROR default values on `struct` fields aren't supported + bat: i32 = 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 = Vec::new(), //~ ERROR default values on `struct` fields aren't supported +} + +impl Qux { + const S: S = S; +} + +trait T { + const K: i32; +} + +impl T for Qux { + 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:: { .. }; //~ ERROR base expression required after `..` + assert!(matches!(Qux:: { 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 `..` +} diff --git a/tests/ui/feature-gates/feature-gate-default-field-values.stderr b/tests/ui/feature-gates/feature-gate-default-field-values.stderr new file mode 100644 index 0000000000000..a24217c072769 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-default-field-values.stderr @@ -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 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 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 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 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::::S, + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #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 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 = as T>::K, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #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 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 = Vec::new(), + | ^^^^^^^^^^^^^ + | + = note: see issue #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 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 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:: { .. }; + | ^ + | + = 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:: { ../* expr */ }; + | ++++++++++ + +error[E0797]: base expression required after `..` + --> $DIR/feature-gate-default-field-values.rs:79:73 + | +LL | assert!(matches!(Qux:: { 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:: { 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`. diff --git a/tests/ui/parser/struct-default-values-and-missing-field-separator.fixed b/tests/ui/parser/struct-default-values-and-missing-field-separator.fixed deleted file mode 100644 index be6ed053c6e3e..0000000000000 --- a/tests/ui/parser/struct-default-values-and-missing-field-separator.fixed +++ /dev/null @@ -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() {} diff --git a/tests/ui/parser/struct-default-values-and-missing-field-separator.rs b/tests/ui/parser/struct-default-values-and-missing-field-separator.rs index 7900d397a5ded..8ecf042ad385d 100644 --- a/tests/ui/parser/struct-default-values-and-missing-field-separator.rs +++ b/tests/ui/parser/struct-default-values-and-missing-field-separator.rs @@ -1,4 +1,3 @@ -//@ run-rustfix #![allow(dead_code)] enum E { diff --git a/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr b/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr index 1fb57ab11f9f2..669147c5685fa 100644 --- a/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr +++ b/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr @@ -1,152 +1,133 @@ -error: default values on `struct` fields aren't supported - --> $DIR/struct-default-values-and-missing-field-separator.rs:9:16 +error: expected `,`, or `}`, found `field2` + --> $DIR/struct-default-values-and-missing-field-separator.rs:17:16 | -LL | field1: i32 = 42, - | ^^^^^ +LL | field1: i32 + | ^ help: try adding a comma: `,` + +error: expected `,`, or `}`, found `field3` + --> $DIR/struct-default-values-and-missing-field-separator.rs:18:14 + | +LL | field2: E + | ^ help: try adding a comma: `,` + +error: expected `:`, found `=` + --> $DIR/struct-default-values-and-missing-field-separator.rs:26:12 + | +LL | field1 = i32, + | ^ + | | + | expected `:` + | help: field names and their types are separated with `:` + +error: expected `:`, found `;` + --> $DIR/struct-default-values-and-missing-field-separator.rs:27:11 | -help: remove this unsupported default value +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, -LL + field1: i32, +LL | field1: i32 = 42, + | ^^^^^ | + = note: see issue #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: default values on `struct` fields aren't supported - --> $DIR/struct-default-values-and-missing-field-separator.rs:10:14 +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, | ^^^^^^^ | -help: remove this unsupported default value - | -LL - field2: E = E::A, -LL + field2: E, - | + = note: see issue #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: default values on `struct` fields aren't supported - --> $DIR/struct-default-values-and-missing-field-separator.rs:11:16 +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, | ^^^^^^^^ | -help: remove this unsupported default value - | -LL - field3: i32 = 1 + 2, -LL + field3: i32, - | + = note: see issue #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: default values on `struct` fields aren't supported - --> $DIR/struct-default-values-and-missing-field-separator.rs:12:16 +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 }, | ^^^^^^^^^^^^ | -help: remove this unsupported default value - | -LL - field4: i32 = { 1 + 2 }, -LL + field4: i32, - | + = note: see issue #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: default values on `struct` fields aren't supported - --> $DIR/struct-default-values-and-missing-field-separator.rs:13:14 +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), | ^^^^^^^^^^ | -help: remove this unsupported default value - | -LL - field5: E = foo(42), -LL + field5: E, - | + = note: see issue #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: default values on `struct` fields aren't supported - --> $DIR/struct-default-values-and-missing-field-separator.rs:14:14 +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) }, | ^^^^^^^^^^^^^^ | -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 - | -LL | field1: i32 - | ^ help: try adding a comma: `,` - -error: expected `,`, or `}`, found `field3` - --> $DIR/struct-default-values-and-missing-field-separator.rs:19:14 - | -LL | field2: E - | ^ help: try adding a comma: `,` + = note: see issue #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: default values on `struct` fields aren't supported - --> $DIR/struct-default-values-and-missing-field-separator.rs:20:16 +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, | ^^^^^^^^ | -help: remove this unsupported default value - | -LL - field3: i32 = 1 + 2, -LL + field3: i32, - | + = note: see issue #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: default values on `struct` fields aren't supported - --> $DIR/struct-default-values-and-missing-field-separator.rs:21:16 +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 }, | ^^^^^^^^^^^^ | -help: remove this unsupported default value - | -LL - field4: i32 = { 1 + 2 }, -LL + field4: i32, - | + = note: see issue #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: default values on `struct` fields aren't supported - --> $DIR/struct-default-values-and-missing-field-separator.rs:22:14 +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), | ^^^^^^^^^^ | -help: remove this unsupported default value - | -LL - field5: E = foo(42), -LL + field5: E, - | + = note: see issue #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: default values on `struct` fields aren't supported - --> $DIR/struct-default-values-and-missing-field-separator.rs:23:14 +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) }, | ^^^^^^^^^^^^^^ | -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 - | -LL | field1 = i32, - | ^ - | | - | expected `:` - | help: field names and their types are separated with `:` - -error: expected `:`, found `;` - --> $DIR/struct-default-values-and-missing-field-separator.rs:28:11 - | -LL | field2; E, - | ^ - | | - | expected `:` - | help: field names and their types are separated with `:` + = note: see issue #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`. diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index 2adbcfab612c6..e3bc68a213410 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -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 diff --git a/tests/ui/structs/default-field-values-failures.rs b/tests/ui/structs/default-field-values-failures.rs new file mode 100644 index 0000000000000..c4de4f5cdade5 --- /dev/null +++ b/tests/ui/structs/default-field-values-failures.rs @@ -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 { + bar: S = Self::S, //~ ERROR generic `Self` types are currently not permitted in anonymous constants + baz: i32 = foo(), + bat: i32 = as T>::K, //~ ERROR generic parameters may not be used in const operations + bay: i32 = C, +} + +impl Qux { + const S: S = S; +} + +trait T { + const K: i32; +} + +impl T for Qux { + 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> { .. }; +} diff --git a/tests/ui/structs/default-field-values-failures.stderr b/tests/ui/structs/default-field-values-failures.stderr new file mode 100644 index 0000000000000..bd96777f86521 --- /dev/null +++ b/tests/ui/structs/default-field-values-failures.stderr @@ -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 = 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`. diff --git a/tests/ui/structs/default-field-values-invalid-const.rs b/tests/ui/structs/default-field-values-invalid-const.rs new file mode 100644 index 0000000000000..6838238064be5 --- /dev/null +++ b/tests/ui/structs/default-field-values-invalid-const.rs @@ -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 { + pub bax: u8 = 130 + C, // ok + pub bat: u8 = 130 + 130, + //~^ ERROR evaluation of `Baz::::bat::{constant#0}` failed + //~| ERROR default field fails const-evaluation + pub bay: u8 = 1, // ok +} + +fn main() {} diff --git a/tests/ui/structs/default-field-values-invalid-const.stderr b/tests/ui/structs/default-field-values-invalid-const.stderr new file mode 100644 index 0000000000000..a0d84271f4bc9 --- /dev/null +++ b/tests/ui/structs/default-field-values-invalid-const.stderr @@ -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::::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`. diff --git a/tests/ui/structs/default-field-values-support.rs b/tests/ui/structs/default-field-values-support.rs new file mode 100644 index 0000000000000..bdf21e1272d87 --- /dev/null +++ b/tests/ui/structs/default-field-values-support.rs @@ -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 { + bar: S = Qux::::S, + baz: i32 = foo(), + bat: i32 = as T>::K, + baq: i32 = Self::K, + bay: i32 = C, + bak: Vec = Vec::new(), +} + +impl Qux { + const S: S = S; +} + +trait T { + const K: i32; +} + +impl T for Qux { + 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:: { .. }; + assert!(matches!(Qux:: { bar: S, baz: 42, bat: 2, baq: 2, bay: 4, .. }, x)); + assert!(x.bak.is_empty()); +} diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index d56f15fb221c8..916f296ccfc6b 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -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: [] From 550bcae8aa7859abace52c20b8b9149ae6cb37f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 26 Nov 2024 17:53:00 +0000 Subject: [PATCH 2/7] Detect `struct S(ty = val);` Emit a specific error for unsupported default field value syntax in tuple structs. --- compiler/rustc_ast_lowering/messages.ftl | 3 ++ compiler/rustc_ast_lowering/src/errors.rs | 8 +++++ compiler/rustc_ast_lowering/src/item.rs | 33 ++++++++++++++----- compiler/rustc_parse/src/parser/item.rs | 18 +++++++++- .../structs/default-field-values-failures.rs | 2 ++ .../default-field-values-failures.stderr | 10 ++++-- 6 files changed, 63 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index b53cb7a3822ad..b31e21f5c3568 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -53,6 +53,9 @@ ast_lowering_closure_cannot_be_static = closures cannot be static ast_lowering_coroutine_too_many_parameters = too many parameters for a coroutine (expected 0 or 1 parameters) +ast_lowering_default_field_in_tuple = default field in tuple struct + .label = default fields are only supported on structs + ast_lowering_does_not_support_modifiers = the `{$class_name}` register class does not support template modifiers diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 665da14e86123..2564d4e27726c 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -38,6 +38,14 @@ pub(crate) struct InvalidAbi { pub suggestion: Option, } +#[derive(Diagnostic)] +#[diag(ast_lowering_default_field_in_tuple)] +pub(crate) struct TupleStructWithDefault { + #[primary_span] + #[label] + pub span: Span, +} + pub(crate) struct InvalidAbiReason(pub &'static str); impl Subdiagnostic for InvalidAbiReason { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 1078aeea22b15..7d6c41992eb09 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -17,7 +17,10 @@ use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; use tracing::instrument; -use super::errors::{InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound}; +use super::errors::{ + InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound, + TupleStructWithDefault, +}; use super::{ AstOwner, FnDeclKind, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, ResolverAstLoweringExt, @@ -690,13 +693,27 @@ impl<'hir> LoweringContext<'_, 'hir> { VariantData::Tuple(fields, id) => { let ctor_id = self.lower_node_id(*id); self.alias_attrs(ctor_id, parent_id); - hir::VariantData::Tuple( - self.arena.alloc_from_iter( - fields.iter().enumerate().map(|f| self.lower_field_def(f)), - ), - ctor_id, - self.local_def_id(*id), - ) + let fields = self + .arena + .alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))); + for field in &fields[..] { + if let Some(default) = field.default { + // Default values in tuple struct and tuple variants are not allowed by the + // RFC due to concerns about the syntax, both in the item definition and the + // expression. We could in the future allow `struct S(i32 = 0);` and force + // users to construct the value with `let _ = S { .. };`. + if self.tcx.features().default_field_values() { + self.dcx().emit_err(TupleStructWithDefault { span: default.span }); + } else { + let _ = self.dcx().span_delayed_bug( + default.span, + "expected `default values on `struct` fields aren't supported` \ + feature-gate error but none was produced", + ); + } + } + } + hir::VariantData::Tuple(fields, ctor_id, self.local_def_id(*id)) } VariantData::Unit(id) => { let ctor_id = self.lower_node_id(*id); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index f12b4ca249d65..58b4bf8980b1f 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1836,6 +1836,22 @@ impl<'a> Parser<'a> { return Err(err); } }; + let mut default = None; + if p.token == token::Eq { + let mut snapshot = p.create_snapshot_for_diagnostic(); + snapshot.bump(); + match snapshot.parse_expr_anon_const() { + Ok(const_expr) => { + let sp = ty.span.shrink_to_hi().to(const_expr.value.span); + p.psess.gated_spans.gate(sym::default_field_values, sp); + p.restore_snapshot(snapshot); + default = Some(const_expr); + } + Err(err) => { + err.cancel(); + } + } + } Ok(( FieldDef { @@ -1845,7 +1861,7 @@ impl<'a> Parser<'a> { ident: None, id: DUMMY_NODE_ID, ty, - default: None, + default, attrs, is_placeholder: false, }, diff --git a/tests/ui/structs/default-field-values-failures.rs b/tests/ui/structs/default-field-values-failures.rs index c4de4f5cdade5..27eff70dd3e27 100644 --- a/tests/ui/structs/default-field-values-failures.rs +++ b/tests/ui/structs/default-field-values-failures.rs @@ -23,6 +23,8 @@ pub struct Qux { bay: i32 = C, } +pub struct Rak(i32 = 42); //~ ERROR default field in tuple struct + impl Qux { const S: S = S; } diff --git a/tests/ui/structs/default-field-values-failures.stderr b/tests/ui/structs/default-field-values-failures.stderr index bd96777f86521..3564c1dc20e88 100644 --- a/tests/ui/structs/default-field-values-failures.stderr +++ b/tests/ui/structs/default-field-values-failures.stderr @@ -7,6 +7,12 @@ LL | bat: i32 = as T>::K, = 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: default field in tuple struct + --> $DIR/default-field-values-failures.rs:26:22 + | +LL | pub struct Rak(i32 = 42); + | ^^ default fields are only supported on structs + error[E0277]: the trait bound `S: Default` is not satisfied --> $DIR/default-field-values-failures.rs:14:5 | @@ -24,7 +30,7 @@ LL | pub struct S; | error: missing mandatory field `bar` - --> $DIR/default-field-values-failures.rs:45:21 + --> $DIR/default-field-values-failures.rs:47:21 | LL | let _ = Bar { .. }; | ^ @@ -35,6 +41,6 @@ error: generic `Self` types are currently not permitted in anonymous constants LL | bar: S = Self::S, | ^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0277`. From e0752ad2572218dfc7764951b72d4e3e84ea842c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 26 Nov 2024 22:28:51 +0000 Subject: [PATCH 3/7] Provide diagnostic for `Struct(a, .., z)` expression People might extrapolate from `Struct { .. }` that `Struct(..)` would work, but it doesn't. --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 31 +++++++++ tests/ui/range/issue-54505-no-std.rs | 1 + tests/ui/range/issue-54505-no-std.stderr | 11 ++- tests/ui/range/issue-54505.fixed | 1 + tests/ui/range/issue-54505.rs | 1 + tests/ui/range/issue-54505.stderr | 11 ++- .../structs/default-field-values-failures.rs | 7 ++ .../default-field-values-failures.stderr | 68 ++++++++++++++++++- 8 files changed, 123 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 44582390a4bf3..6b1cceefbeea1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -903,6 +903,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + let detect_dotdot = |err: &mut Diag<'_>, ty: Ty<'_>, expr: &hir::Expr<'_>| { + if let ty::Adt(adt, _) = ty.kind() + && self.tcx().lang_items().get(hir::LangItem::RangeFull) == Some(adt.did()) + && let hir::ExprKind::Struct( + hir::QPath::LangItem(hir::LangItem::RangeFull, _), + [], + _, + ) = expr.kind + { + // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax + // from default field values, which is not supported on tuples. + let explanation = if self.tcx.features().default_field_values() { + "this is only supported on non-tuple struct literals" + } else if self.tcx.sess.is_nightly_build() { + "this is only supported on non-tuple struct literals when \ + `#![feature(default_field_values)]` is enabled" + } else { + "this is not supported" + }; + let msg = format!( + "you might have meant to use `..` to skip providing a value for \ + expected fields, but {explanation}; it is instead interpreted as a \ + `std::ops::RangeFull` literal", + ); + err.span_help(expr.span, msg); + } + }; + let mut reported = None; errors.retain(|error| { let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = @@ -1009,6 +1037,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments, ); suggest_confusable(&mut err); + detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); return err.emit(); } @@ -1133,6 +1162,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, None, ); + detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); } Error::Extra(arg_idx) => { let (provided_ty, provided_span) = provided_arg_tys[arg_idx]; @@ -1216,6 +1246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; prev_extra_idx = Some(arg_idx.index()) } + detect_dotdot(&mut err, provided_ty, provided_args[arg_idx]); } Error::Missing(expected_idx) => { // If there are multiple missing arguments adjacent to each other, diff --git a/tests/ui/range/issue-54505-no-std.rs b/tests/ui/range/issue-54505-no-std.rs index a15956853729a..0c913f766b709 100644 --- a/tests/ui/range/issue-54505-no-std.rs +++ b/tests/ui/range/issue-54505-no-std.rs @@ -38,6 +38,7 @@ fn main() { take_range(..); //~^ ERROR mismatched types [E0308] + //~| HELP you might have meant //~| HELP consider borrowing here //~| SUGGESTION &( diff --git a/tests/ui/range/issue-54505-no-std.stderr b/tests/ui/range/issue-54505-no-std.stderr index f15a0ae61389a..2aa1d584046dd 100644 --- a/tests/ui/range/issue-54505-no-std.stderr +++ b/tests/ui/range/issue-54505-no-std.stderr @@ -53,13 +53,18 @@ note: function defined here | LL | fn take_range(_r: &impl RangeBounds) {} | ^^^^^^^^^^ ------------------------- +help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals when `#![feature(default_field_values)]` is enabled; it is instead interpreted as a `std::ops::RangeFull` literal + --> $DIR/issue-54505-no-std.rs:39:16 + | +LL | take_range(..); + | ^^ help: consider borrowing here | LL | take_range(&(..)); | ++ + error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:44:16 + --> $DIR/issue-54505-no-std.rs:45:16 | LL | take_range(0..=1); | ---------- ^^^^^ expected `&_`, found `RangeInclusive<{integer}>` @@ -79,7 +84,7 @@ LL | take_range(&(0..=1)); | ++ + error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:49:16 + --> $DIR/issue-54505-no-std.rs:50:16 | LL | take_range(..5); | ---------- ^^^ expected `&_`, found `RangeTo<{integer}>` @@ -99,7 +104,7 @@ LL | take_range(&(..5)); | ++ + error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:54:16 + --> $DIR/issue-54505-no-std.rs:55:16 | LL | take_range(..=42); | ---------- ^^^^^ expected `&_`, found `RangeToInclusive<{integer}>` diff --git a/tests/ui/range/issue-54505.fixed b/tests/ui/range/issue-54505.fixed index 054d3c2cf5e02..08a2682140a42 100644 --- a/tests/ui/range/issue-54505.fixed +++ b/tests/ui/range/issue-54505.fixed @@ -23,6 +23,7 @@ fn main() { take_range(&(..)); //~^ ERROR mismatched types [E0308] + //~| HELP you might have meant //~| HELP consider borrowing here //~| SUGGESTION &( diff --git a/tests/ui/range/issue-54505.rs b/tests/ui/range/issue-54505.rs index f5cec83176098..0a9d7083e4f82 100644 --- a/tests/ui/range/issue-54505.rs +++ b/tests/ui/range/issue-54505.rs @@ -23,6 +23,7 @@ fn main() { take_range(..); //~^ ERROR mismatched types [E0308] + //~| HELP you might have meant //~| HELP consider borrowing here //~| SUGGESTION &( diff --git a/tests/ui/range/issue-54505.stderr b/tests/ui/range/issue-54505.stderr index 0e959fc05e279..291e097e8659f 100644 --- a/tests/ui/range/issue-54505.stderr +++ b/tests/ui/range/issue-54505.stderr @@ -53,13 +53,18 @@ note: function defined here | LL | fn take_range(_r: &impl RangeBounds) {} | ^^^^^^^^^^ ------------------------- +help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals when `#![feature(default_field_values)]` is enabled; it is instead interpreted as a `std::ops::RangeFull` literal + --> $DIR/issue-54505.rs:24:16 + | +LL | take_range(..); + | ^^ help: consider borrowing here | LL | take_range(&(..)); | ++ + error[E0308]: mismatched types - --> $DIR/issue-54505.rs:29:16 + --> $DIR/issue-54505.rs:30:16 | LL | take_range(0..=1); | ---------- ^^^^^ expected `&_`, found `RangeInclusive<{integer}>` @@ -79,7 +84,7 @@ LL | take_range(&(0..=1)); | ++ + error[E0308]: mismatched types - --> $DIR/issue-54505.rs:34:16 + --> $DIR/issue-54505.rs:35:16 | LL | take_range(..5); | ---------- ^^^ expected `&_`, found `RangeTo<{integer}>` @@ -99,7 +104,7 @@ LL | take_range(&(..5)); | ++ + error[E0308]: mismatched types - --> $DIR/issue-54505.rs:39:16 + --> $DIR/issue-54505.rs:40:16 | LL | take_range(..=42); | ---------- ^^^^^ expected `&_`, found `RangeToInclusive<{integer}>` diff --git a/tests/ui/structs/default-field-values-failures.rs b/tests/ui/structs/default-field-values-failures.rs index 27eff70dd3e27..e850ad387aaf3 100644 --- a/tests/ui/structs/default-field-values-failures.rs +++ b/tests/ui/structs/default-field-values-failures.rs @@ -48,4 +48,11 @@ fn main () { let _ = Bar::default(); // silenced let _ = Bar { bar: S, .. }; // ok let _ = Qux::<4> { .. }; + let _ = Rak(..); //~ ERROR E0308 + //~^ you might have meant to use `..` to skip providing + let _ = Rak(0, ..); //~ ERROR E0061 + //~^ you might have meant to use `..` to skip providing + let _ = Rak(.., 0); //~ ERROR E0061 + //~^ you might have meant to use `..` to skip providing + let _ = Rak { .. }; // ok } diff --git a/tests/ui/structs/default-field-values-failures.stderr b/tests/ui/structs/default-field-values-failures.stderr index 3564c1dc20e88..bd8d1922e9127 100644 --- a/tests/ui/structs/default-field-values-failures.stderr +++ b/tests/ui/structs/default-field-values-failures.stderr @@ -35,12 +35,76 @@ error: missing mandatory field `bar` LL | let _ = Bar { .. }; | ^ +error[E0308]: mismatched types + --> $DIR/default-field-values-failures.rs:51:17 + | +LL | let _ = Rak(..); + | --- ^^ expected `i32`, found `RangeFull` + | | + | arguments to this struct are incorrect + | +note: tuple struct defined here + --> $DIR/default-field-values-failures.rs:26:12 + | +LL | pub struct Rak(i32 = 42); + | ^^^ +help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal + --> $DIR/default-field-values-failures.rs:51:17 + | +LL | let _ = Rak(..); + | ^^ + +error[E0061]: this struct takes 1 argument but 2 arguments were supplied + --> $DIR/default-field-values-failures.rs:53:13 + | +LL | let _ = Rak(0, ..); + | ^^^ -- unexpected argument #2 of type `RangeFull` + | +help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal + --> $DIR/default-field-values-failures.rs:53:20 + | +LL | let _ = Rak(0, ..); + | ^^ +note: tuple struct defined here + --> $DIR/default-field-values-failures.rs:26:12 + | +LL | pub struct Rak(i32 = 42); + | ^^^ +help: remove the extra argument + | +LL - let _ = Rak(0, ..); +LL + let _ = Rak(0); + | + +error[E0061]: this struct takes 1 argument but 2 arguments were supplied + --> $DIR/default-field-values-failures.rs:55:13 + | +LL | let _ = Rak(.., 0); + | ^^^ -- unexpected argument #1 of type `RangeFull` + | +help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal + --> $DIR/default-field-values-failures.rs:55:17 + | +LL | let _ = Rak(.., 0); + | ^^ +note: tuple struct defined here + --> $DIR/default-field-values-failures.rs:26:12 + | +LL | pub struct Rak(i32 = 42); + | ^^^ +help: remove the extra argument + | +LL - let _ = Rak(.., 0); +LL + let _ = Rak(0); + | + 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 5 previous errors +error: aborting due to 8 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0061, E0277, E0308. +For more information about an error, try `rustc --explain E0061`. From 148a77dfde03dba553b2e4f3cf817cd6d105b3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 6 Dec 2024 19:43:01 +0000 Subject: [PATCH 4/7] review comments: rewordings --- compiler/rustc_ast_lowering/messages.ftl | 2 +- compiler/rustc_ast_passes/src/feature_gate.rs | 2 +- compiler/rustc_middle/src/ty/adt.rs | 4 ++-- .../rustc_mir_build/src/build/expr/into.rs | 2 +- compiler/rustc_resolve/src/late.rs | 2 +- .../feature-gate-default-field-values.rs | 22 +++++++++---------- .../feature-gate-default-field-values.stderr | 22 +++++++++---------- ...ault-values-and-missing-field-separator.rs | 20 ++++++++--------- ...-values-and-missing-field-separator.stderr | 20 ++++++++--------- .../structs/default-field-values-failures.rs | 2 +- .../default-field-values-failures.stderr | 2 +- 11 files changed, 50 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index b31e21f5c3568..f96c9fe8e3273 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -53,7 +53,7 @@ ast_lowering_closure_cannot_be_static = closures cannot be static ast_lowering_coroutine_too_many_parameters = too many parameters for a coroutine (expected 0 or 1 parameters) -ast_lowering_default_field_in_tuple = default field in tuple struct +ast_lowering_default_field_in_tuple = default fields are not supported in tuple structs .label = default fields are only supported on structs ast_lowering_does_not_support_modifiers = diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 688d4d635cf52..aa3b772efb159 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -557,7 +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!(default_field_values, "default values on fields are experimental"); 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"); diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 9678863ee4d8a..447cbc8932ee5 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -259,10 +259,10 @@ impl Into for AdtKind { } } -impl<'tcx> AdtDefData { +impl AdtDefData { /// Creates a new `AdtDefData`. pub(super) fn new( - tcx: TyCtxt<'tcx>, + tcx: TyCtxt<'_>, did: DefId, kind: AdtKind, variants: IndexVec, diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index b31f61a75ffe2..a3d5376dcd405 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -352,7 +352,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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 + // We desugar FRU as we lower to MIR, so for each // base-supplied field, generate an operand that // reads it from the base. itertools::zip_eq(field_names, &**field_types) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f5e1a5988649c..789d74876f722 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -752,7 +752,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r 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` {constant:#?}"); + 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); diff --git a/tests/ui/feature-gates/feature-gate-default-field-values.rs b/tests/ui/feature-gates/feature-gate-default-field-values.rs index 01441de67e0a8..d2e41a7160259 100644 --- a/tests/ui/feature-gates/feature-gate-default-field-values.rs +++ b/tests/ui/feature-gates/feature-gate-default-field-values.rs @@ -5,26 +5,26 @@ 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 + pub bar: S = S, //~ ERROR default values on fields are experimental + pub baz: i32 = 42 + 3, //~ ERROR default values on fields are experimental } #[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 + bar: S = S, //~ ERROR default values on fields are experimental + baz: i32 = 42 + 3, //~ ERROR default values on fields are experimental } } #[derive(Default)] pub struct Qux { - bar: S = Qux::::S, //~ ERROR default values on `struct` fields aren't supported - baz: i32 = foo(), //~ ERROR default values on `struct` fields aren't supported - bat: i32 = 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 = Vec::new(), //~ ERROR default values on `struct` fields aren't supported + bar: S = Qux::::S, //~ ERROR default values on fields are experimental + baz: i32 = foo(), //~ ERROR default values on fields are experimental + bat: i32 = as T>::K, //~ ERROR default values on fields are experimental + bay: i32 = C, //~ ERROR default values on fields are experimental + bak: Vec = Vec::new(), //~ ERROR default values on fields are experimental } impl Qux { @@ -46,7 +46,7 @@ const fn foo() -> i32 { #[derive(Default)] pub struct Opt { mandatory: Option<()>, - optional: () = (), //~ ERROR default values on `struct` fields aren't supported + optional: () = (), //~ ERROR default values on fields are experimental } #[derive(Default)] @@ -54,7 +54,7 @@ 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 + optional: () = (), //~ ERROR default values on fields are experimental } } diff --git a/tests/ui/feature-gates/feature-gate-default-field-values.stderr b/tests/ui/feature-gates/feature-gate-default-field-values.stderr index a24217c072769..d882c322c8eda 100644 --- a/tests/ui/feature-gates/feature-gate-default-field-values.stderr +++ b/tests/ui/feature-gates/feature-gate-default-field-values.stderr @@ -14,7 +14,7 @@ LL | Variant { | = help: consider a manual implementation of `Default` -error[E0658]: default values on `struct` fields aren't supported +error[E0658]: default values on fields are experimental --> $DIR/feature-gate-default-field-values.rs:8:15 | LL | pub bar: S = S, @@ -24,7 +24,7 @@ LL | pub bar: S = S, = 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 +error[E0658]: default values on fields are experimental --> $DIR/feature-gate-default-field-values.rs:9:17 | LL | pub baz: i32 = 42 + 3, @@ -34,7 +34,7 @@ LL | pub baz: i32 = 42 + 3, = 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 +error[E0658]: default values on fields are experimental --> $DIR/feature-gate-default-field-values.rs:16:15 | LL | bar: S = S, @@ -44,7 +44,7 @@ LL | bar: S = S, = 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 +error[E0658]: default values on fields are experimental --> $DIR/feature-gate-default-field-values.rs:17:17 | LL | baz: i32 = 42 + 3, @@ -54,7 +54,7 @@ LL | baz: i32 = 42 + 3, = 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 +error[E0658]: default values on fields are experimental --> $DIR/feature-gate-default-field-values.rs:23:11 | LL | bar: S = Qux::::S, @@ -64,7 +64,7 @@ LL | bar: S = Qux::::S, = 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 +error[E0658]: default values on fields are experimental --> $DIR/feature-gate-default-field-values.rs:24:13 | LL | baz: i32 = foo(), @@ -74,7 +74,7 @@ LL | baz: i32 = foo(), = 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 +error[E0658]: default values on fields are experimental --> $DIR/feature-gate-default-field-values.rs:25:13 | LL | bat: i32 = as T>::K, @@ -84,7 +84,7 @@ LL | bat: i32 = as T>::K, = 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 +error[E0658]: default values on fields are experimental --> $DIR/feature-gate-default-field-values.rs:26:13 | LL | bay: i32 = C, @@ -94,7 +94,7 @@ LL | bay: i32 = C, = 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 +error[E0658]: default values on fields are experimental --> $DIR/feature-gate-default-field-values.rs:27:16 | LL | bak: Vec = Vec::new(), @@ -104,7 +104,7 @@ LL | bak: Vec = Vec::new(), = 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 +error[E0658]: default values on fields are experimental --> $DIR/feature-gate-default-field-values.rs:49:17 | LL | optional: () = (), @@ -114,7 +114,7 @@ LL | optional: () = (), = 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 +error[E0658]: default values on fields are experimental --> $DIR/feature-gate-default-field-values.rs:57:21 | LL | optional: () = (), diff --git a/tests/ui/parser/struct-default-values-and-missing-field-separator.rs b/tests/ui/parser/struct-default-values-and-missing-field-separator.rs index 8ecf042ad385d..bb9de98bddbfe 100644 --- a/tests/ui/parser/struct-default-values-and-missing-field-separator.rs +++ b/tests/ui/parser/struct-default-values-and-missing-field-separator.rs @@ -5,21 +5,21 @@ enum E { } struct S { - field1: i32 = 42, //~ ERROR default values on `struct` fields aren't supported - field2: E = E::A, //~ ERROR default values on `struct` fields aren't supported - field3: i32 = 1 + 2, //~ ERROR default values on `struct` fields aren't supported - field4: i32 = { 1 + 2 }, //~ ERROR default values on `struct` fields aren't supported - field5: E = foo(42), //~ ERROR default values on `struct` fields aren't supported - field6: E = { foo(42) }, //~ ERROR default values on `struct` fields aren't supported + field1: i32 = 42, //~ ERROR default values on fields are experimental + field2: E = E::A, //~ ERROR default values on fields are experimental + field3: i32 = 1 + 2, //~ ERROR default values on fields are experimental + field4: i32 = { 1 + 2 }, //~ ERROR default values on fields are experimental + field5: E = foo(42), //~ ERROR default values on fields are experimental + field6: E = { foo(42) }, //~ ERROR default values on fields are experimental } struct S1 { field1: i32 //~ ERROR expected `,`, or `}`, found `field2` field2: E //~ ERROR expected `,`, or `}`, found `field3` - field3: i32 = 1 + 2, //~ ERROR default values on `struct` fields aren't supported - field4: i32 = { 1 + 2 }, //~ ERROR default values on `struct` fields aren't supported - field5: E = foo(42), //~ ERROR default values on `struct` fields aren't supported - field6: E = { foo(42) }, //~ ERROR default values on `struct` fields aren't supported + field3: i32 = 1 + 2, //~ ERROR default values on fields are experimental + field4: i32 = { 1 + 2 }, //~ ERROR default values on fields are experimental + field5: E = foo(42), //~ ERROR default values on fields are experimental + field6: E = { foo(42) }, //~ ERROR default values on fields are experimental } struct S2 { diff --git a/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr b/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr index 669147c5685fa..fdd9f0d6dce82 100644 --- a/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr +++ b/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr @@ -28,7 +28,7 @@ LL | field2; E, | expected `:` | help: field names and their types are separated with `:` -error[E0658]: default values on `struct` fields aren't supported +error[E0658]: default values on fields are experimental --> $DIR/struct-default-values-and-missing-field-separator.rs:8:16 | LL | field1: i32 = 42, @@ -38,7 +38,7 @@ LL | field1: i32 = 42, = 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 +error[E0658]: default values on fields are experimental --> $DIR/struct-default-values-and-missing-field-separator.rs:9:14 | LL | field2: E = E::A, @@ -48,7 +48,7 @@ LL | field2: E = E::A, = 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 +error[E0658]: default values on fields are experimental --> $DIR/struct-default-values-and-missing-field-separator.rs:10:16 | LL | field3: i32 = 1 + 2, @@ -58,7 +58,7 @@ LL | field3: i32 = 1 + 2, = 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 +error[E0658]: default values on fields are experimental --> $DIR/struct-default-values-and-missing-field-separator.rs:11:16 | LL | field4: i32 = { 1 + 2 }, @@ -68,7 +68,7 @@ LL | field4: i32 = { 1 + 2 }, = 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 +error[E0658]: default values on fields are experimental --> $DIR/struct-default-values-and-missing-field-separator.rs:12:14 | LL | field5: E = foo(42), @@ -78,7 +78,7 @@ LL | field5: E = foo(42), = 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 +error[E0658]: default values on fields are experimental --> $DIR/struct-default-values-and-missing-field-separator.rs:13:14 | LL | field6: E = { foo(42) }, @@ -88,7 +88,7 @@ LL | field6: E = { foo(42) }, = 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 +error[E0658]: default values on fields are experimental --> $DIR/struct-default-values-and-missing-field-separator.rs:19:16 | LL | field3: i32 = 1 + 2, @@ -98,7 +98,7 @@ LL | field3: i32 = 1 + 2, = 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 +error[E0658]: default values on fields are experimental --> $DIR/struct-default-values-and-missing-field-separator.rs:20:16 | LL | field4: i32 = { 1 + 2 }, @@ -108,7 +108,7 @@ LL | field4: i32 = { 1 + 2 }, = 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 +error[E0658]: default values on fields are experimental --> $DIR/struct-default-values-and-missing-field-separator.rs:21:14 | LL | field5: E = foo(42), @@ -118,7 +118,7 @@ LL | field5: E = foo(42), = 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 +error[E0658]: default values on fields are experimental --> $DIR/struct-default-values-and-missing-field-separator.rs:22:14 | LL | field6: E = { foo(42) }, diff --git a/tests/ui/structs/default-field-values-failures.rs b/tests/ui/structs/default-field-values-failures.rs index e850ad387aaf3..d67bea18c6994 100644 --- a/tests/ui/structs/default-field-values-failures.rs +++ b/tests/ui/structs/default-field-values-failures.rs @@ -23,7 +23,7 @@ pub struct Qux { bay: i32 = C, } -pub struct Rak(i32 = 42); //~ ERROR default field in tuple struct +pub struct Rak(i32 = 42); //~ ERROR default fields are not supported in tuple structs impl Qux { const S: S = S; diff --git a/tests/ui/structs/default-field-values-failures.stderr b/tests/ui/structs/default-field-values-failures.stderr index bd8d1922e9127..195ee0dbb5f1a 100644 --- a/tests/ui/structs/default-field-values-failures.stderr +++ b/tests/ui/structs/default-field-values-failures.stderr @@ -7,7 +7,7 @@ LL | bat: i32 = as T>::K, = 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: default field in tuple struct +error: default fields are not supported in tuple structs --> $DIR/default-field-values-failures.rs:26:22 | LL | pub struct Rak(i32 = 42); From 2d6e763cc68b2433b9df33da2a80dc4cf4953079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 6 Dec 2024 19:50:55 +0000 Subject: [PATCH 5/7] Disallow `#[default] Variant {}` regardless of feature flag --- compiler/rustc_builtin_macros/messages.ftl | 2 +- .../src/deriving/default.rs | 16 +++++++++++-- compiler/rustc_builtin_macros/src/errors.rs | 1 + .../structs/default-field-values-failures.rs | 6 +++++ .../default-field-values-failures.stderr | 24 ++++++++++++------- 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index c05d44cb45251..87d3d288013a3 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -261,7 +261,7 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive builtin_macros_non_generic_pointee = the `#[pointee]` attribute may only be used on generic parameters -builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants +builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants{$post} .help = consider a manual implementation of `Default` builtin_macros_only_one_argument = {$name} takes 1 argument diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 12d5587b5db84..6b1a6effad758 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -199,10 +199,17 @@ fn extract_default_variant<'a>( if cx.ecfg.features.default_field_values() && let VariantData::Struct { fields, .. } = &variant.data && fields.iter().all(|f| f.default.is_some()) + // Disallow `#[default] Variant {}` + && !fields.is_empty() { // Allowed } else if !matches!(variant.data, VariantData::Unit(..)) { - let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span }); + let post = if cx.ecfg.features.default_field_values() { + " or variants where every field has a default value" + } else { + "" + }; + let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span, post }); return Err(guar); } @@ -261,7 +268,12 @@ struct DetectNonVariantDefaultAttr<'a, 'b> { impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> { fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) { if attr.has_name(kw::Default) { - self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span }); + let post = if self.cx.ecfg.features.default_field_values() { + " or variants where every field has a default value" + } else { + "" + }; + self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span, post }); } rustc_ast::visit::walk_attribute(self, attr); diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index f8e65661e52e2..c9bd3371e559d 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -424,6 +424,7 @@ pub(crate) struct MultipleDefaultsSugg { pub(crate) struct NonUnitDefault { #[primary_span] pub(crate) span: Span, + pub(crate) post: &'static str, } #[derive(Diagnostic)] diff --git a/tests/ui/structs/default-field-values-failures.rs b/tests/ui/structs/default-field-values-failures.rs index d67bea18c6994..0ac071d91d65e 100644 --- a/tests/ui/structs/default-field-values-failures.rs +++ b/tests/ui/structs/default-field-values-failures.rs @@ -41,6 +41,12 @@ const fn foo() -> i32 { 42 } +#[derive(Debug, Default)] +enum E { + #[default] + Variant {} //~ ERROR the `#[default]` attribute may only be used on unit enum variants +} + fn main () { let _ = Foo { .. }; // ok let _ = Foo::default(); // ok diff --git a/tests/ui/structs/default-field-values-failures.stderr b/tests/ui/structs/default-field-values-failures.stderr index 195ee0dbb5f1a..e60ec392fdd6d 100644 --- a/tests/ui/structs/default-field-values-failures.stderr +++ b/tests/ui/structs/default-field-values-failures.stderr @@ -1,3 +1,11 @@ +error: the `#[default]` attribute may only be used on unit enum variants or variants where every field has a default value + --> $DIR/default-field-values-failures.rs:47:5 + | +LL | Variant {} + | ^^^^^^^ + | + = help: consider a manual implementation of `Default` + error: generic parameters may not be used in const operations --> $DIR/default-field-values-failures.rs:22:23 | @@ -30,13 +38,13 @@ LL | pub struct S; | error: missing mandatory field `bar` - --> $DIR/default-field-values-failures.rs:47:21 + --> $DIR/default-field-values-failures.rs:53:21 | LL | let _ = Bar { .. }; | ^ error[E0308]: mismatched types - --> $DIR/default-field-values-failures.rs:51:17 + --> $DIR/default-field-values-failures.rs:57:17 | LL | let _ = Rak(..); | --- ^^ expected `i32`, found `RangeFull` @@ -49,19 +57,19 @@ note: tuple struct defined here LL | pub struct Rak(i32 = 42); | ^^^ help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/default-field-values-failures.rs:51:17 + --> $DIR/default-field-values-failures.rs:57:17 | LL | let _ = Rak(..); | ^^ error[E0061]: this struct takes 1 argument but 2 arguments were supplied - --> $DIR/default-field-values-failures.rs:53:13 + --> $DIR/default-field-values-failures.rs:59:13 | LL | let _ = Rak(0, ..); | ^^^ -- unexpected argument #2 of type `RangeFull` | help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/default-field-values-failures.rs:53:20 + --> $DIR/default-field-values-failures.rs:59:20 | LL | let _ = Rak(0, ..); | ^^ @@ -77,13 +85,13 @@ LL + let _ = Rak(0); | error[E0061]: this struct takes 1 argument but 2 arguments were supplied - --> $DIR/default-field-values-failures.rs:55:13 + --> $DIR/default-field-values-failures.rs:61:13 | LL | let _ = Rak(.., 0); | ^^^ -- unexpected argument #1 of type `RangeFull` | help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/default-field-values-failures.rs:55:17 + --> $DIR/default-field-values-failures.rs:61:17 | LL | let _ = Rak(.., 0); | ^^ @@ -104,7 +112,7 @@ error: generic `Self` types are currently not permitted in anonymous constants LL | bar: S = Self::S, | ^^^^ -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0061, E0277, E0308. For more information about an error, try `rustc --explain E0061`. From 0757641f4d89418091a6381a4ad6426769ee0fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 6 Dec 2024 23:08:17 +0000 Subject: [PATCH 6/7] Unconditionally error at definition if default field value has const errors Emit E0080 always on struct definition with default fields that have unconditional const errors and remove `default_field_always_invalid_const` lint. --- .../rustc_hir_analysis/src/check/wfcheck.rs | 19 ++++ compiler/rustc_lint/messages.ftl | 4 - .../src/default_field_always_invalid.rs | 91 ------------------- compiler/rustc_lint/src/lib.rs | 3 - compiler/rustc_lint/src/lints.rs | 9 -- .../default-field-values-failures.stderr | 12 +-- .../default-field-values-invalid-const.rs | 3 - .../default-field-values-invalid-const.stderr | 36 +------- 8 files changed, 29 insertions(+), 148 deletions(-) delete mode 100644 compiler/rustc_lint/src/default_field_always_invalid.rs diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 2b33da3c49a07..c9773972d9a24 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1104,6 +1104,25 @@ fn check_type_defn<'tcx>( for variant in variants.iter() { // All field types must be well-formed. for field in &variant.fields { + if let Some(def_id) = field.value + && let Some(_ty) = tcx.type_of(def_id).no_bound_vars() + { + // FIXME(generic_const_exprs, default_field_values): this is a hack and needs to + // be refactored to check the instantiate-ability of the code better. + if let Some(def_id) = def_id.as_local() + && let hir::Node::AnonConst(anon) = tcx.hir_node_by_def_id(def_id) + && let expr = &tcx.hir().body(anon.body).value + && let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let Res::Def(DefKind::ConstParam, _def_id) = path.res + { + // Do not evaluate bare `const` params, as those would ICE and are only + // usable if `#![feature(generic_const_exprs)]` is enabled. + } else { + // Evaluate the constant proactively, to emit an error if the constant has + // an unconditional error. We only do so if the const has no type params. + let _ = tcx.const_eval_poly(def_id.into()); + } + } let field_id = field.did.expect_local(); let hir::FieldDef { ty: hir_ty, .. } = tcx.hir_node_by_def_id(field_id).expect_field(); diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index f5d2ebc3e87e0..422629cd11d08 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -211,10 +211,6 @@ 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 -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 diff --git a/compiler/rustc_lint/src/default_field_always_invalid.rs b/compiler/rustc_lint/src/default_field_always_invalid.rs deleted file mode 100644 index 46cffb53b4b63..0000000000000 --- a/compiler/rustc_lint/src/default_field_always_invalid.rs +++ /dev/null @@ -1,91 +0,0 @@ -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: () }, - ); - } - } - } -} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index baf2703511e2e..a99c94592b302 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -41,7 +41,6 @@ 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; @@ -86,7 +85,6 @@ 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; @@ -195,7 +193,6 @@ late_lint_methods!( DropForgetUseless: DropForgetUseless, ImproperCTypesDeclarations: ImproperCTypesDeclarations, ImproperCTypesDefinitions: ImproperCTypesDefinitions, - DefaultFieldAlwaysInvalid: DefaultFieldAlwaysInvalid, InvalidFromUtf8: InvalidFromUtf8, VariantSizeDifferences: VariantSizeDifferences, PathStatements: PathStatements, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 07f1c48bafb57..9fa263799ebf1 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -730,15 +730,6 @@ 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 { diff --git a/tests/ui/structs/default-field-values-failures.stderr b/tests/ui/structs/default-field-values-failures.stderr index e60ec392fdd6d..5b9d2df5a5d73 100644 --- a/tests/ui/structs/default-field-values-failures.stderr +++ b/tests/ui/structs/default-field-values-failures.stderr @@ -21,6 +21,12 @@ error: default fields are not supported in tuple structs LL | pub struct Rak(i32 = 42); | ^^ default fields are only supported on structs +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[E0277]: the trait bound `S: Default` is not satisfied --> $DIR/default-field-values-failures.rs:14:5 | @@ -106,12 +112,6 @@ LL - let _ = Rak(.., 0); LL + let _ = Rak(0); | -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 9 previous errors Some errors have detailed explanations: E0061, E0277, E0308. diff --git a/tests/ui/structs/default-field-values-invalid-const.rs b/tests/ui/structs/default-field-values-invalid-const.rs index 6838238064be5..203a712868b7e 100644 --- a/tests/ui/structs/default-field-values-invalid-const.rs +++ b/tests/ui/structs/default-field-values-invalid-const.rs @@ -1,18 +1,15 @@ #![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 { pub bax: u8 = 130 + C, // ok pub bat: u8 = 130 + 130, //~^ ERROR evaluation of `Baz::::bat::{constant#0}` failed - //~| ERROR default field fails const-evaluation pub bay: u8 = 1, // ok } diff --git a/tests/ui/structs/default-field-values-invalid-const.stderr b/tests/ui/structs/default-field-values-invalid-const.stderr index a0d84271f4bc9..47f25a1f38e3f 100644 --- a/tests/ui/structs/default-field-values-invalid-const.stderr +++ b/tests/ui/structs/default-field-values-invalid-const.stderr @@ -1,45 +1,17 @@ -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 + --> $DIR/default-field-values-invalid-const.rs:5:19 | LL | pub bax: u8 = panic!("asdf"), - | ^^^^^^^^^^^^^^ the evaluated program panicked at 'asdf', $DIR/default-field-values-invalid-const.rs:6:19 + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'asdf', $DIR/default-field-values-invalid-const.rs:5: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::::bat::{constant#0}` failed - --> $DIR/default-field-values-invalid-const.rs:13:19 + --> $DIR/default-field-values-invalid-const.rs:11: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 +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. From fa331f4d0d2d7657c76972574868c5c00afae459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 7 Dec 2024 03:04:51 +0000 Subject: [PATCH 7/7] Support x-crate default fields --- compiler/rustc_metadata/src/rmeta/decoder.rs | 6 +++++- compiler/rustc_metadata/src/rmeta/encoder.rs | 7 +++++++ compiler/rustc_metadata/src/rmeta/mod.rs | 1 + tests/ui/structs/auxiliary/struct_field_default.rs | 5 +++++ tests/ui/structs/default-field-values-support.rs | 6 ++++++ 5 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 tests/ui/structs/auxiliary/struct_field_default.rs diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 4a4930fff9d53..b9586338655e7 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1104,7 +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, + value: self.get_default_field(did.index), }) .collect(), adt_kind, @@ -1170,6 +1170,10 @@ impl<'a> CrateMetadataRef<'a> { self.root.tables.safety.get(self, id).unwrap_or_else(|| self.missing("safety", id)) } + fn get_default_field(self, id: DefIndex) -> Option { + self.root.tables.default_fields.get(self, id).map(|d| d.decode(self)) + } + fn get_trait_item_def_id(self, id: DefIndex) -> Option { self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode_from_cdata(self)) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index d4ea1276d0027..5c80d24f502d1 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1401,6 +1401,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { continue; } + if def_kind == DefKind::Field + && let hir::Node::Field(field) = tcx.hir_node_by_def_id(local_id) + && let Some(anon) = field.default + { + record!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id()); + } + if should_encode_span(def_kind) { let def_span = tcx.def_span(local_id); record!(self.tables.def_span[def_id] <- def_span); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a5e21ab51fd82..4961464833af0 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -450,6 +450,7 @@ define_tables! { trait_def: Table>, trait_item_def_id: Table, expn_that_defined: Table>, + default_fields: Table>, params_in_repr: Table>>, repr_options: Table>, // `def_keys` and `def_path_hashes` represent a lazy version of a diff --git a/tests/ui/structs/auxiliary/struct_field_default.rs b/tests/ui/structs/auxiliary/struct_field_default.rs new file mode 100644 index 0000000000000..b315df5dba287 --- /dev/null +++ b/tests/ui/structs/auxiliary/struct_field_default.rs @@ -0,0 +1,5 @@ +#![feature(default_field_values)] + +pub struct A { + pub a: isize = 42, +} diff --git a/tests/ui/structs/default-field-values-support.rs b/tests/ui/structs/default-field-values-support.rs index bdf21e1272d87..da0379af94b8f 100644 --- a/tests/ui/structs/default-field-values-support.rs +++ b/tests/ui/structs/default-field-values-support.rs @@ -1,7 +1,10 @@ //@ run-pass +//@ aux-build:struct_field_default.rs #![feature(default_field_values, generic_const_exprs)] #![allow(unused_variables, dead_code, incomplete_features)] +extern crate struct_field_default as xc; + pub struct S; #[derive(Default)] @@ -65,4 +68,7 @@ fn main () { let x = Qux:: { .. }; assert!(matches!(Qux:: { bar: S, baz: 42, bat: 2, baq: 2, bay: 4, .. }, x)); assert!(x.bak.is_empty()); + + let x = xc::A { .. }; + assert!(matches!(xc::A { a: 42 }, x)); }