Introduce default_field_values
feature
Initial implementation of `#[feature(default_field_values]`, proposed in https://github.com/rust-lang/rfcs/pull/3681. Support default fields in enum struct variant Allow default values in an enum struct variant definition: ```rust pub enum Bar { Foo { bar: S = S, baz: i32 = 42 + 3, } } ``` Allow using `..` without a base on an enum struct variant ```rust Bar::Foo { .. } ``` `#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants. Support `#[derive(Default)]` on enum struct variants with all defaulted fields ```rust pub enum Bar { #[default] Foo { bar: S = S, baz: i32 = 42 + 3, } } ``` Check for missing fields in typeck instead of mir_build. Expand test with `const` param case (needs `generic_const_exprs` enabled). Properly instantiate MIR const The following works: ```rust struct S<A> { a: Vec<A> = Vec::new(), } S::<i32> { .. } ``` Add lint for default fields that will always fail const-eval We *allow* this to happen for API writers that might want to rely on users' getting a compile error when using the default field, different to the error that they would get when the field isn't default. We could change this to *always* error instead of being a lint, if we wanted. This will *not* catch errors for partially evaluated consts, like when the expression relies on a const parameter. Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`: - Suggest adding a base expression if there are missing fields. - Suggest enabling the feature if all the missing fields have optional values. - Suggest removing `..` if there are no missing fields.
This commit is contained in:
parent
f6cb952dc1
commit
9ac95c10c0
70 changed files with 1469 additions and 392 deletions
|
@ -3119,6 +3119,7 @@ pub struct FieldDef {
|
|||
pub ident: Option<Ident>,
|
||||
|
||||
pub ty: P<Ty>,
|
||||
pub default: Option<AnonConst>,
|
||||
pub is_placeholder: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -1120,13 +1120,14 @@ fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) {
|
|||
}
|
||||
|
||||
pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) {
|
||||
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety } = fd;
|
||||
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety, default } = fd;
|
||||
visitor.visit_id(id);
|
||||
visit_attrs(visitor, attrs);
|
||||
visitor.visit_vis(vis);
|
||||
visit_safety(visitor, safety);
|
||||
visit_opt(ident, |ident| visitor.visit_ident(ident));
|
||||
visitor.visit_ty(ty);
|
||||
visit_opt(default, |default| visitor.visit_anon_const(default));
|
||||
visitor.visit_span(span);
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -1151,7 +1151,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
expr: &hir::Expr<'_>,
|
||||
) {
|
||||
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
|
||||
let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return };
|
||||
let hir::ExprKind::Struct(struct_qpath, fields, hir::StructTailExpr::Base(base)) =
|
||||
expr.kind
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let hir::QPath::Resolved(_, path) = struct_qpath else { return };
|
||||
let hir::def::Res::Def(_, def_id) = path.res else { return };
|
||||
let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return };
|
||||
|
@ -1239,7 +1243,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
expr: &'tcx hir::Expr<'tcx>,
|
||||
use_spans: Option<UseSpans<'tcx>>,
|
||||
) {
|
||||
if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind {
|
||||
if let hir::ExprKind::Struct(_, _, hir::StructTailExpr::Base(_)) = expr.kind {
|
||||
// We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single
|
||||
// `Location` that covers both the `S { ... }` literal, all of its fields and the
|
||||
// `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr`
|
||||
|
|
|
@ -205,7 +205,7 @@ where
|
|||
let fields = fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, &(ident, span))| {
|
||||
.map(|(i, &(ident, span, _))| {
|
||||
let arg = getarg(cx, span, ident.name, i);
|
||||
cx.field_imm(span, ident, arg)
|
||||
})
|
||||
|
|
|
@ -54,26 +54,38 @@ pub(crate) fn expand_deriving_default(
|
|||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
fn default_call(cx: &ExtCtxt<'_>, span: Span) -> ast::ptr::P<ast::Expr> {
|
||||
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
|
||||
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
|
||||
cx.expr_call_global(span, default_ident, ThinVec::new())
|
||||
}
|
||||
|
||||
fn default_struct_substructure(
|
||||
cx: &ExtCtxt<'_>,
|
||||
trait_span: Span,
|
||||
substr: &Substructure<'_>,
|
||||
summary: &StaticFields,
|
||||
) -> BlockOrExpr {
|
||||
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
|
||||
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
|
||||
let default_call = |span| cx.expr_call_global(span, default_ident.clone(), ThinVec::new());
|
||||
|
||||
let expr = match summary {
|
||||
Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident),
|
||||
Unnamed(fields, IsTuple::Yes) => {
|
||||
let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
|
||||
let exprs = fields.iter().map(|sp| default_call(cx, *sp)).collect();
|
||||
cx.expr_call_ident(trait_span, substr.type_ident, exprs)
|
||||
}
|
||||
Named(fields) => {
|
||||
let default_fields = fields
|
||||
.iter()
|
||||
.map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
|
||||
.map(|(ident, span, default_val)| {
|
||||
let value = match default_val {
|
||||
// We use `Default::default()`.
|
||||
None => default_call(cx, *span),
|
||||
// We use the field default const expression.
|
||||
Some(val) => {
|
||||
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
|
||||
}
|
||||
};
|
||||
cx.field_imm(*span, *ident, value)
|
||||
})
|
||||
.collect();
|
||||
cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
|
||||
}
|
||||
|
@ -93,10 +105,38 @@ fn default_enum_substructure(
|
|||
} {
|
||||
Ok(default_variant) => {
|
||||
// We now know there is exactly one unit variant with exactly one `#[default]` attribute.
|
||||
cx.expr_path(cx.path(default_variant.span, vec![
|
||||
Ident::new(kw::SelfUpper, default_variant.span),
|
||||
default_variant.ident,
|
||||
]))
|
||||
match &default_variant.data {
|
||||
VariantData::Unit(_) => cx.expr_path(cx.path(default_variant.span, vec![
|
||||
Ident::new(kw::SelfUpper, default_variant.span),
|
||||
default_variant.ident,
|
||||
])),
|
||||
VariantData::Struct { fields, .. } => {
|
||||
// This only happens if `#![feature(default_field_values)]`. We have validated
|
||||
// all fields have default values in the definition.
|
||||
let default_fields = fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
cx.field_imm(field.span, field.ident.unwrap(), match &field.default {
|
||||
// We use `Default::default()`.
|
||||
None => default_call(cx, field.span),
|
||||
// We use the field default const expression.
|
||||
Some(val) => {
|
||||
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let path = cx.path(default_variant.span, vec![
|
||||
Ident::new(kw::SelfUpper, default_variant.span),
|
||||
default_variant.ident,
|
||||
]);
|
||||
cx.expr_struct(default_variant.span, path, default_fields)
|
||||
}
|
||||
// Logic error in `extract_default_variant`.
|
||||
VariantData::Tuple(..) => {
|
||||
cx.dcx().bug("encountered tuple variant annotated with `#[default]`")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
|
||||
};
|
||||
|
@ -156,7 +196,12 @@ fn extract_default_variant<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
if !matches!(variant.data, VariantData::Unit(..)) {
|
||||
if cx.ecfg.features.default_field_values()
|
||||
&& let VariantData::Struct { fields, .. } = &variant.data
|
||||
&& fields.iter().all(|f| f.default.is_some())
|
||||
{
|
||||
// Allowed
|
||||
} else if !matches!(variant.data, VariantData::Unit(..)) {
|
||||
let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span });
|
||||
return Err(guar);
|
||||
}
|
||||
|
|
|
@ -182,8 +182,8 @@ pub(crate) use StaticFields::*;
|
|||
pub(crate) use SubstructureFields::*;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::{
|
||||
self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics,
|
||||
Mutability, PatKind, VariantData,
|
||||
self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
|
||||
Generics, Mutability, PatKind, VariantData,
|
||||
};
|
||||
use rustc_attr as attr;
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
|
@ -296,7 +296,7 @@ pub(crate) enum StaticFields {
|
|||
/// Tuple and unit structs/enum variants like this.
|
||||
Unnamed(Vec<Span>, IsTuple),
|
||||
/// Normal structs/struct variants.
|
||||
Named(Vec<(Ident, Span)>),
|
||||
Named(Vec<(Ident, Span, Option<AnonConst>)>),
|
||||
}
|
||||
|
||||
/// A summary of the possible sets of fields.
|
||||
|
@ -1435,7 +1435,7 @@ impl<'a> TraitDef<'a> {
|
|||
for field in struct_def.fields() {
|
||||
let sp = field.span.with_ctxt(self.span.ctxt());
|
||||
match field.ident {
|
||||
Some(ident) => named_idents.push((ident, sp)),
|
||||
Some(ident) => named_idents.push((ident, sp, field.default.clone())),
|
||||
_ => just_spans.push(sp),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1857,7 +1857,12 @@ impl Expr<'_> {
|
|||
base.can_have_side_effects()
|
||||
}
|
||||
ExprKind::Struct(_, fields, init) => {
|
||||
fields.iter().map(|field| field.expr).chain(init).any(|e| e.can_have_side_effects())
|
||||
let init_side_effects = match init {
|
||||
StructTailExpr::Base(init) => init.can_have_side_effects(),
|
||||
StructTailExpr::DefaultFields(_) | StructTailExpr::None => false,
|
||||
};
|
||||
fields.iter().map(|field| field.expr).any(|e| e.can_have_side_effects())
|
||||
|| init_side_effects
|
||||
}
|
||||
|
||||
ExprKind::Array(args)
|
||||
|
@ -1926,20 +1931,52 @@ impl Expr<'_> {
|
|||
ExprKind::Path(QPath::Resolved(None, path2)),
|
||||
) => path1.res == path2.res,
|
||||
(
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val1], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val2], None),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeTo, _),
|
||||
[val1],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeTo, _),
|
||||
[val2],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
)
|
||||
| (
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val1], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val2], None),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeToInclusive, _),
|
||||
[val1],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeToInclusive, _),
|
||||
[val2],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
)
|
||||
| (
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val1], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val2], None),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeFrom, _),
|
||||
[val1],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeFrom, _),
|
||||
[val2],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
) => val1.expr.equivalent_for_indexing(val2.expr),
|
||||
(
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val1, val3], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val2, val4], None),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::Range, _),
|
||||
[val1, val3],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::Range, _),
|
||||
[val2, val4],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
) => {
|
||||
val1.expr.equivalent_for_indexing(val2.expr)
|
||||
&& val3.expr.equivalent_for_indexing(val4.expr)
|
||||
|
@ -2096,7 +2133,7 @@ pub enum ExprKind<'hir> {
|
|||
///
|
||||
/// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`,
|
||||
/// where `base` is the `Option<Expr>`.
|
||||
Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], Option<&'hir Expr<'hir>>),
|
||||
Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], StructTailExpr<'hir>),
|
||||
|
||||
/// An array literal constructed from one repeated element.
|
||||
///
|
||||
|
@ -2111,6 +2148,19 @@ pub enum ExprKind<'hir> {
|
|||
Err(rustc_span::ErrorGuaranteed),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, HashStable_Generic)]
|
||||
pub enum StructTailExpr<'hir> {
|
||||
/// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`.
|
||||
None,
|
||||
/// A struct expression with a "base", an expression of the same type as the outer struct that
|
||||
/// will be used to populate any fields not explicitly mentioned: `Foo { ..base }`
|
||||
Base(&'hir Expr<'hir>),
|
||||
/// A struct expression with a `..` tail but no "base" expression. The values from the struct
|
||||
/// fields' default values will be used to populate any fields not explicitly mentioned:
|
||||
/// `Foo { .. }`.
|
||||
DefaultFields(Span),
|
||||
}
|
||||
|
||||
/// Represents an optionally `Self`-qualified value/type path or associated extension.
|
||||
///
|
||||
/// To resolve the path to a `DefId`, call [`qpath_res`].
|
||||
|
@ -3172,6 +3222,7 @@ pub struct FieldDef<'hir> {
|
|||
pub def_id: LocalDefId,
|
||||
pub ty: &'hir Ty<'hir>,
|
||||
pub safety: Safety,
|
||||
pub default: Option<&'hir AnonConst>,
|
||||
}
|
||||
|
||||
impl FieldDef<'_> {
|
||||
|
|
|
@ -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>>(
|
||||
|
|
|
@ -1021,12 +1021,12 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_variant(
|
||||
tcx: TyCtxt<'_>,
|
||||
fn lower_variant<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
variant_did: Option<LocalDefId>,
|
||||
ident: Ident,
|
||||
discr: ty::VariantDiscr,
|
||||
def: &hir::VariantData<'_>,
|
||||
def: &hir::VariantData<'tcx>,
|
||||
adt_kind: ty::AdtKind,
|
||||
parent_did: LocalDefId,
|
||||
) -> ty::VariantDef {
|
||||
|
@ -1042,6 +1042,7 @@ fn lower_variant(
|
|||
name: f.ident.name,
|
||||
vis: tcx.visibility(f.def_id),
|
||||
safety: f.safety,
|
||||
value: f.default.map(|v| v.def_id.to_def_id()),
|
||||
})
|
||||
.collect();
|
||||
let recovered = match def {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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("}");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -15,6 +15,47 @@ use rustc_span::{Span, Symbol};
|
|||
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_typeck_base_expression_double_dot, code = E0797)]
|
||||
pub(crate) struct BaseExpressionDoubleDot {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub default_field_values: Option<BaseExpressionDoubleDotEnableDefaultFieldValues>,
|
||||
#[subdiagnostic]
|
||||
pub add_expr: Option<BaseExpressionDoubleDotAddExpr>,
|
||||
#[subdiagnostic]
|
||||
pub remove_dots: Option<BaseExpressionDoubleDotRemove>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(
|
||||
hir_typeck_base_expression_double_dot_remove,
|
||||
code = "",
|
||||
applicability = "machine-applicable",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub(crate) struct BaseExpressionDoubleDotRemove {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(
|
||||
hir_typeck_base_expression_double_dot_add_expr,
|
||||
code = "/* expr */",
|
||||
applicability = "has-placeholders",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub(crate) struct BaseExpressionDoubleDotAddExpr {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[help(hir_typeck_base_expression_double_dot_enable_default_field_values)]
|
||||
pub(crate) struct BaseExpressionDoubleDotEnableDefaultFieldValues;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)]
|
||||
pub(crate) struct FieldMultiplySpecifiedInInitializer {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -211,6 +211,10 @@ lint_dangling_pointers_from_temporaries = a dangling pointer will be produced be
|
|||
.note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
|
||||
.help = for more information, see <https://doc.rust-lang.org/reference/destructors.html>
|
||||
|
||||
lint_default_field_always_invalid_const = default field fails const-evaluation
|
||||
.label = this field's constant fails const-evaluation, as seen in the previous error
|
||||
.help = you can skip const-evaluation of default fields by enabling this lint
|
||||
|
||||
lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance
|
||||
.note = a `use rustc_data_structures::fx::{$preferred}` may be necessary
|
||||
|
||||
|
|
91
compiler/rustc_lint/src/default_field_always_invalid.rs
Normal file
91
compiler/rustc_lint/src/default_field_always_invalid.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
use rustc_hir as hir;
|
||||
use rustc_middle::lint::LintLevelSource;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_session::lint::Level;
|
||||
use rustc_session::{declare_lint, declare_lint_pass};
|
||||
|
||||
use crate::lints::DefaultFieldAlwaysInvalidConst;
|
||||
use crate::{LateContext, LateLintPass};
|
||||
|
||||
declare_lint! {
|
||||
/// The `default_field_always_invalid_const` lint checks for structs with
|
||||
/// default fields const values that will *always* fail to be created.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![feature(default_field_values)]
|
||||
/// #[deny(default_field_always_invalid_const)]
|
||||
/// struct Foo {
|
||||
/// bar: u8 = 130 + 130, // `260` doesn't fit in `u8`
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Without this lint, the error would only happen only during construction
|
||||
/// of the affected type. For example, given the type above, `Foo { .. }`
|
||||
/// would always fail to build, but `Foo { bar: 0 }` would be accepted. This
|
||||
/// lint will catch accidental cases of const values that would fail to
|
||||
/// compile, but won't detect cases that are only partially evaluated.
|
||||
pub DEFAULT_FIELD_ALWAYS_INVALID_CONST,
|
||||
Deny,
|
||||
"using this default field will always fail to compile"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DefaultFieldAlwaysInvalid => [DEFAULT_FIELD_ALWAYS_INVALID_CONST]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DefaultFieldAlwaysInvalid {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
|
||||
let data = match item.kind {
|
||||
hir::ItemKind::Struct(data, _generics) => data,
|
||||
_ => return,
|
||||
};
|
||||
let hir::VariantData::Struct { fields, recovered: _ } = data else {
|
||||
return;
|
||||
};
|
||||
|
||||
let (level, source) =
|
||||
cx.tcx.lint_level_at_node(DEFAULT_FIELD_ALWAYS_INVALID_CONST, item.hir_id());
|
||||
match level {
|
||||
Level::Deny | Level::Forbid => {}
|
||||
Level::Warn | Level::ForceWarn(_) | Level::Expect(_) => {
|
||||
// We *can't* turn the const eval error into a warning, so we make it a
|
||||
// warning to not use `#[warn(default_field_always_invalid_const)]`.
|
||||
let invalid_msg = "lint `default_field_always_invalid_const` can't be warned on";
|
||||
#[allow(rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic)]
|
||||
if let LintLevelSource::Node { span, .. } = source {
|
||||
let mut err = cx.tcx.sess.dcx().struct_span_warn(span, invalid_msg);
|
||||
err.span_label(
|
||||
span,
|
||||
"either `deny` or `allow`, no other lint level is supported for this lint",
|
||||
);
|
||||
err.emit();
|
||||
} else {
|
||||
cx.tcx.sess.dcx().warn(invalid_msg);
|
||||
}
|
||||
}
|
||||
Level::Allow => {
|
||||
// We don't even look at the fields.
|
||||
return;
|
||||
}
|
||||
}
|
||||
for field in fields {
|
||||
if let Some(c) = field.default
|
||||
&& let Some(_ty) = cx.tcx.type_of(c.def_id).no_bound_vars()
|
||||
&& let Err(ErrorHandled::Reported(_, _)) = cx.tcx.const_eval_poly(c.def_id.into())
|
||||
{
|
||||
// We use the item's hir id because the const's hir id might resolve inside of a
|
||||
// foreign macro, meaning the lint won't trigger.
|
||||
cx.tcx.emit_node_span_lint(
|
||||
DEFAULT_FIELD_ALWAYS_INVALID_CONST,
|
||||
item.hir_id(),
|
||||
field.span,
|
||||
DefaultFieldAlwaysInvalidConst { span: field.span, help: () },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -158,8 +158,21 @@ pub struct AdtExpr<'tcx> {
|
|||
pub user_ty: UserTy<'tcx>,
|
||||
|
||||
pub fields: Box<[FieldExpr]>,
|
||||
/// The base, e.g. `Foo {x: 1, .. base}`.
|
||||
pub base: Option<FruInfo<'tcx>>,
|
||||
/// The base, e.g. `Foo {x: 1, ..base}`.
|
||||
pub base: AdtExprBase<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable)]
|
||||
pub enum AdtExprBase<'tcx> {
|
||||
/// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`.
|
||||
None,
|
||||
/// A struct expression with a "base", an expression of the same type as the outer struct that
|
||||
/// will be used to populate any fields not explicitly mentioned: `Foo { ..base }`
|
||||
Base(FruInfo<'tcx>),
|
||||
/// A struct expression with a `..` tail but no "base" expression. The values from the struct
|
||||
/// fields' default values will be used to populate any fields not explicitly mentioned:
|
||||
/// `Foo { .. }`.
|
||||
DefaultFields(Box<[Ty<'tcx>]>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable)]
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -259,10 +259,10 @@ impl Into<DataTypeKind> for AdtKind {
|
|||
}
|
||||
}
|
||||
|
||||
impl AdtDefData {
|
||||
impl<'tcx> AdtDefData {
|
||||
/// Creates a new `AdtDefData`.
|
||||
pub(super) fn new(
|
||||
tcx: TyCtxt<'_>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
did: DefId,
|
||||
kind: AdtKind,
|
||||
variants: IndexVec<VariantIdx, VariantDef>,
|
||||
|
|
|
@ -1364,6 +1364,7 @@ pub struct FieldDef {
|
|||
pub name: Symbol,
|
||||
pub vis: Visibility<DefId>,
|
||||
pub safety: hir::Safety,
|
||||
pub value: Option<DefId>,
|
||||
}
|
||||
|
||||
impl PartialEq for FieldDef {
|
||||
|
@ -1376,9 +1377,9 @@ impl PartialEq for FieldDef {
|
|||
// of `FieldDef` changes, a compile-error will be produced, reminding
|
||||
// us to revisit this assumption.
|
||||
|
||||
let Self { did: lhs_did, name: _, vis: _, safety: _ } = &self;
|
||||
let Self { did: lhs_did, name: _, vis: _, safety: _, value: _ } = &self;
|
||||
|
||||
let Self { did: rhs_did, name: _, vis: _, safety: _ } = other;
|
||||
let Self { did: rhs_did, name: _, vis: _, safety: _, value: _ } = other;
|
||||
|
||||
let res = lhs_did == rhs_did;
|
||||
|
||||
|
@ -1405,7 +1406,7 @@ impl Hash for FieldDef {
|
|||
// of `FieldDef` changes, a compile-error will be produced, reminding
|
||||
// us to revisit this assumption.
|
||||
|
||||
let Self { did, name: _, vis: _, safety: _ } = &self;
|
||||
let Self { did, name: _, vis: _, safety: _, value: _ } = &self;
|
||||
|
||||
did.hash(s)
|
||||
}
|
||||
|
|
|
@ -283,7 +283,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
|
|||
fields.iter().map(|e| self.parse_operand(*e)).collect::<Result<_, _>>()?
|
||||
))
|
||||
},
|
||||
ExprKind::Adt(box AdtExpr{ adt_def, variant_index, args, fields, .. }) => {
|
||||
ExprKind::Adt(box AdtExpr { adt_def, variant_index, args, fields, .. }) => {
|
||||
let is_union = adt_def.is_union();
|
||||
let active_field_index = is_union.then(|| fields[0].name);
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
//! See docs in build/expr/mod.rs
|
||||
|
||||
use std::iter;
|
||||
|
||||
use rustc_ast::{AsmMacro, InlineAsmOptions};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
|
@ -344,25 +342,51 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let field_names = adt_def.variant(variant_index).fields.indices();
|
||||
let variant = adt_def.variant(variant_index);
|
||||
let field_names = variant.fields.indices();
|
||||
|
||||
let fields = if let Some(FruInfo { base, field_types }) = base {
|
||||
let place_builder = unpack!(block = this.as_place_builder(block, *base));
|
||||
let fields = match base {
|
||||
AdtExprBase::None => {
|
||||
field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
|
||||
}
|
||||
AdtExprBase::Base(FruInfo { base, field_types }) => {
|
||||
let place_builder = unpack!(block = this.as_place_builder(block, *base));
|
||||
|
||||
// MIR does not natively support FRU, so for each
|
||||
// base-supplied field, generate an operand that
|
||||
// reads it from the base.
|
||||
iter::zip(field_names, &**field_types)
|
||||
.map(|(n, ty)| match fields_map.get(&n) {
|
||||
Some(v) => v.clone(),
|
||||
None => {
|
||||
let place = place_builder.clone_project(PlaceElem::Field(n, *ty));
|
||||
this.consume_by_copy_or_move(place.to_place(this))
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
|
||||
// MIR does not natively support FRU, so for each
|
||||
// base-supplied field, generate an operand that
|
||||
// reads it from the base.
|
||||
itertools::zip_eq(field_names, &**field_types)
|
||||
.map(|(n, ty)| match fields_map.get(&n) {
|
||||
Some(v) => v.clone(),
|
||||
None => {
|
||||
let place =
|
||||
place_builder.clone_project(PlaceElem::Field(n, *ty));
|
||||
this.consume_by_copy_or_move(place.to_place(this))
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
AdtExprBase::DefaultFields(field_types) => {
|
||||
itertools::zip_eq(field_names, &**field_types)
|
||||
.map(|(n, ty)| match fields_map.get(&n) {
|
||||
Some(v) => v.clone(),
|
||||
None => match variant.fields[n].value {
|
||||
Some(def) => {
|
||||
let value = Const::from_unevaluated(this.tcx, def)
|
||||
.instantiate(this.tcx, args);
|
||||
this.literal_operand(expr_span, value)
|
||||
}
|
||||
None => {
|
||||
let name = variant.fields[n].name;
|
||||
span_bug!(
|
||||
expr_span,
|
||||
"missing mandatory field `{name}` of type `{ty}`",
|
||||
);
|
||||
}
|
||||
},
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
|
||||
let inferred_ty = expr.ty;
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -8,7 +8,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{
|
||||
BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, Stmt, StmtKind,
|
||||
UnsafeSource, is_range_literal,
|
||||
UnsafeSource, StructTailExpr, is_range_literal,
|
||||
};
|
||||
use rustc_infer::infer::TyCtxtInferExt as _;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
@ -238,7 +238,10 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
ExprKind::Struct(_, fields, ref base) => {
|
||||
!has_drop(cx, cx.typeck_results().expr_ty(expr))
|
||||
&& fields.iter().all(|field| has_no_effect(cx, field.expr))
|
||||
&& base.as_ref().is_none_or(|base| has_no_effect(cx, base))
|
||||
&& match &base {
|
||||
StructTailExpr::None | StructTailExpr::DefaultFields(_) => true,
|
||||
StructTailExpr::Base(base) => has_no_effect(cx, base),
|
||||
}
|
||||
},
|
||||
ExprKind::Call(callee, args) => {
|
||||
if let ExprKind::Path(ref qpath) = callee.kind {
|
||||
|
@ -342,6 +345,10 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
|
|||
if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
|
||||
None
|
||||
} else {
|
||||
let base = match base {
|
||||
StructTailExpr::Base(base) => Some(base),
|
||||
StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
|
||||
};
|
||||
Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
},
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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.
|
||||
|
|
106
tests/ui/feature-gates/feature-gate-default-field-values.rs
Normal file
106
tests/ui/feature-gates/feature-gate-default-field-values.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
#![feature(generic_const_exprs)]
|
||||
#![allow(unused_variables, dead_code, incomplete_features)]
|
||||
|
||||
pub struct S;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Foo {
|
||||
pub bar: S = S, //~ ERROR default values on `struct` fields aren't supported
|
||||
pub baz: i32 = 42 + 3, //~ ERROR default values on `struct` fields aren't supported
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum Bar {
|
||||
#[default]
|
||||
Foo { //~ ERROR the `#[default]` attribute may only be used on unit enum variants
|
||||
bar: S = S, //~ ERROR default values on `struct` fields aren't supported
|
||||
baz: i32 = 42 + 3, //~ ERROR default values on `struct` fields aren't supported
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Qux<A, const C: i32> {
|
||||
bar: S = Qux::<A, C>::S, //~ ERROR default values on `struct` fields aren't supported
|
||||
baz: i32 = foo(), //~ ERROR default values on `struct` fields aren't supported
|
||||
bat: i32 = <Qux<A, C> as T>::K, //~ ERROR default values on `struct` fields aren't supported
|
||||
bay: i32 = C, //~ ERROR default values on `struct` fields aren't supported
|
||||
bak: Vec<A> = Vec::new(), //~ ERROR default values on `struct` fields aren't supported
|
||||
}
|
||||
|
||||
impl<A, const C: i32> Qux<A, C> {
|
||||
const S: S = S;
|
||||
}
|
||||
|
||||
trait T {
|
||||
const K: i32;
|
||||
}
|
||||
|
||||
impl<A, const C: i32> T for Qux<A, C> {
|
||||
const K: i32 = 2;
|
||||
}
|
||||
|
||||
const fn foo() -> i32 {
|
||||
42
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Opt {
|
||||
mandatory: Option<()>,
|
||||
optional: () = (), //~ ERROR default values on `struct` fields aren't supported
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum OptEnum {
|
||||
#[default]
|
||||
Variant { //~ ERROR the `#[default]` attribute may only be used on unit enum variants
|
||||
mandatory: Option<()>,
|
||||
optional: () = (), //~ ERROR default values on `struct` fields aren't supported
|
||||
}
|
||||
}
|
||||
|
||||
fn main () {
|
||||
let x = Foo { .. }; //~ ERROR base expression required after `..`
|
||||
let y = Foo::default();
|
||||
let z = Foo { baz: 1, .. }; //~ ERROR base expression required after `..`
|
||||
|
||||
assert_eq!(45, x.baz);
|
||||
assert_eq!(45, y.baz);
|
||||
assert_eq!(1, z.baz);
|
||||
|
||||
let x = Bar::Foo { .. }; //~ ERROR base expression required after `..`
|
||||
let y = Bar::default();
|
||||
let z = Bar::Foo { baz: 1, .. }; //~ ERROR base expression required after `..`
|
||||
|
||||
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, x));
|
||||
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, y));
|
||||
assert!(matches!(Bar::Foo { bar: S, baz: 1 }, z));
|
||||
|
||||
let x = Qux::<i32, 4> { .. }; //~ ERROR base expression required after `..`
|
||||
assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, bay: 4, .. }, x));
|
||||
//~^ ERROR base expression required after `..`
|
||||
assert!(x.bak.is_empty());
|
||||
let y = Opt { mandatory: None, .. };
|
||||
//~^ ERROR base expression required after `..`
|
||||
assert!(matches!(Opt::default(), y));
|
||||
let z = Opt::default();
|
||||
assert!(matches!(Opt { mandatory: None, .. }, z));
|
||||
//~^ ERROR base expression required after `..`
|
||||
assert!(matches!(Opt { .. }, z));
|
||||
//~^ ERROR base expression required after `..`
|
||||
assert!(matches!(Opt { optional: (), .. }, z));
|
||||
//~^ ERROR base expression required after `..`
|
||||
assert!(matches!(Opt { optional: (), mandatory: None, .. }, z));
|
||||
//~^ ERROR base expression required after `..`
|
||||
let y = OptEnum::Variant { mandatory: None, .. };
|
||||
//~^ ERROR base expression required after `..`
|
||||
assert!(matches!(OptEnum::default(), y));
|
||||
let z = OptEnum::default();
|
||||
assert!(matches!(OptEnum::Variant { mandatory: None, .. }, z));
|
||||
//~^ ERROR base expression required after `..`
|
||||
assert!(matches!(OptEnum::Variant { .. }, z));
|
||||
//~^ ERROR base expression required after `..`
|
||||
assert!(matches!(OptEnum::Variant { optional: (), .. }, z));
|
||||
//~^ ERROR base expression required after `..`
|
||||
assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }, z));
|
||||
//~^ ERROR base expression required after `..`
|
||||
}
|
318
tests/ui/feature-gates/feature-gate-default-field-values.stderr
Normal file
318
tests/ui/feature-gates/feature-gate-default-field-values.stderr
Normal file
|
@ -0,0 +1,318 @@
|
|||
error: the `#[default]` attribute may only be used on unit enum variants
|
||||
--> $DIR/feature-gate-default-field-values.rs:15:5
|
||||
|
|
||||
LL | Foo {
|
||||
| ^^^
|
||||
|
|
||||
= help: consider a manual implementation of `Default`
|
||||
|
||||
error: the `#[default]` attribute may only be used on unit enum variants
|
||||
--> $DIR/feature-gate-default-field-values.rs:55:5
|
||||
|
|
||||
LL | Variant {
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: consider a manual implementation of `Default`
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/feature-gate-default-field-values.rs:8:15
|
||||
|
|
||||
LL | pub bar: S = S,
|
||||
| ^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/feature-gate-default-field-values.rs:9:17
|
||||
|
|
||||
LL | pub baz: i32 = 42 + 3,
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/feature-gate-default-field-values.rs:16:15
|
||||
|
|
||||
LL | bar: S = S,
|
||||
| ^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/feature-gate-default-field-values.rs:17:17
|
||||
|
|
||||
LL | baz: i32 = 42 + 3,
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/feature-gate-default-field-values.rs:23:11
|
||||
|
|
||||
LL | bar: S = Qux::<A, C>::S,
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/feature-gate-default-field-values.rs:24:13
|
||||
|
|
||||
LL | baz: i32 = foo(),
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/feature-gate-default-field-values.rs:25:13
|
||||
|
|
||||
LL | bat: i32 = <Qux<A, C> as T>::K,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/feature-gate-default-field-values.rs:26:13
|
||||
|
|
||||
LL | bay: i32 = C,
|
||||
| ^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/feature-gate-default-field-values.rs:27:16
|
||||
|
|
||||
LL | bak: Vec<A> = Vec::new(),
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/feature-gate-default-field-values.rs:49:17
|
||||
|
|
||||
LL | optional: () = (),
|
||||
| ^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/feature-gate-default-field-values.rs:57:21
|
||||
|
|
||||
LL | optional: () = (),
|
||||
| ^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:62:21
|
||||
|
|
||||
LL | let x = Foo { .. };
|
||||
| ^
|
||||
|
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | let x = Foo { ../* expr */ };
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:64:29
|
||||
|
|
||||
LL | let z = Foo { baz: 1, .. };
|
||||
| ^
|
||||
|
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | let z = Foo { baz: 1, ../* expr */ };
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:70:26
|
||||
|
|
||||
LL | let x = Bar::Foo { .. };
|
||||
| ^
|
||||
|
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | let x = Bar::Foo { ../* expr */ };
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:72:34
|
||||
|
|
||||
LL | let z = Bar::Foo { baz: 1, .. };
|
||||
| ^
|
||||
|
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | let z = Bar::Foo { baz: 1, ../* expr */ };
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:78:31
|
||||
|
|
||||
LL | let x = Qux::<i32, 4> { .. };
|
||||
| ^
|
||||
|
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | let x = Qux::<i32, 4> { ../* expr */ };
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:79:73
|
||||
|
|
||||
LL | assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, bay: 4, .. }, x));
|
||||
| ^
|
||||
|
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, bay: 4, ../* expr */ }, x));
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:82:38
|
||||
|
|
||||
LL | let y = Opt { mandatory: None, .. };
|
||||
| ^
|
||||
|
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | let y = Opt { mandatory: None, ../* expr */ };
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:86:47
|
||||
|
|
||||
LL | assert!(matches!(Opt { mandatory: None, .. }, z));
|
||||
| ^
|
||||
|
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | assert!(matches!(Opt { mandatory: None, ../* expr */ }, z));
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:88:30
|
||||
|
|
||||
LL | assert!(matches!(Opt { .. }, z));
|
||||
| ^
|
||||
|
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | assert!(matches!(Opt { ../* expr */ }, z));
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:90:44
|
||||
|
|
||||
LL | assert!(matches!(Opt { optional: (), .. }, z));
|
||||
| ^
|
||||
|
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | assert!(matches!(Opt { optional: (), ../* expr */ }, z));
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:92:61
|
||||
|
|
||||
LL | assert!(matches!(Opt { optional: (), mandatory: None, .. }, z));
|
||||
| ^
|
||||
|
|
||||
help: remove the `..` as all the fields are already present
|
||||
|
|
||||
LL - assert!(matches!(Opt { optional: (), mandatory: None, .. }, z));
|
||||
LL + assert!(matches!(Opt { optional: (), mandatory: None, }, z));
|
||||
|
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:94:51
|
||||
|
|
||||
LL | let y = OptEnum::Variant { mandatory: None, .. };
|
||||
| ^
|
||||
|
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | let y = OptEnum::Variant { mandatory: None, ../* expr */ };
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:98:60
|
||||
|
|
||||
LL | assert!(matches!(OptEnum::Variant { mandatory: None, .. }, z));
|
||||
| ^
|
||||
|
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | assert!(matches!(OptEnum::Variant { mandatory: None, ../* expr */ }, z));
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:100:43
|
||||
|
|
||||
LL | assert!(matches!(OptEnum::Variant { .. }, z));
|
||||
| ^
|
||||
|
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | assert!(matches!(OptEnum::Variant { ../* expr */ }, z));
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:102:57
|
||||
|
|
||||
LL | assert!(matches!(OptEnum::Variant { optional: (), .. }, z));
|
||||
| ^
|
||||
|
|
||||
help: add a base expression here
|
||||
|
|
||||
LL | assert!(matches!(OptEnum::Variant { optional: (), ../* expr */ }, z));
|
||||
| ++++++++++
|
||||
|
||||
error[E0797]: base expression required after `..`
|
||||
--> $DIR/feature-gate-default-field-values.rs:104:74
|
||||
|
|
||||
LL | assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }, z));
|
||||
| ^
|
||||
|
|
||||
help: remove the `..` as all the fields are already present
|
||||
|
|
||||
LL - assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }, z));
|
||||
LL + assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, }, z));
|
||||
|
|
||||
|
||||
error: aborting due to 29 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0658, E0797.
|
||||
For more information about an error, try `rustc --explain E0658`.
|
|
@ -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() {}
|
|
@ -1,4 +1,3 @@
|
|||
//@ run-rustfix
|
||||
#![allow(dead_code)]
|
||||
|
||||
enum E {
|
||||
|
|
|
@ -1,137 +1,17 @@
|
|||
error: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:9:16
|
||||
|
|
||||
LL | field1: i32 = 42,
|
||||
| ^^^^^
|
||||
|
|
||||
help: remove this unsupported default value
|
||||
|
|
||||
LL - field1: i32 = 42,
|
||||
LL + field1: i32,
|
||||
|
|
||||
|
||||
error: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:10:14
|
||||
|
|
||||
LL | field2: E = E::A,
|
||||
| ^^^^^^^
|
||||
|
|
||||
help: remove this unsupported default value
|
||||
|
|
||||
LL - field2: E = E::A,
|
||||
LL + field2: E,
|
||||
|
|
||||
|
||||
error: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:11:16
|
||||
|
|
||||
LL | field3: i32 = 1 + 2,
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: remove this unsupported default value
|
||||
|
|
||||
LL - field3: i32 = 1 + 2,
|
||||
LL + field3: i32,
|
||||
|
|
||||
|
||||
error: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:12:16
|
||||
|
|
||||
LL | field4: i32 = { 1 + 2 },
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
help: remove this unsupported default value
|
||||
|
|
||||
LL - field4: i32 = { 1 + 2 },
|
||||
LL + field4: i32,
|
||||
|
|
||||
|
||||
error: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:13:14
|
||||
|
|
||||
LL | field5: E = foo(42),
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: remove this unsupported default value
|
||||
|
|
||||
LL - field5: E = foo(42),
|
||||
LL + field5: E,
|
||||
|
|
||||
|
||||
error: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:14:14
|
||||
|
|
||||
LL | field6: E = { foo(42) },
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove this unsupported default value
|
||||
|
|
||||
LL - field6: E = { foo(42) },
|
||||
LL + field6: E,
|
||||
|
|
||||
|
||||
error: expected `,`, or `}`, found `field2`
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:18:16
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:17:16
|
||||
|
|
||||
LL | field1: i32
|
||||
| ^ help: try adding a comma: `,`
|
||||
|
||||
error: expected `,`, or `}`, found `field3`
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:19:14
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:18:14
|
||||
|
|
||||
LL | field2: E
|
||||
| ^ help: try adding a comma: `,`
|
||||
|
||||
error: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:20:16
|
||||
|
|
||||
LL | field3: i32 = 1 + 2,
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: remove this unsupported default value
|
||||
|
|
||||
LL - field3: i32 = 1 + 2,
|
||||
LL + field3: i32,
|
||||
|
|
||||
|
||||
error: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:21:16
|
||||
|
|
||||
LL | field4: i32 = { 1 + 2 },
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
help: remove this unsupported default value
|
||||
|
|
||||
LL - field4: i32 = { 1 + 2 },
|
||||
LL + field4: i32,
|
||||
|
|
||||
|
||||
error: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:22:14
|
||||
|
|
||||
LL | field5: E = foo(42),
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: remove this unsupported default value
|
||||
|
|
||||
LL - field5: E = foo(42),
|
||||
LL + field5: E,
|
||||
|
|
||||
|
||||
error: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:23:14
|
||||
|
|
||||
LL | field6: E = { foo(42) },
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove this unsupported default value
|
||||
|
|
||||
LL - field6: E = { foo(42) },
|
||||
LL + field6: E,
|
||||
|
|
||||
|
||||
error: expected `:`, found `=`
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:27:12
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:26:12
|
||||
|
|
||||
LL | field1 = i32,
|
||||
| ^
|
||||
|
@ -140,7 +20,7 @@ LL | field1 = i32,
|
|||
| help: field names and their types are separated with `:`
|
||||
|
||||
error: expected `:`, found `;`
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:28:11
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:27:11
|
||||
|
|
||||
LL | field2; E,
|
||||
| ^
|
||||
|
@ -148,5 +28,106 @@ LL | field2; E,
|
|||
| expected `:`
|
||||
| help: field names and their types are separated with `:`
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:8:16
|
||||
|
|
||||
LL | field1: i32 = 42,
|
||||
| ^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:9:14
|
||||
|
|
||||
LL | field2: E = E::A,
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:10:16
|
||||
|
|
||||
LL | field3: i32 = 1 + 2,
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:11:16
|
||||
|
|
||||
LL | field4: i32 = { 1 + 2 },
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:12:14
|
||||
|
|
||||
LL | field5: E = foo(42),
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:13:14
|
||||
|
|
||||
LL | field6: E = { foo(42) },
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:19:16
|
||||
|
|
||||
LL | field3: i32 = 1 + 2,
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:20:16
|
||||
|
|
||||
LL | field4: i32 = { 1 + 2 },
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:21:14
|
||||
|
|
||||
LL | field5: E = foo(42),
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: default values on `struct` fields aren't supported
|
||||
--> $DIR/struct-default-values-and-missing-field-separator.rs:22:14
|
||||
|
|
||||
LL | field6: E = { foo(42) },
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
|
||||
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
|
|
|
@ -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
|
||||
|
|
49
tests/ui/structs/default-field-values-failures.rs
Normal file
49
tests/ui/structs/default-field-values-failures.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
#![feature(default_field_values)]
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct S;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Foo {
|
||||
pub bar: S = S,
|
||||
pub baz: i32 = 42 + 3,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Bar {
|
||||
pub bar: S, //~ ERROR the trait bound `S: Default` is not satisfied
|
||||
pub baz: i32 = 42 + 3,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Qux<const C: i32> {
|
||||
bar: S = Self::S, //~ ERROR generic `Self` types are currently not permitted in anonymous constants
|
||||
baz: i32 = foo(),
|
||||
bat: i32 = <Qux<{ C }> as T>::K, //~ ERROR generic parameters may not be used in const operations
|
||||
bay: i32 = C,
|
||||
}
|
||||
|
||||
impl<const C: i32> Qux<C> {
|
||||
const S: S = S;
|
||||
}
|
||||
|
||||
trait T {
|
||||
const K: i32;
|
||||
}
|
||||
|
||||
impl<const C: i32> T for Qux<C> {
|
||||
const K: i32 = 2;
|
||||
}
|
||||
|
||||
const fn foo() -> i32 {
|
||||
42
|
||||
}
|
||||
|
||||
fn main () {
|
||||
let _ = Foo { .. }; // ok
|
||||
let _ = Foo::default(); // ok
|
||||
let _ = Bar { .. }; //~ ERROR mandatory field
|
||||
let _ = Bar::default(); // silenced
|
||||
let _ = Bar { bar: S, .. }; // ok
|
||||
let _ = Qux::<4> { .. };
|
||||
}
|
40
tests/ui/structs/default-field-values-failures.stderr
Normal file
40
tests/ui/structs/default-field-values-failures.stderr
Normal file
|
@ -0,0 +1,40 @@
|
|||
error: generic parameters may not be used in const operations
|
||||
--> $DIR/default-field-values-failures.rs:22:23
|
||||
|
|
||||
LL | bat: i32 = <Qux<{ C }> as T>::K,
|
||||
| ^ cannot perform const operation using `C`
|
||||
|
|
||||
= help: const parameters may only be used as standalone arguments, i.e. `C`
|
||||
= help: add `#![feature(generic_const_exprs)]` to allow generic const expressions
|
||||
|
||||
error[E0277]: the trait bound `S: Default` is not satisfied
|
||||
--> $DIR/default-field-values-failures.rs:14:5
|
||||
|
|
||||
LL | #[derive(Debug, Default)]
|
||||
| ------- in this derive macro expansion
|
||||
LL | pub struct Bar {
|
||||
LL | pub bar: S,
|
||||
| ^^^^^^^^^^ the trait `Default` is not implemented for `S`
|
||||
|
|
||||
= note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider annotating `S` with `#[derive(Default)]`
|
||||
|
|
||||
LL + #[derive(Default)]
|
||||
LL | pub struct S;
|
||||
|
|
||||
|
||||
error: missing mandatory field `bar`
|
||||
--> $DIR/default-field-values-failures.rs:45:21
|
||||
|
|
||||
LL | let _ = Bar { .. };
|
||||
| ^
|
||||
|
||||
error: generic `Self` types are currently not permitted in anonymous constants
|
||||
--> $DIR/default-field-values-failures.rs:20:14
|
||||
|
|
||||
LL | bar: S = Self::S,
|
||||
| ^^^^
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
19
tests/ui/structs/default-field-values-invalid-const.rs
Normal file
19
tests/ui/structs/default-field-values-invalid-const.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
#![feature(default_field_values, generic_const_exprs)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
#[warn(default_field_always_invalid_const)] //~ WARN lint `default_field_always_invalid_const` can't be warned on
|
||||
pub struct Bat {
|
||||
pub bax: u8 = panic!("asdf"),
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
//~| WARN default field fails const-evaluation
|
||||
}
|
||||
|
||||
pub struct Baz<const C: u8> {
|
||||
pub bax: u8 = 130 + C, // ok
|
||||
pub bat: u8 = 130 + 130,
|
||||
//~^ ERROR evaluation of `Baz::<C>::bat::{constant#0}` failed
|
||||
//~| ERROR default field fails const-evaluation
|
||||
pub bay: u8 = 1, // ok
|
||||
}
|
||||
|
||||
fn main() {}
|
45
tests/ui/structs/default-field-values-invalid-const.stderr
Normal file
45
tests/ui/structs/default-field-values-invalid-const.stderr
Normal file
|
@ -0,0 +1,45 @@
|
|||
warning: lint `default_field_always_invalid_const` can't be warned on
|
||||
--> $DIR/default-field-values-invalid-const.rs:4:8
|
||||
|
|
||||
LL | #[warn(default_field_always_invalid_const)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ either `deny` or `allow`, no other lint level is supported for this lint
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/default-field-values-invalid-const.rs:6:19
|
||||
|
|
||||
LL | pub bax: u8 = panic!("asdf"),
|
||||
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'asdf', $DIR/default-field-values-invalid-const.rs:6:19
|
||||
|
|
||||
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
warning: default field fails const-evaluation
|
||||
--> $DIR/default-field-values-invalid-const.rs:6:5
|
||||
|
|
||||
LL | pub bax: u8 = panic!("asdf"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this field's constant fails const-evaluation, as seen in the previous error
|
||||
|
|
||||
= help: you can skip const-evaluation of default fields by enabling this lint
|
||||
note: the lint level is defined here
|
||||
--> $DIR/default-field-values-invalid-const.rs:4:8
|
||||
|
|
||||
LL | #[warn(default_field_always_invalid_const)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0080]: evaluation of `Baz::<C>::bat::{constant#0}` failed
|
||||
--> $DIR/default-field-values-invalid-const.rs:13:19
|
||||
|
|
||||
LL | pub bat: u8 = 130 + 130,
|
||||
| ^^^^^^^^^ attempt to compute `130_u8 + 130_u8`, which would overflow
|
||||
|
||||
error: default field fails const-evaluation
|
||||
--> $DIR/default-field-values-invalid-const.rs:13:5
|
||||
|
|
||||
LL | pub bat: u8 = 130 + 130,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ this field's constant fails const-evaluation, as seen in the previous error
|
||||
|
|
||||
= help: you can skip const-evaluation of default fields by enabling this lint
|
||||
= note: `#[deny(default_field_always_invalid_const)]` on by default
|
||||
|
||||
error: aborting due to 3 previous errors; 2 warnings emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
68
tests/ui/structs/default-field-values-support.rs
Normal file
68
tests/ui/structs/default-field-values-support.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
//@ run-pass
|
||||
#![feature(default_field_values, generic_const_exprs)]
|
||||
#![allow(unused_variables, dead_code, incomplete_features)]
|
||||
|
||||
pub struct S;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Foo {
|
||||
pub bar: S = S,
|
||||
pub baz: i32 = 42 + 3,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum Bar {
|
||||
#[default]
|
||||
Foo {
|
||||
bar: S = S,
|
||||
baz: i32 = 42 + 3,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Qux<A, const C: i32> {
|
||||
bar: S = Qux::<A, C>::S,
|
||||
baz: i32 = foo(),
|
||||
bat: i32 = <Qux<A, C> as T>::K,
|
||||
baq: i32 = Self::K,
|
||||
bay: i32 = C,
|
||||
bak: Vec<A> = Vec::new(),
|
||||
}
|
||||
|
||||
impl<A, const C: i32> Qux<A, C> {
|
||||
const S: S = S;
|
||||
}
|
||||
|
||||
trait T {
|
||||
const K: i32;
|
||||
}
|
||||
|
||||
impl<A, const C: i32> T for Qux<A, C> {
|
||||
const K: i32 = 2;
|
||||
}
|
||||
|
||||
const fn foo() -> i32 {
|
||||
42
|
||||
}
|
||||
|
||||
fn main () {
|
||||
let x = Foo { .. };
|
||||
let y = Foo::default();
|
||||
let z = Foo { baz: 1, .. };
|
||||
|
||||
assert_eq!(45, x.baz);
|
||||
assert_eq!(45, y.baz);
|
||||
assert_eq!(1, z.baz);
|
||||
|
||||
let x = Bar::Foo { .. };
|
||||
let y = Bar::default();
|
||||
let z = Bar::Foo { baz: 1, .. };
|
||||
|
||||
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, x));
|
||||
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, y));
|
||||
assert!(matches!(Bar::Foo { bar: S, baz: 1 }, z));
|
||||
|
||||
let x = Qux::<i32, 4> { .. };
|
||||
assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, baq: 2, bay: 4, .. }, x));
|
||||
assert!(x.bak.is_empty());
|
||||
}
|
|
@ -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: []
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue