Auto merge of #87123 - RalfJung:miri-provenance-overhaul, r=oli-obk
CTFE/Miri engine Pointer type overhaul This fixes the long-standing problem that we are using `Scalar` as a type to represent pointers that might be integer values (since they point to a ZST). The main problem is that with int-to-ptr casts, there are multiple ways to represent the same pointer as a `Scalar` and it is unclear if "normalization" (i.e., the cast) already happened or not. This leads to ugly methods like `force_mplace_ptr` and `force_op_ptr`. Another problem this solves is that in Miri, it would make a lot more sense to have the `Pointer::offset` field represent the full absolute address (instead of being relative to the `AllocId`). This means we can do ptr-to-int casts without access to any machine state, and it means that the overflow checks on pointer arithmetic are (finally!) accurate. To solve this, the `Pointer` type is made entirely parametric over the provenance, so that we can use `Pointer<AllocId>` inside `Scalar` but use `Pointer<Option<AllocId>>` when accessing memory (where `None` represents the case that we could not figure out an `AllocId`; in that case the `offset` is an absolute address). Moreover, the `Provenance` trait determines if a pointer with a given provenance can be cast to an integer by simply dropping the provenance. I hope this can be read commit-by-commit, but the first commit does the bulk of the work. It introduces some FIXMEs that are resolved later. Fixes https://github.com/rust-lang/miri/issues/841 Miri PR: https://github.com/rust-lang/miri/pull/1851 r? `@oli-obk`
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use rustc_apfloat::ieee::{Double, Single};
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
||||
use rustc_target::abi::{Size, TargetDataLayout};
|
||||
use rustc_target::abi::Size;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fmt;
|
||||
|
||||
@@ -193,15 +193,6 @@ impl ScalarInt {
|
||||
self.data == 0
|
||||
}
|
||||
|
||||
pub(crate) fn ptr_sized_op<E>(
|
||||
self,
|
||||
dl: &TargetDataLayout,
|
||||
f_int: impl FnOnce(u64) -> Result<u64, E>,
|
||||
) -> Result<Self, E> {
|
||||
assert_eq!(u64::from(self.size), dl.pointer_size.bytes());
|
||||
Ok(Self::try_from_uint(f_int(u64::try_from(self.data).unwrap())?, self.size()).unwrap())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
|
||||
let data = i.into();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::mir::interpret::ConstValue;
|
||||
use crate::mir::interpret::Scalar;
|
||||
use crate::mir::interpret::{AllocId, ConstValue, Scalar};
|
||||
use crate::mir::Promoted;
|
||||
use crate::ty::subst::{InternalSubsts, SubstsRef};
|
||||
use crate::ty::ParamEnv;
|
||||
@@ -59,7 +58,7 @@ impl<'tcx> ConstKind<'tcx> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_to_scalar(self) -> Option<Scalar> {
|
||||
pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
|
||||
self.try_to_value()?.try_to_scalar()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::middle::cstore::{ExternCrate, ExternCrateSource};
|
||||
use crate::mir::interpret::{AllocRange, ConstValue, GlobalAlloc, Pointer, Scalar};
|
||||
use crate::mir::interpret::{AllocRange, ConstValue, GlobalAlloc, Pointer, Provenance, Scalar};
|
||||
use crate::ty::subst::{GenericArg, GenericArgKind, Subst};
|
||||
use crate::ty::{self, ConstInt, DefIdTree, ParamConst, ScalarInt, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_apfloat::ieee::{Double, Single};
|
||||
@@ -974,7 +974,7 @@ pub trait PrettyPrinter<'tcx>:
|
||||
print_ty: bool,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
match scalar {
|
||||
Scalar::Ptr(ptr) => self.pretty_print_const_scalar_ptr(ptr, ty, print_ty),
|
||||
Scalar::Ptr(ptr, _size) => self.pretty_print_const_scalar_ptr(ptr, ty, print_ty),
|
||||
Scalar::Int(int) => self.pretty_print_const_scalar_int(int, ty, print_ty),
|
||||
}
|
||||
}
|
||||
@@ -987,6 +987,7 @@ pub trait PrettyPrinter<'tcx>:
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
define_scoped_cx!(self);
|
||||
|
||||
let (alloc_id, offset) = ptr.into_parts();
|
||||
match ty.kind() {
|
||||
// Byte strings (&[u8; N])
|
||||
ty::Ref(
|
||||
@@ -1002,10 +1003,10 @@ pub trait PrettyPrinter<'tcx>:
|
||||
..
|
||||
},
|
||||
_,
|
||||
) => match self.tcx().get_global_alloc(ptr.alloc_id) {
|
||||
) => match self.tcx().get_global_alloc(alloc_id) {
|
||||
Some(GlobalAlloc::Memory(alloc)) => {
|
||||
let len = int.assert_bits(self.tcx().data_layout.pointer_size);
|
||||
let range = AllocRange { start: ptr.offset, size: Size::from_bytes(len) };
|
||||
let range = AllocRange { start: offset, size: Size::from_bytes(len) };
|
||||
if let Ok(byte_str) = alloc.get_bytes(&self.tcx(), range) {
|
||||
p!(pretty_print_byte_str(byte_str))
|
||||
} else {
|
||||
@@ -1020,7 +1021,7 @@ pub trait PrettyPrinter<'tcx>:
|
||||
ty::FnPtr(_) => {
|
||||
// FIXME: We should probably have a helper method to share code with the "Byte strings"
|
||||
// printing above (which also has to handle pointers to all sorts of things).
|
||||
match self.tcx().get_global_alloc(ptr.alloc_id) {
|
||||
match self.tcx().get_global_alloc(alloc_id) {
|
||||
Some(GlobalAlloc::Function(instance)) => {
|
||||
self = self.typed_value(
|
||||
|this| this.print_value_path(instance.def_id(), instance.substs),
|
||||
@@ -1068,8 +1069,8 @@ pub trait PrettyPrinter<'tcx>:
|
||||
ty::Char if char::try_from(int).is_ok() => {
|
||||
p!(write("{:?}", char::try_from(int).unwrap()))
|
||||
}
|
||||
// Raw pointers
|
||||
ty::RawPtr(_) | ty::FnPtr(_) => {
|
||||
// Pointer types
|
||||
ty::Ref(..) | ty::RawPtr(_) | ty::FnPtr(_) => {
|
||||
let data = int.assert_bits(self.tcx().data_layout.pointer_size);
|
||||
self = self.typed_value(
|
||||
|mut this| {
|
||||
@@ -1106,9 +1107,9 @@ pub trait PrettyPrinter<'tcx>:
|
||||
|
||||
/// This is overridden for MIR printing because we only want to hide alloc ids from users, not
|
||||
/// from MIR where it is actually useful.
|
||||
fn pretty_print_const_pointer(
|
||||
fn pretty_print_const_pointer<Tag: Provenance>(
|
||||
mut self,
|
||||
_: Pointer,
|
||||
_: Pointer<Tag>,
|
||||
ty: Ty<'tcx>,
|
||||
print_ty: bool,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
@@ -1679,9 +1680,9 @@ impl<F: fmt::Write> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> {
|
||||
}
|
||||
}
|
||||
|
||||
fn pretty_print_const_pointer(
|
||||
fn pretty_print_const_pointer<Tag: Provenance>(
|
||||
self,
|
||||
p: Pointer,
|
||||
p: Pointer<Tag>,
|
||||
ty: Ty<'tcx>,
|
||||
print_ty: bool,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
|
||||
@@ -595,9 +595,12 @@ fn check_const_value_eq<R: TypeRelation<'tcx>>(
|
||||
(ConstValue::Scalar(Scalar::Int(a_val)), ConstValue::Scalar(Scalar::Int(b_val))) => {
|
||||
a_val == b_val
|
||||
}
|
||||
(ConstValue::Scalar(Scalar::Ptr(a_val)), ConstValue::Scalar(Scalar::Ptr(b_val))) => {
|
||||
(
|
||||
ConstValue::Scalar(Scalar::Ptr(a_val, _a_size)),
|
||||
ConstValue::Scalar(Scalar::Ptr(b_val, _b_size)),
|
||||
) => {
|
||||
a_val == b_val
|
||||
|| match (tcx.global_alloc(a_val.alloc_id), tcx.global_alloc(b_val.alloc_id)) {
|
||||
|| match (tcx.global_alloc(a_val.provenance), tcx.global_alloc(b_val.provenance)) {
|
||||
(GlobalAlloc::Function(a_instance), GlobalAlloc::Function(b_instance)) => {
|
||||
a_instance == b_instance
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar};
|
||||
use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar, ScalarMaybeUninit};
|
||||
use crate::ty::fold::TypeFoldable;
|
||||
use crate::ty::{self, DefId, SubstsRef, Ty, TyCtxt};
|
||||
use rustc_ast::Mutability;
|
||||
@@ -74,7 +74,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
let instance = ty::Instance::resolve_drop_in_place(tcx, ty);
|
||||
let fn_alloc_id = tcx.create_fn_alloc(instance);
|
||||
let fn_ptr = Pointer::from(fn_alloc_id);
|
||||
fn_ptr.into()
|
||||
ScalarMaybeUninit::from_pointer(fn_ptr, &tcx)
|
||||
}
|
||||
VtblEntry::MetadataSize => Scalar::from_uint(size, ptr_size).into(),
|
||||
VtblEntry::MetadataAlign => Scalar::from_uint(align, ptr_size).into(),
|
||||
@@ -90,7 +90,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
.polymorphize(tcx);
|
||||
let fn_alloc_id = tcx.create_fn_alloc(instance);
|
||||
let fn_ptr = Pointer::from(fn_alloc_id);
|
||||
fn_ptr.into()
|
||||
ScalarMaybeUninit::from_pointer(fn_ptr, &tcx)
|
||||
}
|
||||
};
|
||||
vtable
|
||||
|
||||
Reference in New Issue
Block a user