@@ -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,56 @@ 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 ( 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+ } )
24342477 }
24352478
24362479 /// Return `Some` only if we are sure this type does *not*
@@ -2468,14 +2511,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24682511 RawPtr ( _) if init == InitKind :: Uninit => {
24692512 Some ( ( "raw pointers must not be uninitialized" . to_string ( ) , None ) )
24702513 }
2471- // Recurse and checks for some compound types.
2514+ // Recurse and checks for some compound types. (but not unions)
24722515 Adt ( adt_def, substs) if !adt_def. is_union ( ) => {
24732516 // First check if this ADT has a layout attribute (like `NonNull` and friends).
24742517 use std:: ops:: Bound ;
24752518 match cx. tcx . layout_scalar_valid_range ( adt_def. did ( ) ) {
24762519 // We exploit here that `layout_scalar_valid_range` will never
24772520 // return `Bound::Excluded`. (And we have tests checking that we
24782521 // handle the attribute correctly.)
2522+ // We don't add a span since users cannot declare such types anyway.
24792523 ( Bound :: Included ( lo) , _) if lo > 0 => {
24802524 return Some ( ( format ! ( "`{}` must be non-null" , ty) , None ) ) ;
24812525 }
@@ -2492,50 +2536,64 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24922536 }
24932537 _ => { }
24942538 }
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+ ) ) ;
25372593 }
25382594 }
2595+ // We couldn't find anything wrong here.
2596+ None
25392597 }
25402598 Tuple ( ..) => {
25412599 // Proceed recursively, check all fields.
0 commit comments