Properly analyze captures from unsafe binders

This commit is contained in:
Michael Goulet 2025-05-23 10:18:05 +00:00
parent 52bf0cf795
commit 04ddafc53c
7 changed files with 51 additions and 5 deletions

View file

@ -13,6 +13,7 @@ use hir::Expr;
use hir::def::DefKind;
use hir::pat_util::EnumerateAndAdjustIterator as _;
use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
use rustc_ast::UnsafeBinderCastKind;
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def::{CtorOf, Res};
use rustc_hir::def_id::LocalDefId;
@ -1393,10 +1394,18 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
self.cat_res(expr.hir_id, expr.span, expr_ty, res)
}
// both type ascription and unsafe binder casts don't affect
// the place-ness of the subexpression.
// type ascription doesn't affect the place-ness of the subexpression.
hir::ExprKind::Type(e, _) => self.cat_expr(e),
hir::ExprKind::UnsafeBinderCast(_, e, _) => self.cat_expr(e),
hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Unwrap, e, _) => {
let base = self.cat_expr(e)?;
Ok(self.cat_projection(
expr.hir_id,
base,
expr_ty,
ProjectionKind::UnwrapUnsafeBinder,
))
}
hir::ExprKind::AddrOf(..)
| hir::ExprKind::Call(..)
@ -1427,6 +1436,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
| hir::ExprKind::Repeat(..)
| hir::ExprKind::InlineAsm(..)
| hir::ExprKind::OffsetOf(..)
| hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Wrap, ..)
| hir::ExprKind::Err(_) => Ok(self.cat_rvalue(expr.hir_id, expr_ty)),
}
}

View file

@ -902,7 +902,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn is_field<'a>(p: &&Projection<'a>) -> bool {
match p.kind {
ProjectionKind::Field(_, _) => true,
ProjectionKind::Deref | ProjectionKind::OpaqueCast => false,
ProjectionKind::Deref
| ProjectionKind::OpaqueCast
| ProjectionKind::UnwrapUnsafeBinder => false,
p @ (ProjectionKind::Subslice | ProjectionKind::Index) => {
bug!("ProjectionKind {:?} was unexpected", p)
}
@ -2197,7 +2199,8 @@ fn restrict_capture_precision(
}
ProjectionKind::Deref => {}
ProjectionKind::OpaqueCast => {}
ProjectionKind::Field(..) => {} // ignore
ProjectionKind::Field(..) => {}
ProjectionKind::UnwrapUnsafeBinder => {}
}
}
@ -2268,6 +2271,7 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String
ProjectionKind::Index => String::from("Index"),
ProjectionKind::Subslice => String::from("Subslice"),
ProjectionKind::OpaqueCast => String::from("OpaqueCast"),
ProjectionKind::UnwrapUnsafeBinder => String::from("UnwrapUnsafeBinder"),
};
if i != 0 {
projections_str.push(',');

View file

@ -43,6 +43,9 @@ pub enum ProjectionKind {
///
/// This is unused if `-Znext-solver` is enabled.
OpaqueCast,
/// `unwrap_binder!(expr)`
UnwrapUnsafeBinder,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]

View file

@ -240,6 +240,9 @@ fn strip_prefix<'tcx>(
HirProjectionKind::OpaqueCast => {
assert_matches!(iter.next(), Some(ProjectionElem::OpaqueCast(..)));
}
HirProjectionKind::UnwrapUnsafeBinder => {
assert_matches!(iter.next(), Some(ProjectionElem::UnwrapUnsafeBinder(..)));
}
HirProjectionKind::Index | HirProjectionKind::Subslice => {
bug!("unexpected projection kind: {:?}", projection);
}

View file

@ -1220,6 +1220,9 @@ impl<'tcx> ThirBuildCx<'tcx> {
HirProjectionKind::OpaqueCast => {
ExprKind::Use { source: self.thir.exprs.push(captured_place_expr) }
}
HirProjectionKind::UnwrapUnsafeBinder => ExprKind::PlaceUnwrapUnsafeBinder {
source: self.thir.exprs.push(captured_place_expr),
},
HirProjectionKind::Index | HirProjectionKind::Subslice => {
// We don't capture these projections, so we can ignore them here
continue;

View file

@ -941,6 +941,8 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
ProjectionKind::Subslice |
// Doesn't have surface syntax. Only occurs in patterns.
ProjectionKind::OpaqueCast => (),
// Only occurs in closure captures.
ProjectionKind::UnwrapUnsafeBinder => (),
ProjectionKind::Deref => {
// Explicit derefs are typically handled later on, but
// some items do not need explicit deref, such as array accesses,

View file

@ -0,0 +1,21 @@
//@ check-pass
#![feature(unsafe_binders)]
#![allow(incomplete_features)]
use std::unsafe_binder::unwrap_binder;
#[derive(Copy, Clone)]
pub struct S([usize; 8]);
// Regression test for <https://github.com/rust-lang/rust/issues/141418>.
pub fn by_value(x: unsafe<'a> S) -> usize {
unsafe { (|| unwrap_binder!(x).0[0])() }
}
// Regression test for <https://github.com/rust-lang/rust/issues/141417>.
pub fn by_ref(x: unsafe<'a> &'a S) -> usize {
unsafe { (|| unwrap_binder!(x).0[0])() }
}
fn main() {}