Skip to content

Commit 8317aa1

Browse files
committed
Properly track virtual path remapping during incremental compilation
When the `rust-src` component is installed, rustc will automatically remap paths from the standard library to point into the local system. The target filesystem location is stored in `tcx.sess.real_rust_source_base_dir` This is a form of global state that's not tracked by the incremental compilation system, which can lead to ICEs when the `rust-src` component is added or removed between compilation sessions. This PR fixes several bugs in incremental compilation * We now hash `real_rust_source_base_dir` when computing a crate hash, since it affects any spans that we import from foreign crates (e.g. via macros or inlining). * The `crate_hash` and `host_crate_hash` quereis now return a wrapper around `Svh`, which also hashes the value of `real_rust_source_base_dir` in the *current* compilation session. This reflects the fact that changes to this value affect how we decode values from all foreign crates. * We now properly re-compute the `name_hash` of an imported `SourceFile` when we remap a path. There was a test for this kind of issue, but it was broken when the standard library was moved to `library/std`, and was no longer actually testing anything. To ensure that this doesn't happen again, I've modified the test to build a binary that outputs a remapped standard library path (via a panic), and verifies that the precense of `rust-src` during compilation correctly affects the output of the compiled binary.
1 parent 5c13042 commit 8317aa1

File tree

14 files changed

+146
-84
lines changed

14 files changed

+146
-84
lines changed

compiler/rustc_interface/src/queries.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,8 @@ impl<'tcx> Queries<'tcx> {
345345
pub fn linker(&'tcx self) -> Result<Linker> {
346346
let dep_graph = self.dep_graph()?;
347347
let prepare_outputs = self.prepare_outputs()?;
348-
let crate_hash = self.global_ctxt()?.peek_mut().enter(|tcx| tcx.crate_hash(LOCAL_CRATE));
348+
let crate_hash =
349+
self.global_ctxt()?.peek_mut().enter(|tcx| tcx.crate_hash(LOCAL_CRATE).svh);
349350
let ongoing_codegen = self.ongoing_codegen()?;
350351

351352
let sess = self.session().clone();

compiler/rustc_metadata/src/rmeta/decoder.rs

+63-56
Original file line numberDiff line numberDiff line change
@@ -1611,7 +1611,12 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
16111611
//
16121612
// NOTE: if you update this, you might need to also update bootstrap's code for generating
16131613
// the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`.
1614-
let virtual_rust_source_base_dir = option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR")
1614+
let virtual_rust_source_base_dir = sess
1615+
.opts
1616+
.debugging_opts
1617+
.force_virtual_source_base
1618+
.as_deref()
1619+
.or(option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR"))
16151620
.map(Path::new)
16161621
.filter(|_| {
16171622
// Only spend time on further checks if we have what to translate *to*.
@@ -1623,65 +1628,67 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
16231628
// and we don't want the real path to leak into codegen/debuginfo.
16241629
!sess.opts.remap_path_prefix.iter().any(|(_from, to)| to == virtual_dir)
16251630
});
1626-
let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| {
1627-
debug!(
1628-
"try_to_translate_virtual_to_real(name={:?}): \
1631+
let try_to_translate_virtual_to_real =
1632+
|name: &mut rustc_span::FileName, name_hash: &mut u128| {
1633+
debug!(
1634+
"try_to_translate_virtual_to_real(name={:?}): \
16291635
virtual_rust_source_base_dir={:?}, real_rust_source_base_dir={:?}",
1630-
name, virtual_rust_source_base_dir, sess.real_rust_source_base_dir,
1631-
);
1636+
name, virtual_rust_source_base_dir, sess.real_rust_source_base_dir,
1637+
);
16321638

1633-
if let Some(virtual_dir) = virtual_rust_source_base_dir {
1634-
if let Some(real_dir) = &sess.real_rust_source_base_dir {
1635-
if let rustc_span::FileName::Real(old_name) = name {
1636-
if let rustc_span::RealFileName::Named(one_path) = old_name {
1637-
if let Ok(rest) = one_path.strip_prefix(virtual_dir) {
1638-
let virtual_name = one_path.clone();
1639-
1640-
// The std library crates are in
1641-
// `$sysroot/lib/rustlib/src/rust/library`, whereas other crates
1642-
// may be in `$sysroot/lib/rustlib/src/rust/` directly. So we
1643-
// detect crates from the std libs and handle them specially.
1644-
const STD_LIBS: &[&str] = &[
1645-
"core",
1646-
"alloc",
1647-
"std",
1648-
"test",
1649-
"term",
1650-
"unwind",
1651-
"proc_macro",
1652-
"panic_abort",
1653-
"panic_unwind",
1654-
"profiler_builtins",
1655-
"rtstartup",
1656-
"rustc-std-workspace-core",
1657-
"rustc-std-workspace-alloc",
1658-
"rustc-std-workspace-std",
1659-
"backtrace",
1660-
];
1661-
let is_std_lib = STD_LIBS.iter().any(|l| rest.starts_with(l));
1662-
1663-
let new_path = if is_std_lib {
1664-
real_dir.join("library").join(rest)
1665-
} else {
1666-
real_dir.join(rest)
1667-
};
1668-
1669-
debug!(
1670-
"try_to_translate_virtual_to_real: `{}` -> `{}`",
1671-
virtual_name.display(),
1672-
new_path.display(),
1673-
);
1674-
let new_name = rustc_span::RealFileName::Devirtualized {
1675-
local_path: new_path,
1676-
virtual_name,
1677-
};
1678-
*old_name = new_name;
1639+
if let Some(virtual_dir) = virtual_rust_source_base_dir {
1640+
if let Some(real_dir) = &sess.real_rust_source_base_dir {
1641+
if let rustc_span::FileName::Real(old_name) = name {
1642+
if let rustc_span::RealFileName::Named(one_path) = old_name {
1643+
if let Ok(rest) = one_path.strip_prefix(virtual_dir) {
1644+
let virtual_name = one_path.clone();
1645+
1646+
// The std library crates are in
1647+
// `$sysroot/lib/rustlib/src/rust/library`, whereas other crates
1648+
// may be in `$sysroot/lib/rustlib/src/rust/` directly. So we
1649+
// detect crates from the std libs and handle them specially.
1650+
const STD_LIBS: &[&str] = &[
1651+
"core",
1652+
"alloc",
1653+
"std",
1654+
"test",
1655+
"term",
1656+
"unwind",
1657+
"proc_macro",
1658+
"panic_abort",
1659+
"panic_unwind",
1660+
"profiler_builtins",
1661+
"rtstartup",
1662+
"rustc-std-workspace-core",
1663+
"rustc-std-workspace-alloc",
1664+
"rustc-std-workspace-std",
1665+
"backtrace",
1666+
];
1667+
let is_std_lib = STD_LIBS.iter().any(|l| rest.starts_with(l));
1668+
1669+
let new_path = if is_std_lib {
1670+
real_dir.join("library").join(rest)
1671+
} else {
1672+
real_dir.join(rest)
1673+
};
1674+
1675+
debug!(
1676+
"try_to_translate_virtual_to_real: `{}` -> `{}`",
1677+
virtual_name.display(),
1678+
new_path.display(),
1679+
);
1680+
let new_name = rustc_span::RealFileName::Devirtualized {
1681+
local_path: new_path,
1682+
virtual_name,
1683+
};
1684+
*old_name = new_name;
1685+
*name_hash = name.name_hash();
1686+
}
16791687
}
16801688
}
16811689
}
16821690
}
1683-
}
1684-
};
1691+
};
16851692

16861693
self.cdata.source_map_import_info.get_or_init(|| {
16871694
let external_source_map = self.root.source_map.decode(self);
@@ -1700,7 +1707,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
17001707
mut multibyte_chars,
17011708
mut non_narrow_chars,
17021709
mut normalized_pos,
1703-
name_hash,
1710+
mut name_hash,
17041711
..
17051712
} = source_file_to_import;
17061713

@@ -1709,7 +1716,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
17091716
// on `try_to_translate_virtual_to_real`).
17101717
// FIXME(eddyb) we could check `name_was_remapped` here,
17111718
// but in practice it seems to be always `false`.
1712-
try_to_translate_virtual_to_real(&mut name);
1719+
try_to_translate_virtual_to_real(&mut name, &mut name_hash);
17131720

17141721
let source_length = (end_pos - start_pos).to_usize();
17151722

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_hir::def::DefKind;
1212
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE};
1313
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
1414
use rustc_middle::hir::exports::Export;
15-
use rustc_middle::middle::cstore::ForeignModule;
15+
use rustc_middle::middle::cstore::{CrateHashAndState, ForeignModule, OptCrateHashAndState};
1616
use rustc_middle::middle::cstore::{CrateSource, CrateStore, EncodedMetadata};
1717
use rustc_middle::middle::exported_symbols::ExportedSymbol;
1818
use rustc_middle::middle::stability::DeprecationEntry;
@@ -199,8 +199,8 @@ provide! { <'tcx> tcx, def_id, other, cdata,
199199
})
200200
}
201201
crate_disambiguator => { cdata.root.disambiguator }
202-
crate_hash => { cdata.root.hash }
203-
crate_host_hash => { cdata.host_hash }
202+
crate_hash => { CrateHashAndState::new(tcx, cdata.root.hash) }
203+
crate_host_hash => { OptCrateHashAndState::new(tcx, cdata.host_hash) }
204204
original_crate_name => { cdata.root.name }
205205

206206
extra_filename => { cdata.root.extra_filename.clone() }

compiler/rustc_metadata/src/rmeta/encoder.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
645645
name: tcx.crate_name(LOCAL_CRATE),
646646
extra_filename: tcx.sess.opts.cg.extra_filename.clone(),
647647
triple: tcx.sess.opts.target_triple.clone(),
648-
hash: tcx.crate_hash(LOCAL_CRATE),
648+
hash: tcx.crate_hash(LOCAL_CRATE).svh,
649649
disambiguator: tcx.sess.local_crate_disambiguator(),
650650
stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(),
651651
panic_strategy: tcx.sess.panic_strategy(),
@@ -1646,8 +1646,8 @@ impl EncodeContext<'a, 'tcx> {
16461646
.map(|&cnum| {
16471647
let dep = CrateDep {
16481648
name: self.tcx.original_crate_name(cnum),
1649-
hash: self.tcx.crate_hash(cnum),
1650-
host_hash: self.tcx.crate_host_hash(cnum),
1649+
hash: self.tcx.crate_hash(cnum).svh,
1650+
host_hash: self.tcx.crate_host_hash(cnum).svh,
16511651
kind: self.tcx.dep_kind(cnum),
16521652
extra_filename: self.tcx.extra_filename(cnum),
16531653
};

compiler/rustc_middle/src/hir/map/collector.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
1515
use rustc_hir::*;
1616
use rustc_index::vec::{Idx, IndexVec};
1717
use rustc_session::{CrateDisambiguator, Session};
18-
use rustc_span::source_map::SourceMap;
1918
use rustc_span::{Span, Symbol, DUMMY_SP};
2019

2120
use std::iter::repeat;
@@ -27,8 +26,7 @@ pub(super) struct NodeCollector<'a, 'hir> {
2726
/// The crate
2827
krate: &'hir Crate<'hir>,
2928

30-
/// Source map
31-
source_map: &'a SourceMap,
29+
sess: &'a Session,
3230

3331
map: IndexVec<LocalDefId, HirOwnerData<'hir>>,
3432

@@ -126,7 +124,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
126124
let mut collector = NodeCollector {
127125
arena,
128126
krate,
129-
source_map: sess.source_map(),
127+
sess,
130128
parent_node: hir::CRATE_HIR_ID,
131129
current_dep_node_owner: LocalDefId { local_def_index: CRATE_DEF_INDEX },
132130
definitions,
@@ -174,7 +172,8 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
174172
// reproducible builds by compiling from the same directory. So we just
175173
// hash the result of the mapping instead of the mapping itself.
176174
let mut source_file_names: Vec<_> = self
177-
.source_map
175+
.sess
176+
.source_map()
178177
.files()
179178
.iter()
180179
.filter(|source_file| source_file.cnum == LOCAL_CRATE)
@@ -186,6 +185,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
186185
let crate_hash_input = (
187186
((node_hashes, upstream_crates), source_file_names),
188187
(commandline_args_hash, crate_disambiguator.to_fingerprint()),
188+
&self.sess.real_rust_source_base_dir,
189189
);
190190

191191
let mut stable_hasher = StableHasher::new();
@@ -262,7 +262,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
262262
span,
263263
"inconsistent DepNode at `{:?}` for `{}`: \
264264
current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?})",
265-
self.source_map.span_to_string(span),
265+
self.sess.source_map().span_to_string(span),
266266
node_str,
267267
self.definitions
268268
.def_path(self.current_dep_node_owner)

compiler/rustc_middle/src/middle/cstore.rs

+28
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,31 @@ pub fn used_crates(tcx: TyCtxt<'_>, prefer: LinkagePreference) -> Vec<(CrateNum,
255255
libs.sort_by_cached_key(|&(a, _)| ordering.iter().position(|x| *x == a));
256256
libs
257257
}
258+
259+
/// Holds a crate hash, together with additional global state.
260+
/// This is used as the return value of the `crate_hash` and `crate_host_hash`
261+
/// queries. When the global state changes between compilation sessions,
262+
/// the hash will change, causing the query system to re-run any queries
263+
/// which depend on the crate hash.
264+
#[derive(Clone, Debug, HashStable, Encodable, Decodable)]
265+
pub struct HashAndState<T> {
266+
pub svh: T,
267+
/// The value of `tcx.sess.real_rust_source_base_dir` in the *current* session.
268+
/// This global state is special - it influences how we decode foreign `SourceFile`s
269+
/// (and therefore `Span`s). As a result, a foreign crate's hash can be unchanged across
270+
/// two compilation sessions, but queries run on that crate can nevertheless return
271+
/// different results across those compilation sessions.
272+
/// This field is private, and never needs to be accessed by callers of `crate_hash`
273+
/// or `host_crate_hash`. Its sole purpose is to be hashed as part of the
274+
/// `HashStable` impl, so that its value influences the hash used by incremental compilation.
275+
real_rust_source_base_dir: Option<PathBuf>,
276+
}
277+
278+
impl<T> HashAndState<T> {
279+
pub fn new(tcx: TyCtxt<'tcx>, svh: T) -> HashAndState<T> {
280+
HashAndState { svh, real_rust_source_base_dir: tcx.sess.real_rust_source_base_dir.clone() }
281+
}
282+
}
283+
284+
pub type CrateHashAndState = HashAndState<Svh>;
285+
pub type OptCrateHashAndState = HashAndState<Option<Svh>>;

compiler/rustc_middle/src/query/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1207,11 +1207,11 @@ rustc_queries! {
12071207
}
12081208
// The macro which defines `rustc_metadata::provide_extern` depends on this query's name.
12091209
// Changing the name should cause a compiler error, but in case that changes, be aware.
1210-
query crate_hash(_: CrateNum) -> Svh {
1210+
query crate_hash(_: CrateNum) -> rustc_middle::middle::cstore::CrateHashAndState {
12111211
eval_always
12121212
desc { "looking up the hash a crate" }
12131213
}
1214-
query crate_host_hash(_: CrateNum) -> Option<Svh> {
1214+
query crate_host_hash(_: CrateNum) -> rustc_middle::middle::cstore::OptCrateHashAndState {
12151215
eval_always
12161216
desc { "looking up the hash of a host version of a crate" }
12171217
}

compiler/rustc_middle/src/ty/query/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, T
3434
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
3535
use rustc_data_structures::stable_hasher::StableVec;
3636
use rustc_data_structures::steal::Steal;
37-
use rustc_data_structures::svh::Svh;
3837
use rustc_data_structures::sync::Lrc;
3938
use rustc_errors::{ErrorReported, Handler};
4039
use rustc_hir as hir;

compiler/rustc_session/src/options.rs

+2
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
970970
(default: no)"),
971971
force_overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
972972
"force overflow checks on or off"),
973+
force_virtual_source_base: Option<String> = (None, parse_opt_string, [UNTRACKED],
974+
"forces using the specified directory as the virtual prefix to remap in filenames"),
973975
force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED],
974976
"force all crates to be `rustc_private` unstable (default: no)"),
975977
fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED],

compiler/rustc_session/src/session.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,9 @@ pub fn build_session(
13941394
// Only use this directory if it has a file we can expect to always find.
13951395
if candidate.join("library/std/src/lib.rs").is_file() { Some(candidate) } else { None }
13961396
};
1397+
if real_rust_source_base_dir.is_some() {
1398+
tracing::info!("Got base source dir: {:?}", real_rust_source_base_dir);
1399+
}
13971400

13981401
let asm_arch =
13991402
if target_cfg.allow_asm { InlineAsmArch::from_str(&target_cfg.arch).ok() } else { None };

compiler/rustc_span/src/lib.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,14 @@ pub enum FileName {
188188
InlineAsm(u64),
189189
}
190190

191+
impl FileName {
192+
pub fn name_hash(&self) -> u128 {
193+
let mut hasher: StableHasher = StableHasher::new();
194+
self.hash(&mut hasher);
195+
hasher.finish::<u128>()
196+
}
197+
}
198+
191199
impl std::fmt::Display for FileName {
192200
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193201
use FileName::*;
@@ -1318,11 +1326,7 @@ impl SourceFile {
13181326
let src_hash = SourceFileHash::new(hash_kind, &src);
13191327
let normalized_pos = normalize_src(&mut src, start_pos);
13201328

1321-
let name_hash = {
1322-
let mut hasher: StableHasher = StableHasher::new();
1323-
name.hash(&mut hasher);
1324-
hasher.finish::<u128>()
1325-
};
1329+
let name_hash = name.name_hash();
13261330
let end_pos = start_pos.to_usize() + src.len();
13271331
assert!(end_pos <= u32::MAX as usize);
13281332

compiler/rustc_ty_utils/src/ty.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use rustc_data_structures::fx::FxIndexSet;
2-
use rustc_data_structures::svh::Svh;
32
use rustc_hir as hir;
43
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
54
use rustc_middle::hir::map as hir_map;
5+
use rustc_middle::middle::cstore::CrateHashAndState;
66
use rustc_middle::ty::subst::Subst;
77
use rustc_middle::ty::{
88
self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt, WithConstness,
@@ -400,8 +400,8 @@ fn original_crate_name(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Symbol {
400400
tcx.crate_name
401401
}
402402

403-
fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
404-
tcx.index_hir(crate_num).crate_hash
403+
fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CrateHashAndState {
404+
CrateHashAndState::new(tcx, tcx.index_hir(crate_num).crate_hash)
405405
}
406406

407407
fn instance_def_size_estimate<'tcx>(

0 commit comments

Comments
 (0)