Skip to content

Commit 267085f

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 98c173a + e1f0b8f commit 267085f

File tree

10 files changed

+295
-139
lines changed

10 files changed

+295
-139
lines changed

src/libcore/intrinsics.rs

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

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

src/libcore/mem/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,9 @@ pub const fn needs_drop<T>() -> bool {
469469
#[allow(deprecated_in_future)]
470470
#[allow(deprecated)]
471471
pub unsafe fn zeroed<T>() -> T {
472+
#[cfg(not(bootstrap))]
473+
intrinsics::panic_if_zero_invalid::<T>();
474+
#[cfg(bootstrap)]
472475
intrinsics::panic_if_uninhabited::<T>();
473476
intrinsics::init()
474477
}
@@ -497,6 +500,9 @@ pub unsafe fn zeroed<T>() -> T {
497500
#[allow(deprecated_in_future)]
498501
#[allow(deprecated)]
499502
pub unsafe fn uninitialized<T>() -> T {
503+
#[cfg(not(bootstrap))]
504+
intrinsics::panic_if_any_invalid::<T>();
505+
#[cfg(bootstrap)]
500506
intrinsics::panic_if_uninhabited::<T>();
501507
intrinsics::uninit()
502508
}

src/librustc/ty/layout.rs

-30
Original file line numberDiff line numberDiff line change
@@ -1910,36 +1910,6 @@ impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> {
19101910
}
19111911
}
19121912

1913-
pub trait MaybeResult<T> {
1914-
type Error;
1915-
1916-
fn from(x: Result<T, Self::Error>) -> Self;
1917-
fn to_result(self) -> Result<T, Self::Error>;
1918-
}
1919-
1920-
impl<T> MaybeResult<T> for T {
1921-
type Error = !;
1922-
1923-
fn from(x: Result<T, Self::Error>) -> Self {
1924-
let Ok(x) = x;
1925-
x
1926-
}
1927-
fn to_result(self) -> Result<T, Self::Error> {
1928-
Ok(self)
1929-
}
1930-
}
1931-
1932-
impl<T, E> MaybeResult<T> for Result<T, E> {
1933-
type Error = E;
1934-
1935-
fn from(x: Result<T, Self::Error>) -> Self {
1936-
x
1937-
}
1938-
fn to_result(self) -> Result<T, Self::Error> {
1939-
self
1940-
}
1941-
}
1942-
19431913
pub type TyLayout<'tcx> = ::rustc_target::abi::TyLayout<'tcx, Ty<'tcx>>;
19441914

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

src/librustc_codegen_ssa/mir/block.rs

+28-3
Original file line numberDiff line numberDiff line change
@@ -529,11 +529,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
529529
};
530530

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

src/librustc_index/vec.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,12 @@ macro_rules! newtype_index {
182182
impl Idx for $type {
183183
#[inline]
184184
fn new(value: usize) -> Self {
185-
Self::from(value)
185+
Self::from_usize(value)
186186
}
187187

188188
#[inline]
189189
fn index(self) -> usize {
190-
usize::from(self)
190+
self.as_usize()
191191
}
192192
}
193193

@@ -400,7 +400,7 @@ macro_rules! newtype_index {
400400
(@decodable $type:ident) => (
401401
impl ::rustc_serialize::Decodable for $type {
402402
fn decode<D: ::rustc_serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
403-
d.read_u32().map(Self::from)
403+
d.read_u32().map(Self::from_u32)
404404
}
405405
}
406406
);

src/librustc_target/abi/mod.rs

+119
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,7 @@ impl<'a, Ty> Deref for TyLayout<'a, Ty> {
960960
}
961961
}
962962

963+
/// Trait for context types that can compute layouts of things.
963964
pub trait LayoutOf {
964965
type Ty;
965966
type TyLayout;
@@ -970,6 +971,39 @@ pub trait LayoutOf {
970971
}
971972
}
972973

974+
/// The `TyLayout` above will always be a `MaybeResult<TyLayout<'_, Self>>`.
975+
/// We can't add the bound due to the lifetime, but this trait is still useful when
976+
/// writing code that's generic over the `LayoutOf` impl.
977+
pub trait MaybeResult<T> {
978+
type Error;
979+
980+
fn from(x: Result<T, Self::Error>) -> Self;
981+
fn to_result(self) -> Result<T, Self::Error>;
982+
}
983+
984+
impl<T> MaybeResult<T> for T {
985+
type Error = !;
986+
987+
fn from(x: Result<T, Self::Error>) -> Self {
988+
let Ok(x) = x;
989+
x
990+
}
991+
fn to_result(self) -> Result<T, Self::Error> {
992+
Ok(self)
993+
}
994+
}
995+
996+
impl<T, E> MaybeResult<T> for Result<T, E> {
997+
type Error = E;
998+
999+
fn from(x: Result<T, Self::Error>) -> Self {
1000+
x
1001+
}
1002+
fn to_result(self) -> Result<T, Self::Error> {
1003+
self
1004+
}
1005+
}
1006+
9731007
#[derive(Copy, Clone, PartialEq, Eq)]
9741008
pub enum PointerKind {
9751009
/// Most general case, we know no restrictions to tell LLVM.
@@ -1011,10 +1045,14 @@ impl<'a, Ty> TyLayout<'a, Ty> {
10111045
where Ty: TyLayoutMethods<'a, C>, C: LayoutOf<Ty = Ty> {
10121046
Ty::for_variant(self, cx, variant_index)
10131047
}
1048+
1049+
/// Callers might want to use `C: LayoutOf<Ty=Ty, TyLayout: MaybeResult<Self>>`
1050+
/// to allow recursion (see `might_permit_zero_init` below for an example).
10141051
pub fn field<C>(self, cx: &C, i: usize) -> C::TyLayout
10151052
where Ty: TyLayoutMethods<'a, C>, C: LayoutOf<Ty = Ty> {
10161053
Ty::field(self, cx, i)
10171054
}
1055+
10181056
pub fn pointee_info_at<C>(self, cx: &C, offset: Size) -> Option<PointeeInfo>
10191057
where Ty: TyLayoutMethods<'a, C>, C: LayoutOf<Ty = Ty> {
10201058
Ty::pointee_info_at(self, cx, offset)
@@ -1037,4 +1075,85 @@ impl<'a, Ty> TyLayout<'a, Ty> {
10371075
Abi::Aggregate { sized } => sized && self.size.bytes() == 0
10381076
}
10391077
}
1078+
1079+
/// Determines if this type permits "raw" initialization by just transmuting some
1080+
/// memory into an instance of `T`.
1081+
/// `zero` indicates if the memory is zero-initialized, or alternatively
1082+
/// left entirely uninitialized.
1083+
/// This is conservative: in doubt, it will answer `true`.
1084+
pub fn might_permit_raw_init<C, E>(
1085+
self,
1086+
cx: &C,
1087+
zero: bool,
1088+
) -> Result<bool, E>
1089+
where
1090+
Self: Copy,
1091+
Ty: TyLayoutMethods<'a, C>,
1092+
C: LayoutOf<Ty = Ty, TyLayout: MaybeResult<Self, Error = E>> + HasDataLayout
1093+
{
1094+
let scalar_allows_raw_init = move |s: &Scalar| -> bool {
1095+
if zero {
1096+
let range = &s.valid_range;
1097+
// The range must contain 0.
1098+
range.contains(&0) ||
1099+
(*range.start() > *range.end()) // wrap-around allows 0
1100+
} else {
1101+
// The range must include all values. `valid_range_exclusive` handles
1102+
// the wrap-around using target arithmetic; with wrap-around then the full
1103+
// range is one where `start == end`.
1104+
let range = s.valid_range_exclusive(cx);
1105+
range.start == range.end
1106+
}
1107+
};
1108+
1109+
// Abi is the most informative here.
1110+
let res = match &self.abi {
1111+
Abi::Uninhabited => false, // definitely UB
1112+
Abi::Scalar(s) => scalar_allows_raw_init(s),
1113+
Abi::ScalarPair(s1, s2) =>
1114+
scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2),
1115+
Abi::Vector { element: s, count } =>
1116+
*count == 0 || scalar_allows_raw_init(s),
1117+
Abi::Aggregate { .. } => {
1118+
match self.variants {
1119+
Variants::Multiple { .. } =>
1120+
if zero {
1121+
// FIXME(#66151):
1122+
// could we identify the variant with discriminant 0, check that?
1123+
true
1124+
} else {
1125+
// FIXME(#66151): This needs to have some sort of discriminant,
1126+
// which cannot be undef. But for now we are conservative.
1127+
true
1128+
},
1129+
Variants::Single { .. } => {
1130+
// For aggregates, recurse.
1131+
match self.fields {
1132+
FieldPlacement::Union(..) => true, // An all-0 unit is fine.
1133+
FieldPlacement::Array { .. } =>
1134+
// FIXME(#66151): The widely use smallvec 0.6 creates uninit arrays
1135+
// with any element type, so let us not (yet) complain about that.
1136+
// count == 0 ||
1137+
// self.field(cx, 0).to_result()?.might_permit_raw_init(cx, zero)?
1138+
true,
1139+
FieldPlacement::Arbitrary { ref offsets, .. } => {
1140+
let mut res = true;
1141+
// Check that all fields accept zero-init.
1142+
for idx in 0..offsets.len() {
1143+
let field = self.field(cx, idx).to_result()?;
1144+
if !field.might_permit_raw_init(cx, zero)? {
1145+
res = false;
1146+
break;
1147+
}
1148+
}
1149+
res
1150+
}
1151+
}
1152+
}
1153+
}
1154+
}
1155+
};
1156+
trace!("might_permit_raw_init({:?}, zero={}) = {}", self.details, zero, res);
1157+
Ok(res)
1158+
}
10401159
}

src/librustc_target/lib.rs

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

1619
#[macro_use] extern crate log;
1720

src/librustc_typeck/check/intrinsic.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem) {
155155
.subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
156156
),
157157
),
158-
"panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()),
158+
"panic_if_uninhabited" |
159+
"panic_if_zero_invalid" |
160+
"panic_if_any_invalid" =>
161+
(1, Vec::new(), tcx.mk_unit()),
159162
"init" => (1, Vec::new(), param(0)),
160163
"uninit" => (1, Vec::new(), param(0)),
161164
"forget" => (1, vec![param(0)], tcx.mk_unit()),

0 commit comments

Comments
 (0)