Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22948 from Veykril/lukaswirth/push-ppkyzvvwuppt
fix: Make mir debug execution work fot bitflags items
Lukas Wirth 7 days ago
parent e4e6733 · parent 2313390 · commit 970672b
-rw-r--r--crates/hir-ty/src/mir/eval.rs28
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs37
-rw-r--r--crates/hir-ty/src/mir/eval/tests.rs103
3 files changed, 164 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/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index e569b32bd7..177b2c870f 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -1012,6 +1012,43 @@ impl<'a, 'db> Evaluator<'a, 'db> {
let dst = Interval { addr: dst, size };
dst.write_from_interval(self, src)
}
+ "slice_get_unchecked" => {
+ let [slice_ptr, index] = args else {
+ return Err(MirEvalError::InternalError(
+ "slice_get_unchecked args are not provided".into(),
+ ));
+ };
+ let Some(ty) = generic_args.as_slice().get(2).and_then(|it| it.ty()) else {
+ return Err(MirEvalError::InternalError(
+ "slice_get_unchecked item type is not provided".into(),
+ ));
+ };
+ let slice_ptr = slice_ptr.get(self)?;
+ let ptr_size = self.ptr_size();
+ let Some(data) = slice_ptr.get(..ptr_size) else {
+ return Err(MirEvalError::InternalError(
+ "slice_get_unchecked slice pointer is too small".into(),
+ ));
+ };
+ let Some(len) = slice_ptr.get(ptr_size..2 * ptr_size) else {
+ return Err(MirEvalError::InternalError(
+ "slice_get_unchecked slice metadata is missing".into(),
+ ));
+ };
+ let slice_ptr = Address::from_bytes(data)?;
+ let len = from_bytes!(usize, len);
+ let index = from_bytes!(usize, index.get(self)?);
+ if index >= len {
+ return Err(MirEvalError::UndefinedBehavior(format!(
+ "slice_get_unchecked index {index} is out of bounds for slice of length {len}"
+ )));
+ }
+ let size = self.size_of_sized(ty, locals, "slice_get_unchecked item type")?;
+ let offset = index* size;
+ let addr = slice_ptr.to_usize() + offset;
+ let addr = Address::from_usize(addr);
+ destination.write_from_bytes(self, &addr.to_bytes()[..destination.size])
+ }
"offset" | "arith_offset" => {
let [ptr, offset] = args else {
return Err(MirEvalError::InternalError("offset args are not provided".into()));
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#"