diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 33dd59e99ff05..6ca190c9adddf 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -554,4 +554,8 @@ impl CrateStore for CStore { ) -> ExpnId { self.get_crate_data(cnum).expn_hash_to_expn_id(sess, index_guess, hash) } + + fn import_source_files(&self, sess: &Session, cnum: CrateNum) { + self.get_crate_data(cnum).imported_source_files(sess); + } } diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index 11f54ea66fa51..6a88e1235374c 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -495,6 +495,20 @@ impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { .entry(index) .or_insert_with(|| { let stable_id = file_index_to_stable_id[&index].translate(tcx); + + // If this `SourceFile` is from a foreign crate, then make sure + // that we've imported all of the source files from that crate. + // This has usually already been done during macro invocation. + // However, when encoding query results like `TypeckResults`, + // we might encode an `AdtDef` for a foreign type (because it + // was referenced in the body of the function). There is no guarantee + // that we will load the source files from that crate during macro + // expansion, so we use `import_source_files` to ensure that the foreign + // source files are actually imported before we call `source_file_by_stable_id`. + if stable_id.cnum != LOCAL_CRATE { + self.tcx.cstore_untracked().import_source_files(self.tcx.sess, stable_id.cnum); + } + source_map .source_file_by_stable_id(stable_id) .expect("failed to lookup `SourceFile` in new context") diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index 59e7abc2ea3bd..281fc887633d9 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -201,6 +201,12 @@ pub trait CrateStore: std::fmt::Debug { index_guess: u32, hash: ExpnHash, ) -> ExpnId; + + /// Imports all `SourceFile`s from the given crate into the current session. + /// This normally happens automatically when we decode a `Span` from + /// that crate's metadata - however, the incr comp cache needs + /// to trigger this manually when decoding a foreign `Span` + fn import_source_files(&self, sess: &Session, cnum: CrateNum); } pub type CrateStoreDyn = dyn CrateStore + sync::Sync; diff --git a/src/test/incremental/issue-92163-missing-sourcefile/auxiliary/first_crate.rs b/src/test/incremental/issue-92163-missing-sourcefile/auxiliary/first_crate.rs new file mode 100644 index 0000000000000..134afd4fbb142 --- /dev/null +++ b/src/test/incremental/issue-92163-missing-sourcefile/auxiliary/first_crate.rs @@ -0,0 +1,3 @@ +pub enum Foo { + Variant +} diff --git a/src/test/incremental/issue-92163-missing-sourcefile/auxiliary/second_crate.rs b/src/test/incremental/issue-92163-missing-sourcefile/auxiliary/second_crate.rs new file mode 100644 index 0000000000000..ab7961f382370 --- /dev/null +++ b/src/test/incremental/issue-92163-missing-sourcefile/auxiliary/second_crate.rs @@ -0,0 +1,10 @@ +// compile-flags:--extern first_crate + +// Note: adding `first_crate` to the extern prelude +// (instead of using `extern_crate`) appears to be necessary to +// trigger the original incremental compilation bug. +// I'm not entirely sure why this is the case + +pub fn make_it() -> first_crate::Foo { + panic!() +} diff --git a/src/test/incremental/issue-92163-missing-sourcefile/issue_92163_main.rs b/src/test/incremental/issue-92163-missing-sourcefile/issue_92163_main.rs new file mode 100644 index 0000000000000..e9219c6f5ab29 --- /dev/null +++ b/src/test/incremental/issue-92163-missing-sourcefile/issue_92163_main.rs @@ -0,0 +1,31 @@ +// aux-build:first_crate.rs +// aux-build:second_crate.rs +// revisions:rpass1 rpass2 + +// Regression test for issue #92163 +// Under certain circumstances, we may end up trying to +// decode a foreign `Span` from the incremental cache, without previously +// having imported the `SourceFile`s from the owning crate. This can happen +// if the `Span` comes from a transitive dependency (so we never try to resolve +// items from the crate during expansion/resolution). +// +// Previously, this would result in an ICE, since we would not have loaded +// the corresponding `SourceFile` for the `StableSourceFileId` we decoded. +// This test verifies that the decoding of a foreign `Span` will always +// try to import the `SourceFile`s from the foreign crate, instead of +// relying on that having already happened during expansion. + +extern crate second_crate; + +pub struct Outer; + +impl Outer { + pub fn use_it() { + // This returns `first_crate::Foo`, causing + // us to encode the `AdtDef `first_crate::Foo` (along with its `Span`s) + // into the query cache for the `TypeckResults` for this function. + second_crate::make_it(); + } +} + +fn main() {}