-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Enable contracts for const functions #138374
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,16 +5,15 @@ pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_require | |
/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` | ||
/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` | ||
/// (including the implicit return of the tail expression, if any). | ||
/// | ||
/// This call helps with type inference for the predicate. | ||
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] | ||
#[rustc_const_unstable(feature = "contracts", issue = "128044")] | ||
#[lang = "contract_build_check_ensures"] | ||
#[track_caller] | ||
pub fn build_check_ensures<Ret, C>(cond: C) -> impl (Fn(Ret) -> Ret) + Copy | ||
pub const fn build_check_ensures<Ret, C>(cond: C) -> C | ||
where | ||
Comment on lines
+14
to
15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the point of this function? It's just the identity function?
Surely there's a better way to do that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Likely. Let me give it a try |
||
C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static, | ||
C: Fn(&Ret) -> bool + Copy + 'static, | ||
{ | ||
#[track_caller] | ||
move |ret| { | ||
crate::intrinsics::contract_check_ensures(&ret, cond); | ||
ret | ||
} | ||
cond | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3288,20 +3288,55 @@ pub const fn contract_checks() -> bool { | |
/// | ||
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition | ||
/// returns false. | ||
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] | ||
/// | ||
/// Note that this function is a no-op during constant evaluation. | ||
#[unstable(feature = "contracts_internals", issue = "128044")] | ||
#[rustc_const_unstable(feature = "contracts", issue = "128044")] | ||
#[lang = "contract_check_requires"] | ||
#[rustc_intrinsic] | ||
pub fn contract_check_requires<C: Fn() -> bool>(cond: C) { | ||
if contract_checks() && !cond() { | ||
// Emit no unwind panic in case this was a safety requirement. | ||
crate::panicking::panic_nounwind("failed requires check"); | ||
} | ||
pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) { | ||
const_eval_select!( | ||
@capture[C: Fn() -> bool + Copy] { cond: C } : | ||
if const { | ||
// Do nothing | ||
} else { | ||
if contract_checks() && !cond() { | ||
// Emit no unwind panic in case this was a safety requirement. | ||
crate::panicking::panic_nounwind("failed requires check"); | ||
} | ||
} | ||
) | ||
} | ||
|
||
/// Check if the post-condition `cond` has been met. | ||
/// | ||
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition | ||
/// returns false. | ||
/// | ||
/// Note that this function is a no-op during constant evaluation. | ||
#[cfg(not(bootstrap))] | ||
#[unstable(feature = "contracts_internals", issue = "128044")] | ||
#[rustc_const_unstable(feature = "contracts", issue = "128044")] | ||
Comment on lines
+3318
to
+3319
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are you using two different feature gates here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good question, for some reason the const stability check isn't working with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does "isn't working" mean? We used to have a bug where using a language feature for const stability didn't work (the feature would not get enabled properly), but I thought we had fixed that... |
||
#[lang = "contract_check_ensures"] | ||
#[rustc_intrinsic] | ||
pub const fn contract_check_ensures<Ret, C: Fn(&Ret) -> bool + Copy>(ret: Ret, cond: C) -> Ret { | ||
const_eval_select!( | ||
@capture[Ret, C: Fn(&Ret) -> bool + Copy] { ret: Ret, cond: C } -> Ret : | ||
if const { | ||
// Do nothing | ||
ret | ||
} else { | ||
if contract_checks() && !cond(&ret) { | ||
// Emit no unwind panic in case this was a safety requirement. | ||
crate::panicking::panic_nounwind("failed ensures check"); | ||
} | ||
ret | ||
} | ||
) | ||
} | ||
|
||
/// This is the old version of contract_check_ensures kept here for bootstrap only. | ||
#[cfg(bootstrap)] | ||
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] | ||
#[rustc_intrinsic] | ||
pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why can't you just add the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we changed the return value and the arguments of this function. |
||
|
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/contract-const-fn.rs:17: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 |
---|---|---|
@@ -0,0 +1,56 @@ | ||
//! Check if we can annotate a constant function with contracts. | ||
//! | ||
//! The contract is only checked at runtime, and it will not fail if evaluated statically. | ||
//! This is an existing limitation due to the existing architecture and the lack of constant | ||
//! closures. | ||
//! | ||
//@ revisions: all_pass runtime_fail_pre runtime_fail_post | ||
// | ||
//@ [all_pass] run-pass | ||
// | ||
//@ [runtime_fail_pre] run-fail | ||
//@ [runtime_fail_post] run-fail | ||
// | ||
//@ [all_pass] compile-flags: -Zcontract-checks=yes | ||
//@ [runtime_fail_pre] compile-flags: -Zcontract-checks=yes | ||
//@ [runtime_fail_post] 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(x < 100)] | ||
const fn less_than_100(x: u8) -> u8 { | ||
x | ||
} | ||
|
||
// This is wrong on purpose. | ||
#[ensures(|ret| *ret)] | ||
const fn always_true(b: bool) -> bool { | ||
b | ||
} | ||
|
||
const ZERO: u8 = less_than_100(0); | ||
// This is no-op because the contract cannot be checked at compilation time. | ||
const TWO_HUNDRED: u8 = less_than_100(200); | ||
|
||
/// Example from <https://github.com/rust-lang/rust/issues/136925>. | ||
#[ensures(move |ret: &u32| *ret > x)] | ||
const fn broken_sum(x: u32, y: u32) -> u32 { | ||
x + y | ||
} | ||
|
||
fn main() { | ||
assert_eq!(ZERO, 0); | ||
assert_eq!(TWO_HUNDRED, 200); | ||
assert_eq!(broken_sum(0, 1), 1); | ||
assert_eq!(always_true(true), true); | ||
|
||
#[cfg(runtime_fail_post)] | ||
let _ok = always_true(false); | ||
|
||
// Runtime check should fail. | ||
#[cfg(runtime_fail_pre)] | ||
let _200 = less_than_100(200); | ||
} |
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/contract-const-fn.rs:17: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 |
---|---|---|
@@ -0,0 +1,11 @@ | ||
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes | ||
--> $DIR/contract-const-fn.rs:17: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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now outdated, isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me see if we can get rid of it completely