Skip to content

Conditionally compile contracts instead of deciding at run-time #145229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions compiler/rustc_builtin_macros/src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ impl AttrProcMacro for ExpandRequires {
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
expand_requires_tts(ecx, span, annotation, annotated)
if ecx.sess.contract_checks() {
expand_requires_tts(ecx, span, annotation, annotated)
} else {
Ok(annotated)
}
}
}

Expand All @@ -29,7 +33,11 @@ impl AttrProcMacro for ExpandEnsures {
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
expand_ensures_tts(ecx, span, annotation, annotated)
if ecx.sess.contract_checks() {
expand_ensures_tts(ecx, span, annotation, annotated)
} else {
Ok(annotated)
}
}
}

Expand Down
21 changes: 2 additions & 19 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2557,23 +2557,6 @@ pub const unsafe fn const_make_global(ptr: *mut u8) -> *const u8 {
ptr
}

/// Returns whether we should perform contract-checking at runtime.
///
/// This is meant to be similar to the ub_checks intrinsic, in terms
/// of not prematurely committing at compile-time to whether contract
/// checking is turned on, so that we can specify contracts in libstd
/// and let an end user opt into turning them on.
#[rustc_const_unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
#[inline(always)]
#[rustc_intrinsic]
pub const fn contract_checks() -> bool {
// FIXME: should this be `false` or `cfg!(contract_checks)`?

// cfg!(contract_checks)
false
}

/// Check if the pre-condition `cond` has been met.
///
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
Expand All @@ -2594,7 +2577,7 @@ pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
if const {
// Do nothing
} else {
if contract_checks() && !cond() {
if !cond() {
// Emit no unwind panic in case this was a safety requirement.
crate::panicking::panic_nounwind("failed requires check");
}
Expand Down Expand Up @@ -2622,7 +2605,7 @@ pub const fn contract_check_ensures<C: Fn(&Ret) -> bool + Copy, Ret>(cond: C, re
// Do nothing
ret
} else {
if contract_checks() && !cond(&ret) {
if !cond(&ret) {
// Emit no unwind panic in case this was a safety requirement.
crate::panicking::panic_nounwind("failed ensures check");
}
Expand Down
19 changes: 19 additions & 0 deletions tests/ui/contracts/associated-item-disabled.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Ensure we don't ICE when contract present on an associated item but
// contract-checks are disabled.

//@ compile-flags: --crate-type=lib
//@ check-pass

#![feature(contracts)]
//~^ WARN the feature `contracts` is incomplete and may not be safe to use

extern crate core;

use core::contracts::requires;

struct Foo;

impl Foo {
#[requires(align > 0 && (align & (align - 1)) == 0)]
pub fn foo(align: i32) {}
}
11 changes: 11 additions & 0 deletions tests/ui/contracts/associated-item-disabled.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/associated-item-disabled.rs:7:12
|
LL | #![feature(contracts)]
| ^^^^^^^^^
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted

1 change: 1 addition & 0 deletions tests/ui/contracts/associated-item.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Ensure we don't ICE when lowering contracts on an associated item.

//@ compile-flags: --crate-type=lib
//@ compile-flags: -Zcontract-checks=yes
//@ check-pass

#![feature(contracts)]
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/contracts/associated-item.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/associated-item.rs:6:12
--> $DIR/associated-item.rs:7:12
|
LL | #![feature(contracts)]
| ^^^^^^^^^
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/contracts/contract-annotation-limitations.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Test for some of the existing limitations and the current error messages.
//! Some of these limitations may be removed in the future.

//@ compile-flags: -Zcontract-checks=yes

#![feature(contracts)]
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]
#![allow(dead_code)]
Expand Down
6 changes: 3 additions & 3 deletions tests/ui/contracts/contract-annotation-limitations.stderr
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
error: contract annotations is only supported in functions with bodies
--> $DIR/contract-annotation-limitations.rs:18:5
--> $DIR/contract-annotation-limitations.rs:20:5
|
LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: contract annotations is only supported in functions with bodies
--> $DIR/contract-annotation-limitations.rs:22:5
--> $DIR/contract-annotation-limitations.rs:24:5
|
LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/contract-annotation-limitations.rs:4:12
--> $DIR/contract-annotation-limitations.rs:6:12
|
LL | #![feature(contracts)]
| ^^^^^^^^^
Expand Down
17 changes: 17 additions & 0 deletions tests/ui/contracts/contracts-disabled-side-effect-ensures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//@ run-pass
#![feature(contracts)]
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]

extern crate core;
use core::contracts::ensures;

#[ensures({*x = 0; |_ret| true})]
fn buggy_add(x: &mut u32, y: u32) {
*x = *x + y;
}

fn main() {
let mut x = 10;
buggy_add(&mut x, 100);
assert_eq!(x, 110);
}
11 changes: 11 additions & 0 deletions tests/ui/contracts/contracts-disabled-side-effect-ensures.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/contracts-disabled-side-effect-ensures.rs:2:12
|
LL | #![feature(contracts)]
| ^^^^^^^^^
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Checks for compilation errors related to adding contracts to non-function items.
//@ compile-flags: -Zcontract-checks=yes

#![feature(contracts)]
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]
Expand Down
16 changes: 8 additions & 8 deletions tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
error: contract annotations can only be used on functions
--> $DIR/disallow-contract-annotation-on-non-fn.rs:7:1
--> $DIR/disallow-contract-annotation-on-non-fn.rs:8:1
|
LL | #[core::contracts::requires(true)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: contract annotations can only be used on functions
--> $DIR/disallow-contract-annotation-on-non-fn.rs:11:1
--> $DIR/disallow-contract-annotation-on-non-fn.rs:12:1
|
LL | #[core::contracts::ensures(|v| v == 100)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: contract annotations is only supported in functions with bodies
--> $DIR/disallow-contract-annotation-on-non-fn.rs:16:1
--> $DIR/disallow-contract-annotation-on-non-fn.rs:17:1
|
LL | #[core::contracts::ensures(|v| v == 100)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: contract annotations is only supported in functions with bodies
--> $DIR/disallow-contract-annotation-on-non-fn.rs:20:1
--> $DIR/disallow-contract-annotation-on-non-fn.rs:21:1
|
LL | #[core::contracts::ensures(|v| v == 100)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: contract annotations can only be used on functions
--> $DIR/disallow-contract-annotation-on-non-fn.rs:24:1
--> $DIR/disallow-contract-annotation-on-non-fn.rs:25:1
|
LL | #[core::contracts::requires(true)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: contract annotations can only be used on functions
--> $DIR/disallow-contract-annotation-on-non-fn.rs:35:1
--> $DIR/disallow-contract-annotation-on-non-fn.rs:36:1
|
LL | #[core::contracts::ensures(|dummy| dummy.0 > 0)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: contract annotations can only be used on functions
--> $DIR/disallow-contract-annotation-on-non-fn.rs:46:1
--> $DIR/disallow-contract-annotation-on-non-fn.rs:47:1
|
LL | #[core::contracts::requires(true)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/disallow-contract-annotation-on-non-fn.rs:3:12
--> $DIR/disallow-contract-annotation-on-non-fn.rs:4:12
|
LL | #![feature(contracts)]
| ^^^^^^^^^
Expand Down
16 changes: 16 additions & 0 deletions tests/ui/contracts/empty-ensures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//@ compile-flags: -Zcontract-checks=yes
#![feature(contracts)]
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]

extern crate core;
use core::contracts::ensures;

#[ensures()]
//~^ ERROR expected a `Fn(&_)` closure, found `()` [E0277]
fn foo(x: u32) -> u32 {
x * 2
}

fn main() {
foo(1);
}
25 changes: 25 additions & 0 deletions tests/ui/contracts/empty-ensures.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/empty-ensures.rs:2:12
|
LL | #![feature(contracts)]
| ^^^^^^^^^
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default

error[E0277]: expected a `Fn(&_)` closure, found `()`
--> $DIR/empty-ensures.rs:8:1
|
LL | #[ensures()]
| ^^^^^^^^^^^^
| |
| expected an `Fn(&_)` closure, found `()`
| required by a bound introduced by this call
|
= help: the trait `for<'a> Fn(&'a _)` is not implemented for `()`
note: required by a bound in `build_check_ensures`
--> $SRC_DIR/core/src/contracts.rs:LL:COL

error: aborting due to 1 previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0277`.
18 changes: 18 additions & 0 deletions tests/ui/contracts/empty-requires.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//@ dont-require-annotations: NOTE
//@ compile-flags: -Zcontract-checks=yes
#![feature(contracts)]
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]

extern crate core;
use core::contracts::requires;

#[requires()]
//~^ ERROR mismatched types [E0308]
//~| NOTE expected `bool`, found `()`
fn foo(x: u32) -> u32 {
x * 2
}

fn main() {
foo(1);
}
18 changes: 18 additions & 0 deletions tests/ui/contracts/empty-requires.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/empty-requires.rs:3:12
|
LL | #![feature(contracts)]
| ^^^^^^^^^
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default

error[E0308]: mismatched types
--> $DIR/empty-requires.rs:9:1
|
LL | #[requires()]
| ^^^^^^^^^^^^^ expected `bool`, found `()`

error: aborting due to 1 previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0308`.
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post
//@ revisions: chk_pass chk_fail_pre chk_fail_post
//
//@ [unchk_pass] run-pass
//@ [unchk_fail_pre] run-pass
//@ [unchk_fail_post] run-pass
//@ [chk_pass] run-pass
//
//@ [chk_fail_pre] run-crash
//@ [chk_fail_post] run-crash
//
//@ [unchk_pass] compile-flags: -Zcontract-checks=no
//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no
//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no
//
//@ [chk_pass] compile-flags: -Zcontract-checks=yes
//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes
//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes

#![feature(contracts_internals)]

Expand All @@ -30,15 +19,15 @@ fn nest(x: Baz) -> i32
struct Baz { baz: i32 }

const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 };
#[cfg(any(unchk_fail_post, chk_fail_post))]
#[cfg(chk_fail_post)]
const BAZ_FAIL_POST: Baz = Baz { baz: 10 };
#[cfg(any(unchk_fail_pre, chk_fail_pre))]
#[cfg(chk_fail_pre)]
const BAZ_FAIL_PRE: Baz = Baz { baz: -10 };

fn main() {
assert_eq!(nest(BAZ_PASS_PRE_POST), 150);
#[cfg(any(unchk_fail_pre, chk_fail_pre))]
#[cfg(chk_fail_pre)]
nest(BAZ_FAIL_PRE);
#[cfg(any(unchk_fail_post, chk_fail_post))]
#[cfg(chk_fail_post)]
nest(BAZ_FAIL_POST);
}
Loading
Loading