Skip to content

Commit 2303939

Browse files
committed
Don't visit foreign function bodies when lowering ast to hir
Previously the existence of bodies inside a foreign function block would cause a panic in the hir `NodeCollector` during its collection of crate bodies to compute a crate hash: https://github.com/rust-lang/rust/blob/e59b08e62ea691916d2f063cac5aab4634128022/src/librustc_middle/hir/map/collector.rs#L154-L158 The collector walks the hir tree and creates a map of hir nodes, then attaching bodies in the crate to their owner in the map. For a code like ```rust extern "C" { fn f() { fn g() {} } } ``` The crate bodies include the body of the function `g`. But foreign functions cannot have bodies, and while the parser AST permits a foreign function to have a body, the hir doesn't. This means that the body of `f` is not present in the hir, and so neither is `g`. So when the `NodeCollector` finishes the walking the hir, it has no record of `g`, cannot find an owner for the body of `g` it sees in the crate bodies, and blows up. Why do the crate bodies include the body of `g`? The AST walker has a need a for walking function bodies, and FFIs share the same AST node as functions in other contexts. There are at least two options to fix this: - Don't unwrap the map entry for an hir node in the `NodeCollector` - Modifier the ast->hir lowering visitor to ignore foreign function blocks I don't think the first is preferrable, since we want to know when we can't find a body for an hir node that we thought had one (dropping this information may lead to an invalid hash). So this commit implements the second option. Closes #74120
1 parent 8ac1525 commit 2303939

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed

src/librustc_ast_lowering/item.rs

+39-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use rustc_ast::ast::*;
66
use rustc_ast::attr;
77
use rustc_ast::node_id::NodeMap;
88
use rustc_ast::ptr::P;
9-
use rustc_ast::visit::{self, AssocCtxt, Visitor};
9+
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
10+
use rustc_ast::walk_list;
1011
use rustc_data_structures::fx::FxHashSet;
1112
use rustc_errors::struct_span_err;
1213
use rustc_hir as hir;
@@ -76,6 +77,43 @@ impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> {
7677
}
7778
}
7879

80+
// Forked from the original method because we don't want to descend into foreign function
81+
// blocks. Such blocks are semantically invalid and the hir does not preserve them, so lowering
82+
// items contained in them may be unexpected by later passes.
83+
fn visit_foreign_item(&mut self, item: &'a ForeignItem) {
84+
let Item { id: _, span: _, ident, ref vis, ref attrs, ref kind, tokens: _ } = *item;
85+
self.visit_vis(vis);
86+
self.visit_ident(ident);
87+
walk_list!(self, visit_attribute, attrs);
88+
match kind {
89+
ForeignItemKind::Static(ty, _, expr) => {
90+
self.visit_ty(ty);
91+
walk_list!(self, visit_expr, expr);
92+
}
93+
ForeignItemKind::Fn(_, sig, generics, body) => {
94+
self.visit_generics(generics);
95+
let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, body.as_deref());
96+
match kind {
97+
FnKind::Fn(_, _, sig, _, _) => {
98+
self.visit_fn_header(&sig.header);
99+
visit::walk_fn_decl(self, &sig.decl);
100+
}
101+
FnKind::Closure(decl, _) => {
102+
visit::walk_fn_decl(self, decl);
103+
}
104+
}
105+
}
106+
ForeignItemKind::TyAlias(_, generics, bounds, ty) => {
107+
self.visit_generics(generics);
108+
walk_list!(self, visit_param_bound, bounds);
109+
walk_list!(self, visit_ty, ty);
110+
}
111+
ForeignItemKind::MacCall(mac) => {
112+
self.visit_mac(mac);
113+
}
114+
}
115+
}
116+
79117
fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) {
80118
self.lctx.with_hir_id_owner(item.id, |lctx| match ctxt {
81119
AssocCtxt::Trait => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Previously this ICE'd because `fn g()` would be lowered, but the block associated with `fn f()`
2+
// wasn't.
3+
4+
// compile-flags: --crate-type=lib
5+
6+
extern "C" {
7+
fn f() {
8+
//~^ incorrect function inside `extern` block
9+
fn g() {}
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: incorrect function inside `extern` block
2+
--> $DIR/issue-74120-lowering-of-ffi-block-bodies.rs:7:8
3+
|
4+
LL | extern "C" {
5+
| ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body
6+
LL | fn f() {
7+
| ________^___-
8+
| | |
9+
| | cannot have a body
10+
LL | |
11+
LL | | fn g() {}
12+
LL | | }
13+
| |_____- help: remove the invalid body: `;`
14+
|
15+
= help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block
16+
= note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html
17+
18+
error: aborting due to previous error
19+

0 commit comments

Comments
 (0)