Skip to content

Commit caa9632

Browse files
authored
Rollup merge of rust-lang#138374 - celinval:issue-136925-const-contract, r=compiler-errors,oli-obk,RalfJung
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 since we no longer build a closure around the ensures predicate. Resolves rust-lang#136925 **Call-out:** This is still a draft PR since CI is broken due to a new warning message for unreachable code when the bottom of the function is indeed unreachable. It's not clear to me why the warning wasn't triggered before. r? ```@compiler-errors```
2 parents 5db3c5f + 0d4f906 commit caa9632

File tree

3 files changed

+63
-18
lines changed

3 files changed

+63
-18
lines changed

core/src/contracts.rs

+15-11
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@
22
33
pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires};
44

5-
/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }`
6-
/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }`
7-
/// (including the implicit return of the tail expression, if any).
5+
/// This is an identity function used as part of the desugaring of the `#[ensures]` attribute.
6+
///
7+
/// This is an existing hack to allow users to omit the type of the return value in their ensures
8+
/// attribute.
9+
///
10+
/// Ideally, rustc should be able to generate the type annotation.
11+
/// The existing lowering logic makes it rather hard to add the explicit type annotation,
12+
/// while the function call is fairly straight forward.
813
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
14+
// Similar to `contract_check_requires`, we need to use the user-facing
15+
// `contracts` feature rather than the perma-unstable `contracts_internals`.
16+
// Const-checking doesn't honor allow_internal_unstable logic used by contract expansion.
17+
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
918
#[lang = "contract_build_check_ensures"]
10-
#[track_caller]
11-
pub fn build_check_ensures<Ret, C>(cond: C) -> impl (Fn(Ret) -> Ret) + Copy
19+
pub const fn build_check_ensures<Ret, C>(cond: C) -> C
1220
where
13-
C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static,
21+
C: Fn(&Ret) -> bool + Copy + 'static,
1422
{
15-
#[track_caller]
16-
move |ret| {
17-
crate::intrinsics::contract_check_ensures(&ret, cond);
18-
ret
19-
}
23+
cond
2024
}

core/src/intrinsics/mod.rs

+48-6
Original file line numberDiff line numberDiff line change
@@ -3402,20 +3402,62 @@ pub const fn contract_checks() -> bool {
34023402
///
34033403
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
34043404
/// returns false.
3405-
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
3405+
///
3406+
/// Note that this function is a no-op during constant evaluation.
3407+
#[unstable(feature = "contracts_internals", issue = "128044")]
3408+
// Calls to this function get inserted by an AST expansion pass, which uses the equivalent of
3409+
// `#[allow_internal_unstable]` to allow using `contracts_internals` functions. Const-checking
3410+
// doesn't honor `#[allow_internal_unstable]`, so for the const feature gate we use the user-facing
3411+
// `contracts` feature rather than the perma-unstable `contracts_internals`
3412+
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
34063413
#[lang = "contract_check_requires"]
34073414
#[rustc_intrinsic]
3408-
pub fn contract_check_requires<C: Fn() -> bool>(cond: C) {
3409-
if contract_checks() && !cond() {
3410-
// Emit no unwind panic in case this was a safety requirement.
3411-
crate::panicking::panic_nounwind("failed requires check");
3412-
}
3415+
pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
3416+
const_eval_select!(
3417+
@capture[C: Fn() -> bool + Copy] { cond: C } :
3418+
if const {
3419+
// Do nothing
3420+
} else {
3421+
if contract_checks() && !cond() {
3422+
// Emit no unwind panic in case this was a safety requirement.
3423+
crate::panicking::panic_nounwind("failed requires check");
3424+
}
3425+
}
3426+
)
34133427
}
34143428

34153429
/// Check if the post-condition `cond` has been met.
34163430
///
34173431
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
34183432
/// returns false.
3433+
///
3434+
/// Note that this function is a no-op during constant evaluation.
3435+
#[cfg(not(bootstrap))]
3436+
#[unstable(feature = "contracts_internals", issue = "128044")]
3437+
// Similar to `contract_check_requires`, we need to use the user-facing
3438+
// `contracts` feature rather than the perma-unstable `contracts_internals`.
3439+
// Const-checking doesn't honor allow_internal_unstable logic used by contract expansion.
3440+
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
3441+
#[lang = "contract_check_ensures"]
3442+
#[rustc_intrinsic]
3443+
pub const fn contract_check_ensures<C: Fn(&Ret) -> bool + Copy, Ret>(cond: C, ret: Ret) -> Ret {
3444+
const_eval_select!(
3445+
@capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: C, ret: Ret } -> Ret :
3446+
if const {
3447+
// Do nothing
3448+
ret
3449+
} else {
3450+
if contract_checks() && !cond(&ret) {
3451+
// Emit no unwind panic in case this was a safety requirement.
3452+
crate::panicking::panic_nounwind("failed ensures check");
3453+
}
3454+
ret
3455+
}
3456+
)
3457+
}
3458+
3459+
/// This is the old version of contract_check_ensures kept here for bootstrap only.
3460+
#[cfg(bootstrap)]
34193461
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
34203462
#[rustc_intrinsic]
34213463
pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) {

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)