Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement pin!() using super let #139114

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,7 @@ pub enum MacStmtStyle {
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct Local {
pub id: NodeId,
pub super_: Option<Span>,
pub pat: P<Pat>,
pub ty: Option<P<Ty>>,
pub kind: LocalKind,
Expand Down Expand Up @@ -3806,7 +3807,7 @@ mod size_asserts {
static_assert_size!(Item, 136);
static_assert_size!(ItemKind, 64);
static_assert_size!(LitKind, 24);
static_assert_size!(Local, 80);
static_assert_size!(Local, 96);
static_assert_size!(MetaItemLit, 40);
static_assert_size!(Param, 40);
static_assert_size!(Pat, 72);
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,8 @@ fn walk_parenthesized_parameter_data<T: MutVisitor>(vis: &mut T, args: &mut Pare
}

fn walk_local<T: MutVisitor>(vis: &mut T, local: &mut P<Local>) {
let Local { id, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut();
let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut();
visit_opt(super_, |sp| vis.visit_span(sp));
vis.visit_id(id);
visit_attrs(vis, attrs);
vis.visit_pat(pat);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::R
}

pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) -> V::Result {
let Local { id: _, pat, ty, kind, span: _, colon_sp: _, attrs, tokens: _ } = local;
let Local { id: _, super_: _, pat, ty, kind, span: _, colon_sp: _, attrs, tokens: _ } = local;
walk_list!(visitor, visit_attribute, attrs);
try_visit!(visitor.visit_pat(pat));
visit_opt!(visitor, visit_ty, ty);
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_ast_lowering/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {

fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> {
// Let statements are allowed to have impl trait in bindings.
let super_ = l.super_;
let ty = l.ty.as_ref().map(|t| {
self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable))
});
Expand All @@ -109,7 +110,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let span = self.lower_span(l.span);
let source = hir::LocalSource::Normal;
self.lower_attrs(hir_id, &l.attrs, l.span);
self.arena.alloc(hir::LetStmt { hir_id, ty, pat, init, els, span, source })
self.arena.alloc(hir::LetStmt { hir_id, super_, ty, pat, init, els, span, source })
}

fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2223,6 +2223,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.attrs.insert(hir_id.local_id, a);
}
let local = hir::LetStmt {
super_: None,
hir_id,
init,
pat,
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 @@ -512,6 +512,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(contracts, "contracts are incomplete");
gate_all!(contracts_internals, "contract internal machinery is for internal use only");
gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
gate_all!(super_let, "`super let` is experimental");

if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,9 @@ impl<'a> State<'a> {
self.print_outer_attributes(&loc.attrs);
self.space_if_not_bol();
self.ibox(INDENT_UNIT);
if loc.super_.is_some() {
self.word_nbsp("let");
}
self.word_nbsp("let");

self.ibox(INDENT_UNIT);
Expand Down
16 changes: 11 additions & 5 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2972,21 +2972,27 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}

let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{name}`"));
let name = if borrow_span.in_external_macro(self.infcx.tcx.sess.source_map()) {
// Don't name local variables in external macros.
"value".to_string()
} else {
format!("`{name}`")
};

let mut err = self.path_does_not_live_long_enough(borrow_span, &name);

if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
let region_name = annotation.emit(self, &mut err);

err.span_label(
borrow_span,
format!("`{name}` would have to be valid for `{region_name}`..."),
format!("{name} would have to be valid for `{region_name}`..."),
);

err.span_label(
drop_span,
format!(
"...but `{}` will be dropped here, when the {} returns",
name,
"...but {name} will be dropped here, when the {} returns",
self.infcx
.tcx
.opt_item_name(self.mir_def_id().to_def_id())
Expand Down Expand Up @@ -3024,7 +3030,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
} else {
err.span_label(borrow_span, "borrowed value does not live long enough");
err.span_label(drop_span, format!("`{name}` dropped here while still borrowed"));
err.span_label(drop_span, format!("{name} dropped here while still borrowed"));

borrow_spans.args_subdiag(&mut err, |args_span| {
crate::session_diagnostics::CaptureArgLabel::Capture {
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ impl<'tcx> BorrowExplanation<'tcx> {
&& let hir::def::Res::Local(hir_id) = p.res
&& let hir::Node::Pat(pat) = tcx.hir_node(hir_id)
{
err.span_label(pat.span, format!("binding `{ident}` declared here"));
if !ident.span.in_external_macro(tcx.sess.source_map()) {
err.span_label(pat.span, format!("binding `{ident}` declared here"));
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ impl<'a> ExtCtxt<'a> {
self.pat_ident(sp, ident)
};
let local = P(ast::Local {
super_: None,
pat,
ty,
id: ast::DUMMY_NODE_ID,
Expand All @@ -245,6 +246,7 @@ impl<'a> ExtCtxt<'a> {
/// Generates `let _: Type;`, which is usually used for type assertions.
pub fn stmt_let_type_only(&self, span: Span, ty: P<ast::Ty>) -> ast::Stmt {
let local = P(ast::Local {
super_: None,
pat: self.pat_wild(span),
ty: Some(ty),
id: ast::DUMMY_NODE_ID,
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 @@ -630,6 +630,8 @@ declare_features! (
(unstable, strict_provenance_lints, "1.61.0", Some(130351)),
/// Allows string patterns to dereference values to match them.
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
/// Allows `super let` statements.
(unstable, super_let, "CURRENT_RUSTC_VERSION", Some(139076)),
/// Allows subtrait items to shadow supertrait items.
(unstable, supertrait_item_shadowing, "1.86.0", Some(89151)),
/// Allows using `#[thread_local]` on `static` items.
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1817,6 +1817,8 @@ pub enum StmtKind<'hir> {
/// Represents a `let` statement (i.e., `let <pat>:<ty> = <init>;`).
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct LetStmt<'hir> {
/// Span of `super` in `super let`.
pub super_: Option<Span>,
pub pat: &'hir Pat<'hir>,
/// Type annotation, if any (otherwise the type will be inferred).
pub ty: Option<&'hir Ty<'hir>>,
Expand Down Expand Up @@ -4850,7 +4852,7 @@ mod size_asserts {
static_assert_size!(ImplItemKind<'_>, 40);
static_assert_size!(Item<'_>, 88);
static_assert_size!(ItemKind<'_>, 64);
static_assert_size!(LetStmt<'_>, 64);
static_assert_size!(LetStmt<'_>, 72);
static_assert_size!(Param<'_>, 32);
static_assert_size!(Pat<'_>, 72);
static_assert_size!(Path<'_>, 40);
Expand Down
91 changes: 75 additions & 16 deletions compiler/rustc_hir_analysis/src/check/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

use std::mem;

use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
Expand Down Expand Up @@ -44,6 +45,8 @@ struct ScopeResolutionVisitor<'tcx> {
scope_tree: ScopeTree,

cx: Context,

extended_super_lets: FxHashMap<hir::ItemLocalId, Option<Scope>>,
}

/// Records the lifetime of a local variable as `cx.var_parent`
Expand Down Expand Up @@ -214,18 +217,29 @@ fn resolve_stmt<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, stmt: &'tcx hi
let stmt_id = stmt.hir_id.local_id;
debug!("resolve_stmt(stmt.id={:?})", stmt_id);

// Every statement will clean up the temporaries created during
// execution of that statement. Therefore each statement has an
// associated destruction scope that represents the scope of the
// statement plus its destructors, and thus the scope for which
// regions referenced by the destructors need to survive.
if let hir::StmtKind::Let(LetStmt { super_: Some(_), .. }) = stmt.kind {
// `super let` statement does not start a new scope, such that
//
// { super let x = identity(&temp()); &x }.method();
//
// behaves exactly as
//
// (&identity(&temp()).method();
intravisit::walk_stmt(visitor, stmt);
} else {
// Every statement will clean up the temporaries created during
// execution of that statement. Therefore each statement has an
// associated destruction scope that represents the scope of the
// statement plus its destructors, and thus the scope for which
// regions referenced by the destructors need to survive.

let prev_parent = visitor.cx.parent;
visitor.enter_node_scope_with_dtor(stmt_id, true);
let prev_parent = visitor.cx.parent;
visitor.enter_node_scope_with_dtor(stmt_id, true);

intravisit::walk_stmt(visitor, stmt);
intravisit::walk_stmt(visitor, stmt);

visitor.cx.parent = prev_parent;
visitor.cx.parent = prev_parent;
}
}

fn resolve_expr<'tcx>(
Expand Down Expand Up @@ -485,10 +499,9 @@ fn resolve_local<'tcx>(
visitor: &mut ScopeResolutionVisitor<'tcx>,
pat: Option<&'tcx hir::Pat<'tcx>>,
init: Option<&'tcx hir::Expr<'tcx>>,
super_let: bool,
) {
debug!("resolve_local(pat={:?}, init={:?})", pat, init);

let blk_scope = visitor.cx.var_parent;
debug!("resolve_local(pat={:?}, init={:?}, super_let={:?})", pat, init, super_let);

// As an exception to the normal rules governing temporary
// lifetimes, initializers in a let have a temporary lifetime
Expand Down Expand Up @@ -546,14 +559,50 @@ fn resolve_local<'tcx>(
// A, but the inner rvalues `a()` and `b()` have an extended lifetime
// due to rule C.

if super_let {
if let Some(scope) = visitor.extended_super_lets.remove(&pat.unwrap().hir_id.local_id) {
// This expression was lifetime-extended by a parent let binding. E.g.
//
// let a = {
// super let b = temp();
// &b
// };
//
// (Which needs to behave exactly as: let a = &temp();)
//
// Processing of `let a` will have already decided to extend the lifetime of this
// `super let` to its own var_scope. We use that scope.
visitor.cx.var_parent = scope;
} else {
// This `super let` is not subject to lifetime extension from a parent let binding. E.g.
//
// identity({ super let x = temp(); &x }).method();
//
// (Which needs to behave exactly as: identity(&temp()).method();)
//
// Iterate up to the enclosing destruction scope to find the same scope that will also
// be used for the result of the block itself.
while let Some(s) = visitor.cx.var_parent {
let parent = visitor.scope_tree.parent_map.get(&s).cloned();
if let Some(Scope { data: ScopeData::Destruction, .. }) = parent {
break;
}
visitor.cx.var_parent = parent;
}
}
}

if let Some(expr) = init {
record_rvalue_scope_if_borrow_expr(visitor, expr, blk_scope);
record_rvalue_scope_if_borrow_expr(visitor, expr, visitor.cx.var_parent);

if let Some(pat) = pat {
if is_binding_pat(pat) {
visitor.scope_tree.record_rvalue_candidate(
expr.hir_id,
RvalueCandidate { target: expr.hir_id.local_id, lifetime: blk_scope },
RvalueCandidate {
target: expr.hir_id.local_id,
lifetime: visitor.cx.var_parent,
},
);
}
}
Expand All @@ -565,6 +614,7 @@ fn resolve_local<'tcx>(
if let Some(expr) = init {
visitor.visit_expr(expr);
}

if let Some(pat) = pat {
visitor.visit_pat(pat);
}
Expand Down Expand Up @@ -642,6 +692,7 @@ fn resolve_local<'tcx>(
/// | [ ..., E&, ... ]
/// | ( ..., E&, ... )
/// | {...; E&}
/// | { super let ... = E&; ... }
/// | if _ { ...; E& } else { ...; E& }
/// | match _ { ..., _ => E&, ... }
/// | box E&
Expand Down Expand Up @@ -678,6 +729,13 @@ fn resolve_local<'tcx>(
if let Some(subexpr) = block.expr {
record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
}
for stmt in block.stmts {
if let hir::StmtKind::Let(local) = stmt.kind
&& let Some(_) = local.super_
{
visitor.extended_super_lets.insert(local.pat.hir_id.local_id, blk_id);
}
}
}
hir::ExprKind::If(_, then_block, else_block) => {
record_rvalue_scope_if_borrow_expr(visitor, then_block, blk_id);
Expand Down Expand Up @@ -803,7 +861,7 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> {
local_id: body.value.hir_id.local_id,
data: ScopeData::Destruction,
});
resolve_local(this, None, Some(body.value));
resolve_local(this, None, Some(body.value), false);
}
})
}
Expand All @@ -821,7 +879,7 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> {
resolve_expr(self, ex, false);
}
fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) {
resolve_local(self, Some(l.pat), l.init)
resolve_local(self, Some(l.pat), l.init, l.super_.is_some());
}
fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
let body = self.tcx.hir_body(c.body);
Expand Down Expand Up @@ -850,6 +908,7 @@ pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree {
cx: Context { parent: None, var_parent: None },
pessimistic_yield: false,
fixup_scopes: vec![],
extended_super_lets: Default::default(),
};

visitor.scope_tree.root_body = Some(body.value.hir_id);
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -960,12 +960,16 @@ impl<'a> State<'a> {

fn print_local(
&mut self,
super_: bool,
init: Option<&hir::Expr<'_>>,
els: Option<&hir::Block<'_>>,
decl: impl Fn(&mut Self),
) {
self.space_if_not_bol();
self.ibox(INDENT_UNIT);
if super_ {
self.word_nbsp("super");
}
self.word_nbsp("let");

self.ibox(INDENT_UNIT);
Expand Down Expand Up @@ -995,7 +999,9 @@ impl<'a> State<'a> {
self.maybe_print_comment(st.span.lo());
match st.kind {
hir::StmtKind::Let(loc) => {
self.print_local(loc.init, loc.els, |this| this.print_local_decl(loc));
self.print_local(loc.super_.is_some(), loc.init, loc.els, |this| {
this.print_local_decl(loc)
});
}
hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)),
hir::StmtKind::Expr(expr) => {
Expand Down Expand Up @@ -1494,7 +1500,7 @@ impl<'a> State<'a> {

// Print `let _t = $init;`:
let temp = Ident::from_str("_t");
self.print_local(Some(init), None, |this| this.print_ident(temp));
self.print_local(false, Some(init), None, |this| this.print_ident(temp));
self.word(";");

// Print `_t`:
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/gather_locals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub(super) struct Declaration<'a> {

impl<'a> From<&'a hir::LetStmt<'a>> for Declaration<'a> {
fn from(local: &'a hir::LetStmt<'a>) -> Self {
let hir::LetStmt { hir_id, pat, ty, span, init, els, source: _ } = *local;
let hir::LetStmt { hir_id, super_: _, pat, ty, span, init, els, source: _ } = *local;
Declaration { hir_id, pat, ty, span, init, origin: DeclOrigin::LocalDecl { els } }
}
}
Expand Down
Loading
Loading