Skip to content

Commit

Permalink
Rollup merge of rust-lang#74204 - ayazhafiz:i/74120, r=eddyb
Browse files Browse the repository at this point in the history
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 rust-lang#74120
  • Loading branch information
Dylan-DPC authored Aug 16, 2020
2 parents 009551f + d442bf7 commit 67f02ea
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 1 deletion.
14 changes: 13 additions & 1 deletion src/librustc_ast_lowering/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 => {
Expand Down
11 changes: 11 additions & 0 deletions src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs
Original file line number Diff line number Diff line change
@@ -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() {}
}
}
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 67f02ea

Please sign in to comment.