Skip to content

Commit dc4b6a9

Browse files
committed
Ignore invalid_value lint if mem_uninitialized would lint
1 parent 86a98df commit dc4b6a9

File tree

5 files changed

+325
-692
lines changed

5 files changed

+325
-692
lines changed

compiler/rustc_lint/src/builtin.rs

+31-242
Original file line numberDiff line numberDiff line change
@@ -2366,7 +2366,13 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
23662366
#[derive(Debug, Copy, Clone, PartialEq)]
23672367
enum InitKind {
23682368
Zeroed,
2369-
Uninit,
2369+
Uninit { is_mem_uninit: bool },
2370+
}
2371+
2372+
impl InitKind {
2373+
fn is_uninit(self) -> bool {
2374+
matches!(self, InitKind::Uninit { .. })
2375+
}
23702376
}
23712377

23722378
/// Information about why a type cannot be initialized this way.
@@ -2398,7 +2404,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
23982404
let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
23992405
match cx.tcx.get_diagnostic_name(def_id) {
24002406
Some(sym::mem_zeroed) => return Some(InitKind::Zeroed),
2401-
Some(sym::mem_uninitialized) => return Some(InitKind::Uninit),
2407+
Some(sym::mem_uninitialized) => {
2408+
return Some(InitKind::Uninit { is_mem_uninit: true });
2409+
}
24022410
Some(sym::transmute) if is_zero(&args[0]) => return Some(InitKind::Zeroed),
24032411
_ => {}
24042412
}
@@ -2414,7 +2422,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24142422
let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
24152423
match cx.tcx.get_diagnostic_name(def_id) {
24162424
Some(sym::maybe_uninit_zeroed) => return Some(InitKind::Zeroed),
2417-
Some(sym::maybe_uninit_uninit) => return Some(InitKind::Uninit),
2425+
Some(sym::maybe_uninit_uninit) => {
2426+
return Some(InitKind::Uninit { is_mem_uninit: false });
2427+
}
24182428
_ => {}
24192429
}
24202430
}
@@ -2453,19 +2463,19 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24532463
Some(("the vtable of a wide raw pointer must be non-null".to_string(), None))
24542464
}
24552465
// Primitive types with other constraints.
2456-
Bool if init == InitKind::Uninit => {
2466+
Bool if init.is_uninit() => {
24572467
Some(("booleans must be either `true` or `false`".to_string(), None))
24582468
}
2459-
Char if init == InitKind::Uninit => {
2469+
Char if init.is_uninit() => {
24602470
Some(("characters must be a valid Unicode codepoint".to_string(), None))
24612471
}
2462-
Int(_) | Uint(_) if init == InitKind::Uninit => {
2472+
Int(_) | Uint(_) if init.is_uninit() => {
24632473
Some(("integers must not be uninitialized".to_string(), None))
24642474
}
2465-
Float(_) if init == InitKind::Uninit => {
2475+
Float(_) if init.is_uninit() => {
24662476
Some(("floats must not be uninitialized".to_string(), None))
24672477
}
2468-
RawPtr(_) if init == InitKind::Uninit => {
2478+
RawPtr(_) if init.is_uninit() => {
24692479
Some(("raw pointers must not be uninitialized".to_string(), None))
24702480
}
24712481
// Recurse and checks for some compound types.
@@ -2479,9 +2489,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24792489
(Bound::Included(lo), _) if lo > 0 => {
24802490
return Some((format!("`{}` must be non-null", ty), None));
24812491
}
2482-
(Bound::Included(_), _) | (_, Bound::Included(_))
2483-
if init == InitKind::Uninit =>
2484-
{
2492+
(Bound::Included(_), _) | (_, Bound::Included(_)) if init.is_uninit() => {
24852493
return Some((
24862494
format!(
24872495
"`{}` must be initialized inside its custom valid range",
@@ -2523,7 +2531,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
25232531
}
25242532
// Multi-variant enum.
25252533
_ => {
2526-
if init == InitKind::Uninit && is_multi_variant(*adt_def) {
2534+
if init.is_uninit() && is_multi_variant(*adt_def) {
25272535
let span = cx.tcx.def_span(adt_def.did());
25282536
Some((
25292537
"enums have to be initialized to a variant".to_string(),
@@ -2560,6 +2568,16 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
25602568
// using zeroed or uninitialized memory.
25612569
// We are extremely conservative with what we warn about.
25622570
let conjured_ty = cx.typeck_results().expr_ty(expr);
2571+
2572+
if init == (InitKind::Uninit { is_mem_uninit: true }) {
2573+
// We don't want to warn here for things that mem_uninitialized will warn about
2574+
if with_no_trimmed_paths!(
2575+
crate::mem_uninitialized::ty_find_init_error(cx, conjured_ty).is_some()
2576+
) {
2577+
return;
2578+
}
2579+
}
2580+
25632581
if let Some((msg, span)) =
25642582
with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
25652583
{
@@ -2570,7 +2588,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
25702588
conjured_ty,
25712589
match init {
25722590
InitKind::Zeroed => "zero-initialization",
2573-
InitKind::Uninit => "being left uninitialized",
2591+
InitKind::Uninit { .. } => "being left uninitialized",
25742592
},
25752593
));
25762594
err.span_label(expr.span, "this code causes undefined behavior when executed");
@@ -2591,235 +2609,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
25912609
}
25922610
}
25932611

2594-
declare_lint! {
2595-
/// The `mem_uninitialized` lint detects all uses of `std::mem::uninitialized` that are not
2596-
/// known to be safe.
2597-
///
2598-
/// This function is extremely dangerous, and nearly all uses of it cause immediate Undefined
2599-
/// Behavior.
2600-
///
2601-
/// ### Example
2602-
///
2603-
/// ```rust,compile_fail
2604-
/// #![deny(mem_uninitialized)]
2605-
/// fn main() {
2606-
/// let x: [char; 16] = unsafe { std::mem::uninitialized() };
2607-
/// }
2608-
/// ```
2609-
///
2610-
/// {{produces}}
2611-
///
2612-
/// ### Explanation
2613-
///
2614-
/// Creating an invalid value is undefined behavior, and nearly all types are invalid when left
2615-
/// uninitialized.
2616-
///
2617-
/// To avoid churn, however, this will not lint for types made up entirely of integers, floats,
2618-
/// or raw pointers. This is not saying that leaving these types uninitialized is okay,
2619-
/// however.
2620-
pub MEM_UNINITIALIZED,
2621-
Warn,
2622-
"use of mem::uninitialized",
2623-
@future_incompatible = FutureIncompatibleInfo {
2624-
reference: "FIXME: fill this in",
2625-
reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
2626-
explain_reason: false,
2627-
};
2628-
}
2629-
2630-
declare_lint_pass!(MemUninitialized => [MEM_UNINITIALIZED]);
2631-
2632-
impl<'tcx> LateLintPass<'tcx> for MemUninitialized {
2633-
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
2634-
/// Information about why a type cannot be initialized this way.
2635-
/// Contains an error message and optionally a span to point at.
2636-
struct InitError {
2637-
msg: String,
2638-
span: Option<Span>,
2639-
generic: bool,
2640-
}
2641-
2642-
impl InitError {
2643-
fn new(msg: impl Into<String>) -> Self {
2644-
Self { msg: msg.into(), span: None, generic: false }
2645-
}
2646-
2647-
fn with_span(msg: impl Into<String>, span: Span) -> Self {
2648-
Self { msg: msg.into(), span: Some(span), generic: false }
2649-
}
2650-
2651-
fn generic() -> Self {
2652-
Self {
2653-
msg: "type might not be allowed to be left uninitialized".to_string(),
2654-
span: None,
2655-
generic: true,
2656-
}
2657-
}
2658-
}
2659-
2660-
/// Determine if this expression is a "dangerous initialization".
2661-
fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
2662-
if let hir::ExprKind::Call(ref path_expr, _) = expr.kind {
2663-
// Find calls to `mem::{uninitialized,zeroed}` methods.
2664-
if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
2665-
if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
2666-
if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, def_id) {
2667-
return true;
2668-
}
2669-
}
2670-
}
2671-
}
2672-
2673-
false
2674-
}
2675-
2676-
/// Return `None` only if we are sure this type does
2677-
/// allow being left uninitialized.
2678-
fn ty_find_init_error<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<InitError> {
2679-
use rustc_type_ir::sty::TyKind::*;
2680-
match ty.kind() {
2681-
// Primitive types that don't like 0 as a value.
2682-
Ref(..) => Some(InitError::new("references must be non-null")),
2683-
Adt(..) if ty.is_box() => Some(InitError::new("`Box` must be non-null")),
2684-
FnPtr(..) => Some(InitError::new("function pointers must be non-null")),
2685-
Never => Some(InitError::new("the `!` type has no valid value")),
2686-
RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) =>
2687-
// raw ptr to dyn Trait
2688-
{
2689-
Some(InitError::new("the vtable of a wide raw pointer must be non-null"))
2690-
}
2691-
// Primitive types with other constraints.
2692-
Bool => Some(InitError::new("booleans must be either `true` or `false`")),
2693-
Char => Some(InitError::new("characters must be a valid Unicode codepoint")),
2694-
Adt(adt_def, _) if adt_def.is_union() => None,
2695-
// Recurse and checks for some compound types.
2696-
Adt(adt_def, substs) => {
2697-
// First check if this ADT has a layout attribute (like `NonNull` and friends).
2698-
use std::ops::Bound;
2699-
match cx.tcx.layout_scalar_valid_range(adt_def.did()) {
2700-
// We exploit here that `layout_scalar_valid_range` will never
2701-
// return `Bound::Excluded`. (And we have tests checking that we
2702-
// handle the attribute correctly.)
2703-
(Bound::Included(lo), _) if lo > 0 => {
2704-
return Some(InitError::new(format!("`{ty}` must be non-null")));
2705-
}
2706-
(Bound::Included(_), _) | (_, Bound::Included(_)) => {
2707-
return Some(InitError::new(format!(
2708-
"`{ty}` must be initialized inside its custom valid range"
2709-
)));
2710-
}
2711-
_ => {}
2712-
}
2713-
// Now, recurse.
2714-
match adt_def.variants().len() {
2715-
0 => Some(InitError::new("enums with no variants have no valid value")),
2716-
1 => {
2717-
// Struct, or enum with exactly one variant.
2718-
// Proceed recursively, check all fields.
2719-
let variant = &adt_def.variant(VariantIdx::from_u32(0));
2720-
variant.fields.iter().find_map(|field| {
2721-
ty_find_init_error(cx, field.ty(cx.tcx, substs)).map(
2722-
|InitError { mut msg, span, generic }| {
2723-
if span.is_none() {
2724-
// Point to this field, should be helpful for figuring
2725-
// out where the source of the error is.
2726-
let span = cx.tcx.def_span(field.did);
2727-
write!(
2728-
&mut msg,
2729-
" (in this {} field)",
2730-
adt_def.descr()
2731-
)
2732-
.unwrap();
2733-
2734-
InitError { msg, span: Some(span), generic }
2735-
} else {
2736-
// Just forward.
2737-
InitError { msg, span, generic }
2738-
}
2739-
},
2740-
)
2741-
})
2742-
}
2743-
// Multi-variant enum.
2744-
_ => {
2745-
// This will warn on something like Result<MaybeUninit<u32>, !> which
2746-
// is not UB under the current enum layout, even ignoring the 0x01
2747-
// filling.
2748-
//
2749-
// That's probably fine though.
2750-
let span = cx.tcx.def_span(adt_def.did());
2751-
Some(InitError::with_span(
2752-
"enums have to be initialized to a variant",
2753-
span,
2754-
))
2755-
}
2756-
}
2757-
}
2758-
Tuple(..) => {
2759-
// Proceed recursively, check all fields.
2760-
ty.tuple_fields().iter().find_map(|field| ty_find_init_error(cx, field))
2761-
}
2762-
Array(ty, len) => {
2763-
match len.try_eval_usize(cx.tcx, cx.param_env) {
2764-
// Array known to be zero sized, we can't warn.
2765-
Some(0) => None,
2766-
2767-
// Array length known to be nonzero, warn.
2768-
Some(1..) => ty_find_init_error(cx, *ty),
2769-
2770-
// Array length unknown, use the "might not permit" wording.
2771-
None => ty_find_init_error(cx, *ty).map(|mut e| {
2772-
e.generic = true;
2773-
e
2774-
}),
2775-
}
2776-
}
2777-
Int(_) | Uint(_) | Float(_) | RawPtr(_) => {
2778-
// These are Plain Old Data types that people expect to work if they leave them
2779-
// uninitialized.
2780-
None
2781-
}
2782-
// Pessimistic fallback.
2783-
_ => Some(InitError::generic()),
2784-
}
2785-
}
2786-
2787-
if is_dangerous_init(cx, expr) {
2788-
// This conjures an instance of a type out of nothing,
2789-
// using zeroed or uninitialized memory.
2790-
// We are extremely conservative with what we warn about.
2791-
let conjured_ty = cx.typeck_results().expr_ty(expr);
2792-
if let Some(init_error) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty)) {
2793-
let main_msg = with_no_trimmed_paths!(if init_error.generic {
2794-
format!(
2795-
"the type `{conjured_ty}` is generic, and might not permit being left uninitialized"
2796-
)
2797-
} else {
2798-
format!("the type `{conjured_ty}` does not permit being left uninitialized")
2799-
});
2800-
2801-
// FIXME(davidtwco): make translatable
2802-
cx.struct_span_lint(MEM_UNINITIALIZED, expr.span, |lint| {
2803-
let mut err = lint.build(&main_msg);
2804-
2805-
err.span_label(expr.span, "this code causes undefined behavior when executed");
2806-
err.span_label(
2807-
expr.span,
2808-
"help: use `MaybeUninit<T>` instead, \
2809-
and only call `assume_init` after initialization is done",
2810-
);
2811-
if let Some(span) = init_error.span {
2812-
err.span_note(span, &init_error.msg);
2813-
} else {
2814-
err.note(&init_error.msg);
2815-
}
2816-
err.emit();
2817-
});
2818-
}
2819-
}
2820-
}
2821-
}
2822-
28232612
declare_lint! {
28242613
/// The `clashing_extern_declarations` lint detects when an `extern fn`
28252614
/// has been declared with the same name but different types.

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ mod internal;
5757
mod late;
5858
mod let_underscore;
5959
mod levels;
60+
mod mem_uninitialized;
6061
mod methods;
6162
mod non_ascii_idents;
6263
mod non_fmt_panic;
@@ -69,6 +70,8 @@ mod traits;
6970
mod types;
7071
mod unused;
7172

73+
use mem_uninitialized::MemUninitialized;
74+
7275
pub use array_into_iter::ARRAY_INTO_ITER;
7376

7477
use rustc_ast as ast;

0 commit comments

Comments
 (0)