about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/contracts.rs20
-rw-r--r--compiler/rustc_feature/src/unstable.rs4
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs20
-rw-r--r--compiler/rustc_session/src/parse.rs5
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--library/core/src/contracts.rs4
-rw-r--r--library/core/src/intrinsics/mod.rs8
-rw-r--r--library/core/src/lib.rs2
-rw-r--r--library/core/src/macros/mod.rs8
-rw-r--r--tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs (renamed from tests/ui/contracts/contract-ast-extensions-nest.rs)2
-rw-r--r--tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs (renamed from tests/ui/contracts/contract-ast-extensions-tail.rs)2
-rw-r--r--tests/ui/contracts/internal_machinery/contract-intrinsics.rs (renamed from tests/ui/contracts/contract-intrinsics.rs)2
-rw-r--r--tests/ui/contracts/internal_machinery/contract-lang-items.rs (renamed from tests/ui/contracts/contract-lang-items.rs)3
-rw-r--r--tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs (renamed from tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs)2
-rw-r--r--tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs (renamed from tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs)2
-rw-r--r--tests/ui/contracts/internal_machinery/internal-feature-gating.rs23
-rw-r--r--tests/ui/contracts/internal_machinery/internal-feature-gating.stderr73
-rw-r--r--tests/ui/feature-gates/feature-gate-rustc-contracts.rs11
-rw-r--r--tests/ui/feature-gates/feature-gate-rustc-contracts.stderr43
20 files changed, 210 insertions, 28 deletions
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 80b99f94485..a3af942a10f 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -548,6 +548,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
     gate_all!(unsafe_fields, "`unsafe` fields are experimental");
     gate_all!(unsafe_binders, "unsafe binder types are experimental");
+    gate_all!(rustc_contracts, "contracts are experimental");
+    gate_all!(rustc_contracts_internals, "contract internal machinery is for internal use only");
 
     if !visitor.features.never_patterns() {
         if let Some(spans) = spans.get(&sym::never_patterns) {
diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs
index d269287b555..be7f276cdaa 100644
--- a/compiler/rustc_builtin_macros/src/contracts.rs
+++ b/compiler/rustc_builtin_macros/src/contracts.rs
@@ -36,17 +36,17 @@ impl AttrProcMacro for ExpandEnsures {
 }
 
 fn expand_injecting_circa_where_clause(
-    _ecx: &mut ExtCtxt<'_>,
+    ecx: &mut ExtCtxt<'_>,
     attr_span: Span,
     annotated: TokenStream,
     inject: impl FnOnce(&mut Vec<TokenTree>) -> Result<(), ErrorGuaranteed>,
 ) -> Result<TokenStream, ErrorGuaranteed> {
     let mut new_tts = Vec::with_capacity(annotated.len());
-    let mut cursor = annotated.into_trees();
+    let mut cursor = annotated.iter();
 
     // Find the `fn name<G,...>(x:X,...)` and inject the AST contract forms right after
     // the formal parameters (and return type if any).
-    while let Some(tt) = cursor.next_ref() {
+    while let Some(tt) = cursor.next() {
         new_tts.push(tt.clone());
         if let TokenTree::Token(tok, _) = tt
             && tok.is_ident_named(kw::Fn)
@@ -58,7 +58,7 @@ fn expand_injecting_circa_where_clause(
     // Found the `fn` keyword, now find the formal parameters.
     //
     // FIXME: can this fail if you have parentheticals in a generics list, like `fn foo<F: Fn(X) -> Y>` ?
-    while let Some(tt) = cursor.next_ref() {
+    while let Some(tt) = cursor.next() {
         new_tts.push(tt.clone());
 
         if let TokenTree::Delimited(_, _, token::Delimiter::Parenthesis, _) = tt {
@@ -81,7 +81,7 @@ fn expand_injecting_circa_where_clause(
     // parse the type expression itself. But rather than try to fix things with hacks like that,
     // time might be better spent extending the attribute expander to suport tt-annotation atop
     // ast-annotated, which would be an elegant way to sidestep all of this.
-    let mut opt_next_tt = cursor.next_ref();
+    let mut opt_next_tt = cursor.next();
     while let Some(next_tt) = opt_next_tt {
         if let TokenTree::Token(tok, _) = next_tt
             && tok.is_ident_named(kw::Where)
@@ -97,8 +97,7 @@ fn expand_injecting_circa_where_clause(
 
         // for anything else, transcribe the tt and keep looking.
         new_tts.push(next_tt.clone());
-        opt_next_tt = cursor.next_ref();
-        continue;
+        opt_next_tt = cursor.next();
     }
 
     // At this point, we've transcribed everything from the `fn` through the formal parameter list
@@ -118,10 +117,15 @@ fn expand_injecting_circa_where_clause(
     if let Some(tt) = opt_next_tt {
         new_tts.push(tt.clone());
     }
-    while let Some(tt) = cursor.next_ref() {
+    while let Some(tt) = cursor.next() {
         new_tts.push(tt.clone());
     }
 
+    // Record the span as a contract attribute expansion.
+    // This is used later to stop users from using the extended syntax directly
+    // which is gated via `rustc_contracts_internals`.
+    ecx.psess().contract_attribute_spans.push(attr_span);
+
     Ok(TokenStream::new(new_tts))
 }
 
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 08a5e22db3a..57bcd8c5eca 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -608,6 +608,10 @@ declare_features! (
     (unstable, return_type_notation, "1.70.0", Some(109417)),
     /// Allows `extern "rust-cold"`.
     (unstable, rust_cold_cc, "1.63.0", Some(97544)),
+    /// Allows use of contracts attributes.
+    (unstable, rustc_contracts, "CURRENT_RUSTC_VERSION", Some(133866)),
+    /// Allows access to internal machinery used to implement contracts.
+    (unstable, rustc_contracts_internals, "CURRENT_RUSTC_VERSION", Some(133866)),
     /// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics
     (unstable, sha512_sm_x86, "1.82.0", Some(126624)),
     /// Allows the use of SIMD types in functions declared in `extern` blocks.
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index d5ac8d1588d..14b949dbc3d 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -4,7 +4,7 @@ use rustc_ast::{
     WhereClause, token,
 };
 use rustc_errors::{Applicability, PResult};
-use rustc_span::{Ident, Span, kw};
+use rustc_span::{Ident, Span, kw, sym};
 use thin_vec::ThinVec;
 
 use super::{ForceCollect, Parser, Trailing, UsePreAttrPos};
@@ -302,13 +302,27 @@ impl<'a> Parser<'a> {
     pub(super) fn parse_contract(
         &mut self,
     ) -> PResult<'a, Option<rustc_ast::ptr::P<ast::FnContract>>> {
+        let gate = |span| {
+            if self.psess.contract_attribute_spans.contains(span) {
+                // span was generated via a builtin contracts attribute, so gate as end-user visible
+                self.psess.gated_spans.gate(sym::rustc_contracts, span);
+            } else {
+                // span was not generated via a builtin contracts attribute, so gate as internal machinery
+                self.psess.gated_spans.gate(sym::rustc_contracts_internals, span);
+            }
+        };
+
         let requires = if self.eat_keyword_noexpect(exp!(RustcContractRequires).kw) {
-            Some(self.parse_expr()?)
+            let precond = self.parse_expr()?;
+            gate(precond.span);
+            Some(precond)
         } else {
             None
         };
         let ensures = if self.eat_keyword_noexpect(exp!(RustcContractEnsures).kw) {
-            Some(self.parse_expr()?)
+            let postcond = self.parse_expr()?;
+            gate(postcond.span);
+            Some(postcond)
         } else {
             None
         };
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 81ae06602cd..abfd3efc611 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -207,6 +207,10 @@ pub struct ParseSess {
     pub config: Cfg,
     pub check_config: CheckCfg,
     pub edition: Edition,
+    /// Places where contract attributes were expanded into unstable AST forms.
+    /// This is used to allowlist those spans (so that we only check them against the feature
+    /// gate for the externally visible interface, and not internal implmentation machinery).
+    pub contract_attribute_spans: AppendOnlyVec<Span>,
     /// Places where raw identifiers were used. This is used to avoid complaining about idents
     /// clashing with keywords in new editions.
     pub raw_identifier_spans: AppendOnlyVec<Span>,
@@ -255,6 +259,7 @@ impl ParseSess {
             config: Cfg::default(),
             check_config: CheckCfg::default(),
             edition: ExpnId::root().expn_data().edition,
+            contract_attribute_spans: Default::default(),
             raw_identifier_spans: Default::default(),
             bad_unicode_identifiers: Lock::new(Default::default()),
             source_map,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 43bc69e6e7c..ea2ce5475c2 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1716,6 +1716,8 @@ symbols! {
         rustc_const_stable,
         rustc_const_stable_indirect,
         rustc_const_unstable,
+        rustc_contracts,
+        rustc_contracts_internals,
         rustc_conversion_suggestion,
         rustc_deallocator,
         rustc_def_path,
diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs
index 3efd2df0a38..b155dbc213e 100644
--- a/library/core/src/contracts.rs
+++ b/library/core/src/contracts.rs
@@ -8,7 +8,7 @@ pub use crate::macros::builtin::contracts_requires as requires;
 /// Emitted by rustc as a desugaring of `#[requires(PRED)] fn foo(x: X) { ... }`
 /// into: `fn foo(x: X) { check_requires(|| PRED) ... }`
 #[cfg(not(bootstrap))]
-#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
+#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
 #[lang = "contract_check_requires"]
 #[track_caller]
 pub fn check_requires<C: FnOnce() -> bool>(c: C) {
@@ -21,7 +21,7 @@ pub fn check_requires<C: FnOnce() -> bool>(c: C) {
 /// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }`
 /// (including the implicit return of the tail expression, if any).
 #[cfg(not(bootstrap))]
-#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
+#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
 #[lang = "contract_build_check_ensures"]
 #[track_caller]
 pub fn build_check_ensures<Ret, C>(c: C) -> impl (FnOnce(Ret) -> Ret) + Copy
diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs
index beea0996775..a7f0f09f0c6 100644
--- a/library/core/src/intrinsics/mod.rs
+++ b/library/core/src/intrinsics/mod.rs
@@ -4051,8 +4051,8 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize)
 /// checking is turned on, so that we can specify contracts in libstd
 /// and let an end user opt into turning them on.
 #[cfg(not(bootstrap))]
-#[rustc_const_unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
-#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
+#[rustc_const_unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
+#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
 #[inline(always)]
 #[rustc_intrinsic]
 pub const fn contract_checks() -> bool {
@@ -4063,14 +4063,14 @@ pub const fn contract_checks() -> bool {
 }
 
 #[cfg(not(bootstrap))]
-#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
+#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
 #[rustc_intrinsic]
 pub fn contract_check_requires<C: FnOnce() -> bool>(c: C) -> bool {
     c()
 }
 
 #[cfg(not(bootstrap))]
-#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
+#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
 #[rustc_intrinsic]
 pub fn contract_check_ensures<'a, Ret, C: FnOnce(&'a Ret) -> bool>(ret: &'a Ret, c: C) -> bool {
     c(ret)
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index a31cca5d425..6a0051244f0 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -248,7 +248,7 @@ pub mod autodiff {
 }
 
 #[cfg(not(bootstrap))]
-#[unstable(feature = "rustc_contracts", issue = "none")]
+#[unstable(feature = "rustc_contracts", issue = "133866")]
 pub mod contracts;
 
 #[unstable(feature = "cfg_match", issue = "115585")]
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 1a5d2973dfc..cb37530e90d 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -1783,8 +1783,8 @@ pub(crate) mod builtin {
     /// eventually parsed as a unary closure expression that is
     /// invoked on a reference to the return value.
     #[cfg(not(bootstrap))]
-    #[unstable(feature = "rustc_contracts", issue = "none")]
-    #[allow_internal_unstable(core_intrinsics)]
+    #[unstable(feature = "rustc_contracts", issue = "133866")]
+    #[allow_internal_unstable(rustc_contracts_internals)]
     #[rustc_builtin_macro]
     pub macro contracts_ensures($item:item) {
         /* compiler built-in */
@@ -1796,8 +1796,8 @@ pub(crate) mod builtin {
     /// eventually parsed as an boolean expression with access to the
     /// function's formal parameters
     #[cfg(not(bootstrap))]
-    #[unstable(feature = "rustc_contracts", issue = "none")]
-    #[allow_internal_unstable(core_intrinsics)]
+    #[unstable(feature = "rustc_contracts", issue = "133866")]
+    #[allow_internal_unstable(rustc_contracts_internals)]
     #[rustc_builtin_macro]
     pub macro contracts_requires($item:item) {
         /* compiler built-in */
diff --git a/tests/ui/contracts/contract-ast-extensions-nest.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs
index ed137c4a986..d95ccd4f7b9 100644
--- a/tests/ui/contracts/contract-ast-extensions-nest.rs
+++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs
@@ -16,7 +16,7 @@
 //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes
 //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes
 
-#![feature(rustc_contracts)]
+#![feature(rustc_contracts_internals)]
 
 fn nest(x: Baz) -> i32
     rustc_contract_requires(|| x.baz > 0)
diff --git a/tests/ui/contracts/contract-ast-extensions-tail.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs
index b501c3faaed..636a595e06a 100644
--- a/tests/ui/contracts/contract-ast-extensions-tail.rs
+++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs
@@ -16,7 +16,7 @@
 //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes
 //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes
 
-#![feature(rustc_contracts)]
+#![feature(rustc_contracts_internals)]
 
 fn tail(x: Baz) -> i32
     rustc_contract_requires(|| x.baz > 0)
diff --git a/tests/ui/contracts/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs
index 6e3565baf7a..2e1be01e7ca 100644
--- a/tests/ui/contracts/contract-intrinsics.rs
+++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs
@@ -2,7 +2,7 @@
 //@ revisions: yes no none
 //@ [yes] compile-flags: -Zcontract-checks=yes
 //@ [no] compile-flags: -Zcontract-checks=no
-#![feature(cfg_contract_checks, rustc_contracts, core_intrinsics)]
+#![feature(cfg_contract_checks, rustc_contracts_internals, core_intrinsics)]
 
 fn main() {
     #[cfg(none)] // default: disabled
diff --git a/tests/ui/contracts/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs
index 1dbf71722fd..5af467485b1 100644
--- a/tests/ui/contracts/contract-lang-items.rs
+++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs
@@ -16,7 +16,8 @@
 //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes
 //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes
 
-#![feature(rustc_contracts)]
+#![feature(rustc_contracts)] // to access core::contracts
+#![feature(rustc_contracts_internals)] // to access check_requires lang item
 
 fn foo(x: Baz) -> i32 {
     core::contracts::check_requires(|| x.baz > 0);
diff --git a/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs
index 069f26e6796..0b0151c6df7 100644
--- a/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs
+++ b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ compile-flags: -Zcontract-checks=yes
-#![feature(rustc_contracts)]
+#![feature(rustc_contracts_internals)]
 
 fn outer() -> i32
     rustc_contract_ensures(|ret| *ret > 0)
diff --git a/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs
index fbee8277a96..79c50a18f70 100644
--- a/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs
+++ b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ compile-flags: -Zcontract-checks=yes
-#![feature(rustc_contracts)]
+#![feature(rustc_contracts_internals)]
 
 struct Outer { outer: std::cell::Cell<i32> }
 
diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs
new file mode 100644
index 00000000000..7b5f1767942
--- /dev/null
+++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs
@@ -0,0 +1,23 @@
+// gate-test-rustc_contracts_internals
+
+fn main() {
+    // intrinsics are guarded by rustc_contracts_internals feature gate.
+    core::intrinsics::contract_checks();
+    //~^ ERROR use of unstable library feature `rustc_contracts_internals`
+    core::intrinsics::contract_check_requires(|| true);
+    //~^ ERROR use of unstable library feature `rustc_contracts_internals`
+    core::intrinsics::contract_check_ensures(&1, |_|true);
+    //~^ ERROR use of unstable library feature `rustc_contracts_internals`
+
+    // lang items are guarded by rustc_contracts_internals feature gate.
+    core::contracts::check_requires(|| true);
+    //~^ ERROR use of unstable library feature `rustc_contracts_internals`
+    core::contracts::build_check_ensures(|_: &()| true);
+    //~^ ERROR use of unstable library feature `rustc_contracts_internals`
+
+    // ast extensions are guarded by rustc_contracts_internals feature gate
+    fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 }
+    //~^ ERROR contract internal machinery is for internal use only
+    fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 }
+    //~^ ERROR contract internal machinery is for internal use only
+}
diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr
new file mode 100644
index 00000000000..2acd03b9a35
--- /dev/null
+++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr
@@ -0,0 +1,73 @@
+error[E0658]: contract internal machinery is for internal use only
+  --> $DIR/internal-feature-gating.rs:19:51
+   |
+LL |     fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 }
+   |                                                   ^^^^^^^^^
+   |
+   = note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
+   = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: contract internal machinery is for internal use only
+  --> $DIR/internal-feature-gating.rs:21:50
+   |
+LL |     fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 }
+   |                                                  ^^^^^^^^^^
+   |
+   = note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
+   = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `rustc_contracts_internals`
+  --> $DIR/internal-feature-gating.rs:5:5
+   |
+LL |     core::intrinsics::contract_checks();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
+   = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `rustc_contracts_internals`
+  --> $DIR/internal-feature-gating.rs:7:5
+   |
+LL |     core::intrinsics::contract_check_requires(|| true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
+   = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `rustc_contracts_internals`
+  --> $DIR/internal-feature-gating.rs:9:5
+   |
+LL |     core::intrinsics::contract_check_ensures(&1, |_|true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
+   = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `rustc_contracts_internals`
+  --> $DIR/internal-feature-gating.rs:13:5
+   |
+LL |     core::contracts::check_requires(|| true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
+   = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `rustc_contracts_internals`
+  --> $DIR/internal-feature-gating.rs:15:5
+   |
+LL |     core::contracts::build_check_ensures(|_: &()| true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
+   = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/feature-gate-rustc-contracts.rs b/tests/ui/feature-gates/feature-gate-rustc-contracts.rs
new file mode 100644
index 00000000000..d4249c252cd
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-rustc-contracts.rs
@@ -0,0 +1,11 @@
+#![crate_type = "lib"]
+
+#[core::contracts::requires(x > 0)]
+pub fn requires_needs_it(x: i32) { }
+//~^^  ERROR use of unstable library feature `rustc_contracts`
+//~^^^ ERROR contracts are experimental
+
+#[core::contracts::ensures(|ret| *ret > 0)]
+pub fn ensures_needs_it() -> i32 { 10 }
+//~^^  ERROR use of unstable library feature `rustc_contracts`
+//~^^^ ERROR contracts are experimental
diff --git a/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr b/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr
new file mode 100644
index 00000000000..eb7777a4a51
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr
@@ -0,0 +1,43 @@
+error[E0658]: use of unstable library feature `rustc_contracts`
+  --> $DIR/feature-gate-rustc-contracts.rs:3:3
+   |
+LL | #[core::contracts::requires(x > 0)]
+   |   ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
+   = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `rustc_contracts`
+  --> $DIR/feature-gate-rustc-contracts.rs:8:3
+   |
+LL | #[core::contracts::ensures(|ret| *ret > 0)]
+   |   ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
+   = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: contracts are experimental
+  --> $DIR/feature-gate-rustc-contracts.rs:3:1
+   |
+LL | #[core::contracts::requires(x > 0)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
+   = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: contracts are experimental
+  --> $DIR/feature-gate-rustc-contracts.rs:8:1
+   |
+LL | #[core::contracts::ensures(|ret| *ret > 0)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
+   = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0658`.