diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 2a3092d3c7b..372a58857f3 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -23,8 +23,7 @@ use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
 pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
 
 pub fn rust_version_symbol() -> Symbol {
-    let version = option_env!("CFG_VERSION").unwrap_or("<current>");
-    let version = version.split(' ').next().unwrap();
+    let version = option_env!("CFG_RELEASE").unwrap_or("<current>");
     Symbol::intern(&version)
 }
 
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 25fe3cb265d..bd2fba12602 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -806,8 +806,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
     name_in_debuginfo.push(codegen_unit_name);
 
     debug!("build_compile_unit_di_node: {:?}", name_in_debuginfo);
-    let rustc_producer =
-        format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION"),);
+    let rustc_producer = format!("rustc version {}", tcx.sess.cfg_version);
     // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice.
     let producer = format!("clang LLVM ({})", rustc_producer);
 
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index c3cc17c255b..cdf50bdf1a4 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -36,6 +36,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
 use rustc_session::cstore::{self, CrateSource};
 use rustc_session::utils::NativeLibKind;
+use rustc_session::Session;
 use rustc_span::symbol::Symbol;
 use rustc_span::DebuggerVisualizerFile;
 use std::collections::BTreeSet;
@@ -175,11 +176,11 @@ pub struct CodegenResults {
     pub crate_info: CrateInfo,
 }
 
-pub enum CodegenErrors<'a> {
+pub enum CodegenErrors {
     WrongFileType,
     EmptyVersionNumber,
     EncodingVersionMismatch { version_array: String, rlink_version: u32 },
-    RustcVersionMismatch { rustc_version: String, current_version: &'a str },
+    RustcVersionMismatch { rustc_version: String },
 }
 
 pub fn provide(providers: &mut Providers) {
@@ -213,10 +214,9 @@ pub fn looks_like_rust_object_file(filename: &str) -> bool {
 const RLINK_VERSION: u32 = 1;
 const RLINK_MAGIC: &[u8] = b"rustlink";
 
-const RUSTC_VERSION: Option<&str> = option_env!("CFG_VERSION");
-
 impl CodegenResults {
     pub fn serialize_rlink(
+        sess: &Session,
         rlink_file: &Path,
         codegen_results: &CodegenResults,
     ) -> Result<usize, io::Error> {
@@ -225,12 +225,12 @@ impl CodegenResults {
         // `emit_raw_bytes` is used to make sure that the version representation does not depend on
         // Encoder's inner representation of `u32`.
         encoder.emit_raw_bytes(&RLINK_VERSION.to_be_bytes());
-        encoder.emit_str(RUSTC_VERSION.unwrap());
+        encoder.emit_str(sess.cfg_version);
         Encodable::encode(codegen_results, &mut encoder);
         encoder.finish()
     }
 
-    pub fn deserialize_rlink<'a>(data: Vec<u8>) -> Result<Self, CodegenErrors<'a>> {
+    pub fn deserialize_rlink(sess: &Session, data: Vec<u8>) -> Result<Self, CodegenErrors> {
         // The Decodable machinery is not used here because it panics if the input data is invalid
         // and because its internal representation may change.
         if !data.starts_with(RLINK_MAGIC) {
@@ -252,11 +252,9 @@ impl CodegenResults {
 
         let mut decoder = MemDecoder::new(&data[4..], 0);
         let rustc_version = decoder.read_str();
-        let current_version = RUSTC_VERSION.unwrap();
-        if rustc_version != current_version {
+        if rustc_version != sess.cfg_version {
             return Err(CodegenErrors::RustcVersionMismatch {
                 rustc_version: rustc_version.to_string(),
-                current_version,
             });
         }
 
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 405f3d5b66d..ece1e75d7de 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -568,7 +568,7 @@ pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Comp
             let rlink_data = fs::read(file).unwrap_or_else(|err| {
                 sess.emit_fatal(RlinkUnableToRead { err });
             });
-            let codegen_results = match CodegenResults::deserialize_rlink(rlink_data) {
+            let codegen_results = match CodegenResults::deserialize_rlink(sess, rlink_data) {
                 Ok(codegen) => codegen,
                 Err(err) => {
                     match err {
@@ -582,10 +582,10 @@ pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Comp
                                 rlink_version,
                             })
                         }
-                        CodegenErrors::RustcVersionMismatch { rustc_version, current_version } => {
+                        CodegenErrors::RustcVersionMismatch { rustc_version } => {
                             sess.emit_fatal(RLinkRustcVersionMismatch {
                                 rustc_version,
-                                current_version,
+                                current_version: sess.cfg_version,
                             })
                         }
                     };
diff --git a/compiler/rustc_hir/src/tests.rs b/compiler/rustc_hir/src/tests.rs
index c7ac01b3334..a40a0178710 100644
--- a/compiler/rustc_hir/src/tests.rs
+++ b/compiler/rustc_hir/src/tests.rs
@@ -10,13 +10,13 @@ fn def_path_hash_depends_on_crate_id() {
     // the crate-id of the defining crate. This is a desirable property
     // because the crate-id can be more easily changed than the DefPath
     // of an item, so, in the case of a crate-local DefPathHash collision,
-    // the user can simply "role the dice again" for all DefPathHashes in
+    // the user can simply "roll the dice again" for all DefPathHashes in
     // the crate by changing the crate disambiguator (e.g. via bumping the
     // crate's version number).
 
     create_session_if_not_set_then(Edition::Edition2024, |_| {
-        let id0 = StableCrateId::new(Symbol::intern("foo"), false, vec!["1".to_string()]);
-        let id1 = StableCrateId::new(Symbol::intern("foo"), false, vec!["2".to_string()]);
+        let id0 = StableCrateId::new(Symbol::intern("foo"), false, vec!["1".to_string()], "");
+        let id1 = StableCrateId::new(Symbol::intern("foo"), false, vec!["2".to_string()], "");
 
         let h0 = mk_test_hash(id0);
         let h1 = mk_test_hash(id1);
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index bba049c3819..a477013ed6f 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -721,7 +721,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // ICE this expression in particular (see #43162).
                 if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind {
                     if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust {
-                        fatally_break_rust(self.tcx.sess);
+                        fatally_break_rust(self.tcx);
                     }
                 }
             }
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index a2a4362e2f5..8aa798e9d38 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -71,7 +71,6 @@ use rustc_middle::traits;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::config;
-use rustc_session::Session;
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::{sym, Span};
 
@@ -437,8 +436,8 @@ enum TupleArgumentsFlag {
     TupleArguments,
 }
 
-fn fatally_break_rust(sess: &Session) {
-    let handler = sess.diagnostic();
+fn fatally_break_rust(tcx: TyCtxt<'_>) {
+    let handler = tcx.sess.diagnostic();
     handler.span_bug_no_panic(
         MultiSpan::new(),
         "It looks like you're trying to break rust; would you like some ICE?",
@@ -450,7 +449,7 @@ fn fatally_break_rust(sess: &Session) {
     );
     handler.note_without_error(format!(
         "rustc {} running on {}",
-        option_env!("CFG_VERSION").unwrap_or("unknown_version"),
+        tcx.sess.cfg_version,
         config::host_triple(),
     ));
 }
diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs
index dc981c6179e..25bf83f64a0 100644
--- a/compiler/rustc_incremental/src/persist/file_format.rs
+++ b/compiler/rustc_incremental/src/persist/file_format.rs
@@ -14,6 +14,7 @@ use rustc_data_structures::memmap::Mmap;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
 use rustc_serialize::Encoder;
 use rustc_session::Session;
+use std::borrow::Cow;
 use std::env;
 use std::fs;
 use std::io::{self, Read};
@@ -25,17 +26,12 @@ const FILE_MAGIC: &[u8] = b"RSIC";
 /// Change this if the header format changes.
 const HEADER_FORMAT_VERSION: u16 = 0;
 
-/// A version string that hopefully is always different for compiler versions
-/// with different encodings of incremental compilation artifacts. Contains
-/// the Git commit hash.
-const RUSTC_VERSION: Option<&str> = option_env!("CFG_VERSION");
-
-pub(crate) fn write_file_header(stream: &mut FileEncoder, nightly_build: bool) {
+pub(crate) fn write_file_header(stream: &mut FileEncoder, sess: &Session) {
     stream.emit_raw_bytes(FILE_MAGIC);
     stream
         .emit_raw_bytes(&[(HEADER_FORMAT_VERSION >> 0) as u8, (HEADER_FORMAT_VERSION >> 8) as u8]);
 
-    let rustc_version = rustc_version(nightly_build);
+    let rustc_version = rustc_version(sess.is_nightly_build(), sess.cfg_version);
     assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize);
     stream.emit_raw_bytes(&[rustc_version.len() as u8]);
     stream.emit_raw_bytes(rustc_version.as_bytes());
@@ -73,7 +69,7 @@ where
         }
     };
 
-    write_file_header(&mut encoder, sess.is_nightly_build());
+    write_file_header(&mut encoder, sess);
 
     match encode(encoder) {
         Ok(position) => {
@@ -100,9 +96,10 @@ where
 /// - Returns `Err(..)` if some kind of IO error occurred while reading the
 ///   file.
 pub fn read_file(
-    report_incremental_info: bool,
     path: &Path,
-    nightly_build: bool,
+    report_incremental_info: bool,
+    is_nightly_build: bool,
+    cfg_version: &'static str,
 ) -> io::Result<Option<(Mmap, usize)>> {
     let file = match fs::File::open(path) {
         Ok(file) => file,
@@ -152,7 +149,7 @@ pub fn read_file(
         let mut buffer = vec![0; rustc_version_str_len];
         file.read_exact(&mut buffer)?;
 
-        if buffer != rustc_version(nightly_build).as_bytes() {
+        if buffer != rustc_version(is_nightly_build, cfg_version).as_bytes() {
             report_format_mismatch(report_incremental_info, path, "Different compiler version");
             return Ok(None);
         }
@@ -174,17 +171,15 @@ fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: &
     }
 }
 
-fn rustc_version(nightly_build: bool) -> String {
+/// A version string that hopefully is always different for compiler versions
+/// with different encodings of incremental compilation artifacts. Contains
+/// the Git commit hash.
+fn rustc_version(nightly_build: bool, cfg_version: &'static str) -> Cow<'static, str> {
     if nightly_build {
-        if let Some(val) = env::var_os("RUSTC_FORCE_RUSTC_VERSION") {
-            return val.to_string_lossy().into_owned();
+        if let Ok(val) = env::var("RUSTC_FORCE_RUSTC_VERSION") {
+            return val.into();
         }
     }
 
-    RUSTC_VERSION
-        .expect(
-            "Cannot use rustc without explicit version for \
-                          incremental compilation",
-        )
-        .to_string()
+    cfg_version.into()
 }
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index ec7fcbdf884..a4407a93ff3 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -73,12 +73,22 @@ impl<T: Default> LoadResult<T> {
     }
 }
 
-fn load_data(
-    report_incremental_info: bool,
+fn load_data(path: &Path, sess: &Session) -> LoadResult<(Mmap, usize)> {
+    load_data_no_sess(
+        path,
+        sess.opts.unstable_opts.incremental_info,
+        sess.is_nightly_build(),
+        sess.cfg_version,
+    )
+}
+
+fn load_data_no_sess(
     path: &Path,
-    nightly_build: bool,
+    report_incremental_info: bool,
+    is_nightly_build: bool,
+    cfg_version: &'static str,
 ) -> LoadResult<(Mmap, usize)> {
-    match file_format::read_file(report_incremental_info, path, nightly_build) {
+    match file_format::read_file(path, report_incremental_info, is_nightly_build, cfg_version) {
         Ok(Some(data_and_pos)) => LoadResult::Ok { data: data_and_pos },
         Ok(None) => {
             // The file either didn't exist or was produced by an incompatible
@@ -138,14 +148,13 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
     let expected_hash = sess.opts.dep_tracking_hash(false);
 
     let mut prev_work_products = FxHashMap::default();
-    let nightly_build = sess.is_nightly_build();
 
     // If we are only building with -Zquery-dep-graph but without an actual
     // incr. comp. session directory, we skip this. Otherwise we'd fail
     // when trying to load work products.
     if sess.incr_comp_session_dir_opt().is_some() {
         let work_products_path = work_products_path(sess);
-        let load_result = load_data(report_incremental_info, &work_products_path, nightly_build);
+        let load_result = load_data(&work_products_path, sess);
 
         if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result {
             // Decode the list of work_products
@@ -173,10 +182,13 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
         }
     }
 
+    let is_nightly_build = sess.is_nightly_build();
+    let cfg_version = sess.cfg_version;
+
     MaybeAsync::Async(std::thread::spawn(move || {
         let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
 
-        match load_data(report_incremental_info, &path, nightly_build) {
+        match load_data_no_sess(&path, report_incremental_info, is_nightly_build, cfg_version) {
             LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
             LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err),
             LoadResult::DecodeIncrCache(err) => LoadResult::DecodeIncrCache(err),
@@ -218,11 +230,7 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache<'_>> {
 
     let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
 
-    match load_data(
-        sess.opts.unstable_opts.incremental_info,
-        &query_cache_path(sess),
-        sess.is_nightly_build(),
-    ) {
+    match load_data(&query_cache_path(sess), sess) {
         LoadResult::Ok { data: (bytes, start_pos) } => {
             Some(OnDiskCache::new(sess, bytes, start_pos))
         }
diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs
index 1441e64e41f..7376be6be8b 100644
--- a/compiler/rustc_incremental/src/persist/save.rs
+++ b/compiler/rustc_incremental/src/persist/save.rs
@@ -164,7 +164,7 @@ pub fn build_dep_graph(
         }
     };
 
-    file_format::write_file_header(&mut encoder, sess.is_nightly_build());
+    file_format::write_file_header(&mut encoder, sess);
 
     // First encode the commandline arguments hash
     sess.opts.dep_tracking_hash(false).encode(&mut encoder);
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 48401eabd1e..63b8978ff47 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -89,6 +89,7 @@ pub fn register_plugins<'a>(
         crate_name,
         sess.crate_types().contains(&CrateType::Executable),
         sess.opts.cg.metadata.clone(),
+        sess.cfg_version,
     );
     sess.stable_crate_id.set(stable_crate_id).expect("not yet initialized");
     rustc_incremental::prepare_session_directory(sess, crate_name, stable_crate_id)?;
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index 6483d51a0b9..c441a8ffd6f 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -369,7 +369,7 @@ impl Linker {
 
         if sess.opts.unstable_opts.no_link {
             let rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT);
-            CodegenResults::serialize_rlink(&rlink_file, &codegen_results)
+            CodegenResults::serialize_rlink(sess, &rlink_file, &codegen_results)
                 .map_err(|error| sess.emit_fatal(FailedWritingFile { path: &rlink_file, error }))?;
             return Ok(());
         }
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 1bae771e373..28e719a40e5 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -52,7 +52,8 @@ fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) {
         output_file: None,
         temps_dir,
     };
-    let sess = build_session(sessopts, io, None, registry, vec![], Default::default(), None, None);
+    let sess =
+        build_session(sessopts, io, None, registry, vec![], Default::default(), None, None, "");
     (sess, cfg)
 }
 
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 8d37b1053d8..969722fcc3e 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -104,6 +104,7 @@ pub fn create_session(
         lint_caps,
         file_loader,
         target_override,
+        rustc_version_str().unwrap_or("unknown"),
     );
 
     codegen_backend.init(&sess);
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index c6af8d63289..deafd8df44d 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -246,6 +246,7 @@ pub(crate) struct CrateLocator<'a> {
     only_needs_metadata: bool,
     sysroot: &'a Path,
     metadata_loader: &'a dyn MetadataLoader,
+    cfg_version: &'static str,
 
     // Immutable per-search configuration.
     crate_name: Symbol,
@@ -323,6 +324,7 @@ impl<'a> CrateLocator<'a> {
             only_needs_metadata,
             sysroot: &sess.sysroot,
             metadata_loader,
+            cfg_version: sess.cfg_version,
             crate_name,
             exact_paths: if hash.is_none() {
                 sess.opts
@@ -655,7 +657,7 @@ impl<'a> CrateLocator<'a> {
     }
 
     fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option<Svh> {
-        let rustc_version = rustc_version();
+        let rustc_version = rustc_version(self.cfg_version);
         let found_version = metadata.get_rustc_version();
         if found_version != rustc_version {
             info!("Rejecting via version: expected {} got {}", rustc_version, found_version);
@@ -1097,7 +1099,7 @@ impl CrateError {
                         crate_name,
                         add_info,
                         found_crates,
-                        rustc_version: rustc_version(),
+                        rustc_version: rustc_version(sess.cfg_version),
                     });
                 } else if !locator.crate_rejections.via_invalid.is_empty() {
                     let mut crate_rejections = Vec::new();
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 82c66b9dfb9..d6c26c082fa 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -2267,7 +2267,7 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) {
     };
 
     // Encode the rustc version string in a predictable location.
-    rustc_version().encode(&mut ecx);
+    rustc_version(tcx.sess.cfg_version).encode(&mut ecx);
 
     // Encode all the entries and extra information in the crate,
     // culminating in the `CrateRoot` which points to all of it.
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 84f6b7f934d..eef23b93e86 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -48,8 +48,8 @@ mod def_path_hash_map;
 mod encoder;
 mod table;
 
-pub(crate) fn rustc_version() -> String {
-    format!("rustc {}", option_env!("CFG_VERSION").unwrap_or("unknown version"))
+pub(crate) fn rustc_version(cfg_version: &'static str) -> String {
+    format!("rustc {}", cfg_version)
 }
 
 /// Metadata encoding version.
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 7d9aea02289..b03e6e8aa4d 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -28,7 +28,7 @@ pub fn erase<T: EraseType>(src: T) -> Erase<T> {
     };
 
     Erased::<<T as EraseType>::Result> {
-        // SAFETY: Is it safe to transmute to MaybeUninit for types with the same sizes.
+        // SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes.
         data: unsafe { transmute_copy(&src) },
     }
 }
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index a988d7f28e6..cdac74759e3 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -211,6 +211,9 @@ pub struct Session {
 
     /// Set of enabled features for the current target, including unstable ones.
     pub unstable_target_features: FxIndexSet<Symbol>,
+
+    /// The version of the rustc process, possibly including a commit hash and description.
+    pub cfg_version: &'static str,
 }
 
 pub struct PerfStats {
@@ -1380,6 +1383,7 @@ pub fn build_session(
     driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
     file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
     target_override: Option<Target>,
+    cfg_version: &'static str,
 ) -> Session {
     // FIXME: This is not general enough to make the warning lint completely override
     // normal diagnostic warnings, since the warning lint can also be denied and changed
@@ -1524,6 +1528,7 @@ pub fn build_session(
         asm_arch,
         target_features: Default::default(),
         unstable_target_features: Default::default(),
+        cfg_version,
     };
 
     validate_commandline_args_with_session_available(&sess);
diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs
index 8c58b52a5dc..f65a6aa4fb2 100644
--- a/compiler/rustc_span/src/def_id.rs
+++ b/compiler/rustc_span/src/def_id.rs
@@ -146,7 +146,12 @@ pub struct StableCrateId(pub(crate) Hash64);
 impl StableCrateId {
     /// Computes the stable ID for a crate with the given name and
     /// `-Cmetadata` arguments.
-    pub fn new(crate_name: Symbol, is_exe: bool, mut metadata: Vec<String>) -> StableCrateId {
+    pub fn new(
+        crate_name: Symbol,
+        is_exe: bool,
+        mut metadata: Vec<String>,
+        cfg_version: &'static str,
+    ) -> StableCrateId {
         let mut hasher = StableHasher::new();
         // We must hash the string text of the crate name, not the id, as the id is not stable
         // across builds.
@@ -180,7 +185,7 @@ impl StableCrateId {
         if let Some(val) = std::env::var_os("RUSTC_FORCE_RUSTC_VERSION") {
             hasher.write(val.to_string_lossy().into_owned().as_bytes())
         } else {
-            hasher.write(option_env!("CFG_VERSION").unwrap_or("unknown version").as_bytes());
+            hasher.write(cfg_version.as_bytes())
         }
 
         StableCrateId(hasher.finish())
diff --git a/src/tools/replace-version-placeholder/src/main.rs b/src/tools/replace-version-placeholder/src/main.rs
index 0aebfc4aad2..5c9c18b9b36 100644
--- a/src/tools/replace-version-placeholder/src/main.rs
+++ b/src/tools/replace-version-placeholder/src/main.rs
@@ -8,15 +8,13 @@ fn main() {
     let version_path = root_path.join("src").join("version");
     let version_str = t!(std::fs::read_to_string(&version_path), version_path);
     let version_str = version_str.trim();
-    walk::walk(
-        &root_path,
+    walk::walk_many(
+        &[&root_path.join("compiler"), &root_path.join("library")],
         |path, _is_dir| {
             walk::filter_dirs(path)
                 // We exempt these as they require the placeholder
                 // for their operation
                 || path.ends_with("compiler/rustc_attr/src/builtin.rs")
-                || path.ends_with("src/tools/tidy/src/features/version.rs")
-                || path.ends_with("src/tools/replace-version-placeholder")
         },
         &mut |entry, contents| {
             if !contents.contains(VERSION_PLACEHOLDER) {
diff --git a/tests/run-make/CURRENT_RUSTC_VERSION/Makefile b/tests/run-make/CURRENT_RUSTC_VERSION/Makefile
new file mode 100644
index 00000000000..7940dae207b
--- /dev/null
+++ b/tests/run-make/CURRENT_RUSTC_VERSION/Makefile
@@ -0,0 +1,6 @@
+include ../tools.mk
+
+all:
+	$(RUSTC) --emit=metadata --crate-type lib stable.rs
+	$(RUSTC) --emit=metadata --extern stable=$(TMPDIR)/libstable.rmeta main.rs 2>&1 >/dev/null \
+		| $(CGREP) -e "stable since $$(cat $(S)/src/version)(-[a-zA-Z]+)?"
diff --git a/tests/run-make/CURRENT_RUSTC_VERSION/main.rs b/tests/run-make/CURRENT_RUSTC_VERSION/main.rs
new file mode 100644
index 00000000000..466aaa82bd4
--- /dev/null
+++ b/tests/run-make/CURRENT_RUSTC_VERSION/main.rs
@@ -0,0 +1,4 @@
+#![feature(foo)]
+extern crate stable;
+
+fn main() {}
diff --git a/tests/run-make/CURRENT_RUSTC_VERSION/stable.rs b/tests/run-make/CURRENT_RUSTC_VERSION/stable.rs
new file mode 100644
index 00000000000..2fd09aded60
--- /dev/null
+++ b/tests/run-make/CURRENT_RUSTC_VERSION/stable.rs
@@ -0,0 +1,5 @@
+#![feature(staged_api)]
+#![stable(since = "1.0.0", feature = "rust1")]
+
+#[stable(since = "CURRENT_RUSTC_VERSION", feature = "foo")]
+pub fn foo() {}