diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2024-12-13 17:25:31 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-13 17:25:31 +0100 |
| commit | 5c9b227a3df48cff16a7f66957806db46caeeac8 (patch) | |
| tree | 9994cef76fa8b2d8b9d4d77bc90fca06842c8654 | |
| parent | 9f6b07e2f5a1267c976a0f8a30dbea6e461a198e (diff) | |
| parent | b8c5a0f0eba036700775a33c924deaeeb34f3974 (diff) | |
| download | rust-5c9b227a3df48cff16a7f66957806db46caeeac8.tar.gz rust-5c9b227a3df48cff16a7f66957806db46caeeac8.zip | |
Rollup merge of #134140 - compiler-errors:unsafe-binders-ast, r=oli-obk
Add AST support for unsafe binders I'm splitting up #130514 into pieces. It's impossible for me to keep up with a huge PR like that. I'll land type system support for this next, probably w/o MIR lowering, which will come later. r? `@oli-obk` cc `@BoxyUwU` and `@lcnr` who also may want to look at this, though this PR doesn't do too much yet
53 files changed, 616 insertions, 18 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a219f1dc032..f0099fa8adc 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1382,6 +1382,7 @@ impl Expr { | 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 @@ impl GenBlockKind { } } +/// 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 @@ -2223,6 +2242,12 @@ pub struct BareFnTy { pub decl_span: Span, } +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct UnsafeBinderTy { + pub generic_params: ThinVec<GenericParam>, + pub inner_ty: P<Ty>, +} + /// The various kinds of type recognized by the compiler. // // Adding a new variant? Please update `test_ty` in `tests/ui/macros/stringify.rs`. @@ -2242,6 +2267,8 @@ pub enum TyKind { PinnedRef(Option<Lifetime>, MutTy), /// A bare function (e.g., `fn(usize) -> bool`). BareFn(P<BareFnTy>), + /// An unsafe existential lifetime binder (e.g., `unsafe<'a> &'a ()`). + UnsafeBinder(P<UnsafeBinderTy>), /// The never type (`!`). Never, /// A tuple (`(A, B, C, D,...)`). diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 0e299c60479..b44e2d9cace 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -558,6 +558,11 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) { vis.visit_fn_decl(decl); vis.visit_span(decl_span); } + TyKind::UnsafeBinder(binder) => { + let UnsafeBinderTy { generic_params, inner_ty } = binder.deref_mut(); + generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param)); + vis.visit_ty(inner_ty); + } TyKind::Tup(tys) => visit_thin_vec(tys, |ty| vis.visit_ty(ty)), TyKind::Paren(ty) => vis.visit_ty(ty), TyKind::Pat(ty, pat) => { @@ -1780,6 +1785,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 => {} } diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index ae1ca36a3ba..64f2a98b8a6 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -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, } @@ -253,6 +255,10 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { ty = &mut_ty.ty; } + ast::TyKind::UnsafeBinder(binder) => { + ty = &binder.inner_ty; + } + ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output { ast::FnRetTy::Default(_) => break None, ast::FnRetTy::Ty(ret) => ty = ret, diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index a6232b6dfaf..22db4438e31 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -522,6 +522,10 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result { walk_list!(visitor, visit_generic_param, generic_params); try_visit!(visitor.visit_fn_decl(decl)); } + TyKind::UnsafeBinder(binder) => { + walk_list!(visitor, visit_generic_param, &binder.generic_params); + try_visit!(visitor.visit_ty(&binder.inner_ty)); + } TyKind::Path(maybe_qself, path) => { try_visit!(visitor.visit_qself(maybe_qself)); try_visit!(visitor.visit_path(path, *id)); @@ -1226,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 => {} } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 2ad0ff3200e..32905806343 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -379,6 +379,14 @@ impl<'hir> LoweringContext<'_, '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") } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index bac3f974cca..3cbd3e1b637 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1228,6 +1228,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param_names: self.lower_fn_params_to_names(&f.decl), })) } + TyKind::UnsafeBinder(f) => { + let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params); + hir::TyKind::UnsafeBinder(self.arena.alloc(hir::UnsafeBinderTy { + generic_params, + inner_ty: self.lower_ty(&f.inner_ty, itctx), + })) + } TyKind::Never => hir::TyKind::Never, TyKind::Tup(tys) => hir::TyKind::Tup( self.arena.alloc_from_iter(tys.iter().map(|ty| self.lower_ty_direct(ty, itctx))), diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 61a710517ea..c10b3296497 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -560,6 +560,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { 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) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 49e4a559e73..04ffa2cffe3 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1198,6 +1198,14 @@ impl<'a> State<'a> { ast::TyKind::BareFn(f) => { self.print_ty_fn(f.ext, f.safety, &f.decl, None, &f.generic_params); } + ast::TyKind::UnsafeBinder(f) => { + self.ibox(INDENT_UNIT); + self.word("unsafe"); + self.print_generic_params(&f.generic_params); + self.nbsp(); + self.print_type(&f.inner_ty); + self.end(); + } ast::TyKind::Path(None, path) => { self.print_path(path, false, 0); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index c239cb249c3..dce76fb1e77 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -772,6 +772,25 @@ impl<'a> State<'a> { 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*/"); diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 70fa4d00c0f..eb07975d8af 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -323,7 +323,8 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::While(_, _, _) | ExprKind::Yeet(_) | ExprKind::Become(_) - | ExprKind::Yield(_) => {} + | ExprKind::Yield(_) + | ExprKind::UnsafeBinderCast(..) => {} } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 3c9115be7f5..b9776eadbab 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -635,6 +635,8 @@ declare_features! ( /// 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 diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 4800a479ff4..bf800337a4f 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -8,7 +8,7 @@ use rustc_ast::{ }; 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 @@ impl Expr<'_> { | ExprKind::Struct(..) | ExprKind::Tup(_) | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) | ExprKind::Err(_) => ExprPrecedence::Unambiguous, ExprKind::DropTemps(ref expr, ..) => expr.precedence(), @@ -1769,6 +1770,9 @@ impl Expr<'_> { // 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 @@ impl Expr<'_> { | 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), } @@ -2781,6 +2790,12 @@ pub struct BareFnTy<'hir> { } #[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct UnsafeBinderTy<'hir> { + pub generic_params: &'hir [GenericParam<'hir>], + pub inner_ty: &'hir Ty<'hir>, +} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct OpaqueTy<'hir> { pub hir_id: HirId, pub def_id: LocalDefId, @@ -2878,6 +2893,8 @@ pub enum TyKind<'hir> { Ref(&'hir Lifetime, MutTy<'hir>), /// A bare function (e.g., `fn(usize) -> bool`). BareFn(&'hir BareFnTy<'hir>), + /// An unsafe binder type (e.g. `unsafe<'a> Foo<'a>`). + UnsafeBinder(&'hir UnsafeBinderTy<'hir>), /// The never type (`!`). Never, /// A tuple (`(A, B, C, D, ...)`). diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 8dbfefffee4..482940eb5ca 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -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() @@ -886,6 +890,10 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul walk_list!(visitor, visit_generic_param, function_declaration.generic_params); try_visit!(visitor.visit_fn_decl(function_declaration.decl)); } + TyKind::UnsafeBinder(ref unsafe_binder) => { + walk_list!(visitor, visit_generic_param, unsafe_binder.generic_params); + try_visit!(visitor.visit_ty(unsafe_binder.inner_ty)); + } TyKind::Path(ref qpath) => { try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span)); } diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 1d9114b0ef3..f52d4f42eca 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -470,6 +470,12 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S self.outer_index.shift_out(1); res } + hir::TyKind::UnsafeBinder(_) => { + self.outer_index.shift_in(1); + let res = intravisit::walk_ty(self, ty); + self.outer_index.shift_out(1); + res + } _ => intravisit::walk_ty(self, ty), } } diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 74f381d2661..923d2b1fe67 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -781,6 +781,36 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { intravisit::walk_ty(this, ty); }); } + hir::TyKind::UnsafeBinder(binder) => { + let (mut bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) = + binder + .generic_params + .iter() + .enumerate() + .map(|(late_bound_idx, param)| { + ( + (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)), + late_arg_as_bound_arg(self.tcx, param), + ) + }) + .unzip(); + + deny_non_region_late_bound(self.tcx, &mut bound_vars, "function pointer types"); + + self.record_late_bound_vars(ty.hir_id, binders); + let scope = Scope::Binder { + hir_id: ty.hir_id, + bound_vars, + s: self.scope, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + self.with(scope, |this| { + // a bare fn has no bounds, so everything + // contained within is scoped within its binder. + intravisit::walk_ty(this, ty); + }); + } hir::TyKind::TraitObject(bounds, lifetime, _) => { debug!(?bounds, ?lifetime, "TraitObject"); let scope = Scope::TraitRefBoundary { s: self.scope }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 7683c87168b..1bdbde30037 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2312,6 +2312,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lower_fn_ty(hir_ty.hir_id, bf.safety, bf.abi, bf.decl, None, Some(hir_ty)), ) } + hir::TyKind::UnsafeBinder(_binder) => { + let guar = self + .dcx() + .struct_span_err(hir_ty.span, "unsafe binders are not yet implemented") + .emit(); + Ty::new_error(tcx, guar) + } hir::TyKind::TraitObject(bounds, lifetime, repr) => { if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { // Don't continue with type analysis if the `dyn` keyword is missing diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 20ba9ae2632..a17b6321ce8 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -288,6 +288,9 @@ impl<'a> State<'a> { hir::TyKind::BareFn(f) => { self.print_ty_fn(f.abi, f.safety, f.decl, None, f.generic_params, f.param_names); } + hir::TyKind::UnsafeBinder(unsafe_binder) => { + self.print_unsafe_binder(unsafe_binder); + } hir::TyKind::OpaqueDef(..) => self.word("/*impl Trait*/"), hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false), hir::TyKind::TraitObject(bounds, lifetime, syntax) => { @@ -339,6 +342,15 @@ impl<'a> State<'a> { self.end() } + fn print_unsafe_binder(&mut self, unsafe_binder: &hir::UnsafeBinderTy<'_>) { + self.ibox(INDENT_UNIT); + self.word("unsafe"); + self.print_generic_params(unsafe_binder.generic_params); + self.nbsp(); + self.print_type(unsafe_binder.inner_ty); + self.end(); + } + fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); @@ -1530,6 +1542,19 @@ impl<'a> State<'a> { 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); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 56903865277..65345048bfc 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -329,6 +329,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ExprKind::Binary(_, _, _) | ExprKind::Unary(_, _) | ExprKind::Cast(_, _) - | ExprKind::Type(_, _) | ExprKind::DropTemps(_) | ExprKind::If(_, _, _) | ExprKind::Closure(_) @@ -564,7 +570,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + 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>], diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 27ec2e9e0d4..ecbae6ac72f 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -341,6 +341,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx 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 @@ 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. hir::ExprKind::Type(e, _) => self.cat_expr(e), + hir::ExprKind::UnsafeBinderCast(_, e, _) => self.cat_expr(e), hir::ExprKind::AddrOf(..) | hir::ExprKind::Call(..) diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index 7e298a9a63c..10769b57a76 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -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, } diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 2db229ed133..1402129195f 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -422,6 +422,7 @@ impl<'tcx, 'a> Visitor<'tcx> for FindSignificantDropper<'tcx, 'a> { 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, _, _) diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index d75f01dfba0..3cbf1e2055c 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -915,6 +915,11 @@ impl<'tcx> Cx<'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) }, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 44f42e5fbf2..a2136399b0c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -15,7 +15,7 @@ use rustc_ast::visit::{Visitor, walk_expr}; 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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { 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. @@ -4016,7 +4033,9 @@ impl MutVisitor for CondChecker<'_> { 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); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 8cff23c2e32..f696074e66a 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -5,7 +5,7 @@ use rustc_ast::{ self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, - TyKind, + TyKind, UnsafeBinderTy, }; use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::{Ident, kw, sym}; @@ -348,6 +348,10 @@ impl<'a> Parser<'a> { TyKind::Err(guar) } } + } else if self.check_keyword(kw::Unsafe) + && self.look_ahead(1, |tok| matches!(tok.kind, token::Lt)) + { + self.parse_unsafe_binder_ty()? } else { let msg = format!("expected type, found {}", super::token_descr(&self.token)); let mut err = self.dcx().struct_span_err(lo, msg); @@ -369,6 +373,19 @@ impl<'a> Parser<'a> { if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } + fn parse_unsafe_binder_ty(&mut self) -> PResult<'a, TyKind> { + let lo = self.token.span; + assert!(self.eat_keyword(kw::Unsafe)); + self.expect_lt()?; + let generic_params = self.parse_generic_params()?; + self.expect_gt()?; + let inner_ty = self.parse_ty()?; + let span = lo.to(self.prev_token.span); + self.psess.gated_spans.gate(sym::unsafe_binders, span); + + Ok(TyKind::UnsafeBinder(P(UnsafeBinderTy { generic_params, inner_ty }))) + } + /// Parses either: /// - `(TYPE)`, a parenthesized type. /// - `(TYPE,)`, a tuple with a single field of type TYPE. diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 76edb51c0bc..164cbc69595 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -315,9 +315,40 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'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) } @@ -335,6 +366,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { Ptr, Ref, BareFn, + UnsafeBinder, Never, Tup, Path, @@ -571,7 +603,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { 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) @@ -585,6 +617,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Ref, PinnedRef, BareFn, + UnsafeBinder, Never, Tup, Path, diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 09cbb648f9b..034f7308c4a 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -447,6 +447,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'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 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { 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(_) => {} } } diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index b2f8d7dadff..766a9e1bdad 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -186,6 +186,7 @@ impl CheckInlineAssembly { | ExprKind::Lit(..) | ExprKind::Cast(..) | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) | ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::If(..) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 5cb7bb9b461..bde11428d40 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -887,6 +887,28 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r }, ) } + TyKind::UnsafeBinder(unsafe_binder) => { + // FIXME(unsafe_binder): Better span + let span = ty.span; + self.with_generic_param_rib( + &unsafe_binder.generic_params, + RibKind::Normal, + LifetimeRibKind::Generics { + binder: ty.id, + kind: LifetimeBinderKind::BareFnType, + span, + }, + |this| { + this.visit_generic_params(&unsafe_binder.generic_params, false); + this.with_lifetime_rib( + // We don't allow anonymous `unsafe &'_ ()` binders, + // although I guess we could. + LifetimeRibKind::AnonymousReportError, + |this| this.visit_ty(&unsafe_binder.inner_ty), + ); + }, + ) + } TyKind::Array(element_ty, length) => { self.visit_ty(element_ty); self.resolve_anon_const(length, AnonConstKind::ConstArg(IsRepeatExpr::No)); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9b499c71603..e8c7957605c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2105,6 +2105,7 @@ symbols! { unreachable_macro, unrestricted_attribute_tokens, unsafe_attributes, + unsafe_binders, unsafe_block_in_unsafe_fn, unsafe_cell, unsafe_cell_raw_get, @@ -2128,6 +2129,7 @@ symbols! { unwind_attributes, unwind_safe_trait, unwrap, + unwrap_binder, unwrap_or, use_extern_macros, use_nested_groups, @@ -2186,6 +2188,7 @@ symbols! { windows, windows_subsystem, with_negative_coherence, + wrap_binder, wrapping_add, wrapping_div, wrapping_mul, diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index fde6887c5ab..d45cb01910f 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -354,6 +354,8 @@ pub mod random; 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; diff --git a/library/core/src/unsafe_binder.rs b/library/core/src/unsafe_binder.rs new file mode 100644 index 00000000000..98f53e07d9d --- /dev/null +++ b/library/core/src/unsafe_binder.rs @@ -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 ) + }, +} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 047f5b0c4c5..1cbf51463ea 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -544,6 +544,8 @@ pub use core::u64; #[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; diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 50dfe0a1b56..a201a9bbfed 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1841,6 +1841,9 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))), // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s. TyKind::Infer | TyKind::Err(_) | TyKind::Typeof(..) | TyKind::InferDelegation(..) => Infer, + TyKind::UnsafeBinder(..) => { + unimplemented!("unsafe binders are not supported yet") + } } } diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index c300f7cd665..bd4ce7ab922 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -819,6 +819,7 @@ impl TyCoercionStability { | TyKind::TraitObject(..) | TyKind::InferDelegation(..) | TyKind::Err(_) => Self::Reborrow, + TyKind::UnsafeBinder(..) => Self::None, }; } } diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index ed9879de13b..b679fdfadc3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -156,7 +156,8 @@ fn never_loop_expr<'tcx>( | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Repeat(e, _) - | ExprKind::DropTemps(e) => never_loop_expr(cx, e, local_labels, main_loop_id), + | ExprKind::DropTemps(e) + | ExprKind::UnsafeBinderCast(_, e, _) => never_loop_expr(cx, e, local_labels, main_loop_id), ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, local_labels, main_loop_id), ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, es.iter(), local_labels, main_loop_id), ExprKind::MethodCall(_, receiver, es, _) => { diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 311ed427cb9..521bf6a5fed 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -623,6 +623,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("DropTemps({expr})"); self.expr(expr); }, + ExprKind::UnsafeBinderCast(..) => { + unimplemented!("unsafe binders are not implemented yet"); + } } } diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 7f0363ac942..b5bb174e737 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -303,7 +303,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS | ExprKind::AddrOf(..) | ExprKind::Repeat(..) | ExprKind::Block(Block { stmts: [], .. }, _) - | ExprKind::OffsetOf(..) => (), + | ExprKind::OffsetOf(..) + | ExprKind::UnsafeBinderCast(..) => (), // Assignment might be to a local defined earlier, so don't eagerly evaluate. // Blocks with multiple statements might be expensive, so don't eagerly evaluate. diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index e318ad8671c..279025b9bf9 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -370,6 +370,10 @@ impl HirEqInterExpr<'_, '_, '_> { && self.eq_expr(l_receiver, r_receiver) && self.eq_exprs(l_args, r_args) }, + (&ExprKind::UnsafeBinderCast(lkind, le, None), &ExprKind::UnsafeBinderCast(rkind, re, None)) => + lkind == rkind && self.eq_expr(le, re), + (&ExprKind::UnsafeBinderCast(lkind, le, Some(lt)), &ExprKind::UnsafeBinderCast(rkind, re, Some(rt))) => + lkind == rkind && self.eq_expr(le, re) && self.eq_ty(lt, rt), (&ExprKind::OffsetOf(l_container, l_fields), &ExprKind::OffsetOf(r_container, r_fields)) => { self.eq_ty(l_container, r_container) && over(l_fields, r_fields, |l, r| l.name == r.name) }, @@ -424,6 +428,7 @@ impl HirEqInterExpr<'_, '_, '_> { | &ExprKind::Type(..) | &ExprKind::Unary(..) | &ExprKind::Yield(..) + | &ExprKind::UnsafeBinderCast(..) // --- Special cases that do not have a positive branch. @@ -1032,6 +1037,13 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { std::mem::discriminant(&lop).hash(&mut self.s); self.hash_expr(le); }, + ExprKind::UnsafeBinderCast(kind, expr, ty) => { + std::mem::discriminant(&kind).hash(&mut self.s); + self.hash_expr(expr); + if let Some(ty) = ty { + self.hash_ty(ty); + } + } ExprKind::Err(_) => {}, } } @@ -1241,6 +1253,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { TyKind::Typeof(anon_const) => { self.hash_body(anon_const.body); }, + TyKind::UnsafeBinder(binder) => { + self.hash_ty(binder.inner_ty); + } TyKind::Err(_) | TyKind::Infer | TyKind::Never diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 25ebe879192..088abd7c479 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -151,7 +151,8 @@ impl<'a> Sugg<'a> { | ExprKind::Become(..) | ExprKind::Struct(..) | ExprKind::Tup(..) - | ExprKind::Err(_) => Sugg::NonParen(get_snippet(expr.span)), + | ExprKind::Err(_) + | ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(get_snippet(expr.span)), ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet), ExprKind::Assign(lhs, rhs, _) => { Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span)) @@ -226,7 +227,8 @@ impl<'a> Sugg<'a> { | ast::ExprKind::While(..) | ast::ExprKind::Await(..) | ast::ExprKind::Err(_) - | ast::ExprKind::Dummy => Sugg::NonParen(snippet(expr.span)), + | ast::ExprKind::Dummy + | ast::ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(snippet(expr.span)), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp( AssocOp::DotDot, lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)), diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 351e619d7b1..ff58c6358ba 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -694,6 +694,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>( | ExprKind::Continue(_) | ExprKind::InlineAsm(_) | ExprKind::OffsetOf(..) + | ExprKind::UnsafeBinderCast(..) | ExprKind::Err(_) => (), } ControlFlow::Continue(()) diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 77c9818b66b..16b7e7aa709 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -413,7 +413,8 @@ pub(crate) fn format_expr( ast::ExprKind::FormatArgs(..) | ast::ExprKind::Type(..) | ast::ExprKind::IncludedBytes(..) - | ast::ExprKind::OffsetOf(..) => { + | ast::ExprKind::OffsetOf(..) + | ast::ExprKind::UnsafeBinderCast(..) => { // These don't normally occur in the AST because macros aren't expanded. However, // rustfmt tries to parse macro arguments when formatting macros, so it's not totally // impossible for rustfmt to come across one of these nodes when formatting a file. diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index dd4a788c002..f8b713117f4 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1016,6 +1016,31 @@ impl Rewrite for ast::Ty { let pat = pat.rewrite_result(context, shape)?; Ok(format!("{ty} is {pat}")) } + ast::TyKind::UnsafeBinder(ref binder) => { + let mut result = String::new(); + if let Some(ref lifetime_str) = + rewrite_bound_params(context, shape, &binder.generic_params) + { + result.push_str("unsafe<"); + result.push_str(lifetime_str); + result.push_str("> "); + } + + let inner_ty_shape = if context.use_block_indent() { + shape + .offset_left(result.len()) + .max_width_error(shape.width, self.span())? + } else { + shape + .visual_indent(result.len()) + .sub_width(result.len()) + .max_width_error(shape.width, self.span())? + }; + + let rewrite = binder.inner_ty.rewrite_result(context, inner_ty_shape)?; + result.push_str(&rewrite); + Ok(result) + } } } } diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 0ca34a79491..ba4a4c045f1 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -504,6 +504,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::IncludedBytes(..) | ast::ExprKind::InlineAsm(..) | ast::ExprKind::OffsetOf(..) + | ast::ExprKind::UnsafeBinderCast(..) | ast::ExprKind::Let(..) | ast::ExprKind::Path(..) | ast::ExprKind::Range(..) diff --git a/src/tools/rustfmt/tests/source/unsafe-binders.rs b/src/tools/rustfmt/tests/source/unsafe-binders.rs new file mode 100644 index 00000000000..ccf7c8bb9af --- /dev/null +++ b/src/tools/rustfmt/tests/source/unsafe-binders.rs @@ -0,0 +1,11 @@ +fn foo() -> unsafe<'a> +&'a () {} + +struct Foo { + x: unsafe<'a> +&'a (), +} + +struct Bar(unsafe<'a> &'a ()); + +impl Trait for unsafe<'a> &'a () {} diff --git a/src/tools/rustfmt/tests/target/unsafe-binders.rs b/src/tools/rustfmt/tests/target/unsafe-binders.rs new file mode 100644 index 00000000000..9d308f4a894 --- /dev/null +++ b/src/tools/rustfmt/tests/target/unsafe-binders.rs @@ -0,0 +1,9 @@ +fn foo() -> unsafe<'a> &'a () {} + +struct Foo { + x: unsafe<'a> &'a (), +} + +struct Bar(unsafe<'a> &'a ()); + +impl Trait for unsafe<'a> &'a () {} diff --git a/tests/ui/feature-gates/feature-gate-unsafe-binders.rs b/tests/ui/feature-gates/feature-gate-unsafe-binders.rs new file mode 100644 index 00000000000..a2997ced4fa --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe-binders.rs @@ -0,0 +1,7 @@ +#[cfg(any())] +fn test() { + let x: unsafe<> (); + //~^ ERROR unsafe binder types are experimental +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unsafe-binders.stderr b/tests/ui/feature-gates/feature-gate-unsafe-binders.stderr new file mode 100644 index 00000000000..93997d6c14a --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe-binders.stderr @@ -0,0 +1,13 @@ +error[E0658]: unsafe binder types are experimental + --> $DIR/feature-gate-unsafe-binders.rs:3:12 + | +LL | let x: unsafe<> (); + | ^^^^^^^^^^^ + | + = note: see issue #130516 <https://github.com/rust-lang/rust/issues/130516> for more information + = help: add `#![feature(unsafe_binders)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/unsafe-binders/expr.rs b/tests/ui/unsafe-binders/expr.rs new file mode 100644 index 00000000000..d8c4c2df2cd --- /dev/null +++ b/tests/ui/unsafe-binders/expr.rs @@ -0,0 +1,13 @@ +#![feature(unsafe_binders)] +//~^ WARN the feature `unsafe_binders` is incomplete + +use std::unsafe_binder::{wrap_binder, unwrap_binder}; + +fn main() { + let x = 1; + let binder: unsafe<'a> &'a i32 = wrap_binder!(x); + //~^ ERROR unsafe binders are not yet implemented + //~| ERROR unsafe binders are not yet implemented + let rx = *unwrap_binder!(binder); + //~^ ERROR unsafe binders are not yet implemented +} diff --git a/tests/ui/unsafe-binders/expr.stderr b/tests/ui/unsafe-binders/expr.stderr new file mode 100644 index 00000000000..26fae1958b0 --- /dev/null +++ b/tests/ui/unsafe-binders/expr.stderr @@ -0,0 +1,29 @@ +warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/expr.rs:1:12 + | +LL | #![feature(unsafe_binders)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #130516 <https://github.com/rust-lang/rust/issues/130516> for more information + = note: `#[warn(incomplete_features)]` on by default + +error: unsafe binders are not yet implemented + --> $DIR/expr.rs:8:17 + | +LL | let binder: unsafe<'a> &'a i32 = wrap_binder!(x); + | ^^^^^^^^^^^^^^^^^^ + +error: unsafe binders are not yet implemented + --> $DIR/expr.rs:8:51 + | +LL | let binder: unsafe<'a> &'a i32 = wrap_binder!(x); + | ^ + +error: unsafe binders are not yet implemented + --> $DIR/expr.rs:11:30 + | +LL | let rx = *unwrap_binder!(binder); + | ^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + diff --git a/tests/ui/unsafe-binders/lifetime-resolution.rs b/tests/ui/unsafe-binders/lifetime-resolution.rs new file mode 100644 index 00000000000..aebed9599d4 --- /dev/null +++ b/tests/ui/unsafe-binders/lifetime-resolution.rs @@ -0,0 +1,19 @@ +#![feature(unsafe_binders)] +//~^ WARN the feature `unsafe_binders` is incomplete + +fn foo<'a>() { + let good: unsafe<'b> &'a &'b (); + //~^ ERROR unsafe binders are not yet implemented + + let missing: unsafe<> &'missing (); + //~^ ERROR unsafe binders are not yet implemented + //~| ERROR use of undeclared lifetime name `'missing` + + fn inner<'b>() { + let outer: unsafe<> &'a &'b (); + //~^ ERROR unsafe binders are not yet implemented + //~| can't use generic parameters from outer item + } +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/lifetime-resolution.stderr b/tests/ui/unsafe-binders/lifetime-resolution.stderr new file mode 100644 index 00000000000..7a8ce929df1 --- /dev/null +++ b/tests/ui/unsafe-binders/lifetime-resolution.stderr @@ -0,0 +1,65 @@ +error[E0261]: use of undeclared lifetime name `'missing` + --> $DIR/lifetime-resolution.rs:8:28 + | +LL | let missing: unsafe<> &'missing (); + | ^^^^^^^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the type lifetime-generic with a new `'missing` lifetime + | +LL | let missing: unsafe<'missing, > &'missing (); + | +++++++++ +help: consider introducing lifetime `'missing` here + | +LL | fn foo<'missing, 'a>() { + | +++++++++ + +error[E0401]: can't use generic parameters from outer item + --> $DIR/lifetime-resolution.rs:13:30 + | +LL | fn foo<'a>() { + | -- lifetime parameter from outer item +... +LL | let outer: unsafe<> &'a &'b (); + | ^^ use of generic parameter from outer item + | +help: consider making the type lifetime-generic with a new `'a` lifetime + | +LL | let outer: unsafe<'a, > &'a &'b (); + | +++ +help: consider introducing lifetime `'a` here + | +LL | fn inner<'a, 'b>() { + | +++ + +warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/lifetime-resolution.rs:1:12 + | +LL | #![feature(unsafe_binders)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #130516 <https://github.com/rust-lang/rust/issues/130516> for more information + = note: `#[warn(incomplete_features)]` on by default + +error: unsafe binders are not yet implemented + --> $DIR/lifetime-resolution.rs:5:15 + | +LL | let good: unsafe<'b> &'a &'b (); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: unsafe binders are not yet implemented + --> $DIR/lifetime-resolution.rs:8:18 + | +LL | let missing: unsafe<> &'missing (); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: unsafe binders are not yet implemented + --> $DIR/lifetime-resolution.rs:13:20 + | +LL | let outer: unsafe<> &'a &'b (); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0261, E0401. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/unsafe-binders/simple.rs b/tests/ui/unsafe-binders/simple.rs new file mode 100644 index 00000000000..cebff2cbfb8 --- /dev/null +++ b/tests/ui/unsafe-binders/simple.rs @@ -0,0 +1,7 @@ +#![feature(unsafe_binders)] +//~^ WARN the feature `unsafe_binders` is incomplete + +fn main() { + let x: unsafe<'a> &'a (); + //~^ ERROR unsafe binders are not yet implemented +} diff --git a/tests/ui/unsafe-binders/simple.stderr b/tests/ui/unsafe-binders/simple.stderr new file mode 100644 index 00000000000..a21dbd00b4c --- /dev/null +++ b/tests/ui/unsafe-binders/simple.stderr @@ -0,0 +1,17 @@ +warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/simple.rs:1:12 + | +LL | #![feature(unsafe_binders)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #130516 <https://github.com/rust-lang/rust/issues/130516> for more information + = note: `#[warn(incomplete_features)]` on by default + +error: unsafe binders are not yet implemented + --> $DIR/simple.rs:5:12 + | +LL | let x: unsafe<'a> &'a (); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + |
