Celina G. Val
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    1
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # Experimental Contract Attributes The [MCP-759](https://github.com/rust-lang/compiler-team/issues/759) proposed the addition of experimental unstable attributes for behaviorial specification, and intrinsics for enabling better tools to validate and verify those specifications. The goal is to eventually enable Rust, as a language, to allow users to write specifications in formal code-like language. More details can be found in the [MCP-759](https://github.com/rust-lang/compiler-team/issues/759). ## Initial Implementation In the [PR #128045](https://github.com/rust-lang/rust/pull/128045), we are introducing a simple form of `#[contracts::requires]` and `#[contracts::ensures]`, while [PR #138374](https://github.com/rust-lang/rust/pull/138374) adds support for contract in constant functions. While `requires` expects a side-effect free boolean expression, the `ensures` expects a side-effect free closure definition that takes the reference to the function return value. For example, a developer could annotate `NonNull` methods with the following safety contracts: ```rust #![feature(contracts)] extern crate core; use core::contracts; impl<T: ?Sized> NonNull<T> { #[contracts::requires(!ptr.is_null())] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { // SAFETY: the caller must guarantee that `ptr` is non-null. unsafe { NonNull { pointer: ptr as _ } } } #[contracts::ensures(|new_ptr| !new_ptr.is_null())] pub const fn as_ptr(self) -> *mut T { self.pointer as *mut T } } ``` A user can optionally turn the contract into runtime checks executed before and after the function annotated with contract has been called. Additionally, other verification tools can be used in order to check the annotated contracts. ### Compilation Stages In order to turn the contract annotations into runtime checks, we have implemented the following changes. 1. **Built-in Macro**: The `requires` and `ensures` were implemented as builtin macros that expands the function signature by adding a `rustc_contracts_requires` and `rustc_contracts_ensures` clauses respectively, both containing a closure definition. The `NonNull` methods signature from the example above would be expanded to the following in this stage: ```rust pub const unsafe fn new_unchecked(ptr: *mut T) -> Self rustc_contract_requires(|| !ptr.is_null()) { /* body */ } pub const fn as_ptr(self) -> *mut T rustc_contract_ensures(|new_ptr| !new_ptr.is_null()) { /* body */ } ``` 2. **AST Extension**: The parser was extended to accept optional `rustc_contracts_requires` and `rustc_contracts_ensures` clauses, and store them as part of the function AST node. This change to the syntax is perma-unstable. It can be enabled via `rustc_contracts_internals` unstable feature. 3. **AST Lowering**: a. **Contract closures**: During body lowering, the contract clauses lowered to become closures in the function prelude. For the `ensures` closure, the compile will inject a call to `build_check_ensures` with the `ensures` closure to help with type inference. b. **Requires check**: Also in the function prelude, the compiler will inject a call to the new intrinsic `contract_check_requires` to check the `requires` closure. b. **Ensures check**: The compiler will insert a call to `contract_check_ensures` intrinsics at every function return point, as well as at the end of the function. The `NonNull` examples above will look like[^hir] the following after all these stages: ```rust const unsafe fn new_unchecked(ptr: *mut T) -> Self { contract_check_requires(|| !ptr.is_null()); { // SAFETY: the caller must guarantee that `ptr` is non-null. unsafe { NonNull{ pointer: ptr as _,} } } } const fn as_ptr(self) -> *mut T { let __ensures_checker = contract_build_check_ensures(|new_ptr| !new_ptr.is_null()); contract_check_ensures({ self.pointer as *mut T }, __ensures_checker) } ``` [^hir]: The code below was extracted with `-Zunpretty=hir` and edited for clarity. ### Feature gates We have added two feature gates to contracts: 1. `rustc_contracts`: Enables the usage of contract attributes. 2. `rustc_contracts_internal`: Perma-unstable attribute that enables the explicit usage of the extended signature with contract clauses. We have also added a new configuration: `contract_checks` to enable the usage of `#[cfg(contract_checks)` to check if contract checks are enabled. ### Command arguments We have added a new unstable argument `-Zcontract-checks=[yes|no]` that allows users to enable / disable the runtime checks. The behavior of this feature is similar to `-Zub_checks`. ### New Intrinsics We are introducing 3 new intrinsics in this PR. 1. `contract_checks`: Returns whether contract's runtime checks have been enabled. 2. `contract_check_requires` / `contract_check_ensures` : Invoke the contract closure to check whether the specification holds. ## Limitations ### Contract Order of Evaluation TODO: Document the current limitation that ensures clauses are evaluated before dropping arguments. See [this zulip thread](https://rust-lang.zulipchat.com/#narrow/channel/544080-t-lang.2Fcontracts/topic/Order.20of.20evaluation.20between.20ensures.20and.20dropping.20parameters). ### Lack of API for querying contracts TODO: Talk about verification and analysis tools integration. Contracts are embedded into a function body early on, which doesn't allow tools to easily analyze contracts. ### Built-in Macro The current implementation of the contracts built-in macros is rather fragile. We couldn't implement a more robust solution with the existing macro infrastructure. The contract macros differ from all the existing attributes like built-in macros. The input for contracts macro is an arbitrary expression, while existing macros accept a restricted AST as an input (`MetaItem`). :::spoiler Expand this for more background on existing implementation. There are two different ways to define attribute macros in the Rust compiler today: 1. `Attr`: A token-based attribute macro. It takes two `TokenStream` as input, the annotated item, and the content of the attribute. It should produce a new `TokenStream` for the item. 2. `LegacyAttr`: An AST-based attribute macro. It takes a `MetaItem` and an `Annotable` item as input representing the content of the attribute and the annotated item. Note that `MetaItem` is restricted It produces one or more `Annotable` as a result. All the attributes like built-in macros defined in the Rust compiler today implement the this variant. However, for contracts, the input for the macro is an arbitrary expression. Thus, we had to use the `Attr` trait. ```rust /// A syntax extension kind. pub enum SyntaxExtensionKind { /// A token-based attribute macro. Attr( /// An expander with signature (TokenStream, TokenStream) -> TokenStream. /// The first TokenStream is the attribute itself, the second is the annotated item. /// The produced TokenStream replaces the input TokenStream. Box<dyn AttrProcMacro + sync::DynSync + sync::DynSend>, ), /// An AST-based attribute macro. LegacyAttr( /// An expander with signature (AST, AST) -> AST. /// The first AST fragment is the attribute itself, the second is the annotated item. /// The produced AST fragment replaces the input AST fragment. Box<dyn MultiItemModifier + sync::DynSync + sync::DynSend>, ), /* .. */ } /// Trait used by the `Attr` variant. pub trait AttrProcMacro { fn expand<'cx>( &self, ecx: &'cx mut ExtCtxt<'_>, span: Span, annotation: TokenStream, annotated: TokenStream, ) -> Result<TokenStream, ErrorGuaranteed>; } /// Trait used by the `LegacyAttr` variant. pub trait MultiItemModifier { /// `meta_item` is the attribute, and `item` is the item being modified. fn expand( &self, ecx: &mut ExtCtxt<'_>, span: Span, meta_item: &ast::MetaItem, item: Annotatable, is_derive_const: bool, ) -> ExpandResult<Vec<Annotatable>, Annotatable>; } ``` ::: We couldn't leverage the `Parser` either. We were able to use the `Parser` to retrieve the annotated item, and inject the contract expression in the item, however, there is no easy way to convert the modified AST item back to `TokenStream`. Calls to `TokenStream::from_ast` would result in the original `TokenStream`, and the contract clauses would be lost. Ideally we should extend the attribute expander to support a TokenStream annotation atop an AST annotated item, so that it would again be trivial to inject the attribute payload into an unambiguous spot in the AST. Another possibility was proposed [here](https://github.com/rust-lang/rust/issues/146808#issuecomment-3418389466), which would be to directly integrate with syn / quote. ### AST Lowering When writing a contract, users should be able to omit the type annotation on the ensures clause. I.e.: ```rust #[core::contracts::ensures(let old_val = val; |ret| ret.is_none_or(|x| x > old_val))] fn increment(val: isize) -> Option<isize> { val.checked_add(1) } ``` instead of: ```rust #[core::contracts::ensures(let old_val = val; |ret: &Option<isize>| ret.is_none_or(|x| x > old_val))] fn increment(val: isize) -> Option<isize> { val.checked_add(1) } ``` For that, rustc should lower the following code to: ```rust fn increment(val: isize) -> Option<isize> { let __ens_checker : Fn(&Option<isize)) -> bool = { let old_val = val; |ret| ret.is_none_or(|x| x > old_val) }; core::intrinsics::contract_check_ensures(__ens_checker, { val.checked_add(1) }) } ``` The existing lowering logic makes it rather hard to add the explicit type annotation, while the function call is fairly straight forward. Thus, rustc adds a call to an identity function named `build_check_ensures` for now: ```rust fn increment(val: isize) -> Option<isize> { let __ens_checker = core::contracts::build_check_ensures ({ let old_val = val; |ret| ret.is_none_or(|x| x > old_val) }); core::intrinsics::contract_check_ensures(__ens_checker, { val.checked_add(1) }) } ``` ### Target Functions In this initial implementation, contracts can only be attached to functions that have a body, excluding foreign functions, many intrinsics and trait declarations. We would likely to eventually remove that restriction, but it will require redesigning the contract instrumentation. We would also need to decide how to encode the contract check. We could for example add the instrumentation in the callee body. ## Contracts Re-architecture The current contract implementation was designed as a temporary solution to unblock early experimentation and validation. While functional, it presents several architectural limitations documented in previous sections, making it unsuitable for long-term stabilization. We believe a better solution would be to introduce dedicated HIR elements for contract components, similar to the existing closure implementation. Contract instrumentation would be implemented as a dedicated MIR instrumentation pass that runs only when contracts are enabled. The exact implementation details is still not entirely clear, and feedback would be extremely valuable at this point. One possibility would be to generate a separate HIR element that reflects the function signature, and contains the contract logic. For example: ```rust fn func_with_contract(args: FnArgs) { pre_cond(&args); // This could capture argument values let ret_val = original_fn(/*move*/ args); // Drop occurs before post-cond post_cond(&ret_val); ret_val } ``` The MIR transformation would either rewrite the body of the original function, or re-route usages of the original function to the one with contracts. Re-routing would be suitable for functions without bodies, such as external functions, and intrinsics. Replacing function bodies would be more friendly for things like trait implementations. While we theoretically don't need to generate actual functions as HIR elements, and the MIR pass could be responsible for assembling it. This approach may be necessary to ensure proper contract type checking and borrow checking integration. Using dedicated elements would provide consistency with existing language constructs in the HIR, and ensure zero runtime overhead for builds without contracts. ## Future work 1. Add interface for external tools to retrieve the contract specification. 2. Split correctness / safety contracts (per [all-hands discussion](https://hackmd.io/@qnR1-HVLRx-dekU5dvtvkw/SyUuR6SZgx#Spec)) 3. Improve the existing architecture. 4. Annotate the standard library with contracts. a. Relationship between `ub_checks` and `contracts`. 4. Add support for type invariant. 5. Add more tests + improve implementation. 6. Improve error handling and diagnostic messages. 7. Potential changes to the contract annotation syntax: a. Safety vs correctness vs panic freedom b. Contract stability ## Questions *Please feel free to add any questions you might have*

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully