Skip to content

Commit 2bed079

Browse files
committed
Compute layout instead of manually procesisng the layout restriction attributes
1 parent 5cbf172 commit 2bed079

File tree

2 files changed

+99
-31
lines changed

2 files changed

+99
-31
lines changed

compiler/rustc_lint/src/builtin.rs

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use rustc_span::edition::Edition;
5252
use rustc_span::source_map::Spanned;
5353
use rustc_span::symbol::{kw, sym, Ident, Symbol};
5454
use rustc_span::{BytePos, InnerSpan, Span};
55-
use rustc_target::abi::VariantIdx;
55+
use rustc_target::abi::{Abi, VariantIdx};
5656
use rustc_trait_selection::traits::{self, misc::can_type_implement_copy};
5757

5858
use crate::nonstandard_style::{method_context, MethodLateContext};
@@ -2418,9 +2418,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24182418
Self { span: Some(span), ..self }
24192419
}
24202420

2421-
fn nested(self, nested: InitError) -> InitError {
2421+
fn nested(self, nested: impl Into<Option<InitError>>) -> InitError {
24222422
assert!(self.nested.is_none());
2423-
Self { nested: Some(Box::new(nested)), ..self }
2423+
Self { nested: nested.into().map(Box::new), ..self }
24242424
}
24252425
}
24262426

@@ -2489,18 +2489,47 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24892489

24902490
fn variant_find_init_error<'tcx>(
24912491
cx: &LateContext<'tcx>,
2492+
ty: Ty<'tcx>,
24922493
variant: &VariantDef,
24932494
substs: ty::SubstsRef<'tcx>,
24942495
descr: &str,
24952496
init: InitKind,
24962497
) -> Option<InitError> {
2497-
variant.fields.iter().find_map(|field| {
2498+
let field_err = variant.fields.iter().find_map(|field| {
24982499
ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(|err| {
24992500
InitError::from(format!("in this {descr}"))
25002501
.spanned(cx.tcx.def_span(field.did))
25012502
.nested(err)
25022503
})
2503-
})
2504+
});
2505+
2506+
// Check if this ADT has a constrained layout (like `NonNull` and friends).
2507+
let layout = cx.tcx.layout_of(cx.param_env.and(ty)).unwrap();
2508+
2509+
match &layout.abi {
2510+
Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) => {
2511+
let range = scalar.valid_range(cx);
2512+
if !range.contains(0) {
2513+
Some(
2514+
InitError::from(format!("`{}` must be non-null", ty)).nested(field_err),
2515+
)
2516+
} else if init == InitKind::Uninit && !scalar.is_always_valid(cx) {
2517+
// Prefer reporting on the fields over the entire struct for uninit,
2518+
// as the information bubbles out and it may be unclear why the type can't
2519+
// be null from just its outside signature.
2520+
Some(
2521+
InitError::from(format!(
2522+
"`{}` must be initialized inside its custom valid range",
2523+
ty,
2524+
))
2525+
.nested(field_err),
2526+
)
2527+
} else {
2528+
field_err
2529+
}
2530+
}
2531+
_ => field_err,
2532+
}
25042533
}
25052534

25062535
/// Return `Some` only if we are sure this type does *not*
@@ -2540,36 +2569,11 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
25402569
}
25412570
// Recurse and checks for some compound types. (but not unions)
25422571
Adt(adt_def, substs) if !adt_def.is_union() => {
2543-
// First check if this ADT has a layout attribute (like `NonNull` and friends).
2544-
use std::ops::Bound;
2545-
match cx.tcx.layout_scalar_valid_range(adt_def.did()) {
2546-
// We exploit here that `layout_scalar_valid_range` will never
2547-
// return `Bound::Excluded`. (And we have tests checking that we
2548-
// handle the attribute correctly.)
2549-
// We don't add a span since users cannot declare such types anyway.
2550-
(Bound::Included(lo), Bound::Included(hi)) if 0 < lo && lo < hi => {
2551-
return Some(format!("`{}` must be non-null", ty).into());
2552-
}
2553-
(Bound::Included(lo), Bound::Unbounded) if 0 < lo => {
2554-
return Some(format!("`{}` must be non-null", ty).into());
2555-
}
2556-
(Bound::Included(_), _) | (_, Bound::Included(_))
2557-
if init == InitKind::Uninit =>
2558-
{
2559-
return Some(
2560-
format!(
2561-
"`{}` must be initialized inside its custom valid range",
2562-
ty,
2563-
)
2564-
.into(),
2565-
);
2566-
}
2567-
_ => {}
2568-
}
25692572
// Handle structs.
25702573
if adt_def.is_struct() {
25712574
return variant_find_init_error(
25722575
cx,
2576+
ty,
25732577
adt_def.non_enum_variant(),
25742578
substs,
25752579
"struct field",
@@ -2600,6 +2604,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
26002604
// There is only one potentially inhabited variant. So we can recursively check that variant!
26012605
return variant_find_init_error(
26022606
cx,
2607+
ty,
26032608
&first_variant.0,
26042609
substs,
26052610
"field of the only potentially inhabited enum variant",

src/test/ui/lint/invalid_value.stderr

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ LL | let _val: Wrap<&'static T> = mem::zeroed();
3434
| this code causes undefined behavior when executed
3535
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
3636
|
37+
= note: `Wrap<&T>` must be non-null
3738
note: in this struct field
3839
--> $DIR/invalid_value.rs:17:18
3940
|
@@ -50,6 +51,7 @@ LL | let _val: Wrap<&'static T> = mem::uninitialized();
5051
| this code causes undefined behavior when executed
5152
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
5253
|
54+
= note: `Wrap<&T>` must be non-null
5355
note: in this struct field
5456
--> $DIR/invalid_value.rs:17:18
5557
|
@@ -162,6 +164,7 @@ LL | let _val: Ref = mem::zeroed();
162164
| this code causes undefined behavior when executed
163165
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
164166
|
167+
= note: `Ref` must be non-null
165168
note: in this struct field
166169
--> $DIR/invalid_value.rs:14:12
167170
|
@@ -178,6 +181,7 @@ LL | let _val: Ref = mem::uninitialized();
178181
| this code causes undefined behavior when executed
179182
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
180183
|
184+
= note: `Ref` must be non-null
181185
note: in this struct field
182186
--> $DIR/invalid_value.rs:14:12
183187
|
@@ -216,6 +220,7 @@ LL | let _val: Wrap<fn()> = mem::zeroed();
216220
| this code causes undefined behavior when executed
217221
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
218222
|
223+
= note: `Wrap<fn()>` must be non-null
219224
note: in this struct field
220225
--> $DIR/invalid_value.rs:17:18
221226
|
@@ -232,6 +237,7 @@ LL | let _val: Wrap<fn()> = mem::uninitialized();
232237
| this code causes undefined behavior when executed
233238
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
234239
|
240+
= note: `Wrap<fn()>` must be non-null
235241
note: in this struct field
236242
--> $DIR/invalid_value.rs:17:18
237243
|
@@ -248,6 +254,7 @@ LL | let _val: WrapEnum<fn()> = mem::zeroed();
248254
| this code causes undefined behavior when executed
249255
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
250256
|
257+
= note: `WrapEnum<fn()>` must be non-null
251258
note: in this field of the only potentially inhabited enum variant
252259
--> $DIR/invalid_value.rs:18:28
253260
|
@@ -264,6 +271,7 @@ LL | let _val: WrapEnum<fn()> = mem::uninitialized();
264271
| this code causes undefined behavior when executed
265272
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
266273
|
274+
= note: `WrapEnum<fn()>` must be non-null
267275
note: in this field of the only potentially inhabited enum variant
268276
--> $DIR/invalid_value.rs:18:28
269277
|
@@ -285,6 +293,7 @@ note: in this struct field
285293
|
286294
LL | struct Wrap<T> { wrapped: T }
287295
| ^^^^^^^^^^
296+
= note: `RefPair` must be non-null
288297
note: in this struct field
289298
--> $DIR/invalid_value.rs:15:16
290299
|
@@ -306,6 +315,7 @@ note: in this struct field
306315
|
307316
LL | struct Wrap<T> { wrapped: T }
308317
| ^^^^^^^^^^
318+
= note: `RefPair` must be non-null
309319
note: in this struct field
310320
--> $DIR/invalid_value.rs:15:16
311321
|
@@ -334,6 +344,12 @@ LL | let _val: NonNull<i32> = mem::uninitialized();
334344
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
335345
|
336346
= note: `std::ptr::NonNull<i32>` must be non-null
347+
note: in this struct field
348+
--> $SRC_DIR/core/src/ptr/non_null.rs:LL:COL
349+
|
350+
LL | pointer: *const T,
351+
| ^^^^^^^^^^^^^^^^^
352+
= note: raw pointers must not be uninitialized
337353

338354
error: the type `(NonZeroU32, i32)` does not permit zero-initialization
339355
--> $DIR/invalid_value.rs:95:39
@@ -356,6 +372,19 @@ LL | let _val: (NonZeroU32, i32) = mem::uninitialized();
356372
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
357373
|
358374
= note: `std::num::NonZeroU32` must be non-null
375+
note: in this struct field
376+
--> $SRC_DIR/core/src/num/nonzero.rs:LL:COL
377+
|
378+
LL | / nonzero_integers! {
379+
LL | | #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU8(u8);
380+
LL | | #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16);
381+
LL | | #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32);
382+
... |
383+
LL | | #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIs...
384+
LL | | }
385+
| |_^
386+
= note: integers must not be uninitialized
387+
= note: this error originates in the macro `nonzero_integers` (in Nightly builds, run with -Z macro-backtrace for more info)
359388

360389
error: the type `*const dyn Send` does not permit zero-initialization
361390
--> $DIR/invalid_value.rs:98:37
@@ -440,6 +469,7 @@ LL | let _val: OneFruitNonZero = mem::zeroed();
440469
| this code causes undefined behavior when executed
441470
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
442471
|
472+
= note: `OneFruitNonZero` must be non-null
443473
note: in this field of the only potentially inhabited enum variant
444474
--> $DIR/invalid_value.rs:39:12
445475
|
@@ -456,12 +486,26 @@ LL | let _val: OneFruitNonZero = mem::uninitialized();
456486
| this code causes undefined behavior when executed
457487
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
458488
|
489+
= note: `OneFruitNonZero` must be non-null
459490
note: in this field of the only potentially inhabited enum variant
460491
--> $DIR/invalid_value.rs:39:12
461492
|
462493
LL | Banana(NonZeroU32),
463494
| ^^^^^^^^^^
464495
= note: `std::num::NonZeroU32` must be non-null
496+
note: in this struct field
497+
--> $SRC_DIR/core/src/num/nonzero.rs:LL:COL
498+
|
499+
LL | / nonzero_integers! {
500+
LL | | #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU8(u8);
501+
LL | | #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16);
502+
LL | | #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32);
503+
... |
504+
LL | | #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIs...
505+
LL | | }
506+
| |_^
507+
= note: integers must not be uninitialized
508+
= note: this error originates in the macro `nonzero_integers` (in Nightly builds, run with -Z macro-backtrace for more info)
465509

466510
error: the type `bool` does not permit being left uninitialized
467511
--> $DIR/invalid_value.rs:112:26
@@ -483,6 +527,7 @@ LL | let _val: Wrap<char> = mem::uninitialized();
483527
| this code causes undefined behavior when executed
484528
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
485529
|
530+
= note: `Wrap<char>` must be initialized inside its custom valid range
486531
note: in this struct field
487532
--> $DIR/invalid_value.rs:17:18
488533
|
@@ -500,6 +545,12 @@ LL | let _val: NonBig = mem::uninitialized();
500545
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
501546
|
502547
= note: `NonBig` must be initialized inside its custom valid range
548+
note: in this struct field
549+
--> $DIR/invalid_value.rs:23:26
550+
|
551+
LL | pub(crate) struct NonBig(u64);
552+
| ^^^
553+
= note: integers must not be uninitialized
503554

504555
error: the type `Fruit` does not permit being left uninitialized
505556
--> $DIR/invalid_value.rs:121:27
@@ -581,6 +632,12 @@ LL | let _val: WrapAroundRange = mem::uninitialized();
581632
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
582633
|
583634
= note: `WrapAroundRange` must be initialized inside its custom valid range
635+
note: in this struct field
636+
--> $DIR/invalid_value.rs:49:35
637+
|
638+
LL | pub(crate) struct WrapAroundRange(u8);
639+
| ^^
640+
= note: integers must not be uninitialized
584641

585642
error: the type `Result<i32, i32>` does not permit being left uninitialized
586643
--> $DIR/invalid_value.rs:144:38
@@ -651,6 +708,12 @@ LL | let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
651708
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
652709
|
653710
= note: `std::ptr::NonNull<i32>` must be non-null
711+
note: in this struct field
712+
--> $SRC_DIR/core/src/ptr/non_null.rs:LL:COL
713+
|
714+
LL | pointer: *const T,
715+
| ^^^^^^^^^^^^^^^^^
716+
= note: raw pointers must not be uninitialized
654717

655718
error: the type `bool` does not permit being left uninitialized
656719
--> $DIR/invalid_value.rs:159:26

0 commit comments

Comments
 (0)