@@ -1876,25 +1876,101 @@ declare_lint_pass!(InvalidValue => [INVALID_VALUE]);
1876
1876
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for InvalidValue {
1877
1877
fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & hir:: Expr ) {
1878
1878
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 } ;
1881
1881
1882
1882
/// Information about why a type cannot be initialized this way.
1883
1883
/// Contains an error message and optionally a span to point at.
1884
1884
type InitError = ( String , Option < Span > ) ;
1885
1885
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
+
1886
1936
/// Return `Some` only if we are sure this type does *not*
1887
1937
/// 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 > {
1889
1943
use rustc:: ty:: TyKind :: * ;
1890
1944
match ty. sty {
1891
1945
// Primitive types that don't like 0 as a value.
1892
1946
Ref ( ..) => Some ( ( format ! ( "References must be non-null" ) , None ) ) ,
1893
1947
Adt ( ..) if ty. is_box ( ) => Some ( ( format ! ( "`Box` must be non-null" ) , None ) ) ,
1894
1948
FnPtr ( ..) => Some ( ( format ! ( "Function pointers must be non-null" ) , None ) ) ,
1895
1949
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.
1897
1956
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.
1898
1974
match adt_def. variants . len ( ) {
1899
1975
0 => Some ( ( format ! ( "0-variant enums have no valid value" ) , None ) ) ,
1900
1976
1 => {
@@ -1905,6 +1981,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
1905
1981
ty_find_init_error (
1906
1982
tcx,
1907
1983
field. ty ( tcx, substs) ,
1984
+ init,
1908
1985
) . map ( |( mut msg, span) | if span. is_none ( ) {
1909
1986
// Point to this field, should be helpful for figuring
1910
1987
// out where the source of the error is.
@@ -1918,57 +1995,48 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
1918
1995
} )
1919
1996
} )
1920
1997
}
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.
1921
2001
_ => None , // Conservative fallback for multi-variant enum.
1922
2002
}
1923
2003
}
1924
2004
Tuple ( ..) => {
1925
2005
// 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 ) )
1927
2007
}
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.
1931
2008
// Conservative fallback.
1932
2009
_ => None ,
1933
2010
}
1934
2011
}
1935
2012
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) ;
1971
2038
}
2039
+ err. emit ( ) ;
1972
2040
}
1973
2041
}
1974
2042
}
0 commit comments