From aff8f132c08557a3654b9fd6fbf6307739d8bac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 23 Sep 2026 23:19:13 +0000 Subject: [PATCH 1/8] Always try to evaluate default field values and lint if it is too generic When trying to evaluate constants, if they reference const generics they will not be evaluated. When encountering this in default field values, emit a warn-by-default lint so that API designers are not caught of guard by this behavior. ``` 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:8:15 | LL | struct Z { LL | multiline_field: LL | () LL | = { | _______________^ LL | | f::(); | | -------- this can't be const-evaluated until use LL | | panic!(); LL | | }, | |_____________^ unevaluated default value | = 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 + #[allow(unevaluated_default_field_value)] LL | struct Z { | ``` Try spans better for `TooGeneric` errors. Support `Span` context in lints. --- .../rustc_const_eval/src/const_eval/error.rs | 18 ++++- .../rustc_hir_analysis/src/check/wfcheck.rs | 65 +++++++++++++------ .../rustc_hir_analysis/src/diagnostics.rs | 16 +++++ compiler/rustc_lint_defs/src/builtin.rs | 33 ++++++++++ compiler/rustc_middle/src/lint.rs | 3 + .../structs/default-field-values/failures.rs | 2 +- .../default-field-values/failures.stderr | 17 ++++- .../field-references-param-accurate-span.rs | 25 +++++++ ...ield-references-param-accurate-span.stderr | 45 +++++++++++++ .../field-references-param.allow_field.stderr | 17 +++++ .../field-references-param.rs | 19 +++++- .../field-references-param.warn.stderr | 59 +++++++++++++++++ .../default-field-values/invalid-const.rs | 9 ++- .../default-field-values/invalid-const.stderr | 44 ++++++++++++- .../post-mono.direct.stderr | 16 ++++- .../post-mono.indirect.stderr | 16 ++++- .../structs/default-field-values/post-mono.rs | 2 +- .../structs/default-field-values/support.rs | 3 + .../default-field-values/support.stderr | 45 +++++++++++++ 19 files changed, 419 insertions(+), 35 deletions(-) create mode 100644 tests/ui/structs/default-field-values/field-references-param-accurate-span.rs create mode 100644 tests/ui/structs/default-field-values/field-references-param-accurate-span.stderr create mode 100644 tests/ui/structs/default-field-values/field-references-param.allow_field.stderr create mode 100644 tests/ui/structs/default-field-values/field-references-param.warn.stderr create mode 100644 tests/ui/structs/default-field-values/support.stderr diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 1cb6a8aed947d..8235151d2659c 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -94,13 +94,21 @@ impl<'tcx> Into> for ConstEvalErrKind { } } -pub(crate) fn get_span_and_frames<'tcx>( +pub(crate) fn get_stacktrace<'tcx>( tcx: TyCtxtAt<'tcx>, stack: &[Frame<'tcx, impl Provenance, impl Sized>], -) -> (Span, Vec) { +) -> Vec> { let mut stacktrace = Frame::generate_stacktrace_from_stack(stack, *tcx); // Filter out `requires_caller_location` frames. stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx)); + stacktrace +} + +pub(crate) fn get_span_and_frames<'tcx>( + tcx: TyCtxtAt<'tcx>, + stack: &[Frame<'tcx, impl Provenance, impl Sized>], +) -> (Span, Vec) { + let stacktrace = get_stacktrace(tcx, stack); let span = stacktrace.last().map(|f| f.span).unwrap_or(tcx.span); let mut frames = Vec::new(); @@ -178,7 +186,11 @@ pub(super) fn report<'tcx>( // should remain silent. err_inval!(AlreadyReported(info)) => ErrorHandled::Reported(info, DUMMY_SP), err_inval!(Layout(LayoutError::TooGeneric(_))) | err_inval!(TooGeneric) => { - ErrorHandled::TooGeneric(DUMMY_SP) + let span = super::get_stacktrace(ecx.tcx, ecx.stack()) + .last() + .map(|f| f.span) + .unwrap_or(ecx.tcx.span); + ErrorHandled::TooGeneric(span) } err_inval!(Layout(LayoutError::ReferencesError(guar))) => { // This can occur in infallible promoteds e.g. when a non-existent type or field is diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index dc4b6a6fd319c..6c91cf0368119 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>>, @@ -989,24 +996,42 @@ pub(crate) fn check_type_defn<'tcx>( for variant in variants.iter() { // All field types must be well-formed. for field in &variant.fields { - if let Some(def_id) = field.value - && let Some(_ty) = tcx.type_of(def_id).no_bound_vars() - { - // FIXME(generic_const_exprs, default_field_values): this is a hack and needs to - // be refactored to check the instantiate-ability of the code better. - if let Some(def_id) = def_id.as_local() - && let 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 + if let Some(def_id) = field.value { + if let Err(ErrorHandled::TooGeneric(span)) = tcx.const_eval_poly(def_id) + && let Some(local_def_id) = def_id.as_local() + // Do not redundantly trigger lint if the feature is not actually available. + && tcx.features().default_field_values() { - // Do not evaluate bare `const` params, as those would ICE and are only - // usable if `#![feature(generic_const_exprs)]` is enabled. - } else { - // Evaluate the constant proactively, to emit an error if the constant has - // an unconditional error. We only do so if the const has no type params. - let _ = tcx.const_eval_poly(def_id); + let field_span = tcx.def_span(def_id); + let mut multispan: MultiSpan = field_span.into(); + if !span.is_dummy() && span != field_span { + multispan + .push_span_label(span, "this can't be const-evaluated until use"); + multispan.push_span_label(field_span, "unevaluated default value"); + } else { + 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..e56989170ef04 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -2157,3 +2157,19 @@ 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" +)] +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}#[allow(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..6c727b4462c47 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,35 @@ 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"#, +} 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..a31640b0f939d 100644 --- a/tests/ui/structs/default-field-values/failures.rs +++ b/tests/ui/structs/default-field-values/failures.rs @@ -22,7 +22,7 @@ pub struct Qux { bar: S = Self::S, baz: i32 = foo(), bat: i32 = as T>::K, - bay: i32 = C, + 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..87546f3b390fa 100644 --- a/tests/ui/structs/default-field-values/failures.stderr +++ b/tests/ui/structs/default-field-values/failures.stderr @@ -24,6 +24,21 @@ error: unions cannot have default field values LL | y: f32 = 2., | ^^ +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 + | + = 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 + #[allow(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 +129,7 @@ LL - let _ = Rak(.., 0); LL + let _ = Rak(0); | -error: aborting due to 10 previous errors +error: aborting due to 10 previous errors; 1 warning 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..e2f63e81a53f8 --- /dev/null +++ b/tests/ui/structs/default-field-values/field-references-param-accurate-span.rs @@ -0,0 +1,25 @@ +//@ build-fail +#![feature(default_field_values)] + +struct Z { + // Ensure that proper context is shown in lint. + multiline_field: + () + = { //~ WARN default value + f::(); + panic!(); //~ ERROR: explicit panic + }, +} + +pub const fn f() { + let _ = [0u8; N]; // <-- comment out this line to break downstream! +} + +fn use_generically() { + let x: Z = Z { .. }; +} + +fn main() { + let x: Z<0> = Z { .. }; + use_generically::<0>(); +} 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..c96328fd37bad --- /dev/null +++ b/tests/ui/structs/default-field-values/field-references-param-accurate-span.stderr @@ -0,0 +1,45 @@ +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:8:15 + | +LL | struct Z { +LL | // Ensure that proper context is shown in lint. +LL | multiline_field: +LL | () +LL | = { + | _______________^ +LL | | f::(); + | | -------- this can't be const-evaluated until use +LL | | panic!(); +LL | | }, + | |_____________^ unevaluated default value + | + = 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 + #[allow(unevaluated_default_field_value)] +LL | struct Z { + | + +error[E0080]: evaluation panicked: explicit panic + --> $DIR/field-references-param-accurate-span.rs:10:17 + | +LL | panic!(); + | ^^^^^^^^ evaluation of `Z::<0>::multiline_field::{constant#0}` failed here + +note: erroneous constant encountered + --> $DIR/field-references-param-accurate-span.rs:23:19 + | +LL | let x: Z<0> = Z { .. }; + | ^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/field-references-param-accurate-span.rs:23:19 + | +LL | let x: Z<0> = Z { .. }; + | ^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +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/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..daa6dc85ba8fd --- /dev/null +++ b/tests/ui/structs/default-field-values/field-references-param.allow_field.stderr @@ -0,0 +1,17 @@ +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 + | + = 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 + #[allow(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..2de97458c8b26 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]; // <-- comment out this line to break downstream! } 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..929b3a1fe95e8 --- /dev/null +++ b/tests/ui/structs/default-field-values/field-references-param.warn.stderr @@ -0,0 +1,59 @@ +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 + | + = 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 + #[allow(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: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[allow(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: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[allow(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: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[allow(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..8e7b73f2821f5 100644 --- a/tests/ui/structs/default-field-values/invalid-const.rs +++ b/tests/ui/structs/default-field-values/invalid-const.rs @@ -1,16 +1,19 @@ -#![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 bax: u8 = 130 + C, //~ WARN pub bat: u8 = 130 + 130, //~^ ERROR attempt to compute `130_u8 + 130_u8`, which would overflow pub bay: u8 = 1, // ok + pub bap: u8 = C, //~ WARN + pub ban: u8 = panic!("asdf"), //~ ERROR 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..3d45f7392d486 100644 --- a/tests/ui/structs/default-field-values/invalid-const.stderr +++ b/tests/ui/structs/default-field-values/invalid-const.stderr @@ -1,15 +1,55 @@ 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 + | + = 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 + #[allow(unevaluated_default_field_value)] +LL | pub struct Baz { + | + +error[E0080]: attempt to compute `130_u8 + 130_u8`, which would overflow + --> $DIR/invalid-const.rs:12:19 + | LL | pub bat: u8 = 130 + 130, | ^^^^^^^^^ evaluation of `Baz::::bat::{constant#0}` failed here -error: aborting due to 2 previous errors +warning: field `bap` 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 bap: u8 = C, + | ^ this can't be const-evaluated until use + | +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[allow(unevaluated_default_field_value)] +LL | pub struct Baz { + | + +error[E0080]: evaluation panicked: asdf + --> $DIR/invalid-const.rs:16:19 + | +LL | pub ban: u8 = panic!("asdf"), + | ^^^^^^^^^^^^^^ evaluation of `Baz::::ban::{constant#0}` failed here + +error: aborting due to 4 previous errors; 2 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..192328e77a487 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,17 @@ +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 + | + = 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 + #[allow(unevaluated_default_field_value)] +LL | struct Z { + | + error[E0080]: attempt to divide `1_usize` by zero --> $DIR/post-mono.rs:7:24 | @@ -18,6 +32,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..67df7f817e5d1 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,17 @@ +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 + | + = 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 + #[allow(unevaluated_default_field_value)] +LL | struct Z { + | + error[E0080]: attempt to divide `1_usize` by zero --> $DIR/post-mono.rs:7:24 | @@ -24,6 +38,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/support.rs b/tests/ui/structs/default-field-values/support.rs index 5fb4408dcdc96..1e717502c99a3 100644 --- a/tests/ui/structs/default-field-values/support.rs +++ b/tests/ui/structs/default-field-values/support.rs @@ -43,8 +43,11 @@ pub struct Qux { bat: i32 = as T>::K, // Associated constant from explicit trait baq: i32 = Self::K, // Associated constant from implicit trait 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..ae28e277910a4 --- /dev/null +++ b/tests/ui/structs/default-field-values/support.stderr @@ -0,0 +1,45 @@ +warning: field `bay` 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 | bay: i32 = C, // `const` parameter + | ^ this can't be const-evaluated until use + | + = 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 + #[allow(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:47:19 + | +LL | pub struct Qux { +... +LL | bak: Vec = Vec::new(), // Associated constant function + | ^^^^^^^^^^ this can't be const-evaluated until use + | +help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[allow(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:49: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: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value + | +LL + #[allow(unevaluated_default_field_value)] +LL | pub struct Qux { + | + +warning: 3 warnings emitted + From 282b810e6255cde7fae834892f981674f477db7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 24 Sep 2026 17:13:48 +0000 Subject: [PATCH 2/8] Suggest `expect` instead of `allow`, so that the annotation is only present when needed --- compiler/rustc_hir_analysis/src/diagnostics.rs | 2 +- tests/ui/structs/default-field-values/failures.stderr | 2 +- .../field-references-param-accurate-span.stderr | 2 +- .../field-references-param.allow_field.stderr | 2 +- .../field-references-param.warn.stderr | 8 ++++---- .../ui/structs/default-field-values/invalid-const.stderr | 4 ++-- .../structs/default-field-values/post-mono.direct.stderr | 2 +- .../default-field-values/post-mono.indirect.stderr | 2 +- tests/ui/structs/default-field-values/support.stderr | 6 +++--- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index e56989170ef04..1e4a4f70eff3a 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -2167,7 +2167,7 @@ pub(crate) struct UnevaluatedDefaultFieldValue { 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}#[allow(unevaluated_default_field_value)]\n", + code = "{padding}#[expect(unevaluated_default_field_value)]\n", applicability = "maybe-incorrect" )] pub(crate) struct_start: Span, diff --git a/tests/ui/structs/default-field-values/failures.stderr b/tests/ui/structs/default-field-values/failures.stderr index 87546f3b390fa..f0247a7c754a0 100644 --- a/tests/ui/structs/default-field-values/failures.stderr +++ b/tests/ui/structs/default-field-values/failures.stderr @@ -35,7 +35,7 @@ LL | bay: i32 = C, = 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 + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | pub struct Qux { | 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 index c96328fd37bad..dd94110b1afa7 100644 --- 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 @@ -16,7 +16,7 @@ LL | | }, = 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 + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | struct Z { | 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 index daa6dc85ba8fd..c0604fa487e15 100644 --- 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 @@ -9,7 +9,7 @@ LL | too_generic: usize = X + 1, = 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 + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | struct Z { | 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 index 929b3a1fe95e8..6386a260b6ada 100644 --- a/tests/ui/structs/default-field-values/field-references-param.warn.stderr +++ b/tests/ui/structs/default-field-values/field-references-param.warn.stderr @@ -9,7 +9,7 @@ LL | one: W = W::::new(), = 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 + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | struct Z { | @@ -23,7 +23,7 @@ LL | two: W = W::new(), | help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value | -LL + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | struct Z { | @@ -37,7 +37,7 @@ LL | too_generic: usize = X + 1, | help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value | -LL + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | struct Z { | @@ -51,7 +51,7 @@ LL | direct: usize = X, | help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value | -LL + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | struct Z { | diff --git a/tests/ui/structs/default-field-values/invalid-const.stderr b/tests/ui/structs/default-field-values/invalid-const.stderr index 3d45f7392d486..6be6bdf9e9264 100644 --- a/tests/ui/structs/default-field-values/invalid-const.stderr +++ b/tests/ui/structs/default-field-values/invalid-const.stderr @@ -20,7 +20,7 @@ LL | pub bax: u8 = 130 + C, = 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 + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | pub struct Baz { | @@ -40,7 +40,7 @@ LL | pub bap: u8 = C, | help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value | -LL + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | pub struct Baz { | 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 192328e77a487..d46f3de61cc04 100644 --- a/tests/ui/structs/default-field-values/post-mono.direct.stderr +++ b/tests/ui/structs/default-field-values/post-mono.direct.stderr @@ -8,7 +8,7 @@ LL | post_mono: usize = X / 0, = 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 + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | struct Z { | 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 67df7f817e5d1..c10a69e2f4db3 100644 --- a/tests/ui/structs/default-field-values/post-mono.indirect.stderr +++ b/tests/ui/structs/default-field-values/post-mono.indirect.stderr @@ -8,7 +8,7 @@ LL | post_mono: usize = X / 0, = 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 + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | struct Z { | diff --git a/tests/ui/structs/default-field-values/support.stderr b/tests/ui/structs/default-field-values/support.stderr index ae28e277910a4..4c8f46c9b3e2b 100644 --- a/tests/ui/structs/default-field-values/support.stderr +++ b/tests/ui/structs/default-field-values/support.stderr @@ -9,7 +9,7 @@ LL | bay: i32 = C, // `const` parameter = 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 + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | pub struct Qux { | @@ -23,7 +23,7 @@ LL | bak: Vec = Vec::new(), // Associated constant function | help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value | -LL + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | pub struct Qux { | @@ -37,7 +37,7 @@ LL | ban: X = X::value(), // Associated constant function from `const` trait | help: if this behavior is acceptable, allow the lint and preferably write a test relying on the default value | -LL + #[allow(unevaluated_default_field_value)] +LL + #[expect(unevaluated_default_field_value)] LL | pub struct Qux { | From 3000f746e074e618b22f86c10257b753a19bc702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 24 Sep 2026 17:53:50 +0000 Subject: [PATCH 3/8] Use proposed strategy of never evaluating default field values for types with generic params --- .../rustc_hir_analysis/src/check/wfcheck.rs | 22 +++--- .../rustc_hir_analysis/src/diagnostics.rs | 7 +- .../structs/default-field-values/failures.rs | 6 +- .../default-field-values/failures.stderr | 49 ++++++++++++- ...ield-references-param-accurate-span.stderr | 4 +- .../field-references-param.allow_field.stderr | 1 + .../field-references-param.warn.stderr | 4 ++ .../default-field-values/invalid-const.rs | 12 ++-- .../default-field-values/invalid-const.stderr | 36 +++++++--- .../post-mono.direct.stderr | 1 + .../post-mono.indirect.stderr | 1 + .../struct-type-parameter-with-default.rs | 3 + .../struct-type-parameter-with-default.stderr | 17 ++++- .../struct-with-only-lifetimes.rs | 10 +++ .../struct-with-only-lifetimes.stderr | 9 +++ .../structs/default-field-values/support.rs | 4 ++ .../default-field-values/support.stderr | 72 +++++++++++++++++-- 17 files changed, 219 insertions(+), 39 deletions(-) create mode 100644 tests/ui/structs/default-field-values/struct-with-only-lifetimes.rs create mode 100644 tests/ui/structs/default-field-values/struct-with-only-lifetimes.stderr diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 6c91cf0368119..d7714996ef1e0 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -992,28 +992,24 @@ 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 = + LazyCell::new(|| 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 { - if let Err(ErrorHandled::TooGeneric(span)) = tcx.const_eval_poly(def_id) + 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. - && tcx.features().default_field_values() { + // Do not redundantly trigger lint if the feature is not actually available. + let field_span = tcx.def_span(def_id); let mut multispan: MultiSpan = field_span.into(); - if !span.is_dummy() && span != field_span { - multispan - .push_span_label(span, "this can't be const-evaluated until use"); - multispan.push_span_label(field_span, "unevaluated default value"); - } else { - multispan.push_span_label( - field_span, - "this can't be const-evaluated until use", - ); - } + 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); diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index 1e4a4f70eff3a..9fbab8af7d353 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -2160,7 +2160,12 @@ pub(crate) struct ParamInTyOfConstParam<'tcx> { #[derive(Diagnostic)] #[diag( - "field `{$field}` has a default value that is only checked when a value of `{$ty}` is constructed" + "field `{$field}` has a default value that is only checked when a value of `{$ty}` is \ + constructed" +)] +#[help( + "structs 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, diff --git a/tests/ui/structs/default-field-values/failures.rs b/tests/ui/structs/default-field-values/failures.rs index a31640b0f939d..d796053f1cb24 100644 --- a/tests/ui/structs/default-field-values/failures.rs +++ b/tests/ui/structs/default-field-values/failures.rs @@ -19,9 +19,9 @@ pub struct Bar { #[derive(Default)] pub struct Qux { - bar: S = Self::S, - baz: i32 = foo(), - bat: i32 = as T>::K, + bar: S = Self::S, //~ WARN + baz: i32 = foo(), //~ WARN + bat: i32 = as T>::K, //~ WARN bay: i32 = C, //~ WARN } diff --git a/tests/ui/structs/default-field-values/failures.stderr b/tests/ui/structs/default-field-values/failures.stderr index f0247a7c754a0..aa4b38f9eb7f5 100644 --- a/tests/ui/structs/default-field-values/failures.stderr +++ b/tests/ui/structs/default-field-values/failures.stderr @@ -24,6 +24,51 @@ 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 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 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 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 | @@ -32,7 +77,7 @@ LL | pub struct Qux { LL | bay: i32 = C, | ^ this can't be const-evaluated until use | - = note: `#[warn(unevaluated_default_field_value)]` on by default + = help: structs 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)] @@ -129,7 +174,7 @@ LL - let _ = Rak(.., 0); LL + let _ = Rak(0); | -error: aborting due to 10 previous errors; 1 warning emitted +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.stderr b/tests/ui/structs/default-field-values/field-references-param-accurate-span.stderr index dd94110b1afa7..06abfcd88062d 100644 --- 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 @@ -8,11 +8,11 @@ LL | () LL | = { | _______________^ LL | | f::(); - | | -------- this can't be const-evaluated until use LL | | panic!(); LL | | }, - | |_____________^ unevaluated default value + | |_____________^ this can't be const-evaluated until use | + = help: structs 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 | 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 index c0604fa487e15..c25c96db5ae2f 100644 --- 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 @@ -6,6 +6,7 @@ LL | struct Z { LL | too_generic: usize = X + 1, | ^^^^^ this can't be const-evaluated until use | + = help: structs 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 | 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 index 6386a260b6ada..95b663d7d165b 100644 --- a/tests/ui/structs/default-field-values/field-references-param.warn.stderr +++ b/tests/ui/structs/default-field-values/field-references-param.warn.stderr @@ -6,6 +6,7 @@ LL | struct Z { LL | one: W = W::::new(), | ^^^^^^^^^^^^^ this can't be const-evaluated until use | + = help: structs 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 | @@ -21,6 +22,7 @@ LL | struct Z { LL | two: W = W::new(), | ^^^^^^^^ this can't be const-evaluated until use | + = help: structs 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)] @@ -35,6 +37,7 @@ LL | struct Z { LL | too_generic: usize = X + 1, | ^^^^^ this can't be const-evaluated until use | + = help: structs 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)] @@ -49,6 +52,7 @@ LL | struct Z { LL | direct: usize = X, | ^ this can't be const-evaluated until use | + = help: structs 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)] diff --git a/tests/ui/structs/default-field-values/invalid-const.rs b/tests/ui/structs/default-field-values/invalid-const.rs index 8e7b73f2821f5..fa93f64c8e4f6 100644 --- a/tests/ui/structs/default-field-values/invalid-const.rs +++ b/tests/ui/structs/default-field-values/invalid-const.rs @@ -9,11 +9,15 @@ pub struct Bat { pub struct Baz { pub bax: u8 = 130 + C, //~ WARN - pub bat: u8 = 130 + 130, - //~^ ERROR attempt to compute `130_u8 + 130_u8`, which would overflow - pub bay: u8 = 1, // ok + 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 ban: u8 = panic!("asdf"), //~ ERROR evaluation panicked: asdf + pub ban: u8 = panic!("asdf"), + // ^ If we run `const_eval_poly` without restricting const params, this would be + // evaluation panicked: asdf + // FIXME: This whould WARN! } 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 6be6bdf9e9264..3fe7bce0c4506 100644 --- a/tests/ui/structs/default-field-values/invalid-const.stderr +++ b/tests/ui/structs/default-field-values/invalid-const.stderr @@ -17,6 +17,7 @@ LL | pub struct Baz { LL | pub bax: u8 = 130 + C, | ^^^^^^^ this can't be const-evaluated until use | + = help: structs 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 | @@ -24,32 +25,51 @@ LL + #[expect(unevaluated_default_field_value)] LL | pub struct Baz { | -error[E0080]: attempt to compute `130_u8 + 130_u8`, which would overflow +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 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 +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 bap: u8 = C, +LL | pub bay: u8 = 1, | ^ this can't be const-evaluated until use | + = help: structs 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[E0080]: evaluation panicked: asdf +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 ban: u8 = panic!("asdf"), - | ^^^^^^^^^^^^^^ evaluation of `Baz::::ban::{constant#0}` failed here +LL | pub struct Baz { +... +LL | pub bap: u8 = C, + | ^ this can't be const-evaluated until use + | + = help: structs 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 4 previous errors; 2 warnings emitted +error: aborting due to 2 previous errors; 4 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 d46f3de61cc04..e005b89d9a25e 100644 --- a/tests/ui/structs/default-field-values/post-mono.direct.stderr +++ b/tests/ui/structs/default-field-values/post-mono.direct.stderr @@ -5,6 +5,7 @@ LL | struct Z { LL | post_mono: usize = X / 0, | ^^^^^ this can't be const-evaluated until use | + = help: structs 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 | 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 c10a69e2f4db3..8787698d0c0fd 100644 --- a/tests/ui/structs/default-field-values/post-mono.indirect.stderr +++ b/tests/ui/structs/default-field-values/post-mono.indirect.stderr @@ -5,6 +5,7 @@ LL | struct Z { LL | post_mono: usize = X / 0, | ^^^^^ this can't be const-evaluated until use | + = help: structs 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 | 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..b307fe2ee0c13 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 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 1e717502c99a3..2d6eb47c1c534 100644 --- a/tests/ui/structs/default-field-values/support.rs +++ b/tests/ui/structs/default-field-values/support.rs @@ -39,9 +39,13 @@ 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 diff --git a/tests/ui/structs/default-field-values/support.stderr b/tests/ui/structs/default-field-values/support.stderr index 4c8f46c9b3e2b..0ecd0e4203864 100644 --- a/tests/ui/structs/default-field-values/support.stderr +++ b/tests/ui/structs/default-field-values/support.stderr @@ -1,12 +1,72 @@ -warning: field `bay` has a default value that is only checked when a value of `Qux` is constructed +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 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 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 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 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 | - = note: `#[warn(unevaluated_default_field_value)]` on by default + = help: structs 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)] @@ -14,13 +74,14 @@ 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:47:19 + --> $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 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)] @@ -28,18 +89,19 @@ 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:49:14 + --> $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 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: 3 warnings emitted +warning: 7 warnings emitted From 1435c41fd693b2635ddc100f0e446af20b48c747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 5 Oct 2026 17:14:48 +0000 Subject: [PATCH 4/8] review comments --- .../rustc_const_eval/src/const_eval/error.rs | 18 +++--------------- compiler/rustc_hir_analysis/src/diagnostics.rs | 4 ++-- compiler/rustc_lint_defs/src/builtin.rs | 1 + .../default-field-values/failures.stderr | 8 ++++---- ...field-references-param-accurate-span.stderr | 2 +- .../field-references-param.allow_field.stderr | 2 +- .../field-references-param.warn.stderr | 8 ++++---- .../default-field-values/invalid-const.stderr | 8 ++++---- .../post-mono.direct.stderr | 2 +- .../post-mono.indirect.stderr | 2 +- .../struct-type-parameter-with-default.stderr | 2 +- .../default-field-values/support.stderr | 14 +++++++------- 12 files changed, 30 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 8235151d2659c..1cb6a8aed947d 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -94,21 +94,13 @@ impl<'tcx> Into> for ConstEvalErrKind { } } -pub(crate) fn get_stacktrace<'tcx>( +pub(crate) fn get_span_and_frames<'tcx>( tcx: TyCtxtAt<'tcx>, stack: &[Frame<'tcx, impl Provenance, impl Sized>], -) -> Vec> { +) -> (Span, Vec) { let mut stacktrace = Frame::generate_stacktrace_from_stack(stack, *tcx); // Filter out `requires_caller_location` frames. stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx)); - stacktrace -} - -pub(crate) fn get_span_and_frames<'tcx>( - tcx: TyCtxtAt<'tcx>, - stack: &[Frame<'tcx, impl Provenance, impl Sized>], -) -> (Span, Vec) { - let stacktrace = get_stacktrace(tcx, stack); let span = stacktrace.last().map(|f| f.span).unwrap_or(tcx.span); let mut frames = Vec::new(); @@ -186,11 +178,7 @@ pub(super) fn report<'tcx>( // should remain silent. err_inval!(AlreadyReported(info)) => ErrorHandled::Reported(info, DUMMY_SP), err_inval!(Layout(LayoutError::TooGeneric(_))) | err_inval!(TooGeneric) => { - let span = super::get_stacktrace(ecx.tcx, ecx.stack()) - .last() - .map(|f| f.span) - .unwrap_or(ecx.tcx.span); - ErrorHandled::TooGeneric(span) + ErrorHandled::TooGeneric(DUMMY_SP) } err_inval!(Layout(LayoutError::ReferencesError(guar))) => { // This can occur in infallible promoteds e.g. when a non-existent type or field is diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index 9fbab8af7d353..696b0d193c267 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -2164,8 +2164,8 @@ pub(crate) struct ParamInTyOfConstParam<'tcx> { constructed" )] #[help( - "structs with type and const parameters only evaluate their default field values during \ - construction, not eagerly when declared" + "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, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 6c727b4462c47..0f9a8b0f69cc2 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -5954,4 +5954,5 @@ declare_lint! { 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/tests/ui/structs/default-field-values/failures.stderr b/tests/ui/structs/default-field-values/failures.stderr index aa4b38f9eb7f5..13e6480236ff5 100644 --- a/tests/ui/structs/default-field-values/failures.stderr +++ b/tests/ui/structs/default-field-values/failures.stderr @@ -31,7 +31,7 @@ LL | pub struct Qux { LL | bar: S = Self::S, | ^^^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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 | @@ -47,7 +47,7 @@ LL | bar: S = Self::S, LL | baz: i32 = foo(), | ^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] @@ -62,7 +62,7 @@ LL | pub struct Qux { LL | bat: i32 = as T>::K, | ^^^^^^^^^^^^^^^^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] @@ -77,7 +77,7 @@ LL | pub struct Qux { LL | bay: i32 = C, | ^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] 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 index 06abfcd88062d..acc9b3e2571bf 100644 --- 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 @@ -12,7 +12,7 @@ LL | | panic!(); LL | | }, | |_____________^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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 | 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 index c25c96db5ae2f..52dd197fb9fdb 100644 --- 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 @@ -6,7 +6,7 @@ LL | struct Z { LL | too_generic: usize = X + 1, | ^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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 | 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 index 95b663d7d165b..60376fdadd96c 100644 --- a/tests/ui/structs/default-field-values/field-references-param.warn.stderr +++ b/tests/ui/structs/default-field-values/field-references-param.warn.stderr @@ -6,7 +6,7 @@ LL | struct Z { LL | one: W = W::::new(), | ^^^^^^^^^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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 | @@ -22,7 +22,7 @@ LL | struct Z { LL | two: W = W::new(), | ^^^^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] @@ -37,7 +37,7 @@ LL | struct Z { LL | too_generic: usize = X + 1, | ^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] @@ -52,7 +52,7 @@ LL | struct Z { LL | direct: usize = X, | ^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] diff --git a/tests/ui/structs/default-field-values/invalid-const.stderr b/tests/ui/structs/default-field-values/invalid-const.stderr index 3fe7bce0c4506..0284c4a85b97c 100644 --- a/tests/ui/structs/default-field-values/invalid-const.stderr +++ b/tests/ui/structs/default-field-values/invalid-const.stderr @@ -17,7 +17,7 @@ LL | pub struct Baz { LL | pub bax: u8 = 130 + C, | ^^^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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 | @@ -33,7 +33,7 @@ LL | pub bax: u8 = 130 + C, LL | pub bat: u8 = 130 + 130, | ^^^^^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] @@ -48,7 +48,7 @@ LL | pub struct Baz { LL | pub bay: u8 = 1, | ^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] @@ -63,7 +63,7 @@ LL | pub struct Baz { LL | pub bap: u8 = C, | ^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] 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 e005b89d9a25e..0342d119c298b 100644 --- a/tests/ui/structs/default-field-values/post-mono.direct.stderr +++ b/tests/ui/structs/default-field-values/post-mono.direct.stderr @@ -5,7 +5,7 @@ LL | struct Z { LL | post_mono: usize = X / 0, | ^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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 | 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 8787698d0c0fd..3a62080d62cc9 100644 --- a/tests/ui/structs/default-field-values/post-mono.indirect.stderr +++ b/tests/ui/structs/default-field-values/post-mono.indirect.stderr @@ -5,7 +5,7 @@ LL | struct Z { LL | post_mono: usize = X / 0, | ^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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 | 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 b307fe2ee0c13..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 @@ -5,7 +5,7 @@ LL | struct Foo { LL | x: T = String::new(), | ^^^^^^^^^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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 | diff --git a/tests/ui/structs/default-field-values/support.stderr b/tests/ui/structs/default-field-values/support.stderr index 0ecd0e4203864..41f0eefc3bbac 100644 --- a/tests/ui/structs/default-field-values/support.stderr +++ b/tests/ui/structs/default-field-values/support.stderr @@ -5,7 +5,7 @@ LL | pub struct Qux { LL | bar: S = Qux::::S, // Associated constant from inherent impl | ^^^^^^^^^^^^^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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 | @@ -21,7 +21,7 @@ LL | pub struct Qux { LL | baz: i32 = foo(), // Constant function | ^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] @@ -36,7 +36,7 @@ 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 with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] @@ -51,7 +51,7 @@ LL | pub struct Qux { LL | baq: i32 = Self::K, // Associated constant from implicit trait | ^^^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] @@ -66,7 +66,7 @@ LL | pub struct Qux { LL | bay: i32 = C, // `const` parameter | ^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] @@ -81,7 +81,7 @@ LL | pub struct Qux { LL | bak: Vec = Vec::new(), // Associated constant function | ^^^^^^^^^^ this can't be const-evaluated until use | - = help: structs with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] @@ -96,7 +96,7 @@ 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 with type and const parameters only evaluate their default field values during construction, not eagerly when declared + = 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)] From 702470b4a5a66283596745f2d16b5adcad3eb00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 5 Oct 2026 17:24:42 +0000 Subject: [PATCH 5/8] Update test comments --- .../field-references-param-accurate-span.rs | 6 +++++- .../field-references-param-accurate-span.stderr | 8 ++++---- .../default-field-values/field-references-param.rs | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) 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 index e2f63e81a53f8..451e394eefc49 100644 --- 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 @@ -1,4 +1,5 @@ //@ build-fail +// https://github.com/rust-lang/rust/issues/146496 #![feature(default_field_values)] struct Z { @@ -12,7 +13,10 @@ struct Z { } pub const fn f() { - let _ = [0u8; N]; // <-- comment out this line to break downstream! + // *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]; } fn use_generically() { 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 index acc9b3e2571bf..e02341be5f4be 100644 --- 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 @@ -1,5 +1,5 @@ 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:8:15 + --> $DIR/field-references-param-accurate-span.rs:9:15 | LL | struct Z { LL | // Ensure that proper context is shown in lint. @@ -21,19 +21,19 @@ LL | struct Z { | error[E0080]: evaluation panicked: explicit panic - --> $DIR/field-references-param-accurate-span.rs:10:17 + --> $DIR/field-references-param-accurate-span.rs:11:17 | LL | panic!(); | ^^^^^^^^ evaluation of `Z::<0>::multiline_field::{constant#0}` failed here note: erroneous constant encountered - --> $DIR/field-references-param-accurate-span.rs:23:19 + --> $DIR/field-references-param-accurate-span.rs:27:19 | LL | let x: Z<0> = Z { .. }; | ^^^^^^^^ note: erroneous constant encountered - --> $DIR/field-references-param-accurate-span.rs:23:19 + --> $DIR/field-references-param-accurate-span.rs:27:19 | LL | let x: Z<0> = Z { .. }; | ^^^^^^^^ 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 2de97458c8b26..dadef09a1de50 100644 --- a/tests/ui/structs/default-field-values/field-references-param.rs +++ b/tests/ui/structs/default-field-values/field-references-param.rs @@ -31,7 +31,7 @@ struct Z { } pub const fn f() { - let _ = [0u8; N]; // <-- comment out this line to break downstream! + let _ = [0u8; N]; } fn use_generically() { From 1788cb4d439ef6dd3bde241cf6f62ff151688242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 5 Oct 2026 17:36:09 +0000 Subject: [PATCH 6/8] Add more cases to test --- .../field-references-param-accurate-span.rs | 14 +++++-- ...ield-references-param-accurate-span.stderr | 40 ++++++++++++++++--- 2 files changed, 45 insertions(+), 9 deletions(-) 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 index 451e394eefc49..baba9d8d844a3 100644 --- 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 @@ -8,7 +8,9 @@ struct Z { () = { //~ WARN default value f::(); - panic!(); //~ ERROR: explicit panic + panic!(); + //~^ ERROR: explicit panic + //~| ERROR: explicit panic }, } @@ -19,11 +21,17 @@ pub const fn f() { let _ = [0u8; N]; } +const fn const_use_generically() { + let x: Z = Z { .. }; +} + fn use_generically() { let x: Z = Z { .. }; } fn main() { - let x: Z<0> = Z { .. }; - use_generically::<0>(); + 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 index e02341be5f4be..a0b937afd1112 100644 --- 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 @@ -9,6 +9,8 @@ LL | = { | _______________^ LL | | f::(); LL | | panic!(); +LL | | +LL | | LL | | }, | |_____________^ this can't be const-evaluated until use | @@ -24,22 +26,48 @@ error[E0080]: evaluation panicked: explicit panic --> $DIR/field-references-param-accurate-span.rs:11:17 | LL | panic!(); - | ^^^^^^^^ evaluation of `Z::<0>::multiline_field::{constant#0}` failed here + | ^^^^^^^^ evaluation of `Z::<1>::multiline_field::{constant#0}` failed here note: erroneous constant encountered - --> $DIR/field-references-param-accurate-span.rs:27:19 + --> $DIR/field-references-param-accurate-span.rs:33:19 | -LL | let x: Z<0> = Z { .. }; +LL | let x: Z<1> = Z { .. }; | ^^^^^^^^ note: erroneous constant encountered - --> $DIR/field-references-param-accurate-span.rs:27:19 + --> $DIR/field-references-param-accurate-span.rs:33:19 | -LL | let x: Z<0> = Z { .. }; +LL | let x: Z<1> = Z { .. }; | ^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 1 previous error; 1 warning emitted +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`. From c9ed30fbf5487125ebefaa45ceab50ccb41feeda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 5 Oct 2026 17:37:45 +0000 Subject: [PATCH 7/8] Eager "requires mono" check --- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index d7714996ef1e0..f28a8112e8f42 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -993,13 +993,13 @@ pub(crate) fn check_type_defn<'tcx>( let variants = adt_def.variants(); let packed = adt_def.repr().packed(); let own_params_require_monomorphization = - LazyCell::new(|| tcx.generics_of(item).own_requires_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 { - if !*own_params_require_monomorphization { + 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() From 4531f72edc356765e9896b2b5774493819131d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 5 Oct 2026 17:54:38 +0000 Subject: [PATCH 8/8] Account for macro calls in default field values --- .../rustc_hir_analysis/src/check/wfcheck.rs | 4 ++- .../default-field-values/invalid-const.rs | 6 ++-- .../default-field-values/invalid-const.stderr | 32 ++++++++++++++++++- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f28a8112e8f42..e7c5161bb0d43 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1006,7 +1006,9 @@ pub(crate) fn check_type_defn<'tcx>( { // Do not redundantly trigger lint if the feature is not actually available. - let field_span = tcx.def_span(def_id); + // 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"); diff --git a/tests/ui/structs/default-field-values/invalid-const.rs b/tests/ui/structs/default-field-values/invalid-const.rs index fa93f64c8e4f6..9822489b524ce 100644 --- a/tests/ui/structs/default-field-values/invalid-const.rs +++ b/tests/ui/structs/default-field-values/invalid-const.rs @@ -14,10 +14,10 @@ pub struct Baz { // attempt to compute `130_u8 + 130_u8`, which would overflow pub bay: u8 = 1, //~ WARN pub bap: u8 = C, //~ WARN - pub ban: u8 = panic!("asdf"), - // ^ If we run `const_eval_poly` without restricting const params, this would be + 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 - // FIXME: This whould WARN! } 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 0284c4a85b97c..c9962dbbe38d5 100644 --- a/tests/ui/structs/default-field-values/invalid-const.stderr +++ b/tests/ui/structs/default-field-values/invalid-const.stderr @@ -70,6 +70,36 @@ LL + #[expect(unevaluated_default_field_value)] LL | pub struct Baz { | -error: aborting due to 2 previous errors; 4 warnings emitted +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; 6 warnings emitted For more information about this error, try `rustc --explain E0080`.