Unnamed repository; edit this file 'description' to name the repository.
Recursively patch addresses of slices
Lukas Wirth 10 days ago
parent 03259f1 · commit 2313390
-rw-r--r--crates/hir-ty/src/mir/eval.rs28
-rw-r--r--crates/hir-ty/src/mir/eval/tests.rs103
2 files changed, 127 insertions, 4 deletions
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index e968da5111..b083816034 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -2548,7 +2548,6 @@ impl<'a, 'db> Evaluator<'a, 'db> {
ty: Ty<'db>,
locals: &Locals<'a, 'db>,
) -> Result<'db, ()> {
- // FIXME: support indirect references
let layout = self.layout(ty)?;
let my_size = self.size_of_sized(ty, locals, "value to patch address")?;
use rustc_type_ir::TyKind;
@@ -2574,9 +2573,30 @@ impl<'a, 'db> Evaluator<'a, 'db> {
)?;
}
None => {
- let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?);
- if let Some(it) = patch_map.get(&current) {
- self.write_memory(addr, &it.to_le_bytes())?;
+ let bytes = self.read_memory(addr, my_size)?;
+ let (current, metadata) = bytes.split_at(my_size / 2);
+ let metadata = metadata.to_vec();
+ let current = from_bytes!(usize, current);
+ let patched = match patch_map.get(&current) {
+ Some(it) => {
+ self.write_memory(addr, &it.to_le_bytes())?;
+ *it
+ }
+ None => current,
+ };
+ let patched = Address::from_usize(patched);
+ if let TyKind::Slice(inner) = t.kind() {
+ let len = from_bytes!(usize, metadata);
+ let size = self.size_of_sized(inner, locals, "slice item to patch")?;
+ for i in 0..len {
+ self.patch_addresses(
+ patch_map,
+ ty_of_bytes,
+ patched.offset(i * size),
+ inner,
+ locals,
+ )?;
+ }
}
}
}
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index 68d19769d4..7431ac8293 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -1115,6 +1115,109 @@ fn main() {
}
#[test]
+fn slice_get_unchecked_intrinsic() {
+ check_pass(
+ r#"
+//- minicore: panic
+#[rustc_intrinsic]
+unsafe fn slice_get_unchecked<ItemPtr, SlicePtr, T>(
+ slice_ptr: SlicePtr,
+ index: usize,
+) -> ItemPtr;
+
+fn should_not_reach() { panic!() }
+
+fn main() {
+ let values = [10, 20, 30];
+ let slice_ptr = &values as *const [i32];
+ let item_ptr = unsafe {
+ slice_get_unchecked::<*const i32, *const [i32], i32>(slice_ptr, 1)
+ };
+ if unsafe { *item_ptr } != 20 {
+ should_not_reach();
+ }
+}
+"#,
+ );
+}
+
+#[test]
+fn slice_get_unchecked_out_of_bounds() {
+ check_error_with(
+ r#"
+#[rustc_intrinsic]
+unsafe fn slice_get_unchecked<ItemPtr, SlicePtr, T>(
+ slice_ptr: SlicePtr,
+ index: usize,
+) -> ItemPtr;
+
+fn main() {
+ let values = [()];
+ let slice_ptr = &values as *const [()];
+ let _item = unsafe {
+ slice_get_unchecked::<*const (), *const [()], ()>(slice_ptr, 1)
+ };
+}
+"#,
+ |e| {
+ let mut err = &e;
+ while let MirEvalError::InFunction(inner, _) = err {
+ err = inner;
+ }
+ matches!(err, MirEvalError::UndefinedBehavior(_))
+ },
+ );
+}
+
+#[test]
+fn slice_get_unchecked_const_slice() {
+ check_pass(
+ r#"
+//- minicore: panic
+#[rustc_intrinsic]
+unsafe fn slice_get_unchecked<ItemPtr, SlicePtr, T>(
+ slice_ptr: SlicePtr,
+ index: usize,
+) -> ItemPtr;
+
+struct Flag {
+ name: &'static str,
+ value: u16,
+}
+
+const PURE: &str = "PURE";
+const NOMEM: &str = "NOMEM";
+const READONLY: &str = "READONLY";
+const PRESERVES_FLAGS: &str = "PRESERVES_FLAGS";
+const NORETURN: &str = "NORETURN";
+const NOSTACK: &str = "NOSTACK";
+const ATT_SYNTAX: &str = "ATT_SYNTAX";
+const FLAGS: &[Flag] = &[
+ Flag { name: PURE, value: 1 },
+ Flag { name: NOMEM, value: 2 },
+ Flag { name: READONLY, value: 4 },
+ Flag { name: PRESERVES_FLAGS, value: 8 },
+ Flag { name: NORETURN, value: 16 },
+ Flag { name: NOSTACK, value: 32 },
+ Flag { name: ATT_SYNTAX, value: 64 },
+];
+
+fn should_not_reach() { panic!() }
+
+fn main() {
+ let flag = unsafe {
+ slice_get_unchecked::<&Flag, &[Flag], Flag>(FLAGS, 6)
+ };
+ let name = flag.name as *const str as *const u8;
+ if unsafe { *name } != b'A' || flag.value != 64 {
+ should_not_reach();
+ }
+}
+"#,
+ );
+}
+
+#[test]
fn unreachable_intrinsic() {
check_error_with(
r#"