@@ -52,7 +52,7 @@ use rustc_span::edition::Edition;
5252use rustc_span:: source_map:: Spanned ;
5353use rustc_span:: symbol:: { kw, sym, Ident , Symbol } ;
5454use rustc_span:: { BytePos , InnerSpan , Span } ;
55- use rustc_target:: abi:: VariantIdx ;
55+ use rustc_target:: abi:: { Abi , VariantIdx } ;
5656use rustc_trait_selection:: traits:: { self , misc:: can_type_implement_copy} ;
5757
5858use crate :: nonstandard_style:: { method_context, MethodLateContext } ;
@@ -2413,8 +2413,34 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24132413 }
24142414
24152415 /// Information about why a type cannot be initialized this way.
2416- /// Contains an error message and optionally a span to point at.
2417- type InitError = ( String , Option < Span > ) ;
2416+ struct InitError {
2417+ message : String ,
2418+ /// Spans from struct fields and similar that can be obtained from just the type.
2419+ span : Option < Span > ,
2420+ /// Used to report a trace through adts.
2421+ nested : Option < Box < InitError > > ,
2422+ }
2423+ impl InitError {
2424+ fn spanned ( self , span : Span ) -> InitError {
2425+ Self { span : Some ( span) , ..self }
2426+ }
2427+
2428+ fn nested ( self , nested : impl Into < Option < InitError > > ) -> InitError {
2429+ assert ! ( self . nested. is_none( ) ) ;
2430+ Self { nested : nested. into ( ) . map ( Box :: new) , ..self }
2431+ }
2432+ }
2433+
2434+ impl < ' a > From < & ' a str > for InitError {
2435+ fn from ( s : & ' a str ) -> Self {
2436+ s. to_owned ( ) . into ( )
2437+ }
2438+ }
2439+ impl From < String > for InitError {
2440+ fn from ( message : String ) -> Self {
2441+ Self { message, span : None , nested : None }
2442+ }
2443+ }
24182444
24192445 /// Test if this constant is all-0.
24202446 fn is_zero ( expr : & hir:: Expr < ' _ > ) -> bool {
@@ -2470,25 +2496,54 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24702496
24712497 fn variant_find_init_error < ' tcx > (
24722498 cx : & LateContext < ' tcx > ,
2499+ ty : Ty < ' tcx > ,
24732500 variant : & VariantDef ,
24742501 substs : ty:: SubstsRef < ' tcx > ,
24752502 descr : & str ,
24762503 init : InitKind ,
24772504 ) -> Option < InitError > {
2478- variant. fields . iter ( ) . find_map ( |field| {
2479- ty_find_init_error ( cx, field. ty ( cx. tcx , substs) , init) . map ( |( mut msg , span ) | {
2480- if span . is_none ( ) {
2481- // Point to this field, should be helpful for figuring
2482- // out where the source of the error is.
2483- let span = cx. tcx . def_span ( field. did ) ;
2484- write ! ( & mut msg , " (in this {descr})" ) . unwrap ( ) ;
2485- ( msg , Some ( span ) )
2505+ let mut field_err = variant. fields . iter ( ) . find_map ( |field| {
2506+ ty_find_init_error ( cx, field. ty ( cx. tcx , substs) , init) . map ( |mut err | {
2507+ if !field . did . is_local ( ) {
2508+ err
2509+ } else if err . span . is_none ( ) {
2510+ err . span = Some ( cx. tcx . def_span ( field. did ) ) ;
2511+ write ! ( & mut err . message , " (in this {descr})" ) . unwrap ( ) ;
2512+ err
24862513 } else {
2487- // Just forward.
2488- ( msg, span)
2514+ InitError :: from ( format ! ( "in this {descr}" ) )
2515+ . spanned ( cx. tcx . def_span ( field. did ) )
2516+ . nested ( err)
24892517 }
24902518 } )
2491- } )
2519+ } ) ;
2520+
2521+ // Check if this ADT has a constrained layout (like `NonNull` and friends).
2522+ if let Ok ( layout) = cx. tcx . layout_of ( cx. param_env . and ( ty) ) {
2523+ if let Abi :: Scalar ( scalar) | Abi :: ScalarPair ( scalar, _) = & layout. abi {
2524+ let range = scalar. valid_range ( cx) ;
2525+ let msg = if !range. contains ( 0 ) {
2526+ "must be non-null"
2527+ } else if init == InitKind :: Uninit && !scalar. is_always_valid ( cx) {
2528+ // Prefer reporting on the fields over the entire struct for uninit,
2529+ // as the information bubbles out and it may be unclear why the type can't
2530+ // be null from just its outside signature.
2531+
2532+ "must be initialized inside its custom valid range"
2533+ } else {
2534+ return field_err;
2535+ } ;
2536+ if let Some ( field_err) = & mut field_err {
2537+ // Most of the time, if the field error is the same as the struct error,
2538+ // the struct error only happens because of the field error.
2539+ if field_err. message . contains ( msg) {
2540+ field_err. message = format ! ( "because {}" , field_err. message) ;
2541+ }
2542+ }
2543+ return Some ( InitError :: from ( format ! ( "`{ty}` {msg}" ) ) . nested ( field_err) ) ;
2544+ }
2545+ }
2546+ field_err
24922547 }
24932548
24942549 /// Return `Some` only if we are sure this type does *not*
@@ -2501,63 +2556,36 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
25012556 use rustc_type_ir:: sty:: TyKind :: * ;
25022557 match ty. kind ( ) {
25032558 // Primitive types that don't like 0 as a value.
2504- Ref ( ..) => Some ( ( "references must be non-null" . to_string ( ) , None ) ) ,
2505- Adt ( ..) if ty. is_box ( ) => Some ( ( "`Box` must be non-null" . to_string ( ) , None ) ) ,
2506- FnPtr ( ..) => Some ( ( "function pointers must be non-null" . to_string ( ) , None ) ) ,
2507- Never => Some ( ( "the `!` type has no valid value" . to_string ( ) , None ) ) ,
2559+ Ref ( ..) => Some ( "references must be non-null" . into ( ) ) ,
2560+ Adt ( ..) if ty. is_box ( ) => Some ( "`Box` must be non-null" . into ( ) ) ,
2561+ FnPtr ( ..) => Some ( "function pointers must be non-null" . into ( ) ) ,
2562+ Never => Some ( "the `!` type has no valid value" . into ( ) ) ,
25082563 RawPtr ( tm) if matches ! ( tm. ty. kind( ) , Dynamic ( ..) ) =>
25092564 // raw ptr to dyn Trait
25102565 {
2511- Some ( ( "the vtable of a wide raw pointer must be non-null" . to_string ( ) , None ) )
2566+ Some ( "the vtable of a wide raw pointer must be non-null" . into ( ) )
25122567 }
25132568 // Primitive types with other constraints.
25142569 Bool if init == InitKind :: Uninit => {
2515- Some ( ( "booleans must be either `true` or `false`" . to_string ( ) , None ) )
2570+ Some ( "booleans must be either `true` or `false`" . into ( ) )
25162571 }
25172572 Char if init == InitKind :: Uninit => {
2518- Some ( ( "characters must be a valid Unicode codepoint" . to_string ( ) , None ) )
2573+ Some ( "characters must be a valid Unicode codepoint" . into ( ) )
25192574 }
25202575 Int ( _) | Uint ( _) if init == InitKind :: Uninit => {
2521- Some ( ( "integers must not be uninitialized" . to_string ( ) , None ) )
2522- }
2523- Float ( _) if init == InitKind :: Uninit => {
2524- Some ( ( "floats must not be uninitialized" . to_string ( ) , None ) )
2576+ Some ( "integers must be initialized" . into ( ) )
25252577 }
2578+ Float ( _) if init == InitKind :: Uninit => Some ( "floats must be initialized" . into ( ) ) ,
25262579 RawPtr ( _) if init == InitKind :: Uninit => {
2527- Some ( ( "raw pointers must not be uninitialized" . to_string ( ) , None ) )
2580+ Some ( "raw pointers must be initialized" . into ( ) )
25282581 }
25292582 // Recurse and checks for some compound types. (but not unions)
25302583 Adt ( adt_def, substs) if !adt_def. is_union ( ) => {
2531- // First check if this ADT has a layout attribute (like `NonNull` and friends).
2532- use std:: ops:: Bound ;
2533- match cx. tcx . layout_scalar_valid_range ( adt_def. did ( ) ) {
2534- // We exploit here that `layout_scalar_valid_range` will never
2535- // return `Bound::Excluded`. (And we have tests checking that we
2536- // handle the attribute correctly.)
2537- // We don't add a span since users cannot declare such types anyway.
2538- ( Bound :: Included ( lo) , Bound :: Included ( hi) ) if 0 < lo && lo < hi => {
2539- return Some ( ( format ! ( "`{}` must be non-null" , ty) , None ) ) ;
2540- }
2541- ( Bound :: Included ( lo) , Bound :: Unbounded ) if 0 < lo => {
2542- return Some ( ( format ! ( "`{}` must be non-null" , ty) , None ) ) ;
2543- }
2544- ( Bound :: Included ( _) , _) | ( _, Bound :: Included ( _) )
2545- if init == InitKind :: Uninit =>
2546- {
2547- return Some ( (
2548- format ! (
2549- "`{}` must be initialized inside its custom valid range" ,
2550- ty,
2551- ) ,
2552- None ,
2553- ) ) ;
2554- }
2555- _ => { }
2556- }
25572584 // Handle structs.
25582585 if adt_def. is_struct ( ) {
25592586 return variant_find_init_error (
25602587 cx,
2588+ ty,
25612589 adt_def. non_enum_variant ( ) ,
25622590 substs,
25632591 "struct field" ,
@@ -2581,13 +2609,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
25812609 Some ( ( variant, definitely_inhabited) )
25822610 } ) ;
25832611 let Some ( first_variant) = potential_variants. next ( ) else {
2584- return Some ( ( "enums with no inhabited variants have no valid value" . to_string ( ) , Some ( span) ) ) ;
2612+ return Some ( InitError :: from ( "enums with no inhabited variants have no valid value" ) . spanned ( span) ) ;
25852613 } ;
25862614 // So we have at least one potentially inhabited variant. Might we have two?
25872615 let Some ( second_variant) = potential_variants. next ( ) else {
25882616 // There is only one potentially inhabited variant. So we can recursively check that variant!
25892617 return variant_find_init_error (
25902618 cx,
2619+ ty,
25912620 & first_variant. 0 ,
25922621 substs,
25932622 "field of the only potentially inhabited enum variant" ,
@@ -2605,10 +2634,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
26052634 . filter ( |( _variant, definitely_inhabited) | * definitely_inhabited)
26062635 . count ( ) ;
26072636 if definitely_inhabited > 1 {
2608- return Some ( (
2609- "enums with multiple inhabited variants have to be initialized to a variant" . to_string ( ) ,
2610- Some ( span) ,
2611- ) ) ;
2637+ return Some ( InitError :: from (
2638+ "enums with multiple inhabited variants have to be initialized to a variant" ,
2639+ ) . spanned ( span) ) ;
26122640 }
26132641 }
26142642 // We couldn't find anything wrong here.
@@ -2637,8 +2665,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
26372665 // using zeroed or uninitialized memory.
26382666 // We are extremely conservative with what we warn about.
26392667 let conjured_ty = cx. typeck_results ( ) . expr_ty ( expr) ;
2640- if let Some ( ( msg, span) ) =
2641- with_no_trimmed_paths ! ( ty_find_init_error( cx, conjured_ty, init) )
2668+ if let Some ( mut err) = with_no_trimmed_paths ! ( ty_find_init_error( cx, conjured_ty, init) )
26422669 {
26432670 // FIXME(davidtwco): make translatable
26442671 cx. struct_span_lint (
@@ -2664,10 +2691,17 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
26642691 "help: use `MaybeUninit<T>` instead, \
26652692 and only call `assume_init` after initialization is done",
26662693 ) ;
2667- if let Some ( span) = span {
2668- lint. span_note ( span, & msg) ;
2669- } else {
2670- lint. note ( & msg) ;
2694+ loop {
2695+ if let Some ( span) = err. span {
2696+ lint. span_note ( span, & err. message ) ;
2697+ } else {
2698+ lint. note ( & err. message ) ;
2699+ }
2700+ if let Some ( e) = err. nested {
2701+ err = * e;
2702+ } else {
2703+ break ;
2704+ }
26712705 }
26722706 lint
26732707 } ,
0 commit comments