-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Miri engine cleanup #53671
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Miri engine cleanup #53671
Changes from all commits
c141ccf
2592b20
286fc5c
66d64ba
aa645f3
a5baea6
035c69f
ef96a60
9cfc9f0
548b373
89cfd08
c898e19
07bdd48
c38cc89
5b737db
6c78fa8
f96208c
066d2ee
e6a5a94
506dd70
c9b5fac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
//! Intrinsics and other functions that the miri engine executes without | ||
//! looking at their MIR. Intrinsics/functions supported here are shared by CTFE | ||
//! and miri. | ||
use syntax::symbol::Symbol; | ||
use rustc::ty; | ||
use rustc::ty::layout::{LayoutOf, Primitive}; | ||
use rustc::mir::interpret::{ | ||
EvalResult, EvalErrorKind, Scalar, | ||
}; | ||
|
||
use super::{ | ||
Machine, PlaceTy, OpTy, EvalContext, | ||
}; | ||
|
||
|
||
fn numeric_intrinsic<'tcx>( | ||
name: &str, | ||
bits: u128, | ||
kind: Primitive, | ||
) -> EvalResult<'tcx, Scalar> { | ||
let size = match kind { | ||
Primitive::Int(integer, _) => integer.size(), | ||
_ => bug!("invalid `{}` argument: {:?}", name, bits), | ||
}; | ||
let extra = 128 - size.bits() as u128; | ||
let bits_out = match name { | ||
"ctpop" => bits.count_ones() as u128, | ||
"ctlz" => bits.leading_zeros() as u128 - extra, | ||
"cttz" => (bits << extra).trailing_zeros() as u128 - extra, | ||
"bswap" => (bits << extra).swap_bytes(), | ||
_ => bug!("not a numeric intrinsic: {}", name), | ||
}; | ||
Ok(Scalar::Bits { bits: bits_out, size: size.bytes() as u8 }) | ||
} | ||
|
||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | ||
/// Returns whether emulation happened. | ||
pub fn emulate_intrinsic( | ||
&mut self, | ||
instance: ty::Instance<'tcx>, | ||
args: &[OpTy<'tcx>], | ||
dest: PlaceTy<'tcx>, | ||
) -> EvalResult<'tcx, bool> { | ||
let substs = instance.substs; | ||
|
||
let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..]; | ||
match intrinsic_name { | ||
"min_align_of" => { | ||
let elem_ty = substs.type_at(0); | ||
let elem_align = self.layout_of(elem_ty)?.align.abi(); | ||
let align_val = Scalar::Bits { | ||
bits: elem_align as u128, | ||
size: dest.layout.size.bytes() as u8, | ||
}; | ||
self.write_scalar(align_val, dest)?; | ||
} | ||
|
||
"size_of" => { | ||
let ty = substs.type_at(0); | ||
let size = self.layout_of(ty)?.size.bytes() as u128; | ||
let size_val = Scalar::Bits { | ||
bits: size, | ||
size: dest.layout.size.bytes() as u8, | ||
}; | ||
self.write_scalar(size_val, dest)?; | ||
} | ||
|
||
"type_id" => { | ||
let ty = substs.type_at(0); | ||
let type_id = self.tcx.type_id_hash(ty) as u128; | ||
let id_val = Scalar::Bits { | ||
bits: type_id, | ||
size: dest.layout.size.bytes() as u8, | ||
}; | ||
self.write_scalar(id_val, dest)?; | ||
} | ||
"ctpop" | "cttz" | "cttz_nonzero" | "ctlz" | "ctlz_nonzero" | "bswap" => { | ||
let ty = substs.type_at(0); | ||
let layout_of = self.layout_of(ty)?; | ||
let bits = self.read_scalar(args[0])?.to_bits(layout_of.size)?; | ||
let kind = match layout_of.abi { | ||
ty::layout::Abi::Scalar(ref scalar) => scalar.value, | ||
_ => Err(::rustc::mir::interpret::EvalErrorKind::TypeNotPrimitive(ty))?, | ||
}; | ||
let out_val = if intrinsic_name.ends_with("_nonzero") { | ||
if bits == 0 { | ||
return err!(Intrinsic(format!("{} called on 0", intrinsic_name))); | ||
} | ||
numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), bits, kind)? | ||
} else { | ||
numeric_intrinsic(intrinsic_name, bits, kind)? | ||
}; | ||
self.write_scalar(out_val, dest)?; | ||
} | ||
|
||
_ => return Ok(false), | ||
} | ||
|
||
Ok(true) | ||
} | ||
|
||
/// "Intercept" a function call because we have something special to do for it. | ||
/// Returns whether an intercept happened. | ||
pub fn hook_fn( | ||
&mut self, | ||
instance: ty::Instance<'tcx>, | ||
args: &[OpTy<'tcx>], | ||
dest: Option<PlaceTy<'tcx>>, | ||
) -> EvalResult<'tcx, bool> { | ||
let def_id = instance.def_id(); | ||
// Some fn calls are actually BinOp intrinsics | ||
if let Some((op, oflo)) = self.tcx.is_binop_lang_item(def_id) { | ||
let dest = dest.expect("128 lowerings can't diverge"); | ||
let l = self.read_value(args[0])?; | ||
let r = self.read_value(args[1])?; | ||
if oflo { | ||
self.binop_with_overflow(op, l, r, dest)?; | ||
} else { | ||
self.binop_ignore_overflow(op, l, r, dest)?; | ||
} | ||
return Ok(true); | ||
} else if Some(def_id) == self.tcx.lang_items().panic_fn() { | ||
assert!(args.len() == 1); | ||
// &(&'static str, &'static str, u32, u32) | ||
let ptr = self.read_value(args[0])?; | ||
let place = self.ref_to_mplace(ptr)?; | ||
let (msg, file, line, col) = ( | ||
self.mplace_field(place, 0)?, | ||
self.mplace_field(place, 1)?, | ||
self.mplace_field(place, 2)?, | ||
self.mplace_field(place, 3)?, | ||
); | ||
|
||
let msg_place = self.ref_to_mplace(self.read_value(msg.into())?)?; | ||
let msg = Symbol::intern(self.read_str(msg_place)?); | ||
let file_place = self.ref_to_mplace(self.read_value(file.into())?)?; | ||
let file = Symbol::intern(self.read_str(file_place)?); | ||
let line = self.read_scalar(line.into())?.to_u32()?; | ||
let col = self.read_scalar(col.into())?.to_u32()?; | ||
return Err(EvalErrorKind::Panic { msg, file, line, col }.into()); | ||
} else if Some(def_id) == self.tcx.lang_items().begin_panic_fn() { | ||
assert!(args.len() == 2); | ||
// &'static str, &(&'static str, u32, u32) | ||
let msg = args[0]; | ||
let ptr = self.read_value(args[1])?; | ||
let place = self.ref_to_mplace(ptr)?; | ||
let (file, line, col) = ( | ||
self.mplace_field(place, 0)?, | ||
self.mplace_field(place, 1)?, | ||
self.mplace_field(place, 2)?, | ||
); | ||
|
||
let msg_place = self.ref_to_mplace(self.read_value(msg.into())?)?; | ||
let msg = Symbol::intern(self.read_str(msg_place)?); | ||
let file_place = self.ref_to_mplace(self.read_value(file.into())?)?; | ||
let file = Symbol::intern(self.read_str(file_place)?); | ||
let line = self.read_scalar(line.into())?.to_u32()?; | ||
let col = self.read_scalar(col.into())?.to_u32()?; | ||
return Err(EvalErrorKind::Panic { msg, file, line, col }.into()); | ||
} else { | ||
return Ok(false); | ||
} | ||
} | ||
} |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,16 +11,17 @@ | |
//! Functions concerning immediate values and operands, and reading from operands. | ||
//! All high-level functions to read from memory work on operands as sources. | ||
use std::hash::{Hash, Hasher}; | ||
use std::convert::TryInto; | ||
|
||
use rustc::mir; | ||
use rustc::ty::layout::{self, Align, LayoutOf, TyLayout, HasDataLayout, IntegerExt}; | ||
use rustc::{mir, ty}; | ||
use rustc::ty::layout::{self, Size, Align, LayoutOf, TyLayout, HasDataLayout, IntegerExt}; | ||
use rustc_data_structures::indexed_vec::Idx; | ||
|
||
use rustc::mir::interpret::{ | ||
GlobalId, ConstValue, Scalar, EvalResult, Pointer, ScalarMaybeUndef, EvalErrorKind | ||
}; | ||
use super::{EvalContext, Machine, MemPlace, MPlaceTy, PlaceExtra, MemoryKind}; | ||
use super::{EvalContext, Machine, MemPlace, MPlaceTy, MemoryKind}; | ||
|
||
/// A `Value` represents a single immediate self-contained Rust value. | ||
/// | ||
|
@@ -64,6 +65,14 @@ impl<'tcx> Value { | |
self.to_scalar_or_undef().not_undef() | ||
} | ||
|
||
#[inline] | ||
pub fn to_scalar_pair(self) -> EvalResult<'tcx, (Scalar, Scalar)> { | ||
match self { | ||
Value::Scalar(..) => bug!("Got a thin pointer where a scalar pair was expected"), | ||
Value::ScalarPair(a, b) => Ok((a.not_undef()?, b.not_undef()?)) | ||
} | ||
} | ||
|
||
/// Convert the value into a pointer (or a pointer-sized integer). | ||
/// Throws away the second half of a ScalarPair! | ||
#[inline] | ||
|
@@ -73,24 +82,6 @@ impl<'tcx> Value { | |
Value::ScalarPair(ptr, _) => ptr.not_undef(), | ||
} | ||
} | ||
|
||
pub fn to_scalar_dyn_trait(self) -> EvalResult<'tcx, (Scalar, Pointer)> { | ||
match self { | ||
Value::ScalarPair(ptr, vtable) => | ||
Ok((ptr.not_undef()?, vtable.to_ptr()?)), | ||
_ => bug!("expected ptr and vtable, got {:?}", self), | ||
} | ||
} | ||
|
||
pub fn to_scalar_slice(self, cx: impl HasDataLayout) -> EvalResult<'tcx, (Scalar, u64)> { | ||
match self { | ||
Value::ScalarPair(ptr, val) => { | ||
let len = val.to_bits(cx.data_layout().pointer_size)?; | ||
Ok((ptr.not_undef()?, len as u64)) | ||
} | ||
_ => bug!("expected ptr and length, got {:?}", self), | ||
} | ||
} | ||
} | ||
|
||
// ScalarPair needs a type to interpret, so we often have a value and a type together | ||
|
@@ -150,7 +141,7 @@ impl Operand { | |
|
||
#[derive(Copy, Clone, Debug)] | ||
pub struct OpTy<'tcx> { | ||
pub op: Operand, | ||
crate op: Operand, // ideally we'd make this private, but we are not there yet | ||
pub layout: TyLayout<'tcx>, | ||
} | ||
|
||
|
@@ -182,6 +173,20 @@ impl<'tcx> From<ValTy<'tcx>> for OpTy<'tcx> { | |
} | ||
} | ||
|
||
// Validation needs to hash OpTy, but we cannot hash Layout -- so we just hash the type | ||
impl<'tcx> Hash for OpTy<'tcx> { | ||
fn hash<H: Hasher>(&self, state: &mut H) { | ||
self.op.hash(state); | ||
self.layout.ty.hash(state); | ||
} | ||
} | ||
impl<'tcx> PartialEq for OpTy<'tcx> { | ||
fn eq(&self, other: &Self) -> bool { | ||
self.op == other.op && self.layout.ty == other.layout.ty | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a similar problem as with #53424. Maybe we should just derive There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is the same problem in fact. Why have they not been derived in the first place? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I deleted the comment after seeing that PR. |
||
} | ||
} | ||
impl<'tcx> Eq for OpTy<'tcx> {} | ||
|
||
impl<'tcx> OpTy<'tcx> { | ||
#[inline] | ||
pub fn from_ptr(ptr: Pointer, align: Align, layout: TyLayout<'tcx>) -> Self { | ||
|
@@ -227,7 +232,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
&self, | ||
mplace: MPlaceTy<'tcx>, | ||
) -> EvalResult<'tcx, Option<Value>> { | ||
if mplace.extra != PlaceExtra::None { | ||
if mplace.layout.is_unsized() { | ||
// Dont touch unsized | ||
return Ok(None); | ||
} | ||
let (ptr, ptr_align) = mplace.to_scalar_ptr_align(); | ||
|
@@ -266,7 +272,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
/// Note that for a given layout, this operation will either always fail or always | ||
/// succeed! Whether it succeeds depends on whether the layout can be represented | ||
/// in a `Value`, not on which data is stored there currently. | ||
pub(super) fn try_read_value( | ||
pub(crate) fn try_read_value( | ||
&self, | ||
src: OpTy<'tcx>, | ||
) -> EvalResult<'tcx, Result<Value, MemPlace>> { | ||
|
@@ -300,6 +306,18 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
} | ||
} | ||
|
||
// Turn the MPlace into a string (must already be dereferenced!) | ||
pub fn read_str( | ||
&self, | ||
mplace: MPlaceTy<'tcx>, | ||
) -> EvalResult<'tcx, &str> { | ||
let len = mplace.len(self)?; | ||
let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len as u64))?; | ||
let str = ::std::str::from_utf8(bytes) | ||
.map_err(|err| EvalErrorKind::ValidationFailure(err.to_string()))?; | ||
Ok(str) | ||
} | ||
|
||
pub fn uninit_operand(&mut self, layout: TyLayout<'tcx>) -> EvalResult<'tcx, Operand> { | ||
// This decides which types we will use the Immediate optimization for, and hence should | ||
// match what `try_read_value` and `eval_place_to_op` support. | ||
|
@@ -360,7 +378,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
Ok(OpTy { op: Operand::Immediate(value), layout: field_layout }) | ||
} | ||
|
||
pub(super) fn operand_downcast( | ||
pub fn operand_downcast( | ||
&self, | ||
op: OpTy<'tcx>, | ||
variant: usize, | ||
|
@@ -411,12 +429,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
// avoid allocations. If you already know the layout, you can pass it in | ||
// to avoid looking it up again. | ||
fn eval_place_to_op( | ||
&mut self, | ||
&self, | ||
mir_place: &mir::Place<'tcx>, | ||
layout: Option<TyLayout<'tcx>>, | ||
) -> EvalResult<'tcx, OpTy<'tcx>> { | ||
use rustc::mir::Place::*; | ||
Ok(match *mir_place { | ||
let op = match *mir_place { | ||
Local(mir::RETURN_PLACE) => return err!(ReadFromReturnPointer), | ||
Local(local) => { | ||
let op = *self.frame().locals[local].access()?; | ||
|
@@ -430,21 +448,18 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
self.operand_projection(op, &proj.elem)? | ||
} | ||
|
||
// Everything else is an mplace, so we just call `eval_place`. | ||
// Note that getting an mplace for a static aways requires `&mut`, | ||
// so this does not "cost" us anything in terms if mutability. | ||
Promoted(_) | Static(_) => { | ||
let place = self.eval_place(mir_place)?; | ||
place.to_mem_place().into() | ||
} | ||
}) | ||
_ => self.eval_place_to_mplace(mir_place)?.into(), | ||
}; | ||
|
||
trace!("eval_place_to_op: got {:?}", *op); | ||
Ok(op) | ||
} | ||
|
||
/// Evaluate the operand, returning a place where you can then find the data. | ||
/// if you already know the layout, you can save two some table lookups | ||
/// by passing it in here. | ||
pub fn eval_operand( | ||
&mut self, | ||
&self, | ||
mir_op: &mir::Operand<'tcx>, | ||
layout: Option<TyLayout<'tcx>>, | ||
) -> EvalResult<'tcx, OpTy<'tcx>> { | ||
|
@@ -469,8 +484,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
} | ||
|
||
/// Evaluate a bunch of operands at once | ||
pub(crate) fn eval_operands( | ||
&mut self, | ||
pub(super) fn eval_operands( | ||
&self, | ||
ops: &[mir::Operand<'tcx>], | ||
) -> EvalResult<'tcx, Vec<OpTy<'tcx>>> { | ||
ops.into_iter() | ||
|
@@ -479,12 +494,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
} | ||
|
||
// Also used e.g. when miri runs into a constant. | ||
// Unfortunately, this needs an `&mut` to be able to allocate a copy of a `ByRef` | ||
// constant. This bleeds up to `eval_operand` needing `&mut`. | ||
pub fn const_value_to_op( | ||
&mut self, | ||
pub(super) fn const_value_to_op( | ||
&self, | ||
val: ConstValue<'tcx>, | ||
) -> EvalResult<'tcx, Operand> { | ||
trace!("const_value_to_op: {:?}", val); | ||
match val { | ||
ConstValue::Unevaluated(def_id, substs) => { | ||
let instance = self.resolve(def_id, substs)?; | ||
|
@@ -493,9 +507,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
promoted: None, | ||
}) | ||
} | ||
ConstValue::ByRef(alloc, offset) => { | ||
// FIXME: Allocate new AllocId for all constants inside | ||
let id = self.memory.allocate_value(alloc.clone(), MemoryKind::Stack)?; | ||
ConstValue::ByRef(id, alloc, offset) => { | ||
// We rely on mutability being set correctly in that allocation to prevent writes | ||
// where none should happen -- and for `static mut`, we copy on demand anyway. | ||
Ok(Operand::from_ptr(Pointer::new(id, offset), alloc.align)) | ||
}, | ||
ConstValue::ScalarPair(a, b) => | ||
|
@@ -504,52 +518,24 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
Ok(Operand::Immediate(Value::Scalar(x.into()))), | ||
} | ||
} | ||
pub fn const_to_op( | ||
&self, | ||
cnst: &ty::Const<'tcx>, | ||
) -> EvalResult<'tcx, OpTy<'tcx>> { | ||
let op = self.const_value_to_op(cnst.val)?; | ||
Ok(OpTy { op, layout: self.layout_of(cnst.ty)? }) | ||
} | ||
|
||
pub(super) fn global_to_op(&mut self, gid: GlobalId<'tcx>) -> EvalResult<'tcx, Operand> { | ||
pub(super) fn global_to_op(&self, gid: GlobalId<'tcx>) -> EvalResult<'tcx, Operand> { | ||
let cv = self.const_eval(gid)?; | ||
self.const_value_to_op(cv.val) | ||
} | ||
|
||
/// We cannot do self.read_value(self.eval_operand) due to eval_operand taking &mut self, | ||
/// so this helps avoid unnecessary let. | ||
#[inline] | ||
pub fn eval_operand_and_read_value( | ||
&mut self, | ||
op: &mir::Operand<'tcx>, | ||
layout: Option<TyLayout<'tcx>>, | ||
) -> EvalResult<'tcx, ValTy<'tcx>> { | ||
let op = self.eval_operand(op, layout)?; | ||
self.read_value(op) | ||
} | ||
|
||
/// reads a tag and produces the corresponding variant index | ||
pub fn read_discriminant_as_variant_index( | ||
/// Read discriminant, return the runtime value as well as the variant index. | ||
pub fn read_discriminant( | ||
&self, | ||
rval: OpTy<'tcx>, | ||
) -> EvalResult<'tcx, usize> { | ||
match rval.layout.variants { | ||
layout::Variants::Single { index } => Ok(index), | ||
layout::Variants::Tagged { .. } => { | ||
let discr_val = self.read_discriminant_value(rval)?; | ||
rval.layout.ty | ||
.ty_adt_def() | ||
.expect("tagged layout for non adt") | ||
.discriminants(self.tcx.tcx) | ||
.position(|var| var.val == discr_val) | ||
.ok_or_else(|| EvalErrorKind::InvalidDiscriminant.into()) | ||
} | ||
layout::Variants::NicheFilling { .. } => { | ||
let discr_val = self.read_discriminant_value(rval)?; | ||
assert_eq!(discr_val as usize as u128, discr_val); | ||
Ok(discr_val as usize) | ||
}, | ||
} | ||
} | ||
|
||
pub fn read_discriminant_value( | ||
&self, | ||
rval: OpTy<'tcx>, | ||
) -> EvalResult<'tcx, u128> { | ||
) -> EvalResult<'tcx, (u128, usize)> { | ||
trace!("read_discriminant_value {:#?}", rval.layout); | ||
if rval.layout.abi == layout::Abi::Uninhabited { | ||
return err!(Unreachable); | ||
|
@@ -560,20 +546,21 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
let discr_val = rval.layout.ty.ty_adt_def().map_or( | ||
index as u128, | ||
|def| def.discriminant_for_variant(*self.tcx, index).val); | ||
return Ok(discr_val); | ||
return Ok((discr_val, index)); | ||
} | ||
layout::Variants::Tagged { .. } | | ||
layout::Variants::NicheFilling { .. } => {}, | ||
} | ||
// read raw discriminant value | ||
let discr_op = self.operand_field(rval, 0)?; | ||
let discr_val = self.read_value(discr_op)?; | ||
trace!("discr value: {:?}", discr_val); | ||
let raw_discr = discr_val.to_scalar()?; | ||
trace!("discr value: {:?}", raw_discr); | ||
// post-process | ||
Ok(match rval.layout.variants { | ||
layout::Variants::Single { .. } => bug!(), | ||
// FIXME: We should catch invalid discriminants here! | ||
layout::Variants::Tagged { .. } => { | ||
if discr_val.layout.ty.is_signed() { | ||
let real_discr = if discr_val.layout.ty.is_signed() { | ||
let i = raw_discr.to_bits(discr_val.layout.size)? as i128; | ||
// going from layout tag type to typeck discriminant type | ||
// requires first sign extending with the layout discriminant | ||
|
@@ -590,7 +577,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
(truncatee << shift) >> shift | ||
} else { | ||
raw_discr.to_bits(discr_val.layout.size)? | ||
} | ||
}; | ||
// Make sure we catch invalid discriminants | ||
let index = rval.layout.ty | ||
.ty_adt_def() | ||
.expect("tagged layout for non adt") | ||
.discriminants(self.tcx.tcx) | ||
.position(|var| var.val == real_discr) | ||
.ok_or_else(|| EvalErrorKind::InvalidDiscriminant(real_discr))?; | ||
(real_discr, index) | ||
}, | ||
layout::Variants::NicheFilling { | ||
dataful_variant, | ||
|
@@ -600,8 +595,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
} => { | ||
let variants_start = *niche_variants.start() as u128; | ||
let variants_end = *niche_variants.end() as u128; | ||
match raw_discr { | ||
let real_discr = match raw_discr { | ||
Scalar::Ptr(_) => { | ||
// The niche must be just 0 (which a pointer value never is) | ||
assert!(niche_start == 0); | ||
assert!(variants_start == variants_end); | ||
dataful_variant as u128 | ||
|
@@ -616,7 +612,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { | |
dataful_variant as u128 | ||
} | ||
}, | ||
} | ||
}; | ||
let index = real_discr as usize; | ||
assert_eq!(index as u128, real_discr); | ||
assert!(index < rval.layout.ty | ||
.ty_adt_def() | ||
.expect("tagged layout for non adt") | ||
.variants.len()); | ||
(real_discr, index) | ||
} | ||
}) | ||
} | ||
|
Large diffs are not rendered by default.
Large diffs are not rendered by default.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,83 +1,115 @@ | ||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:79:1 | ||
--> $DIR/union-ub-fat-ptr.rs:87:1 | ||
| | ||
LL | const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str}; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or out-of-bounds memory at .<deref> | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:82:1 | ||
--> $DIR/union-ub-fat-ptr.rs:90:1 | ||
| | ||
LL | const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str}; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in fat pointer | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:85:1 | ||
--> $DIR/union-ub-fat-ptr.rs:93:1 | ||
| | ||
LL | const C2: &Str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str}; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer | ||
LL | const C2: &MyStr = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str}; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in fat pointer | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:91:1 | ||
--> $DIR/union-ub-fat-ptr.rs:99:1 | ||
| | ||
LL | const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice}; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or out-of-bounds memory at .<deref>[1] | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:94:1 | ||
--> $DIR/union-ub-fat-ptr.rs:102:1 | ||
| | ||
LL | const C3: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice}; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in fat pointer | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:98:1 | ||
--> $DIR/union-ub-fat-ptr.rs:106:1 | ||
| | ||
LL | const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust}; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tried to access memory with alignment N, but alignment N is required | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid vtable in fat pointer at .<deref> | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:101:1 | ||
--> $DIR/union-ub-fat-ptr.rs:109:1 | ||
| | ||
LL | const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust}; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a memory access tried to interpret some bytes as a pointer | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid vtable in fat pointer at .<deref> | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:104:1 | ||
--> $DIR/union-ub-fat-ptr.rs:112:1 | ||
| | ||
LL | const F: &Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust}; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer vtable is not a valid pointer | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-pointer vtable in fat pointer | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:108:1 | ||
--> $DIR/union-ub-fat-ptr.rs:116:1 | ||
| | ||
LL | const G: &Trait = &unsafe { BoolTransmute { val: 3 }.bl }; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>, but expected something in the range 0..=1 | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:112:1 | ||
--> $DIR/union-ub-fat-ptr.rs:119:1 | ||
| | ||
LL | const H: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }]; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>[0], but expected something in the range 0..=1 | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error: aborting due to 10 previous errors | ||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:125:1 | ||
| | ||
LL | const I2: &MySliceBool = &MySlice(unsafe { BoolTransmute { val: 3 }.bl }, [false]); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>.0, but expected something in the range 0..=1 | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:128:1 | ||
| | ||
LL | const I3: &MySliceBool = &MySlice(true, [unsafe { BoolTransmute { val: 3 }.bl }]); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>.1[0], but expected something in the range 0..=1 | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:132:1 | ||
| | ||
LL | const J1: &str = unsafe { SliceTransmute { slice: &[0xFF] }.str }; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-UTF-8 data in str at .<deref> | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error[E0080]: this constant likely exhibits undefined behavior | ||
--> $DIR/union-ub-fat-ptr.rs:135:1 | ||
| | ||
LL | const J2: &MyStr = unsafe { SliceTransmute { slice: &[0xFF] }.my_str }; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-UTF-8 data in str at .<deref>.0 | ||
| | ||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior | ||
|
||
error: aborting due to 14 previous errors | ||
|
||
For more information about this error, try `rustc --explain E0080`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this made mutable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how all allocations are created. Previously, mutability was entirely ignored by the miri engine, so this flag only mattered once the static got interned (where it gets set, in
intern_static
inmemory.rs
).But now that the engine enforces mutability, we have to make some allocations mutable or else we have no heap ;)