From da6fbb18951547da08c2e353838e6b1d47d9b3be Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 6 Aug 2019 23:11:52 +0200 Subject: [PATCH 1/9] add basic lint testing for misuse of mem::zeroed and mem::uninitialized --- src/librustc_lint/builtin.rs | 59 ++++++++++++++++++++ src/librustc_lint/lib.rs | 1 + src/libsyntax_pos/symbol.rs | 3 + src/test/ui/lint/uninitialized-zeroed.rs | 28 ++++++++++ src/test/ui/lint/uninitialized-zeroed.stderr | 44 +++++++++++++++ src/test/ui/panic-uninitialized-zeroed.rs | 2 +- 6 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/lint/uninitialized-zeroed.rs create mode 100644 src/test/ui/lint/uninitialized-zeroed.stderr diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index c9153f285fff7..b4155646c891f 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1862,3 +1862,62 @@ impl EarlyLintPass for IncompleteFeatures { }); } } + +declare_lint! { + pub INVALID_VALUE, + Warn, + "an invalid value is being created (such as a NULL reference)" +} + +declare_lint_pass!(InvalidValue => [INVALID_VALUE]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &hir::Expr) { + + const ZEROED_PATH: &[Symbol] = &[sym::core, sym::mem, sym::zeroed]; + const UININIT_PATH: &[Symbol] = &[sym::core, sym::mem, sym::uninitialized]; + + /// Return `false` only if we are sure this type does *not* + /// allow zero initialization. + fn ty_maybe_allows_zero_init(ty: Ty<'_>) -> bool { + use rustc::ty::TyKind::*; + match ty.sty { + // Primitive types that don't like 0 as a value. + Ref(..) | FnPtr(..) | Never => false, + // Conservative fallback. + _ => true, + } + } + + if let hir::ExprKind::Call(ref path_expr, ref _args) = expr.node { + if let hir::ExprKind::Path(ref qpath) = path_expr.node { + if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() { + if cx.match_def_path(def_id, &ZEROED_PATH) || + cx.match_def_path(def_id, &UININIT_PATH) + { + // This conjures an instance of a type out of nothing, + // using zeroed or uninitialized memory. + // We are extremely conservative with what we warn about. + let conjured_ty = cx.tables.expr_ty(expr); + + if !ty_maybe_allows_zero_init(conjured_ty) { + cx.span_lint( + INVALID_VALUE, + expr.span, + &format!( + "the type `{}` does not permit {}", + conjured_ty, + if cx.match_def_path(def_id, &ZEROED_PATH) { + "zero-initialization" + } else { + "being left uninitialized" + } + ), + ); + } + } + } + } + } + } +} diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 78bc164ba1a0f..3a540fdf4b91f 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -177,6 +177,7 @@ macro_rules! late_lint_mod_passes { UnreachablePub: UnreachablePub, ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, + InvalidValue: InvalidValue, ]); ) } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index f7e1b983e5446..2d9556233d15f 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -412,6 +412,7 @@ symbols! { match_beginning_vert, match_default_bindings, may_dangle, + mem, member_constraints, message, meta, @@ -695,6 +696,7 @@ symbols! { underscore_imports, underscore_lifetimes, uniform_paths, + uninitialized, universal_impl_trait, unmarked_api, unreachable_code, @@ -726,6 +728,7 @@ symbols! { windows, windows_subsystem, Yield, + zeroed, } } diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs new file mode 100644 index 0000000000000..40b17651e47b2 --- /dev/null +++ b/src/test/ui/lint/uninitialized-zeroed.rs @@ -0,0 +1,28 @@ +// ignore-tidy-linelength +// This test checks that calling `mem::{uninitialized,zeroed}` with certain types results +// in a lint. + +#![feature(never_type)] +#![allow(deprecated)] +#![deny(invalid_value)] + +use std::mem; + +fn main() { + unsafe { + let _val: ! = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: ! = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + + let _val: &'static i32 = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: &'static i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + + let _val: fn() = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: fn() = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + + // Some types that should work just fine. + let _val: Option<&'static i32> = mem::zeroed(); + let _val: Option = mem::zeroed(); + let _val: bool = mem::zeroed(); + let _val: i32 = mem::zeroed(); + } +} diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr new file mode 100644 index 0000000000000..c6a47638d38e5 --- /dev/null +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -0,0 +1,44 @@ +error: the type `!` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:15:23 + | +LL | let _val: ! = mem::zeroed(); + | ^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/uninitialized-zeroed.rs:7:9 + | +LL | #![deny(invalid_value)] + | ^^^^^^^^^^^^^ + +error: the type `!` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:16:23 + | +LL | let _val: ! = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + +error: the type `&'static i32` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:21:34 + | +LL | let _val: &'static i32 = mem::zeroed(); + | ^^^^^^^^^^^^^ + +error: the type `&'static i32` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:22:34 + | +LL | let _val: &'static i32 = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + +error: the type `fn()` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:24:26 + | +LL | let _val: fn() = mem::zeroed(); + | ^^^^^^^^^^^^^ + +error: the type `fn()` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:25:26 + | +LL | let _val: fn() = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/panic-uninitialized-zeroed.rs b/src/test/ui/panic-uninitialized-zeroed.rs index 0c97babd51cd4..b0d6629561803 100644 --- a/src/test/ui/panic-uninitialized-zeroed.rs +++ b/src/test/ui/panic-uninitialized-zeroed.rs @@ -4,7 +4,7 @@ // in a runtime panic. #![feature(never_type)] -#![allow(deprecated)] +#![allow(deprecated, invalid_value)] use std::{mem, panic}; From ca1e94b131eec4795f1a8dcacdb12f574e7a4a74 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 Aug 2019 08:42:50 +0200 Subject: [PATCH 2/9] warn for more cases --- src/librustc_lint/builtin.rs | 43 +++++- src/test/ui/lint/uninitialized-zeroed.rs | 32 +++- src/test/ui/lint/uninitialized-zeroed.stderr | 145 +++++++++++++++++-- 3 files changed, 204 insertions(+), 16 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index b4155646c891f..c652c196afd0d 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -23,7 +23,7 @@ use rustc::hir::def::{Res, DefKind}; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt, layout::VariantIdx}; use rustc::{lint, util}; use hir::Node; use util::nodemap::HirIdSet; @@ -1879,11 +1879,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { /// Return `false` only if we are sure this type does *not* /// allow zero initialization. - fn ty_maybe_allows_zero_init(ty: Ty<'_>) -> bool { + fn ty_maybe_allows_zero_init<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { use rustc::ty::TyKind::*; match ty.sty { // Primitive types that don't like 0 as a value. Ref(..) | FnPtr(..) | Never => false, + Adt(..) if ty.is_box() => false, + // Recurse for some compound types. + Adt(adt_def, substs) if !adt_def.is_union() => { + match adt_def.variants.len() { + 0 => false, // Uninhabited enum! + 1 => { + // Struct, or enum with exactly one variant. + // Proceed recursively, check all fields. + let variant = &adt_def.variants[VariantIdx::from_u32(0)]; + variant.fields.iter().all(|field| { + ty_maybe_allows_zero_init( + tcx, + field.ty(tcx, substs), + ) + }) + } + _ => true, // Conservative fallback for multi-variant enum. + } + } + Tuple(substs) => { + // Proceed recursively, check all fields. + substs.iter().all(|field| { + ty_maybe_allows_zero_init( + tcx, + field.expect_ty(), + ) + }) + } + // FIXME: Would be nice to also warn for `NonNull`/`NonZero*`. // Conservative fallback. _ => true, } @@ -1900,8 +1929,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { // We are extremely conservative with what we warn about. let conjured_ty = cx.tables.expr_ty(expr); - if !ty_maybe_allows_zero_init(conjured_ty) { - cx.span_lint( + if !ty_maybe_allows_zero_init(cx.tcx, conjured_ty) { + cx.struct_span_lint( INVALID_VALUE, expr.span, &format!( @@ -1913,7 +1942,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { "being left uninitialized" } ), - ); + ) + .note("this means that this code causes undefined behavior \ + when executed") + .help("use `MaybeUninit` instead") + .emit(); } } } diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs index 40b17651e47b2..8f9ca8717bda6 100644 --- a/src/test/ui/lint/uninitialized-zeroed.rs +++ b/src/test/ui/lint/uninitialized-zeroed.rs @@ -6,22 +6,52 @@ #![allow(deprecated)] #![deny(invalid_value)] -use std::mem; +use std::mem::{self, MaybeUninit}; + +enum Void {} + +struct Ref(&'static i32); + +struct Wrap { wrapped: T } + +#[allow(unused)] +fn generic() { + unsafe { + let _val: &'static T = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: &'static T = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + + let _val: Wrap<&'static T> = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: Wrap<&'static T> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + } +} fn main() { unsafe { let _val: ! = mem::zeroed(); //~ ERROR: does not permit zero-initialization let _val: ! = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: (i32, !) = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: (i32, !) = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + + let _val: Void = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: Void = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: &'static i32 = mem::zeroed(); //~ ERROR: does not permit zero-initialization let _val: &'static i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: Ref = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: Ref = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: fn() = mem::zeroed(); //~ ERROR: does not permit zero-initialization let _val: fn() = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: Wrap = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: Wrap = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + // Some types that should work just fine. let _val: Option<&'static i32> = mem::zeroed(); let _val: Option = mem::zeroed(); + let _val: MaybeUninit<&'static i32> = mem::zeroed(); let _val: bool = mem::zeroed(); let _val: i32 = mem::zeroed(); } diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index c6a47638d38e5..af54b16bd0b24 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -1,44 +1,169 @@ -error: the type `!` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:15:23 +error: the type `&'static T` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:20:32 | -LL | let _val: ! = mem::zeroed(); - | ^^^^^^^^^^^^^ +LL | let _val: &'static T = mem::zeroed(); + | ^^^^^^^^^^^^^ | note: lint level defined here --> $DIR/uninitialized-zeroed.rs:7:9 | LL | #![deny(invalid_value)] | ^^^^^^^^^^^^^ + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `&'static T` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:21:32 + | +LL | let _val: &'static T = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `Wrap<&'static T>` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:23:38 + | +LL | let _val: Wrap<&'static T> = mem::zeroed(); + | ^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `Wrap<&'static T>` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:24:38 + | +LL | let _val: Wrap<&'static T> = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `!` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:30:23 + | +LL | let _val: ! = mem::zeroed(); + | ^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead error: the type `!` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:16:23 + --> $DIR/uninitialized-zeroed.rs:31:23 | LL | let _val: ! = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `(i32, !)` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:33:30 + | +LL | let _val: (i32, !) = mem::zeroed(); + | ^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `(i32, !)` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:34:30 + | +LL | let _val: (i32, !) = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `Void` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:36:26 + | +LL | let _val: Void = mem::zeroed(); + | ^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `Void` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:37:26 + | +LL | let _val: Void = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead error: the type `&'static i32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:21:34 + --> $DIR/uninitialized-zeroed.rs:39:34 | LL | let _val: &'static i32 = mem::zeroed(); | ^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead error: the type `&'static i32` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:22:34 + --> $DIR/uninitialized-zeroed.rs:40:34 | LL | let _val: &'static i32 = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `Ref` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:42:25 + | +LL | let _val: Ref = mem::zeroed(); + | ^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `Ref` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:43:25 + | +LL | let _val: Ref = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead error: the type `fn()` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:24:26 + --> $DIR/uninitialized-zeroed.rs:45:26 | LL | let _val: fn() = mem::zeroed(); | ^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead error: the type `fn()` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:25:26 + --> $DIR/uninitialized-zeroed.rs:46:26 | LL | let _val: fn() = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `Wrap` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:48:32 + | +LL | let _val: Wrap = mem::zeroed(); + | ^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead + +error: the type `Wrap` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:49:32 + | +LL | let _val: Wrap = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this means that this code causes undefined behavior when executed + = help: use `MaybeUninit` instead -error: aborting due to 6 previous errors +error: aborting due to 18 previous errors From fbd56131a937ffd2ebaef88c8f9788bd4447d66c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 Aug 2019 08:43:42 +0200 Subject: [PATCH 3/9] fix a comment --- src/librustc/ty/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index a10578b0a4390..da7f894e84d0a 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1949,7 +1949,7 @@ pub struct FieldDef { pub struct AdtDef { /// `DefId` of the struct, enum or union item. pub did: DefId, - /// Variants of the ADT. If this is a struct or enum, then there will be a single variant. + /// Variants of the ADT. If this is a struct or union, then there will be a single variant. pub variants: IndexVec, /// Flags of the ADT (e.g. is this a struct? is this non-exhaustive?) flags: AdtFlags, From 8e6fbbec83707fe46c9f406f6ccd0ca75817dd5e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 Aug 2019 09:01:53 +0200 Subject: [PATCH 4/9] add tuple_fields convenience method and use it in a few places --- src/librustc/ty/mod.rs | 2 ++ src/librustc/ty/sty.rs | 14 ++++++++++++-- src/librustc/ty/util.rs | 8 ++++---- src/librustc/ty/walk.rs | 4 ++-- src/librustc_lint/builtin.rs | 9 ++------- src/librustc_mir/shim.rs | 2 +- src/librustc_mir/util/elaborate_drops.rs | 4 ++-- 7 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index da7f894e84d0a..f653f0561776c 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2565,6 +2565,8 @@ impl<'tcx> AdtDef { } impl<'tcx> FieldDef { + /// Returns the type of this field. The `subst` is typically obtained + /// via the second field of `TyKind::AdtDef`. pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> { tcx.type_of(self.did).subst(tcx, subst) } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 064c374de2b4c..129ea9b5b674a 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -171,6 +171,7 @@ pub enum TyKind<'tcx> { Never, /// A tuple type. For example, `(i32, bool)`. + /// Use `TyS::tuple_fields` to iterate over the field types. Tuple(SubstsRef<'tcx>), /// The projection of an associated type. For example, @@ -1723,8 +1724,8 @@ impl<'tcx> TyS<'tcx> { }) }) } - ty::Tuple(tys) => tys.iter().any(|ty| { - ty.expect_ty().conservative_is_privately_uninhabited(tcx) + ty::Tuple(..) => self.tuple_fields().any(|ty| { + ty.conservative_is_privately_uninhabited(tcx) }), ty::Array(ty, len) => { match len.try_eval_usize(tcx, ParamEnv::empty()) { @@ -2103,6 +2104,15 @@ impl<'tcx> TyS<'tcx> { } } + /// Iterates over tuple fields. + /// Panics when called on anything but a tuple. + pub fn tuple_fields(&self) -> impl DoubleEndedIterator> { + match self.sty { + Tuple(substs) => substs.iter().map(|field| field.expect_ty()), + _ => bug!("tuple_fields called on non-tuple"), + } + } + /// If the type contains variants, returns the valid range of variant indices. /// FIXME This requires the optimized MIR in the case of generators. #[inline] diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index c3ecc04b12e01..96e16efd1300a 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -845,15 +845,15 @@ impl<'tcx> ty::TyS<'tcx> { ty: Ty<'tcx>, ) -> Representability { match ty.sty { - Tuple(ref ts) => { + Tuple(..) => { // Find non representable - fold_repr(ts.iter().map(|ty| { + fold_repr(ty.tuple_fields().map(|ty| { is_type_structurally_recursive( tcx, sp, seen, representable_cache, - ty.expect_ty(), + ty, ) })) } @@ -1095,7 +1095,7 @@ fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx> // state transformation pass ty::Generator(..) => true, - ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).any(needs_drop), + ty::Tuple(..) => ty.tuple_fields().any(needs_drop), // unions don't have destructors because of the child types, // only if they manually implement `Drop` (handled above). diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs index c74511cf0fdda..8c3110792a8b4 100644 --- a/src/librustc/ty/walk.rs +++ b/src/librustc/ty/walk.rs @@ -119,8 +119,8 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) { ty::GeneratorWitness(ts) => { stack.extend(ts.skip_binder().iter().cloned().rev()); } - ty::Tuple(ts) => { - stack.extend(ts.iter().map(|k| k.expect_ty()).rev()); + ty::Tuple(..) => { + stack.extend(parent_ty.tuple_fields().rev()); } ty::FnDef(_, substs) => { stack.extend(substs.types().rev()); diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index c652c196afd0d..5c2a86f8f3feb 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1903,14 +1903,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { _ => true, // Conservative fallback for multi-variant enum. } } - Tuple(substs) => { + Tuple(..) => { // Proceed recursively, check all fields. - substs.iter().all(|field| { - ty_maybe_allows_zero_init( - tcx, - field.expect_ty(), - ) - }) + ty.tuple_fields().all(|field| ty_maybe_allows_zero_init(tcx, field)) } // FIXME: Would be nice to also warn for `NonNull`/`NonZero*`. // Conservative fallback. diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 42945c79ddf0e..33447eba7492a 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -324,7 +324,7 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) - substs.upvar_tys(def_id, tcx) ) } - ty::Tuple(tys) => builder.tuple_like_shim(dest, src, tys.iter().map(|k| k.expect_ty())), + ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()), _ => { bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty) } diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index d17dcaafc04f5..52fd645e38e22 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -804,8 +804,8 @@ where let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect(); self.open_drop_for_tuple(&tys) } - ty::Tuple(tys) => { - let tys: Vec<_> = tys.iter().map(|k| k.expect_ty()).collect(); + ty::Tuple(..) => { + let tys: Vec<_> = ty.tuple_fields().collect(); self.open_drop_for_tuple(&tys) } ty::Adt(def, substs) => { From 4b062a175fa3b9613995eea7d026edbbb3be715e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 Aug 2019 10:09:42 +0200 Subject: [PATCH 5/9] note a FIXME --- src/librustc/ty/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index f653f0561776c..b104bbf466a36 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1843,6 +1843,7 @@ pub struct VariantDef { /// Flags of the variant (e.g. is field list non-exhaustive)? flags: VariantFlags, /// Recovered? + // FIXME: Needs proper doc. Recovered whom from what? pub recovered: bool, } From 3972d05fec01cd46c207f6e98aca5ee20102e17a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 Aug 2019 11:52:40 +0200 Subject: [PATCH 6/9] proper doc comment for 'recovered' field of variant Curtesy of petrochenkov --- src/librustc/ty/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index b104bbf466a36..9d563e290de96 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1842,8 +1842,8 @@ pub struct VariantDef { pub ctor_kind: CtorKind, /// Flags of the variant (e.g. is field list non-exhaustive)? flags: VariantFlags, - /// Recovered? - // FIXME: Needs proper doc. Recovered whom from what? + /// Variant is obtained as part of recovering from a syntactic error. + /// May be incomplete or bogus. pub recovered: bool, } From c5a63566d6d8d70687410b41b6464bcef3ef89f3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 Aug 2019 11:59:23 +0200 Subject: [PATCH 7/9] allow the lint if a few UB-demonstrating doc tests --- src/libcore/mem/maybe_uninit.rs | 3 +++ src/libcore/mem/mod.rs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libcore/mem/maybe_uninit.rs b/src/libcore/mem/maybe_uninit.rs index 8d064de6f4751..1bbea02e0c7c9 100644 --- a/src/libcore/mem/maybe_uninit.rs +++ b/src/libcore/mem/maybe_uninit.rs @@ -13,6 +13,7 @@ use crate::mem::ManuallyDrop; /// ever gets used to access memory: /// /// ```rust,no_run +/// # #![allow(invalid_value)] /// use std::mem::{self, MaybeUninit}; /// /// let x: &i32 = unsafe { mem::zeroed() }; // undefined behavior! @@ -27,6 +28,7 @@ use crate::mem::ManuallyDrop; /// always be `true` or `false`. Hence, creating an uninitialized `bool` is undefined behavior: /// /// ```rust,no_run +/// # #![allow(invalid_value)] /// use std::mem::{self, MaybeUninit}; /// /// let b: bool = unsafe { mem::uninitialized() }; // undefined behavior! @@ -40,6 +42,7 @@ use crate::mem::ManuallyDrop; /// which otherwise can hold any *fixed* bit pattern: /// /// ```rust,no_run +/// # #![allow(invalid_value)] /// use std::mem::{self, MaybeUninit}; /// /// let x: i32 = unsafe { mem::uninitialized() }; // undefined behavior! diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index 86dae985fdb00..16cfe0f7683a8 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -445,7 +445,8 @@ pub const fn needs_drop() -> bool { /// /// *Incorrect* usage of this function: initializing a reference with zero. /// -/// ```no_run +/// ```rust,no_run +/// # #![allow(invalid_value)] /// use std::mem; /// /// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior! From 5c77a17d182fd05f06eaf899281b2eda49047e91 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 Aug 2019 16:23:11 +0200 Subject: [PATCH 8/9] note down some more future plans --- src/librustc_lint/builtin.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 5c2a86f8f3feb..13ec27aa1ab3f 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1908,6 +1908,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { ty.tuple_fields().all(|field| ty_maybe_allows_zero_init(tcx, field)) } // FIXME: Would be nice to also warn for `NonNull`/`NonZero*`. + // FIXME: *Only for `mem::uninitialized`*, we could also warn for `bool`, + // `char`, and any multivariant enum. // Conservative fallback. _ => true, } From 09307474c28eacf0d971ef95ecab0a2186a18c3b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 11 Aug 2019 12:06:12 +0200 Subject: [PATCH 9/9] update clippy --- src/tools/clippy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/clippy b/src/tools/clippy index b041511b5fcd3..72da1015d6d91 160000 --- a/src/tools/clippy +++ b/src/tools/clippy @@ -1 +1 @@ -Subproject commit b041511b5fcd386c4ae74a30b60a5081f8717fbe +Subproject commit 72da1015d6d918fe1b29170acbf486d30e0c2695