Skip to content

Commit d398ad3

Browse files
committed
Auto merge of #15380 - HKalbasi:mir, r=HKalbasi
Fix unsized struct problems in mir eval
2 parents 2f2cf21 + 6990d0f commit d398ad3

File tree

7 files changed

+220
-35
lines changed

7 files changed

+220
-35
lines changed

crates/hir-ty/src/consteval/tests.rs

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,25 @@ fn pattern_matching_ergonomics() {
11861186
);
11871187
}
11881188

1189+
#[test]
1190+
fn destructing_assignment() {
1191+
check_number(
1192+
r#"
1193+
//- minicore: add
1194+
const fn f(i: &mut u8) -> &mut u8 {
1195+
*i += 1;
1196+
i
1197+
}
1198+
const GOAL: u8 = {
1199+
let mut i = 4;
1200+
_ = f(&mut i);
1201+
i
1202+
};
1203+
"#,
1204+
5,
1205+
);
1206+
}
1207+
11891208
#[test]
11901209
fn let_else() {
11911210
check_number(
@@ -1745,6 +1764,24 @@ fn function_pointer_in_constants() {
17451764
);
17461765
}
17471766

1767+
#[test]
1768+
fn function_pointer_and_niche_optimization() {
1769+
check_number(
1770+
r#"
1771+
//- minicore: option
1772+
const GOAL: i32 = {
1773+
let f: fn(i32) -> i32 = |x| x + 2;
1774+
let init = Some(f);
1775+
match init {
1776+
Some(t) => t(3),
1777+
None => 222,
1778+
}
1779+
};
1780+
"#,
1781+
5,
1782+
);
1783+
}
1784+
17481785
#[test]
17491786
fn function_pointer() {
17501787
check_number(
@@ -2359,11 +2396,14 @@ fn const_loop() {
23592396
fn const_transfer_memory() {
23602397
check_number(
23612398
r#"
2362-
const A1: &i32 = &2;
2363-
const A2: &i32 = &5;
2364-
const GOAL: i32 = *A1 + *A2;
2399+
//- minicore: slice, index, coerce_unsized
2400+
const A1: &i32 = &1;
2401+
const A2: &i32 = &10;
2402+
const A3: [&i32; 3] = [&1, &2, &100];
2403+
const A4: (i32, &i32) = (1, &1000);
2404+
const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1;
23652405
"#,
2366-
7,
2406+
1111,
23672407
);
23682408
}
23692409

@@ -2634,9 +2674,9 @@ fn exec_limits() {
26342674
}
26352675
sum
26362676
}
2637-
const GOAL: i32 = f(10000);
2677+
const GOAL: i32 = f(1000);
26382678
"#,
2639-
10000 * 10000,
2679+
1000 * 1000,
26402680
);
26412681
}
26422682

@@ -2683,27 +2723,27 @@ fn unsized_field() {
26832723
//- minicore: coerce_unsized, index, slice, transmute
26842724
use core::mem::transmute;
26852725
2686-
struct Slice([u8]);
2726+
struct Slice([usize]);
26872727
struct Slice2(Slice);
26882728
26892729
impl Slice2 {
26902730
fn as_inner(&self) -> &Slice {
26912731
&self.0
26922732
}
26932733
2694-
fn as_bytes(&self) -> &[u8] {
2734+
fn as_bytes(&self) -> &[usize] {
26952735
&self.as_inner().0
26962736
}
26972737
}
26982738
2699-
const GOAL: u8 = unsafe {
2700-
let x: &[u8] = &[1, 2, 3];
2739+
const GOAL: usize = unsafe {
2740+
let x: &[usize] = &[1, 2, 3];
27012741
let x: &Slice2 = transmute(x);
27022742
let x = x.as_bytes();
2703-
x[0] + x[1] + x[2]
2743+
x[0] + x[1] + x[2] + x.len() * 100
27042744
};
27052745
"#,
2706-
6,
2746+
306,
27072747
);
27082748
}
27092749

crates/hir-ty/src/consteval/tests/intrinsics.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,28 @@ fn wrapping_add() {
251251
);
252252
}
253253

254+
#[test]
255+
fn ptr_offset_from() {
256+
check_number(
257+
r#"
258+
//- minicore: index, slice, coerce_unsized
259+
extern "rust-intrinsic" {
260+
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
261+
pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;
262+
}
263+
264+
const GOAL: isize = {
265+
let x = [1, 2, 3, 4, 5i32];
266+
let r1 = -ptr_offset_from(&x[0], &x[4]);
267+
let r2 = ptr_offset_from(&x[3], &x[1]);
268+
let r3 = ptr_offset_from_unsigned(&x[3], &x[0]) as isize;
269+
r3 * 100 + r2 * 10 + r1
270+
};
271+
"#,
272+
324,
273+
);
274+
}
275+
254276
#[test]
255277
fn saturating() {
256278
check_number(

crates/hir-ty/src/mir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ impl Place {
234234
self.local == child.local && child.projection.starts_with(&self.projection)
235235
}
236236

237+
/// The place itself is not included
237238
fn iterate_over_parents(&self) -> impl Iterator<Item = Place> + '_ {
238239
(0..self.projection.len())
239240
.map(|x| &self.projection[0..x])

crates/hir-ty/src/mir/eval.rs

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,22 @@ pub struct VTableMap {
6868
}
6969

7070
impl VTableMap {
71+
const OFFSET: usize = 1000; // We should add some offset to ids to make 0 (null) an invalid id.
72+
7173
fn id(&mut self, ty: Ty) -> usize {
7274
if let Some(it) = self.ty_to_id.get(&ty) {
7375
return *it;
7476
}
75-
let id = self.id_to_ty.len();
77+
let id = self.id_to_ty.len() + VTableMap::OFFSET;
7678
self.id_to_ty.push(ty.clone());
7779
self.ty_to_id.insert(ty, id);
7880
id
7981
}
8082

8183
pub(crate) fn ty(&self, id: usize) -> Result<&Ty> {
82-
self.id_to_ty.get(id).ok_or(MirEvalError::InvalidVTableId(id))
84+
id.checked_sub(VTableMap::OFFSET)
85+
.and_then(|id| self.id_to_ty.get(id))
86+
.ok_or(MirEvalError::InvalidVTableId(id))
8387
}
8488

8589
fn ty_of_bytes(&self, bytes: &[u8]) -> Result<&Ty> {
@@ -467,6 +471,10 @@ impl DropFlags {
467471

468472
fn remove_place(&mut self, p: &Place) -> bool {
469473
// FIXME: replace parents with parts
474+
if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(&it)) {
475+
self.need_drop.remove(&parent);
476+
return true;
477+
}
470478
self.need_drop.remove(p)
471479
}
472480
}
@@ -511,6 +519,11 @@ pub fn interpret_mir(
511519
)
512520
}
513521

522+
#[cfg(test)]
523+
const EXECUTION_LIMIT: usize = 100_000;
524+
#[cfg(not(test))]
525+
const EXECUTION_LIMIT: usize = 10_000_000;
526+
514527
impl Evaluator<'_> {
515528
pub fn new<'a>(
516529
db: &'a dyn HirDatabase,
@@ -534,7 +547,7 @@ impl Evaluator<'_> {
534547
stderr: vec![],
535548
assert_placeholder_ty_is_unused,
536549
stack_depth_limit: 100,
537-
execution_limit: 1000_000,
550+
execution_limit: EXECUTION_LIMIT,
538551
memory_limit: 1000_000_000, // 2GB, 1GB for stack and 1GB for heap
539552
layout_cache: RefCell::new(HashMap::default()),
540553
}
@@ -683,8 +696,10 @@ impl Evaluator<'_> {
683696
.offset(u32::from(f.local_id.into_raw()) as usize)
684697
.bytes_usize();
685698
addr = addr.offset(offset);
686-
// FIXME: support structs with unsized fields
687-
metadata = None;
699+
// Unsized field metadata is equal to the metadata of the struct
700+
if self.size_align_of(&ty, locals)?.is_some() {
701+
metadata = None;
702+
}
688703
}
689704
ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"),
690705
}
@@ -1803,6 +1818,17 @@ impl Evaluator<'_> {
18031818
}
18041819
}
18051820
}
1821+
chalk_ir::TyKind::Array(inner, len) => {
1822+
let len = match try_const_usize(this.db, &len) {
1823+
Some(it) => it as usize,
1824+
None => not_supported!("non evaluatable array len in patching addresses"),
1825+
};
1826+
let size = this.size_of_sized(inner, locals, "inner of array")?;
1827+
for i in 0..len {
1828+
let offset = i * size;
1829+
rec(this, &bytes[offset..offset + size], inner, locals, mm)?;
1830+
}
1831+
}
18061832
chalk_ir::TyKind::Tuple(_, subst) => {
18071833
let layout = this.layout(ty)?;
18081834
for (id, ty) in subst.iter(Interner).enumerate() {
@@ -1911,10 +1937,31 @@ impl Evaluator<'_> {
19111937
AdtId::UnionId(_) => (),
19121938
AdtId::EnumId(_) => (),
19131939
},
1940+
TyKind::Tuple(_, subst) => {
1941+
for (id, ty) in subst.iter(Interner).enumerate() {
1942+
let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
1943+
let offset = layout.fields.offset(id).bytes_usize();
1944+
self.patch_addresses(patch_map, old_vtable, addr.offset(offset), ty, locals)?;
1945+
}
1946+
}
1947+
TyKind::Array(inner, len) => {
1948+
let len = match try_const_usize(self.db, &len) {
1949+
Some(it) => it as usize,
1950+
None => not_supported!("non evaluatable array len in patching addresses"),
1951+
};
1952+
let size = self.size_of_sized(inner, locals, "inner of array")?;
1953+
for i in 0..len {
1954+
self.patch_addresses(
1955+
patch_map,
1956+
old_vtable,
1957+
addr.offset(i * size),
1958+
inner,
1959+
locals,
1960+
)?;
1961+
}
1962+
}
19141963
TyKind::AssociatedType(_, _)
19151964
| TyKind::Scalar(_)
1916-
| TyKind::Tuple(_, _)
1917-
| TyKind::Array(_, _)
19181965
| TyKind::Slice(_)
19191966
| TyKind::Raw(_, _)
19201967
| TyKind::OpaqueType(_, _)

crates/hir-ty/src/mir/eval/shim.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -694,12 +694,15 @@ impl Evaluator<'_> {
694694
else {
695695
return Err(MirEvalError::TypeError("type_name generic arg is not provided"));
696696
};
697-
let Ok(ty_name) = ty.display_source_code(
697+
let ty_name = match ty.display_source_code(
698698
self.db,
699699
locals.body.owner.module(self.db.upcast()),
700700
true,
701-
) else {
702-
not_supported!("fail in generating type_name using source code display");
701+
) {
702+
Ok(ty_name) => ty_name,
703+
// Fallback to human readable display in case of `Err`. Ideally we want to use `display_source_code` to
704+
// render full paths.
705+
Err(_) => ty.display(self.db).to_string(),
703706
};
704707
let len = ty_name.len();
705708
let addr = self.heap_allocate(len, 1)?;
@@ -755,7 +758,22 @@ impl Evaluator<'_> {
755758
let ans = lhs.wrapping_add(rhs);
756759
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
757760
}
758-
"wrapping_sub" | "unchecked_sub" | "ptr_offset_from_unsigned" | "ptr_offset_from" => {
761+
"ptr_offset_from_unsigned" | "ptr_offset_from" => {
762+
let [lhs, rhs] = args else {
763+
return Err(MirEvalError::TypeError("wrapping_sub args are not provided"));
764+
};
765+
let lhs = i128::from_le_bytes(pad16(lhs.get(self)?, false));
766+
let rhs = i128::from_le_bytes(pad16(rhs.get(self)?, false));
767+
let ans = lhs.wrapping_sub(rhs);
768+
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
769+
else {
770+
return Err(MirEvalError::TypeError("ptr_offset_from generic arg is not provided"));
771+
};
772+
let size = self.size_of_sized(ty, locals, "ptr_offset_from arg")? as i128;
773+
let ans = ans / size;
774+
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
775+
}
776+
"wrapping_sub" | "unchecked_sub" => {
759777
let [lhs, rhs] = args else {
760778
return Err(MirEvalError::TypeError("wrapping_sub args are not provided"));
761779
};

crates/hir-ty/src/mir/eval/tests.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,50 @@ fn main() {
182182
);
183183
}
184184

185+
#[test]
186+
fn drop_struct_field() {
187+
check_pass(
188+
r#"
189+
//- minicore: drop, add, option, cell, builtin_impls
190+
191+
use core::cell::Cell;
192+
193+
fn should_not_reach() {
194+
_ // FIXME: replace this function with panic when that works
195+
}
196+
197+
struct X<'a>(&'a Cell<i32>);
198+
impl<'a> Drop for X<'a> {
199+
fn drop(&mut self) {
200+
self.0.set(self.0.get() + 1)
201+
}
202+
}
203+
204+
struct Tuple<'a>(X<'a>, X<'a>, X<'a>);
205+
206+
fn main() {
207+
let s = Cell::new(0);
208+
{
209+
let x0 = X(&s);
210+
let xt = Tuple(x0, X(&s), X(&s));
211+
let x1 = xt.1;
212+
if s.get() != 0 {
213+
should_not_reach();
214+
}
215+
drop(xt.0);
216+
if s.get() != 1 {
217+
should_not_reach();
218+
}
219+
}
220+
// FIXME: this should be 3
221+
if s.get() != 2 {
222+
should_not_reach();
223+
}
224+
}
225+
"#,
226+
);
227+
}
228+
185229
#[test]
186230
fn drop_in_place() {
187231
check_pass(

0 commit comments

Comments
 (0)