Skip to content

Commit e7a17c5

Browse files
authored
Rollup merge of rust-lang#73103 - ecstatic-morse:replace-body-with-loop, r=pnkfelix
Preserve `Expr`s that have `DefId`s in `ReplaceBodyWithLoop` This PR fixes the last part of rust-lang#71104 by preserving expressions that are assigned their own `DefId`s (closures and `async` blocks) when passing them to `rustdoc`. This avoids having a `DefId` without a corresponding `HirId`. The first commit in this PR makes `-Zunpretty=everybody_loops` actually work again, and the subsequent two are miscellaneous cleanup. They should probably get merged regardless of what we end up doing here. Sample input: ```rust fn foo() -> Box<i32> { let x = |a: i64| { const FOO: i64 = 1; }; let a = 4; Box::new(a) } ``` Sample output: ```rust fn foo() -> Box<i32> { || -> ! { const FOO: i64 = 1; loop { } }; loop { } } ``` r? @ghost
2 parents aeb8dd7 + e319f20 commit e7a17c5

File tree

6 files changed

+165
-96
lines changed

6 files changed

+165
-96
lines changed

src/librustc_ast/ast.rs

+21
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub use UnsafeSource::*;
2525
use crate::ptr::P;
2626
use crate::token::{self, DelimToken};
2727
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
28+
use crate::visit::{walk_ty, Visitor};
2829

2930
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
3031
use rustc_data_structures::sync::Lrc;
@@ -1795,6 +1796,26 @@ pub struct Ty {
17951796
pub span: Span,
17961797
}
17971798

1799+
impl Ty {
1800+
/// Returns `true` if the kind of this type or any types contained within are `impl Trait`.
1801+
pub fn contains_impl_trait(&self) -> bool {
1802+
struct ContainsImplTrait(bool);
1803+
1804+
impl<'ast> Visitor<'ast> for ContainsImplTrait {
1805+
fn visit_ty(&mut self, t: &'ast Ty) {
1806+
self.0 |= matches!(t.kind, TyKind::ImplTrait(..));
1807+
if !self.0 {
1808+
walk_ty(self, t);
1809+
}
1810+
}
1811+
}
1812+
1813+
let mut vis = ContainsImplTrait(false);
1814+
vis.visit_ty(self);
1815+
vis.0
1816+
}
1817+
}
1818+
17981819
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
17991820
pub struct BareFnTy {
18001821
pub unsafety: Unsafe,

src/librustc_interface/util.rs

+122-89
Original file line numberDiff line numberDiff line change
@@ -592,97 +592,54 @@ pub fn build_output_filenames(
592592
//
593593
// [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401
594594
pub struct ReplaceBodyWithLoop<'a, 'b> {
595-
within_static_or_const: bool,
595+
ignore_item: bool,
596596
nested_blocks: Option<Vec<ast::Block>>,
597597
resolver: &'a mut Resolver<'b>,
598598
}
599599

600600
impl<'a, 'b> ReplaceBodyWithLoop<'a, 'b> {
601601
pub fn new(resolver: &'a mut Resolver<'b>) -> ReplaceBodyWithLoop<'a, 'b> {
602-
ReplaceBodyWithLoop { within_static_or_const: false, nested_blocks: None, resolver }
602+
ReplaceBodyWithLoop { ignore_item: false, nested_blocks: None, resolver }
603603
}
604604

605-
fn run<R, F: FnOnce(&mut Self) -> R>(&mut self, is_const: bool, action: F) -> R {
606-
let old_const = mem::replace(&mut self.within_static_or_const, is_const);
605+
fn run<R, F: FnOnce(&mut Self) -> R>(&mut self, ignore_item: bool, action: F) -> R {
606+
let old_ignore_item = mem::replace(&mut self.ignore_item, ignore_item);
607607
let old_blocks = self.nested_blocks.take();
608608
let ret = action(self);
609-
self.within_static_or_const = old_const;
609+
self.ignore_item = old_ignore_item;
610610
self.nested_blocks = old_blocks;
611611
ret
612612
}
613613

614-
fn should_ignore_fn(ret_ty: &ast::FnRetTy) -> bool {
615-
if let ast::FnRetTy::Ty(ref ty) = ret_ty {
616-
fn involves_impl_trait(ty: &ast::Ty) -> bool {
617-
match ty.kind {
618-
ast::TyKind::ImplTrait(..) => true,
619-
ast::TyKind::Slice(ref subty)
620-
| ast::TyKind::Array(ref subty, _)
621-
| ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. })
622-
| ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. })
623-
| ast::TyKind::Paren(ref subty) => involves_impl_trait(subty),
624-
ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()),
625-
ast::TyKind::Path(_, ref path) => {
626-
path.segments.iter().any(|seg| match seg.args.as_deref() {
627-
None => false,
628-
Some(&ast::GenericArgs::AngleBracketed(ref data)) => {
629-
data.args.iter().any(|arg| match arg {
630-
ast::AngleBracketedArg::Arg(arg) => match arg {
631-
ast::GenericArg::Type(ty) => involves_impl_trait(ty),
632-
ast::GenericArg::Lifetime(_)
633-
| ast::GenericArg::Const(_) => false,
634-
},
635-
ast::AngleBracketedArg::Constraint(c) => match c.kind {
636-
ast::AssocTyConstraintKind::Bound { .. } => true,
637-
ast::AssocTyConstraintKind::Equality { ref ty } => {
638-
involves_impl_trait(ty)
639-
}
640-
},
641-
})
642-
}
643-
Some(&ast::GenericArgs::Parenthesized(ref data)) => {
644-
any_involves_impl_trait(data.inputs.iter())
645-
|| ReplaceBodyWithLoop::should_ignore_fn(&data.output)
646-
}
647-
})
648-
}
649-
_ => false,
650-
}
651-
}
652-
653-
fn any_involves_impl_trait<'a, I: Iterator<Item = &'a P<ast::Ty>>>(mut it: I) -> bool {
654-
it.any(|subty| involves_impl_trait(subty))
655-
}
656-
657-
involves_impl_trait(ty)
658-
} else {
659-
false
660-
}
614+
fn should_ignore_fn(sig: &ast::FnSig) -> bool {
615+
matches!(sig.header.constness, ast::Const::Yes(_))
616+
|| matches!(&sig.decl.output, ast::FnRetTy::Ty(ty) if ty.contains_impl_trait())
661617
}
662618

663-
fn is_sig_const(sig: &ast::FnSig) -> bool {
664-
matches!(sig.header.constness, ast::Const::Yes(_))
665-
|| ReplaceBodyWithLoop::should_ignore_fn(&sig.decl.output)
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(..))
666623
}
667624
}
668625

669626
impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
670627
fn visit_item_kind(&mut self, i: &mut ast::ItemKind) {
671-
let is_const = match i {
628+
let ignore_item = match i {
672629
ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true,
673-
ast::ItemKind::Fn(_, ref sig, _, _) => Self::is_sig_const(sig),
630+
ast::ItemKind::Fn(_, ref sig, _, _) => Self::should_ignore_fn(sig),
674631
_ => false,
675632
};
676-
self.run(is_const, |s| noop_visit_item_kind(i, s))
633+
self.run(ignore_item, |s| noop_visit_item_kind(i, s))
677634
}
678635

679636
fn flat_map_trait_item(&mut self, i: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
680-
let is_const = match i.kind {
637+
let ignore_item = match i.kind {
681638
ast::AssocItemKind::Const(..) => true,
682-
ast::AssocItemKind::Fn(_, ref sig, _, _) => Self::is_sig_const(sig),
639+
ast::AssocItemKind::Fn(_, ref sig, _, _) => Self::should_ignore_fn(sig),
683640
_ => false,
684641
};
685-
self.run(is_const, |s| noop_flat_map_assoc_item(i, s))
642+
self.run(ignore_item, |s| noop_flat_map_assoc_item(i, s))
686643
}
687644

688645
fn flat_map_impl_item(&mut self, i: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
@@ -693,6 +650,75 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
693650
self.run(true, |s| noop_visit_anon_const(c, s))
694651
}
695652

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+
696722
fn visit_block(&mut self, b: &mut P<ast::Block>) {
697723
fn stmt_to_block(
698724
rules: ast::BlockCheckMode,
@@ -723,6 +749,11 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
723749
}
724750
}
725751

752+
if self.ignore_item {
753+
noop_visit_block(b, self);
754+
return;
755+
}
756+
726757
let empty_block = stmt_to_block(BlockCheckMode::Default, None, self.resolver);
727758
let loop_expr = P(ast::Expr {
728759
kind: ast::ExprKind::Loop(P(empty_block), None),
@@ -738,39 +769,41 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
738769
kind: ast::StmtKind::Expr(loop_expr),
739770
};
740771

741-
if self.within_static_or_const {
742-
noop_visit_block(b, self)
743-
} else {
744-
visit_clobber(b.deref_mut(), |b| {
745-
let mut stmts = vec![];
746-
for s in b.stmts {
747-
let old_blocks = self.nested_blocks.replace(vec![]);
772+
visit_clobber(b.deref_mut(), |b| {
773+
let mut stmts = vec![];
774+
for s in b.stmts {
775+
let old_blocks = self.nested_blocks.replace(vec![]);
776+
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+
}));
784+
785+
// we put a Some in there earlier with that replace(), so this is valid
786+
let new_blocks = self.nested_blocks.take().unwrap();
787+
self.nested_blocks = old_blocks;
788+
stmts.extend(new_blocks.into_iter().map(|b| block_to_stmt(b, self.resolver)));
789+
}
748790

749-
stmts.extend(self.flat_map_stmt(s).into_iter().filter(|s| s.is_item()));
791+
let mut new_block = ast::Block { stmts, ..b };
750792

751-
// we put a Some in there earlier with that replace(), so this is valid
752-
let new_blocks = self.nested_blocks.take().unwrap();
753-
self.nested_blocks = old_blocks;
754-
stmts.extend(new_blocks.into_iter().map(|b| block_to_stmt(b, self.resolver)));
793+
if let Some(old_blocks) = self.nested_blocks.as_mut() {
794+
//push our fresh block onto the cache and yield an empty block with `loop {}`
795+
if !new_block.stmts.is_empty() {
796+
old_blocks.push(new_block);
755797
}
756798

757-
let mut new_block = ast::Block { stmts, ..b };
758-
759-
if let Some(old_blocks) = self.nested_blocks.as_mut() {
760-
//push our fresh block onto the cache and yield an empty block with `loop {}`
761-
if !new_block.stmts.is_empty() {
762-
old_blocks.push(new_block);
763-
}
764-
765-
stmt_to_block(b.rules, Some(loop_stmt), &mut self.resolver)
766-
} else {
767-
//push `loop {}` onto the end of our fresh block and yield that
768-
new_block.stmts.push(loop_stmt);
799+
stmt_to_block(b.rules, Some(loop_stmt), &mut self.resolver)
800+
} else {
801+
//push `loop {}` onto the end of our fresh block and yield that
802+
new_block.stmts.push(loop_stmt);
769803

770-
new_block
771-
}
772-
})
773-
}
804+
new_block
805+
}
806+
})
774807
}
775808

776809
// in general the pretty printer processes unexpanded code, so

src/librustc_privacy/lib.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -943,11 +943,8 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
943943
let macro_module_def_id =
944944
ty::DefIdTree::parent(self.tcx, self.tcx.hir().local_def_id(md.hir_id).to_def_id())
945945
.unwrap();
946-
// FIXME(#71104) Should really be using just `as_local_hir_id` but
947-
// some `DefId` do not seem to have a corresponding HirId.
948-
let hir_id = macro_module_def_id
949-
.as_local()
950-
.and_then(|def_id| self.tcx.hir().opt_local_def_id_to_hir_id(def_id));
946+
let hir_id =
947+
macro_module_def_id.as_local().map(|def_id| self.tcx.hir().as_local_hir_id(def_id));
951948
let mut module_id = match hir_id {
952949
Some(module_id) if self.tcx.hir().is_hir_id_module(module_id) => module_id,
953950
// `module_id` doesn't correspond to a `mod`, return early (#63164, #65252).

src/librustc_session/config.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1954,9 +1954,11 @@ impl PpMode {
19541954
use PpMode::*;
19551955
use PpSourceMode::*;
19561956
match *self {
1957-
PpmSource(PpmNormal | PpmEveryBodyLoops | PpmIdentified) => false,
1957+
PpmSource(PpmNormal | PpmIdentified) => false,
19581958

1959-
PpmSource(PpmExpanded | PpmExpandedIdentified | PpmExpandedHygiene)
1959+
PpmSource(
1960+
PpmExpanded | PpmEveryBodyLoops | PpmExpandedIdentified | PpmExpandedHygiene,
1961+
)
19601962
| PpmHir(_)
19611963
| PpmHirTree(_)
19621964
| PpmMir
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Regression issue for rustdoc ICE encountered in PR #72088.
2+
// edition:2018
3+
#![feature(decl_macro)]
4+
5+
fn main() {
6+
async {
7+
macro m() {}
8+
};
9+
}

src/test/rustdoc/macro-in-closure.rs

+7
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,11 @@ fn main() {
66
|| {
77
macro m() {}
88
};
9+
10+
let _ = || {
11+
macro n() {}
12+
};
13+
14+
let cond = true;
15+
let _ = || if cond { macro n() {} } else { panic!() };
916
}

0 commit comments

Comments
 (0)