@@ -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,63 @@ 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 < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> Option < bool > {
2429
+ use rustc_type_ir:: sty:: TyKind :: * ;
2430
+ if !cx. tcx . type_uninhabited_from ( cx. param_env . and ( ty) ) . is_empty ( ) {
2431
+ // This is definitely uninhabited from some module.
2432
+ return Some ( false ) ;
2433
+ }
2434
+ match ty. kind ( ) {
2435
+ Never => Some ( false ) ,
2436
+ Int ( _) | Uint ( _) | Float ( _) | Bool | Char | RawPtr ( _) => Some ( true ) ,
2437
+ // Fallback for more complicated types. (Note that `&!` might be considered
2438
+ // uninhabited so references are "complicated", too.)
2439
+ _ => None ,
2440
+ }
2441
+ }
2442
+ /// Determines whether a product type formed from a list of types is inhabited.
2443
+ fn tys_inhabited < ' tcx > (
2444
+ cx : & LateContext < ' tcx > ,
2445
+ tys : impl Iterator < Item = Ty < ' tcx > > ,
2446
+ ) -> Option < bool > {
2447
+ let mut definitely_inhabited = true ; // with no fields, we are definitely inhabited.
2448
+ for ty in tys {
2449
+ match ty_inhabited ( cx, ty) {
2450
+ // If any type is uninhabited, the product is uninhabited.
2451
+ Some ( false ) => return Some ( false ) ,
2452
+ // Otherwise go searching for a `None`.
2453
+ None => {
2454
+ // We don't know.
2455
+ definitely_inhabited = false ;
2456
+ }
2457
+ Some ( true ) => { }
2458
+ }
2459
+ }
2460
+ if definitely_inhabited { Some ( true ) } else { None }
2461
+ }
2462
+
2463
+ fn variant_find_init_error < ' tcx > (
2464
+ cx : & LateContext < ' tcx > ,
2465
+ variant : & VariantDef ,
2466
+ substs : ty:: SubstsRef < ' tcx > ,
2467
+ descr : & str ,
2468
+ init : InitKind ,
2469
+ ) -> Option < InitError > {
2470
+ variant. fields . iter ( ) . find_map ( |field| {
2471
+ ty_find_init_error ( cx, field. ty ( cx. tcx , substs) , init) . map ( |( mut msg, span) | {
2472
+ if span. is_none ( ) {
2473
+ // Point to this field, should be helpful for figuring
2474
+ // out where the source of the error is.
2475
+ let span = cx. tcx . def_span ( field. did ) ;
2476
+ write ! ( & mut msg, " (in this {descr})" ) . unwrap ( ) ;
2477
+ ( msg, Some ( span) )
2478
+ } else {
2479
+ // Just forward.
2480
+ ( msg, span)
2481
+ }
2482
+ } )
2483
+ } )
2434
2484
}
2435
2485
2436
2486
/// Return `Some` only if we are sure this type does *not*
@@ -2468,14 +2518,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2468
2518
RawPtr ( _) if init == InitKind :: Uninit => {
2469
2519
Some ( ( "raw pointers must not be uninitialized" . to_string ( ) , None ) )
2470
2520
}
2471
- // Recurse and checks for some compound types.
2521
+ // Recurse and checks for some compound types. (but not unions)
2472
2522
Adt ( adt_def, substs) if !adt_def. is_union ( ) => {
2473
2523
// First check if this ADT has a layout attribute (like `NonNull` and friends).
2474
2524
use std:: ops:: Bound ;
2475
2525
match cx. tcx . layout_scalar_valid_range ( adt_def. did ( ) ) {
2476
2526
// We exploit here that `layout_scalar_valid_range` will never
2477
2527
// return `Bound::Excluded`. (And we have tests checking that we
2478
2528
// handle the attribute correctly.)
2529
+ // We don't add a span since users cannot declare such types anyway.
2479
2530
( Bound :: Included ( lo) , _) if lo > 0 => {
2480
2531
return Some ( ( format ! ( "`{}` must be non-null" , ty) , None ) ) ;
2481
2532
}
@@ -2492,50 +2543,65 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2492
2543
}
2493
2544
_ => { }
2494
2545
}
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
- }
2546
+ // Handle structs.
2547
+ if adt_def. is_struct ( ) {
2548
+ return variant_find_init_error (
2549
+ cx,
2550
+ adt_def. non_enum_variant ( ) ,
2551
+ substs,
2552
+ "struct field" ,
2553
+ init,
2554
+ ) ;
2555
+ }
2556
+ // And now, enums.
2557
+ let span = cx. tcx . def_span ( adt_def. did ( ) ) ;
2558
+ let mut potential_variants = adt_def. variants ( ) . iter ( ) . filter_map ( |variant| {
2559
+ let inhabited = tys_inhabited (
2560
+ cx,
2561
+ variant. fields . iter ( ) . map ( |field| field. ty ( cx. tcx , substs) ) ,
2562
+ ) ;
2563
+ let definitely_inhabited = match inhabited {
2564
+ // Entirely skip uninhbaited variants.
2565
+ Some ( false ) => return None ,
2566
+ // Forward the others, but remember which ones are definitely inhabited.
2567
+ Some ( true ) => true ,
2568
+ None => false ,
2569
+ } ;
2570
+ Some ( ( variant, definitely_inhabited) )
2571
+ } ) ;
2572
+ let Some ( first_variant) = potential_variants. next ( ) else {
2573
+ return Some ( ( "enums with no inhabited variants have no valid value" . to_string ( ) , Some ( span) ) ) ;
2574
+ } ;
2575
+ // So we have at least one potentially inhabited variant. Might we have two?
2576
+ let Some ( second_variant) = potential_variants. next ( ) else {
2577
+ // There is only one potentially inhabited variant. So we can recursively check that variant!
2578
+ return variant_find_init_error (
2579
+ cx,
2580
+ & first_variant. 0 ,
2581
+ substs,
2582
+ "field of the only potentially inhabited enum variant" ,
2583
+ init,
2584
+ ) ;
2585
+ } ;
2586
+ // So we have at least two potentially inhabited variants.
2587
+ // If we can prove that we have at least two *definitely* inhabited variants,
2588
+ // then we have a tag and hence leaving this uninit is definitely disallowed.
2589
+ // (Leaving it zeroed could be okay, depending on which variant is encoded as zero tag.)
2590
+ if init == InitKind :: Uninit {
2591
+ let definitely_inhabited = ( first_variant. 1 as usize )
2592
+ + ( second_variant. 1 as usize )
2593
+ + potential_variants
2594
+ . filter ( |( _variant, definitely_inhabited) | * definitely_inhabited)
2595
+ . count ( ) ;
2596
+ if definitely_inhabited > 1 {
2597
+ return Some ( (
2598
+ "enums with multiple inhabited variants have to be initialized to a variant" . to_string ( ) ,
2599
+ Some ( span) ,
2600
+ ) ) ;
2537
2601
}
2538
2602
}
2603
+ // We couldn't find anything wrong here.
2604
+ None
2539
2605
}
2540
2606
Tuple ( ..) => {
2541
2607
// Proceed recursively, check all fields.
0 commit comments