Introduce ConstAllocation
.
Currently some `Allocation`s are interned, some are not, and it's very hard to tell at a use point which is which. This commit introduces `ConstAllocation` for the known-interned ones, which makes the division much clearer. `ConstAllocation::inner()` is used to get the underlying `Allocation`. In some places it's natural to use an `Allocation`, in some it's natural to use a `ConstAllocation`, and in some places there's no clear choice. I've tried to make things look as nice as possible, while generally favouring `ConstAllocation`, which is the type that embodies more information. This does require quite a few calls to `inner()`. The commit also tweaks how `PartialOrd` works for `Interned`. The previous code was too clever by half, building on `T: Ord` to make the code shorter. That caused problems with deriving `PartialOrd` and `Ord` for `ConstAllocation`, so I changed it to build on `T: PartialOrd`, which is slightly more verbose but much more standard and avoided the problems.
This commit is contained in:
parent
c38b8a8c62
commit
4852291417
30 changed files with 166 additions and 119 deletions
|
@ -2,11 +2,13 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
use std::ops::{Deref, Range};
|
||||
use std::ptr;
|
||||
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_target::abi::{Align, HasDataLayout, Size};
|
||||
|
@ -47,6 +49,34 @@ pub struct Allocation<Tag = AllocId, Extra = ()> {
|
|||
pub extra: Extra,
|
||||
}
|
||||
|
||||
/// Interned types generally have an `Outer` type and an `Inner` type, where
|
||||
/// `Outer` is a newtype around `Interned<Inner>`, and all the operations are
|
||||
/// done on `Outer`, because all occurrences are interned. E.g. `Ty` is an
|
||||
/// outer type and `TyS` is its inner type.
|
||||
///
|
||||
/// Here things are different because only const allocations are interned. This
|
||||
/// means that both the inner type (`Allocation`) and the outer type
|
||||
/// (`ConstAllocation`) are used quite a bit.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)]
|
||||
#[cfg_attr(not(bootstrap), rustc_pass_by_value)]
|
||||
pub struct ConstAllocation<'tcx, Tag = AllocId, Extra = ()>(
|
||||
pub Interned<'tcx, Allocation<Tag, Extra>>,
|
||||
);
|
||||
|
||||
impl<'tcx> fmt::Debug for ConstAllocation<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// This matches how `Allocation` is printed. We print it like this to
|
||||
// avoid having to update expected output in a lot of tests.
|
||||
write!(f, "{:?}", self.inner())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Tag, Extra> ConstAllocation<'tcx, Tag, Extra> {
|
||||
pub fn inner(self) -> &'tcx Allocation<Tag, Extra> {
|
||||
self.0.0
|
||||
}
|
||||
}
|
||||
|
||||
/// We have our own error type that does not know about the `AllocId`; that information
|
||||
/// is added when converting to `InterpError`.
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -126,7 +126,8 @@ pub use self::error::{
|
|||
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar, ScalarMaybeUninit};
|
||||
|
||||
pub use self::allocation::{
|
||||
alloc_range, AllocRange, Allocation, InitChunk, InitChunkIter, InitMask, Relocations,
|
||||
alloc_range, AllocRange, Allocation, ConstAllocation, InitChunk, InitChunkIter, InitMask,
|
||||
Relocations,
|
||||
};
|
||||
|
||||
pub use self::pointer::{Pointer, PointerArithmetic, Provenance};
|
||||
|
@ -343,7 +344,7 @@ impl<'s> AllocDecodingSession<'s> {
|
|||
let alloc_id = decoder.with_position(pos, |decoder| {
|
||||
match alloc_kind {
|
||||
AllocDiscriminant::Alloc => {
|
||||
let alloc = <&'tcx Allocation as Decodable<_>>::decode(decoder);
|
||||
let alloc = <ConstAllocation<'tcx> as Decodable<_>>::decode(decoder);
|
||||
// We already have a reserved `AllocId`.
|
||||
let alloc_id = alloc_id.unwrap();
|
||||
trace!("decoded alloc {:?}: {:#?}", alloc_id, alloc);
|
||||
|
@ -387,14 +388,14 @@ pub enum GlobalAlloc<'tcx> {
|
|||
/// This is also used to break the cycle in recursive statics.
|
||||
Static(DefId),
|
||||
/// The alloc ID points to memory.
|
||||
Memory(&'tcx Allocation),
|
||||
Memory(ConstAllocation<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> GlobalAlloc<'tcx> {
|
||||
/// Panics if the `GlobalAlloc` does not refer to an `GlobalAlloc::Memory`
|
||||
#[track_caller]
|
||||
#[inline]
|
||||
pub fn unwrap_memory(&self) -> &'tcx Allocation {
|
||||
pub fn unwrap_memory(&self) -> ConstAllocation<'tcx> {
|
||||
match *self {
|
||||
GlobalAlloc::Memory(mem) => mem,
|
||||
_ => bug!("expected memory, got {:?}", self),
|
||||
|
@ -512,7 +513,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
/// Statics with identical content will still point to the same `Allocation`, i.e.,
|
||||
/// their data will be deduplicated through `Allocation` interning -- but they
|
||||
/// are different places in memory and as such need different IDs.
|
||||
pub fn create_memory_alloc(self, mem: &'tcx Allocation) -> AllocId {
|
||||
pub fn create_memory_alloc(self, mem: ConstAllocation<'tcx>) -> AllocId {
|
||||
let id = self.reserve_alloc_id();
|
||||
self.set_alloc_id_memory(id, mem);
|
||||
id
|
||||
|
@ -543,7 +544,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
|
||||
/// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to
|
||||
/// call this function twice, even with the same `Allocation` will ICE the compiler.
|
||||
pub fn set_alloc_id_memory(self, id: AllocId, mem: &'tcx Allocation) {
|
||||
pub fn set_alloc_id_memory(self, id: AllocId, mem: ConstAllocation<'tcx>) {
|
||||
if let Some(old) = self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Memory(mem)) {
|
||||
bug!("tried to set allocation ID {}, but it was already existing as {:#?}", id, old);
|
||||
}
|
||||
|
@ -551,7 +552,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
|
||||
/// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called
|
||||
/// twice for the same `(AllocId, Allocation)` pair.
|
||||
fn set_alloc_id_same_memory(self, id: AllocId, mem: &'tcx Allocation) {
|
||||
fn set_alloc_id_same_memory(self, id: AllocId, mem: ConstAllocation<'tcx>) {
|
||||
self.alloc_map.lock().alloc_map.insert_same(id, GlobalAlloc::Memory(mem));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
pub fn eval_static_initializer(
|
||||
self,
|
||||
def_id: DefId,
|
||||
) -> Result<&'tcx mir::Allocation, ErrorHandled> {
|
||||
) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled> {
|
||||
trace!("eval_static_initializer: Need to compute {:?}", def_id);
|
||||
assert!(self.is_static(def_id));
|
||||
let instance = ty::Instance::mono(self, def_id);
|
||||
|
@ -92,7 +92,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self,
|
||||
gid: GlobalId<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Result<&'tcx mir::Allocation, ErrorHandled> {
|
||||
) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled> {
|
||||
let param_env = param_env.with_const();
|
||||
trace!("eval_to_allocation: Need to compute {:?}", gid);
|
||||
let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?;
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_target::abi::{HasDataLayout, Size};
|
|||
use crate::ty::{Lift, ParamEnv, ScalarInt, Ty, TyCtxt};
|
||||
|
||||
use super::{
|
||||
AllocId, AllocRange, Allocation, InterpResult, Pointer, PointerArithmetic, Provenance,
|
||||
AllocId, AllocRange, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
|
||||
};
|
||||
|
||||
/// Represents the result of const evaluation via the `eval_to_allocation` query.
|
||||
|
@ -34,13 +34,13 @@ pub enum ConstValue<'tcx> {
|
|||
Scalar(Scalar),
|
||||
|
||||
/// Used only for `&[u8]` and `&str`
|
||||
Slice { data: &'tcx Allocation, start: usize, end: usize },
|
||||
Slice { data: ConstAllocation<'tcx>, start: usize, end: usize },
|
||||
|
||||
/// A value not represented/representable by `Scalar` or `Slice`
|
||||
ByRef {
|
||||
/// The backing memory of the value, may contain more memory than needed for just the value
|
||||
/// in order to share `Allocation`s between values
|
||||
alloc: &'tcx Allocation,
|
||||
/// in order to share `ConstAllocation`s between values
|
||||
alloc: ConstAllocation<'tcx>,
|
||||
/// Offset into `alloc`
|
||||
offset: Size,
|
||||
},
|
||||
|
@ -603,11 +603,12 @@ impl<'tcx, Tag: Provenance> ScalarMaybeUninit<Tag> {
|
|||
pub fn get_slice_bytes<'tcx>(cx: &impl HasDataLayout, val: ConstValue<'tcx>) -> &'tcx [u8] {
|
||||
if let ConstValue::Slice { data, start, end } = val {
|
||||
let len = end - start;
|
||||
data.get_bytes(
|
||||
cx,
|
||||
AllocRange { start: Size::from_bytes(start), size: Size::from_bytes(len) },
|
||||
)
|
||||
.unwrap_or_else(|err| bug!("const slice is invalid: {:?}", err))
|
||||
data.inner()
|
||||
.get_bytes(
|
||||
cx,
|
||||
AllocRange { start: Size::from_bytes(start), size: Size::from_bytes(len) },
|
||||
)
|
||||
.unwrap_or_else(|err| bug!("const slice is invalid: {:?}", err))
|
||||
} else {
|
||||
bug!("expected const slice, but found another const value");
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
|
||||
|
||||
use crate::mir::coverage::{CodeRegion, CoverageKind};
|
||||
use crate::mir::interpret::{Allocation, ConstValue, GlobalAlloc, Scalar};
|
||||
use crate::mir::interpret::{ConstAllocation, ConstValue, GlobalAlloc, Scalar};
|
||||
use crate::mir::visit::MirVisitable;
|
||||
use crate::ty::adjustment::PointerCast;
|
||||
use crate::ty::codec::{TyDecoder, TyEncoder};
|
||||
|
|
|
@ -12,7 +12,8 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::vec::Idx;
|
||||
use rustc_middle::mir::interpret::{
|
||||
read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, Pointer, Provenance,
|
||||
read_target_uint, AllocId, Allocation, ConstAllocation, ConstValue, GlobalAlloc, Pointer,
|
||||
Provenance,
|
||||
};
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::MirSource;
|
||||
|
@ -652,8 +653,10 @@ pub fn write_allocations<'tcx>(
|
|||
body: &Body<'_>,
|
||||
w: &mut dyn Write,
|
||||
) -> io::Result<()> {
|
||||
fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
|
||||
alloc.relocations().values().map(|id| *id)
|
||||
fn alloc_ids_from_alloc(
|
||||
alloc: ConstAllocation<'_>,
|
||||
) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
|
||||
alloc.inner().relocations().values().map(|id| *id)
|
||||
}
|
||||
fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
|
||||
match val {
|
||||
|
@ -686,14 +689,14 @@ pub fn write_allocations<'tcx>(
|
|||
let mut todo: Vec<_> = seen.iter().copied().collect();
|
||||
while let Some(id) = todo.pop() {
|
||||
let mut write_allocation_track_relocs =
|
||||
|w: &mut dyn Write, alloc: &Allocation| -> io::Result<()> {
|
||||
|w: &mut dyn Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
|
||||
// `.rev()` because we are popping them from the back of the `todo` vector.
|
||||
for id in alloc_ids_from_alloc(alloc).rev() {
|
||||
if seen.insert(id) {
|
||||
todo.push(id);
|
||||
}
|
||||
}
|
||||
write!(w, "{}", display_allocation(tcx, alloc))
|
||||
write!(w, "{}", display_allocation(tcx, alloc.inner()))
|
||||
};
|
||||
write!(w, "\n{}", id)?;
|
||||
match tcx.get_global_alloc(id) {
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::arena::ArenaAllocatable;
|
|||
use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos};
|
||||
use crate::mir::{
|
||||
self,
|
||||
interpret::{AllocId, Allocation},
|
||||
interpret::{AllocId, ConstAllocation},
|
||||
};
|
||||
use crate::thir;
|
||||
use crate::traits;
|
||||
|
@ -150,6 +150,12 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for ty::Const<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for ConstAllocation<'tcx> {
|
||||
fn encode(&self, e: &mut E) -> Result<(), E::Error> {
|
||||
self.inner().encode(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for AllocId {
|
||||
fn encode(&self, e: &mut E) -> Result<(), E::Error> {
|
||||
e.encode_alloc_id(self)
|
||||
|
@ -355,8 +361,8 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [ty::ValTree<'tcx>] {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for Allocation {
|
||||
fn decode(decoder: &mut D) -> &'tcx Self {
|
||||
impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for ConstAllocation<'tcx> {
|
||||
fn decode(decoder: &mut D) -> Self {
|
||||
decoder.tcx().intern_const_alloc(Decodable::decode(decoder))
|
||||
}
|
||||
}
|
||||
|
@ -399,7 +405,6 @@ impl_decodable_via_ref! {
|
|||
&'tcx ty::List<Ty<'tcx>>,
|
||||
&'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
|
||||
&'tcx traits::ImplSource<'tcx, ()>,
|
||||
&'tcx Allocation,
|
||||
&'tcx mir::Body<'tcx>,
|
||||
&'tcx mir::UnsafetyCheckResult,
|
||||
&'tcx mir::BorrowCheckResult<'tcx>,
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
|
|||
use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource};
|
||||
use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath};
|
||||
use crate::middle::stability;
|
||||
use crate::mir::interpret::{self, Allocation, ConstValue, Scalar};
|
||||
use crate::mir::interpret::{self, Allocation, ConstAllocation, ConstValue, Scalar};
|
||||
use crate::mir::{
|
||||
Body, BorrowCheckResult, Field, Local, Place, PlaceElem, ProjectionKind, Promoted,
|
||||
};
|
||||
|
@ -1653,22 +1653,6 @@ pub trait Lift<'tcx>: fmt::Debug {
|
|||
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted>;
|
||||
}
|
||||
|
||||
// Deprecated: we are in the process of converting all uses to `nop_lift`.
|
||||
macro_rules! nop_lift_old {
|
||||
($set:ident; $ty:ty => $lifted:ty) => {
|
||||
impl<'a, 'tcx> Lift<'tcx> for $ty {
|
||||
type Lifted = $lifted;
|
||||
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
|
||||
if tcx.interners.$set.contains_pointer_to(&InternedInSet(self)) {
|
||||
Some(unsafe { mem::transmute(self) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! nop_lift {
|
||||
($set:ident; $ty:ty => $lifted:ty) => {
|
||||
impl<'a, 'tcx> Lift<'tcx> for $ty {
|
||||
|
@ -1726,7 +1710,7 @@ macro_rules! nop_list_lift {
|
|||
nop_lift! {type_; Ty<'a> => Ty<'tcx>}
|
||||
nop_lift! {region; Region<'a> => Region<'tcx>}
|
||||
nop_lift! {const_; Const<'a> => Const<'tcx>}
|
||||
nop_lift_old! {const_allocation; &'a Allocation => &'tcx Allocation}
|
||||
nop_lift! {const_allocation; ConstAllocation<'a> => ConstAllocation<'tcx>}
|
||||
nop_lift! {predicate; Predicate<'a> => Predicate<'tcx>}
|
||||
|
||||
nop_list_lift! {poly_existential_predicates; ty::Binder<'a, ExistentialPredicate<'a>> => ty::Binder<'tcx, ExistentialPredicate<'tcx>>}
|
||||
|
@ -2161,6 +2145,7 @@ macro_rules! direct_interners {
|
|||
direct_interners! {
|
||||
region: mk_region(RegionKind): Region -> Region<'tcx>,
|
||||
const_: mk_const(ConstS<'tcx>): Const -> Const<'tcx>,
|
||||
const_allocation: intern_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>,
|
||||
}
|
||||
|
||||
macro_rules! direct_interners_old {
|
||||
|
@ -2201,7 +2186,6 @@ macro_rules! direct_interners_old {
|
|||
|
||||
// FIXME: eventually these should all be converted to `direct_interners`.
|
||||
direct_interners_old! {
|
||||
const_allocation: intern_const_alloc(Allocation),
|
||||
layout: intern_layout(Layout),
|
||||
adt_def: intern_adt_def(AdtDef),
|
||||
}
|
||||
|
|
|
@ -1267,7 +1267,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
Some(GlobalAlloc::Memory(alloc)) => {
|
||||
let len = int.assert_bits(self.tcx().data_layout.pointer_size);
|
||||
let range = AllocRange { start: offset, size: Size::from_bytes(len) };
|
||||
if let Ok(byte_str) = alloc.get_bytes(&self.tcx(), range) {
|
||||
if let Ok(byte_str) = alloc.inner().get_bytes(&self.tcx(), range) {
|
||||
p!(pretty_print_byte_str(byte_str))
|
||||
} else {
|
||||
p!("<too short allocation>")
|
||||
|
@ -1424,7 +1424,8 @@ pub trait PrettyPrinter<'tcx>:
|
|||
// The `inspect` here is okay since we checked the bounds, and there are
|
||||
// no relocations (we have an active slice reference here). We don't use
|
||||
// this result to affect interpreter execution.
|
||||
let byte_str = data.inspect_with_uninit_and_ptr_outside_interpreter(start..end);
|
||||
let byte_str =
|
||||
data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end);
|
||||
self.pretty_print_byte_str(byte_str)
|
||||
}
|
||||
(
|
||||
|
@ -1434,7 +1435,8 @@ pub trait PrettyPrinter<'tcx>:
|
|||
// The `inspect` here is okay since we checked the bounds, and there are no
|
||||
// relocations (we have an active `str` reference here). We don't use this
|
||||
// result to affect interpreter execution.
|
||||
let slice = data.inspect_with_uninit_and_ptr_outside_interpreter(start..end);
|
||||
let slice =
|
||||
data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end);
|
||||
p!(write("{:?}", String::from_utf8_lossy(slice)));
|
||||
Ok(self)
|
||||
}
|
||||
|
@ -1443,7 +1445,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
// cast is ok because we already checked for pointer size (32 or 64 bit) above
|
||||
let range = AllocRange { start: offset, size: Size::from_bytes(n) };
|
||||
|
||||
let byte_str = alloc.get_bytes(&self.tcx(), range).unwrap();
|
||||
let byte_str = alloc.inner().get_bytes(&self.tcx(), range).unwrap();
|
||||
p!("*");
|
||||
p!(pretty_print_byte_str(byte_str));
|
||||
Ok(self)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue