Skip to content

Commit fcc4d8c

Browse files
committed
include refs in valtree creation
1 parent 51aa3f8 commit fcc4d8c

File tree

3 files changed

+158
-29
lines changed

3 files changed

+158
-29
lines changed

compiler/rustc_const_eval/src/const_eval/mod.rs

Lines changed: 113 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ use rustc_hir::Mutability;
66
use rustc_middle::ty::{self, TyCtxt};
77
use rustc_middle::{
88
mir::{self, interpret::ConstAlloc},
9-
ty::ScalarInt,
9+
ty::{ScalarInt, Ty},
1010
};
1111
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
12+
use rustc_target::abi::VariantIdx;
1213

1314
use crate::interpret::{
1415
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MPlaceTy,
@@ -55,28 +56,43 @@ pub(crate) fn const_to_valtree<'tcx>(
5556
const_to_valtree_inner(&ecx, &place)
5657
}
5758

59+
#[instrument(skip(ecx), level = "debug")]
60+
fn branches<'tcx>(
61+
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
62+
place: &MPlaceTy<'tcx>,
63+
n: usize,
64+
variant: Option<VariantIdx>,
65+
) -> Option<ty::ValTree<'tcx>> {
66+
let place = match variant {
67+
Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
68+
None => *place,
69+
};
70+
let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
71+
debug!(?place, ?variant);
72+
73+
let fields = (0..n).map(|i| {
74+
let field = ecx.mplace_field(&place, i).unwrap();
75+
const_to_valtree_inner(ecx, &field)
76+
});
77+
// For enums, we preped their variant index before the variant's fields so we can figure out
78+
// the variant again when just seeing a valtree.
79+
let branches = variant.into_iter().chain(fields);
80+
Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
81+
}
82+
83+
#[instrument(skip(ecx), level = "debug")]
5884
fn const_to_valtree_inner<'tcx>(
5985
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
6086
place: &MPlaceTy<'tcx>,
6187
) -> Option<ty::ValTree<'tcx>> {
62-
let branches = |n, variant| {
63-
let place = match variant {
64-
Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
65-
None => *place,
66-
};
67-
let variant =
68-
variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
69-
let fields = (0..n).map(|i| {
70-
let field = ecx.mplace_field(&place, i).unwrap();
71-
const_to_valtree_inner(ecx, &field)
72-
});
73-
// For enums, we preped their variant index before the variant's fields so we can figure out
74-
// the variant again when just seeing a valtree.
75-
let branches = variant.into_iter().chain(fields);
76-
Some(ty::ValTree::Branch(
77-
ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?),
78-
))
88+
// We only want to use raw bytes in ValTrees for string slices or &[<integer_ty>]
89+
let use_bytes_for_ref = |ty: Ty<'tcx>| -> bool {
90+
match ty.kind() {
91+
ty::Str | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Bool => true,
92+
_ => false,
93+
}
7994
};
95+
8096
match place.layout.ty.kind() {
8197
ty::FnDef(..) => Some(ty::ValTree::zst()),
8298
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
@@ -90,19 +106,91 @@ fn const_to_valtree_inner<'tcx>(
90106
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
91107
// agree with runtime equality tests.
92108
ty::FnPtr(_) | ty::RawPtr(_) => None,
93-
ty::Ref(..) => unimplemented!("need to use deref_const"),
109+
110+
ty::Ref(_, ref_ty, _) if place.layout.ty.is_slice() => {
111+
match ecx.try_read_immediate_from_mplace(&place) {
112+
Ok(Some(imm)) => {
113+
// `imm` is a ScalarPair. We try to get the underlying bytes behind that
114+
// fat pointer for string slices and slices of integer types. For any other
115+
// slice types we use `branches` to recursively construct the Valtree.
116+
117+
if use_bytes_for_ref(*ref_ty) {
118+
let (alloc, range) = ecx.get_alloc_from_imm_scalar_pair(imm);
119+
let alloc_bytes = match alloc.get_bytes(&ecx.tcx, range) {
120+
Ok(bytes) => bytes,
121+
Err(_e) => return None,
122+
};
123+
debug!(?alloc_bytes);
124+
125+
let bytes = ecx.tcx.arena.alloc_slice(alloc_bytes);
126+
let len = bytes.len();
127+
debug!(?bytes, ?len);
128+
129+
let slice = ty::ValSlice { bytes};
130+
131+
Some(ty::ValTree::SliceOrStr(slice))
132+
} else {
133+
let derefd = ecx.deref_operand(&imm.into()).expect(&format!("couldnt deref {:?}", imm));
134+
debug!("derefd: {:?}", derefd);
135+
136+
let derefd_imm = match ecx.try_read_immediate_from_mplace(&derefd) {
137+
Ok(Some(imm)) => imm,
138+
_ => return None,
139+
};
140+
debug!(?derefd_imm);
141+
142+
let tcx = ecx.tcx.tcx;
143+
let scalar_len= derefd.meta.unwrap_meta();
144+
let len = match scalar_len {
145+
Scalar::Int(int) => {
146+
int.try_to_machine_usize(tcx).expect(&format!("Expected a valid ScalarInt in {:?}", scalar_len))
147+
}
148+
_ => bug!("expected a ScalarInt in meta data for {:?}", place),
149+
};
150+
debug!(?len);
151+
152+
let valtree = branches(ecx, place, len.try_into().expect("BLA"), None);
153+
debug!(?valtree);
154+
155+
valtree
156+
}
157+
}
158+
_ => {
159+
None
160+
}
161+
}
162+
}
163+
164+
ty::Ref(_, inner_ty, _) => {
165+
debug!("Ref with inner_ty: {:?}", inner_ty);
166+
let imm = ecx.try_read_immediate_from_mplace(&place).unwrap_or_else(|e| bug!("couldnt read immediate from {:?}, error: {:?}", place, e));
167+
match imm {
168+
Some(imm) => {
169+
debug!(?imm);
170+
171+
let derefd_place = ecx.deref_mplace(place).unwrap_or_else(|e| bug!("couldn't deref {:?}, error: {:?}", place, e));
172+
debug!(?derefd_place);
173+
174+
const_to_valtree_inner(ecx, &derefd_place)
175+
}
176+
None => None,
177+
}
178+
}
179+
ty::Str => {
180+
bug!("ty::Str should have been handled in ty::Ref branch that uses raw bytes");
181+
}
182+
ty::Slice(_) => {
183+
bug!("should have been handled in the Ref arm");
184+
}
94185

95186
// Trait objects are not allowed in type level constants, as we have no concept for
96187
// resolving their backing type, even if we can do that at const eval time. We may
97188
// hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
98189
// but it is unclear if this is useful.
99190
ty::Dynamic(..) => None,
100191

101-
ty::Slice(_) | ty::Str => {
102-
unimplemented!("need to find the backing data of the slice/str and recurse on that")
103-
}
104-
ty::Tuple(substs) => branches(substs.len(), None),
105-
ty::Array(_, len) => branches(usize::try_from(len.eval_usize(ecx.tcx.tcx, ecx.param_env)).unwrap(), None),
192+
ty::Tuple(substs) => branches(ecx, place, substs.len(), None),
193+
ty::Array(_, len) => branches(ecx, place, usize::try_from(len.eval_usize(ecx.tcx.tcx, ecx.param_env)).unwrap(), None),
106194

107195
ty::Adt(def, _) => {
108196
if def.variants().is_empty() {
@@ -111,7 +199,7 @@ fn const_to_valtree_inner<'tcx>(
111199

112200
let variant = ecx.read_discriminant(&place.into()).unwrap().1;
113201

114-
branches(def.variant(variant).fields.len(), def.is_enum().then_some(variant))
202+
branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant))
115203
}
116204

117205
ty::Never

compiler/rustc_const_eval/src/interpret/operand.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ use rustc_target::abi::{Abi, HasDataLayout, Size, TagEncoding};
1414
use rustc_target::abi::{VariantIdx, Variants};
1515

1616
use super::{
17-
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, GlobalId,
18-
InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, Provenance,
19-
Scalar, ScalarMaybeUninit,
17+
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, AllocRange, Allocation,
18+
ConstValue, GlobalId, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, Place, PlaceTy,
19+
Pointer, Provenance, Scalar, ScalarMaybeUninit,
2020
};
2121

2222
/// An `Immediate` represents a single immediate self-contained Rust value.
@@ -248,7 +248,7 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
248248
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
249249
/// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
250250
/// Returns `None` if the layout does not permit loading this as a value.
251-
fn try_read_immediate_from_mplace(
251+
pub(crate) fn try_read_immediate_from_mplace(
252252
&self,
253253
mplace: &MPlaceTy<'tcx, M::PointerTag>,
254254
) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::PointerTag>>> {
@@ -777,3 +777,32 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
777777
})
778778
}
779779
}
780+
781+
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx, PointerTag = AllocId>> InterpCx<'mir, 'tcx, M> {
782+
pub fn get_alloc_from_imm_scalar_pair(
783+
&self,
784+
imm: ImmTy<'tcx, M::PointerTag>,
785+
) -> (&Allocation, AllocRange) {
786+
match imm.imm {
787+
Immediate::ScalarPair(a, b) => {
788+
// We know `offset` is relative to the allocation, so we can use `into_parts`.
789+
let (data, start) = match self.scalar_to_ptr(a.check_init().unwrap()).into_parts() {
790+
(Some(alloc_id), offset) => {
791+
(self.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes())
792+
}
793+
(None, _offset) => (
794+
self.tcx.intern_const_alloc(Allocation::from_bytes_byte_aligned_immutable(
795+
b"" as &[u8],
796+
)),
797+
0,
798+
),
799+
};
800+
let len = b.to_machine_usize(self).unwrap();
801+
let size = Size::from_bytes(len);
802+
let start = Size::from_bytes(start);
803+
(data.inner(), AllocRange { start, size })
804+
}
805+
_ => bug!("{:?} not a ScalarPair", imm),
806+
}
807+
}
808+
}

compiler/rustc_const_eval/src/interpret/place.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,18 @@ where
305305
Ok(mplace)
306306
}
307307

308+
#[instrument(skip(self), level = "debug")]
309+
pub fn deref_mplace(
310+
&self,
311+
src: &MPlaceTy<'tcx, M::PointerTag>,
312+
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
313+
let val = self.try_read_immediate_from_mplace(src)?;
314+
let mplace = self.ref_to_mplace(&val.unwrap())?;
315+
self.check_mplace_access(mplace, CheckInAllocMsg::DerefTest)?;
316+
317+
Ok(mplace)
318+
}
319+
308320
#[inline]
309321
pub(super) fn get_alloc(
310322
&self,

0 commit comments

Comments
 (0)