-
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?
Conversation
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.
In what case is the unreachable error being generated? Can you turn it into a minimal example? I'm not totally sure what's going on with the code generation.
I am still trying to understand why the new logic triggers the new warning, when the old one didn't.
So, if you look at the following example from one of the tests: #![feature(contracts)]
pub struct Baz { baz: i32 }
#[core::contracts::requires(x.baz > 0)]
#[core::contracts::ensures(|ret| *ret > 100)]
pub fn nest(x: Baz) -> i32
{
loop {
return x.baz + 50;
}
} with the existing changes, you will get a warning:
which in a way it makes sense, since we do add a call to check the post-condition after the loop statement. What I don't get is why it wasn't being triggered before, and whether I can mark the new code as If I compile with fn nest(x: Baz) -> i32 {
#[lang = "contract_check_requires"](|| x.baz > 0);
let __ensures_checker = #[lang = "contract_build_check_ensures"](|ret| *ret > 100);
#[lang = "contract_check_ensures"]({
loop { return #[lang = "contract_check_ensures"](x.baz + 50, __ensures_checker); }
}, __ensures_checker)
} before the changes, we had: fn nest(x: Baz) -> i32 {
#[lang = "contract_check_requires"](|| x.baz > 0);
let __ensures_checker = #[lang = "contract_build_check_ensures"](|ret| *ret > 100);
__ensures_checker({ loop { return __ensures_checker(x.baz + 50); } })
} |
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, however, it currently has a spurious warning message when the bottom of the function is unreachable.
2ff3baa
to
4dd0bca
Compare
The job Click to see the possible cause of the failure (guessed by this bot)
|
@compiler-errors any thoughts? |
No, I didn't look at it yet |
@@ -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);] ... }` |
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
} | ||
|
||
/// 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't you just add the Copy
here? Why is cfg(bootstrap)
needed?
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.
Because we changed the return value and the arguments of this function.
#[unstable(feature = "contracts_internals", issue = "128044")] | ||
#[rustc_const_unstable(feature = "contracts", issue = "128044")] |
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.
Why are you using two different feature gates here?
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.
Good question, for some reason the const stability check isn't working with contracts_internal
.
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.
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...
pub const fn build_check_ensures<Ret, C>(cond: C) -> C | ||
where |
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.
What is the point of this function? It's just the identity function?
This call helps with type inference for the predicate.
Surely there's a better way to do that?
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.
Likely. Let me give it a try
In the example,
My understanding is that fn nest(x: Baz) -> i32 {
#[lang = "contract_check_requires"](|| x.baz > 0);
let __ensures_checker = #[lang = "contract_build_check_ensures"](|ret| *ret > 100);
#[lang = "contract_check_ensures"]({
loop { return #[lang = "contract_check_ensures"](x.baz + 50, __ensures_checker); }
}, #[allow(unreachable_code)] __ensures_checker)
} |
The call is added as part of lowering the AST today. We can reconsider it though. |
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 #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