mir-interpret now treats unions as non-immediate, even if they have scalar layout, allowing partially initializing them

This commit is contained in:
Oli Scherer 2022-03-03 12:02:52 +00:00
parent d32ce37a17
commit 09b291f0b2
6 changed files with 115 additions and 47 deletions

View File

@ -15,7 +15,7 @@
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, subst::Subst, TyCtxt};
use rustc_span::source_map::Span;
use rustc_target::abi::Abi;
use rustc_target::abi::{self, Abi};
use std::borrow::Cow;
use std::convert::TryInto;
@ -119,7 +119,7 @@ pub(super) fn op_to_const<'tcx>(
// the usual cases of extracting e.g. a `usize`, without there being a real use case for the
// `Undef` situation.
let try_as_immediate = match op.layout.abi {
Abi::Scalar(..) => true,
Abi::Scalar(abi::Scalar::Initialized { .. }) => true,
Abi::ScalarPair(..) => match op.layout.ty.kind() {
ty::Ref(_, inner, _) => match *inner.kind() {
ty::Slice(elem) => elem == ecx.tcx.types.u8,

View File

@ -10,7 +10,7 @@
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer};
use rustc_middle::ty::{ConstInt, DelaySpanBugEmitted, Ty};
use rustc_middle::{mir, ty};
use rustc_target::abi::{Abi, HasDataLayout, Size, TagEncoding};
use rustc_target::abi::{self, Abi, HasDataLayout, Size, TagEncoding};
use rustc_target::abi::{VariantIdx, Variants};
use super::{
@ -268,14 +268,18 @@ fn try_read_immediate_from_mplace(
// It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
// However, `MaybeUninit<u64>` is considered a `Scalar` as far as its layout is concerned --
// and yet cannot be represented by an interpreter `Scalar`, since we have to handle the
// case where some of the bytes are initialized and others are not. So, we only permit
// reads from `Scalar`s and `ScalarPair`s that cannot be uninitialized.
// case where some of the bytes are initialized and others are not. So, we need an extra
// check that walks over the type of `mplace` to make sure it is truly correct to treat this
// like a `Scalar` (or `ScalarPair`).
match mplace.layout.abi {
Abi::Scalar(..) => {
Abi::Scalar(abi::Scalar::Initialized { .. }) => {
let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?;
Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }))
}
Abi::ScalarPair(a, b) => {
Abi::ScalarPair(
abi::Scalar::Initialized { value: a, .. },
abi::Scalar::Initialized { value: b, .. },
) => {
// We checked `ptr_align` above, so all fields will have the alignment they need.
// We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
// which `ptr.offset(b_offset)` cannot possibly fail to satisfy.

View File

@ -3,56 +3,70 @@
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/invalid_constant.rs:15:11: 15:11
let _1: main::InvalidChar; // in scope 0 at $DIR/invalid_constant.rs:21:9: 21:22
let mut _3: main::InvalidTag; // in scope 0 at $DIR/invalid_constant.rs:28:25: 28:46
let mut _5: main::NoVariants; // in scope 0 at $DIR/invalid_constant.rs:35:35: 35:56
let _1: char; // in scope 0 at $DIR/invalid_constant.rs:21:9: 21:22
let mut _2: main::InvalidChar; // in scope 0 at $DIR/invalid_constant.rs:21:34: 21:63
let mut _4: E; // in scope 0 at $DIR/invalid_constant.rs:28:25: 28:59
let mut _5: main::InvalidTag; // in scope 0 at $DIR/invalid_constant.rs:28:34: 28:55
let mut _7: Empty; // in scope 0 at $DIR/invalid_constant.rs:35:35: 35:73
let mut _8: main::NoVariants; // in scope 0 at $DIR/invalid_constant.rs:35:44: 35:65
scope 1 {
debug _invalid_char => _1; // in scope 1 at $DIR/invalid_constant.rs:21:9: 21:22
let _2: [main::InvalidTag; 1]; // in scope 1 at $DIR/invalid_constant.rs:28:9: 28:21
scope 2 {
debug _invalid_tag => _2; // in scope 2 at $DIR/invalid_constant.rs:28:9: 28:21
let _4: [main::NoVariants; 1]; // in scope 2 at $DIR/invalid_constant.rs:35:9: 35:31
scope 3 {
debug _enum_without_variants => _4; // in scope 3 at $DIR/invalid_constant.rs:35:9: 35:31
let _6: main::Str<"<22><><EFBFBD>">; // in scope 3 at $DIR/invalid_constant.rs:39:9: 39:22
scope 4 {
debug _non_utf8_str => _6; // in scope 4 at $DIR/invalid_constant.rs:39:9: 39:22
let _3: [E; 1]; // in scope 1 at $DIR/invalid_constant.rs:28:9: 28:21
scope 3 {
debug _invalid_tag => _3; // in scope 3 at $DIR/invalid_constant.rs:28:9: 28:21
let _6: [Empty; 1]; // in scope 3 at $DIR/invalid_constant.rs:35:9: 35:31
scope 5 {
debug _enum_without_variants => _6; // in scope 5 at $DIR/invalid_constant.rs:35:9: 35:31
let _9: main::Str<"<22><><EFBFBD>">; // in scope 5 at $DIR/invalid_constant.rs:39:9: 39:22
scope 7 {
debug _non_utf8_str => _9; // in scope 7 at $DIR/invalid_constant.rs:39:9: 39:22
}
}
scope 6 {
}
}
scope 4 {
}
}
scope 2 {
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/invalid_constant.rs:21:9: 21:22
- _1 = const { InvalidChar { int: 0x110001 } }; // scope 0 at $DIR/invalid_constant.rs:21:25: 21:64
+ _1 = const InvalidChar { int: 1114113_u32, chr: {transmute(0x00110001): char} }; // scope 0 at $DIR/invalid_constant.rs:21:25: 21:64
// mir::Constant
// + span: $DIR/invalid_constant.rs:21:25: 21:64
- // + literal: Const { ty: InvalidChar, val: Unevaluated(main::{constant#0}, [main::InvalidChar], None) }
+ // + literal: Const { ty: InvalidChar, val: Value(Scalar(0x00110001)) }
StorageLive(_2); // scope 1 at $DIR/invalid_constant.rs:28:9: 28:21
StorageLive(_3); // scope 1 at $DIR/invalid_constant.rs:28:25: 28:46
(_3.0: u32) = const 4_u32; // scope 1 at $DIR/invalid_constant.rs:28:25: 28:46
- _2 = [move _3]; // scope 1 at $DIR/invalid_constant.rs:28:24: 28:47
+ _2 = [const InvalidTag { int: 4_u32, e: Scalar(0x00000004): E }]; // scope 1 at $DIR/invalid_constant.rs:28:24: 28:47
StorageLive(_2); // scope 2 at $DIR/invalid_constant.rs:21:34: 21:63
(_2.0: u32) = const 1114113_u32; // scope 2 at $DIR/invalid_constant.rs:21:34: 21:63
- _1 = (_2.1: char); // scope 2 at $DIR/invalid_constant.rs:21:34: 21:67
+ _1 = const {transmute(0x00110001): char}; // scope 2 at $DIR/invalid_constant.rs:21:34: 21:67
StorageDead(_2); // scope 0 at $DIR/invalid_constant.rs:21:69: 21:70
StorageLive(_3); // scope 1 at $DIR/invalid_constant.rs:28:9: 28:21
StorageLive(_4); // scope 1 at $DIR/invalid_constant.rs:28:25: 28:59
StorageLive(_5); // scope 4 at $DIR/invalid_constant.rs:28:34: 28:55
(_5.0: u32) = const 4_u32; // scope 4 at $DIR/invalid_constant.rs:28:34: 28:55
- _4 = (_5.1: E); // scope 4 at $DIR/invalid_constant.rs:28:34: 28:57
- _3 = [move _4]; // scope 1 at $DIR/invalid_constant.rs:28:24: 28:60
+ _4 = const Scalar(0x00000004): E; // scope 4 at $DIR/invalid_constant.rs:28:34: 28:57
+ // mir::Constant
+ // + span: $DIR/invalid_constant.rs:28:24: 28:47
+ // + literal: Const { ty: InvalidTag, val: Value(Scalar(0x00000004)) }
StorageDead(_3); // scope 1 at $DIR/invalid_constant.rs:28:46: 28:47
StorageLive(_4); // scope 2 at $DIR/invalid_constant.rs:35:9: 35:31
StorageLive(_5); // scope 2 at $DIR/invalid_constant.rs:35:35: 35:56
(_5.0: u32) = const 0_u32; // scope 2 at $DIR/invalid_constant.rs:35:35: 35:56
- _4 = [move _5]; // scope 2 at $DIR/invalid_constant.rs:35:34: 35:57
+ _4 = [const NoVariants { int: 0_u32, empty: Scalar(<ZST>): Empty }]; // scope 2 at $DIR/invalid_constant.rs:35:34: 35:57
+ // + span: $DIR/invalid_constant.rs:28:34: 28:57
+ // + literal: Const { ty: E, val: Value(Scalar(0x00000004)) }
+ _3 = [const Scalar(0x00000004): E]; // scope 1 at $DIR/invalid_constant.rs:28:24: 28:60
+ // mir::Constant
+ // + span: $DIR/invalid_constant.rs:35:34: 35:57
+ // + literal: Const { ty: NoVariants, val: Value(Scalar(0x00000000)) }
StorageDead(_5); // scope 2 at $DIR/invalid_constant.rs:35:56: 35:57
StorageLive(_6); // scope 3 at $DIR/invalid_constant.rs:39:9: 39:22
+ // + span: $DIR/invalid_constant.rs:28:24: 28:60
+ // + literal: Const { ty: E, val: Value(Scalar(0x00000004)) }
StorageDead(_4); // scope 1 at $DIR/invalid_constant.rs:28:59: 28:60
StorageDead(_5); // scope 1 at $DIR/invalid_constant.rs:28:60: 28:61
StorageLive(_6); // scope 3 at $DIR/invalid_constant.rs:35:9: 35:31
StorageLive(_7); // scope 3 at $DIR/invalid_constant.rs:35:35: 35:73
StorageLive(_8); // scope 6 at $DIR/invalid_constant.rs:35:44: 35:65
(_8.0: u32) = const 0_u32; // scope 6 at $DIR/invalid_constant.rs:35:44: 35:65
nop; // scope 6 at $DIR/invalid_constant.rs:35:44: 35:71
nop; // scope 3 at $DIR/invalid_constant.rs:35:34: 35:74
StorageDead(_7); // scope 3 at $DIR/invalid_constant.rs:35:73: 35:74
StorageDead(_8); // scope 3 at $DIR/invalid_constant.rs:35:74: 35:75
StorageLive(_9); // scope 5 at $DIR/invalid_constant.rs:39:9: 39:22
nop; // scope 0 at $DIR/invalid_constant.rs:15:11: 42:2
StorageDead(_9); // scope 5 at $DIR/invalid_constant.rs:42:1: 42:2
StorageDead(_6); // scope 3 at $DIR/invalid_constant.rs:42:1: 42:2
StorageDead(_4); // scope 2 at $DIR/invalid_constant.rs:42:1: 42:2
StorageDead(_2); // scope 1 at $DIR/invalid_constant.rs:42:1: 42:2
StorageDead(_3); // scope 1 at $DIR/invalid_constant.rs:42:1: 42:2
StorageDead(_1); // scope 0 at $DIR/invalid_constant.rs:42:1: 42:2
return; // scope 0 at $DIR/invalid_constant.rs:42:2: 42:2
}

View File

@ -18,21 +18,21 @@ union InvalidChar {
int: u32,
chr: char,
}
let _invalid_char = const { InvalidChar { int: 0x110001 } };
let _invalid_char = unsafe { InvalidChar { int: 0x110001 }.chr };
// An enum with an invalid tag. Regression test for #93688.
union InvalidTag {
int: u32,
e: E,
}
let _invalid_tag = [InvalidTag { int: 4 }];
let _invalid_tag = [unsafe { InvalidTag { int: 4 }.e }];
// An enum without variants. Regression test for #94073.
union NoVariants {
int: u32,
empty: Empty,
}
let _enum_without_variants = [NoVariants { int: 0 }];
let _enum_without_variants = [unsafe { NoVariants { int: 0 }.empty }];
// A non-UTF-8 string slice. Regression test for #75763 and #78520.
struct Str<const S: &'static str>;

View File

@ -0,0 +1,34 @@
// run-pass
#![feature(const_ptr_write)]
#![feature(const_mut_refs)]
// Or, equivalently: `MaybeUninit`.
pub union BagOfBits<T: Copy> {
uninit: (),
_storage: T,
}
pub const fn make_1u8_bag<T: Copy>() -> BagOfBits<T> {
assert!(core::mem::size_of::<T>() >= 1);
let mut bag = BagOfBits { uninit: () };
unsafe { (&mut bag as *mut _ as *mut u8).write(1); };
bag
}
pub fn check_bag<T: Copy>(bag: &BagOfBits<T>) {
let val = unsafe { (bag as *const _ as *const u8).read() };
assert_eq!(val, 1);
}
fn main() {
check_bag(&make_1u8_bag::<[usize; 1]>()); // Fine
check_bag(&make_1u8_bag::<usize>()); // Fine
const CONST_ARRAY_BAG: BagOfBits<[usize; 1]> = make_1u8_bag();
check_bag(&CONST_ARRAY_BAG); // Fine.
const CONST_USIZE_BAG: BagOfBits<usize> = make_1u8_bag();
// Used to panic since CTFE would make the entire `BagOfBits<usize>` uninit
check_bag(&CONST_USIZE_BAG);
}

View File

@ -0,0 +1,16 @@
// check-pass
#![feature(const_swap)]
#![feature(const_mut_refs)]
#[repr(C)]
struct Demo(u64, bool, u64, u32, u64, u64, u64);
const C: (Demo, Demo) = {
let mut x = Demo(1, true, 3, 4, 5, 6, 7);
let mut y = Demo(10, false, 12, 13, 14, 15, 16);
std::mem::swap(&mut x, &mut y);
(x, y)
};
fn main() {}