Skip to content
/ rust Public
forked from rust-lang/rust

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

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

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
}

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.
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
}
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
}

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

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)