Skip to content

Commit fe98f53

Browse files
committed
Implement super let.
1 parent 809267a commit fe98f53

File tree

13 files changed

+124
-48
lines changed

13 files changed

+124
-48
lines changed

compiler/rustc_ast/src/ast.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,7 @@ pub enum MacStmtStyle {
11001100
#[derive(Clone, Encodable, Decodable, Debug)]
11011101
pub struct Local {
11021102
pub id: NodeId,
1103+
pub super_: Option<Span>,
11031104
pub pat: P<Pat>,
11041105
pub ty: Option<P<Ty>>,
11051106
pub kind: LocalKind,
@@ -3806,7 +3807,7 @@ mod size_asserts {
38063807
static_assert_size!(Item, 136);
38073808
static_assert_size!(ItemKind, 64);
38083809
static_assert_size!(LitKind, 24);
3809-
static_assert_size!(Local, 80);
3810+
static_assert_size!(Local, 96);
38103811
static_assert_size!(MetaItemLit, 40);
38113812
static_assert_size!(Param, 40);
38123813
static_assert_size!(Pat, 72);

compiler/rustc_ast/src/mut_visit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,8 @@ fn walk_parenthesized_parameter_data<T: MutVisitor>(vis: &mut T, args: &mut Pare
705705
}
706706

707707
fn walk_local<T: MutVisitor>(vis: &mut T, local: &mut P<Local>) {
708-
let Local { id, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut();
708+
let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut();
709+
visit_opt(super_, |sp| vis.visit_span(sp));
709710
vis.visit_id(id);
710711
visit_attrs(vis, attrs);
711712
vis.visit_pat(pat);

compiler/rustc_ast/src/visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::R
324324
}
325325

326326
pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) -> V::Result {
327-
let Local { id: _, pat, ty, kind, span: _, colon_sp: _, attrs, tokens: _ } = local;
327+
let Local { id: _, super_: _, pat, ty, kind, span: _, colon_sp: _, attrs, tokens: _ } = local;
328328
walk_list!(visitor, visit_attribute, attrs);
329329
try_visit!(visitor.visit_pat(pat));
330330
visit_opt!(visitor, visit_ty, ty);

compiler/rustc_ast_lowering/src/block.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
9595

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

115116
fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {

compiler/rustc_ast_lowering/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2223,6 +2223,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
22232223
self.attrs.insert(hir_id.local_id, a);
22242224
}
22252225
let local = hir::LetStmt {
2226+
super_: None,
22262227
hir_id,
22272228
init,
22282229
pat,

compiler/rustc_ast_pretty/src/pprust/state.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,9 @@ impl<'a> State<'a> {
13361336
self.print_outer_attributes(&loc.attrs);
13371337
self.space_if_not_bol();
13381338
self.ibox(INDENT_UNIT);
1339+
if loc.super_.is_some() {
1340+
self.word_nbsp("let");
1341+
}
13391342
self.word_nbsp("let");
13401343

13411344
self.ibox(INDENT_UNIT);

compiler/rustc_expand/src/build.rs

+2
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ impl<'a> ExtCtxt<'a> {
230230
self.pat_ident(sp, ident)
231231
};
232232
let local = P(ast::Local {
233+
super_: None,
233234
pat,
234235
ty,
235236
id: ast::DUMMY_NODE_ID,
@@ -245,6 +246,7 @@ impl<'a> ExtCtxt<'a> {
245246
/// Generates `let _: Type;`, which is usually used for type assertions.
246247
pub fn stmt_let_type_only(&self, span: Span, ty: P<ast::Ty>) -> ast::Stmt {
247248
let local = P(ast::Local {
249+
super_: None,
248250
pat: self.pat_wild(span),
249251
ty: Some(ty),
250252
id: ast::DUMMY_NODE_ID,

compiler/rustc_hir/src/hir.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1817,6 +1817,8 @@ pub enum StmtKind<'hir> {
18171817
/// Represents a `let` statement (i.e., `let <pat>:<ty> = <init>;`).
18181818
#[derive(Debug, Clone, Copy, HashStable_Generic)]
18191819
pub struct LetStmt<'hir> {
1820+
/// Span of `super` in `super let`.
1821+
pub super_: Option<Span>,
18201822
pub pat: &'hir Pat<'hir>,
18211823
/// Type annotation, if any (otherwise the type will be inferred).
18221824
pub ty: Option<&'hir Ty<'hir>>,
@@ -4850,7 +4852,7 @@ mod size_asserts {
48504852
static_assert_size!(ImplItemKind<'_>, 40);
48514853
static_assert_size!(Item<'_>, 88);
48524854
static_assert_size!(ItemKind<'_>, 64);
4853-
static_assert_size!(LetStmt<'_>, 64);
4855+
static_assert_size!(LetStmt<'_>, 72);
48544856
static_assert_size!(Param<'_>, 32);
48554857
static_assert_size!(Pat<'_>, 72);
48564858
static_assert_size!(Path<'_>, 40);

compiler/rustc_hir_analysis/src/check/region.rs

+75-16
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
99
use std::mem;
1010

11+
use rustc_data_structures::fx::FxHashMap;
1112
use rustc_hir as hir;
1213
use rustc_hir::def_id::DefId;
1314
use rustc_hir::intravisit::{self, Visitor};
@@ -44,6 +45,8 @@ struct ScopeResolutionVisitor<'tcx> {
4445
scope_tree: ScopeTree,
4546

4647
cx: Context,
48+
49+
extended_super_lets: FxHashMap<hir::ItemLocalId, Option<Scope>>,
4750
}
4851

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

217-
// Every statement will clean up the temporaries created during
218-
// execution of that statement. Therefore each statement has an
219-
// associated destruction scope that represents the scope of the
220-
// statement plus its destructors, and thus the scope for which
221-
// regions referenced by the destructors need to survive.
220+
if let hir::StmtKind::Let(LetStmt { super_: Some(_), .. }) = stmt.kind {
221+
// `super let` statement does not start a new scope, such that
222+
//
223+
// { super let x = identity(&temp()); &x }.method();
224+
//
225+
// behaves exactly as
226+
//
227+
// (&identity(&temp()).method();
228+
intravisit::walk_stmt(visitor, stmt);
229+
} else {
230+
// Every statement will clean up the temporaries created during
231+
// execution of that statement. Therefore each statement has an
232+
// associated destruction scope that represents the scope of the
233+
// statement plus its destructors, and thus the scope for which
234+
// regions referenced by the destructors need to survive.
222235

223-
let prev_parent = visitor.cx.parent;
224-
visitor.enter_node_scope_with_dtor(stmt_id, true);
236+
let prev_parent = visitor.cx.parent;
237+
visitor.enter_node_scope_with_dtor(stmt_id, true);
225238

226-
intravisit::walk_stmt(visitor, stmt);
239+
intravisit::walk_stmt(visitor, stmt);
227240

228-
visitor.cx.parent = prev_parent;
241+
visitor.cx.parent = prev_parent;
242+
}
229243
}
230244

231245
fn resolve_expr<'tcx>(
@@ -485,10 +499,9 @@ fn resolve_local<'tcx>(
485499
visitor: &mut ScopeResolutionVisitor<'tcx>,
486500
pat: Option<&'tcx hir::Pat<'tcx>>,
487501
init: Option<&'tcx hir::Expr<'tcx>>,
502+
super_let: bool,
488503
) {
489-
debug!("resolve_local(pat={:?}, init={:?})", pat, init);
490-
491-
let blk_scope = visitor.cx.var_parent;
504+
debug!("resolve_local(pat={:?}, init={:?}, super_let={:?})", pat, init, super_let);
492505

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

562+
if super_let {
563+
if let Some(scope) = visitor.extended_super_lets.remove(&pat.unwrap().hir_id.local_id) {
564+
// This expression was lifetime-extended by a parent let binding. E.g.
565+
//
566+
// let a = {
567+
// super let b = temp();
568+
// &b
569+
// };
570+
//
571+
// (Which needs to behave exactly as: let a = &temp();)
572+
//
573+
// Processing of `let a` will have already decided to extend the lifetime of this
574+
// `super let` to its own var_scope. We use that scope.
575+
visitor.cx.var_parent = scope;
576+
} else {
577+
// This `super let` is not subject to lifetime extension from a parent let binding. E.g.
578+
//
579+
// identity({ super let x = temp(); &x }).method();
580+
//
581+
// (Which needs to behave exactly as: identity(&temp()).method();)
582+
//
583+
// Iterate up to the enclosing destruction scope to find the same scope that will also
584+
// be used for the result of the block itself.
585+
while let Some(s) = visitor.cx.var_parent {
586+
let parent = visitor.scope_tree.parent_map.get(&s).cloned();
587+
if let Some(Scope { data: ScopeData::Destruction, .. }) = parent {
588+
break;
589+
}
590+
visitor.cx.var_parent = parent;
591+
}
592+
}
593+
}
594+
549595
if let Some(expr) = init {
550-
record_rvalue_scope_if_borrow_expr(visitor, expr, blk_scope);
596+
record_rvalue_scope_if_borrow_expr(visitor, expr, visitor.cx.var_parent);
551597

552598
if let Some(pat) = pat {
553599
if is_binding_pat(pat) {
554600
visitor.scope_tree.record_rvalue_candidate(
555601
expr.hir_id,
556-
RvalueCandidate { target: expr.hir_id.local_id, lifetime: blk_scope },
602+
RvalueCandidate {
603+
target: expr.hir_id.local_id,
604+
lifetime: visitor.cx.var_parent,
605+
},
557606
);
558607
}
559608
}
@@ -565,6 +614,7 @@ fn resolve_local<'tcx>(
565614
if let Some(expr) = init {
566615
visitor.visit_expr(expr);
567616
}
617+
568618
if let Some(pat) = pat {
569619
visitor.visit_pat(pat);
570620
}
@@ -642,6 +692,7 @@ fn resolve_local<'tcx>(
642692
/// | [ ..., E&, ... ]
643693
/// | ( ..., E&, ... )
644694
/// | {...; E&}
695+
/// | { super let ... = E&; ... }
645696
/// | if _ { ...; E& } else { ...; E& }
646697
/// | match _ { ..., _ => E&, ... }
647698
/// | box E&
@@ -678,6 +729,13 @@ fn resolve_local<'tcx>(
678729
if let Some(subexpr) = block.expr {
679730
record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
680731
}
732+
for stmt in block.stmts {
733+
if let hir::StmtKind::Let(local) = stmt.kind
734+
&& let Some(_) = local.super_
735+
{
736+
visitor.extended_super_lets.insert(local.pat.hir_id.local_id, blk_id);
737+
}
738+
}
681739
}
682740
hir::ExprKind::If(_, then_block, else_block) => {
683741
record_rvalue_scope_if_borrow_expr(visitor, then_block, blk_id);
@@ -803,7 +861,7 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> {
803861
local_id: body.value.hir_id.local_id,
804862
data: ScopeData::Destruction,
805863
});
806-
resolve_local(this, None, Some(body.value));
864+
resolve_local(this, None, Some(body.value), false);
807865
}
808866
})
809867
}
@@ -821,7 +879,7 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> {
821879
resolve_expr(self, ex, false);
822880
}
823881
fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) {
824-
resolve_local(self, Some(l.pat), l.init)
882+
resolve_local(self, Some(l.pat), l.init, l.super_.is_some());
825883
}
826884
fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
827885
let body = self.tcx.hir_body(c.body);
@@ -850,6 +908,7 @@ pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree {
850908
cx: Context { parent: None, var_parent: None },
851909
pessimistic_yield: false,
852910
fixup_scopes: vec![],
911+
extended_super_lets: Default::default(),
853912
};
854913

855914
visitor.scope_tree.root_body = Some(body.value.hir_id);

compiler/rustc_hir_pretty/src/lib.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -960,12 +960,16 @@ impl<'a> State<'a> {
960960

961961
fn print_local(
962962
&mut self,
963+
super_: bool,
963964
init: Option<&hir::Expr<'_>>,
964965
els: Option<&hir::Block<'_>>,
965966
decl: impl Fn(&mut Self),
966967
) {
967968
self.space_if_not_bol();
968969
self.ibox(INDENT_UNIT);
970+
if super_ {
971+
self.word_nbsp("super");
972+
}
969973
self.word_nbsp("let");
970974

971975
self.ibox(INDENT_UNIT);
@@ -995,7 +999,7 @@ impl<'a> State<'a> {
995999
self.maybe_print_comment(st.span.lo());
9961000
match st.kind {
9971001
hir::StmtKind::Let(loc) => {
998-
self.print_local(loc.init, loc.els, |this| this.print_local_decl(loc));
1002+
self.print_local(loc.super_.is_some(), loc.init, loc.els, |this| this.print_local_decl(loc));
9991003
}
10001004
hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)),
10011005
hir::StmtKind::Expr(expr) => {
@@ -1494,7 +1498,7 @@ impl<'a> State<'a> {
14941498

14951499
// Print `let _t = $init;`:
14961500
let temp = Ident::from_str("_t");
1497-
self.print_local(Some(init), None, |this| this.print_ident(temp));
1501+
self.print_local(false, Some(init), None, |this| this.print_ident(temp));
14981502
self.word(";");
14991503

15001504
// Print `_t`:

compiler/rustc_hir_typeck/src/gather_locals.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub(super) struct Declaration<'a> {
4343

4444
impl<'a> From<&'a hir::LetStmt<'a>> for Declaration<'a> {
4545
fn from(local: &'a hir::LetStmt<'a>) -> Self {
46-
let hir::LetStmt { hir_id, pat, ty, span, init, els, source: _ } = *local;
46+
let hir::LetStmt { hir_id, super_: _, pat, ty, span, init, els, source: _ } = *local;
4747
Declaration { hir_id, pat, ty, span, init, origin: DeclOrigin::LocalDecl { els } }
4848
}
4949
}

compiler/rustc_parse/src/parser/stmt.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,11 @@ impl<'a> Parser<'a> {
7575

7676
let stmt = if self.token.is_keyword(kw::Super) && self.is_keyword_ahead(1, &[kw::Let]) {
7777
self.collect_tokens(None, attrs, force_collect, |this, attrs| {
78+
let super_span = this.token.span;
7879
this.expect_keyword(exp!(Super))?;
79-
this.psess.gated_spans.gate(sym::super_let, this.prev_token.span);
8080
this.expect_keyword(exp!(Let))?;
81-
let local = this.parse_local(attrs)?; // FIXME(mara): implement super let
81+
this.psess.gated_spans.gate(sym::super_let, super_span);
82+
let local = this.parse_local(Some(super_span), attrs)?;
8283
let trailing = Trailing::from(capture_semi && this.token == token::Semi);
8384
Ok((
8485
this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
@@ -89,7 +90,7 @@ impl<'a> Parser<'a> {
8990
} else if self.token.is_keyword(kw::Let) {
9091
self.collect_tokens(None, attrs, force_collect, |this, attrs| {
9192
this.expect_keyword(exp!(Let))?;
92-
let local = this.parse_local(attrs)?;
93+
let local = this.parse_local(None, attrs)?;
9394
let trailing = Trailing::from(capture_semi && this.token == token::Semi);
9495
Ok((
9596
this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
@@ -294,7 +295,7 @@ impl<'a> Parser<'a> {
294295
force_collect: ForceCollect,
295296
) -> PResult<'a, Stmt> {
296297
let stmt = self.collect_tokens(None, attrs, force_collect, |this, attrs| {
297-
let local = this.parse_local(attrs)?;
298+
let local = this.parse_local(None, attrs)?;
298299
// FIXME - maybe capture semicolon in recovery?
299300
Ok((
300301
this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
@@ -308,8 +309,8 @@ impl<'a> Parser<'a> {
308309
}
309310

310311
/// Parses a local variable declaration.
311-
fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> {
312-
let lo = self.prev_token.span;
312+
fn parse_local(&mut self, super_: Option<Span>, attrs: AttrVec) -> PResult<'a, P<Local>> {
313+
let lo = super_.unwrap_or(self.prev_token.span);
313314

314315
if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) {
315316
self.dcx().emit_err(errors::ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
@@ -411,6 +412,7 @@ impl<'a> Parser<'a> {
411412
};
412413
let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span };
413414
Ok(P(ast::Local {
415+
super_,
414416
ty,
415417
pat,
416418
kind,

0 commit comments

Comments
 (0)