Skip to content

[experiment] first draft of init array syntax #143553

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

Draft
wants to merge 1 commit 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
20 changes: 20 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,9 @@ impl Expr {
}
}

ExprKind::InitBlock(..) => ExprPrecedence::Jump,
ExprKind::InitTail(..) => ExprPrecedence::Jump,

ExprKind::Break(_ /*label*/, value)
| ExprKind::Ret(value)
| ExprKind::Yield(YieldKind::Prefix(value))
Expand Down Expand Up @@ -1595,6 +1598,19 @@ pub struct Closure {
pub fn_arg_span: Span,
}

#[derive(Clone, Encodable, Decodable, Debug)]
pub struct InitBlock {
pub init_kw_span: Span,
pub expr: P<Expr>,
pub fn_decl: P<FnDecl>,
}

#[derive(Clone, Encodable, Decodable, Debug)]
pub enum InitKind {
Free(P<Expr>),
Array(ThinVec<P<Expr>>),
}

/// Limit types of a range (inclusive or exclusive).
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug)]
pub enum RangeLimits {
Expand Down Expand Up @@ -1712,6 +1728,10 @@ pub enum ExprKind {
Match(P<Expr>, ThinVec<Arm>, MatchKind),
/// A closure (e.g., `move |a, b, c| a + b + c`).
Closure(Box<Closure>),
/// An `init` block
InitBlock(P<InitBlock>),
/// An in-place initialization at the tail position of an `init` block
InitTail(P<InitKind>),
/// A block (`'label: { ... }`).
Block(P<Block>, Option<Label>),
/// An `async` block (`async move { ... }`),
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast/src/util/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
| Become(..)
| Break(..)
| Closure(..)
| InitBlock(..)
| InitTail(..)
| ConstBlock(..)
| Continue(..)
| FormatArgs(..)
Expand Down Expand Up @@ -230,6 +232,8 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
| Use(_, _)
| Field(_, _)
| Index(_, _, _)
| InitBlock(_)
| InitTail(_)
| Underscore
| Path(_, _)
| Continue(_)
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1750,6 +1750,19 @@ macro_rules! common_visitor_and_walkers {
try_visit!(visit_span(vis, fn_decl_span));
try_visit!(visit_span(vis, fn_arg_span));
}
ExprKind::InitBlock(expr) => {
try_visit!(vis.visit_fn(
FnKind::Closure(&$($mut)?ClosureBinder::NotPresent, &$($mut)?None, &$($mut)?expr.fn_decl, &$($mut)?expr.expr),
*span,
*id,
));
}
ExprKind::InitTail(tail) => {
match **tail {
InitKind::Free(ref $($mut)? expr) => try_visit!(vis.visit_expr(expr)),
InitKind::Array(ref $($mut)? exprs) => try_visit!(visit_exprs(vis, exprs)),
}
}
ExprKind::Block(block, opt_label) => {
visit_opt!(vis, visit_label, opt_label);
try_visit!(vis.visit_block(block));
Expand Down
112 changes: 66 additions & 46 deletions compiler/rustc_ast_lowering/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,61 +26,81 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
}

fn lower_stmts(
fn lower_one_stmt(
&mut self,
mut ast_stmts: &[Stmt],
) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) {
let mut stmts = SmallVec::<[hir::Stmt<'hir>; 8]>::new();
let mut expr = None;
while let [s, tail @ ..] = ast_stmts {
match &s.kind {
StmtKind::Let(local) => {
let hir_id = self.lower_node_id(s.id);
let local = self.lower_local(local);
self.alias_attrs(hir_id, local.hir_id);
let kind = hir::StmtKind::Let(local);
let span = self.lower_span(s.span);
stmts.push(hir::Stmt { hir_id, kind, span });
}
StmtKind::Item(it) => {
stmts.extend(self.lower_item_ref(it).into_iter().enumerate().map(
|(i, item_id)| {
let hir_id = match i {
0 => self.lower_node_id(s.id),
_ => self.next_id(),
};
let kind = hir::StmtKind::Item(item_id);
let span = self.lower_span(s.span);
hir::Stmt { hir_id, kind, span }
},
));
}
StmtKind::Expr(e) => {
let e = self.lower_expr(e);
if tail.is_empty() {
expr = Some(e);
} else {
let hir_id = self.lower_node_id(s.id);
self.alias_attrs(hir_id, e.hir_id);
let kind = hir::StmtKind::Expr(e);
s: &Stmt,
at_tail: Option<&mut Option<&'hir hir::Expr<'hir>>>,
hir_stmts: &mut impl Extend<hir::Stmt<'hir>>,
) {
match &s.kind {
StmtKind::Let(local) => {
let hir_id = self.lower_node_id(s.id);
let local = self.lower_local(local);
self.alias_attrs(hir_id, local.hir_id);
let kind = hir::StmtKind::Let(local);
let span = self.lower_span(s.span);
hir_stmts.extend([hir::Stmt { hir_id, kind, span }]);
}
StmtKind::Item(it) => {
hir_stmts.extend(self.lower_item_ref(it).into_iter().enumerate().map(
|(i, item_id)| {
let hir_id = match i {
0 => self.lower_node_id(s.id),
_ => self.next_id(),
};
let kind = hir::StmtKind::Item(item_id);
let span = self.lower_span(s.span);
stmts.push(hir::Stmt { hir_id, kind, span });
}
}
StmtKind::Semi(e) => {
hir::Stmt { hir_id, kind, span }
},
));
}
StmtKind::Expr(e) => {
if let Some(expr) = at_tail {
let e = if self.is_in_init_tail {
let hir_id = self.lower_node_id(s.id);
let kind = self.lower_implicit_init_tail(e);
self.arena.alloc(hir::Expr { hir_id, kind, span: s.span })
} else {
self.lower_expr(e)
};
*expr = Some(e);
} else {
let e = self.lower_expr(e);
let hir_id = self.lower_node_id(s.id);
self.alias_attrs(hir_id, e.hir_id);
let kind = hir::StmtKind::Semi(e);
let kind = hir::StmtKind::Expr(e);
let span = self.lower_span(s.span);
stmts.push(hir::Stmt { hir_id, kind, span });
hir_stmts.extend([hir::Stmt { hir_id, kind, span }]);
}
StmtKind::Empty => {}
StmtKind::MacCall(..) => panic!("shouldn't exist here"),
}
ast_stmts = tail;
StmtKind::Semi(e) => {
let e = self.lower_expr(e);
let hir_id = self.lower_node_id(s.id);
self.alias_attrs(hir_id, e.hir_id);
let kind = hir::StmtKind::Semi(e);
let span = self.lower_span(s.span);
hir_stmts.extend([hir::Stmt { hir_id, kind, span }]);
}
StmtKind::Empty => {}
StmtKind::MacCall(..) => panic!("shouldn't exist here"),
}
}

fn lower_stmts(
&mut self,
ast_stmts: &[Stmt],
) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) {
let mut hir_stmts = SmallVec::<[hir::Stmt<'hir>; 8]>::new();
let mut expr = None;
if let [stmts @ .., tail] = ast_stmts {
self.enter_non_init_tail_lowering(|this| {
for s in stmts {
this.lower_one_stmt(s, None, &mut hir_stmts);
}
});
self.lower_one_stmt(tail, Some(&mut expr), &mut hir_stmts);
}
(self.arena.alloc_from_iter(stmts), expr)
(self.arena.alloc_from_iter(hir_stmts), expr)
}

/// Return an `ImplTraitContext` that allows impl trait in bindings if
Expand Down
36 changes: 36 additions & 0 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
*fn_arg_span,
),
},
ExprKind::InitBlock(block) => self.lower_expr_init_block(
e.id,
block.init_kw_span,
&block.fn_decl,
&block.expr,
),
ExprKind::InitTail(kind) => self.lower_expr_init_tail(kind),
ExprKind::Gen(capture_clause, block, genblock_kind, decl_span) => {
let desugaring_kind = match genblock_kind {
GenBlockKind::Async => hir::CoroutineDesugaring::Async,
Expand Down Expand Up @@ -1250,6 +1257,35 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Closure(c)
}

fn lower_expr_init_block(
&mut self,
init_id: NodeId,
init_kw_span: Span,
fn_decl: &FnDecl,
body: &Expr,
) -> hir::ExprKind<'hir> {
let def_id = self.local_def_id(init_id);

let body_id = self.with_new_scopes(body.span, move |this| {
this.enter_init_tail_lowering(|this| {
this.lower_fn_body(fn_decl, None, |this| this.lower_expr_mut(body))
})
});
let block = self.arena.alloc(hir::InitBlock {
def_id,
body: body_id,
init_kw_span,
fn_decl: self.arena.alloc(hir::FnDecl {
inputs: &[],
output: hir::FnRetTy::DefaultReturn(body.span),
c_variadic: false,
implicit_self: hir::ImplicitSelfKind::None,
lifetime_elision_allowed: false,
}),
});
hir::ExprKind::InitBlock(block)
}

/// Destructure the LHS of complex assignments.
/// For instance, lower `(a, b) = t` to `{ let (lhs1, lhs2) = t; a = lhs1; b = lhs2; }`.
fn lower_expr_assign(
Expand Down
60 changes: 60 additions & 0 deletions compiler/rustc_ast_lowering/src/init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use rustc_ast::ptr::P as AstP;
use rustc_ast::*;
use rustc_hir as hir;

use super::LoweringContext;

impl<'hir> LoweringContext<'_, 'hir> {
pub(crate) fn lower_free_expr_init_tail(&mut self, expr: &Expr) -> hir::ExprKind<'hir> {
if let ExprKind::Block(block, label) = &expr.kind {
self.lower_block_init_tail(block, *label)
} else {
let expr = self.lower_expr(expr);
hir::ExprKind::InitTail(self.arena.alloc(hir::InitKind::Free(expr)))
}
}

/// This lower machine transforms a block expression within a in-place initialisation context
pub(crate) fn lower_block_init_tail(
&mut self,
block: &Block,
label: Option<Label>,
) -> hir::ExprKind<'hir> {
let block = self.enter_init_tail_lowering(|this| this.lower_block(block, false));
hir::ExprKind::InitTail(self.arena.alloc(hir::InitKind::Block(block, label)))
}

pub(crate) fn lower_implicit_init_tail(&mut self, expr: &Expr) -> hir::ExprKind<'hir> {
match &expr.kind {
ExprKind::Block(block, label) => self.lower_block_init_tail(block, *label),
ExprKind::Array(exprs) => self.lower_array_init_tail(&exprs[..]),
ExprKind::Struct(_) | ExprKind::Repeat(_, _) | ExprKind::Call(_, _) => {
todo!(
"pinit: these are cases we still need to implement,
especially Call *must* be lowered to a `struct` init tail expr,
and block-style `struct` supports non-member fields so that computation
and initialisation can interleave"
)
}
_ => self.lower_free_expr_init_tail(expr),
}
}

pub(crate) fn lower_array_init_tail(&mut self, exprs: &[AstP<Expr>]) -> hir::ExprKind<'hir> {
let exprs = self.arena.alloc_from_iter(exprs.iter().map(|expr| {
let hir_id = self.next_id();
let span = expr.span;
self.lower_attrs(hir_id, &expr.attrs, span);
let kind = self.lower_implicit_init_tail(expr);
hir::Expr { hir_id, kind, span }
}));
hir::ExprKind::InitTail(self.arena.alloc(hir::InitKind::Array(exprs)))
}

pub(crate) fn lower_expr_init_tail(&mut self, kind: &InitKind) -> hir::ExprKind<'hir> {
match kind {
InitKind::Free(expr) => self.lower_free_expr_init_tail(expr),
InitKind::Array(exprs) => self.lower_array_init_tail(exprs),
}
}
}
34 changes: 30 additions & 4 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ mod errors;
mod expr;
mod format;
mod index;
mod init;
mod item;
mod pat;
mod path;
Expand Down Expand Up @@ -120,6 +121,7 @@ struct LoweringContext<'a, 'hir> {
catch_scope: Option<HirId>,
loop_scope: Option<HirId>,
is_in_loop_condition: bool,
is_in_init_tail: bool,
is_in_dyn_type: bool,

current_hir_id_owner: hir::OwnerId,
Expand Down Expand Up @@ -173,6 +175,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
catch_scope: None,
loop_scope: None,
is_in_loop_condition: false,
is_in_init_tail: false,
is_in_dyn_type: false,
coroutine_kind: None,
task_context: None,
Expand Down Expand Up @@ -886,27 +889,50 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn with_new_scopes<T>(&mut self, scope_span: Span, f: impl FnOnce(&mut Self) -> T) -> T {
let current_item = self.current_item;
self.current_item = Some(scope_span);

let was_in_init_tail = self.is_in_init_tail;
self.is_in_init_tail = false;
let was_in_loop_condition = self.is_in_loop_condition;
self.is_in_loop_condition = false;

let old_contract = self.contract_ensures.take();

let catch_scope = self.catch_scope.take();
let loop_scope = self.loop_scope.take();

let ret = f(self);

self.catch_scope = catch_scope;
self.loop_scope = loop_scope;

self.contract_ensures = old_contract;

self.is_in_init_tail = was_in_init_tail;
self.is_in_loop_condition = was_in_loop_condition;

self.current_item = current_item;

ret
}

/// Signal from the lowering context that the tail value of a current block
/// should be in-place initialised and should be lowered as an `init` tail.
#[inline]
fn enter_init_tail_lowering<T>(&mut self, lowering: impl FnOnce(&mut Self) -> T) -> T {
let was_in_init_tail = self.is_in_init_tail;
self.is_in_init_tail = true;
let ret = lowering(self);
self.is_in_init_tail = was_in_init_tail;
ret
}

/// Signal from the lowering context that blocks in statements of a current block
/// shall not be in-place initialised.
#[inline]
fn enter_non_init_tail_lowering<T>(&mut self, lowering: impl FnOnce(&mut Self) -> T) -> T {
let was_in_init_tail = self.is_in_init_tail;
self.is_in_init_tail = false;
let ret = lowering(self);
self.is_in_init_tail = was_in_init_tail;
ret
}

fn lower_attrs(
&mut self,
id: HirId,
Expand Down
Loading
Loading