Skip to content

Commit d96e3e7

Browse files
committed
Enable contracts for const functions
Use `const_eval_select!()` macro to enable contract checking only at runtime. The existing contract logic relies on closures, which are not supported in constant functions. This commit also removes one level of indirection for ensures clauses, however, it currently has a spurious warning message when the bottom of the function is unreachable.
1 parent 35672c7 commit d96e3e7

File tree

3 files changed

+47
-14
lines changed

3 files changed

+47
-14
lines changed

Diff for: core/src/contracts.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@ pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_require
55
/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }`
66
/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }`
77
/// (including the implicit return of the tail expression, if any).
8+
///
9+
/// This call helps with type inference for the predicate.
810
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
11+
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
912
#[lang = "contract_build_check_ensures"]
1013
#[track_caller]
11-
pub fn build_check_ensures<Ret, C>(cond: C) -> impl (Fn(Ret) -> Ret) + Copy
14+
pub const fn build_check_ensures<Ret, C>(cond: C) -> C
1215
where
13-
C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static,
16+
C: Fn(&Ret) -> bool + Copy + 'static,
1417
{
15-
#[track_caller]
16-
move |ret| {
17-
crate::intrinsics::contract_check_ensures(&ret, cond);
18-
ret
19-
}
18+
cond
2019
}

Diff for: core/src/intrinsics/mod.rs

+41-6
Original file line numberDiff line numberDiff line change
@@ -3450,20 +3450,55 @@ pub const fn contract_checks() -> bool {
34503450
///
34513451
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
34523452
/// returns false.
3453-
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
3453+
///
3454+
/// Note that this function is a no-op during constant evaluation.
3455+
#[unstable(feature = "contracts_internals", issue = "128044")]
3456+
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
34543457
#[lang = "contract_check_requires"]
34553458
#[rustc_intrinsic]
3456-
pub fn contract_check_requires<C: Fn() -> bool>(cond: C) {
3457-
if contract_checks() && !cond() {
3458-
// Emit no unwind panic in case this was a safety requirement.
3459-
crate::panicking::panic_nounwind("failed requires check");
3460-
}
3459+
pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
3460+
const_eval_select!(
3461+
@capture[C: Fn() -> bool + Copy] { cond: C } :
3462+
if const {
3463+
// Do nothing
3464+
} else {
3465+
if contract_checks() && !cond() {
3466+
// Emit no unwind panic in case this was a safety requirement.
3467+
crate::panicking::panic_nounwind("failed requires check");
3468+
}
3469+
}
3470+
)
34613471
}
34623472

34633473
/// Check if the post-condition `cond` has been met.
34643474
///
34653475
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
34663476
/// returns false.
3477+
///
3478+
/// Note that this function is a no-op during constant evaluation.
3479+
#[cfg(not(bootstrap))]
3480+
#[unstable(feature = "contracts_internals", issue = "128044")]
3481+
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
3482+
#[lang = "contract_check_ensures"]
3483+
#[rustc_intrinsic]
3484+
pub const fn contract_check_ensures<Ret, C: Fn(&Ret) -> bool + Copy>(ret: Ret, cond: C) -> Ret {
3485+
const_eval_select!(
3486+
@capture[Ret, C: Fn(&Ret) -> bool + Copy] { ret: Ret, cond: C } -> Ret :
3487+
if const {
3488+
// Do nothing
3489+
ret
3490+
} else {
3491+
if contract_checks() && !cond(&ret) {
3492+
// Emit no unwind panic in case this was a safety requirement.
3493+
crate::panicking::panic_nounwind("failed ensures check");
3494+
}
3495+
ret
3496+
}
3497+
)
3498+
}
3499+
3500+
/// This is the old version of contract_check_ensures kept here for bootstrap only.
3501+
#[cfg(bootstrap)]
34673502
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
34683503
#[rustc_intrinsic]
34693504
pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) {

Diff for: core/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@
101101
#![feature(bstr)]
102102
#![feature(bstr_internals)]
103103
#![feature(cfg_match)]
104-
#![feature(closure_track_caller)]
105104
#![feature(const_carrying_mul_add)]
106105
#![feature(const_eval_select)]
107106
#![feature(core_intrinsics)]

0 commit comments

Comments
 (0)