about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir/src/lang_items.rs4
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/core/src/contracts.rs33
-rw-r--r--library/core/src/lib.rs5
-rw-r--r--tests/ui/contracts/contract-lang-items.rs47
5 files changed, 90 insertions, 0 deletions
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index d9759580e8f..75898cbec14 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -418,6 +418,10 @@ language_item_table! {
 
     String,                  sym::String,              string,                     Target::Struct,         GenericRequirement::None;
     CStr,                    sym::CStr,                c_str,                      Target::Struct,         GenericRequirement::None;
+
+    // Experimental lang items for implementing contract pre- and post-condition checking.
+    ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None;
+    ContractCheckRequires,     sym::contract_check_requires,      contract_check_requires_fn,      Target::Fn, GenericRequirement::None;
 }
 
 pub enum GenericRequirement {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 3138efdfd9b..df424b3312a 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -676,6 +676,7 @@ symbols! {
         const_ty_placeholder: "<const_ty>",
         constant,
         constructor,
+        contract_build_check_ensures,
         contract_check_ensures,
         contract_check_requires,
         contract_checks,
diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs
new file mode 100644
index 00000000000..c13565d59d5
--- /dev/null
+++ b/library/core/src/contracts.rs
@@ -0,0 +1,33 @@
+//! Unstable module containing the unstable contracts lang items and attribute macros.
+
+/// 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 */)]
+#[lang = "contract_check_requires"]
+#[track_caller]
+pub fn check_requires<C: FnOnce() -> bool>(c: C) {
+    if core::intrinsics::contract_checks() {
+        assert!(core::intrinsics::contract_check_requires(c), "failed requires check");
+    }
+}
+
+/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }`
+/// 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 */)]
+#[lang = "contract_build_check_ensures"]
+#[track_caller]
+pub fn build_check_ensures<Ret, C>(c: C) -> impl (FnOnce(Ret) -> Ret) + Copy
+where
+    C: for<'a> FnOnce(&'a Ret) -> bool + Copy + 'static,
+{
+    #[track_caller]
+    move |ret| {
+        if core::intrinsics::contract_checks() {
+            assert!(core::intrinsics::contract_check_ensures(&ret, c), "failed ensures check");
+        }
+        ret
+    }
+}
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index c18e0405f72..fd283fe92fa 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -114,6 +114,7 @@
 #![feature(bstr)]
 #![feature(bstr_internals)]
 #![feature(const_carrying_mul_add)]
+#![feature(closure_track_caller)]
 #![feature(const_eval_select)]
 #![feature(core_intrinsics)]
 #![feature(coverage_attribute)]
@@ -246,6 +247,10 @@ pub mod autodiff {
     pub use crate::macros::builtin::autodiff;
 }
 
+#[cfg(not(bootstrap))]
+#[unstable(feature = "rustc_contracts", issue = "none")]
+pub mod contracts;
+
 #[unstable(feature = "cfg_match", issue = "115585")]
 pub use crate::macros::cfg_match;
 
diff --git a/tests/ui/contracts/contract-lang-items.rs b/tests/ui/contracts/contract-lang-items.rs
new file mode 100644
index 00000000000..1dbf71722fd
--- /dev/null
+++ b/tests/ui/contracts/contract-lang-items.rs
@@ -0,0 +1,47 @@
+//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post
+//
+//@ [unchk_pass] run-pass
+//@ [unchk_fail_pre] run-pass
+//@ [unchk_fail_post] run-pass
+//@ [chk_pass] run-pass
+//
+//@ [chk_fail_pre] run-fail
+//@ [chk_fail_post] run-fail
+//
+//@ [unchk_pass] compile-flags: -Zcontract-checks=no
+//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no
+//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no
+//
+//@ [chk_pass] compile-flags: -Zcontract-checks=yes
+//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes
+//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes
+
+#![feature(rustc_contracts)]
+
+fn foo(x: Baz) -> i32 {
+    core::contracts::check_requires(|| x.baz > 0);
+
+    let injected_checker = {
+        core::contracts::build_check_ensures(|ret| *ret > 100)
+    };
+
+    let ret = x.baz + 50;
+    injected_checker(ret)
+}
+
+struct Baz { baz: i32 }
+
+
+const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 };
+#[cfg(any(unchk_fail_post, chk_fail_post))]
+const BAZ_FAIL_POST: Baz = Baz { baz: 10 };
+#[cfg(any(unchk_fail_pre, chk_fail_pre))]
+const BAZ_FAIL_PRE: Baz = Baz { baz: -10 };
+
+fn main() {
+    assert_eq!(foo(BAZ_PASS_PRE_POST), 150);
+    #[cfg(any(unchk_fail_pre, chk_fail_pre))]
+    foo(BAZ_FAIL_PRE);
+    #[cfg(any(unchk_fail_post, chk_fail_post))]
+    foo(BAZ_FAIL_POST);
+}