diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 26baaf07880f1..5dd4236a8ddad 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -586,12 +586,14 @@ pub fn eval_condition(
                     return false;
                 }
             };
-            let channel = env!("CFG_RELEASE_CHANNEL");
-            let nightly = channel == "nightly" || channel == "dev";
             let rustc_version = parse_version(env!("CFG_RELEASE"), true).unwrap();
 
-            // See https://github.com/rust-lang/rust/issues/64796#issuecomment-625474439 for details
-            if nightly { rustc_version > min_version } else { rustc_version >= min_version }
+            // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
+            if sess.assume_incomplete_release {
+                rustc_version > min_version
+            } else {
+                rustc_version >= min_version
+            }
         }
         ast::MetaItemKind::List(ref mis) => {
             for mi in mis.iter() {
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 55d521a9b5ff5..762a8da632e7e 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -538,6 +538,7 @@ fn test_debugging_options_tracking_hash() {
     // This list is in alphabetical order.
     tracked!(allow_features, Some(vec![String::from("lang_items")]));
     tracked!(always_encode_mir, true);
+    tracked!(assume_incomplete_release, true);
     tracked!(asm_comments, true);
     tracked!(binary_dep_depinfo, true);
     tracked!(chalk, true);
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 30af65e49a075..e8e6a17b4d83f 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -856,6 +856,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "only allow the listed language features to be enabled in code (space separated)"),
     always_encode_mir: bool = (false, parse_bool, [TRACKED],
         "encode MIR of all functions into the crate metadata (default: no)"),
+    assume_incomplete_release: bool = (false, parse_bool, [TRACKED],
+        "make cfg(version) treat the current version as incomplete (default: no)"),
     asm_comments: bool = (false, parse_bool, [TRACKED],
         "generate comments into the assembly (may change behavior) (default: no)"),
     ast_json: bool = (false, parse_bool, [UNTRACKED],
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index b1a4834241730..81b38347414e8 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -138,6 +138,8 @@ pub struct ParseSess {
     pub env_depinfo: Lock<FxHashSet<(Symbol, Option<Symbol>)>>,
     /// All the type ascriptions expressions that have had a suggestion for likely path typo.
     pub type_ascription_path_suggestions: Lock<FxHashSet<Span>>,
+    /// Whether cfg(version) should treat the current release as incomplete
+    pub assume_incomplete_release: bool,
 }
 
 impl ParseSess {
@@ -164,6 +166,7 @@ impl ParseSess {
             reached_eof: Lock::new(false),
             env_depinfo: Default::default(),
             type_ascription_path_suggestions: Default::default(),
+            assume_incomplete_release: false,
         }
     }
 
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 6d01854228662..891b9616c2c1d 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1336,7 +1336,8 @@ pub fn build_session(
         None
     };
 
-    let parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map);
+    let mut parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map);
+    parse_sess.assume_incomplete_release = sopts.debugging_opts.assume_incomplete_release;
     let sysroot = match &sopts.maybe_sysroot {
         Some(sysroot) => sysroot.clone(),
         None => filesearch::get_or_default_sysroot(),
diff --git a/src/test/ui/cfg/assume-incomplete-release/assume-incomplete.rs b/src/test/ui/cfg/assume-incomplete-release/assume-incomplete.rs
new file mode 100644
index 0000000000000..24d2dc645519d
--- /dev/null
+++ b/src/test/ui/cfg/assume-incomplete-release/assume-incomplete.rs
@@ -0,0 +1,38 @@
+// run-pass
+// aux-build:ver-cfg-rel.rs
+// revisions: assume no_assume
+// [assume]compile-flags: -Z assume-incomplete-release
+
+#![feature(cfg_version)]
+
+extern crate ver_cfg_rel;
+
+use ver_cfg_rel::ver_cfg_rel;
+
+#[ver_cfg_rel("-2")]
+fn foo_2() { }
+
+#[ver_cfg_rel("-1")]
+fn foo_1() { }
+
+#[cfg(assume)]
+#[ver_cfg_rel("0")]
+fn foo() { compile_error!("wrong+0") }
+
+#[cfg(no_assume)]
+#[ver_cfg_rel("0")]
+fn foo() { }
+
+#[ver_cfg_rel("1")]
+fn bar() { compile_error!("wrong+1") }
+
+#[ver_cfg_rel("2")]
+fn bar() { compile_error!("wrong+2") }
+
+fn main() {
+    foo_2();
+    foo_1();
+
+    #[cfg(no_assume)]
+    foo();
+}
diff --git a/src/test/ui/cfg/assume-incomplete-release/auxiliary/ver-cfg-rel.rs b/src/test/ui/cfg/assume-incomplete-release/auxiliary/ver-cfg-rel.rs
new file mode 100644
index 0000000000000..6787527027e33
--- /dev/null
+++ b/src/test/ui/cfg/assume-incomplete-release/auxiliary/ver-cfg-rel.rs
@@ -0,0 +1,56 @@
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::{TokenStream, TokenTree as Tt};
+use std::str::FromStr;
+
+// String containing the current version number of the tip, i.e. "1.41.2"
+static VERSION_NUMBER: &str = include_str!("../../../../../version");
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+struct Version {
+    major: i16,
+    minor: i16,
+    patch: i16,
+}
+
+fn parse_version(s: &str) -> Option<Version> {
+    let mut digits = s.splitn(3, '.');
+    let major = digits.next()?.parse().ok()?;
+    let minor = digits.next()?.parse().ok()?;
+    let patch = digits.next().unwrap_or("0").trim().parse().ok()?;
+    Some(Version { major, minor, patch })
+}
+
+#[proc_macro_attribute]
+/// Emits a #[cfg(version)] relative to the current one, so passing
+/// -1 as argument on compiler 1.50 will emit #[cfg(version("1.49.0"))],
+/// while 1 will emit #[cfg(version("1.51.0"))]
+pub fn ver_cfg_rel(attr: TokenStream, input: TokenStream) -> TokenStream {
+    let mut v_rel = None;
+    for a in attr.into_iter() {
+        match a {
+            Tt::Literal(l) => {
+                let mut s = l.to_string();
+                let s = s.trim_matches('"');
+                let v: i16 = s.parse().unwrap();
+                v_rel = Some(v);
+                break;
+            },
+            _ => panic!("{:?}", a),
+        }
+    }
+    let v_rel = v_rel.unwrap();
+
+    let mut v = parse_version(VERSION_NUMBER).unwrap();
+    v.minor += v_rel;
+
+    let attr_str = format!("#[cfg(version(\"{}.{}.{}\"))]", v.major, v.minor, v.patch);
+    let mut res = Vec::<Tt>::new();
+    res.extend(TokenStream::from_str(&attr_str).unwrap().into_iter());
+    res.extend(input.into_iter());
+    res.into_iter().collect()
+}