diff options
| -rw-r--r-- | compiler/rustc_hir/src/lang_items.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_span/src/symbol.rs | 1 | ||||
| -rw-r--r-- | library/core/src/contracts.rs | 33 | ||||
| -rw-r--r-- | library/core/src/lib.rs | 5 | ||||
| -rw-r--r-- | tests/ui/contracts/contract-lang-items.rs | 47 |
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); +} |
