Auto merge of #103917 - oli-obk:layout_math, r=RalfJung,lcnr
Various cleanups around scalar layout restrictions Pulled out of https://github.com/rust-lang/rust/pull/103724
This commit is contained in:
commit
df04d28163
16 changed files with 261 additions and 129 deletions
|
@ -382,28 +382,26 @@ pub trait LayoutCalculator {
|
|||
let (start, end) = scalar_valid_range;
|
||||
match st.abi {
|
||||
Abi::Scalar(ref mut scalar) | Abi::ScalarPair(ref mut scalar, _) => {
|
||||
// the asserts ensure that we are not using the
|
||||
// `#[rustc_layout_scalar_valid_range(n)]`
|
||||
// attribute to widen the range of anything as that would probably
|
||||
// result in UB somewhere
|
||||
// FIXME(eddyb) the asserts are probably not needed,
|
||||
// as larger validity ranges would result in missed
|
||||
// Enlarging validity ranges would result in missed
|
||||
// optimizations, *not* wrongly assuming the inner
|
||||
// value is valid. e.g. unions enlarge validity ranges,
|
||||
// value is valid. e.g. unions already enlarge validity ranges,
|
||||
// because the values may be uninitialized.
|
||||
//
|
||||
// Because of that we only check that the start and end
|
||||
// of the range is representable with this scalar type.
|
||||
|
||||
let max_value = scalar.size(dl).unsigned_int_max();
|
||||
if let Bound::Included(start) = start {
|
||||
// FIXME(eddyb) this might be incorrect - it doesn't
|
||||
// account for wrap-around (end < start) ranges.
|
||||
let valid_range = scalar.valid_range_mut();
|
||||
assert!(valid_range.start <= start);
|
||||
valid_range.start = start;
|
||||
assert!(start <= max_value, "{start} > {max_value}");
|
||||
scalar.valid_range_mut().start = start;
|
||||
}
|
||||
if let Bound::Included(end) = end {
|
||||
// FIXME(eddyb) this might be incorrect - it doesn't
|
||||
// account for wrap-around (end < start) ranges.
|
||||
let valid_range = scalar.valid_range_mut();
|
||||
assert!(valid_range.end >= end);
|
||||
valid_range.end = end;
|
||||
assert!(end <= max_value, "{end} > {max_value}");
|
||||
scalar.valid_range_mut().end = end;
|
||||
}
|
||||
|
||||
// Update `largest_niche` if we have introduced a larger niche.
|
||||
|
|
|
@ -785,18 +785,10 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
}
|
||||
}
|
||||
Abi::ScalarPair(a_layout, b_layout) => {
|
||||
// There is no `rustc_layout_scalar_valid_range_start` for pairs, so
|
||||
// we would validate these things as we descend into the fields,
|
||||
// but that can miss bugs in layout computation. Layout computation
|
||||
// is subtle due to enums having ScalarPair layout, where one field
|
||||
// is the discriminant.
|
||||
if cfg!(debug_assertions)
|
||||
&& !a_layout.is_uninit_valid()
|
||||
&& !b_layout.is_uninit_valid()
|
||||
{
|
||||
// We can only proceed if *both* scalars need to be initialized.
|
||||
// FIXME: find a way to also check ScalarPair when one side can be uninit but
|
||||
// the other must be init.
|
||||
// We can only proceed if *both* scalars need to be initialized.
|
||||
// FIXME: find a way to also check ScalarPair when one side can be uninit but
|
||||
// the other must be init.
|
||||
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
|
||||
let (a, b) =
|
||||
self.read_immediate(op, "initiailized scalar value")?.to_scalar_pair();
|
||||
self.visit_scalar(a, a_layout)?;
|
||||
|
|
|
@ -81,6 +81,7 @@ struct TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
#[track_caller]
|
||||
fn fail(&self, location: Location, msg: impl AsRef<str>) {
|
||||
let span = self.body.source_info(location).span;
|
||||
// We use `delay_span_bug` as we might see broken MIR when other errors have already
|
||||
|
@ -226,12 +227,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
let check_equal = |this: &Self, location, f_ty| {
|
||||
if !this.mir_assign_valid_types(ty, f_ty) {
|
||||
this.fail(
|
||||
location,
|
||||
format!(
|
||||
"Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`",
|
||||
parent, f, ty, f_ty
|
||||
location,
|
||||
format!(
|
||||
"Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`",
|
||||
parent, f, ty, f_ty
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ use rustc_span::edition::Edition;
|
|||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{BytePos, InnerSpan, Span};
|
||||
use rustc_target::abi::VariantIdx;
|
||||
use rustc_target::abi::{Abi, VariantIdx};
|
||||
use rustc_trait_selection::traits::{self, misc::can_type_implement_copy};
|
||||
|
||||
use crate::nonstandard_style::{method_context, MethodLateContext};
|
||||
|
@ -2413,8 +2413,34 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
|||
}
|
||||
|
||||
/// Information about why a type cannot be initialized this way.
|
||||
/// Contains an error message and optionally a span to point at.
|
||||
type InitError = (String, Option<Span>);
|
||||
struct InitError {
|
||||
message: String,
|
||||
/// Spans from struct fields and similar that can be obtained from just the type.
|
||||
span: Option<Span>,
|
||||
/// Used to report a trace through adts.
|
||||
nested: Option<Box<InitError>>,
|
||||
}
|
||||
impl InitError {
|
||||
fn spanned(self, span: Span) -> InitError {
|
||||
Self { span: Some(span), ..self }
|
||||
}
|
||||
|
||||
fn nested(self, nested: impl Into<Option<InitError>>) -> InitError {
|
||||
assert!(self.nested.is_none());
|
||||
Self { nested: nested.into().map(Box::new), ..self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for InitError {
|
||||
fn from(s: &'a str) -> Self {
|
||||
s.to_owned().into()
|
||||
}
|
||||
}
|
||||
impl From<String> for InitError {
|
||||
fn from(message: String) -> Self {
|
||||
Self { message, span: None, nested: None }
|
||||
}
|
||||
}
|
||||
|
||||
/// Test if this constant is all-0.
|
||||
fn is_zero(expr: &hir::Expr<'_>) -> bool {
|
||||
|
@ -2470,25 +2496,54 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
|||
|
||||
fn variant_find_init_error<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
variant: &VariantDef,
|
||||
substs: ty::SubstsRef<'tcx>,
|
||||
descr: &str,
|
||||
init: InitKind,
|
||||
) -> Option<InitError> {
|
||||
variant.fields.iter().find_map(|field| {
|
||||
ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(|(mut msg, span)| {
|
||||
if span.is_none() {
|
||||
// Point to this field, should be helpful for figuring
|
||||
// out where the source of the error is.
|
||||
let span = cx.tcx.def_span(field.did);
|
||||
write!(&mut msg, " (in this {descr})").unwrap();
|
||||
(msg, Some(span))
|
||||
let mut field_err = variant.fields.iter().find_map(|field| {
|
||||
ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(|mut err| {
|
||||
if !field.did.is_local() {
|
||||
err
|
||||
} else if err.span.is_none() {
|
||||
err.span = Some(cx.tcx.def_span(field.did));
|
||||
write!(&mut err.message, " (in this {descr})").unwrap();
|
||||
err
|
||||
} else {
|
||||
// Just forward.
|
||||
(msg, span)
|
||||
InitError::from(format!("in this {descr}"))
|
||||
.spanned(cx.tcx.def_span(field.did))
|
||||
.nested(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// Check if this ADT has a constrained layout (like `NonNull` and friends).
|
||||
if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) {
|
||||
if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &layout.abi {
|
||||
let range = scalar.valid_range(cx);
|
||||
let msg = if !range.contains(0) {
|
||||
"must be non-null"
|
||||
} else if init == InitKind::Uninit && !scalar.is_always_valid(cx) {
|
||||
// Prefer reporting on the fields over the entire struct for uninit,
|
||||
// as the information bubbles out and it may be unclear why the type can't
|
||||
// be null from just its outside signature.
|
||||
|
||||
"must be initialized inside its custom valid range"
|
||||
} else {
|
||||
return field_err;
|
||||
};
|
||||
if let Some(field_err) = &mut field_err {
|
||||
// Most of the time, if the field error is the same as the struct error,
|
||||
// the struct error only happens because of the field error.
|
||||
if field_err.message.contains(msg) {
|
||||
field_err.message = format!("because {}", field_err.message);
|
||||
}
|
||||
}
|
||||
return Some(InitError::from(format!("`{ty}` {msg}")).nested(field_err));
|
||||
}
|
||||
}
|
||||
field_err
|
||||
}
|
||||
|
||||
/// Return `Some` only if we are sure this type does *not*
|
||||
|
@ -2501,63 +2556,36 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
|||
use rustc_type_ir::sty::TyKind::*;
|
||||
match ty.kind() {
|
||||
// Primitive types that don't like 0 as a value.
|
||||
Ref(..) => Some(("references must be non-null".to_string(), None)),
|
||||
Adt(..) if ty.is_box() => Some(("`Box` must be non-null".to_string(), None)),
|
||||
FnPtr(..) => Some(("function pointers must be non-null".to_string(), None)),
|
||||
Never => Some(("the `!` type has no valid value".to_string(), None)),
|
||||
Ref(..) => Some("references must be non-null".into()),
|
||||
Adt(..) if ty.is_box() => Some("`Box` must be non-null".into()),
|
||||
FnPtr(..) => Some("function pointers must be non-null".into()),
|
||||
Never => Some("the `!` type has no valid value".into()),
|
||||
RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) =>
|
||||
// raw ptr to dyn Trait
|
||||
{
|
||||
Some(("the vtable of a wide raw pointer must be non-null".to_string(), None))
|
||||
Some("the vtable of a wide raw pointer must be non-null".into())
|
||||
}
|
||||
// Primitive types with other constraints.
|
||||
Bool if init == InitKind::Uninit => {
|
||||
Some(("booleans must be either `true` or `false`".to_string(), None))
|
||||
Some("booleans must be either `true` or `false`".into())
|
||||
}
|
||||
Char if init == InitKind::Uninit => {
|
||||
Some(("characters must be a valid Unicode codepoint".to_string(), None))
|
||||
Some("characters must be a valid Unicode codepoint".into())
|
||||
}
|
||||
Int(_) | Uint(_) if init == InitKind::Uninit => {
|
||||
Some(("integers must not be uninitialized".to_string(), None))
|
||||
}
|
||||
Float(_) if init == InitKind::Uninit => {
|
||||
Some(("floats must not be uninitialized".to_string(), None))
|
||||
Some("integers must be initialized".into())
|
||||
}
|
||||
Float(_) if init == InitKind::Uninit => Some("floats must be initialized".into()),
|
||||
RawPtr(_) if init == InitKind::Uninit => {
|
||||
Some(("raw pointers must not be uninitialized".to_string(), None))
|
||||
Some("raw pointers must be initialized".into())
|
||||
}
|
||||
// Recurse and checks for some compound types. (but not unions)
|
||||
Adt(adt_def, substs) if !adt_def.is_union() => {
|
||||
// First check if this ADT has a layout attribute (like `NonNull` and friends).
|
||||
use std::ops::Bound;
|
||||
match cx.tcx.layout_scalar_valid_range(adt_def.did()) {
|
||||
// We exploit here that `layout_scalar_valid_range` will never
|
||||
// return `Bound::Excluded`. (And we have tests checking that we
|
||||
// handle the attribute correctly.)
|
||||
// We don't add a span since users cannot declare such types anyway.
|
||||
(Bound::Included(lo), Bound::Included(hi)) if 0 < lo && lo < hi => {
|
||||
return Some((format!("`{}` must be non-null", ty), None));
|
||||
}
|
||||
(Bound::Included(lo), Bound::Unbounded) if 0 < lo => {
|
||||
return Some((format!("`{}` must be non-null", ty), None));
|
||||
}
|
||||
(Bound::Included(_), _) | (_, Bound::Included(_))
|
||||
if init == InitKind::Uninit =>
|
||||
{
|
||||
return Some((
|
||||
format!(
|
||||
"`{}` must be initialized inside its custom valid range",
|
||||
ty,
|
||||
),
|
||||
None,
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Handle structs.
|
||||
if adt_def.is_struct() {
|
||||
return variant_find_init_error(
|
||||
cx,
|
||||
ty,
|
||||
adt_def.non_enum_variant(),
|
||||
substs,
|
||||
"struct field",
|
||||
|
@ -2581,13 +2609,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
|||
Some((variant, definitely_inhabited))
|
||||
});
|
||||
let Some(first_variant) = potential_variants.next() else {
|
||||
return Some(("enums with no inhabited variants have no valid value".to_string(), Some(span)));
|
||||
return Some(InitError::from("enums with no inhabited variants have no valid value").spanned(span));
|
||||
};
|
||||
// So we have at least one potentially inhabited variant. Might we have two?
|
||||
let Some(second_variant) = potential_variants.next() else {
|
||||
// There is only one potentially inhabited variant. So we can recursively check that variant!
|
||||
return variant_find_init_error(
|
||||
cx,
|
||||
ty,
|
||||
&first_variant.0,
|
||||
substs,
|
||||
"field of the only potentially inhabited enum variant",
|
||||
|
@ -2605,10 +2634,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
|||
.filter(|(_variant, definitely_inhabited)| *definitely_inhabited)
|
||||
.count();
|
||||
if definitely_inhabited > 1 {
|
||||
return Some((
|
||||
"enums with multiple inhabited variants have to be initialized to a variant".to_string(),
|
||||
Some(span),
|
||||
));
|
||||
return Some(InitError::from(
|
||||
"enums with multiple inhabited variants have to be initialized to a variant",
|
||||
).spanned(span));
|
||||
}
|
||||
}
|
||||
// We couldn't find anything wrong here.
|
||||
|
@ -2637,8 +2665,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
|||
// using zeroed or uninitialized memory.
|
||||
// We are extremely conservative with what we warn about.
|
||||
let conjured_ty = cx.typeck_results().expr_ty(expr);
|
||||
if let Some((msg, span)) =
|
||||
with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
|
||||
if let Some(mut err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
|
||||
{
|
||||
// FIXME(davidtwco): make translatable
|
||||
cx.struct_span_lint(
|
||||
|
@ -2664,10 +2691,17 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
|||
"help: use `MaybeUninit<T>` instead, \
|
||||
and only call `assume_init` after initialization is done",
|
||||
);
|
||||
if let Some(span) = span {
|
||||
lint.span_note(span, &msg);
|
||||
} else {
|
||||
lint.note(&msg);
|
||||
loop {
|
||||
if let Some(span) = err.span {
|
||||
lint.span_note(span, &err.message);
|
||||
} else {
|
||||
lint.note(&err.message);
|
||||
}
|
||||
if let Some(e) = err.nested {
|
||||
err = *e;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
lint
|
||||
},
|
||||
|
|
|
@ -245,6 +245,18 @@ impl ScalarInt {
|
|||
self.to_bits(size)
|
||||
}
|
||||
|
||||
// Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
|
||||
// in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size`
|
||||
// value of the `ScalarInt` in that case.
|
||||
#[inline]
|
||||
pub fn try_to_bool(self) -> Result<bool, Size> {
|
||||
match self.try_to_u8()? {
|
||||
0 => Ok(false),
|
||||
1 => Ok(true),
|
||||
_ => Err(self.size()),
|
||||
}
|
||||
}
|
||||
|
||||
// Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
|
||||
// in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in
|
||||
// that case.
|
||||
|
|
|
@ -34,7 +34,7 @@ impl CrateNum {
|
|||
|
||||
impl fmt::Display for CrateNum {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.private, f)
|
||||
fmt::Display::fmt(&self.as_u32(), f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ pub struct ExpnId {
|
|||
impl fmt::Debug for ExpnId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Generate crate_::{{expn_}}.
|
||||
write!(f, "{:?}::{{{{expn{}}}}}", self.krate, self.local_id.private)
|
||||
write!(f, "{:?}::{{{{expn{}}}}}", self.krate, self.local_id.as_u32())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -325,8 +325,8 @@ fn layout_of_uncached<'tcx>(
|
|||
|
||||
// Extract the number of elements from the layout of the array field:
|
||||
let FieldsShape::Array { count, .. } = cx.layout_of(f0_ty)?.layout.fields() else {
|
||||
return Err(LayoutError::Unknown(ty));
|
||||
};
|
||||
return Err(LayoutError::Unknown(ty));
|
||||
};
|
||||
|
||||
(*e_ty, *count, true)
|
||||
} else {
|
||||
|
@ -351,14 +351,14 @@ fn layout_of_uncached<'tcx>(
|
|||
// Compute the ABI of the element type:
|
||||
let e_ly = cx.layout_of(e_ty)?;
|
||||
let Abi::Scalar(e_abi) = e_ly.abi else {
|
||||
// This error isn't caught in typeck, e.g., if
|
||||
// the element type of the vector is generic.
|
||||
tcx.sess.fatal(&format!(
|
||||
"monomorphising SIMD type `{}` with a non-primitive-scalar \
|
||||
(integer/float/pointer) element type `{}`",
|
||||
ty, e_ty
|
||||
))
|
||||
};
|
||||
// This error isn't caught in typeck, e.g., if
|
||||
// the element type of the vector is generic.
|
||||
tcx.sess.fatal(&format!(
|
||||
"monomorphising SIMD type `{}` with a non-primitive-scalar \
|
||||
(integer/float/pointer) element type `{}`",
|
||||
ty, e_ty
|
||||
))
|
||||
};
|
||||
|
||||
// Compute the size and alignment of the vector:
|
||||
let size = e_ly.size.checked_mul(e_len, dl).ok_or(LayoutError::SizeOverflow(ty))?;
|
||||
|
@ -597,8 +597,8 @@ fn generator_layout<'tcx>(
|
|||
let subst_field = |ty: Ty<'tcx>| EarlyBinder(ty).subst(tcx, substs);
|
||||
|
||||
let Some(info) = tcx.generator_layout(def_id) else {
|
||||
return Err(LayoutError::Unknown(ty));
|
||||
};
|
||||
return Err(LayoutError::Unknown(ty));
|
||||
};
|
||||
let (ineligible_locals, assignments) = generator_saved_local_eligibility(&info);
|
||||
|
||||
// Build a prefix layout, including "promoting" all ineligible
|
||||
|
@ -701,8 +701,8 @@ fn generator_layout<'tcx>(
|
|||
variant.variants = Variants::Single { index };
|
||||
|
||||
let FieldsShape::Arbitrary { offsets, memory_index } = variant.fields else {
|
||||
bug!();
|
||||
};
|
||||
bug!();
|
||||
};
|
||||
|
||||
// Now, stitch the promoted and variant-only fields back together in
|
||||
// the order they are mentioned by our GeneratorLayout.
|
||||
|
|
|
@ -65,6 +65,17 @@ LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) };
|
|||
14 00 00 00 │ ....
|
||||
}
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-nonnull.rs:50:1
|
||||
|
|
||||
LL | const NULL_FAT_PTR: NonNull<dyn Send> = unsafe {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 8, align: 4) {
|
||||
00 00 00 00 ╾─alloc26─╼ │ ....╾──╼
|
||||
}
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
||||
|
|
|
@ -65,6 +65,17 @@ LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) };
|
|||
14 00 00 00 │ ....
|
||||
}
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-nonnull.rs:50:1
|
||||
|
|
||||
LL | const NULL_FAT_PTR: NonNull<dyn Send> = unsafe {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 16, align: 8) {
|
||||
00 00 00 00 00 00 00 00 ╾───────alloc26───────╼ │ ........╾──────╼
|
||||
}
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// stderr-per-bitwidth
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustc_attrs, ptr_metadata)]
|
||||
#![allow(invalid_value)] // make sure we cannot allow away the errors tested here
|
||||
|
||||
use std::mem;
|
||||
|
@ -47,4 +47,11 @@ struct RestrictedRange2(u32);
|
|||
const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) };
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
|
||||
const NULL_FAT_PTR: NonNull<dyn Send> = unsafe {
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
let x: &dyn Send = &42;
|
||||
let meta = std::ptr::metadata(x);
|
||||
mem::transmute((0_usize, meta))
|
||||
};
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -40,6 +40,11 @@ LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: in this struct field
|
||||
--> $DIR/validate_uninhabited_zsts.rs:16:22
|
||||
|
|
||||
LL | pub struct Empty(Void);
|
||||
| ^^^^
|
||||
note: enums with no inhabited variants have no valid value
|
||||
--> $DIR/validate_uninhabited_zsts.rs:13:5
|
||||
|
|
||||
|
|
|
@ -40,6 +40,11 @@ LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: in this struct field
|
||||
--> $DIR/validate_uninhabited_zsts.rs:16:22
|
||||
|
|
||||
LL | pub struct Empty(Void);
|
||||
| ^^^^
|
||||
note: enums with no inhabited variants have no valid value
|
||||
--> $DIR/validate_uninhabited_zsts.rs:13:5
|
||||
|
|
||||
|
|
15
src/test/ui/layout/valid_range_oob.rs
Normal file
15
src/test/ui/layout/valid_range_oob.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
// failure-status: 101
|
||||
// normalize-stderr-test "note: .*\n\n" -> ""
|
||||
// normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
|
||||
// rustc-env:RUST_BACKTRACE=0
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_layout_scalar_valid_range_end(257)]
|
||||
struct Foo(i8);
|
||||
|
||||
// Need to do in a constant, as runtime codegen
|
||||
// does not compute the layout of `Foo` in check builds.
|
||||
const FOO: Foo = unsafe { Foo(1) };
|
||||
|
||||
fn main() {}
|
6
src/test/ui/layout/valid_range_oob.stderr
Normal file
6
src/test/ui/layout/valid_range_oob.stderr
Normal file
|
@ -0,0 +1,6 @@
|
|||
error: internal compiler error: unexpected panic
|
||||
|
||||
query stack during panic:
|
||||
#0 [layout_of] computing layout of `Foo`
|
||||
#1 [eval_to_allocation_raw] const-evaluating + checking `FOO`
|
||||
end of query stack
|
|
@ -34,7 +34,8 @@ LL | let _val: Wrap<&'static T> = mem::zeroed();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: references must be non-null (in this struct field)
|
||||
= note: `Wrap<&T>` must be non-null
|
||||
note: because references must be non-null (in this struct field)
|
||||
--> $DIR/invalid_value.rs:17:18
|
||||
|
|
||||
LL | struct Wrap<T> { wrapped: T }
|
||||
|
@ -49,7 +50,8 @@ LL | let _val: Wrap<&'static T> = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: references must be non-null (in this struct field)
|
||||
= note: `Wrap<&T>` must be non-null
|
||||
note: because references must be non-null (in this struct field)
|
||||
--> $DIR/invalid_value.rs:17:18
|
||||
|
|
||||
LL | struct Wrap<T> { wrapped: T }
|
||||
|
@ -97,7 +99,7 @@ LL | let _val: (i32, !) = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
= note: integers must not be uninitialized
|
||||
= note: integers must be initialized
|
||||
|
||||
error: the type `Void` does not permit zero-initialization
|
||||
--> $DIR/invalid_value.rs:71:26
|
||||
|
@ -160,7 +162,8 @@ LL | let _val: Ref = mem::zeroed();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: references must be non-null (in this struct field)
|
||||
= note: `Ref` must be non-null
|
||||
note: because references must be non-null (in this struct field)
|
||||
--> $DIR/invalid_value.rs:14:12
|
||||
|
|
||||
LL | struct Ref(&'static i32);
|
||||
|
@ -175,7 +178,8 @@ LL | let _val: Ref = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: references must be non-null (in this struct field)
|
||||
= note: `Ref` must be non-null
|
||||
note: because references must be non-null (in this struct field)
|
||||
--> $DIR/invalid_value.rs:14:12
|
||||
|
|
||||
LL | struct Ref(&'static i32);
|
||||
|
@ -212,7 +216,8 @@ LL | let _val: Wrap<fn()> = mem::zeroed();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: function pointers must be non-null (in this struct field)
|
||||
= note: `Wrap<fn()>` must be non-null
|
||||
note: because function pointers must be non-null (in this struct field)
|
||||
--> $DIR/invalid_value.rs:17:18
|
||||
|
|
||||
LL | struct Wrap<T> { wrapped: T }
|
||||
|
@ -227,7 +232,8 @@ LL | let _val: Wrap<fn()> = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: function pointers must be non-null (in this struct field)
|
||||
= note: `Wrap<fn()>` must be non-null
|
||||
note: because function pointers must be non-null (in this struct field)
|
||||
--> $DIR/invalid_value.rs:17:18
|
||||
|
|
||||
LL | struct Wrap<T> { wrapped: T }
|
||||
|
@ -242,7 +248,8 @@ LL | let _val: WrapEnum<fn()> = mem::zeroed();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: function pointers must be non-null (in this field of the only potentially inhabited enum variant)
|
||||
= note: `WrapEnum<fn()>` must be non-null
|
||||
note: because function pointers must be non-null (in this field of the only potentially inhabited enum variant)
|
||||
--> $DIR/invalid_value.rs:18:28
|
||||
|
|
||||
LL | enum WrapEnum<T> { Wrapped(T) }
|
||||
|
@ -257,7 +264,8 @@ LL | let _val: WrapEnum<fn()> = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: function pointers must be non-null (in this field of the only potentially inhabited enum variant)
|
||||
= note: `WrapEnum<fn()>` must be non-null
|
||||
note: because function pointers must be non-null (in this field of the only potentially inhabited enum variant)
|
||||
--> $DIR/invalid_value.rs:18:28
|
||||
|
|
||||
LL | enum WrapEnum<T> { Wrapped(T) }
|
||||
|
@ -272,7 +280,12 @@ LL | let _val: Wrap<(RefPair, i32)> = mem::zeroed();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: references must be non-null (in this struct field)
|
||||
note: `RefPair` must be non-null (in this struct field)
|
||||
--> $DIR/invalid_value.rs:17:18
|
||||
|
|
||||
LL | struct Wrap<T> { wrapped: T }
|
||||
| ^^^^^^^^^^
|
||||
note: because references must be non-null (in this struct field)
|
||||
--> $DIR/invalid_value.rs:15:16
|
||||
|
|
||||
LL | struct RefPair((&'static i32, i32));
|
||||
|
@ -287,7 +300,12 @@ LL | let _val: Wrap<(RefPair, i32)> = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: references must be non-null (in this struct field)
|
||||
note: `RefPair` must be non-null (in this struct field)
|
||||
--> $DIR/invalid_value.rs:17:18
|
||||
|
|
||||
LL | struct Wrap<T> { wrapped: T }
|
||||
| ^^^^^^^^^^
|
||||
note: because references must be non-null (in this struct field)
|
||||
--> $DIR/invalid_value.rs:15:16
|
||||
|
|
||||
LL | struct RefPair((&'static i32, i32));
|
||||
|
@ -314,6 +332,7 @@ LL | let _val: NonNull<i32> = mem::uninitialized();
|
|||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
= note: `std::ptr::NonNull<i32>` must be non-null
|
||||
= note: raw pointers must be initialized
|
||||
|
||||
error: the type `(NonZeroU32, i32)` does not permit zero-initialization
|
||||
--> $DIR/invalid_value.rs:95:39
|
||||
|
@ -336,6 +355,7 @@ LL | let _val: (NonZeroU32, i32) = mem::uninitialized();
|
|||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
= note: `std::num::NonZeroU32` must be non-null
|
||||
= note: integers must be initialized
|
||||
|
||||
error: the type `*const dyn Send` does not permit zero-initialization
|
||||
--> $DIR/invalid_value.rs:98:37
|
||||
|
@ -420,7 +440,8 @@ LL | let _val: OneFruitNonZero = mem::zeroed();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: `std::num::NonZeroU32` must be non-null (in this field of the only potentially inhabited enum variant)
|
||||
= note: `OneFruitNonZero` must be non-null
|
||||
note: because `std::num::NonZeroU32` must be non-null (in this field of the only potentially inhabited enum variant)
|
||||
--> $DIR/invalid_value.rs:39:12
|
||||
|
|
||||
LL | Banana(NonZeroU32),
|
||||
|
@ -435,11 +456,13 @@ LL | let _val: OneFruitNonZero = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
note: `std::num::NonZeroU32` must be non-null (in this field of the only potentially inhabited enum variant)
|
||||
= note: `OneFruitNonZero` must be non-null
|
||||
note: because `std::num::NonZeroU32` must be non-null (in this field of the only potentially inhabited enum variant)
|
||||
--> $DIR/invalid_value.rs:39:12
|
||||
|
|
||||
LL | Banana(NonZeroU32),
|
||||
| ^^^^^^^^^^
|
||||
= note: integers must be initialized
|
||||
|
||||
error: the type `bool` does not permit being left uninitialized
|
||||
--> $DIR/invalid_value.rs:112:26
|
||||
|
@ -461,6 +484,7 @@ LL | let _val: Wrap<char> = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
= note: `Wrap<char>` must be initialized inside its custom valid range
|
||||
note: characters must be a valid Unicode codepoint (in this struct field)
|
||||
--> $DIR/invalid_value.rs:17:18
|
||||
|
|
||||
|
@ -477,6 +501,11 @@ LL | let _val: NonBig = mem::uninitialized();
|
|||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
= note: `NonBig` must be initialized inside its custom valid range
|
||||
note: integers must be initialized (in this struct field)
|
||||
--> $DIR/invalid_value.rs:23:26
|
||||
|
|
||||
LL | pub(crate) struct NonBig(u64);
|
||||
| ^^^
|
||||
|
||||
error: the type `Fruit` does not permit being left uninitialized
|
||||
--> $DIR/invalid_value.rs:121:27
|
||||
|
@ -513,7 +542,7 @@ LL | let _val: i32 = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
= note: integers must not be uninitialized
|
||||
= note: integers must be initialized
|
||||
|
||||
error: the type `f32` does not permit being left uninitialized
|
||||
--> $DIR/invalid_value.rs:130:25
|
||||
|
@ -524,7 +553,7 @@ LL | let _val: f32 = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
= note: floats must not be uninitialized
|
||||
= note: floats must be initialized
|
||||
|
||||
error: the type `*const ()` does not permit being left uninitialized
|
||||
--> $DIR/invalid_value.rs:133:31
|
||||
|
@ -535,7 +564,7 @@ LL | let _val: *const () = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
= note: raw pointers must not be uninitialized
|
||||
= note: raw pointers must be initialized
|
||||
|
||||
error: the type `*const [()]` does not permit being left uninitialized
|
||||
--> $DIR/invalid_value.rs:136:33
|
||||
|
@ -546,7 +575,7 @@ LL | let _val: *const [()] = mem::uninitialized();
|
|||
| this code causes undefined behavior when executed
|
||||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
= note: raw pointers must not be uninitialized
|
||||
= note: raw pointers must be initialized
|
||||
|
||||
error: the type `WrapAroundRange` does not permit being left uninitialized
|
||||
--> $DIR/invalid_value.rs:139:37
|
||||
|
@ -558,6 +587,11 @@ LL | let _val: WrapAroundRange = mem::uninitialized();
|
|||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
= note: `WrapAroundRange` must be initialized inside its custom valid range
|
||||
note: integers must be initialized (in this struct field)
|
||||
--> $DIR/invalid_value.rs:49:35
|
||||
|
|
||||
LL | pub(crate) struct WrapAroundRange(u8);
|
||||
| ^^
|
||||
|
||||
error: the type `Result<i32, i32>` does not permit being left uninitialized
|
||||
--> $DIR/invalid_value.rs:144:38
|
||||
|
@ -628,6 +662,7 @@ LL | let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
|
|||
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
||||
= note: `std::ptr::NonNull<i32>` must be non-null
|
||||
= note: raw pointers must be initialized
|
||||
|
||||
error: the type `bool` does not permit being left uninitialized
|
||||
--> $DIR/invalid_value.rs:159:26
|
||||
|
|
Loading…
Add table
Reference in a new issue