@@ -46,8 +46,7 @@ use rustc_middle::lint::in_external_macro;
4646use rustc_middle:: ty:: layout:: { LayoutError , LayoutOf } ;
4747use rustc_middle:: ty:: print:: with_no_trimmed_paths;
4848use 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 } ;
5150use rustc_session:: lint:: { BuiltinLintDiagnostics , FutureIncompatibilityReason } ;
5251use rustc_span:: edition:: Edition ;
5352use rustc_span:: source_map:: Spanned ;
@@ -2425,12 +2424,63 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24252424 None
24262425 }
24272426
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+ } )
24342484 }
24352485
24362486 /// Return `Some` only if we are sure this type does *not*
@@ -2468,14 +2518,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24682518 RawPtr ( _) if init == InitKind :: Uninit => {
24692519 Some ( ( "raw pointers must not be uninitialized" . to_string ( ) , None ) )
24702520 }
2471- // Recurse and checks for some compound types.
2521+ // Recurse and checks for some compound types. (but not unions)
24722522 Adt ( adt_def, substs) if !adt_def. is_union ( ) => {
24732523 // First check if this ADT has a layout attribute (like `NonNull` and friends).
24742524 use std:: ops:: Bound ;
24752525 match cx. tcx . layout_scalar_valid_range ( adt_def. did ( ) ) {
24762526 // We exploit here that `layout_scalar_valid_range` will never
24772527 // return `Bound::Excluded`. (And we have tests checking that we
24782528 // handle the attribute correctly.)
2529+ // We don't add a span since users cannot declare such types anyway.
24792530 ( Bound :: Included ( lo) , _) if lo > 0 => {
24802531 return Some ( ( format ! ( "`{}` must be non-null" , ty) , None ) ) ;
24812532 }
@@ -2492,50 +2543,65 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24922543 }
24932544 _ => { }
24942545 }
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+ ) ) ;
25372601 }
25382602 }
2603+ // We couldn't find anything wrong here.
2604+ None
25392605 }
25402606 Tuple ( ..) => {
25412607 // Proceed recursively, check all fields.
0 commit comments