- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13.9k
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
          
     Merged
      
      
            bors
  merged 1 commit into
  rust-lang:master
from
dawidl022:contracts/variable-scoping-rebased
  
      
      
   
  Oct 29, 2025 
      
    
                
     Merged
            
            Contract variable declarations #144444
                    bors
  merged 1 commit into
  rust-lang:master
from
dawidl022:contracts/variable-scoping-rebased
  
      
      
   
  Oct 29, 2025 
              
            Conversation
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
    68621b6    to
    4a765bc      
    Compare
  
    
      
        
              This comment has been minimized.
        
        
      
    
  This comment has been minimized.
4a765bc    to
    ab96f97      
    Compare
  
    
      
        
              This comment has been minimized.
        
        
      
    
  This comment has been minimized.
ab96f97    to
    e41951a      
    Compare
  
    
      
        
              This comment has been minimized.
        
        
      
    
  This comment has been minimized.
    
  matthiaskrgr 
      added a commit
        to matthiaskrgr/rust
      that referenced
      this pull request
    
      Oct 16, 2025 
    
    
      
  
    
      
    
  
…ng, r=oli-obk Guard HIR lowered contracts with `contract_checks` 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. This partially addresses rust-lang#139548, i.e. the bad behavior no longer happens with contract checks disabled (`-Zcontract-checks=no`). The change is made in preparation for adding contract variable declarations - variables declared before the `requires` assertion, and accessible from both `requires` and `ensures`, but not in the function body (PR rust-lang#144444). As those declarations may also have side-effects, it's good to guard them with `contract_checks` - the new lowering approach allows for this to be done easily. Contracts tracking issue: rust-lang#128044 **Known limiatations**: - It is still possible to early return from the *function* from within a contract, e.g. ```rust #[ensures({if x > 0 { return 0 }; |_| true})] fn foo(x: u32) -> i32 { 42 } ``` When `foo` is called with an argument greater than 0, instead of `42`, `0` will be returned. As this is not a regression, it is not addressed in this PR. However, it may be worth revisiting later down the line, as users may expect a form of early return from *contract specifications*, and so returning from the entire *function* could cause confusion. - ~Contracts are still not optimised out when disabled. Currently, even when contracts are disabled, the code generated causes existing optimisations to fail, meaning even disabled contracts could impact runtime performance. This issue is blocking rust-lang#136578, and has not been addressed in this PR, i.e. the `mir-opt` and `codegen` tests that fail in rust-lang#136578 still fail with these new HIR lowering changes.~ Contracts should now be optimised out when disabled, however some regressions tests still need to be added to be sure that is indeed the case.
    
  rust-timer 
      added a commit
      that referenced
      this pull request
    
      Oct 16, 2025 
    
    
      
  
    
      
    
  
Rollup merge of #144438 - dawidl022:contracts/guarded-lowering, r=oli-obk Guard HIR lowered contracts with `contract_checks` 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. This partially addresses #139548, i.e. the bad behavior no longer happens with contract checks disabled (`-Zcontract-checks=no`). The change is made in preparation for adding contract variable declarations - variables declared before the `requires` assertion, and accessible from both `requires` and `ensures`, but not in the function body (PR #144444). As those declarations may also have side-effects, it's good to guard them with `contract_checks` - the new lowering approach allows for this to be done easily. Contracts tracking issue: #128044 **Known limiatations**: - It is still possible to early return from the *function* from within a contract, e.g. ```rust #[ensures({if x > 0 { return 0 }; |_| true})] fn foo(x: u32) -> i32 { 42 } ``` When `foo` is called with an argument greater than 0, instead of `42`, `0` will be returned. As this is not a regression, it is not addressed in this PR. However, it may be worth revisiting later down the line, as users may expect a form of early return from *contract specifications*, and so returning from the entire *function* could cause confusion. - ~Contracts are still not optimised out when disabled. Currently, even when contracts are disabled, the code generated causes existing optimisations to fail, meaning even disabled contracts could impact runtime performance. This issue is blocking #136578, and has not been addressed in this PR, i.e. the `mir-opt` and `codegen` tests that fail in #136578 still fail with these new HIR lowering changes.~ Contracts should now be optimised out when disabled, however some regressions tests still need to be added to be sure that is indeed the case.
e41951a    to
    838dda6      
    Compare
  
    838dda6    to
    a291a4d      
    Compare
  
    
      
        
              This comment has been minimized.
        
        
      
    
  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.
a291a4d    to
    aeae085      
    Compare
  
    
    
  RalfJung 
      pushed a commit
        to RalfJung/miri
      that referenced
      this pull request
    
      Oct 22, 2025 
    
    
      
  
    
      
    
  
…-obk Guard HIR lowered contracts with `contract_checks` 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. This partially addresses rust-lang/rust#139548, i.e. the bad behavior no longer happens with contract checks disabled (`-Zcontract-checks=no`). The change is made in preparation for adding contract variable declarations - variables declared before the `requires` assertion, and accessible from both `requires` and `ensures`, but not in the function body (PR rust-lang/rust#144444). As those declarations may also have side-effects, it's good to guard them with `contract_checks` - the new lowering approach allows for this to be done easily. Contracts tracking issue: rust-lang/rust#128044 **Known limiatations**: - It is still possible to early return from the *function* from within a contract, e.g. ```rust #[ensures({if x > 0 { return 0 }; |_| true})] fn foo(x: u32) -> i32 { 42 } ``` When `foo` is called with an argument greater than 0, instead of `42`, `0` will be returned. As this is not a regression, it is not addressed in this PR. However, it may be worth revisiting later down the line, as users may expect a form of early return from *contract specifications*, and so returning from the entire *function* could cause confusion. - ~Contracts are still not optimised out when disabled. Currently, even when contracts are disabled, the code generated causes existing optimisations to fail, meaning even disabled contracts could impact runtime performance. This issue is blocking rust-lang/rust#136578, and has not been addressed in this PR, i.e. the `mir-opt` and `codegen` tests that fail in rust-lang/rust#136578 still fail with these new HIR lowering changes.~ Contracts should now be optimised out when disabled, however some regressions tests still need to be added to be sure that is indeed the case.
| Seems pretty straightforward. @bors r+ | 
    
  bors 
      added a commit
      that referenced
      this pull request
    
      Oct 29, 2025 
    
    
      
  
    
      
    
  
Rollup of 5 pull requests Successful merges: - #144444 (Contract variable declarations) - #147281 (Make diagnostics clearer for binop-related errors in foreign crates) - #148131 (Skip parameter attribute deduction for MIR with `spread_arg`) - #148224 (bootstrap: `ensure(doc::Std)` no longer opens a browser) - #148226 (Bootstrap update) r? `@ghost` `@rustbot` modify labels: rollup
    
  rust-timer 
      added a commit
      that referenced
      this pull request
    
      Oct 29, 2025 
    
    
      
  
    
      
    
  
Rollup merge of #144444 - dawidl022:contracts/variable-scoping-rebased, r=jackh726 Contract variable declarations 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](https://dl.acm.org/doi/10.1145/3704879)-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 (which has now been merged). 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: ```rust #[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.
    
  github-actions bot
      pushed a commit
        to rust-lang/miri
      that referenced
      this pull request
    
      Oct 30, 2025 
    
    
      
  
    
      
    
  
Rollup of 5 pull requests Successful merges: - rust-lang/rust#144444 (Contract variable declarations) - rust-lang/rust#147281 (Make diagnostics clearer for binop-related errors in foreign crates) - rust-lang/rust#148131 (Skip parameter attribute deduction for MIR with `spread_arg`) - rust-lang/rust#148224 (bootstrap: `ensure(doc::Std)` no longer opens a browser) - rust-lang/rust#148226 (Bootstrap update) r? `@ghost` `@rustbot` modify labels: rollup
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment
  
      Labels
      
    S-waiting-on-bors
  Status: Waiting on bors to run and complete tests. Bors will change the label on completion. 
  
    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. 
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
This change adds contract variables that can be declared in the
requiresclause and can be referenced both inrequiresandensures, subject to usual borrow checking rules. This allows any setup common to both therequiresandensuresclauses 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,
requirescan now be an arbitrary sequence of statements, with the final expression being of typebool. They are executed in sequence as expected, before checking if the finalboolexpression holds.This PR depends on #144438 (which has now been merged).
Contracts tracking issue: #128044
Other changes introduced:
{}when using multiple statements. The change is backwards compatible, in that wrapping the content in{}still works as before. The macros also now treatrequiresandensuresuniformally, meaning therequiresclosure 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:
We have used the new variable declarations feature to remember the initial value pointed to by
x, however, movingxinto theensuresdoes not pass the borrow checker, meaning the above function contract is illegal. Ideally, something like the above should be expressable in contracts.