Skip to content

Conversation

@dawidl022
Copy link
Contributor

@dawidl022 dawidl022 commented Nov 6, 2025

The 'static bound on the closure of build_check_ensures prevented some patterns of contracts from type checking, without a clear reason for doing so. As such, this change removes the 'static bound.

While working on the proposal for owned and block for contracts, I came across a pattern that was rejected by type checker while attempting to specify std::ptr::write:

#![feature(core_intrinsics)]
#![feature(contracts)]
use core::contracts::{requires, ensures};
use core::intrinsics;

use std::mem::MaybeUninit;

fn owned<T>(p: *const T) -> T {
    // Stub implementation for ownership assertion. Ignore this.
    unsafe { std::ptr::read(p) }
}

#[requires(owned::<MaybeUninit<T>>(dst as *const _); true)]
#[ensures(move |_| { owned::<T>(dst); true })]
pub unsafe fn write<T>(dst: *mut T, src: T) {
    unsafe {
        intrinsics::write_via_move(dst, src)
    }
}
error[E0310]: the parameter type `T` may not live long enough
  --> src/main.rs:13:1
   |
13 | #[ensures(move |_| { owned::<T>(dst); true })]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   | |
   | the parameter type `T` must be valid for the static lifetime...
   | ...so that the type `T` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound
   |
14 | pub unsafe fn write<T: 'static>(dst: *mut T, src: T) {
   |                      +++++++++

For more information about this error, try `rustc --explain E0310`.

Eventually, I was able to track down the issue to a trait bound in core::contracts::build_check_ensures, which puts a 'static bound on the ensures closure. Remvoing the 'static bound allows the above contract to type-check, and additionally removes the need for the move in the ensures closure.

I have reduced the problem down to some minimal reproducible examples, which I included as part of this change as regression tests. The error messages for the regression tests before the change were as follows:

error: lifetime may not live long enough
  --> /Users/dawidl022/Development/rust/tests/ui/contracts/ensures-lifetime.rs:14:1
   |
LL | #[ensures(|_| { x; true })]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'1` must outlive `'static`
LL | pub fn noop<T>(x: &T) {}
   |                   - let's call the lifetime of this reference `'1`

error[E0597]: `x` does not live long enough
  --> /Users/dawidl022/Development/rust/tests/ui/contracts/ensures-lifetime.rs:14:17
   |
LL | #[ensures(|_| { x; true })]
   | ----------------^----------
   | |         |     |
   | |         |     borrowed value does not live long enough
   | |         value captured here
   | requires that `x` is borrowed for `'static`
LL | pub fn noop<T>(x: &T) {}
   |                -        - `x` dropped here while still borrowed
   |                |
   |                binding `x` declared here
   |
note: requirement that the value outlives `'static` introduced here
  --> /rustc/FAKE_PREFIX/library/core/src/contracts.rs:21:34

error: lifetime may not live long enough
  --> /Users/dawidl022/Development/rust/tests/ui/contracts/ensures-lifetime.rs:17:1
   |
LL | #[ensures(move |_| { x; true })]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'1` must outlive `'static`
LL | pub fn noop_mv<T>(x: &T) {}
   |                      - let's call the lifetime of this reference `'1`

error[E0310]: the parameter type `T` may not live long enough
  --> /Users/dawidl022/Development/rust/tests/ui/contracts/ensures-lifetime.rs:20:1
   |
LL | #[ensures(|_| { x; true })]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
   | |
   | the parameter type `T` must be valid for the static lifetime...
   | ...so that the type `T` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound
   |
LL | pub fn noop_ptr<T: 'static>(x: *const T) {}
   |                  +++++++++

error[E0310]: the parameter type `T` may not live long enough
  --> /Users/dawidl022/Development/rust/tests/ui/contracts/ensures-lifetime.rs:20:1
   |
LL | #[ensures(|_| { x; true })]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
   | |
   | the parameter type `T` must be valid for the static lifetime...
   | ...so that the type `T` will meet its required lifetime bounds
   |
   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit lifetime bound
   |
LL | pub fn noop_ptr<T: 'static>(x: *const T) {}
   |                  +++++++++

error[E0310]: the parameter type `T` may not live long enough
  --> /Users/dawidl022/Development/rust/tests/ui/contracts/ensures-lifetime.rs:20:1
   |
LL | #[ensures(|_| { x; true })]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
   | |
   | the parameter type `T` must be valid for the static lifetime...
   | ...so that the type `T` will meet its required lifetime bounds
   |
   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit lifetime bound
   |
LL | pub fn noop_ptr<T: 'static>(x: *const T) {}
   |                  +++++++++

error[E0597]: `x` does not live long enough
  --> /Users/dawidl022/Development/rust/tests/ui/contracts/ensures-lifetime.rs:20:17
   |
LL | #[ensures(|_| { x; true })]
   | ----------------^----------
   | |         |     |
   | |         |     borrowed value does not live long enough
   | |         value captured here
   | requires that `x` is borrowed for `'static`
LL | pub fn noop_ptr<T>(x: *const T) {}
   |                    -              - `x` dropped here while still borrowed
   |                    |
   |                    binding `x` declared here
   |
note: requirement that the value outlives `'static` introduced here
  --> /rustc/FAKE_PREFIX/library/core/src/contracts.rs:21:34

error[E0310]: the parameter type `T` may not live long enough
  --> /Users/dawidl022/Development/rust/tests/ui/contracts/ensures-lifetime.rs:23:1
   |
LL | #[ensures(move |_| { x; true })]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   | |
   | the parameter type `T` must be valid for the static lifetime...
   | ...so that the type `T` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound
   |
LL | pub fn noop_ptr_mv<T: 'static>(x: *const T) {}
   |                     +++++++++

error: aborting due to 8 previous errors; 1 warning emitted

Some errors have detailed explanations: E0310, E0597.
For more information about an error, try `rustc --explain E0310`.
------------------------------------------

---- [ui] tests/ui/contracts/ensures-lifetime.rs stdout end ----

failures:
    [ui] tests/ui/contracts/ensures-lifetime.rs

@celinval are you aware of any reason why the 'static bound was put on build_check_ensures in the first place?

I have checked that the contracts in #136578 and #147148 still type check with this change.

Contracts tracking issue: #128044

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. 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 Nov 6, 2025
@rustbot
Copy link
Collaborator

rustbot commented Nov 6, 2025

r? @davidtwco

rustbot has assigned @davidtwco.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rust-log-analyzer

This comment has been minimized.

The `'static` bound on the closure of `build_check_ensures` prevented
some patterns of contracts from type checking, without a clear
reason for doing so. As such, this change removes the `'static` bound.
@dawidl022 dawidl022 force-pushed the contracts/ensures-lifetime-bound branch from 857b066 to 75caeab Compare November 6, 2025 19:39
| ^^^^^^^^^
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you may add #![allow(incomplete_features)] to suppress this warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out! So far all the other contract UI tests have included this warning in the stderr, so I followed that pattern.

@tautschnig
Copy link
Contributor

Brilliant, this will enable adding a lot more contracts that #147148 currently has commented out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. 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.

6 participants