Skip to content

Commit 7446ed3

Browse files
committed
Auto merge of #66059 - RalfJung:panic-on-non-zero, r=<try>
mem::zeroed/uninit: panic on types that do not permit zero-initialization r? @eddyb @oli-obk Cc #62825
2 parents a916ac2 + f57cfe4 commit 7446ed3

File tree

10 files changed

+307
-139
lines changed

10 files changed

+307
-139
lines changed

src/libcore/intrinsics.rs

+10
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,16 @@ extern "rust-intrinsic" {
700700
/// This will statically either panic, or do nothing.
701701
pub fn panic_if_uninhabited<T>();
702702

703+
/// A guard for unsafe functions that cannot ever be executed if `T` does not permit
704+
/// zero-initialization: This will statically either panic, or do nothing.
705+
#[cfg(not(bootstrap))]
706+
pub fn panic_if_zero_invalid<T>();
707+
708+
/// A guard for unsafe functions that cannot ever be executed if `T` has invalid
709+
/// bit patterns: This will statically either panic, or do nothing.
710+
#[cfg(not(bootstrap))]
711+
pub fn panic_if_any_invalid<T>();
712+
703713
/// Gets a reference to a static `Location` indicating where it was called.
704714
pub fn caller_location() -> &'static crate::panic::Location<'static>;
705715

src/libcore/mem/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,9 @@ pub const fn needs_drop<T>() -> bool {
495495
#[allow(deprecated)]
496496
#[rustc_diagnostic_item = "mem_zeroed"]
497497
pub unsafe fn zeroed<T>() -> T {
498+
#[cfg(not(bootstrap))]
499+
intrinsics::panic_if_zero_invalid::<T>();
500+
#[cfg(bootstrap)]
498501
intrinsics::panic_if_uninhabited::<T>();
499502
intrinsics::init()
500503
}
@@ -528,6 +531,9 @@ pub unsafe fn zeroed<T>() -> T {
528531
#[allow(deprecated)]
529532
#[rustc_diagnostic_item = "mem_uninitialized"]
530533
pub unsafe fn uninitialized<T>() -> T {
534+
#[cfg(not(bootstrap))]
535+
intrinsics::panic_if_any_invalid::<T>();
536+
#[cfg(bootstrap)]
531537
intrinsics::panic_if_uninhabited::<T>();
532538
intrinsics::uninit()
533539
}

src/librustc/ty/layout.rs

-30
Original file line numberDiff line numberDiff line change
@@ -1896,36 +1896,6 @@ impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> {
18961896
}
18971897
}
18981898

1899-
pub trait MaybeResult<T> {
1900-
type Error;
1901-
1902-
fn from(x: Result<T, Self::Error>) -> Self;
1903-
fn to_result(self) -> Result<T, Self::Error>;
1904-
}
1905-
1906-
impl<T> MaybeResult<T> for T {
1907-
type Error = !;
1908-
1909-
fn from(x: Result<T, Self::Error>) -> Self {
1910-
let Ok(x) = x;
1911-
x
1912-
}
1913-
fn to_result(self) -> Result<T, Self::Error> {
1914-
Ok(self)
1915-
}
1916-
}
1917-
1918-
impl<T, E> MaybeResult<T> for Result<T, E> {
1919-
type Error = E;
1920-
1921-
fn from(x: Result<T, Self::Error>) -> Self {
1922-
x
1923-
}
1924-
fn to_result(self) -> Result<T, Self::Error> {
1925-
self
1926-
}
1927-
}
1928-
19291899
pub type TyLayout<'tcx> = ::rustc_target::abi::TyLayout<'tcx, Ty<'tcx>>;
19301900

19311901
impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> {

src/librustc_codegen_ssa/mir/block.rs

+30-3
Original file line numberDiff line numberDiff line change
@@ -525,15 +525,42 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
525525
}
526526

527527
// Emit a panic or a no-op for `panic_if_uninhabited`.
528-
if intrinsic == Some("panic_if_uninhabited") {
528+
// These are intrinsics that compile to panics so that we can get a message
529+
// which mentions the offending type, even from a const context.
530+
#[derive(Debug, PartialEq)]
531+
enum PanicIntrinsic { IfUninhabited, IfZeroInvalid, IfAnyInvalid };
532+
let panic_intrinsic = intrinsic.and_then(|i| match i {
533+
// FIXME: Move to symbols instead of strings.
534+
"panic_if_uninhabited" => Some(PanicIntrinsic::IfUninhabited),
535+
"panic_if_zero_invalid" => Some(PanicIntrinsic::IfZeroInvalid),
536+
"panic_if_any_invalid" => Some(PanicIntrinsic::IfAnyInvalid),
537+
_ => None
538+
});
539+
if let Some(intrinsic) = panic_intrinsic {
540+
use PanicIntrinsic::*;
529541
let ty = instance.unwrap().substs.type_at(0);
530542
let layout = bx.layout_of(ty);
531-
if layout.abi.is_uninhabited() {
532-
let msg_str = format!("Attempted to instantiate uninhabited type {}", ty);
543+
let do_panic = match intrinsic {
544+
IfUninhabited => layout.abi.is_uninhabited(),
545+
IfZeroInvalid => // We unwrap as the error type is `!`.
546+
!layout.might_permit_raw_init(&bx, /*zero:*/ true).unwrap(),
547+
IfAnyInvalid => // We unwrap as the error type is `!`.
548+
!layout.might_permit_raw_init(&bx, /*zero:*/ false).unwrap(),
549+
};
550+
if do_panic {
551+
let msg_str = if layout.abi.is_uninhabited() {
552+
// Use this error even for the other intrinsics as it is more precise.
553+
format!("attempted to instantiate uninhabited type `{}`", ty)
554+
} else if intrinsic == IfZeroInvalid {
555+
format!("attempted to zero-initialize type `{}`, which is invalid", ty)
556+
} else {
557+
format!("attempted to leave type `{}` uninitialized, which is invalid", ty)
558+
};
533559
let msg = bx.const_str(Symbol::intern(&msg_str));
534560
let location = self.get_caller_location(&mut bx, span).immediate();
535561

536562
// Obtain the panic entry point.
563+
// FIXME: dedup this with `codegen_assert_terminator` above.
537564
let def_id =
538565
common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
539566
let instance = ty::Instance::mono(bx.tcx(), def_id);

src/librustc_index/vec.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,12 @@ macro_rules! newtype_index {
191191
impl Idx for $type {
192192
#[inline]
193193
fn new(value: usize) -> Self {
194-
Self::from(value)
194+
Self::from_usize(value)
195195
}
196196

197197
#[inline]
198198
fn index(self) -> usize {
199-
usize::from(self)
199+
self.as_usize()
200200
}
201201
}
202202

@@ -409,7 +409,7 @@ macro_rules! newtype_index {
409409
(@decodable $type:ident) => (
410410
impl ::rustc_serialize::Decodable for $type {
411411
fn decode<D: ::rustc_serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
412-
d.read_u32().map(Self::from)
412+
d.read_u32().map(Self::from_u32)
413413
}
414414
}
415415
);

src/librustc_target/abi/mod.rs

+127
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,7 @@ impl<'a, Ty> Deref for TyLayout<'a, Ty> {
919919
}
920920
}
921921

922+
/// Trait for context types that can compute layouts of things.
922923
pub trait LayoutOf {
923924
type Ty;
924925
type TyLayout;
@@ -929,6 +930,39 @@ pub trait LayoutOf {
929930
}
930931
}
931932

933+
/// The `TyLayout` above will always be a `MaybeResult<TyLayout<'_, Self>>`.
934+
/// We can't add the bound due to the lifetime, but this trait is still useful when
935+
/// writing code that's generic over the `LayoutOf` impl.
936+
pub trait MaybeResult<T> {
937+
type Error;
938+
939+
fn from(x: Result<T, Self::Error>) -> Self;
940+
fn to_result(self) -> Result<T, Self::Error>;
941+
}
942+
943+
impl<T> MaybeResult<T> for T {
944+
type Error = !;
945+
946+
fn from(x: Result<T, Self::Error>) -> Self {
947+
let Ok(x) = x;
948+
x
949+
}
950+
fn to_result(self) -> Result<T, Self::Error> {
951+
Ok(self)
952+
}
953+
}
954+
955+
impl<T, E> MaybeResult<T> for Result<T, E> {
956+
type Error = E;
957+
958+
fn from(x: Result<T, Self::Error>) -> Self {
959+
x
960+
}
961+
fn to_result(self) -> Result<T, Self::Error> {
962+
self
963+
}
964+
}
965+
932966
#[derive(Copy, Clone, PartialEq, Eq)]
933967
pub enum PointerKind {
934968
/// Most general case, we know no restrictions to tell LLVM.
@@ -969,13 +1003,17 @@ impl<'a, Ty> TyLayout<'a, Ty> {
9691003
{
9701004
Ty::for_variant(self, cx, variant_index)
9711005
}
1006+
1007+
/// Callers might want to use `C: LayoutOf<Ty=Ty, TyLayout: MaybeResult<Self>>`
1008+
/// to allow recursion (see `might_permit_zero_init` below for an example).
9721009
pub fn field<C>(self, cx: &C, i: usize) -> C::TyLayout
9731010
where
9741011
Ty: TyLayoutMethods<'a, C>,
9751012
C: LayoutOf<Ty = Ty>,
9761013
{
9771014
Ty::field(self, cx, i)
9781015
}
1016+
9791017
pub fn pointee_info_at<C>(self, cx: &C, offset: Size) -> Option<PointeeInfo>
9801018
where
9811019
Ty: TyLayoutMethods<'a, C>,
@@ -999,4 +1037,93 @@ impl<'a, Ty> TyLayout<'a, Ty> {
9991037
Abi::Aggregate { sized } => sized && self.size.bytes() == 0,
10001038
}
10011039
}
1040+
1041+
/// Determines if this type permits "raw" initialization by just transmuting some
1042+
/// memory into an instance of `T`.
1043+
/// `zero` indicates if the memory is zero-initialized, or alternatively
1044+
/// left entirely uninitialized.
1045+
/// This is conservative: in doubt, it will answer `true`.
1046+
///
1047+
/// FIXME: Once we removed all the conservatism, we could alternatively
1048+
/// create an all-0/all-undef constant and run the vonst value validator to see if
1049+
/// this is a valid value for the given type.
1050+
pub fn might_permit_raw_init<C, E>(
1051+
self,
1052+
cx: &C,
1053+
zero: bool,
1054+
) -> Result<bool, E>
1055+
where
1056+
Self: Copy,
1057+
Ty: TyLayoutMethods<'a, C>,
1058+
C: LayoutOf<Ty = Ty, TyLayout: MaybeResult<Self, Error = E>> + HasDataLayout
1059+
{
1060+
let scalar_allows_raw_init = move |s: &Scalar| -> bool {
1061+
if zero {
1062+
let range = &s.valid_range;
1063+
// The range must contain 0.
1064+
range.contains(&0) ||
1065+
(*range.start() > *range.end()) // wrap-around allows 0
1066+
} else {
1067+
// The range must include all values. `valid_range_exclusive` handles
1068+
// the wrap-around using target arithmetic; with wrap-around then the full
1069+
// range is one where `start == end`.
1070+
let range = s.valid_range_exclusive(cx);
1071+
range.start == range.end
1072+
}
1073+
};
1074+
1075+
// Abi is the most informative here.
1076+
let res = match &self.abi {
1077+
Abi::Uninhabited => false, // definitely UB
1078+
Abi::Scalar(s) => scalar_allows_raw_init(s),
1079+
Abi::ScalarPair(s1, s2) =>
1080+
scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2),
1081+
Abi::Vector { element: s, count } =>
1082+
*count == 0 || scalar_allows_raw_init(s),
1083+
Abi::Aggregate { .. } => {
1084+
match self.variants {
1085+
Variants::Multiple { .. } =>
1086+
if zero {
1087+
// FIXME(#66151):
1088+
// could we identify the variant with discriminant 0, check that?
1089+
true
1090+
} else {
1091+
// FIXME(#66151): This needs to have some sort of discriminant,
1092+
// which cannot be undef. But for now we are conservative.
1093+
true
1094+
},
1095+
Variants::Single { .. } => {
1096+
// For aggregates, recurse.
1097+
match self.fields {
1098+
FieldPlacement::Union(..) => true, // An all-0 unit is fine.
1099+
FieldPlacement::Array { .. } =>
1100+
// FIXME(#66151): The widely use smallvec 0.6 creates uninit arrays
1101+
// with any element type, so let us not (yet) complain about that.
1102+
/* count == 0 ||
1103+
self.field(cx, 0).to_result()?.might_permit_raw_init(cx, zero)? */
1104+
true,
1105+
FieldPlacement::Arbitrary { .. } => {
1106+
// FIXME(#66151) cargo depends on sized-chunks 0.3.0 which
1107+
// has some illegal zero-initialization, so let us not (yet)
1108+
// complain about aggregates either.
1109+
/* let mut res = true;
1110+
// Check that all fields accept zero-init.
1111+
for idx in 0..offsets.len() {
1112+
let field = self.field(cx, idx).to_result()?;
1113+
if !field.might_permit_raw_init(cx, zero)? {
1114+
res = false;
1115+
break;
1116+
}
1117+
}
1118+
res */
1119+
true
1120+
}
1121+
}
1122+
}
1123+
}
1124+
}
1125+
};
1126+
trace!("might_permit_raw_init({:?}, zero={}) = {}", self.details, zero, res);
1127+
Ok(res)
1128+
}
10021129
}

src/librustc_target/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#![feature(bool_to_option)]
1313
#![feature(nll)]
1414
#![feature(slice_patterns)]
15+
#![feature(never_type)]
16+
#![feature(associated_type_bounds)]
17+
#![feature(exhaustive_patterns)]
1518

1619
#[macro_use]
1720
extern crate log;

src/librustc_typeck/check/intrinsic.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
149149
),
150150
"rustc_peek" => (1, vec![param(0)], param(0)),
151151
"caller_location" => (0, vec![], tcx.caller_location_ty()),
152-
"panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()),
152+
"panic_if_uninhabited" |
153+
"panic_if_zero_invalid" |
154+
"panic_if_any_invalid" =>
155+
(1, Vec::new(), tcx.mk_unit()),
153156
"init" => (1, Vec::new(), param(0)),
154157
"uninit" => (1, Vec::new(), param(0)),
155158
"forget" => (1, vec![param(0)], tcx.mk_unit()),

0 commit comments

Comments
 (0)