diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index dc4b6a6fd319c..e7c5161bb0d43 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -9,7 +9,9 @@ use rustc_attr_ir::{EiiDecl, EiiImpl, EiiImplResolution, find_attr}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::transitive_relation::TransitiveRelationBuilder; use rustc_errors::codes::*; -use rustc_errors::{Applicability, ErrorGuaranteed, msg, pluralize, struct_span_code_err}; +use rustc_errors::{ + Applicability, ErrorGuaranteed, MultiSpan, msg, pluralize, struct_span_code_err, +}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -17,10 +19,13 @@ use rustc_hir::{AmbigArg, ItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{BoundRegionConversionTime, SolverRegionConstraint, TyCtxtInferExt}; use rustc_infer::traits::{PredicateObligations, TraitErrors}; -use rustc_lint_defs::builtin::{REDUNDANT_LIFETIMES, SHADOWING_SUPERTRAIT_ITEMS}; +use rustc_lint_defs::builtin::{ + REDUNDANT_LIFETIMES, SHADOWING_SUPERTRAIT_ITEMS, UNEVALUATED_DEFAULT_FIELD_VALUE, +}; use rustc_macros::{Diagnostic, TypeFoldable, TypeVisitable}; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::traits::solve::NoSolution; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::region_constraint::{And, LeafRegionConstraint, Or}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ @@ -47,7 +52,9 @@ use tracing::{debug, instrument}; use super::compare_eii::{compare_eii_function_types, compare_eii_statics}; use crate::autoderef::Autoderef; use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params}; -use crate::diagnostics::{self, InvalidReceiverTyHint, ParamInTyOfConstParam}; +use crate::diagnostics::{ + self, InvalidReceiverTyHint, ParamInTyOfConstParam, UnevaluatedDefaultFieldValue, +}; pub(super) struct WfCheckingCtxt<'a, 'tcx> { pub(super) ocx: ObligationCtxt<'a, 'tcx, FulfillmentError<'tcx>>, @@ -985,28 +992,44 @@ pub(crate) fn check_type_defn<'tcx>( enter_wf_checking_ctxt(tcx, item, |wfcx| { let variants = adt_def.variants(); let packed = adt_def.repr().packed(); + let own_params_require_monomorphization = + tcx.generics_of(item).own_requires_monomorphization(); for variant in variants.iter() { // All field types must be well-formed. for field in &variant.fields { - if let Some(def_id) = field.value - && let Some(_ty) = tcx.type_of(def_id).no_bound_vars() - { - // FIXME(generic_const_exprs, default_field_values): this is a hack and needs to - // be refactored to check the instantiate-ability of the code better. - if let Some(def_id) = def_id.as_local() - && let DefKind::AnonConst = tcx.def_kind(def_id) - && let hir::Node::AnonConst(anon) = tcx.hir_node_by_def_id(def_id) - && let expr = &tcx.hir_body(anon.body).value - && let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind - && let Res::Def(DefKind::ConstParam, _def_id) = path.res - { - // Do not evaluate bare `const` params, as those would ICE and are only - // usable if `#![feature(generic_const_exprs)]` is enabled. - } else { - // Evaluate the constant proactively, to emit an error if the constant has - // an unconditional error. We only do so if the const has no type params. + if let Some(def_id) = field.value { + if !own_params_require_monomorphization { let _ = tcx.const_eval_poly(def_id); + } else if tcx.features().default_field_values() + && let Some(local_def_id) = def_id.as_local() + { + // Do not redundantly trigger lint if the feature is not actually available. + + // We use the callsite span for default values that are actually macro calls + // into other crates, like for example `field: () = panic!(),`. + let field_span = tcx.def_span(def_id).source_callsite(); + let mut multispan: MultiSpan = field_span.into(); + multispan + .push_span_label(field_span, "this can't be const-evaluated until use"); + let struct_start = tcx.def_span(item).shrink_to_lo(); + multispan.push_span_context(tcx.def_span(field.did)); + multispan.push_span_context(struct_start); + tcx.emit_node_span_lint( + UNEVALUATED_DEFAULT_FIELD_VALUE, + tcx.local_def_id_to_hir_id(local_def_id), + multispan, + UnevaluatedDefaultFieldValue { + field: field.name, + ty: with_no_trimmed_paths!(tcx.def_path_str(item)), + padding: tcx + .sess + .source_map() + .indentation_before(struct_start) + .unwrap_or_default(), + struct_start, + }, + ); } } let field_id = field.did.expect_local(); diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index b003a358f9589..696b0d193c267 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -2157,3 +2157,24 @@ pub(crate) struct ParamInTyOfConstParam<'tcx> { pub(crate) span: Span, pub(crate) ty: Ty<'tcx>, } + +#[derive(Diagnostic)] +#[diag( + "field `{$field}` has a default value that is only checked when a value of `{$ty}` is \ + constructed" +)] +#[help( + "structs and enums with type and const parameters only evaluate their default field values \ + during construction, not eagerly when declared" +)] +pub(crate) struct UnevaluatedDefaultFieldValue { + pub(crate) field: Symbol, + pub(crate) ty: String, + #[suggestion( + "if this behavior is acceptable, allow the lint and preferably write a test relying on the default value", + code = "{padding}#[expect(unevaluated_default_field_value)]\n", + applicability = "maybe-incorrect" + )] + pub(crate) struct_start: Span, + pub(crate) padding: String, +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index d1dc5e05a0f09..0f9a8b0f69cc2 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -124,6 +124,7 @@ pub mod hardwired { UNCONDITIONAL_PANIC, UNCONDITIONAL_RECURSION, UNCOVERED_PARAM_IN_PROJECTION, + UNEVALUATED_DEFAULT_FIELD_VALUE, UNEXPECTED_CFGS, UNFULFILLED_LINT_EXPECTATIONS, UNINHABITED_STATIC, @@ -5922,3 +5923,36 @@ declare_lint! { reason: fcw!(FutureReleaseError #163263), }; } + +declare_lint! { + /// The `unevaluated_default_field_value` lint detects when a struct has a field with a default + /// value that requires a type parameter to be evaluated, meaning that checking that default for + /// correctness is delayed to *instantiation*, instead of happening eagerly. + /// + /// ### Example + /// + /// ```rust,no_run + /// #![feature(default_field_values)] + /// + /// struct Struct { + /// field: u8 = 100 + T, // Because `T` is unknown at this point, the value won't be checked + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Const evaluation requires all values to be known. In the case of default field values, they + /// will get evaluated eagerly, but if there are any references to a const parameter, the + /// evaluation of the default will only occur when materializing a value of its owning struct. + /// For the example prior, if you wrote `let _: Struct<180> = Struct { .. };`, you would get a + /// const evaluation compile error. + /// + /// This inconsistency can be confusing, and surprising, because the fields *are* sometimes + /// evaluated eagerly. + pub UNEVALUATED_DEFAULT_FIELD_VALUE, + Warn, + r#"detects incompatible uses of `#[sanitize(realtime = "nonblocking")]` on async functions"#, + @feature_gate = default_field_values; +} diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 3655d4594aa38..414d4a8f52ff1 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -508,6 +508,9 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a> + 'a>( for (label_span, label) in span.span_labels_raw() { err.span.push_span_diag(*label_span, label.clone()); } + for span in span.span_context() { + err.span.push_span_context(*span); + } } if disable_suggestions { diff --git a/tests/ui/structs/default-field-values/failures.rs b/tests/ui/structs/default-field-values/failures.rs index 9c5b7172929c6..d796053f1cb24 100644 --- a/tests/ui/structs/default-field-values/failures.rs +++ b/tests/ui/structs/default-field-values/failures.rs @@ -19,10 +19,10 @@ pub struct Bar { #[derive(Default)] pub struct Qux { - bar: S = Self::S, - baz: i32 = foo(), - bat: i32 = as T>::K, - bay: i32 = C, + bar: S = Self::S, //~ WARN + baz: i32 = foo(), //~ WARN + bat: i32 = as T>::K, //~ WARN + bay: i32 = C, //~ WARN } pub struct Rak(i32 = 42); //~ ERROR default fields are not supported in tuple structs diff --git a/tests/ui/structs/default-field-values/failures.stderr b/tests/ui/structs/default-field-values/failures.stderr index 5e3d4c89c2a3c..13e6480236ff5 100644 --- a/tests/ui/structs/default-field-values/failures.stderr +++ b/tests/ui/structs/default-field-values/failures.stderr @@ -24,6 +24,66 @@ error: unions cannot have default field values LL | y: f32 = 2., | ^^ +warning: field `bar` has a default value that is only checked when a value of `Qux` is constructed + --> $DIR/failures.rs:22:14 + | +LL | pub struct Qux { +LL | bar: S = Self::S, + | ^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = note: `#[warn(unevaluated_default_field_value)]` on by default +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Qux { + | + +warning: field `baz` has a default value that is only checked when a value of `Qux` is constructed + --> $DIR/failures.rs:23:16 + | +LL | pub struct Qux { +LL | bar: S = Self::S, +LL | baz: i32 = foo(), + | ^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Qux { + | + +warning: field `bat` has a default value that is only checked when a value of `Qux` is constructed + --> $DIR/failures.rs:24:16 + | +LL | pub struct Qux { +... +LL | bat: i32 = as T>::K, + | ^^^^^^^^^^^^^^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Qux { + | + +warning: field `bay` has a default value that is only checked when a value of `Qux` is constructed + --> $DIR/failures.rs:25:16 + | +LL | pub struct Qux { +... +LL | bay: i32 = C, + | ^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Qux { + | + error[E0277]: the trait bound `S: Default` is not satisfied --> $DIR/failures.rs:16:5 | @@ -114,7 +174,7 @@ LL - let _ = Rak(.., 0); LL + let _ = Rak(0); | -error: aborting due to 10 previous errors +error: aborting due to 10 previous errors; 4 warnings emitted Some errors have detailed explanations: E0061, E0277, E0308. For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/structs/default-field-values/field-references-param-accurate-span.rs b/tests/ui/structs/default-field-values/field-references-param-accurate-span.rs new file mode 100644 index 0000000000000..baba9d8d844a3 --- /dev/null +++ b/tests/ui/structs/default-field-values/field-references-param-accurate-span.rs @@ -0,0 +1,37 @@ +//@ build-fail +// https://github.com/rust-lang/rust/issues/146496 +#![feature(default_field_values)] + +struct Z { + // Ensure that proper context is shown in lint. + multiline_field: + () + = { //~ WARN default value + f::(); + panic!(); + //~^ ERROR: explicit panic + //~| ERROR: explicit panic + }, +} + +pub const fn f() { + // *If* we const evaluated `Z.multiline_field` at definition, but then bailed because `f` needs + // to be const evaluated, commenting out this line would suddenly allow `f` to be evaluated and + // cause the panic in `multiline_field` to be reached. + let _ = [0u8; N]; +} + +const fn const_use_generically() { + let x: Z = Z { .. }; +} + +fn use_generically() { + let x: Z = Z { .. }; +} + +fn main() { + let x: Z<1> = Z { .. }; + use_generically::<2>(); + const_use_generically::<3>(); + const { const_use_generically::<4>() }; +} diff --git a/tests/ui/structs/default-field-values/field-references-param-accurate-span.stderr b/tests/ui/structs/default-field-values/field-references-param-accurate-span.stderr new file mode 100644 index 0000000000000..a0b937afd1112 --- /dev/null +++ b/tests/ui/structs/default-field-values/field-references-param-accurate-span.stderr @@ -0,0 +1,73 @@ +warning: field `multiline_field` has a default value that is only checked when a value of `Z` is constructed + --> $DIR/field-references-param-accurate-span.rs:9:15 + | +LL | struct Z { +LL | // Ensure that proper context is shown in lint. +LL | multiline_field: +LL | () +LL | = { + | _______________^ +LL | | f::(); +LL | | panic!(); +LL | | +LL | | +LL | | }, + | |_____________^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = note: `#[warn(unevaluated_default_field_value)]` on by default +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | struct Z { + | + +error[E0080]: evaluation panicked: explicit panic + --> $DIR/field-references-param-accurate-span.rs:11:17 + | +LL | panic!(); + | ^^^^^^^^ evaluation of `Z::<1>::multiline_field::{constant#0}` failed here + +note: erroneous constant encountered + --> $DIR/field-references-param-accurate-span.rs:33:19 + | +LL | let x: Z<1> = Z { .. }; + | ^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/field-references-param-accurate-span.rs:33:19 + | +LL | let x: Z<1> = Z { .. }; + | ^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: evaluation panicked: explicit panic + --> $DIR/field-references-param-accurate-span.rs:11:17 + | +LL | panic!(); + | ^^^^^^^^ evaluation of `Z::<4>::multiline_field::{constant#0}` failed here + +note: erroneous constant encountered + --> $DIR/field-references-param-accurate-span.rs:25:19 + | +LL | let x: Z = Z { .. }; + | ^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/field-references-param-accurate-span.rs:36:5 + | +LL | const { const_use_generically::<4>() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/field-references-param-accurate-span.rs:36:5 + | +LL | const { const_use_generically::<4>() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/structs/default-field-values/field-references-param.allow_field.stderr b/tests/ui/structs/default-field-values/field-references-param.allow_field.stderr new file mode 100644 index 0000000000000..52dd197fb9fdb --- /dev/null +++ b/tests/ui/structs/default-field-values/field-references-param.allow_field.stderr @@ -0,0 +1,18 @@ +warning: field `too_generic` has a default value that is only checked when a value of `Z` is constructed + --> $DIR/field-references-param.rs:23:26 + | +LL | struct Z { +... +LL | too_generic: usize = X + 1, + | ^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = note: `#[warn(unevaluated_default_field_value)]` on by default +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | struct Z { + | + +warning: 1 warning emitted + diff --git a/tests/ui/structs/default-field-values/field-references-param.rs b/tests/ui/structs/default-field-values/field-references-param.rs index ecee37edd42c0..dadef09a1de50 100644 --- a/tests/ui/structs/default-field-values/field-references-param.rs +++ b/tests/ui/structs/default-field-values/field-references-param.rs @@ -1,4 +1,5 @@ //@ build-pass +//@ revisions: warn allow_struct allow_field #![feature(default_field_values)] @@ -8,15 +9,29 @@ impl W { const fn new() -> Self { W } } +#[cfg_attr(allow_struct, allow(unevaluated_default_field_value))] struct Z { // No inference. - one: W = W::::new(), + #[cfg_attr(allow_field, allow(unevaluated_default_field_value))] + one: W = W::::new(), //[warn]~ WARN // Inference works too. - two: W = W::new(), + #[cfg_attr(allow_field, allow(unevaluated_default_field_value))] + two: W = W::new(), //[warn]~ WARN // An anon const that is too generic before substitution. too_generic: usize = X + 1, + //[warn]~^ WARN + //[allow_field]~^^ WARN + + // Directly using a const parameter. + #[cfg_attr(allow_field, allow(unevaluated_default_field_value))] + direct: usize = X, + //[warn]~^ WARN +} + +pub const fn f() { + let _ = [0u8; N]; } fn use_generically() { diff --git a/tests/ui/structs/default-field-values/field-references-param.warn.stderr b/tests/ui/structs/default-field-values/field-references-param.warn.stderr new file mode 100644 index 0000000000000..60376fdadd96c --- /dev/null +++ b/tests/ui/structs/default-field-values/field-references-param.warn.stderr @@ -0,0 +1,63 @@ +warning: field `one` has a default value that is only checked when a value of `Z` is constructed + --> $DIR/field-references-param.rs:16:17 + | +LL | struct Z { +... +LL | one: W = W::::new(), + | ^^^^^^^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = note: `#[warn(unevaluated_default_field_value)]` on by default +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | struct Z { + | + +warning: field `two` has a default value that is only checked when a value of `Z` is constructed + --> $DIR/field-references-param.rs:20:17 + | +LL | struct Z { +... +LL | two: W = W::new(), + | ^^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | struct Z { + | + +warning: field `too_generic` has a default value that is only checked when a value of `Z` is constructed + --> $DIR/field-references-param.rs:23:26 + | +LL | struct Z { +... +LL | too_generic: usize = X + 1, + | ^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | struct Z { + | + +warning: field `direct` has a default value that is only checked when a value of `Z` is constructed + --> $DIR/field-references-param.rs:29:21 + | +LL | struct Z { +... +LL | direct: usize = X, + | ^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | struct Z { + | + +warning: 4 warnings emitted + diff --git a/tests/ui/structs/default-field-values/invalid-const.rs b/tests/ui/structs/default-field-values/invalid-const.rs index ddb73f688659c..9822489b524ce 100644 --- a/tests/ui/structs/default-field-values/invalid-const.rs +++ b/tests/ui/structs/default-field-values/invalid-const.rs @@ -1,16 +1,23 @@ -#![feature(default_field_values, generic_const_exprs)] -#![allow(incomplete_features)] +#![feature(default_field_values)] pub struct Bat { pub bax: u8 = panic!("asdf"), //~^ ERROR evaluation panicked: asdf + pub bat: u8 = 130 + 130, + //~^ ERROR attempt to compute `130_u8 + 130_u8`, which would overflow } pub struct Baz { - pub bax: u8 = 130 + C, // ok - pub bat: u8 = 130 + 130, - //~^ ERROR attempt to compute `130_u8 + 130_u8`, which would overflow - pub bay: u8 = 1, // ok + pub bax: u8 = 130 + C, //~ WARN + pub bat: u8 = 130 + 130, //~ WARN + // ^ If we run `const_eval_poly` without restricting const params, this would be + // attempt to compute `130_u8 + 130_u8`, which would overflow + pub bay: u8 = 1, //~ WARN + pub bap: u8 = C, //~ WARN + pub bas: u8 = const { panic!("asdf") }, //~ WARN + pub ban: u8 = panic!("asdf"), //~ WARN + // ^ If we run `const_eval_poly` without restricting const params, these would be + // evaluation panicked: asdf } fn main() {} diff --git a/tests/ui/structs/default-field-values/invalid-const.stderr b/tests/ui/structs/default-field-values/invalid-const.stderr index 545783e6c74ce..c9962dbbe38d5 100644 --- a/tests/ui/structs/default-field-values/invalid-const.stderr +++ b/tests/ui/structs/default-field-values/invalid-const.stderr @@ -1,15 +1,105 @@ error[E0080]: evaluation panicked: asdf - --> $DIR/invalid-const.rs:5:19 + --> $DIR/invalid-const.rs:4:19 | LL | pub bax: u8 = panic!("asdf"), | ^^^^^^^^^^^^^^ evaluation of `Bat::bax::{constant#0}` failed here error[E0080]: attempt to compute `130_u8 + 130_u8`, which would overflow + --> $DIR/invalid-const.rs:6:19 + | +LL | pub bat: u8 = 130 + 130, + | ^^^^^^^^^ evaluation of `Bat::bat::{constant#0}` failed here + +warning: field `bax` has a default value that is only checked when a value of `Baz` is constructed --> $DIR/invalid-const.rs:11:19 | +LL | pub struct Baz { +LL | pub bax: u8 = 130 + C, + | ^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = note: `#[warn(unevaluated_default_field_value)]` on by default +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Baz { + | + +warning: field `bat` has a default value that is only checked when a value of `Baz` is constructed + --> $DIR/invalid-const.rs:12:19 + | +LL | pub struct Baz { +LL | pub bax: u8 = 130 + C, LL | pub bat: u8 = 130 + 130, - | ^^^^^^^^^ evaluation of `Baz::::bat::{constant#0}` failed here + | ^^^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Baz { + | + +warning: field `bay` has a default value that is only checked when a value of `Baz` is constructed + --> $DIR/invalid-const.rs:15:19 + | +LL | pub struct Baz { +... +LL | pub bay: u8 = 1, + | ^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Baz { + | + +warning: field `bap` has a default value that is only checked when a value of `Baz` is constructed + --> $DIR/invalid-const.rs:16:19 + | +LL | pub struct Baz { +... +LL | pub bap: u8 = C, + | ^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Baz { + | + +warning: field `bas` has a default value that is only checked when a value of `Baz` is constructed + --> $DIR/invalid-const.rs:17:19 + | +LL | pub struct Baz { +... +LL | pub bas: u8 = const { panic!("asdf") }, + | ^^^^^^^^^^^^^^^^^^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Baz { + | + +warning: field `ban` has a default value that is only checked when a value of `Baz` is constructed + --> $DIR/invalid-const.rs:18:19 + | +LL | pub struct Baz { +... +LL | pub ban: u8 = panic!("asdf"), + | ^^^^^^^^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Baz { + | -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 6 warnings emitted For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/structs/default-field-values/post-mono.direct.stderr b/tests/ui/structs/default-field-values/post-mono.direct.stderr index 74e37795f0697..0342d119c298b 100644 --- a/tests/ui/structs/default-field-values/post-mono.direct.stderr +++ b/tests/ui/structs/default-field-values/post-mono.direct.stderr @@ -1,3 +1,18 @@ +warning: field `post_mono` has a default value that is only checked when a value of `Z` is constructed + --> $DIR/post-mono.rs:7:24 + | +LL | struct Z { +LL | post_mono: usize = X / 0, + | ^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = note: `#[warn(unevaluated_default_field_value)]` on by default +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | struct Z { + | + error[E0080]: attempt to divide `1_usize` by zero --> $DIR/post-mono.rs:7:24 | @@ -18,6 +33,6 @@ LL | let x: Z<1> = Z { .. }; | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/structs/default-field-values/post-mono.indirect.stderr b/tests/ui/structs/default-field-values/post-mono.indirect.stderr index c8af9c959c103..3a62080d62cc9 100644 --- a/tests/ui/structs/default-field-values/post-mono.indirect.stderr +++ b/tests/ui/structs/default-field-values/post-mono.indirect.stderr @@ -1,3 +1,18 @@ +warning: field `post_mono` has a default value that is only checked when a value of `Z` is constructed + --> $DIR/post-mono.rs:7:24 + | +LL | struct Z { +LL | post_mono: usize = X / 0, + | ^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = note: `#[warn(unevaluated_default_field_value)]` on by default +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | struct Z { + | + error[E0080]: attempt to divide `1_usize` by zero --> $DIR/post-mono.rs:7:24 | @@ -24,6 +39,6 @@ note: the above error was encountered while instantiating `fn indirect::<1>` LL | indirect::<1>(); | ^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/structs/default-field-values/post-mono.rs b/tests/ui/structs/default-field-values/post-mono.rs index 68dfa391bb485..748d15a7070d4 100644 --- a/tests/ui/structs/default-field-values/post-mono.rs +++ b/tests/ui/structs/default-field-values/post-mono.rs @@ -4,7 +4,7 @@ #![feature(default_field_values)] struct Z { - post_mono: usize = X / 0, + post_mono: usize = X / 0, //~ WARN //~^ ERROR attempt to divide `1_usize` by zero } diff --git a/tests/ui/structs/default-field-values/struct-type-parameter-with-default.rs b/tests/ui/structs/default-field-values/struct-type-parameter-with-default.rs index c9e5aa9afb41f..5ab18d73f2f43 100644 --- a/tests/ui/structs/default-field-values/struct-type-parameter-with-default.rs +++ b/tests/ui/structs/default-field-values/struct-type-parameter-with-default.rs @@ -7,5 +7,8 @@ struct Foo { //~ NOTE: expected this type parameter //~| NOTE: expected type parameter //~| NOTE: expected type parameter //~| NOTE: the type of default fields referencing type parameters can't be assumed inside the struct defining them + //~| WARN: field `x` has a default value that is only checked when a value of `Foo` is constructed + //~| NOTE: this can't be const-evaluated until use + //~| NOTE: `#[warn(unevaluated_default_field_value)]` } fn main() {} diff --git a/tests/ui/structs/default-field-values/struct-type-parameter-with-default.stderr b/tests/ui/structs/default-field-values/struct-type-parameter-with-default.stderr index 553b49023835f..794648f910cfd 100644 --- a/tests/ui/structs/default-field-values/struct-type-parameter-with-default.stderr +++ b/tests/ui/structs/default-field-values/struct-type-parameter-with-default.stderr @@ -1,3 +1,18 @@ +warning: field `x` has a default value that is only checked when a value of `Foo` is constructed + --> $DIR/struct-type-parameter-with-default.rs:5:12 + | +LL | struct Foo { +LL | x: T = String::new(), + | ^^^^^^^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = note: `#[warn(unevaluated_default_field_value)]` on by default +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | struct Foo { + | + error[E0308]: mismatched types --> $DIR/struct-type-parameter-with-default.rs:5:12 | @@ -10,6 +25,6 @@ LL | x: T = String::new(), found struct `String` = note: the type of default fields referencing type parameters can't be assumed inside the struct defining them -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/structs/default-field-values/struct-with-only-lifetimes.rs b/tests/ui/structs/default-field-values/struct-with-only-lifetimes.rs new file mode 100644 index 0000000000000..efd6be763b0ac --- /dev/null +++ b/tests/ui/structs/default-field-values/struct-with-only-lifetimes.rs @@ -0,0 +1,10 @@ +// Ensure that for types with lifetime parameters, but not type or const parameters, we still +// evaluate the default field values. +#![feature(default_field_values)] + +pub struct Bat<'a> { + pub bax: &'a u8 = panic!("asdf"), + //~^ ERROR evaluation panicked: asdf +} + +fn main() {} diff --git a/tests/ui/structs/default-field-values/struct-with-only-lifetimes.stderr b/tests/ui/structs/default-field-values/struct-with-only-lifetimes.stderr new file mode 100644 index 0000000000000..6159194ce95f6 --- /dev/null +++ b/tests/ui/structs/default-field-values/struct-with-only-lifetimes.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: asdf + --> $DIR/struct-with-only-lifetimes.rs:6:23 + | +LL | pub bax: &'a u8 = panic!("asdf"), + | ^^^^^^^^^^^^^^ evaluation of `Bat::<'_>::bax::{constant#0}` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/structs/default-field-values/support.rs b/tests/ui/structs/default-field-values/support.rs index 5fb4408dcdc96..2d6eb47c1c534 100644 --- a/tests/ui/structs/default-field-values/support.rs +++ b/tests/ui/structs/default-field-values/support.rs @@ -39,12 +39,19 @@ const impl ConstDefault for i32 { pub struct Qux { bar: S = Qux::::S, // Associated constant from inherent impl + //~^ WARN baz: i32 = foo(), // Constant function + //~^ WARN bat: i32 = as T>::K, // Associated constant from explicit trait + //~^ WARN baq: i32 = Self::K, // Associated constant from implicit trait + //~^ WARN bay: i32 = C, // `const` parameter + //~^ WARN bak: Vec = Vec::new(), // Associated constant function + //~^ WARN ban: X = X::value(), // Associated constant function from `const` trait parameter + //~^ WARN } impl Qux { diff --git a/tests/ui/structs/default-field-values/support.stderr b/tests/ui/structs/default-field-values/support.stderr new file mode 100644 index 0000000000000..41f0eefc3bbac --- /dev/null +++ b/tests/ui/structs/default-field-values/support.stderr @@ -0,0 +1,107 @@ +warning: field `bar` has a default value that is only checked when a value of `Qux` is constructed + --> $DIR/support.rs:41:14 + | +LL | pub struct Qux { +LL | bar: S = Qux::::S, // Associated constant from inherent impl + | ^^^^^^^^^^^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = note: `#[warn(unevaluated_default_field_value)]` on by default +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Qux { + | + +warning: field `baz` has a default value that is only checked when a value of `Qux` is constructed + --> $DIR/support.rs:43:16 + | +LL | pub struct Qux { +... +LL | baz: i32 = foo(), // Constant function + | ^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Qux { + | + +warning: field `bat` has a default value that is only checked when a value of `Qux` is constructed + --> $DIR/support.rs:45:16 + | +LL | pub struct Qux { +... +LL | bat: i32 = as T>::K, // Associated constant from explicit trait + | ^^^^^^^^^^^^^^^^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Qux { + | + +warning: field `baq` has a default value that is only checked when a value of `Qux` is constructed + --> $DIR/support.rs:47:16 + | +LL | pub struct Qux { +... +LL | baq: i32 = Self::K, // Associated constant from implicit trait + | ^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Qux { + | + +warning: field `bay` has a default value that is only checked when a value of `Qux` is constructed + --> $DIR/support.rs:49:16 + | +LL | pub struct Qux { +... +LL | bay: i32 = C, // `const` parameter + | ^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Qux { + | + +warning: field `bak` has a default value that is only checked when a value of `Qux` is constructed + --> $DIR/support.rs:51:19 + | +LL | pub struct Qux { +... +LL | bak: Vec = Vec::new(), // Associated constant function + | ^^^^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Qux { + | + +warning: field `ban` has a default value that is only checked when a value of `Qux` is constructed + --> $DIR/support.rs:53:14 + | +LL | pub struct Qux { +... +LL | ban: X = X::value(), // Associated constant function from `const` trait parameter + | ^^^^^^^^^^ this can't be const-evaluated until use + | + = help: structs and enums with type and const parameters only evaluate their default field values during construction, not eagerly when declared +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[expect(unevaluated_default_field_value)] +LL | pub struct Qux { + | + +warning: 7 warnings emitted +