@@ -1876,25 +1876,101 @@ declare_lint_pass!(InvalidValue => [INVALID_VALUE]);
18761876impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for InvalidValue {
18771877 fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & hir:: Expr ) {
18781878
1879- const ZEROED_PATH : & [ Symbol ] = & [ sym :: core , sym :: mem , sym :: zeroed ] ;
1880- const UININIT_PATH : & [ Symbol ] = & [ sym :: core , sym :: mem , sym :: uninitialized ] ;
1879+ # [ derive ( Debug , Copy , Clone , PartialEq ) ]
1880+ enum InitKind { Zeroed , Uninit } ;
18811881
18821882 /// Information about why a type cannot be initialized this way.
18831883 /// Contains an error message and optionally a span to point at.
18841884 type InitError = ( String , Option < Span > ) ;
18851885
1886+ /// Test if this constant is all-0.
1887+ fn is_zero ( expr : & hir:: Expr ) -> bool {
1888+ use hir:: ExprKind :: * ;
1889+ use syntax:: ast:: LitKind :: * ;
1890+ match & expr. node {
1891+ Lit ( lit) =>
1892+ if let Int ( i, _) = lit. node {
1893+ i == 0
1894+ } else {
1895+ false
1896+ } ,
1897+ Tup ( tup) =>
1898+ tup. iter ( ) . all ( is_zero) ,
1899+ _ =>
1900+ false
1901+ }
1902+ }
1903+
1904+ /// Determine if this expression is a "dangerous initialization".
1905+ fn is_dangerous_init ( cx : & LateContext < ' _ , ' _ > , expr : & hir:: Expr ) -> Option < InitKind > {
1906+ const ZEROED_PATH : & [ Symbol ] = & [ sym:: core, sym:: mem, sym:: zeroed] ;
1907+ const UININIT_PATH : & [ Symbol ] = & [ sym:: core, sym:: mem, sym:: uninitialized] ;
1908+ // `transmute` is inside an anonymous module (the `extern` block?);
1909+ // `Invalid` represents the empty string and matches that.
1910+ const TRANSMUTE_PATH : & [ Symbol ] =
1911+ & [ sym:: core, sym:: intrinsics, kw:: Invalid , sym:: transmute] ;
1912+
1913+ if let hir:: ExprKind :: Call ( ref path_expr, ref args) = expr. node {
1914+ if let hir:: ExprKind :: Path ( ref qpath) = path_expr. node {
1915+ let def_id = cx. tables . qpath_res ( qpath, path_expr. hir_id ) . opt_def_id ( ) ?;
1916+
1917+ if cx. match_def_path ( def_id, ZEROED_PATH ) {
1918+ return Some ( InitKind :: Zeroed ) ;
1919+ }
1920+ if cx. match_def_path ( def_id, UININIT_PATH ) {
1921+ return Some ( InitKind :: Uninit ) ;
1922+ }
1923+ if cx. match_def_path ( def_id, TRANSMUTE_PATH ) {
1924+ if is_zero ( & args[ 0 ] ) {
1925+ return Some ( InitKind :: Zeroed ) ;
1926+ }
1927+ }
1928+ // FIXME: Also detect `MaybeUninit::zeroed().assume_init()` and
1929+ // `MaybeUninit::uninit().assume_init()`.
1930+ }
1931+ }
1932+
1933+ None
1934+ }
1935+
18861936 /// Return `Some` only if we are sure this type does *not*
18871937 /// allow zero initialization.
1888- fn ty_find_init_error < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Option < InitError > {
1938+ fn ty_find_init_error < ' tcx > (
1939+ tcx : TyCtxt < ' tcx > ,
1940+ ty : Ty < ' tcx > ,
1941+ init : InitKind ,
1942+ ) -> Option < InitError > {
18891943 use rustc:: ty:: TyKind :: * ;
18901944 match ty. sty {
18911945 // Primitive types that don't like 0 as a value.
18921946 Ref ( ..) => Some ( ( format ! ( "References must be non-null" ) , None ) ) ,
18931947 Adt ( ..) if ty. is_box ( ) => Some ( ( format ! ( "`Box` must be non-null" ) , None ) ) ,
18941948 FnPtr ( ..) => Some ( ( format ! ( "Function pointers must be non-null" ) , None ) ) ,
18951949 Never => Some ( ( format ! ( "The never type (`!`) has no valid value" ) , None ) ) ,
1896- // Recurse for some compound types.
1950+ // Primitive types with other constraints.
1951+ Bool if init == InitKind :: Uninit =>
1952+ Some ( ( format ! ( "Booleans must be `true` or `false`" ) , None ) ) ,
1953+ Char if init == InitKind :: Uninit =>
1954+ Some ( ( format ! ( "Characters must be a valid unicode codepoint" ) , None ) ) ,
1955+ // Recurse and checks for some compound types.
18971956 Adt ( adt_def, substs) if !adt_def. is_union ( ) => {
1957+ // First check f this ADT has a layout attribute (like `NonNull` and friends).
1958+ use std:: ops:: Bound ;
1959+ match tcx. layout_scalar_valid_range ( adt_def. did ) {
1960+ // We exploit here that `layout_scalar_valid_range` will never
1961+ // return `Bound::Excluded`. (And we have tests checking that we
1962+ // handle the attribute correctly.)
1963+ ( Bound :: Included ( lo) , _) if lo > 0 =>
1964+ return Some ( ( format ! ( "{} must be non-null" , ty) , None ) ) ,
1965+ ( Bound :: Included ( _) , _) | ( _, Bound :: Included ( _) )
1966+ if init == InitKind :: Uninit =>
1967+ return Some ( (
1968+ format ! ( "{} must be initialized inside its custom valid range" , ty) ,
1969+ None ,
1970+ ) ) ,
1971+ _ => { }
1972+ }
1973+ // Now, recurse.
18981974 match adt_def. variants . len ( ) {
18991975 0 => Some ( ( format ! ( "0-variant enums have no valid value" ) , None ) ) ,
19001976 1 => {
@@ -1905,6 +1981,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
19051981 ty_find_init_error (
19061982 tcx,
19071983 field. ty ( tcx, substs) ,
1984+ init,
19081985 ) . map ( |( mut msg, span) | if span. is_none ( ) {
19091986 // Point to this field, should be helpful for figuring
19101987 // out where the source of the error is.
@@ -1918,57 +1995,48 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
19181995 } )
19191996 } )
19201997 }
1998+ // Multi-variant enums are tricky: if all but one variant are
1999+ // uninhabited, we might actually do layout like for a single-variant
2000+ // enum, and then even leaving them uninitialized could be okay.
19212001 _ => None , // Conservative fallback for multi-variant enum.
19222002 }
19232003 }
19242004 Tuple ( ..) => {
19252005 // Proceed recursively, check all fields.
1926- ty. tuple_fields ( ) . find_map ( |field| ty_find_init_error ( tcx, field) )
2006+ ty. tuple_fields ( ) . find_map ( |field| ty_find_init_error ( tcx, field, init ) )
19272007 }
1928- // FIXME: Would be nice to also warn for `NonNull`/`NonZero*`.
1929- // FIXME: *Only for `mem::uninitialized`*, we could also warn for `bool`,
1930- // `char`, and any multivariant enum.
19312008 // Conservative fallback.
19322009 _ => None ,
19332010 }
19342011 }
19352012
1936- if let hir:: ExprKind :: Call ( ref path_expr, ref _args) = expr. node {
1937- if let hir:: ExprKind :: Path ( ref qpath) = path_expr. node {
1938- if let Some ( def_id) = cx. tables . qpath_res ( qpath, path_expr. hir_id ) . opt_def_id ( ) {
1939- if cx. match_def_path ( def_id, & ZEROED_PATH ) ||
1940- cx. match_def_path ( def_id, & UININIT_PATH )
1941- {
1942- // This conjures an instance of a type out of nothing,
1943- // using zeroed or uninitialized memory.
1944- // We are extremely conservative with what we warn about.
1945- let conjured_ty = cx. tables . expr_ty ( expr) ;
1946- if let Some ( ( msg, span) ) = ty_find_init_error ( cx. tcx , conjured_ty) {
1947- let mut err = cx. struct_span_lint (
1948- INVALID_VALUE ,
1949- expr. span ,
1950- & format ! (
1951- "the type `{}` does not permit {}" ,
1952- conjured_ty,
1953- if cx. match_def_path( def_id, & ZEROED_PATH ) {
1954- "zero-initialization"
1955- } else {
1956- "being left uninitialized"
1957- }
1958- ) ,
1959- ) ;
1960- err. span_label ( expr. span ,
1961- "this code causes undefined behavior when executed" ) ;
1962- err. span_label ( expr. span , "help: use `MaybeUninit<T>` instead" ) ;
1963- if let Some ( span) = span {
1964- err. span_note ( span, & msg) ;
1965- } else {
1966- err. note ( & msg) ;
1967- }
1968- err. emit ( ) ;
1969- }
1970- }
2013+ if let Some ( init) = is_dangerous_init ( cx, expr) {
2014+ // This conjures an instance of a type out of nothing,
2015+ // using zeroed or uninitialized memory.
2016+ // We are extremely conservative with what we warn about.
2017+ let conjured_ty = cx. tables . expr_ty ( expr) ;
2018+ if let Some ( ( msg, span) ) = ty_find_init_error ( cx. tcx , conjured_ty, init) {
2019+ let mut err = cx. struct_span_lint (
2020+ INVALID_VALUE ,
2021+ expr. span ,
2022+ & format ! (
2023+ "the type `{}` does not permit {}" ,
2024+ conjured_ty,
2025+ match init {
2026+ InitKind :: Zeroed => "zero-initialization" ,
2027+ InitKind :: Uninit => "being left uninitialized" ,
2028+ } ,
2029+ ) ,
2030+ ) ;
2031+ err. span_label ( expr. span ,
2032+ "this code causes undefined behavior when executed" ) ;
2033+ err. span_label ( expr. span , "help: use `MaybeUninit<T>` instead" ) ;
2034+ if let Some ( span) = span {
2035+ err. span_note ( span, & msg) ;
2036+ } else {
2037+ err. note ( & msg) ;
19712038 }
2039+ err. emit ( ) ;
19722040 }
19732041 }
19742042 }
0 commit comments