diff --git a/Cargo.lock b/Cargo.lock
index 8bd1e5f3308b4..0fed32f4a9d04 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3593,6 +3593,7 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "serialize",
+ "version_check",
 ]
 
 [[package]]
diff --git a/src/doc/unstable-book/src/language-features/cfg-version.md b/src/doc/unstable-book/src/language-features/cfg-version.md
new file mode 100644
index 0000000000000..2b1e50835b767
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/cfg-version.md
@@ -0,0 +1,34 @@
+# `cfg_version`
+
+The tracking issue for this feature is: [#64796]
+
+[#64796]: https://github.com/rust-lang/rust/issues/64796
+
+------------------------
+
+The `cfg_version` feature makes it possible to execute different code
+depending on the compiler version.
+
+## Examples
+
+```rust
+#![feature(cfg_version)]
+
+#[cfg(version("1.42"))]
+fn a() {
+    // ...
+}
+
+#[cfg(not(version("1.42")))]
+fn a() {
+    // ...
+}
+
+fn b() {
+    if cfg!(version("1.42")) {
+        // ...
+    } else {
+        // ...
+    }
+}
+```
diff --git a/src/librustc_attr/Cargo.toml b/src/librustc_attr/Cargo.toml
index a7a7e3dcc5f02..d7af7fe6143e5 100644
--- a/src/librustc_attr/Cargo.toml
+++ b/src/librustc_attr/Cargo.toml
@@ -3,6 +3,7 @@ authors = ["The Rust Project Developers"]
 name = "rustc_attr"
 version = "0.0.0"
 edition = "2018"
+build = "build.rs"
 
 [lib]
 name = "rustc_attr"
@@ -19,3 +20,4 @@ rustc_feature = { path = "../librustc_feature" }
 rustc_macros = { path = "../librustc_macros" }
 rustc_session = { path = "../librustc_session" }
 rustc_ast = { path = "../librustc_ast" }
+version_check = "0.9"
diff --git a/src/librustc_attr/build.rs b/src/librustc_attr/build.rs
new file mode 100644
index 0000000000000..d230ba91039ad
--- /dev/null
+++ b/src/librustc_attr/build.rs
@@ -0,0 +1,4 @@
+fn main() {
+    println!("cargo:rerun-if-changed=build.rs");
+    println!("cargo:rerun-if-env-changed=CFG_VERSION");
+}
diff --git a/src/librustc_attr/builtin.rs b/src/librustc_attr/builtin.rs
index 0a6a4821e278e..ce38e3f5f4e4e 100644
--- a/src/librustc_attr/builtin.rs
+++ b/src/librustc_attr/builtin.rs
@@ -2,7 +2,7 @@
 
 use super::{find_by_name, mark_used};
 
-use rustc_ast::ast::{self, Attribute, MetaItem, MetaItemKind, NestedMetaItem};
+use rustc_ast::ast::{self, Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem};
 use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, Handler};
 use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
@@ -11,6 +11,7 @@ use rustc_session::parse::{feature_err, ParseSess};
 use rustc_span::hygiene::Transparency;
 use rustc_span::{symbol::sym, symbol::Symbol, Span};
 use std::num::NonZeroU32;
+use version_check::Version;
 
 pub fn is_builtin_attr(attr: &Attribute) -> bool {
     attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some()
@@ -568,11 +569,8 @@ pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
 
 /// Tests if a cfg-pattern matches the cfg set
 pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
-    eval_condition(cfg, sess, &mut |cfg| {
-        let gate = find_gated_cfg(|sym| cfg.check_name(sym));
-        if let (Some(feats), Some(gated_cfg)) = (features, gate) {
-            gate_cfg(&gated_cfg, cfg.span, sess, feats);
-        }
+    eval_condition(cfg, sess, features, &mut |cfg| {
+        try_gate_cfg(cfg, sess, features);
         let error = |span, msg| {
             sess.span_diagnostic.span_err(span, msg);
             true
@@ -603,6 +601,13 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
     })
 }
 
+fn try_gate_cfg(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) {
+    let gate = find_gated_cfg(|sym| cfg.check_name(sym));
+    if let (Some(feats), Some(gated_cfg)) = (features, gate) {
+        gate_cfg(&gated_cfg, cfg.span, sess, feats);
+    }
+}
+
 fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &Features) {
     let (cfg, feature, has_feature) = gated_cfg;
     if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
@@ -616,9 +621,41 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F
 pub fn eval_condition(
     cfg: &ast::MetaItem,
     sess: &ParseSess,
+    features: Option<&Features>,
     eval: &mut impl FnMut(&ast::MetaItem) -> bool,
 ) -> bool {
     match cfg.kind {
+        ast::MetaItemKind::List(ref mis) if cfg.name_or_empty() == sym::version => {
+            try_gate_cfg(cfg, sess, features);
+            let (min_version, span) = match &mis[..] {
+                [NestedMetaItem::Literal(Lit { kind: LitKind::Str(sym, ..), span, .. })] => {
+                    (sym, span)
+                }
+                [NestedMetaItem::Literal(Lit { span, .. })
+                | NestedMetaItem::MetaItem(MetaItem { span, .. })] => {
+                    sess.span_diagnostic
+                        .struct_span_err(*span, &*format!("expected a version literal"))
+                        .emit();
+                    return false;
+                }
+                [..] => {
+                    sess.span_diagnostic
+                        .struct_span_err(cfg.span, "expected single version literal")
+                        .emit();
+                    return false;
+                }
+            };
+            let min_version = match Version::parse(&min_version.as_str()) {
+                Some(ver) => ver,
+                None => {
+                    sess.span_diagnostic.struct_span_err(*span, "invalid version literal").emit();
+                    return false;
+                }
+            };
+            let version = Version::parse(env!("CFG_VERSION")).unwrap();
+
+            version >= min_version
+        }
         ast::MetaItemKind::List(ref mis) => {
             for mi in mis.iter() {
                 if !mi.is_meta_item() {
@@ -634,12 +671,12 @@ pub fn eval_condition(
             // The unwraps below may look dangerous, but we've already asserted
             // that they won't fail with the loop above.
             match cfg.name_or_empty() {
-                sym::any => {
-                    mis.iter().any(|mi| eval_condition(mi.meta_item().unwrap(), sess, eval))
-                }
-                sym::all => {
-                    mis.iter().all(|mi| eval_condition(mi.meta_item().unwrap(), sess, eval))
-                }
+                sym::any => mis
+                    .iter()
+                    .any(|mi| eval_condition(mi.meta_item().unwrap(), sess, features, eval)),
+                sym::all => mis
+                    .iter()
+                    .all(|mi| eval_condition(mi.meta_item().unwrap(), sess, features, eval)),
                 sym::not => {
                     if mis.len() != 1 {
                         struct_span_err!(
@@ -652,7 +689,7 @@ pub fn eval_condition(
                         return false;
                     }
 
-                    !eval_condition(mis[0].meta_item().unwrap(), sess, eval)
+                    !eval_condition(mis[0].meta_item().unwrap(), sess, features, eval)
                 }
                 _ => {
                     struct_span_err!(
diff --git a/src/librustc_attr/lib.rs b/src/librustc_attr/lib.rs
index 9803501fb96c2..66c4495c5afc8 100644
--- a/src/librustc_attr/lib.rs
+++ b/src/librustc_attr/lib.rs
@@ -4,6 +4,8 @@
 //! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
 //! to this crate.
 
+#![feature(or_patterns)]
+
 mod builtin;
 
 pub use builtin::*;
diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs
index cec9e0ce3af71..a1dd7a5ca5225 100644
--- a/src/librustc_feature/active.rs
+++ b/src/librustc_feature/active.rs
@@ -562,6 +562,9 @@ declare_features! (
     /// Allows the use of `#[target_feature]` on safe functions.
     (active, target_feature_11, "1.45.0", Some(69098), None),
 
+    /// Allow conditional compilation depending on rust version
+    (active, cfg_version, "1.45.0", Some(64796), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/src/librustc_feature/builtin_attrs.rs b/src/librustc_feature/builtin_attrs.rs
index e4975aae6b983..466b318bca730 100644
--- a/src/librustc_feature/builtin_attrs.rs
+++ b/src/librustc_feature/builtin_attrs.rs
@@ -26,6 +26,7 @@ const GATED_CFGS: &[GatedCfg] = &[
     (sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
     (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
     (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)),
+    (sym::version, sym::cfg_version, cfg_fn!(cfg_version)),
 ];
 
 /// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index 74d73404acd8e..f194506e66069 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -192,6 +192,7 @@ symbols! {
         cfg_target_has_atomic,
         cfg_target_thread_local,
         cfg_target_vendor,
+        cfg_version,
         char,
         clippy,
         clone,
@@ -805,6 +806,7 @@ symbols! {
         var,
         vec,
         Vec,
+        version,
         vis,
         visible_private_types,
         volatile,
diff --git a/src/librustc_trait_selection/traits/on_unimplemented.rs b/src/librustc_trait_selection/traits/on_unimplemented.rs
index cf29c4249c019..3fbc0b7f08e5f 100644
--- a/src/librustc_trait_selection/traits/on_unimplemented.rs
+++ b/src/librustc_trait_selection/traits/on_unimplemented.rs
@@ -81,7 +81,7 @@ impl<'tcx> OnUnimplementedDirective {
                         None,
                     )
                 })?;
-            attr::eval_condition(cond, &tcx.sess.parse_sess, &mut |_| true);
+            attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |_| true);
             Some(cond.clone())
         };
 
@@ -208,11 +208,16 @@ impl<'tcx> OnUnimplementedDirective {
 
         for command in self.subcommands.iter().chain(Some(self)).rev() {
             if let Some(ref condition) = command.condition {
-                if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
-                    c.ident().map_or(false, |ident| {
-                        options.contains(&(ident.name, c.value_str().map(|s| s.to_string())))
-                    })
-                }) {
+                if !attr::eval_condition(
+                    condition,
+                    &tcx.sess.parse_sess,
+                    Some(tcx.features()),
+                    &mut |c| {
+                        c.ident().map_or(false, |ident| {
+                            options.contains(&(ident.name, c.value_str().map(|s| s.to_string())))
+                        })
+                    },
+                ) {
                     debug!("evaluate: skipping {:?} due to condition", command);
                     continue;
                 }
diff --git a/src/test/ui/feature-gates/feature-gate-cfg-version.rs b/src/test/ui/feature-gates/feature-gate-cfg-version.rs
new file mode 100644
index 0000000000000..c29ef99945e71
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-cfg-version.rs
@@ -0,0 +1,41 @@
+#[cfg(version("1.44"))]
+//~^ ERROR `cfg(version)` is experimental and subject to change
+fn foo() -> bool { true }
+#[cfg(not(version("1.44")))]
+//~^ ERROR `cfg(version)` is experimental and subject to change
+fn foo() -> bool { false }
+
+#[cfg(version("1.43", "1.44", "1.45"))] //~ ERROR: expected single version literal
+//~^ ERROR `cfg(version)` is experimental and subject to change
+fn bar() -> bool  { false }
+#[cfg(version(false))] //~ ERROR: expected a version literal
+//~^ ERROR `cfg(version)` is experimental and subject to change
+fn bar() -> bool  { false }
+#[cfg(version("foo"))] //~ ERROR: invalid version literal
+//~^ ERROR `cfg(version)` is experimental and subject to change
+fn bar() -> bool  { false }
+#[cfg(version("999"))]
+//~^ ERROR `cfg(version)` is experimental and subject to change
+fn bar() -> bool  { false }
+#[cfg(version("-1"))] //~ ERROR: invalid version literal
+//~^ ERROR `cfg(version)` is experimental and subject to change
+fn bar() -> bool  { false }
+#[cfg(version("65536"))] //~ ERROR: invalid version literal
+//~^ ERROR `cfg(version)` is experimental and subject to change
+fn bar() -> bool  { false }
+#[cfg(version("0"))]
+//~^ ERROR `cfg(version)` is experimental and subject to change
+fn bar() -> bool { true }
+
+#[cfg(version("1.65536.2"))]
+//~^ ERROR `cfg(version)` is experimental and subject to change
+fn version_check_bug() {}
+
+fn main() {
+    // This should fail but due to a bug in version_check `1.65536.2` is interpreted as `1.2`.
+    // See https://github.com/SergioBenitez/version_check/issues/11
+    version_check_bug();
+    assert!(foo());
+    assert!(bar());
+    assert!(cfg!(version("1.42"))); //~ ERROR `cfg(version)` is experimental and subject to change
+}
diff --git a/src/test/ui/feature-gates/feature-gate-cfg-version.stderr b/src/test/ui/feature-gates/feature-gate-cfg-version.stderr
new file mode 100644
index 0000000000000..bdf160b5a0270
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-cfg-version.stderr
@@ -0,0 +1,132 @@
+error[E0658]: `cfg(version)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-version.rs:1:7
+   |
+LL | #[cfg(version("1.44"))]
+   |       ^^^^^^^^^^^^^^^
+   |
+   = note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
+   = help: add `#![feature(cfg_version)]` to the crate attributes to enable
+
+error[E0658]: `cfg(version)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-version.rs:4:11
+   |
+LL | #[cfg(not(version("1.44")))]
+   |           ^^^^^^^^^^^^^^^
+   |
+   = note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
+   = help: add `#![feature(cfg_version)]` to the crate attributes to enable
+
+error[E0658]: `cfg(version)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-version.rs:8:7
+   |
+LL | #[cfg(version("1.43", "1.44", "1.45"))]
+   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
+   = help: add `#![feature(cfg_version)]` to the crate attributes to enable
+
+error: expected single version literal
+  --> $DIR/feature-gate-cfg-version.rs:8:7
+   |
+LL | #[cfg(version("1.43", "1.44", "1.45"))]
+   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0658]: `cfg(version)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-version.rs:11:7
+   |
+LL | #[cfg(version(false))]
+   |       ^^^^^^^^^^^^^^
+   |
+   = note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
+   = help: add `#![feature(cfg_version)]` to the crate attributes to enable
+
+error: expected a version literal
+  --> $DIR/feature-gate-cfg-version.rs:11:15
+   |
+LL | #[cfg(version(false))]
+   |               ^^^^^
+
+error[E0658]: `cfg(version)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-version.rs:14:7
+   |
+LL | #[cfg(version("foo"))]
+   |       ^^^^^^^^^^^^^^
+   |
+   = note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
+   = help: add `#![feature(cfg_version)]` to the crate attributes to enable
+
+error: invalid version literal
+  --> $DIR/feature-gate-cfg-version.rs:14:15
+   |
+LL | #[cfg(version("foo"))]
+   |               ^^^^^
+
+error[E0658]: `cfg(version)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-version.rs:17:7
+   |
+LL | #[cfg(version("999"))]
+   |       ^^^^^^^^^^^^^^
+   |
+   = note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
+   = help: add `#![feature(cfg_version)]` to the crate attributes to enable
+
+error[E0658]: `cfg(version)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-version.rs:20:7
+   |
+LL | #[cfg(version("-1"))]
+   |       ^^^^^^^^^^^^^
+   |
+   = note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
+   = help: add `#![feature(cfg_version)]` to the crate attributes to enable
+
+error: invalid version literal
+  --> $DIR/feature-gate-cfg-version.rs:20:15
+   |
+LL | #[cfg(version("-1"))]
+   |               ^^^^
+
+error[E0658]: `cfg(version)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-version.rs:23:7
+   |
+LL | #[cfg(version("65536"))]
+   |       ^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
+   = help: add `#![feature(cfg_version)]` to the crate attributes to enable
+
+error: invalid version literal
+  --> $DIR/feature-gate-cfg-version.rs:23:15
+   |
+LL | #[cfg(version("65536"))]
+   |               ^^^^^^^
+
+error[E0658]: `cfg(version)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-version.rs:26:7
+   |
+LL | #[cfg(version("0"))]
+   |       ^^^^^^^^^^^^
+   |
+   = note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
+   = help: add `#![feature(cfg_version)]` to the crate attributes to enable
+
+error[E0658]: `cfg(version)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-version.rs:30:7
+   |
+LL | #[cfg(version("1.65536.2"))]
+   |       ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
+   = help: add `#![feature(cfg_version)]` to the crate attributes to enable
+
+error[E0658]: `cfg(version)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-version.rs:40:18
+   |
+LL |     assert!(cfg!(version("1.42")));
+   |                  ^^^^^^^^^^^^^^^
+   |
+   = note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
+   = help: add `#![feature(cfg_version)]` to the crate attributes to enable
+
+error: aborting due to 16 previous errors
+
+For more information about this error, try `rustc --explain E0658`.