Improve contracts intrisics and remove wrapper function
1. Document the new intrinsics. 2. Make the intrinsics actually check the contract if enabled, and remove `contract::check_requires` function. 3. Use panic with no unwind in case contract is using to check for safety, we probably don't want to unwind. Following the same reasoning as UB checks.
This commit is contained in:
parent
804cce47d9
commit
2bb1464cb6
@ -223,17 +223,15 @@ pub fn check_intrinsic_type(
|
|||||||
};
|
};
|
||||||
(n_tps, 0, 0, inputs, output, hir::Safety::Unsafe)
|
(n_tps, 0, 0, inputs, output, hir::Safety::Unsafe)
|
||||||
} else if intrinsic_name == sym::contract_check_ensures {
|
} else if intrinsic_name == sym::contract_check_ensures {
|
||||||
// contract_check_ensures::<'a, Ret, C>(&'a Ret, C) -> bool
|
// contract_check_ensures::<'a, Ret, C>(&'a Ret, C)
|
||||||
// where C: impl Fn(&'a Ret) -> bool,
|
// where C: impl Fn(&'a Ret) -> bool,
|
||||||
//
|
//
|
||||||
// so: two type params, one lifetime param, 0 const params, two inputs, returns boolean
|
// so: two type params, one lifetime param, 0 const params, two inputs, no return
|
||||||
|
|
||||||
let p = generics.param_at(0, tcx);
|
let p = generics.param_at(0, tcx);
|
||||||
let r = ty::Region::new_early_param(tcx, p.to_early_bound_region_data());
|
let r = ty::Region::new_early_param(tcx, p.to_early_bound_region_data());
|
||||||
let ref_ret = Ty::new_imm_ref(tcx, r, param(1));
|
let ref_ret = Ty::new_imm_ref(tcx, r, param(1));
|
||||||
// let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon };
|
(2, 1, 0, vec![ref_ret, param(2)], tcx.types.unit, hir::Safety::Safe)
|
||||||
// let ref_ret = Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0));
|
|
||||||
(2, 1, 0, vec![ref_ret, param(2)], tcx.types.bool, hir::Safety::Safe)
|
|
||||||
} else {
|
} else {
|
||||||
let safety = intrinsic_operation_unsafety(tcx, intrinsic_id);
|
let safety = intrinsic_operation_unsafety(tcx, intrinsic_id);
|
||||||
let (n_tps, n_cts, inputs, output) = match intrinsic_name {
|
let (n_tps, n_cts, inputs, output) = match intrinsic_name {
|
||||||
@ -628,7 +626,7 @@ pub fn check_intrinsic_type(
|
|||||||
// contract_checks() -> bool
|
// contract_checks() -> bool
|
||||||
sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool),
|
sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool),
|
||||||
// contract_check_requires::<C>(C) -> bool, where C: impl Fn() -> bool
|
// contract_check_requires::<C>(C) -> bool, where C: impl Fn() -> bool
|
||||||
sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.bool),
|
sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.unit),
|
||||||
|
|
||||||
sym::simd_eq
|
sym::simd_eq
|
||||||
| sym::simd_ne
|
| sym::simd_ne
|
||||||
|
@ -1,38 +1,21 @@
|
|||||||
//! Unstable module containing the unstable contracts lang items and attribute macros.
|
//! Unstable module containing the unstable contracts lang items and attribute macros.
|
||||||
|
#![cfg(not(bootstrap))]
|
||||||
|
|
||||||
#[cfg(not(bootstrap))]
|
pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires};
|
||||||
pub use crate::macros::builtin::contracts_ensures as ensures;
|
|
||||||
#[cfg(not(bootstrap))]
|
|
||||||
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_internals", issue = "133866" /* 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;] ... }`
|
/// 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);] ... }`
|
/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }`
|
||||||
/// (including the implicit return of the tail expression, if any).
|
/// (including the implicit return of the tail expression, if any).
|
||||||
#[cfg(not(bootstrap))]
|
|
||||||
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
|
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
|
||||||
#[lang = "contract_build_check_ensures"]
|
#[lang = "contract_build_check_ensures"]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn build_check_ensures<Ret, C>(c: C) -> impl (FnOnce(Ret) -> Ret) + Copy
|
pub fn build_check_ensures<Ret, C>(cond: C) -> impl (Fn(Ret) -> Ret) + Copy
|
||||||
where
|
where
|
||||||
C: for<'a> FnOnce(&'a Ret) -> bool + Copy + 'static,
|
C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static,
|
||||||
{
|
{
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
move |ret| {
|
move |ret| {
|
||||||
if core::intrinsics::contract_checks() {
|
crate::intrinsics::contract_check_ensures(&ret, cond);
|
||||||
assert!(core::intrinsics::contract_check_ensures(&ret, c), "failed ensures check");
|
|
||||||
}
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4062,18 +4062,32 @@ pub const fn contract_checks() -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the pre-condition `cond` has been met.
|
||||||
|
///
|
||||||
|
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
|
||||||
|
/// returns false.
|
||||||
#[cfg(not(bootstrap))]
|
#[cfg(not(bootstrap))]
|
||||||
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
|
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
|
||||||
|
#[lang = "contract_check_requires"]
|
||||||
#[rustc_intrinsic]
|
#[rustc_intrinsic]
|
||||||
pub fn contract_check_requires<C: FnOnce() -> bool>(c: C) -> bool {
|
pub fn contract_check_requires<C: Fn() -> bool>(cond: C) {
|
||||||
c()
|
if contract_checks() && !cond() {
|
||||||
|
// Emit no unwind panic in case this was a safety requirement.
|
||||||
|
crate::panicking::panic_nounwind("failed requires check");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the post-condition `cond` has been met.
|
||||||
|
///
|
||||||
|
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
|
||||||
|
/// returns false.
|
||||||
#[cfg(not(bootstrap))]
|
#[cfg(not(bootstrap))]
|
||||||
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
|
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
|
||||||
#[rustc_intrinsic]
|
#[rustc_intrinsic]
|
||||||
pub fn contract_check_ensures<'a, Ret, C: FnOnce(&'a Ret) -> bool>(ret: &'a Ret, c: C) -> bool {
|
pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) {
|
||||||
c(ret)
|
if contract_checks() && !cond(ret) {
|
||||||
|
crate::panicking::panic_nounwind("failed ensures check");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The intrinsic will return the size stored in that vtable.
|
/// The intrinsic will return the size stored in that vtable.
|
||||||
|
@ -1,23 +1,36 @@
|
|||||||
//@ run-pass
|
//@ revisions: default unchk_pass chk_pass chk_fail_ensures chk_fail_requires
|
||||||
//@ revisions: yes no none
|
//
|
||||||
//@ [yes] compile-flags: -Zcontract-checks=yes
|
//@ [default] run-pass
|
||||||
//@ [no] compile-flags: -Zcontract-checks=no
|
//@ [unchk_pass] run-pass
|
||||||
|
//@ [chk_pass] run-pass
|
||||||
|
//@ [chk_fail_requires] run-fail
|
||||||
|
//@ [chk_fail_ensures] run-fail
|
||||||
|
//
|
||||||
|
//@ [unchk_pass] compile-flags: -Zcontract-checks=no
|
||||||
|
//@ [chk_pass] compile-flags: -Zcontract-checks=yes
|
||||||
|
//@ [chk_fail_requires] compile-flags: -Zcontract-checks=yes
|
||||||
|
//@ [chk_fail_ensures] compile-flags: -Zcontract-checks=yes
|
||||||
#![feature(cfg_contract_checks, rustc_contracts_internals, core_intrinsics)]
|
#![feature(cfg_contract_checks, rustc_contracts_internals, core_intrinsics)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
#[cfg(none)] // default: disabled
|
#[cfg(any(default, unchk_pass))] // default: disabled
|
||||||
assert_eq!(core::intrinsics::contract_checks(), false);
|
assert_eq!(core::intrinsics::contract_checks(), false);
|
||||||
|
|
||||||
#[cfg(yes)] // explicitly enabled
|
#[cfg(chk_pass)] // explicitly enabled
|
||||||
assert_eq!(core::intrinsics::contract_checks(), true);
|
assert_eq!(core::intrinsics::contract_checks(), true);
|
||||||
|
|
||||||
#[cfg(no)] // explicitly disabled
|
// always pass
|
||||||
assert_eq!(core::intrinsics::contract_checks(), false);
|
core::intrinsics::contract_check_requires(|| true);
|
||||||
|
|
||||||
assert_eq!(core::intrinsics::contract_check_requires(|| true), true);
|
// fail if enabled
|
||||||
assert_eq!(core::intrinsics::contract_check_requires(|| false), false);
|
#[cfg(any(default, unchk_pass, chk_fail_requires))]
|
||||||
|
core::intrinsics::contract_check_requires(|| false);
|
||||||
|
|
||||||
let doubles_to_two = { let old = 2; move |ret| ret + ret == old };
|
let doubles_to_two = { let old = 2; move |ret| ret + ret == old };
|
||||||
assert_eq!(core::intrinsics::contract_check_ensures(&1, doubles_to_two), true);
|
// Always pass
|
||||||
assert_eq!(core::intrinsics::contract_check_ensures(&2, doubles_to_two), false);
|
core::intrinsics::contract_check_ensures(&1, doubles_to_two);
|
||||||
|
|
||||||
|
// Fail if enabled
|
||||||
|
#[cfg(any(default, unchk_pass, chk_fail_ensures))]
|
||||||
|
core::intrinsics::contract_check_ensures(&2, doubles_to_two);
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,21 @@
|
|||||||
//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post
|
//@ revisions: unchk_pass unchk_fail_post chk_pass chk_fail_post
|
||||||
//
|
//
|
||||||
//@ [unchk_pass] run-pass
|
//@ [unchk_pass] run-pass
|
||||||
//@ [unchk_fail_pre] run-pass
|
|
||||||
//@ [unchk_fail_post] run-pass
|
//@ [unchk_fail_post] run-pass
|
||||||
//@ [chk_pass] run-pass
|
//@ [chk_pass] run-pass
|
||||||
//
|
//
|
||||||
//@ [chk_fail_pre] run-fail
|
|
||||||
//@ [chk_fail_post] run-fail
|
//@ [chk_fail_post] run-fail
|
||||||
//
|
//
|
||||||
//@ [unchk_pass] compile-flags: -Zcontract-checks=no
|
//@ [unchk_pass] compile-flags: -Zcontract-checks=no
|
||||||
//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no
|
|
||||||
//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no
|
//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no
|
||||||
//
|
//
|
||||||
//@ [chk_pass] compile-flags: -Zcontract-checks=yes
|
//@ [chk_pass] compile-flags: -Zcontract-checks=yes
|
||||||
//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes
|
|
||||||
//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes
|
//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes
|
||||||
|
|
||||||
#![feature(rustc_contracts)] // to access core::contracts
|
#![feature(rustc_contracts)] // to access core::contracts
|
||||||
#![feature(rustc_contracts_internals)] // to access check_requires lang item
|
#![feature(rustc_contracts_internals)] // to access check_requires lang item
|
||||||
|
|
||||||
fn foo(x: Baz) -> i32 {
|
fn foo(x: Baz) -> i32 {
|
||||||
core::contracts::check_requires(|| x.baz > 0);
|
|
||||||
|
|
||||||
let injected_checker = {
|
let injected_checker = {
|
||||||
core::contracts::build_check_ensures(|ret| *ret > 100)
|
core::contracts::build_check_ensures(|ret| *ret > 100)
|
||||||
};
|
};
|
||||||
@ -36,13 +30,9 @@ struct Baz { baz: i32 }
|
|||||||
const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 };
|
const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 };
|
||||||
#[cfg(any(unchk_fail_post, chk_fail_post))]
|
#[cfg(any(unchk_fail_post, chk_fail_post))]
|
||||||
const BAZ_FAIL_POST: Baz = Baz { baz: 10 };
|
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() {
|
fn main() {
|
||||||
assert_eq!(foo(BAZ_PASS_PRE_POST), 150);
|
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))]
|
#[cfg(any(unchk_fail_post, chk_fail_post))]
|
||||||
foo(BAZ_FAIL_POST);
|
foo(BAZ_FAIL_POST);
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,6 @@ fn main() {
|
|||||||
core::intrinsics::contract_check_ensures(&1, |_|true);
|
core::intrinsics::contract_check_ensures(&1, |_|true);
|
||||||
//~^ ERROR use of unstable library feature `rustc_contracts_internals`
|
//~^ 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);
|
core::contracts::build_check_ensures(|_: &()| true);
|
||||||
//~^ ERROR use of unstable library feature `rustc_contracts_internals`
|
//~^ ERROR use of unstable library feature `rustc_contracts_internals`
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error[E0658]: contract internal machinery is for internal use only
|
error[E0658]: contract internal machinery is for internal use only
|
||||||
--> $DIR/internal-feature-gating.rs:19:51
|
--> $DIR/internal-feature-gating.rs:16:51
|
||||||
|
|
|
|
||||||
LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 }
|
LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 }
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^
|
||||||
@ -9,7 +9,7 @@ LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 }
|
|||||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
= 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
|
error[E0658]: contract internal machinery is for internal use only
|
||||||
--> $DIR/internal-feature-gating.rs:21:50
|
--> $DIR/internal-feature-gating.rs:18:50
|
||||||
|
|
|
|
||||||
LL | fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 }
|
LL | fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 }
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
@ -49,17 +49,7 @@ LL | core::intrinsics::contract_check_ensures(&1, |_|true);
|
|||||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
= 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`
|
error[E0658]: use of unstable library feature `rustc_contracts_internals`
|
||||||
--> $DIR/internal-feature-gating.rs:13:5
|
--> $DIR/internal-feature-gating.rs:12: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);
|
LL | core::contracts::build_check_ensures(|_: &()| true);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -68,6 +58,6 @@ LL | core::contracts::build_check_ensures(|_: &()| true);
|
|||||||
= help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
|
= 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
|
= 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
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0658`.
|
For more information about this error, try `rustc --explain E0658`.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user