From 09b291f0b256e7955e3dc8819404d8cf61cad34a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 3 Mar 2022 12:02:52 +0000 Subject: [PATCH] mir-interpret now treats unions as non-immediate, even if they have scalar layout, allowing partially initializing them --- .../src/const_eval/eval_queries.rs | 4 +- .../rustc_const_eval/src/interpret/operand.rs | 14 +-- .../invalid_constant.main.ConstProp.diff | 88 +++++++++++-------- .../mir-opt/const_prop/invalid_constant.rs | 6 +- src/test/ui/consts/issue-69488.rs | 34 +++++++ src/test/ui/consts/issue-94371.rs | 16 ++++ 6 files changed, 115 insertions(+), 47 deletions(-) create mode 100644 src/test/ui/consts/issue-69488.rs create mode 100644 src/test/ui/consts/issue-94371.rs diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 946546263ea..ed23df8289d 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -15,7 +15,7 @@ use rustc_middle::ty::layout::LayoutOf; 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, diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 73009052e04..1bfd10408bf 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; 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 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point. // However, `MaybeUninit` 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. diff --git a/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff b/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff index f2a1b9d69e1..cf6d8a52a76 100644 --- a/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff @@ -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<"���">; // 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<"���">; // 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(): 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 } diff --git a/src/test/mir-opt/const_prop/invalid_constant.rs b/src/test/mir-opt/const_prop/invalid_constant.rs index 492ef404916..0337a7ca851 100644 --- a/src/test/mir-opt/const_prop/invalid_constant.rs +++ b/src/test/mir-opt/const_prop/invalid_constant.rs @@ -18,21 +18,21 @@ fn main() { 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; diff --git a/src/test/ui/consts/issue-69488.rs b/src/test/ui/consts/issue-69488.rs new file mode 100644 index 00000000000..46546eada7a --- /dev/null +++ b/src/test/ui/consts/issue-69488.rs @@ -0,0 +1,34 @@ +// run-pass + +#![feature(const_ptr_write)] +#![feature(const_mut_refs)] + +// Or, equivalently: `MaybeUninit`. +pub union BagOfBits { + uninit: (), + _storage: T, +} + +pub const fn make_1u8_bag() -> BagOfBits { + assert!(core::mem::size_of::() >= 1); + let mut bag = BagOfBits { uninit: () }; + unsafe { (&mut bag as *mut _ as *mut u8).write(1); }; + bag +} + +pub fn check_bag(bag: &BagOfBits) { + 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::()); // Fine + + const CONST_ARRAY_BAG: BagOfBits<[usize; 1]> = make_1u8_bag(); + check_bag(&CONST_ARRAY_BAG); // Fine. + const CONST_USIZE_BAG: BagOfBits = make_1u8_bag(); + + // Used to panic since CTFE would make the entire `BagOfBits` uninit + check_bag(&CONST_USIZE_BAG); +} diff --git a/src/test/ui/consts/issue-94371.rs b/src/test/ui/consts/issue-94371.rs new file mode 100644 index 00000000000..de9ff730b66 --- /dev/null +++ b/src/test/ui/consts/issue-94371.rs @@ -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() {}