Rollup merge of #109435 - oli-obk:🇨🇭🥚_copy_op, r=RalfJung

Detect uninhabited types early in const eval

r? `@RalfJung`

implements https://github.com/rust-lang/rust/pull/108442#discussion_r1143003840

this is a breaking change, as some UB during const eval is now detected instead of silently being ignored. Users can see this and other UB that may cause future breakage with `-Zextra-const-ub-checks` or just by running miri on their code, which sets that flag by default.
This commit is contained in:
Dylan DPC 2023-03-23 00:00:35 +05:30 committed by GitHub
commit eda88a30c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 38 additions and 38 deletions

View File

@ -2,7 +2,7 @@ use rustc_hir::def::DefKind;
use rustc_hir::{LangItem, CRATE_HIR_ID}; use rustc_hir::{LangItem, CRATE_HIR_ID};
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::mir::interpret::PointerArithmetic; use rustc_middle::mir::interpret::PointerArithmetic;
use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint::builtin::INVALID_ALIGNMENT; use rustc_session::lint::builtin::INVALID_ALIGNMENT;
use std::borrow::Borrow; use std::borrow::Borrow;
@ -335,8 +335,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
} }
#[inline(always)] #[inline(always)]
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool {
ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks || layout.abi.is_uninhabited()
} }
fn alignment_check_failed( fn alignment_check_failed(

View File

@ -8,6 +8,7 @@ use std::hash::Hash;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_target::abi::{Align, Size}; use rustc_target::abi::{Align, Size};
@ -145,8 +146,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
check: CheckAlignment, check: CheckAlignment,
) -> InterpResult<'tcx, ()>; ) -> InterpResult<'tcx, ()>;
/// Whether to enforce the validity invariant /// Whether to enforce the validity invariant for a specific layout.
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool;
/// Whether function calls should be [ABI](CallAbi)-checked. /// Whether function calls should be [ABI](CallAbi)-checked.
fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {

View File

@ -461,7 +461,7 @@ where
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
self.write_immediate_no_validate(src, dest)?; self.write_immediate_no_validate(src, dest)?;
if M::enforce_validity(self) { if M::enforce_validity(self, dest.layout) {
// Data got changed, better make sure it matches the type! // Data got changed, better make sure it matches the type!
self.validate_operand(&self.place_to_op(dest)?)?; self.validate_operand(&self.place_to_op(dest)?)?;
} }
@ -616,7 +616,7 @@ where
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
self.copy_op_no_validate(src, dest, allow_transmute)?; self.copy_op_no_validate(src, dest, allow_transmute)?;
if M::enforce_validity(self) { if M::enforce_validity(self, dest.layout) {
// Data got changed, better make sure it matches the type! // Data got changed, better make sure it matches the type!
self.validate_operand(&self.place_to_op(dest)?)?; self.validate_operand(&self.place_to_op(dest)?)?;
} }

View File

@ -180,7 +180,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
} }
#[inline(always)] #[inline(always)]
fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
false // for now, we don't enforce validity false // for now, we don't enforce validity
} }
fn alignment_check_failed( fn alignment_check_failed(

View File

@ -8,6 +8,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_middle::mir::visit::{MutVisitor, Visitor}; use rustc_middle::mir::visit::{MutVisitor, Visitor};
use rustc_middle::mir::*; use rustc_middle::mir::*;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_mir_dataflow::value_analysis::{Map, State, TrackElem, ValueAnalysis, ValueOrPlace}; use rustc_mir_dataflow::value_analysis::{Map, State, TrackElem, ValueAnalysis, ValueOrPlace};
use rustc_mir_dataflow::{lattice::FlatSet, Analysis, ResultsVisitor, SwitchIntEdgeEffects}; use rustc_mir_dataflow::{lattice::FlatSet, Analysis, ResultsVisitor, SwitchIntEdgeEffects};
@ -548,7 +549,7 @@ impl<'mir, 'tcx> rustc_const_eval::interpret::Machine<'mir, 'tcx> for DummyMachi
unimplemented!() unimplemented!()
} }
fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
unimplemented!() unimplemented!()
} }
fn alignment_check_failed( fn alignment_check_failed(

View File

@ -812,7 +812,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
} }
#[inline(always)] #[inline(always)]
fn enforce_validity(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { fn enforce_validity(ecx: &MiriInterpCx<'mir, 'tcx>, _layout: TyAndLayout<'tcx>) -> bool {
ecx.machine.validate ecx.machine.validate
} }

View File

@ -14,12 +14,12 @@ union MaybeUninit<T: Copy> {
} }
const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init };
//~^ ERROR it is undefined behavior to use this value //~^ ERROR evaluation of constant value failed
const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) };
//~^ ERROR it is undefined behavior to use this value //~^ ERROR it is undefined behavior to use this value
const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init }; const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init };
//~^ ERROR it is undefined behavior to use this value //~^ ERROR evaluation of constant value failed
fn main() {} fn main() {}

View File

@ -1,11 +1,8 @@
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-uninhabit.rs:16:1 --> $DIR/ub-uninhabit.rs:16:35
| |
LL | const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; LL | const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init };
| ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a value of uninhabited type Bar | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a value of uninhabited type Bar
|
= 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: $SIZE, align: $ALIGN) {}
error[E0080]: it is undefined behavior to use this value error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-uninhabit.rs:19:1 --> $DIR/ub-uninhabit.rs:19:1
@ -18,14 +15,11 @@ LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) };
HEX_DUMP HEX_DUMP
} }
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-uninhabit.rs:22:1 --> $DIR/ub-uninhabit.rs:22:42
| |
LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init }; LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered a value of uninhabited type Bar | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered a value of uninhabited type Bar
|
= 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: $SIZE, align: $ALIGN) {}
error: aborting due to 3 previous errors error: aborting due to 3 previous errors

View File

@ -24,14 +24,11 @@ note: inside `FOO`
LL | const FOO: [empty::Empty; 3] = [foo(); 3]; LL | const FOO: [empty::Empty; 3] = [foo(); 3];
| ^^^^^ | ^^^^^
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/validate_uninhabited_zsts.rs:21:1 --> $DIR/validate_uninhabited_zsts.rs:21:42
| |
LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0].0: encountered a value of uninhabited type empty::Void | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered a value of uninhabited type empty::Void
|
= 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: 0, align: 1) {}
warning: the type `empty::Empty` does not permit zero-initialization warning: the type `empty::Empty` does not permit zero-initialization
--> $DIR/validate_uninhabited_zsts.rs:21:42 --> $DIR/validate_uninhabited_zsts.rs:21:42

View File

@ -24,14 +24,11 @@ note: inside `FOO`
LL | const FOO: [empty::Empty; 3] = [foo(); 3]; LL | const FOO: [empty::Empty; 3] = [foo(); 3];
| ^^^^^ | ^^^^^
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/validate_uninhabited_zsts.rs:21:1 --> $DIR/validate_uninhabited_zsts.rs:21:42
| |
LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0].0: encountered a value of uninhabited type empty::Void | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered a value of uninhabited type empty::Void
|
= 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: 0, align: 1) {}
warning: the type `empty::Empty` does not permit zero-initialization warning: the type `empty::Empty` does not permit zero-initialization
--> $DIR/validate_uninhabited_zsts.rs:21:42 --> $DIR/validate_uninhabited_zsts.rs:21:42

View File

@ -19,7 +19,7 @@ pub mod empty {
const FOO: [empty::Empty; 3] = [foo(); 3]; const FOO: [empty::Empty; 3] = [foo(); 3];
const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
//~^ ERROR it is undefined behavior to use this value //~^ ERROR evaluation of constant value failed
//~| WARN the type `empty::Empty` does not permit zero-initialization //~| WARN the type `empty::Empty` does not permit zero-initialization
fn main() { fn main() {

View File

@ -1,4 +1,4 @@
// check-pass // check-fail
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct ChildStdin { pub struct ChildStdin {
@ -14,6 +14,7 @@ const FOO: () = {
b: (), b: (),
} }
let x = unsafe { Foo { b: () }.a }; let x = unsafe { Foo { b: () }.a };
//~^ ERROR: evaluation of constant value failed
let x = &x.inner; let x = &x.inner;
}; };

View File

@ -0,0 +1,9 @@
error[E0080]: evaluation of constant value failed
--> $DIR/issue-64506.rs:16:22
|
LL | let x = unsafe { Foo { b: () }.a };
| ^^^^^^^^^^^^^^^ constructing invalid value at .inner: encountered a value of uninhabited type AnonPipe
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.