Skip to content

Commit 8e59bb9

Browse files
authored
Rollup merge of rust-lang#144438 - dawidl022:contracts/guarded-lowering, r=oli-obk
Guard HIR lowered contracts with `contract_checks` Refactor contract HIR lowering to ensure no contract code is executed when contract-checks are disabled. The call to `contract_checks` is moved to inside the lowered fn body, and contract closures are built conditionally, ensuring no side-effects present in contracts occur when those are disabled. This partially addresses rust-lang#139548, i.e. the bad behavior no longer happens with contract checks disabled (`-Zcontract-checks=no`). The change is made in preparation for adding contract variable declarations - variables declared before the `requires` assertion, and accessible from both `requires` and `ensures`, but not in the function body (PR rust-lang#144444). As those declarations may also have side-effects, it's good to guard them with `contract_checks` - the new lowering approach allows for this to be done easily. Contracts tracking issue: rust-lang#128044 **Known limiatations**: - It is still possible to early return from the *function* from within a contract, e.g. ```rust #[ensures({if x > 0 { return 0 }; |_| true})] fn foo(x: u32) -> i32 { 42 } ``` When `foo` is called with an argument greater than 0, instead of `42`, `0` will be returned. As this is not a regression, it is not addressed in this PR. However, it may be worth revisiting later down the line, as users may expect a form of early return from *contract specifications*, and so returning from the entire *function* could cause confusion. - ~Contracts are still not optimised out when disabled. Currently, even when contracts are disabled, the code generated causes existing optimisations to fail, meaning even disabled contracts could impact runtime performance. This issue is blocking rust-lang#136578, and has not been addressed in this PR, i.e. the `mir-opt` and `codegen` tests that fail in rust-lang#136578 still fail with these new HIR lowering changes.~ Contracts should now be optimised out when disabled, however some regressions tests still need to be added to be sure that is indeed the case.
2 parents d68e6ed + 0e99d9a commit 8e59bb9

File tree

1 file changed

+16
-23
lines changed

1 file changed

+16
-23
lines changed

core/src/intrinsics/mod.rs

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2631,23 +2631,6 @@ pub const unsafe fn const_make_global(ptr: *mut u8) -> *const u8 {
26312631
ptr
26322632
}
26332633

2634-
/// Returns whether we should perform contract-checking at runtime.
2635-
///
2636-
/// This is meant to be similar to the ub_checks intrinsic, in terms
2637-
/// of not prematurely committing at compile-time to whether contract
2638-
/// checking is turned on, so that we can specify contracts in libstd
2639-
/// and let an end user opt into turning them on.
2640-
#[rustc_const_unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
2641-
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
2642-
#[inline(always)]
2643-
#[rustc_intrinsic]
2644-
pub const fn contract_checks() -> bool {
2645-
// FIXME: should this be `false` or `cfg!(contract_checks)`?
2646-
2647-
// cfg!(contract_checks)
2648-
false
2649-
}
2650-
26512634
/// Check if the pre-condition `cond` has been met.
26522635
///
26532636
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
@@ -2668,7 +2651,7 @@ pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
26682651
if const {
26692652
// Do nothing
26702653
} else {
2671-
if contract_checks() && !cond() {
2654+
if !cond() {
26722655
// Emit no unwind panic in case this was a safety requirement.
26732656
crate::panicking::panic_nounwind("failed requires check");
26742657
}
@@ -2681,6 +2664,8 @@ pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
26812664
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
26822665
/// returns false.
26832666
///
2667+
/// If `cond` is `None`, then no postcondition checking is performed.
2668+
///
26842669
/// Note that this function is a no-op during constant evaluation.
26852670
#[unstable(feature = "contracts_internals", issue = "128044")]
26862671
// Similar to `contract_check_requires`, we need to use the user-facing
@@ -2689,16 +2674,24 @@ pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
26892674
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
26902675
#[lang = "contract_check_ensures"]
26912676
#[rustc_intrinsic]
2692-
pub const fn contract_check_ensures<C: Fn(&Ret) -> bool + Copy, Ret>(cond: C, ret: Ret) -> Ret {
2677+
pub const fn contract_check_ensures<C: Fn(&Ret) -> bool + Copy, Ret>(
2678+
cond: Option<C>,
2679+
ret: Ret,
2680+
) -> Ret {
26932681
const_eval_select!(
2694-
@capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: C, ret: Ret } -> Ret :
2682+
@capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: Option<C>, ret: Ret } -> Ret :
26952683
if const {
26962684
// Do nothing
26972685
ret
26982686
} else {
2699-
if contract_checks() && !cond(&ret) {
2700-
// Emit no unwind panic in case this was a safety requirement.
2701-
crate::panicking::panic_nounwind("failed ensures check");
2687+
match cond {
2688+
crate::option::Option::Some(cond) => {
2689+
if !cond(&ret) {
2690+
// Emit no unwind panic in case this was a safety requirement.
2691+
crate::panicking::panic_nounwind("failed ensures check");
2692+
}
2693+
},
2694+
crate::option::Option::None => {},
27022695
}
27032696
ret
27042697
}

0 commit comments

Comments
 (0)