@@ -52,7 +52,7 @@ use rustc_span::edition::Edition;
52
52
use rustc_span:: source_map:: Spanned ;
53
53
use rustc_span:: symbol:: { kw, sym, Ident , Symbol } ;
54
54
use rustc_span:: { BytePos , InnerSpan , Span } ;
55
- use rustc_target:: abi:: VariantIdx ;
55
+ use rustc_target:: abi:: { Abi , VariantIdx } ;
56
56
use rustc_trait_selection:: traits:: { self , misc:: can_type_implement_copy} ;
57
57
58
58
use crate :: nonstandard_style:: { method_context, MethodLateContext } ;
@@ -2413,8 +2413,34 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2413
2413
}
2414
2414
2415
2415
/// 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
+ }
2418
2444
2419
2445
/// Test if this constant is all-0.
2420
2446
fn is_zero ( expr : & hir:: Expr < ' _ > ) -> bool {
@@ -2470,25 +2496,54 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2470
2496
2471
2497
fn variant_find_init_error < ' tcx > (
2472
2498
cx : & LateContext < ' tcx > ,
2499
+ ty : Ty < ' tcx > ,
2473
2500
variant : & VariantDef ,
2474
2501
substs : ty:: SubstsRef < ' tcx > ,
2475
2502
descr : & str ,
2476
2503
init : InitKind ,
2477
2504
) -> 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
2486
2513
} 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)
2489
2517
}
2490
2518
} )
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
2492
2547
}
2493
2548
2494
2549
/// Return `Some` only if we are sure this type does *not*
@@ -2501,63 +2556,36 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2501
2556
use rustc_type_ir:: sty:: TyKind :: * ;
2502
2557
match ty. kind ( ) {
2503
2558
// 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 ( ) ) ,
2508
2563
RawPtr ( tm) if matches ! ( tm. ty. kind( ) , Dynamic ( ..) ) =>
2509
2564
// raw ptr to dyn Trait
2510
2565
{
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 ( ) )
2512
2567
}
2513
2568
// Primitive types with other constraints.
2514
2569
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 ( ) )
2516
2571
}
2517
2572
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 ( ) )
2519
2574
}
2520
2575
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 ( ) )
2525
2577
}
2578
+ Float ( _) if init == InitKind :: Uninit => Some ( "floats must be initialized" . into ( ) ) ,
2526
2579
RawPtr ( _) if init == InitKind :: Uninit => {
2527
- Some ( ( "raw pointers must not be uninitialized" . to_string ( ) , None ) )
2580
+ Some ( "raw pointers must be initialized" . into ( ) )
2528
2581
}
2529
2582
// Recurse and checks for some compound types. (but not unions)
2530
2583
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
- }
2557
2584
// Handle structs.
2558
2585
if adt_def. is_struct ( ) {
2559
2586
return variant_find_init_error (
2560
2587
cx,
2588
+ ty,
2561
2589
adt_def. non_enum_variant ( ) ,
2562
2590
substs,
2563
2591
"struct field" ,
@@ -2581,13 +2609,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2581
2609
Some ( ( variant, definitely_inhabited) )
2582
2610
} ) ;
2583
2611
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) ) ;
2585
2613
} ;
2586
2614
// So we have at least one potentially inhabited variant. Might we have two?
2587
2615
let Some ( second_variant) = potential_variants. next ( ) else {
2588
2616
// There is only one potentially inhabited variant. So we can recursively check that variant!
2589
2617
return variant_find_init_error (
2590
2618
cx,
2619
+ ty,
2591
2620
& first_variant. 0 ,
2592
2621
substs,
2593
2622
"field of the only potentially inhabited enum variant" ,
@@ -2605,10 +2634,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2605
2634
. filter ( |( _variant, definitely_inhabited) | * definitely_inhabited)
2606
2635
. count ( ) ;
2607
2636
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) ) ;
2612
2640
}
2613
2641
}
2614
2642
// We couldn't find anything wrong here.
@@ -2637,8 +2665,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2637
2665
// using zeroed or uninitialized memory.
2638
2666
// We are extremely conservative with what we warn about.
2639
2667
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) )
2642
2669
{
2643
2670
// FIXME(davidtwco): make translatable
2644
2671
cx. struct_span_lint (
@@ -2664,10 +2691,17 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2664
2691
"help: use `MaybeUninit<T>` instead, \
2665
2692
and only call `assume_init` after initialization is done",
2666
2693
) ;
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
+ }
2671
2705
}
2672
2706
lint
2673
2707
} ,
0 commit comments