Skip to content

Commit 86a98df

Browse files
committed
Indicate when a type is generic or not generic
1 parent ad8bc89 commit 86a98df

7 files changed

+270
-156
lines changed

compiler/rustc_lint/src/builtin.rs

+64-33
Original file line numberDiff line numberDiff line change
@@ -2633,7 +2633,29 @@ impl<'tcx> LateLintPass<'tcx> for MemUninitialized {
26332633
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
26342634
/// Information about why a type cannot be initialized this way.
26352635
/// Contains an error message and optionally a span to point at.
2636-
type InitError = (String, Option<Span>);
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+
}
26372659

26382660
/// Determine if this expression is a "dangerous initialization".
26392661
fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
@@ -2657,18 +2679,18 @@ impl<'tcx> LateLintPass<'tcx> for MemUninitialized {
26572679
use rustc_type_ir::sty::TyKind::*;
26582680
match ty.kind() {
26592681
// Primitive types that don't like 0 as a value.
2660-
Ref(..) => Some(("references must be non-null".to_string(), None)),
2661-
Adt(..) if ty.is_box() => Some(("`Box` must be non-null".to_string(), None)),
2662-
FnPtr(..) => Some(("function pointers must be non-null".to_string(), None)),
2663-
Never => Some(("the `!` type has no valid value".to_string(), None)),
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")),
26642686
RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) =>
26652687
// raw ptr to dyn Trait
26662688
{
2667-
Some(("the vtable of a wide raw pointer must be non-null".to_string(), None))
2689+
Some(InitError::new("the vtable of a wide raw pointer must be non-null"))
26682690
}
26692691
// Primitive types with other constraints.
2670-
Bool => Some(("booleans must be either `true` or `false`".to_string(), None)),
2671-
Char => Some(("characters must be a valid Unicode codepoint".to_string(), None)),
2692+
Bool => Some(InitError::new("booleans must be either `true` or `false`")),
2693+
Char => Some(InitError::new("characters must be a valid Unicode codepoint")),
26722694
Adt(adt_def, _) if adt_def.is_union() => None,
26732695
// Recurse and checks for some compound types.
26742696
Adt(adt_def, substs) => {
@@ -2679,29 +2701,25 @@ impl<'tcx> LateLintPass<'tcx> for MemUninitialized {
26792701
// return `Bound::Excluded`. (And we have tests checking that we
26802702
// handle the attribute correctly.)
26812703
(Bound::Included(lo), _) if lo > 0 => {
2682-
return Some((format!("`{}` must be non-null", ty), None));
2704+
return Some(InitError::new(format!("`{ty}` must be non-null")));
26832705
}
26842706
(Bound::Included(_), _) | (_, Bound::Included(_)) => {
2685-
return Some((
2686-
format!(
2687-
"`{}` must be initialized inside its custom valid range",
2688-
ty,
2689-
),
2690-
None,
2691-
));
2707+
return Some(InitError::new(format!(
2708+
"`{ty}` must be initialized inside its custom valid range"
2709+
)));
26922710
}
26932711
_ => {}
26942712
}
26952713
// Now, recurse.
26962714
match adt_def.variants().len() {
2697-
0 => Some(("enums with no variants have no valid value".to_string(), None)),
2715+
0 => Some(InitError::new("enums with no variants have no valid value")),
26982716
1 => {
26992717
// Struct, or enum with exactly one variant.
27002718
// Proceed recursively, check all fields.
27012719
let variant = &adt_def.variant(VariantIdx::from_u32(0));
27022720
variant.fields.iter().find_map(|field| {
27032721
ty_find_init_error(cx, field.ty(cx.tcx, substs)).map(
2704-
|(mut msg, span)| {
2722+
|InitError { mut msg, span, generic }| {
27052723
if span.is_none() {
27062724
// Point to this field, should be helpful for figuring
27072725
// out where the source of the error is.
@@ -2712,10 +2730,11 @@ impl<'tcx> LateLintPass<'tcx> for MemUninitialized {
27122730
adt_def.descr()
27132731
)
27142732
.unwrap();
2715-
(msg, Some(span))
2733+
2734+
InitError { msg, span: Some(span), generic }
27162735
} else {
27172736
// Just forward.
2718-
(msg, span)
2737+
InitError { msg, span, generic }
27192738
}
27202739
},
27212740
)
@@ -2729,9 +2748,9 @@ impl<'tcx> LateLintPass<'tcx> for MemUninitialized {
27292748
//
27302749
// That's probably fine though.
27312750
let span = cx.tcx.def_span(adt_def.did());
2732-
Some((
2733-
"enums have to be initialized to a variant".to_string(),
2734-
Some(span),
2751+
Some(InitError::with_span(
2752+
"enums have to be initialized to a variant",
2753+
span,
27352754
))
27362755
}
27372756
}
@@ -2745,7 +2764,14 @@ impl<'tcx> LateLintPass<'tcx> for MemUninitialized {
27452764
// Array known to be zero sized, we can't warn.
27462765
Some(0) => None,
27472766

2748-
Some(_) | None => ty_find_init_error(cx, *ty),
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+
}),
27492775
}
27502776
}
27512777
Int(_) | Uint(_) | Float(_) | RawPtr(_) => {
@@ -2754,7 +2780,7 @@ impl<'tcx> LateLintPass<'tcx> for MemUninitialized {
27542780
None
27552781
}
27562782
// Pessimistic fallback.
2757-
_ => Some(("type might not be allowed to be left uninitialized".to_string(), None)),
2783+
_ => Some(InitError::generic()),
27582784
}
27592785
}
27602786

@@ -2763,24 +2789,29 @@ impl<'tcx> LateLintPass<'tcx> for MemUninitialized {
27632789
// using zeroed or uninitialized memory.
27642790
// We are extremely conservative with what we warn about.
27652791
let conjured_ty = cx.typeck_results().expr_ty(expr);
2766-
if let Some((msg, span)) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty)) {
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+
27672801
// FIXME(davidtwco): make translatable
27682802
cx.struct_span_lint(MEM_UNINITIALIZED, expr.span, |lint| {
2769-
let mut err = with_no_trimmed_paths!(lint.build(&format!(
2770-
"the type `{}` does not definitely permit being left uninitialized",
2771-
conjured_ty,
2772-
)));
2803+
let mut err = lint.build(&main_msg);
27732804

27742805
err.span_label(expr.span, "this code causes undefined behavior when executed");
27752806
err.span_label(
27762807
expr.span,
27772808
"help: use `MaybeUninit<T>` instead, \
27782809
and only call `assume_init` after initialization is done",
27792810
);
2780-
if let Some(span) = span {
2781-
err.span_note(span, &msg);
2811+
if let Some(span) = init_error.span {
2812+
err.span_note(span, &init_error.msg);
27822813
} else {
2783-
err.note(&msg);
2814+
err.note(&init_error.msg);
27842815
}
27852816
err.emit();
27862817
});

src/test/ui/intrinsics/mem-uninitialized-future-compat.rs

+19-12
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ struct UninitStruct {
1111

1212
unsafe fn unknown_type<T, const N: usize>() {
1313
std::mem::uninitialized::<T>();
14-
//~^ ERROR the type `T` does not definitely permit being left uninitialized
14+
//~^ ERROR the type `T` is generic, and might not permit being left uninitialized
1515

1616
std::mem::uninitialized::<[T; N]>();
17-
//~^ ERROR the type `[T; N]` does not definitely permit being left uninitialized
17+
//~^ ERROR the type `[T; N]` is generic, and might not permit being left uninitialized
1818

1919
std::mem::uninitialized::<[char; N]>();
20-
//~^ ERROR the type `[char; N]` does not definitely permit being left uninitialized
20+
//~^ ERROR the type `[char; N]` is generic, and might not permit being left uninitialized
21+
22+
std::mem::uninitialized::<[UninitStruct; N]>();
23+
//~^ ERROR the type `[UninitStruct; N]` is generic, and might not permit being left uninitialized
2124

2225
std::mem::uninitialized::<[T; 0]>();
2326
std::mem::uninitialized::<[char; 0]>();
@@ -26,35 +29,39 @@ unsafe fn unknown_type<T, const N: usize>() {
2629
fn main() {
2730
unsafe {
2831
std::mem::uninitialized::<&'static u32>();
29-
//~^ ERROR the type `&u32` does not definitely permit being left uninitialized
32+
//~^ ERROR the type `&u32` does not permit being left uninitialized
3033

3134
std::mem::uninitialized::<Box<u32>>();
32-
//~^ ERROR the type `std::boxed::Box<u32>` does not definitely permit being left uninitialized
35+
//~^ ERROR the type `std::boxed::Box<u32>` does not permit being left uninitialized
3336

3437
std::mem::uninitialized::<fn()>();
35-
//~^ ERROR the type `fn()` does not definitely permit being left uninitialized
38+
//~^ ERROR the type `fn()` does not permit being left uninitialized
3639

3740
std::mem::uninitialized::<!>();
38-
//~^ ERROR the type `!` does not definitely permit being left uninitialized
41+
//~^ ERROR the type `!` does not permit being left uninitialized
3942

4043
std::mem::uninitialized::<*mut dyn std::io::Write>();
41-
//~^ ERROR the type `*mut dyn std::io::Write` does not definitely permit being left uninitialized
44+
//~^ ERROR the type `*mut dyn std::io::Write` does not permit being left uninitialized
4245

4346
std::mem::uninitialized::<bool>();
44-
//~^ ERROR the type `bool` does not definitely permit being left uninitialized
47+
//~^ ERROR the type `bool` does not permit being left uninitialized
4548

4649
std::mem::uninitialized::<char>();
47-
//~^ ERROR the type `char` does not definitely permit being left uninitialized
50+
//~^ ERROR the type `char` does not permit being left uninitialized
4851

4952
std::mem::uninitialized::<UninitStruct>();
50-
//~^ ERROR the type `UninitStruct` does not definitely permit being left uninitialized
53+
//~^ ERROR the type `UninitStruct` does not permit being left uninitialized
54+
55+
std::mem::uninitialized::<[UninitStruct; 16]>();
56+
//~^ ERROR the type `[UninitStruct; 16]` does not permit being left uninitialized
5157

5258
std::mem::uninitialized::<(u32, char)>();
53-
//~^ ERROR the type `(u32, char)` does not definitely permit being left uninitialized
59+
//~^ ERROR the type `(u32, char)` does not permit being left uninitialized
5460

5561
std::mem::uninitialized::<MaybeUninit<Box<u32>>>();
5662
std::mem::uninitialized::<usize>();
5763
std::mem::uninitialized::<f32>();
5864
std::mem::uninitialized::<*const u8>();
65+
std::mem::uninitialized::<[u8; 64]>();
5966
}
6067
}

0 commit comments

Comments
 (0)