]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #81635 - michaelwoerister:structured_def_path_hash, r=pnkfelix
authorbors <bors@rust-lang.org>
Sun, 7 Mar 2021 23:45:57 +0000 (23:45 +0000)
committerbors <bors@rust-lang.org>
Sun, 7 Mar 2021 23:45:57 +0000 (23:45 +0000)
Let a portion of DefPathHash uniquely identify the DefPath's crate.

This allows to directly map from a `DefPathHash` to the crate it originates from, without constructing side tables to do that mapping -- something that is useful for incremental compilation where we deal with `DefPathHash` instead of `DefId` a lot.

It also allows to reliably and cheaply check for `DefPathHash` collisions which allows the compiler to gracefully abort compilation instead of running into a subsequent ICE at some random place in the code.

The following new piece of documentation describes the most interesting aspects of the changes:

```rust
/// A `DefPathHash` is a fixed-size representation of a `DefPath` that is
/// stable across crate and compilation session boundaries. It consists of two
/// separate 64-bit hashes. The first uniquely identifies the crate this
/// `DefPathHash` originates from (see [StableCrateId]), and the second
/// uniquely identifies the corresponding `DefPath` within that crate. Together
/// they form a unique identifier within an entire crate graph.
///
/// There is a very small chance of hash collisions, which would mean that two
/// different `DefPath`s map to the same `DefPathHash`. Proceeding compilation
/// with such a hash collision would very probably lead to an ICE and, in the
/// worst case, to a silent mis-compilation. The compiler therefore actively
/// and exhaustively checks for such hash collisions and aborts compilation if
/// it finds one.
///
/// `DefPathHash` uses 64-bit hashes for both the crate-id part and the
/// crate-internal part, even though it is likely that there are many more
/// `LocalDefId`s in a single crate than there are individual crates in a crate
/// graph. Since we use the same number of bits in both cases, the collision
/// probability for the crate-local part will be quite a bit higher (though
/// still very small).
///
/// This imbalance is not by accident: A hash collision in the
/// crate-local part of a `DefPathHash` will be detected and reported while
/// compiling the crate in question. Such a collision does not depend on
/// outside factors and can be easily fixed by the crate maintainer (e.g. by
/// renaming the item in question or by bumping the crate version in a harmless
/// way).
///
/// A collision between crate-id hashes on the other hand is harder to fix
/// because it depends on the set of crates in the entire crate graph of a
/// compilation session. Again, using the same crate with a different version
/// number would fix the issue with a high probability -- but that might be
/// easier said then done if the crates in questions are dependencies of
/// third-party crates.
///
/// That being said, given a high quality hash function, the collision
/// probabilities in question are very small. For example, for a big crate like
/// `rustc_middle` (with ~50000 `LocalDefId`s as of the time of writing) there
/// is a probability of roughly 1 in 14,750,000,000 of a crate-internal
/// collision occurring. For a big crate graph with 1000 crates in it, there is
/// a probability of 1 in 36,890,000,000,000 of a `StableCrateId` collision.
```

Given the probabilities involved I hope that no one will ever actually see the error messages. Nonetheless, I'd be glad about some feedback on how to improve them. Should we create a GH issue describing the problem and possible solutions to point to? Or a page in the rustc book?

r? `@pnkfelix` (feel free to re-assign)

1  2 
compiler/rustc_ast/src/lib.rs
compiler/rustc_hir/src/lib.rs
compiler/rustc_metadata/src/creader.rs
compiler/rustc_metadata/src/rmeta/encoder.rs
compiler/rustc_session/src/session.rs
compiler/rustc_span/src/def_id.rs
compiler/rustc_span/src/lib.rs

index 4eaef85043ccde3009f95fe5a737a0cb8cf1a20c,5ddaf9c950a3b2943d1974af00cc57673673b5f1..03ec4b8a44da66158774d9d7d4b2fa6906c3441d
@@@ -9,11 -9,11 +9,11 @@@
      test(attr(deny(warnings)))
  )]
  #![feature(box_syntax)]
 +#![feature(box_patterns)]
  #![feature(const_fn)] // For the `transmute` in `P::new`
  #![feature(const_fn_transmute)]
  #![feature(const_panic)]
  #![feature(crate_visibility_modifier)]
 -#![feature(iterator_fold_self)]
  #![feature(label_break_value)]
  #![feature(nll)]
  #![feature(or_patterns)]
@@@ -40,9 -40,7 +40,8 @@@ pub mod util 
  }
  
  pub mod ast;
 +pub mod ast_like;
  pub mod attr;
- pub mod crate_disambiguator;
  pub mod entry;
  pub mod expand;
  pub mod mut_visit;
@@@ -53,7 -51,6 +52,7 @@@ pub mod tokenstream
  pub mod visit;
  
  pub use self::ast::*;
 +pub use self::ast_like::AstLike;
  
  use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
  
index c69a9b063aeca618cd943422ae4e94ff8ecd409d,c65151cd1305da156761f1e5021dcf8e7621ec6e..60f5a89ef2eeaa43935c125a0a50ece955d717d3
@@@ -6,6 -6,7 +6,6 @@@
  #![feature(const_fn)] // For the unsizing cast on `&[]`
  #![feature(const_panic)]
  #![feature(in_band_lifetimes)]
 -#![feature(iterator_fold_self)]
  #![feature(once_cell)]
  #![feature(or_patterns)]
  #![recursion_limit = "256"]
@@@ -30,6 -31,9 +30,9 @@@ mod stable_hash_impls
  mod target;
  pub mod weak_lang_items;
  
+ #[cfg(test)]
+ mod tests;
  pub use hir::*;
  pub use hir_id::*;
  pub use lang_items::{LangItem, LanguageItems};
index 63c6f369eb685a74fe791ce8cd29c7d4aed01936,42e4f1f8fff15f4abd0bf4ce7dfd88213b34efef..b5506acf73522994001b95b828c8177924028747
@@@ -6,19 -6,18 +6,19 @@@ use crate::rmeta::{CrateDep, CrateMetad
  
  use rustc_ast::expand::allocator::AllocatorKind;
  use rustc_ast::{self as ast, *};
- use rustc_data_structures::fx::FxHashSet;
+ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
  use rustc_data_structures::svh::Svh;
  use rustc_data_structures::sync::Lrc;
  use rustc_expand::base::SyntaxExtension;
- use rustc_hir::def_id::{CrateNum, LocalDefId, LOCAL_CRATE};
+ use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, LOCAL_CRATE};
  use rustc_hir::definitions::Definitions;
  use rustc_index::vec::IndexVec;
  use rustc_middle::middle::cstore::{CrateDepKind, CrateSource, ExternCrate};
  use rustc_middle::middle::cstore::{ExternCrateSource, MetadataLoaderDyn};
  use rustc_middle::ty::TyCtxt;
 +use rustc_serialize::json::ToJson;
  use rustc_session::config::{self, CrateType, ExternLocation};
 -use rustc_session::lint;
 +use rustc_session::lint::{self, BuiltinLintDiagnostics, ExternDepSpec};
  use rustc_session::output::validate_crate_name;
  use rustc_session::search_paths::PathKind;
  use rustc_session::{CrateDisambiguator, Session};
@@@ -28,7 -27,6 +28,7 @@@ use rustc_span::{Span, DUMMY_SP}
  use rustc_target::spec::{PanicStrategy, TargetTriple};
  
  use proc_macro::bridge::client::ProcMacro;
 +use std::collections::BTreeMap;
  use std::path::Path;
  use std::{cmp, env};
  use tracing::{debug, info};
@@@ -42,6 -40,10 +42,10 @@@ pub struct CStore 
      allocator_kind: Option<AllocatorKind>,
      /// This crate has a `#[global_allocator]` item.
      has_global_allocator: bool,
+     /// This map is used to verify we get no hash conflicts between
+     /// `StableCrateId` values.
+     stable_crate_ids: FxHashMap<StableCrateId, CrateNum>,
  }
  
  pub struct CrateLoader<'a> {
@@@ -194,6 -196,11 +198,11 @@@ impl<'a> CrateLoader<'a> 
          metadata_loader: &'a MetadataLoaderDyn,
          local_crate_name: &str,
      ) -> Self {
+         let local_crate_stable_id =
+             StableCrateId::new(local_crate_name, sess.local_crate_disambiguator());
+         let mut stable_crate_ids = FxHashMap::default();
+         stable_crate_ids.insert(local_crate_stable_id, LOCAL_CRATE);
          CrateLoader {
              sess,
              metadata_loader,
                  injected_panic_runtime: None,
                  allocator_kind: None,
                  has_global_allocator: false,
+                 stable_crate_ids,
              },
              used_extern_options: Default::default(),
          }
          res
      }
  
+     fn verify_no_stable_crate_id_hash_conflicts(
+         &mut self,
+         root: &CrateRoot<'_>,
+         cnum: CrateNum,
+     ) -> Result<(), CrateError> {
+         if let Some(existing) = self.cstore.stable_crate_ids.insert(root.stable_crate_id(), cnum) {
+             let crate_name0 = root.name();
+             let crate_name1 = self.cstore.get_crate_data(existing).name();
+             return Err(CrateError::StableCrateIdCollision(crate_name0, crate_name1));
+         }
+         Ok(())
+     }
      fn register_crate(
          &mut self,
          host_lib: Option<Library>,
          // Claim this crate number and cache it
          let cnum = self.cstore.alloc_new_crate_num();
  
+         self.verify_no_stable_crate_id_hash_conflicts(&crate_root, cnum)?;
          info!(
              "register crate `{}` (cnum = {}. private_dep = {})",
              crate_root.name(),
                  // Don't worry about pathless `--extern foo` sysroot references
                  continue;
              }
 -            if !self.used_extern_options.contains(&Symbol::intern(name)) {
 -                self.sess.parse_sess.buffer_lint(
 +            if self.used_extern_options.contains(&Symbol::intern(name)) {
 +                continue;
 +            }
 +
 +            // Got a real unused --extern
 +            let diag = match self.sess.opts.extern_dep_specs.get(name) {
 +                Some(loc) => BuiltinLintDiagnostics::ExternDepSpec(name.clone(), loc.into()),
 +                None => {
 +                    // If we don't have a specific location, provide a json encoding of the `--extern`
 +                    // option.
 +                    let meta: BTreeMap<String, String> =
 +                        std::iter::once(("name".to_string(), name.to_string())).collect();
 +                    BuiltinLintDiagnostics::ExternDepSpec(
 +                        name.clone(),
 +                        ExternDepSpec::Json(meta.to_json()),
 +                    )
 +                }
 +            };
 +            self.sess.parse_sess.buffer_lint_with_diagnostic(
                      lint::builtin::UNUSED_CRATE_DEPENDENCIES,
                      span,
                      ast::CRATE_NODE_ID,
                          name,
                          self.local_crate_name,
                          name),
 +                    diag,
                  );
 -            }
          }
      }
  
index 61265d7204c0b2773814a709f72fa4272597c8f4,25d70d9768a6a948bf801d1bb2e055fb0da29f2f..9336add96929c236682647fc8d71362aa2bdfa80
@@@ -2,17 -2,15 +2,17 @@@ use crate::rmeta::table::{FixedSizeEnco
  use crate::rmeta::*;
  
  use rustc_data_structures::fingerprint::{Fingerprint, FingerprintEncoder};
 -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
 +use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
  use rustc_data_structures::stable_hasher::StableHasher;
 -use rustc_data_structures::sync::{join, Lrc};
 +use rustc_data_structures::sync::{join, par_iter, Lrc, ParallelIterator};
  use rustc_hir as hir;
  use rustc_hir::def::{CtorOf, DefKind};
 -use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
 +use rustc_hir::def_id::{
 +    CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE,
 +};
  use rustc_hir::definitions::DefPathData;
  use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 -use rustc_hir::itemlikevisit::{ItemLikeVisitor, ParItemLikeVisitor};
 +use rustc_hir::itemlikevisit::ItemLikeVisitor;
  use rustc_hir::lang_items;
  use rustc_hir::{AnonConst, GenericParamKind};
  use rustc_index::bit_set::GrowableBitSet;
@@@ -67,6 -65,11 +67,6 @@@ pub(super) struct EncodeContext<'a, 'tc
      required_source_files: Option<GrowableBitSet<usize>>,
      is_proc_macro: bool,
      hygiene_ctxt: &'a HygieneEncodeContext,
 -
 -    // Determines if MIR used for code generation will be included in the crate
 -    // metadata. When emitting only metadata (e.g., cargo check), we can avoid
 -    // generating optimized MIR altogether.
 -    emit_codegen_mir: bool,
  }
  
  /// If the current crate is a proc-macro, returns early with `Lazy:empty()`.
@@@ -433,7 -436,7 +433,7 @@@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> 
  
      fn encode_info_for_items(&mut self) {
          let krate = self.tcx.hir().krate();
 -        self.encode_info_for_mod(hir::CRATE_HIR_ID, &krate.item.module);
 +        self.encode_info_for_mod(CRATE_DEF_ID, &krate.item.module);
  
          // Proc-macro crates only export proc-macro items, which are looked
          // up using `proc_macro_data`
  
          let tcx = self.tcx;
  
 +        // Encode MIR.
 +        i = self.position();
 +        self.encode_mir();
 +        let mir_bytes = self.position() - i;
 +
          // Encode the items.
          i = self.position();
          self.encode_def_ids();
              triple: tcx.sess.opts.target_triple.clone(),
              hash: tcx.crate_hash(LOCAL_CRATE),
              disambiguator: tcx.sess.local_crate_disambiguator(),
+             stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(),
              panic_strategy: tcx.sess.panic_strategy(),
              edition: tcx.sess.edition(),
              has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE),
                  }
              }
  
 -            println!("metadata stats:");
 -            println!("             dep bytes: {}", dep_bytes);
 -            println!("     lib feature bytes: {}", lib_feature_bytes);
 -            println!("       lang item bytes: {}", lang_item_bytes);
 -            println!(" diagnostic item bytes: {}", diagnostic_item_bytes);
 -            println!("          native bytes: {}", native_lib_bytes);
 -            println!("      source_map bytes: {}", source_map_bytes);
 -            println!("            impl bytes: {}", impl_bytes);
 -            println!("    exp. symbols bytes: {}", exported_symbols_bytes);
 -            println!("  def-path table bytes: {}", def_path_table_bytes);
 -            println!(" proc-macro-data-bytes: {}", proc_macro_data_bytes);
 -            println!("            item bytes: {}", item_bytes);
 -            println!("           table bytes: {}", tables_bytes);
 -            println!("         hygiene bytes: {}", hygiene_bytes);
 -            println!("            zero bytes: {}", zero_bytes);
 -            println!("           total bytes: {}", total_bytes);
 +            eprintln!("metadata stats:");
 +            eprintln!("             dep bytes: {}", dep_bytes);
 +            eprintln!("     lib feature bytes: {}", lib_feature_bytes);
 +            eprintln!("       lang item bytes: {}", lang_item_bytes);
 +            eprintln!(" diagnostic item bytes: {}", diagnostic_item_bytes);
 +            eprintln!("          native bytes: {}", native_lib_bytes);
 +            eprintln!("      source_map bytes: {}", source_map_bytes);
 +            eprintln!("            impl bytes: {}", impl_bytes);
 +            eprintln!("    exp. symbols bytes: {}", exported_symbols_bytes);
 +            eprintln!("  def-path table bytes: {}", def_path_table_bytes);
 +            eprintln!(" proc-macro-data-bytes: {}", proc_macro_data_bytes);
 +            eprintln!("             mir bytes: {}", mir_bytes);
 +            eprintln!("            item bytes: {}", item_bytes);
 +            eprintln!("           table bytes: {}", tables_bytes);
 +            eprintln!("         hygiene bytes: {}", hygiene_bytes);
 +            eprintln!("            zero bytes: {}", zero_bytes);
 +            eprintln!("           total bytes: {}", total_bytes);
          }
  
          root
@@@ -788,53 -786,6 +789,53 @@@ fn should_encode_stability(def_kind: De
      }
  }
  
 +/// Whether we should encode MIR.
 +///
 +/// Computing, optimizing and encoding the MIR is a relatively expensive operation.
 +/// We want to avoid this work when not required. Therefore:
 +/// - we only compute `mir_for_ctfe` on items with const-eval semantics;
 +/// - we skip `optimized_mir` for check runs.
 +///
 +/// Return a pair, resp. for CTFE and for LLVM.
 +fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) {
 +    match tcx.def_kind(def_id) {
 +        // Constructors
 +        DefKind::Ctor(_, _) => {
 +            let mir_opt_base = tcx.sess.opts.output_types.should_codegen()
 +                || tcx.sess.opts.debugging_opts.always_encode_mir;
 +            (true, mir_opt_base)
 +        }
 +        // Constants
 +        DefKind::AnonConst | DefKind::AssocConst | DefKind::Static | DefKind::Const => {
 +            (true, false)
 +        }
 +        // Full-fledged functions
 +        DefKind::AssocFn | DefKind::Fn => {
 +            let generics = tcx.generics_of(def_id);
 +            let needs_inline = (generics.requires_monomorphization(tcx)
 +                || tcx.codegen_fn_attrs(def_id).requests_inline())
 +                && tcx.sess.opts.output_types.should_codegen();
 +            // Only check the presence of the `const` modifier.
 +            let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id());
 +            let always_encode_mir = tcx.sess.opts.debugging_opts.always_encode_mir;
 +            (is_const_fn, needs_inline || always_encode_mir)
 +        }
 +        // Closures can't be const fn.
 +        DefKind::Closure => {
 +            let generics = tcx.generics_of(def_id);
 +            let needs_inline = (generics.requires_monomorphization(tcx)
 +                || tcx.codegen_fn_attrs(def_id).requests_inline())
 +                && tcx.sess.opts.output_types.should_codegen();
 +            let always_encode_mir = tcx.sess.opts.debugging_opts.always_encode_mir;
 +            (false, needs_inline || always_encode_mir)
 +        }
 +        // Generators require optimized MIR to compute layout.
 +        DefKind::Generator => (false, true),
 +        // The others don't have MIR.
 +        _ => (false, false),
 +    }
 +}
 +
  impl EncodeContext<'a, 'tcx> {
      fn encode_def_ids(&mut self) {
          if self.is_proc_macro {
  
      fn encode_variances_of(&mut self, def_id: DefId) {
          debug!("EncodeContext::encode_variances_of({:?})", def_id);
 -        record!(self.tables.variances[def_id] <- &self.tcx.variances_of(def_id)[..]);
 +        record!(self.tables.variances[def_id] <- self.tcx.variances_of(def_id));
      }
  
      fn encode_item_type(&mut self, def_id: DefId) {
          self.encode_generics(def_id);
          self.encode_explicit_predicates(def_id);
          self.encode_inferred_outlives(def_id);
 -        let opt_mir = tcx.sess.opts.debugging_opts.always_encode_mir || self.emit_codegen_mir;
 -        if opt_mir {
 -            self.encode_optimized_mir(def_id.expect_local());
 -        }
 -        self.encode_mir_for_ctfe(def_id.expect_local());
      }
  
 -    fn encode_info_for_mod(&mut self, id: hir::HirId, md: &hir::Mod<'_>) {
 +    fn encode_info_for_mod(&mut self, local_def_id: LocalDefId, md: &hir::Mod<'_>) {
          let tcx = self.tcx;
 -        let local_def_id = tcx.hir().local_def_id(id);
          let def_id = local_def_id.to_def_id();
          debug!("EncodeContext::encode_info_for_mod({:?})", def_id);
  
              record!(self.tables.children[def_id] <- &[]);
          } else {
              record!(self.tables.children[def_id] <- md.item_ids.iter().map(|item_id| {
 -                tcx.hir().local_def_id(item_id.id).local_def_index
 +                item_id.def_id.local_def_index
              }));
          }
      }
          self.encode_generics(def_id);
          self.encode_explicit_predicates(def_id);
          self.encode_inferred_outlives(def_id);
 -        let opt_mir = tcx.sess.opts.debugging_opts.always_encode_mir || self.emit_codegen_mir;
 -        if opt_mir {
 -            self.encode_optimized_mir(def_id.expect_local());
 -        }
 -        self.encode_mir_for_ctfe(def_id.expect_local());
      }
  
      fn encode_generics(&mut self, def_id: DefId) {
          self.encode_generics(def_id);
          self.encode_explicit_predicates(def_id);
          self.encode_inferred_outlives(def_id);
 -
 -        // This should be kept in sync with `PrefetchVisitor.visit_trait_item`.
 -        match trait_item.kind {
 -            ty::AssocKind::Type => {}
 -            ty::AssocKind::Const => {
 -                if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id.expect_local()) {
 -                    self.encode_mir_for_ctfe(def_id.expect_local());
 -                    self.encode_promoted_mir(def_id.expect_local());
 -                }
 -            }
 -            ty::AssocKind::Fn => {
 -                let opt_mir =
 -                    tcx.sess.opts.debugging_opts.always_encode_mir || self.emit_codegen_mir;
 -                if opt_mir {
 -                    if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id.expect_local()) {
 -                        self.encode_optimized_mir(def_id.expect_local());
 -                        self.encode_promoted_mir(def_id.expect_local());
 -                    }
 -                }
 -            }
 -        }
 -    }
 -
 -    fn should_encode_fn_opt_mir(&self, def_id: DefId) -> bool {
 -        self.tcx.sess.opts.debugging_opts.always_encode_mir
 -            || (self.emit_codegen_mir
 -                && (self.tcx.generics_of(def_id).requires_monomorphization(self.tcx)
 -                    || self.tcx.codegen_fn_attrs(def_id).requests_inline()))
      }
  
      fn encode_info_for_impl_item(&mut self, def_id: DefId) {
          self.encode_generics(def_id);
          self.encode_explicit_predicates(def_id);
          self.encode_inferred_outlives(def_id);
 -
 -        // The following part should be kept in sync with `PrefetchVisitor.visit_impl_item`.
 -
 -        let (mir, mir_const) = match ast_item.kind {
 -            hir::ImplItemKind::Const(..) => (false, true),
 -            hir::ImplItemKind::Fn(ref sig, _) => {
 -                let opt_mir = self.should_encode_fn_opt_mir(def_id);
 -                let is_const_fn = sig.header.constness == hir::Constness::Const;
 -                (opt_mir, is_const_fn)
 -            }
 -            hir::ImplItemKind::TyAlias(..) => (false, false),
 -        };
 -        if mir {
 -            self.encode_optimized_mir(def_id.expect_local());
 -        }
 -        if mir || mir_const {
 -            self.encode_promoted_mir(def_id.expect_local());
 -        }
 -        if mir_const {
 -            self.encode_mir_for_ctfe(def_id.expect_local());
 -        }
      }
  
      fn encode_fn_param_names_for_body(&mut self, body_id: hir::BodyId) -> Lazy<[Ident]> {
          self.lazy(param_names.iter())
      }
  
 -    fn encode_mir_for_ctfe(&mut self, def_id: LocalDefId) {
 -        debug!("EntryBuilder::encode_mir_for_ctfe({:?})", def_id);
 -        record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- self.tcx.mir_for_ctfe(def_id));
 -
 -        let unused = self.tcx.unused_generic_params(def_id);
 -        if !unused.is_empty() {
 -            record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
 +    fn encode_mir(&mut self) {
 +        if self.is_proc_macro {
 +            return;
          }
  
 -        let abstract_const = self.tcx.mir_abstract_const(def_id);
 -        if let Ok(Some(abstract_const)) = abstract_const {
 -            record!(self.tables.mir_abstract_consts[def_id.to_def_id()] <- abstract_const);
 -        }
 -    }
 +        let mut keys_and_jobs = self
 +            .tcx
 +            .mir_keys(LOCAL_CRATE)
 +            .iter()
 +            .filter_map(|&def_id| {
 +                let (encode_const, encode_opt) = should_encode_mir(self.tcx, def_id);
 +                if encode_const || encode_opt {
 +                    Some((def_id, encode_const, encode_opt))
 +                } else {
 +                    None
 +                }
 +            })
 +            .collect::<Vec<_>>();
 +        // Sort everything to ensure a stable order for diagnotics.
 +        keys_and_jobs.sort_by_key(|&(def_id, _, _)| def_id);
 +        for (def_id, encode_const, encode_opt) in keys_and_jobs.into_iter() {
 +            debug_assert!(encode_const || encode_opt);
  
 -    fn encode_optimized_mir(&mut self, def_id: LocalDefId) {
 -        debug!("EntryBuilder::encode_optimized_mir({:?})", def_id);
 -        record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id));
 +            debug!("EntryBuilder::encode_mir({:?})", def_id);
 +            if encode_opt {
 +                record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id));
 +            }
 +            if encode_const {
 +                record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- self.tcx.mir_for_ctfe(def_id));
  
 -        let unused = self.tcx.unused_generic_params(def_id);
 -        if !unused.is_empty() {
 -            record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
 -        }
 -    }
 +                let abstract_const = self.tcx.mir_abstract_const(def_id);
 +                if let Ok(Some(abstract_const)) = abstract_const {
 +                    record!(self.tables.mir_abstract_consts[def_id.to_def_id()] <- abstract_const);
 +                }
 +            }
 +            record!(self.tables.promoted_mir[def_id.to_def_id()] <- self.tcx.promoted_mir(def_id));
  
 -    fn encode_promoted_mir(&mut self, def_id: LocalDefId) {
 -        debug!("EncodeContext::encode_promoted_mir({:?})", def_id);
 -        record!(self.tables.promoted_mir[def_id.to_def_id()] <- self.tcx.promoted_mir(def_id));
 +            let unused = self.tcx.unused_generic_params(def_id);
 +            if !unused.is_empty() {
 +                record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
 +            }
 +        }
      }
  
      // Encodes the inherent implementations of a structure, enumeration, or trait.
                  EntryKind::Fn(self.lazy(data))
              }
              hir::ItemKind::Mod(ref m) => {
 -                return self.encode_info_for_mod(item.hir_id, m);
 +                return self.encode_info_for_mod(item.def_id, m);
              }
              hir::ItemKind::ForeignMod { .. } => EntryKind::ForeignMod,
              hir::ItemKind::GlobalAsm(..) => EntryKind::GlobalAsm,
              hir::ItemKind::ForeignMod { items, .. } => record!(self.tables.children[def_id] <-
                  items
                      .iter()
 -                    .map(|foreign_item| tcx.hir().local_def_id(
 -                        foreign_item.id.hir_id).local_def_index)
 +                    .map(|foreign_item| foreign_item.id.def_id.local_def_index)
              ),
              hir::ItemKind::Enum(..) => record!(self.tables.children[def_id] <-
                  self.tcx.adt_def(def_id).variants.iter().map(|v| {
              }
              _ => {}
          }
 -
 -        // The following part should be kept in sync with `PrefetchVisitor.visit_item`.
 -
 -        let (mir, const_mir) = match item.kind {
 -            hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => (false, true),
 -            hir::ItemKind::Fn(ref sig, ..) => {
 -                let opt_mir = self.should_encode_fn_opt_mir(def_id);
 -                let is_const_fn = sig.header.constness == hir::Constness::Const;
 -                // We don't need the optimized MIR for const fns.
 -                (opt_mir, is_const_fn)
 -            }
 -            _ => (false, false),
 -        };
 -        if mir {
 -            self.encode_optimized_mir(def_id.expect_local());
 -        }
 -        if mir || const_mir {
 -            self.encode_promoted_mir(def_id.expect_local());
 -        }
 -        if const_mir {
 -            self.encode_mir_for_ctfe(def_id.expect_local());
 -        }
      }
  
      /// Serialize the text of exported macros
      fn encode_info_for_macro_def(&mut self, macro_def: &hir::MacroDef<'_>) {
 -        let def_id = self.tcx.hir().local_def_id(macro_def.hir_id).to_def_id();
 +        let def_id = macro_def.def_id.to_def_id();
          record!(self.tables.kind[def_id] <- EntryKind::MacroDef(self.lazy(macro_def.ast.clone())));
          self.encode_ident_span(def_id, macro_def.ident);
      }
              record!(self.tables.fn_sig[def_id] <- substs.as_closure().sig());
          }
          self.encode_generics(def_id.to_def_id());
 -        let opt_mir = // FIXME: Optimized MIR is necessary to determine the layout of generators.
 -            matches!(ty.kind(), ty::Generator(..))
 -            || self.tcx.sess.opts.debugging_opts.always_encode_mir
 -            || self.emit_codegen_mir;
 -        if opt_mir {
 -            self.encode_optimized_mir(def_id);
 -            self.encode_promoted_mir(def_id);
 -        }
      }
  
      fn encode_info_for_anon_const(&mut self, def_id: LocalDefId) {
          self.encode_generics(def_id.to_def_id());
          self.encode_explicit_predicates(def_id.to_def_id());
          self.encode_inferred_outlives(def_id.to_def_id());
 -        self.encode_mir_for_ctfe(def_id);
 -        self.encode_promoted_mir(def_id);
      }
  
      fn encode_native_libraries(&mut self) -> Lazy<[NativeLib]> {
@@@ -1850,15 -1880,17 +1851,15 @@@ impl Visitor<'tcx> for EncodeContext<'a
      }
      fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
          intravisit::walk_item(self, item);
 -        let def_id = self.tcx.hir().local_def_id(item.hir_id);
          match item.kind {
              hir::ItemKind::ExternCrate(_) | hir::ItemKind::Use(..) => {} // ignore these
 -            _ => self.encode_info_for_item(def_id.to_def_id(), item),
 +            _ => self.encode_info_for_item(item.def_id.to_def_id(), item),
          }
          self.encode_addl_info_for_item(item);
      }
      fn visit_foreign_item(&mut self, ni: &'tcx hir::ForeignItem<'tcx>) {
          intravisit::walk_foreign_item(self, ni);
 -        let def_id = self.tcx.hir().local_def_id(ni.hir_id);
 -        self.encode_info_for_foreign_item(def_id.to_def_id(), ni);
 +        self.encode_info_for_foreign_item(ni.def_id.to_def_id(), ni);
      }
      fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
          intravisit::walk_generics(self, generics);
@@@ -1918,6 -1950,7 +1919,6 @@@ impl EncodeContext<'a, 'tcx> 
      /// so it's easier to do that here then to wait until we would encounter
      /// normally in the visitor walk.
      fn encode_addl_info_for_item(&mut self, item: &hir::Item<'_>) {
 -        let def_id = self.tcx.hir().local_def_id(item.hir_id);
          match item.kind {
              hir::ItemKind::Static(..)
              | hir::ItemKind::Const(..)
                  // no sub-item recording needed in these cases
              }
              hir::ItemKind::Enum(..) => {
 -                let def = self.tcx.adt_def(def_id.to_def_id());
 +                let def = self.tcx.adt_def(item.def_id.to_def_id());
                  self.encode_fields(def);
  
                  for (i, variant) in def.variants.iter_enumerated() {
                  }
              }
              hir::ItemKind::Struct(ref struct_def, _) => {
 -                let def = self.tcx.adt_def(def_id.to_def_id());
 +                let def = self.tcx.adt_def(item.def_id.to_def_id());
                  self.encode_fields(def);
  
                  // If the struct has a constructor, encode it.
                  }
              }
              hir::ItemKind::Union(..) => {
 -                let def = self.tcx.adt_def(def_id.to_def_id());
 +                let def = self.tcx.adt_def(item.def_id.to_def_id());
                  self.encode_fields(def);
              }
              hir::ItemKind::Impl { .. } => {
                  for &trait_item_def_id in
 -                    self.tcx.associated_item_def_ids(def_id.to_def_id()).iter()
 +                    self.tcx.associated_item_def_ids(item.def_id.to_def_id()).iter()
                  {
                      self.encode_info_for_impl_item(trait_item_def_id);
                  }
              }
              hir::ItemKind::Trait(..) => {
 -                for &item_def_id in self.tcx.associated_item_def_ids(def_id.to_def_id()).iter() {
 +                for &item_def_id in self.tcx.associated_item_def_ids(item.def_id.to_def_id()).iter()
 +                {
                      self.encode_info_for_trait_item(item_def_id);
                  }
              }
@@@ -1983,14 -2015,15 +1984,14 @@@ struct ImplVisitor<'tcx> 
  impl<'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'tcx> {
      fn visit_item(&mut self, item: &hir::Item<'_>) {
          if let hir::ItemKind::Impl { .. } = item.kind {
 -            let impl_id = self.tcx.hir().local_def_id(item.hir_id);
 -            if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_id.to_def_id()) {
 +            if let Some(trait_ref) = self.tcx.impl_trait_ref(item.def_id.to_def_id()) {
                  let simplified_self_ty =
                      ty::fast_reject::simplify_type(self.tcx, trait_ref.self_ty(), false);
  
                  self.impls
                      .entry(trait_ref.def_id)
                      .or_default()
 -                    .push((impl_id.local_def_index, simplified_self_ty));
 +                    .push((item.def_id.local_def_index, simplified_self_ty));
              }
          }
      }
  
  /// Used to prefetch queries which will be needed later by metadata encoding.
  /// Only a subset of the queries are actually prefetched to keep this code smaller.
 -struct PrefetchVisitor<'tcx> {
 -    tcx: TyCtxt<'tcx>,
 -    mir_keys: &'tcx FxHashSet<LocalDefId>,
 -}
 -
 -impl<'tcx> PrefetchVisitor<'tcx> {
 -    fn prefetch_ctfe_mir(&self, def_id: LocalDefId) {
 -        if self.mir_keys.contains(&def_id) {
 -            self.tcx.ensure().mir_for_ctfe(def_id);
 -            self.tcx.ensure().promoted_mir(def_id);
 -        }
 +fn prefetch_mir(tcx: TyCtxt<'_>) {
 +    if !tcx.sess.opts.output_types.should_codegen() {
 +        // We won't emit MIR, so don't prefetch it.
 +        return;
      }
 -    fn prefetch_mir(&self, def_id: LocalDefId) {
 -        if self.mir_keys.contains(&def_id) {
 -            self.tcx.ensure().optimized_mir(def_id);
 -            self.tcx.ensure().promoted_mir(def_id);
 -        }
 -    }
 -}
  
 -impl<'tcx, 'v> ParItemLikeVisitor<'v> for PrefetchVisitor<'tcx> {
 -    fn visit_item(&self, item: &hir::Item<'_>) {
 -        // This should be kept in sync with `encode_info_for_item`.
 -        let tcx = self.tcx;
 -        match item.kind {
 -            hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => {
 -                self.prefetch_ctfe_mir(tcx.hir().local_def_id(item.hir_id))
 -            }
 -            hir::ItemKind::Fn(ref sig, ..) => {
 -                let def_id = tcx.hir().local_def_id(item.hir_id);
 -                let opt_mir = tcx.generics_of(def_id.to_def_id()).requires_monomorphization(tcx)
 -                    || tcx.codegen_fn_attrs(def_id.to_def_id()).requests_inline();
 -                if opt_mir {
 -                    self.prefetch_mir(def_id)
 -                }
 -                if sig.header.constness == hir::Constness::Const {
 -                    self.prefetch_ctfe_mir(def_id);
 -                }
 -            }
 -            _ => (),
 -        }
 -    }
 +    par_iter(tcx.mir_keys(LOCAL_CRATE)).for_each(|&def_id| {
 +        let (encode_const, encode_opt) = should_encode_mir(tcx, def_id);
  
 -    fn visit_trait_item(&self, trait_item: &'v hir::TraitItem<'v>) {
 -        // This should be kept in sync with `encode_info_for_trait_item`.
 -        let def_id = self.tcx.hir().local_def_id(trait_item.hir_id);
 -        match trait_item.kind {
 -            hir::TraitItemKind::Type(..) => {}
 -            hir::TraitItemKind::Const(..) => {
 -                self.prefetch_ctfe_mir(def_id);
 -            }
 -            hir::TraitItemKind::Fn(..) => {
 -                self.prefetch_mir(def_id);
 -            }
 +        if encode_const {
 +            tcx.ensure().mir_for_ctfe(def_id);
          }
 -    }
 -
 -    fn visit_impl_item(&self, impl_item: &'v hir::ImplItem<'v>) {
 -        // This should be kept in sync with `encode_info_for_impl_item`.
 -        let tcx = self.tcx;
 -        match impl_item.kind {
 -            hir::ImplItemKind::Const(..) => {
 -                self.prefetch_ctfe_mir(tcx.hir().local_def_id(impl_item.hir_id))
 -            }
 -            hir::ImplItemKind::Fn(ref sig, _) => {
 -                let def_id = tcx.hir().local_def_id(impl_item.hir_id);
 -                let opt_mir = tcx.generics_of(def_id.to_def_id()).requires_monomorphization(tcx)
 -                    || tcx.codegen_fn_attrs(def_id.to_def_id()).requests_inline();
 -                let is_const_fn = sig.header.constness == hir::Constness::Const;
 -                if opt_mir {
 -                    self.prefetch_mir(def_id)
 -                }
 -                if is_const_fn {
 -                    self.prefetch_ctfe_mir(def_id);
 -                }
 -            }
 -            hir::ImplItemKind::TyAlias(..) => (),
 +        if encode_opt {
 +            tcx.ensure().optimized_mir(def_id);
          }
 -    }
 -
 -    fn visit_foreign_item(&self, _foreign_item: &'v hir::ForeignItem<'v>) {
 -        // This should be kept in sync with `encode_info_for_foreign_item`.
 -        // Foreign items contain no MIR.
 -    }
 +        if encode_opt || encode_const {
 +            tcx.ensure().promoted_mir(def_id);
 +        }
 +    })
  }
  
  // NOTE(eddyb) The following comment was preserved for posterity, even
@@@ -2064,7 -2162,19 +2065,7 @@@ pub(super) fn encode_metadata(tcx: TyCt
              // Prefetch some queries used by metadata encoding.
              // This is not necessary for correctness, but is only done for performance reasons.
              // It can be removed if it turns out to cause trouble or be detrimental to performance.
 -            join(
 -                || {
 -                    if !tcx.sess.opts.output_types.should_codegen() {
 -                        // We won't emit MIR, so don't prefetch it.
 -                        return;
 -                    }
 -                    tcx.hir().krate().par_visit_all_item_likes(&PrefetchVisitor {
 -                        tcx,
 -                        mir_keys: tcx.mir_keys(LOCAL_CRATE),
 -                    });
 -                },
 -                || tcx.exported_symbols(LOCAL_CRATE),
 -            );
 +            join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE));
          },
      )
      .0
@@@ -2097,6 -2207,7 +2098,6 @@@ fn encode_metadata_impl(tcx: TyCtxt<'_>
          required_source_files,
          is_proc_macro: tcx.sess.crate_types().contains(&CrateType::ProcMacro),
          hygiene_ctxt: &hygiene_ctxt,
 -        emit_codegen_mir: tcx.sess.opts.output_types.should_codegen(),
      };
  
      // Encode the rustc version string in a predictable location.
index 963df0fb4d70bed1285122e422c4942e7c3bd481,3f690ba87ea05d0fe4d5fb394158091df3a44fe8..4f6111183607c3a483af0d20d4124dd89b8f3aab
@@@ -8,7 -8,6 +8,6 @@@ use crate::parse::ParseSess
  use crate::search_paths::{PathKind, SearchPath};
  
  pub use rustc_ast::attr::MarkedAttrs;
- pub use rustc_ast::crate_disambiguator::CrateDisambiguator;
  pub use rustc_ast::Attribute;
  use rustc_data_structures::flock;
  use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@@ -23,6 -22,7 +22,7 @@@ use rustc_errors::json::JsonEmitter
  use rustc_errors::registry::Registry;
  use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorReported};
  use rustc_lint_defs::FutureBreakage;
+ pub use rustc_span::crate_disambiguator::CrateDisambiguator;
  use rustc_span::edition::Edition;
  use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span};
  use rustc_span::{sym, SourceFileHashAlgorithm, Symbol};
@@@ -77,7 -77,6 +77,7 @@@ impl Limit 
  
      /// Check that `value` is within the limit. Ensures that the same comparisons are used
      /// throughout the compiler, as mismatches can cause ICEs, see #72540.
 +    #[inline]
      pub fn value_within_limit(&self, value: usize) -> bool {
          value <= self.0
      }
@@@ -348,12 -347,10 +348,12 @@@ impl Session 
          self.crate_types.set(crate_types).expect("`crate_types` was initialized twice")
      }
  
 +    #[inline]
      pub fn recursion_limit(&self) -> Limit {
          self.recursion_limit.get().copied().unwrap()
      }
  
 +    #[inline]
      pub fn type_length_limit(&self) -> Limit {
          self.type_length_limit.get().copied().unwrap()
      }
      pub fn binary_dep_depinfo(&self) -> bool {
          self.opts.debugging_opts.binary_dep_depinfo
      }
 +    pub fn mir_opt_level(&self) -> usize {
 +        self.opts
 +            .debugging_opts
 +            .mir_opt_level
 +            .unwrap_or_else(|| if self.opts.optimize != config::OptLevel::No { 2 } else { 1 })
 +    }
  
      /// Gets the features enabled for the current compilation session.
      /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents
      }
  
      pub fn print_perf_stats(&self) {
 -        println!(
 +        eprintln!(
              "Total time spent computing symbol hashes:      {}",
              duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock())
          );
 -        println!(
 +        eprintln!(
              "Total queries canonicalized:                   {}",
              self.perf_stats.queries_canonicalized.load(Ordering::Relaxed)
          );
 -        println!(
 +        eprintln!(
              "normalize_generic_arg_after_erasing_regions:   {}",
              self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed)
          );
 -        println!(
 +        eprintln!(
              "normalize_projection_ty:                       {}",
              self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed)
          );
          self.opts.optimize != config::OptLevel::No
          // AddressSanitizer uses lifetimes to detect use after scope bugs.
          // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
 -        || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY)
 +        // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
 +        || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
      }
  
      pub fn link_dead_code(&self) -> bool {
@@@ -1572,8 -1562,6 +1572,8 @@@ fn validate_commandline_args_with_sessi
          "x86_64-unknown-freebsd",
          "x86_64-unknown-linux-gnu",
      ];
 +    const HWASAN_SUPPORTED_TARGETS: &[&str] =
 +        &["aarch64-linux-android", "aarch64-unknown-linux-gnu"];
  
      // Sanitizers can only be used on some tested platforms.
      for s in sess.opts.debugging_opts.sanitizer {
              SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS,
              SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS,
              SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS,
 +            SanitizerSet::HWADDRESS => HWASAN_SUPPORTED_TARGETS,
              _ => panic!("unrecognized sanitizer {}", s),
          };
          if !supported_targets.contains(&&*sess.opts.target_triple.triple()) {
index 70e9526f626da934f8ab49f237b5ad448d706785,bd8c95fd6617106515d7d36c6bb028a00057f83d..885f30ebb4e6390f122c6bc90302a12733a27bd5
@@@ -1,3 -1,4 +1,4 @@@
+ use crate::crate_disambiguator::CrateDisambiguator;
  use crate::HashStableContext;
  use rustc_data_structures::fingerprint::Fingerprint;
  use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@@@ -105,10 -106,72 +106,72 @@@ impl ::std::fmt::Debug for CrateNum 
      }
  }
  
+ /// A `DefPathHash` is a fixed-size representation of a `DefPath` that is
+ /// stable across crate and compilation session boundaries. It consists of two
+ /// separate 64-bit hashes. The first uniquely identifies the crate this
+ /// `DefPathHash` originates from (see [StableCrateId]), and the second
+ /// uniquely identifies the corresponding `DefPath` within that crate. Together
+ /// they form a unique identifier within an entire crate graph.
+ ///
+ /// There is a very small chance of hash collisions, which would mean that two
+ /// different `DefPath`s map to the same `DefPathHash`. Proceeding compilation
+ /// with such a hash collision would very probably lead to an ICE, and in the
+ /// worst case lead to a silent mis-compilation. The compiler therefore actively
+ /// and exhaustively checks for such hash collisions and aborts compilation if
+ /// it finds one.
+ ///
+ /// `DefPathHash` uses 64-bit hashes for both the crate-id part and the
+ /// crate-internal part, even though it is likely that there are many more
+ /// `LocalDefId`s in a single crate than there are individual crates in a crate
+ /// graph. Since we use the same number of bits in both cases, the collision
+ /// probability for the crate-local part will be quite a bit higher (though
+ /// still very small).
+ ///
+ /// This imbalance is not by accident: A hash collision in the
+ /// crate-local part of a `DefPathHash` will be detected and reported while
+ /// compiling the crate in question. Such a collision does not depend on
+ /// outside factors and can be easily fixed by the crate maintainer (e.g. by
+ /// renaming the item in question or by bumping the crate version in a harmless
+ /// way).
+ ///
+ /// A collision between crate-id hashes on the other hand is harder to fix
+ /// because it depends on the set of crates in the entire crate graph of a
+ /// compilation session. Again, using the same crate with a different version
+ /// number would fix the issue with a high probability -- but that might be
+ /// easier said then done if the crates in questions are dependencies of
+ /// third-party crates.
+ ///
+ /// That being said, given a high quality hash function, the collision
+ /// probabilities in question are very small. For example, for a big crate like
+ /// `rustc_middle` (with ~50000 `LocalDefId`s as of the time of writing) there
+ /// is a probability of roughly 1 in 14,750,000,000 of a crate-internal
+ /// collision occurring. For a big crate graph with 1000 crates in it, there is
+ /// a probability of 1 in 36,890,000,000,000 of a `StableCrateId` collision.
  #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)]
  #[derive(HashStable_Generic, Encodable, Decodable)]
  pub struct DefPathHash(pub Fingerprint);
  
+ impl DefPathHash {
+     /// Returns the [StableCrateId] identifying the crate this [DefPathHash]
+     /// originates from.
+     #[inline]
+     pub fn stable_crate_id(&self) -> StableCrateId {
+         StableCrateId(self.0.as_value().0)
+     }
+     /// Returns the crate-local part of the [DefPathHash].
+     #[inline]
+     pub fn local_hash(&self) -> u64 {
+         self.0.as_value().1
+     }
+     /// Builds a new [DefPathHash] with the given [StableCrateId] and
+     /// `local_hash`, where `local_hash` must be unique within its crate.
+     pub fn new(stable_crate_id: StableCrateId, local_hash: u64) -> DefPathHash {
+         DefPathHash(Fingerprint::new(stable_crate_id.0, local_hash))
+     }
+ }
  impl Borrow<Fingerprint> for DefPathHash {
      #[inline]
      fn borrow(&self) -> &Fingerprint {
      }
  }
  
+ /// A [StableCrateId] is a 64 bit hash of `(crate-name, crate-disambiguator)`. It
+ /// is to [CrateNum] what [DefPathHash] is to [DefId]. It is stable across
+ /// compilation sessions.
+ ///
+ /// Since the ID is a hash value there is a (very small) chance that two crates
+ /// end up with the same [StableCrateId]. The compiler will check for such
+ /// collisions when loading crates and abort compilation in order to avoid
+ /// further trouble.
+ #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Encodable, Decodable)]
+ pub struct StableCrateId(u64);
+ impl StableCrateId {
+     /// Computes the stable ID for a crate with the given name and
+     /// disambiguator.
+     pub fn new(crate_name: &str, crate_disambiguator: CrateDisambiguator) -> StableCrateId {
+         use std::hash::Hash;
+         let mut hasher = StableHasher::new();
+         crate_name.hash(&mut hasher);
+         crate_disambiguator.hash(&mut hasher);
+         StableCrateId(hasher.finish())
+     }
+ }
  rustc_index::newtype_index! {
      /// A DefIndex is an index into the hir-map for a crate, identifying a
      /// particular definition. It should really be considered an interned
@@@ -227,8 -314,6 +314,8 @@@ pub struct LocalDefId 
      pub local_def_index: DefIndex,
  }
  
 +pub const CRATE_DEF_ID: LocalDefId = LocalDefId { local_def_index: CRATE_DEF_INDEX };
 +
  impl Idx for LocalDefId {
      #[inline]
      fn new(idx: usize) -> Self {
@@@ -270,8 -355,6 +357,8 @@@ impl<D: Decoder> Decodable<D> for Local
      }
  }
  
 +rustc_data_structures::define_id_collections!(LocalDefIdMap, LocalDefIdSet, LocalDefId);
 +
  impl<CTX: HashStableContext> HashStable<CTX> for DefId {
      fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
          hcx.hash_def_id(*self, hasher)
index 4dce029e86b5013b7a1b2f02edf9a5192c435519,0f14154047d6c0595b10d0524dcf917c5826673b..fb6c0873d77e96ecf33d9025ba4ff20c227a503f
@@@ -47,6 -47,8 +47,8 @@@ pub mod lev_distance
  mod span_encoding;
  pub use span_encoding::{Span, DUMMY_SP};
  
+ pub mod crate_disambiguator;
  pub mod symbol;
  pub use symbol::{sym, Symbol};
  
@@@ -509,10 -511,11 +511,10 @@@ impl Span 
      /// items can be used (that is, a macro marked with
      /// `#[allow_internal_unstable]`).
      pub fn allows_unstable(&self, feature: Symbol) -> bool {
 -        self.ctxt().outer_expn_data().allow_internal_unstable.map_or(false, |features| {
 -            features
 -                .iter()
 -                .any(|&f| f == feature || f == sym::allow_internal_unstable_backcompat_hack)
 -        })
 +        self.ctxt()
 +            .outer_expn_data()
 +            .allow_internal_unstable
 +            .map_or(false, |features| features.iter().any(|&f| f == feature))
      }
  
      /// Checks if this span arises from a compiler desugaring of kind `kind`.