Skip to content

Commit

Permalink
Rollup merge of #134140 - compiler-errors:unsafe-binders-ast, r=oli-obk
Browse files Browse the repository at this point in the history
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
  • Loading branch information
matthiaskrgr authored Dec 13, 2024
2 parents 9f6b07e + b8c5a0f commit 5c9b227
Show file tree
Hide file tree
Showing 53 changed files with 616 additions and 18 deletions.
29 changes: 28 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,7 @@ impl Expr {
| ExprKind::Tup(_)
| ExprKind::Type(..)
| ExprKind::Underscore
| ExprKind::UnsafeBinderCast(..)
| ExprKind::While(..)
| ExprKind::Err(_)
| ExprKind::Dummy => ExprPrecedence::Unambiguous,
Expand Down Expand Up @@ -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 }`
Expand Down Expand Up @@ -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),

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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`.
Expand All @@ -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,...)`).
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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 => {}
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_ast/src/util/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
| Underscore
| Yeet(..)
| Yield(..)
| UnsafeBinderCast(..)
| Err(..)
| Dummy => return false,
}
Expand Down Expand Up @@ -232,6 +233,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
| Paren(_)
| Try(_)
| Yeet(None)
| UnsafeBinderCast(..)
| Err(_)
| Dummy => break None,
}
Expand All @@ -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,
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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 => {}
}
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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*/");
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_builtin_macros/src/assert/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ impl<'cx, 'a> Context<'cx, 'a> {
| ExprKind::While(_, _, _)
| ExprKind::Yeet(_)
| ExprKind::Become(_)
| ExprKind::Yield(_) => {}
| ExprKind::Yield(_)
| ExprKind::UnsafeBinderCast(..) => {}
}
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 19 additions & 2 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1740,6 +1740,7 @@ impl Expr<'_> {
| ExprKind::Struct(..)
| ExprKind::Tup(_)
| ExprKind::Type(..)
| ExprKind::UnsafeBinderCast(..)
| ExprKind::Err(_) => ExprPrecedence::Unambiguous,

ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
Expand Down Expand Up @@ -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, _, _) => {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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),
}
Expand Down Expand Up @@ -2780,6 +2789,12 @@ pub struct BareFnTy<'hir> {
pub param_names: &'hir [Ident],
}

#[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,
Expand Down Expand Up @@ -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, ...)`).
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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));
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
Expand Down
30 changes: 30 additions & 0 deletions compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 5c9b227

Please sign in to comment.