Skip to content

Commit

Permalink
Auto merge of #15380 - HKalbasi:mir, r=HKalbasi
Browse files Browse the repository at this point in the history
Fix unsized struct problems in mir eval
  • Loading branch information
bors committed Aug 2, 2023
2 parents 2f2cf21 + 6990d0f commit d398ad3
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 35 deletions.
64 changes: 52 additions & 12 deletions crates/hir-ty/src/consteval/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,25 @@ fn pattern_matching_ergonomics() {
);
}

#[test]
fn destructing_assignment() {
check_number(
r#"
//- minicore: add
const fn f(i: &mut u8) -> &mut u8 {
*i += 1;
i
}
const GOAL: u8 = {
let mut i = 4;
_ = f(&mut i);
i
};
"#,
5,
);
}

#[test]
fn let_else() {
check_number(
Expand Down Expand Up @@ -1745,6 +1764,24 @@ fn function_pointer_in_constants() {
);
}

#[test]
fn function_pointer_and_niche_optimization() {
check_number(
r#"
//- minicore: option
const GOAL: i32 = {
let f: fn(i32) -> i32 = |x| x + 2;
let init = Some(f);
match init {
Some(t) => t(3),
None => 222,
}
};
"#,
5,
);
}

#[test]
fn function_pointer() {
check_number(
Expand Down Expand Up @@ -2359,11 +2396,14 @@ fn const_loop() {
fn const_transfer_memory() {
check_number(
r#"
const A1: &i32 = &2;
const A2: &i32 = &5;
const GOAL: i32 = *A1 + *A2;
//- minicore: slice, index, coerce_unsized
const A1: &i32 = &1;
const A2: &i32 = &10;
const A3: [&i32; 3] = [&1, &2, &100];
const A4: (i32, &i32) = (1, &1000);
const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1;
"#,
7,
1111,
);
}

Expand Down Expand Up @@ -2634,9 +2674,9 @@ fn exec_limits() {
}
sum
}
const GOAL: i32 = f(10000);
const GOAL: i32 = f(1000);
"#,
10000 * 10000,
1000 * 1000,
);
}

Expand Down Expand Up @@ -2683,27 +2723,27 @@ fn unsized_field() {
//- minicore: coerce_unsized, index, slice, transmute
use core::mem::transmute;
struct Slice([u8]);
struct Slice([usize]);
struct Slice2(Slice);
impl Slice2 {
fn as_inner(&self) -> &Slice {
&self.0
}
fn as_bytes(&self) -> &[u8] {
fn as_bytes(&self) -> &[usize] {
&self.as_inner().0
}
}
const GOAL: u8 = unsafe {
let x: &[u8] = &[1, 2, 3];
const GOAL: usize = unsafe {
let x: &[usize] = &[1, 2, 3];
let x: &Slice2 = transmute(x);
let x = x.as_bytes();
x[0] + x[1] + x[2]
x[0] + x[1] + x[2] + x.len() * 100
};
"#,
6,
306,
);
}

Expand Down
22 changes: 22 additions & 0 deletions crates/hir-ty/src/consteval/tests/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,28 @@ fn wrapping_add() {
);
}

#[test]
fn ptr_offset_from() {
check_number(
r#"
//- minicore: index, slice, coerce_unsized
extern "rust-intrinsic" {
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;
}
const GOAL: isize = {
let x = [1, 2, 3, 4, 5i32];
let r1 = -ptr_offset_from(&x[0], &x[4]);
let r2 = ptr_offset_from(&x[3], &x[1]);
let r3 = ptr_offset_from_unsigned(&x[3], &x[0]) as isize;
r3 * 100 + r2 * 10 + r1
};
"#,
324,
);
}

#[test]
fn saturating() {
check_number(
Expand Down
1 change: 1 addition & 0 deletions crates/hir-ty/src/mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ impl Place {
self.local == child.local && child.projection.starts_with(&self.projection)
}

/// The place itself is not included
fn iterate_over_parents(&self) -> impl Iterator<Item = Place> + '_ {
(0..self.projection.len())
.map(|x| &self.projection[0..x])
Expand Down
61 changes: 54 additions & 7 deletions crates/hir-ty/src/mir/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,22 @@ pub struct VTableMap {
}

impl VTableMap {
const OFFSET: usize = 1000; // We should add some offset to ids to make 0 (null) an invalid id.

fn id(&mut self, ty: Ty) -> usize {
if let Some(it) = self.ty_to_id.get(&ty) {
return *it;
}
let id = self.id_to_ty.len();
let id = self.id_to_ty.len() + VTableMap::OFFSET;
self.id_to_ty.push(ty.clone());
self.ty_to_id.insert(ty, id);
id
}

pub(crate) fn ty(&self, id: usize) -> Result<&Ty> {
self.id_to_ty.get(id).ok_or(MirEvalError::InvalidVTableId(id))
id.checked_sub(VTableMap::OFFSET)
.and_then(|id| self.id_to_ty.get(id))
.ok_or(MirEvalError::InvalidVTableId(id))
}

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

fn remove_place(&mut self, p: &Place) -> bool {
// FIXME: replace parents with parts
if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(&it)) {
self.need_drop.remove(&parent);
return true;
}
self.need_drop.remove(p)
}
}
Expand Down Expand Up @@ -511,6 +519,11 @@ pub fn interpret_mir(
)
}

#[cfg(test)]
const EXECUTION_LIMIT: usize = 100_000;
#[cfg(not(test))]
const EXECUTION_LIMIT: usize = 10_000_000;

impl Evaluator<'_> {
pub fn new<'a>(
db: &'a dyn HirDatabase,
Expand All @@ -534,7 +547,7 @@ impl Evaluator<'_> {
stderr: vec![],
assert_placeholder_ty_is_unused,
stack_depth_limit: 100,
execution_limit: 1000_000,
execution_limit: EXECUTION_LIMIT,
memory_limit: 1000_000_000, // 2GB, 1GB for stack and 1GB for heap
layout_cache: RefCell::new(HashMap::default()),
}
Expand Down Expand Up @@ -683,8 +696,10 @@ impl Evaluator<'_> {
.offset(u32::from(f.local_id.into_raw()) as usize)
.bytes_usize();
addr = addr.offset(offset);
// FIXME: support structs with unsized fields
metadata = None;
// Unsized field metadata is equal to the metadata of the struct
if self.size_align_of(&ty, locals)?.is_some() {
metadata = None;
}
}
ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"),
}
Expand Down Expand Up @@ -1803,6 +1818,17 @@ impl Evaluator<'_> {
}
}
}
chalk_ir::TyKind::Array(inner, len) => {
let len = match try_const_usize(this.db, &len) {
Some(it) => it as usize,
None => not_supported!("non evaluatable array len in patching addresses"),
};
let size = this.size_of_sized(inner, locals, "inner of array")?;
for i in 0..len {
let offset = i * size;
rec(this, &bytes[offset..offset + size], inner, locals, mm)?;
}
}
chalk_ir::TyKind::Tuple(_, subst) => {
let layout = this.layout(ty)?;
for (id, ty) in subst.iter(Interner).enumerate() {
Expand Down Expand Up @@ -1911,10 +1937,31 @@ impl Evaluator<'_> {
AdtId::UnionId(_) => (),
AdtId::EnumId(_) => (),
},
TyKind::Tuple(_, subst) => {
for (id, ty) in subst.iter(Interner).enumerate() {
let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
let offset = layout.fields.offset(id).bytes_usize();
self.patch_addresses(patch_map, old_vtable, addr.offset(offset), ty, locals)?;
}
}
TyKind::Array(inner, len) => {
let len = match try_const_usize(self.db, &len) {
Some(it) => it as usize,
None => not_supported!("non evaluatable array len in patching addresses"),
};
let size = self.size_of_sized(inner, locals, "inner of array")?;
for i in 0..len {
self.patch_addresses(
patch_map,
old_vtable,
addr.offset(i * size),
inner,
locals,
)?;
}
}
TyKind::AssociatedType(_, _)
| TyKind::Scalar(_)
| TyKind::Tuple(_, _)
| TyKind::Array(_, _)
| TyKind::Slice(_)
| TyKind::Raw(_, _)
| TyKind::OpaqueType(_, _)
Expand Down
26 changes: 22 additions & 4 deletions crates/hir-ty/src/mir/eval/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -694,12 +694,15 @@ impl Evaluator<'_> {
else {
return Err(MirEvalError::TypeError("type_name generic arg is not provided"));
};
let Ok(ty_name) = ty.display_source_code(
let ty_name = match ty.display_source_code(
self.db,
locals.body.owner.module(self.db.upcast()),
true,
) else {
not_supported!("fail in generating type_name using source code display");
) {
Ok(ty_name) => ty_name,
// Fallback to human readable display in case of `Err`. Ideally we want to use `display_source_code` to
// render full paths.
Err(_) => ty.display(self.db).to_string(),
};
let len = ty_name.len();
let addr = self.heap_allocate(len, 1)?;
Expand Down Expand Up @@ -755,7 +758,22 @@ impl Evaluator<'_> {
let ans = lhs.wrapping_add(rhs);
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
}
"wrapping_sub" | "unchecked_sub" | "ptr_offset_from_unsigned" | "ptr_offset_from" => {
"ptr_offset_from_unsigned" | "ptr_offset_from" => {
let [lhs, rhs] = args else {
return Err(MirEvalError::TypeError("wrapping_sub args are not provided"));
};
let lhs = i128::from_le_bytes(pad16(lhs.get(self)?, false));
let rhs = i128::from_le_bytes(pad16(rhs.get(self)?, false));
let ans = lhs.wrapping_sub(rhs);
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError("ptr_offset_from generic arg is not provided"));
};
let size = self.size_of_sized(ty, locals, "ptr_offset_from arg")? as i128;
let ans = ans / size;
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
}
"wrapping_sub" | "unchecked_sub" => {
let [lhs, rhs] = args else {
return Err(MirEvalError::TypeError("wrapping_sub args are not provided"));
};
Expand Down
44 changes: 44 additions & 0 deletions crates/hir-ty/src/mir/eval/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,50 @@ fn main() {
);
}

#[test]
fn drop_struct_field() {
check_pass(
r#"
//- minicore: drop, add, option, cell, builtin_impls
use core::cell::Cell;
fn should_not_reach() {
_ // FIXME: replace this function with panic when that works
}
struct X<'a>(&'a Cell<i32>);
impl<'a> Drop for X<'a> {
fn drop(&mut self) {
self.0.set(self.0.get() + 1)
}
}
struct Tuple<'a>(X<'a>, X<'a>, X<'a>);
fn main() {
let s = Cell::new(0);
{
let x0 = X(&s);
let xt = Tuple(x0, X(&s), X(&s));
let x1 = xt.1;
if s.get() != 0 {
should_not_reach();
}
drop(xt.0);
if s.get() != 1 {
should_not_reach();
}
}
// FIXME: this should be 3
if s.get() != 2 {
should_not_reach();
}
}
"#,
);
}

#[test]
fn drop_in_place() {
check_pass(
Expand Down

0 comments on commit d398ad3

Please sign in to comment.