-
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); } })
} |
2ff3baa
to
4dd0bca
Compare
This comment has been minimized.
This comment has been minimized.
@compiler-errors any thoughts? |
No, I didn't look at it yet |
library/core/src/contracts.rs
Outdated
@@ -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
library/core/src/intrinsics/mod.rs
Outdated
#[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...
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 I'm getting this error:
error: `contract_check_ensures` is not yet stable as a const intrinsic
--> contract-const-fn.rs:39:1
|
39 | #[ensures(move |ret: &u32| *ret > x)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add `#![feature(contracts_internals)]` to the crate attributes to enable
|
20 + #![feature(contracts_internals)]
|
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.
Ah okay, there's a bunch of macro trickery here that I didn't properly understand.
Your original approach is fine, but should come with a comment:
// Calls to this function get inserted by an AST expansion pass, which uses the equivalent of
// `#[allow_internal_unstable]` to allow using `contracts_internals` functions. Const-checking
// doesn't honor `#[allow_internal_unstable]`, so for the const feature gate we use the user-facing
// `contracts` feature rather than the perma-unstable `contracts_internals`.
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
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. |
☔ The latest upstream changes (presumably #138996) made this pull request unmergeable. Please resolve the merge conflicts. |
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.
4dd0bca
to
4f62bc2
Compare
} if gate_already_checked || self.tcx.features().enabled(gate) => { | ||
} if gate_already_checked | ||
|| self.tcx.features().enabled(gate) | ||
|| span.allows_unstable(gate) => |
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.
@RalfJung I had to add this line in order to use the same feature gate for unstable
and rustc_const_unstable
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.
No, we definitely do not want to check the span here, that could be used to bypass recursive const stability.
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