Skip to content

Commit 2bb1464

Browse files
committed
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.
1 parent 804cce4 commit 2bb1464

File tree

7 files changed

+57
-72
lines changed

7 files changed

+57
-72
lines changed

Diff for: compiler/rustc_hir_analysis/src/check/intrinsic.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -223,17 +223,15 @@ pub fn check_intrinsic_type(
223223
};
224224
(n_tps, 0, 0, inputs, output, hir::Safety::Unsafe)
225225
} else if intrinsic_name == sym::contract_check_ensures {
226-
// contract_check_ensures::<'a, Ret, C>(&'a Ret, C) -> bool
226+
// contract_check_ensures::<'a, Ret, C>(&'a Ret, C)
227227
// where C: impl Fn(&'a Ret) -> bool,
228228
//
229-
// so: two type params, one lifetime param, 0 const params, two inputs, returns boolean
229+
// so: two type params, one lifetime param, 0 const params, two inputs, no return
230230

231231
let p = generics.param_at(0, tcx);
232232
let r = ty::Region::new_early_param(tcx, p.to_early_bound_region_data());
233233
let ref_ret = Ty::new_imm_ref(tcx, r, param(1));
234-
// let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon };
235-
// let ref_ret = Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0));
236-
(2, 1, 0, vec![ref_ret, param(2)], tcx.types.bool, hir::Safety::Safe)
234+
(2, 1, 0, vec![ref_ret, param(2)], tcx.types.unit, hir::Safety::Safe)
237235
} else {
238236
let safety = intrinsic_operation_unsafety(tcx, intrinsic_id);
239237
let (n_tps, n_cts, inputs, output) = match intrinsic_name {
@@ -628,7 +626,7 @@ pub fn check_intrinsic_type(
628626
// contract_checks() -> bool
629627
sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool),
630628
// contract_check_requires::<C>(C) -> bool, where C: impl Fn() -> bool
631-
sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.bool),
629+
sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.unit),
632630

633631
sym::simd_eq
634632
| sym::simd_ne

Diff for: library/core/src/contracts.rs

+5-22
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,21 @@
11
//! Unstable module containing the unstable contracts lang items and attribute macros.
2+
#![cfg(not(bootstrap))]
23

3-
#[cfg(not(bootstrap))]
4-
pub use crate::macros::builtin::contracts_ensures as ensures;
5-
#[cfg(not(bootstrap))]
6-
pub use crate::macros::builtin::contracts_requires as requires;
7-
8-
/// Emitted by rustc as a desugaring of `#[requires(PRED)] fn foo(x: X) { ... }`
9-
/// into: `fn foo(x: X) { check_requires(|| PRED) ... }`
10-
#[cfg(not(bootstrap))]
11-
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
12-
#[lang = "contract_check_requires"]
13-
#[track_caller]
14-
pub fn check_requires<C: FnOnce() -> bool>(c: C) {
15-
if core::intrinsics::contract_checks() {
16-
assert!(core::intrinsics::contract_check_requires(c), "failed requires check");
17-
}
18-
}
4+
pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires};
195

206
/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }`
217
/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }`
228
/// (including the implicit return of the tail expression, if any).
23-
#[cfg(not(bootstrap))]
249
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
2510
#[lang = "contract_build_check_ensures"]
2611
#[track_caller]
27-
pub fn build_check_ensures<Ret, C>(c: C) -> impl (FnOnce(Ret) -> Ret) + Copy
12+
pub fn build_check_ensures<Ret, C>(cond: C) -> impl (Fn(Ret) -> Ret) + Copy
2813
where
29-
C: for<'a> FnOnce(&'a Ret) -> bool + Copy + 'static,
14+
C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static,
3015
{
3116
#[track_caller]
3217
move |ret| {
33-
if core::intrinsics::contract_checks() {
34-
assert!(core::intrinsics::contract_check_ensures(&ret, c), "failed ensures check");
35-
}
18+
crate::intrinsics::contract_check_ensures(&ret, cond);
3619
ret
3720
}
3821
}

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

+18-4
Original file line numberDiff line numberDiff line change
@@ -4062,18 +4062,32 @@ pub const fn contract_checks() -> bool {
40624062
false
40634063
}
40644064

4065+
/// Check if the pre-condition `cond` has been met.
4066+
///
4067+
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
4068+
/// returns false.
40654069
#[cfg(not(bootstrap))]
40664070
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
4071+
#[lang = "contract_check_requires"]
40674072
#[rustc_intrinsic]
4068-
pub fn contract_check_requires<C: FnOnce() -> bool>(c: C) -> bool {
4069-
c()
4073+
pub fn contract_check_requires<C: Fn() -> bool>(cond: C) {
4074+
if contract_checks() && !cond() {
4075+
// Emit no unwind panic in case this was a safety requirement.
4076+
crate::panicking::panic_nounwind("failed requires check");
4077+
}
40704078
}
40714079

4080+
/// Check if the post-condition `cond` has been met.
4081+
///
4082+
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
4083+
/// returns false.
40724084
#[cfg(not(bootstrap))]
40734085
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
40744086
#[rustc_intrinsic]
4075-
pub fn contract_check_ensures<'a, Ret, C: FnOnce(&'a Ret) -> bool>(ret: &'a Ret, c: C) -> bool {
4076-
c(ret)
4087+
pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) {
4088+
if contract_checks() && !cond(ret) {
4089+
crate::panicking::panic_nounwind("failed ensures check");
4090+
}
40774091
}
40784092

40794093
/// The intrinsic will return the size stored in that vtable.
+25-12
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
1-
//@ run-pass
2-
//@ revisions: yes no none
3-
//@ [yes] compile-flags: -Zcontract-checks=yes
4-
//@ [no] compile-flags: -Zcontract-checks=no
1+
//@ revisions: default unchk_pass chk_pass chk_fail_ensures chk_fail_requires
2+
//
3+
//@ [default] run-pass
4+
//@ [unchk_pass] run-pass
5+
//@ [chk_pass] run-pass
6+
//@ [chk_fail_requires] run-fail
7+
//@ [chk_fail_ensures] run-fail
8+
//
9+
//@ [unchk_pass] compile-flags: -Zcontract-checks=no
10+
//@ [chk_pass] compile-flags: -Zcontract-checks=yes
11+
//@ [chk_fail_requires] compile-flags: -Zcontract-checks=yes
12+
//@ [chk_fail_ensures] compile-flags: -Zcontract-checks=yes
513
#![feature(cfg_contract_checks, rustc_contracts_internals, core_intrinsics)]
614

715
fn main() {
8-
#[cfg(none)] // default: disabled
16+
#[cfg(any(default, unchk_pass))] // default: disabled
917
assert_eq!(core::intrinsics::contract_checks(), false);
1018

11-
#[cfg(yes)] // explicitly enabled
19+
#[cfg(chk_pass)] // explicitly enabled
1220
assert_eq!(core::intrinsics::contract_checks(), true);
1321

14-
#[cfg(no)] // explicitly disabled
15-
assert_eq!(core::intrinsics::contract_checks(), false);
22+
// always pass
23+
core::intrinsics::contract_check_requires(|| true);
1624

17-
assert_eq!(core::intrinsics::contract_check_requires(|| true), true);
18-
assert_eq!(core::intrinsics::contract_check_requires(|| false), false);
25+
// fail if enabled
26+
#[cfg(any(default, unchk_pass, chk_fail_requires))]
27+
core::intrinsics::contract_check_requires(|| false);
1928

2029
let doubles_to_two = { let old = 2; move |ret| ret + ret == old };
21-
assert_eq!(core::intrinsics::contract_check_ensures(&1, doubles_to_two), true);
22-
assert_eq!(core::intrinsics::contract_check_ensures(&2, doubles_to_two), false);
30+
// Always pass
31+
core::intrinsics::contract_check_ensures(&1, doubles_to_two);
32+
33+
// Fail if enabled
34+
#[cfg(any(default, unchk_pass, chk_fail_ensures))]
35+
core::intrinsics::contract_check_ensures(&2, doubles_to_two);
2336
}
+1-11
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
1-
//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post
1+
//@ revisions: unchk_pass unchk_fail_post chk_pass chk_fail_post
22
//
33
//@ [unchk_pass] run-pass
4-
//@ [unchk_fail_pre] run-pass
54
//@ [unchk_fail_post] run-pass
65
//@ [chk_pass] run-pass
76
//
8-
//@ [chk_fail_pre] run-fail
97
//@ [chk_fail_post] run-fail
108
//
119
//@ [unchk_pass] compile-flags: -Zcontract-checks=no
12-
//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no
1310
//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no
1411
//
1512
//@ [chk_pass] compile-flags: -Zcontract-checks=yes
16-
//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes
1713
//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes
1814

1915
#![feature(rustc_contracts)] // to access core::contracts
2016
#![feature(rustc_contracts_internals)] // to access check_requires lang item
2117

2218
fn foo(x: Baz) -> i32 {
23-
core::contracts::check_requires(|| x.baz > 0);
24-
2519
let injected_checker = {
2620
core::contracts::build_check_ensures(|ret| *ret > 100)
2721
};
@@ -36,13 +30,9 @@ struct Baz { baz: i32 }
3630
const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 };
3731
#[cfg(any(unchk_fail_post, chk_fail_post))]
3832
const BAZ_FAIL_POST: Baz = Baz { baz: 10 };
39-
#[cfg(any(unchk_fail_pre, chk_fail_pre))]
40-
const BAZ_FAIL_PRE: Baz = Baz { baz: -10 };
4133

4234
fn main() {
4335
assert_eq!(foo(BAZ_PASS_PRE_POST), 150);
44-
#[cfg(any(unchk_fail_pre, chk_fail_pre))]
45-
foo(BAZ_FAIL_PRE);
4636
#[cfg(any(unchk_fail_post, chk_fail_post))]
4737
foo(BAZ_FAIL_POST);
4838
}

Diff for: tests/ui/contracts/internal_machinery/internal-feature-gating.rs

-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ fn main() {
99
core::intrinsics::contract_check_ensures(&1, |_|true);
1010
//~^ ERROR use of unstable library feature `rustc_contracts_internals`
1111

12-
// lang items are guarded by rustc_contracts_internals feature gate.
13-
core::contracts::check_requires(|| true);
14-
//~^ ERROR use of unstable library feature `rustc_contracts_internals`
1512
core::contracts::build_check_ensures(|_: &()| true);
1613
//~^ ERROR use of unstable library feature `rustc_contracts_internals`
1714

Diff for: tests/ui/contracts/internal_machinery/internal-feature-gating.stderr

+4-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0658]: contract internal machinery is for internal use only
2-
--> $DIR/internal-feature-gating.rs:19:51
2+
--> $DIR/internal-feature-gating.rs:16:51
33
|
44
LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 }
55
| ^^^^^^^^^
@@ -9,7 +9,7 @@ LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 }
99
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
1010

1111
error[E0658]: contract internal machinery is for internal use only
12-
--> $DIR/internal-feature-gating.rs:21:50
12+
--> $DIR/internal-feature-gating.rs:18:50
1313
|
1414
LL | fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 }
1515
| ^^^^^^^^^^
@@ -49,17 +49,7 @@ LL | core::intrinsics::contract_check_ensures(&1, |_|true);
4949
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
5050

5151
error[E0658]: use of unstable library feature `rustc_contracts_internals`
52-
--> $DIR/internal-feature-gating.rs:13:5
53-
|
54-
LL | core::contracts::check_requires(|| true);
55-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56-
|
57-
= note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
58-
= help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
59-
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
60-
61-
error[E0658]: use of unstable library feature `rustc_contracts_internals`
62-
--> $DIR/internal-feature-gating.rs:15:5
52+
--> $DIR/internal-feature-gating.rs:12:5
6353
|
6454
LL | core::contracts::build_check_ensures(|_: &()| true);
6555
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -68,6 +58,6 @@ LL | core::contracts::build_check_ensures(|_: &()| true);
6858
= help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
6959
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
7060

71-
error: aborting due to 7 previous errors
61+
error: aborting due to 6 previous errors
7262

7363
For more information about this error, try `rustc --explain E0658`.

0 commit comments

Comments
 (0)