From 3941d0b7755f88e9bbd43d186a78bc36b14747c2 Mon Sep 17 00:00:00 2001 From: Tony Kan Date: Thu, 5 Mar 2026 11:14:21 -0800 Subject: [PATCH] fix(interp): Validate values behind `UnsafeBinder` at the inner type --- .../rustc_const_eval/src/interpret/validity.rs | 8 +++++++- .../unsafe-binders/const-eval-validity-ref.rs | 15 +++++++++++++++ .../const-eval-validity-ref.stderr | 14 ++++++++++++++ tests/ui/unsafe-binders/const-eval-validity.rs | 17 +++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/ui/unsafe-binders/const-eval-validity-ref.rs create mode 100644 tests/ui/unsafe-binders/const-eval-validity-ref.stderr create mode 100644 tests/ui/unsafe-binders/const-eval-validity.rs diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index bb461ccb6f370..12c2f365a2ae7 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -991,7 +991,13 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // Nothing to check. interp_ok(true) } - ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), + ty::UnsafeBinder(unsafe_binder_ty) => { + let inner_ty = + self.ecx.tcx.instantiate_bound_regions_with_erased((*unsafe_binder_ty).into()); + let inner = value.transmute(self.ecx.layout_of(inner_ty)?, self.ecx)?; + self.visit_value(&inner)?; + interp_ok(true) + } // The above should be all the primitive types. The rest is compound, we // check them by visiting their fields/variants. ty::Adt(..) diff --git a/tests/ui/unsafe-binders/const-eval-validity-ref.rs b/tests/ui/unsafe-binders/const-eval-validity-ref.rs new file mode 100644 index 0000000000000..5ebe2d2d800a9 --- /dev/null +++ b/tests/ui/unsafe-binders/const-eval-validity-ref.rs @@ -0,0 +1,15 @@ +//@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" +//@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ normalize-stderr: "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP" + +#![feature(unsafe_binders)] +#![allow(incomplete_features)] + +struct RefDst { + b: unsafe<'a> &'a u32, +} + +const C1: &RefDst = unsafe { std::mem::transmute(&1usize) }; +//~^ ERROR: encountered a dangling reference + +fn main() {} diff --git a/tests/ui/unsafe-binders/const-eval-validity-ref.stderr b/tests/ui/unsafe-binders/const-eval-validity-ref.stderr new file mode 100644 index 0000000000000..790a77b5d19a7 --- /dev/null +++ b/tests/ui/unsafe-binders/const-eval-validity-ref.stderr @@ -0,0 +1,14 @@ +error[E0080]: constructing invalid value of type &RefDst: at ..b, encountered a dangling reference (0x1[noalloc] has no provenance) + --> $DIR/const-eval-validity-ref.rs:12:1 + | +LL | const C1: &RefDst = unsafe { std::mem::transmute(&1usize) }; + | ^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/unsafe-binders/const-eval-validity.rs b/tests/ui/unsafe-binders/const-eval-validity.rs new file mode 100644 index 0000000000000..8bf29edbf72e1 --- /dev/null +++ b/tests/ui/unsafe-binders/const-eval-validity.rs @@ -0,0 +1,17 @@ +// regression test for . +//@ check-pass + +#![feature(unsafe_binders)] +#![allow(incomplete_features)] + +struct ThinDst { + b: unsafe<> (), +} + +const fn t(x: &[u8; N]) -> &ThinDst { + unsafe { std::mem::transmute(x.as_ptr()) } +} + +const C1: &ThinDst = t(b"d"); + +fn main() {}