Add unwrap_unsafe_binder and wrap_unsafe_binder macro operators

This commit is contained in:
Michael Goulet
2024-09-13 14:00:10 -04:00
parent 2a9e358c72
commit 3f97c6be8d
25 changed files with 222 additions and 12 deletions
+20 -1
View File
@@ -1382,6 +1382,7 @@ pub fn precedence(&self) -> ExprPrecedence {
| ExprKind::Tup(_)
| ExprKind::Type(..)
| ExprKind::Underscore
| ExprKind::UnsafeBinderCast(..)
| ExprKind::While(..)
| ExprKind::Err(_)
| ExprKind::Dummy => ExprPrecedence::Unambiguous,
@@ -1509,7 +1510,13 @@ pub enum ExprKind {
/// `'label: for await? pat in iter { block }`
///
/// This is desugared to a combination of `loop` and `match` expressions.
ForLoop { pat: P<Pat>, iter: P<Expr>, body: P<Block>, label: Option<Label>, kind: ForLoopKind },
ForLoop {
pat: P<Pat>,
iter: P<Expr>,
body: P<Block>,
label: Option<Label>,
kind: ForLoopKind,
},
/// Conditionless loop (can be exited with `break`, `continue`, or `return`).
///
/// `'label: loop { block }`
@@ -1614,6 +1621,8 @@ pub enum ExprKind {
/// A `format_args!()` expression.
FormatArgs(P<FormatArgs>),
UnsafeBinderCast(UnsafeBinderCastKind, P<Expr>, Option<P<Ty>>),
/// Placeholder for an expression that wasn't syntactically well formed in some way.
Err(ErrorGuaranteed),
@@ -1652,6 +1661,16 @@ pub fn modifier(&self) -> &'static str {
}
}
/// Whether we're unwrapping or wrapping an unsafe binder
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum UnsafeBinderCastKind {
// e.g. `&i32` -> `unsafe<'a> &'a i32`
Wrap,
// e.g. `unsafe<'a> &'a i32` -> `&i32`
Unwrap,
}
/// The explicit `Self` type in a "qualified path". The actual
/// path, including the trait and the associated item, is stored
/// separately. `position` represents the index of the associated
+6
View File
@@ -1780,6 +1780,12 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
ExprKind::TryBlock(body) => vis.visit_block(body),
ExprKind::Lit(_token) => {}
ExprKind::IncludedBytes(_bytes) => {}
ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
vis.visit_expr(expr);
if let Some(ty) = ty {
vis.visit_ty(ty);
}
}
ExprKind::Err(_guar) => {}
ExprKind::Dummy => {}
}
+2
View File
@@ -152,6 +152,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
| Underscore
| Yeet(..)
| Yield(..)
| UnsafeBinderCast(..)
| Err(..)
| Dummy => return false,
}
@@ -232,6 +233,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
| Paren(_)
| Try(_)
| Yeet(None)
| UnsafeBinderCast(..)
| Err(_)
| Dummy => break None,
}
+4
View File
@@ -1230,6 +1230,10 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
ExprKind::TryBlock(body) => try_visit!(visitor.visit_block(body)),
ExprKind::Lit(_token) => {}
ExprKind::IncludedBytes(_bytes) => {}
ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
try_visit!(visitor.visit_expr(expr));
visit_opt!(visitor, visit_ty, ty);
}
ExprKind::Err(_guar) => {}
ExprKind::Dummy => {}
}
+8
View File
@@ -379,6 +379,14 @@ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
ExprKind::Err(guar) => hir::ExprKind::Err(*guar),
ExprKind::UnsafeBinderCast(kind, expr, ty) => hir::ExprKind::UnsafeBinderCast(
*kind,
self.lower_expr(expr),
ty.as_ref().map(|ty| {
self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast))
}),
),
ExprKind::Dummy => {
span_bug!(e.span, "lowered ExprKind::Dummy")
}
@@ -565,6 +565,7 @@ macro_rules! gate_all {
gate_all!(return_type_notation, "return type notation is experimental");
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
gate_all!(unsafe_fields, "`unsafe` fields are experimental");
gate_all!(unsafe_binders, "unsafe binder types are experimental");
if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {
@@ -772,6 +772,25 @@ pub(super) fn print_expr_outer_attr_style(
self.word_nbsp("try");
self.print_block_with_attrs(blk, attrs)
}
ast::ExprKind::UnsafeBinderCast(kind, expr, ty) => {
self.word("builtin # ");
match kind {
ast::UnsafeBinderCastKind::Wrap => self.word("wrap_binder"),
ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder"),
}
self.popen();
self.ibox(0);
self.print_expr(expr, FixupContext::default());
if let Some(ty) = ty {
self.word(",");
self.space();
self.print_type(ty);
}
self.end();
self.pclose();
}
ast::ExprKind::Err(_) => {
self.popen();
self.word("/*ERROR*/");
@@ -323,7 +323,8 @@ fn manage_cond_expr(&mut self, expr: &mut P<Expr>) {
| ExprKind::While(_, _, _)
| ExprKind::Yeet(_)
| ExprKind::Become(_)
| ExprKind::Yield(_) => {}
| ExprKind::Yield(_)
| ExprKind::UnsafeBinderCast(..) => {}
}
}
+2
View File
@@ -635,6 +635,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
/// Allows creation of instances of a struct by moving fields that have
/// not changed from prior instances of the same struct (RFC #2528)
(unstable, type_changing_struct_update, "1.58.0", Some(86555)),
/// Allows using `unsafe<'a> &'a T` unsafe binder types.
(incomplete, unsafe_binders, "CURRENT_RUSTC_VERSION", Some(130516)),
/// Allows declaring fields `unsafe`.
(incomplete, unsafe_fields, "CURRENT_RUSTC_VERSION", Some(132922)),
/// Allows const generic parameters to be defined with types that
+11 -2
View File
@@ -8,7 +8,7 @@
};
pub use rustc_ast::{
BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy,
ImplPolarity, IsAuto, Movability, Mutability, UnOp,
ImplPolarity, IsAuto, Movability, Mutability, UnOp, UnsafeBinderCastKind,
};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::sorted_map::SortedMap;
@@ -1740,6 +1740,7 @@ pub fn precedence(&self) -> ExprPrecedence {
| ExprKind::Struct(..)
| ExprKind::Tup(_)
| ExprKind::Type(..)
| ExprKind::UnsafeBinderCast(..)
| ExprKind::Err(_) => ExprPrecedence::Unambiguous,
ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
@@ -1769,6 +1770,9 @@ pub fn is_place_expr(&self, mut allow_projections_from: impl FnMut(&Self) -> boo
// https://github.com/rust-lang/rfcs/blob/master/text/0803-type-ascription.md#type-ascription-and-temporaries
ExprKind::Type(ref e, _) => e.is_place_expr(allow_projections_from),
// Unsafe binder cast preserves place-ness of the sub-expression.
ExprKind::UnsafeBinderCast(_, e, _) => e.is_place_expr(allow_projections_from),
ExprKind::Unary(UnOp::Deref, _) => true,
ExprKind::Field(ref base, _) | ExprKind::Index(ref base, _, _) => {
@@ -1850,7 +1854,8 @@ pub fn can_have_side_effects(&self) -> bool {
| ExprKind::Field(base, _)
| ExprKind::Index(base, _, _)
| ExprKind::AddrOf(.., base)
| ExprKind::Cast(base, _) => {
| ExprKind::Cast(base, _)
| ExprKind::UnsafeBinderCast(_, base, _) => {
// This isn't exactly true for `Index` and all `Unary`, but we are using this
// method exclusively for diagnostics and there's a *cultural* pressure against
// them being used only for its side-effects.
@@ -2144,6 +2149,10 @@ pub enum ExprKind<'hir> {
/// A suspension point for coroutines (i.e., `yield <expr>`).
Yield(&'hir Expr<'hir>, YieldSource),
/// Operators which can be used to interconvert `unsafe` binder types.
/// e.g. `unsafe<'a> &'a i32` <=> `&i32`.
UnsafeBinderCast(UnsafeBinderCastKind, &'hir Expr<'hir>, Option<&'hir Ty<'hir>>),
/// A placeholder for an expression that wasn't syntactically well formed in some way.
Err(rustc_span::ErrorGuaranteed),
}
+4
View File
@@ -857,6 +857,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
ExprKind::Yield(ref subexpression, _) => {
try_visit!(visitor.visit_expr(subexpression));
}
ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
try_visit!(visitor.visit_expr(expr));
visit_opt!(visitor, visit_ty, ty);
}
ExprKind::Lit(_) | ExprKind::Err(_) => {}
}
V::Result::output()
+13
View File
@@ -1542,6 +1542,19 @@ fn print_expr(&mut self, expr: &hir::Expr<'_>) {
self.word(")");
}
hir::ExprKind::UnsafeBinderCast(kind, expr, ty) => {
match kind {
hir::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("),
hir::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("),
}
self.print_expr(expr);
if let Some(ty) = ty {
self.word(",");
self.space();
self.print_type(ty);
}
self.word(")");
}
hir::ExprKind::Yield(expr, _) => {
self.word_space("yield");
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump);
+23 -2
View File
@@ -329,6 +329,13 @@ pub(super) fn expr_guaranteed_to_constitute_read_for_never(
// Assignment does call `drop_in_place`, though, but its safety
// requirements are not the same.
ExprKind::AddrOf(..) | hir::ExprKind::Field(..) => false,
// Place-preserving expressions only constitute reads if their
// parent expression constitutes a read.
ExprKind::Type(..) | ExprKind::UnsafeBinderCast(..) => {
self.expr_guaranteed_to_constitute_read_for_never(expr)
}
ExprKind::Assign(lhs, _, _) => {
// Only the LHS does not constitute a read
expr.hir_id != lhs.hir_id
@@ -353,7 +360,6 @@ pub(super) fn expr_guaranteed_to_constitute_read_for_never(
| ExprKind::Binary(_, _, _)
| ExprKind::Unary(_, _)
| ExprKind::Cast(_, _)
| ExprKind::Type(_, _)
| ExprKind::DropTemps(_)
| ExprKind::If(_, _, _)
| ExprKind::Closure(_)
@@ -564,7 +570,10 @@ fn check_expr_kind(
self.check_expr_index(base, idx, expr, brackets_span)
}
ExprKind::Yield(value, _) => self.check_expr_yield(value, expr),
hir::ExprKind::Err(guar) => Ty::new_error(tcx, guar),
ExprKind::UnsafeBinderCast(kind, expr, ty) => {
self.check_expr_unsafe_binder_cast(kind, expr, ty, expected)
}
ExprKind::Err(guar) => Ty::new_error(tcx, guar),
}
}
@@ -1634,6 +1643,18 @@ fn check_expr_cast(
}
}
fn check_expr_unsafe_binder_cast(
&self,
_kind: hir::UnsafeBinderCastKind,
expr: &'tcx hir::Expr<'tcx>,
_hir_ty: Option<&'tcx hir::Ty<'tcx>>,
_expected: Expectation<'tcx>,
) -> Ty<'tcx> {
let guar =
self.dcx().struct_span_err(expr.span, "unsafe binders are not yet implemented").emit();
Ty::new_error(self.tcx, guar)
}
fn check_expr_array(
&self,
args: &'tcx [hir::Expr<'tcx>],
@@ -341,6 +341,10 @@ pub fn walk_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
self.walk_expr(subexpr)?;
}
hir::ExprKind::UnsafeBinderCast(_, subexpr, _) => {
self.walk_expr(subexpr)?;
}
hir::ExprKind::Unary(hir::UnOp::Deref, base) => {
// *base
self.walk_expr(base)?;
@@ -1360,7 +1364,10 @@ fn cat_expr_unadjusted(&self, expr: &hir::Expr<'_>) -> Result<PlaceWithHirId<'tc
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.
hir::ExprKind::Type(e, _) => self.cat_expr(e),
hir::ExprKind::UnsafeBinderCast(_, e, _) => self.cat_expr(e),
hir::ExprKind::AddrOf(..)
| hir::ExprKind::Call(..)
+2
View File
@@ -192,6 +192,8 @@ fn is_temporary_rvalue(expr: &Expr<'_>) -> bool {
| ExprKind::DropTemps(..)
| ExprKind::Let(..) => false,
ExprKind::UnsafeBinderCast(..) => false,
// Not applicable
ExprKind::Type(..) | ExprKind::Err(..) => false,
}
@@ -422,6 +422,7 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result {
hir::ExprKind::Unary(_, expr)
| hir::ExprKind::Cast(expr, _)
| hir::ExprKind::Type(expr, _)
| hir::ExprKind::UnsafeBinderCast(_, expr, _)
| hir::ExprKind::Yield(expr, _)
| hir::ExprKind::AddrOf(_, _, expr)
| hir::ExprKind::Match(expr, _, _)
@@ -915,6 +915,11 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
}
}
}
hir::ExprKind::UnsafeBinderCast(_kind, _source, _ty) => {
unreachable!("unsafe binders are not yet implemented")
}
hir::ExprKind::DropTemps(source) => ExprKind::Use { source: self.mirror_expr(source) },
hir::ExprKind::Array(fields) => ExprKind::Array { fields: self.mirror_exprs(fields) },
hir::ExprKind::Tup(fields) => ExprKind::Tuple { fields: self.mirror_exprs(fields) },
+21 -2
View File
@@ -15,7 +15,7 @@
use rustc_ast::{
self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy,
ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, FnRetTy, Label, MacCall,
MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp,
MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -1931,6 +1931,12 @@ fn parse_expr_builtin(&mut self) -> PResult<'a, P<Expr>> {
Ok(match ident.name {
sym::offset_of => Some(this.parse_expr_offset_of(lo)?),
sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?),
sym::wrap_binder => {
Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap)?)
}
sym::unwrap_binder => {
Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Unwrap)?)
}
_ => None,
})
})
@@ -2006,6 +2012,17 @@ pub(crate) fn parse_expr_type_ascribe(&mut self, lo: Span) -> PResult<'a, P<Expr
Ok(self.mk_expr(span, ExprKind::Type(expr, ty)))
}
pub(crate) fn parse_expr_unsafe_binder_cast(
&mut self,
lo: Span,
kind: UnsafeBinderCastKind,
) -> PResult<'a, P<Expr>> {
let expr = self.parse_expr()?;
let ty = if self.eat(&TokenKind::Comma) { Some(self.parse_ty()?) } else { None };
let span = lo.to(self.token.span);
Ok(self.mk_expr(span, ExprKind::UnsafeBinderCast(kind, expr, ty)))
}
/// Returns a string literal if the next token is a string literal.
/// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind,
/// and returns `None` if the next token is not literal at all.
@@ -4019,7 +4036,9 @@ fn visit_expr(&mut self, e: &mut P<Expr>) {
mut_visit::walk_expr(self, e);
self.forbid_let_reason = forbid_let_reason;
}
ExprKind::Cast(ref mut op, _) | ExprKind::Type(ref mut op, _) => {
ExprKind::Cast(ref mut op, _)
| ExprKind::Type(ref mut op, _)
| ExprKind::UnsafeBinderCast(_, ref mut op, _) => {
let forbid_let_reason = self.forbid_let_reason;
self.forbid_let_reason = Some(OtherForbidden);
self.visit_expr(op);
+35 -4
View File
@@ -315,9 +315,40 @@ fn visit_pat_field(&mut self, f: &'v hir::PatField<'v>) {
fn visit_expr(&mut self, e: &'v hir::Expr<'v>) {
record_variants!((self, e, e.kind, Some(e.hir_id), hir, Expr, ExprKind), [
ConstBlock, Array, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, DropTemps,
Let, If, Loop, Match, Closure, Block, Assign, AssignOp, Field, Index, Path, AddrOf,
Break, Continue, Ret, Become, InlineAsm, OffsetOf, Struct, Repeat, Yield, Err
ConstBlock,
Array,
Call,
MethodCall,
Tup,
Binary,
Unary,
Lit,
Cast,
Type,
DropTemps,
Let,
If,
Loop,
Match,
Closure,
Block,
Assign,
AssignOp,
Field,
Index,
Path,
AddrOf,
Break,
Continue,
Ret,
Become,
InlineAsm,
OffsetOf,
Struct,
Repeat,
Yield,
UnsafeBinderCast,
Err
]);
hir_visit::walk_expr(self, e)
}
@@ -572,7 +603,7 @@ fn visit_expr(&mut self, e: &'v ast::Expr) {
If, While, ForLoop, Loop, Match, Closure, Block, Await, TryBlock, Assign,
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
Become, IncludedBytes, Gen, Err, Dummy
Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy
]
);
ast_visit::walk_expr(self, e)
+3
View File
@@ -447,6 +447,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
| hir::ExprKind::InlineAsm(..)
| hir::ExprKind::OffsetOf(..)
| hir::ExprKind::Type(..)
| hir::ExprKind::UnsafeBinderCast(..)
| hir::ExprKind::Err(_)
| hir::ExprKind::Path(hir::QPath::TypeRelative(..))
| hir::ExprKind::Path(hir::QPath::LangItem(..)) => {}
@@ -1051,6 +1052,7 @@ fn propagate_through_expr(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNod
hir::ExprKind::AddrOf(_, _, ref e)
| hir::ExprKind::Cast(ref e, _)
| hir::ExprKind::Type(ref e, _)
| hir::ExprKind::UnsafeBinderCast(_, ref e, _)
| hir::ExprKind::DropTemps(ref e)
| hir::ExprKind::Unary(_, ref e)
| hir::ExprKind::Repeat(ref e, _) => self.propagate_through_expr(e, succ),
@@ -1443,6 +1445,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
| hir::ExprKind::Path(_)
| hir::ExprKind::Yield(..)
| hir::ExprKind::Type(..)
| hir::ExprKind::UnsafeBinderCast(..)
| hir::ExprKind::Err(_) => {}
}
}
@@ -186,6 +186,7 @@ fn check_expr<'tcx>(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) {
| ExprKind::Lit(..)
| ExprKind::Cast(..)
| ExprKind::Type(..)
| ExprKind::UnsafeBinderCast(..)
| ExprKind::Loop(..)
| ExprKind::Match(..)
| ExprKind::If(..)
+3
View File
@@ -2104,6 +2104,7 @@
unreachable_macro,
unrestricted_attribute_tokens,
unsafe_attributes,
unsafe_binders,
unsafe_block_in_unsafe_fn,
unsafe_cell,
unsafe_cell_raw_get,
@@ -2127,6 +2128,7 @@
unwind_attributes,
unwind_safe_trait,
unwrap,
unwrap_binder,
unwrap_or,
use_extern_macros,
use_nested_groups,
@@ -2185,6 +2187,7 @@
windows,
windows_subsystem,
with_negative_coherence,
wrap_binder,
wrapping_add,
wrapping_div,
wrapping_mul,
+2
View File
@@ -354,6 +354,8 @@ pub mod autodiff {
pub mod range;
pub mod result;
pub mod sync;
#[unstable(feature = "unsafe_binders", issue = "130516")]
pub mod unsafe_binder;
pub mod fmt;
pub mod hash;
+25
View File
@@ -0,0 +1,25 @@
//! Operators used to turn types into unsafe binders and back.
/// Unwrap an unsafe binder into its underlying type.
#[allow_internal_unstable(builtin_syntax)]
#[unstable(feature = "unsafe_binders", issue = "130516")]
pub macro unwrap_binder {
($expr:expr) => {
builtin # unwrap_binder ( $expr )
},
($expr:expr ; $ty:ty) => {
builtin # unwrap_binder ( $expr, $ty )
},
}
/// Wrap a type into an unsafe binder.
#[allow_internal_unstable(builtin_syntax)]
#[unstable(feature = "unsafe_binders", issue = "130516")]
pub macro wrap_binder {
($expr:expr) => {
builtin # wrap_binder ( $expr )
},
($expr:expr ; $ty:ty) => {
builtin # wrap_binder ( $expr, $ty )
},
}
+2
View File
@@ -544,6 +544,8 @@
#[stable(feature = "i128", since = "1.26.0")]
#[allow(deprecated, deprecated_in_future)]
pub use core::u128;
#[unstable(feature = "unsafe_binders", issue = "130516")]
pub use core::unsafe_binder;
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated, deprecated_in_future)]
pub use core::usize;