@@ -46,8 +46,7 @@ use rustc_middle::lint::in_external_macro;
46
46
use rustc_middle:: ty:: layout:: { LayoutError , LayoutOf } ;
47
47
use rustc_middle:: ty:: print:: with_no_trimmed_paths;
48
48
use rustc_middle:: ty:: subst:: GenericArgKind ;
49
- use rustc_middle:: ty:: Instance ;
50
- use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
49
+ use rustc_middle:: ty:: { self , Instance , Ty , TyCtxt , VariantDef } ;
51
50
use rustc_session:: lint:: { BuiltinLintDiagnostics , FutureIncompatibilityReason } ;
52
51
use rustc_span:: edition:: Edition ;
53
52
use rustc_span:: source_map:: Spanned ;
@@ -2425,12 +2424,56 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2425
2424
None
2426
2425
}
2427
2426
2428
- /// Test if this enum has several actually "existing" variants.
2429
- /// Zero-sized uninhabited variants do not always have a tag assigned and thus do not "exist".
2430
- fn is_multi_variant < ' tcx > ( adt : ty:: AdtDef < ' tcx > ) -> bool {
2431
- // As an approximation, we only count dataless variants. Those are definitely inhabited.
2432
- let existing_variants = adt. variants ( ) . iter ( ) . filter ( |v| v. fields . is_empty ( ) ) . count ( ) ;
2433
- existing_variants > 1
2427
+ /// Determines whether the given type is inhabited. `None` means that we don't know.
2428
+ fn ty_inhabited ( ty : Ty < ' _ > ) -> Option < bool > {
2429
+ use rustc_type_ir:: sty:: TyKind :: * ;
2430
+ match ty. kind ( ) {
2431
+ Never => Some ( false ) ,
2432
+ Int ( _) | Uint ( _) | Float ( _) | Bool | Char | RawPtr ( _) => Some ( true ) ,
2433
+ // Fallback for more complicated types. (Note that `&!` might be considered
2434
+ // uninhabited so references are "complicated", too.)
2435
+ _ => None ,
2436
+ }
2437
+ }
2438
+ /// Determines whether a product type formed from a list of types is inhabited.
2439
+ fn tys_inhabited < ' tcx > ( tys : impl Iterator < Item = Ty < ' tcx > > ) -> Option < bool > {
2440
+ let mut definitely_inhabited = true ; // with no fields, we are definitely inhabited.
2441
+ for ty in tys {
2442
+ match ty_inhabited ( ty) {
2443
+ // If any type is uninhabited, the product is uninhabited.
2444
+ Some ( false ) => return Some ( false ) ,
2445
+ // Otherwise go searching for a `None`.
2446
+ None => {
2447
+ // We don't know.
2448
+ definitely_inhabited = false ;
2449
+ }
2450
+ Some ( true ) => { }
2451
+ }
2452
+ }
2453
+ if definitely_inhabited { Some ( true ) } else { None }
2454
+ }
2455
+
2456
+ fn variant_find_init_error < ' tcx > (
2457
+ cx : & LateContext < ' tcx > ,
2458
+ variant : & VariantDef ,
2459
+ substs : ty:: SubstsRef < ' tcx > ,
2460
+ descr : & str ,
2461
+ init : InitKind ,
2462
+ ) -> Option < InitError > {
2463
+ variant. fields . iter ( ) . find_map ( |field| {
2464
+ ty_find_init_error ( cx, field. ty ( cx. tcx , substs) , init) . map ( |( mut msg, span) | {
2465
+ if span. is_none ( ) {
2466
+ // Point to this field, should be helpful for figuring
2467
+ // out where the source of the error is.
2468
+ let span = cx. tcx . def_span ( field. did ) ;
2469
+ write ! ( & mut msg, " (in this {descr})" ) . unwrap ( ) ;
2470
+ ( msg, Some ( span) )
2471
+ } else {
2472
+ // Just forward.
2473
+ ( msg, span)
2474
+ }
2475
+ } )
2476
+ } )
2434
2477
}
2435
2478
2436
2479
/// Return `Some` only if we are sure this type does *not*
@@ -2468,14 +2511,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2468
2511
RawPtr ( _) if init == InitKind :: Uninit => {
2469
2512
Some ( ( "raw pointers must not be uninitialized" . to_string ( ) , None ) )
2470
2513
}
2471
- // Recurse and checks for some compound types.
2514
+ // Recurse and checks for some compound types. (but not unions)
2472
2515
Adt ( adt_def, substs) if !adt_def. is_union ( ) => {
2473
2516
// First check if this ADT has a layout attribute (like `NonNull` and friends).
2474
2517
use std:: ops:: Bound ;
2475
2518
match cx. tcx . layout_scalar_valid_range ( adt_def. did ( ) ) {
2476
2519
// We exploit here that `layout_scalar_valid_range` will never
2477
2520
// return `Bound::Excluded`. (And we have tests checking that we
2478
2521
// handle the attribute correctly.)
2522
+ // We don't add a span since users cannot declare such types anyway.
2479
2523
( Bound :: Included ( lo) , _) if lo > 0 => {
2480
2524
return Some ( ( format ! ( "`{}` must be non-null" , ty) , None ) ) ;
2481
2525
}
@@ -2492,50 +2536,64 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2492
2536
}
2493
2537
_ => { }
2494
2538
}
2495
- // Now, recurse.
2496
- match adt_def. variants ( ) . len ( ) {
2497
- 0 => Some ( ( "enums with no variants have no valid value" . to_string ( ) , None ) ) ,
2498
- 1 => {
2499
- // Struct, or enum with exactly one variant.
2500
- // Proceed recursively, check all fields.
2501
- let variant = & adt_def. variant ( VariantIdx :: from_u32 ( 0 ) ) ;
2502
- variant. fields . iter ( ) . find_map ( |field| {
2503
- ty_find_init_error ( cx, field. ty ( cx. tcx , substs) , init) . map (
2504
- |( mut msg, span) | {
2505
- if span. is_none ( ) {
2506
- // Point to this field, should be helpful for figuring
2507
- // out where the source of the error is.
2508
- let span = cx. tcx . def_span ( field. did ) ;
2509
- write ! (
2510
- & mut msg,
2511
- " (in this {} field)" ,
2512
- adt_def. descr( )
2513
- )
2514
- . unwrap ( ) ;
2515
- ( msg, Some ( span) )
2516
- } else {
2517
- // Just forward.
2518
- ( msg, span)
2519
- }
2520
- } ,
2521
- )
2522
- } )
2523
- }
2524
- // Multi-variant enum.
2525
- _ => {
2526
- if init == InitKind :: Uninit && is_multi_variant ( * adt_def) {
2527
- let span = cx. tcx . def_span ( adt_def. did ( ) ) ;
2528
- Some ( (
2529
- "enums have to be initialized to a variant" . to_string ( ) ,
2530
- Some ( span) ,
2531
- ) )
2532
- } else {
2533
- // In principle, for zero-initialization we could figure out which variant corresponds
2534
- // to tag 0, and check that... but for now we just accept all zero-initializations.
2535
- None
2536
- }
2539
+ // Handle structs.
2540
+ if adt_def. is_struct ( ) {
2541
+ return variant_find_init_error (
2542
+ cx,
2543
+ adt_def. non_enum_variant ( ) ,
2544
+ substs,
2545
+ "struct field" ,
2546
+ init,
2547
+ ) ;
2548
+ }
2549
+ // And now, enums.
2550
+ let span = cx. tcx . def_span ( adt_def. did ( ) ) ;
2551
+ let mut potential_variants = adt_def. variants ( ) . iter ( ) . filter_map ( |variant| {
2552
+ let inhabited = tys_inhabited (
2553
+ variant. fields . iter ( ) . map ( |field| field. ty ( cx. tcx , substs) ) ,
2554
+ ) ;
2555
+ let definitely_inhabited = match inhabited {
2556
+ // Entirely skip uninhbaited variants.
2557
+ Some ( false ) => return None ,
2558
+ // Forward the others, but remember which ones are definitely inhabited.
2559
+ Some ( true ) => true ,
2560
+ None => false ,
2561
+ } ;
2562
+ Some ( ( variant, definitely_inhabited) )
2563
+ } ) ;
2564
+ let Some ( first_variant) = potential_variants. next ( ) else {
2565
+ return Some ( ( "enums with no inhabited variants have no valid value" . to_string ( ) , Some ( span) ) ) ;
2566
+ } ;
2567
+ // So we have at least one potentially inhabited variant. Might we have two?
2568
+ let Some ( second_variant) = potential_variants. next ( ) else {
2569
+ // There is only one potentially inhabited variant. So we can recursively check that variant!
2570
+ return variant_find_init_error (
2571
+ cx,
2572
+ & first_variant. 0 ,
2573
+ substs,
2574
+ "field of the only potentially inhabited enum variant" ,
2575
+ init,
2576
+ ) ;
2577
+ } ;
2578
+ // So we have at least two potentially inhabited variants.
2579
+ // If we can prove that we have at least two *definitely* inhabited variants,
2580
+ // then we have a tag and hence leaving this uninit is definitely disallowed.
2581
+ // (Leaving it zeroed could be okay, depending on which variant is encoded as zero tag.)
2582
+ if init == InitKind :: Uninit {
2583
+ let definitely_inhabited = ( first_variant. 1 as usize )
2584
+ + ( second_variant. 1 as usize )
2585
+ + potential_variants
2586
+ . filter ( |( _variant, definitely_inhabited) | * definitely_inhabited)
2587
+ . count ( ) ;
2588
+ if definitely_inhabited > 1 {
2589
+ return Some ( (
2590
+ "enums with multiple inhabited variants have to be initialized to a variant" . to_string ( ) ,
2591
+ Some ( span) ,
2592
+ ) ) ;
2537
2593
}
2538
2594
}
2595
+ // We couldn't find anything wrong here.
2596
+ None
2539
2597
}
2540
2598
Tuple ( ..) => {
2541
2599
// Proceed recursively, check all fields.
0 commit comments