Skip to content

Commit 5c81eea

Browse files
Preserve expressions that get a DefId
Namely closures and `async` blocks. We have to make a few modifications to closures to make them compile.
1 parent 07ccf3c commit 5c81eea

File tree

1 file changed

+82
-1
lines changed

1 file changed

+82
-1
lines changed

src/librustc_interface/util.rs

+82-1
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,12 @@ impl<'a, 'b> ReplaceBodyWithLoop<'a, 'b> {
615615
matches!(sig.header.constness, ast::Const::Yes(_))
616616
|| matches!(&sig.decl.output, ast::FnRetTy::Ty(ty) if ty.contains_impl_trait())
617617
}
618+
619+
/// Keep some `Expr`s that are ultimately assigned `DefId`s. This keeps the `HirId` parent
620+
/// mappings correct even after this pass runs.
621+
fn should_preserve_expr(e: &ast::Expr) -> bool {
622+
matches!(&e.kind, ast::ExprKind::Closure(..) | ast::ExprKind::Async(..))
623+
}
618624
}
619625

620626
impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
@@ -644,6 +650,75 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
644650
self.run(true, |s| noop_visit_anon_const(c, s))
645651
}
646652

653+
fn filter_map_expr(&mut self, mut e: P<ast::Expr>) -> Option<P<ast::Expr>> {
654+
if self.ignore_item {
655+
return noop_filter_map_expr(e, self);
656+
}
657+
658+
if let ast::ExprKind::Closure(.., decl, expr, _) = &mut e.kind {
659+
// Replacing a closure body and removing its callsites will break inference. Give
660+
// all closures the signature `|| -> !` to work around this.
661+
decl.inputs = vec![];
662+
if let ast::FnRetTy::Default(_) = decl.output {
663+
decl.output = ast::FnRetTy::Ty(P(ast::Ty {
664+
id: self.resolver.next_node_id(),
665+
span: rustc_span::DUMMY_SP,
666+
kind: ast::TyKind::Never,
667+
}));
668+
}
669+
670+
// Replace `|| expr` with `|| { expr }` so that `visit_block` gets called on the
671+
// closure body.
672+
if !matches!(expr.kind, ast::ExprKind::Block(..)) {
673+
let new_stmt = ast::Stmt {
674+
kind: ast::StmtKind::Expr(expr.clone()),
675+
id: self.resolver.next_node_id(),
676+
span: expr.span,
677+
};
678+
679+
let new_block = ast::Block {
680+
stmts: vec![new_stmt],
681+
rules: BlockCheckMode::Default,
682+
id: self.resolver.next_node_id(),
683+
span: expr.span,
684+
};
685+
686+
expr.kind = ast::ExprKind::Block(P(new_block), None);
687+
}
688+
}
689+
690+
if Self::should_preserve_expr(&e) {
691+
self.run(false, |s| noop_filter_map_expr(e, s))
692+
} else {
693+
noop_filter_map_expr(e, self)
694+
}
695+
}
696+
697+
fn flat_map_stmt(&mut self, s: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
698+
if self.ignore_item {
699+
return noop_flat_map_stmt(s, self);
700+
}
701+
702+
let ast::Stmt { id, span, .. } = s;
703+
match s.kind {
704+
// Replace `let x = || {};` with `|| {};`
705+
ast::StmtKind::Local(mut local) if local.init.is_some() => self
706+
.filter_map_expr(local.init.take().unwrap())
707+
.into_iter()
708+
.map(|e| ast::Stmt { kind: ast::StmtKind::Semi(e), id, span })
709+
.collect(),
710+
711+
// Replace `|| {}` with `|| {};`
712+
ast::StmtKind::Expr(expr) => self
713+
.filter_map_expr(expr)
714+
.into_iter()
715+
.map(|e| ast::Stmt { kind: ast::StmtKind::Semi(e), id, span })
716+
.collect(),
717+
718+
_ => noop_flat_map_stmt(s, self),
719+
}
720+
}
721+
647722
fn visit_block(&mut self, b: &mut P<ast::Block>) {
648723
fn stmt_to_block(
649724
rules: ast::BlockCheckMode,
@@ -699,7 +774,13 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
699774
for s in b.stmts {
700775
let old_blocks = self.nested_blocks.replace(vec![]);
701776

702-
stmts.extend(self.flat_map_stmt(s).into_iter().filter(|s| s.is_item()));
777+
stmts.extend(self.flat_map_stmt(s).into_iter().filter(|s| {
778+
s.is_item()
779+
|| matches!(
780+
&s.kind,
781+
ast::StmtKind::Semi(expr) if Self::should_preserve_expr(expr)
782+
)
783+
}));
703784

704785
// we put a Some in there earlier with that replace(), so this is valid
705786
let new_blocks = self.nested_blocks.take().unwrap();

0 commit comments

Comments
 (0)