Skip to content

Contract variable declarations #144444

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

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

dawidl022
Copy link

@dawidl022 dawidl022 commented Jul 25, 2025

This change adds contract variables that can be declared in the requires clause and can be referenced both in requires and ensures, subject to usual borrow checking rules. This allows any setup common to both the requires and ensures clauses to only be done once.

In particular, one future use case would be for Fulminate-like ownership assertions in contracts, that are essentially side-effects, and executing them twice would alter the semantics of the contract.

As of this change, requires can now be an arbitrary sequence of statements, with the final expression being of type bool. They are executed in sequence as expected, before checking if the final bool expression holds.

This PR depends on #144438, and is set as a draft PR until those changes are merged in, so that this branch can be rebased to contain just the new code changes.

Contracts tracking issue: #128044

Other changes introduced:

  • Contract macros now wrap the content in braces to produce blocks, meaning there's no need to wrap the content in {} when using multiple statements. The change is backwards compatible, in that wrapping the content in {} still works as before. The macros also now treat requires and ensures uniformally, meaning the requires closure is built inside the parser, as opposed to in the macro.

Known limiatations:

  • Contracts with variable declarations are subject to the regular borrow checking rules, and the way contracts are currently lowered limits the usefulness of contract variable declarations. Consider the below example:

    #[requires(let init_x = *x; true)]
    #[ensures(move |_| *x == 2 * init_x)]
    fn double_in_place(x: &mut i32) {
        *x *= 2;
    }

    We have used the new variable declarations feature to remember the initial value pointed to by x, however, moving x into the ensures does not pass the borrow checker, meaning the above function contract is illegal. Ideally, something like the above should be expressable in contracts.

Refactor contract HIR lowering to ensure no contract code is
executed when contract-checks are disabled.

The call to contract_checks is moved to inside the lowered fn
body, and contract closures are built conditionally, ensuring
no side-effects present in contracts occur when those are disabled.
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jul 25, 2025
@dawidl022 dawidl022 force-pushed the contracts/variable-scoping-rebased branch from 68621b6 to 4a765bc Compare July 25, 2025 11:03
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

Contract variables can be declared in the `requires` clause and
can be referenced both in `requires` and `ensures`, subject to usual
borrow checking rules.

This allows any setup common to both the `requires` and `ensures`
clauses to only be done once.
@dawidl022 dawidl022 force-pushed the contracts/variable-scoping-rebased branch from ab96f97 to e41951a Compare July 25, 2025 11:30
@rust-log-analyzer
Copy link
Collaborator

The job pr-check-2 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
 Documenting rustc_hashes v0.0.0 (/checkout/compiler/rustc_hashes)
error: unresolved link to `requires`
    --> compiler/rustc_ast/src/ast.rs:3777:59
     |
3777 |     /// Declarations of variables accessible both in the [requires] and
     |                                                           ^^^^^^^^ no item named `requires` in scope
     |
     = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
     = note: `-D rustdoc::broken-intra-doc-links` implied by `-D warnings`
     = help: to override `-D warnings` add `#[allow(rustdoc::broken_intra_doc_links)]`

error: unresolved link to `ensures`
    --> compiler/rustc_ast/src/ast.rs:3778:10
     |
3778 |     /// [ensures] clauses.
     |          ^^^^^^^ no item named `ensures` in scope
     |
     = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`

 Documenting rustc_arena v0.0.0 (/checkout/compiler/rustc_arena)
 Documenting rustc_graphviz v0.0.0 (/checkout/compiler/rustc_graphviz)
error: could not document `rustc_ast`
warning: build failed, waiting for other jobs to finish...
Command has failed. Rerun with -v to see more details.
Build completed unsuccessfully in 0:01:52
  local time: Fri Jul 25 12:30:34 UTC 2025
  network time: Fri, 25 Jul 2025 12:30:35 GMT
##[error]Process completed with exit code 1.
Post job cleanup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants