diff options
| author | Tyler Mandry <tmandry@gmail.com> | 2020-08-16 14:59:20 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-08-16 14:59:20 -0700 |
| commit | 8eba138d5b502cbcb4e0deabccb14d4ae572ead1 (patch) | |
| tree | e42d09f93385509d4b771a1745b86bb2acc36e19 | |
| parent | 9b4db695b0ab13885a61deb1b2e4d6599b8c5bbc (diff) | |
| parent | d442bf7162647743f941977a5154676322a5614b (diff) | |
| download | rust-8eba138d5b502cbcb4e0deabccb14d4ae572ead1.tar.gz rust-8eba138d5b502cbcb4e0deabccb14d4ae572ead1.zip | |
Rollup merge of #74204 - ayazhafiz:i/74120, r=eddyb
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
3 files changed, 43 insertions, 1 deletions
diff --git a/src/librustc_ast_lowering/item.rs b/src/librustc_ast_lowering/item.rs index 5186e62fbf9..699f5c9778a 100644 --- a/src/librustc_ast_lowering/item.rs +++ b/src/librustc_ast_lowering/item.rs @@ -5,7 +5,7 @@ use crate::Arena; use rustc_ast::ast::*; use rustc_ast::node_id::NodeMap; use rustc_ast::ptr::P; -use rustc_ast::visit::{self, AssocCtxt, Visitor}; +use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; @@ -75,6 +75,18 @@ impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> { } } + fn visit_fn(&mut self, fk: FnKind<'a>, sp: Span, _: NodeId) { + match fk { + FnKind::Fn(FnCtxt::Foreign, _, sig, _, _) => { + self.visit_fn_header(&sig.header); + visit::walk_fn_decl(self, &sig.decl); + // Don't visit the foreign function body even if it has one, since lowering the + // body would have no meaning and will have already been caught as a parse error. + } + _ => visit::walk_fn(self, fk, sp), + } + } + fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) { self.lctx.with_hir_id_owner(item.id, |lctx| match ctxt { AssocCtxt::Trait => { diff --git a/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs b/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs new file mode 100644 index 00000000000..a84065e0218 --- /dev/null +++ b/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs @@ -0,0 +1,11 @@ +// Previously this ICE'd because `fn g()` would be lowered, but the block associated with `fn f()` +// wasn't. + +// compile-flags: --crate-type=lib + +extern "C" { + fn f() { + //~^ incorrect function inside `extern` block + fn g() {} + } +} diff --git a/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.stderr b/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.stderr new file mode 100644 index 00000000000..d4a9ca3e7c6 --- /dev/null +++ b/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.stderr @@ -0,0 +1,19 @@ +error: incorrect function inside `extern` block + --> $DIR/issue-74120-lowering-of-ffi-block-bodies.rs:7:8 + | +LL | extern "C" { + | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body +LL | fn f() { + | ________^___- + | | | + | | cannot have a body +LL | | +LL | | fn g() {} +LL | | } + | |_____- help: remove the invalid body: `;` + | + = 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 + = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html + +error: aborting due to previous error + |
