Skip to content

Rollup of 11 pull requests #139842

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

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b9754f9
Enable contracts for const functions
celinval Feb 25, 2025
3feac59
Fix unreachable expression warning
celinval Apr 8, 2025
13f1c84
Apply suggestions from code review
celinval Apr 10, 2025
86e2a07
opt-dist: use executable-extension for host llvm-profdata
ognevny Apr 12, 2025
a404015
std: sys: process: uefi: Use NULL stdin by default
Ayush1325 Apr 8, 2025
af25995
std: sys: stdio: uefi: Tread UNSUPPORTED Status as read(0)
Ayush1325 Apr 13, 2025
d994fef
std: sys: process: uefi: Allow specifying Stdin
Ayush1325 Apr 10, 2025
ed5f31a
Avoid unused clones in Cloned<I> and Copied<I>
thaliaarchi Apr 13, 2025
f3344ef
tests: use `compiletest-ignore-dir` for bootstrap self-tests
jieyouxu Apr 14, 2025
6a8718c
Add test for issue 34834
reddevilmidzy Apr 14, 2025
2c2c9df
drop global where-bounds before merging candidates
lcnr Apr 14, 2025
ce9d867
do not leak auto traits in item bounds
lcnr Apr 14, 2025
2e79f7c
move tests
lcnr Apr 14, 2025
836ea25
add RPITIT tests: method compat auto trait leakage
lcnr Apr 14, 2025
7ad1697
Allow const patterns of matches to contain pattern types
oli-obk Jan 24, 2025
c2712bc
ci: add runners for vanilla LLVM 20
cuviper Mar 12, 2025
9676d4a
std: add Output::exit_ok
lolbinarycat Apr 8, 2025
15fa891
Rollup merge of #138374 - celinval:issue-136925-const-contract, r=com…
jieyouxu Apr 15, 2025
928b2f8
Rollup merge of #138380 - cuviper:ci-llvm-20, r=Kobzol
jieyouxu Apr 15, 2025
5a5bda9
Rollup merge of #138393 - oli-obk:pattern-type-in-pattern, r=BoxyUwU
jieyouxu Apr 15, 2025
e5e75d3
Rollup merge of #139517 - Ayush1325:uefi-cmd-stdin-null, r=joboet
jieyouxu Apr 15, 2025
cf47022
Rollup merge of #139554 - lolbinarycat:std-output-exit_ok, r=tgross35
jieyouxu Apr 15, 2025
0a46c01
Rollup merge of #139745 - thaliaarchi:iter-unused-clone-copy, r=joboet
jieyouxu Apr 15, 2025
ea0406a
Rollup merge of #139757 - ognevny:opt-dist-hostllvm, r=Kobzol
jieyouxu Apr 15, 2025
3b21c26
Rollup merge of #139778 - reddevilmidzy:add-success-test, r=lcnr
jieyouxu Apr 15, 2025
3a32370
Rollup merge of #139783 - jieyouxu:ignore-dir, r=Zalathar
jieyouxu Apr 15, 2025
7823d49
Rollup merge of #139789 - lcnr:opaques-auto-trait-leakage, r=compiler…
jieyouxu Apr 15, 2025
c823f24
Rollup merge of #139791 - lcnr:ignore-global-where-bounds, r=compiler…
jieyouxu Apr 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Enable contracts for const functions
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.
  • Loading branch information
celinval committed Apr 7, 2025
commit b9754f9e7bfe2d8eed780962b550a25a87118ce4
13 changes: 9 additions & 4 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,12 +397,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
expr: &'hir hir::Expr<'hir>,
span: Span,
check_ident: Ident,
check_hir_id: HirId,
cond_ident: Ident,
cond_hir_id: HirId,
) -> &'hir hir::Expr<'hir> {
let checker_fn = self.expr_ident(span, check_ident, check_hir_id);
let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id);
let span = self.mark_span_with_reason(DesugaringKind::Contract, span, None);
self.expr_call(span, checker_fn, std::slice::from_ref(expr))
let call_expr = self.expr_call_lang_item_fn_mut(
span,
hir::LangItem::ContractCheckEnsures,
arena_vec![self; *expr, *cond_fn],
);
self.arena.alloc(call_expr)
}

pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ language_item_table! {
DefaultTrait3, sym::default_trait3, default_trait3_trait, Target::Trait, GenericRequirement::None;
DefaultTrait2, sym::default_trait2, default_trait2_trait, Target::Trait, GenericRequirement::None;
DefaultTrait1, sym::default_trait1, default_trait1_trait, Target::Trait, GenericRequirement::None;

ContractCheckEnsures, sym::contract_check_ensures, contract_check_ensures_fn, Target::Fn, GenericRequirement::None;
}

/// The requirement imposed on the generics of a lang item
Expand Down
12 changes: 4 additions & 8 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,15 +232,11 @@ pub fn check_intrinsic_type(
};
(n_tps, 0, 0, inputs, output, hir::Safety::Unsafe)
} else if intrinsic_name == sym::contract_check_ensures {
// contract_check_ensures::<'a, Ret, C>(&'a Ret, C)
// where C: impl Fn(&'a Ret) -> bool,
// contract_check_ensures::<Ret, C>(Ret, C) -> Ret
// where C: for<'a> Fn(&'a Ret) -> bool,
//
// so: two type params, one lifetime param, 0 const params, two inputs, no return

let p = generics.param_at(0, tcx);
let r = ty::Region::new_early_param(tcx, p.to_early_bound_region_data());
let ref_ret = Ty::new_imm_ref(tcx, r, param(1));
(2, 1, 0, vec![ref_ret, param(2)], tcx.types.unit, hir::Safety::Safe)
// so: two type params, 0 lifetime param, 0 const params, two inputs, no return
(2, 0, 0, vec![param(0), param(1)], param(0), hir::Safety::Safe)
} else {
let safety = intrinsic_operation_unsafety(tcx, intrinsic_id);
let (n_tps, n_cts, inputs, output) = match intrinsic_name {
Expand Down
13 changes: 6 additions & 7 deletions library/core/src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);] ... }`
/// (including the implicit return of the tail expression, if any).
///
/// This call helps with type inference for the predicate.
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[lang = "contract_build_check_ensures"]
#[track_caller]
pub fn build_check_ensures<Ret, C>(cond: C) -> impl (Fn(Ret) -> Ret) + Copy
pub const fn build_check_ensures<Ret, C>(cond: C) -> C
where
C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static,
C: Fn(&Ret) -> bool + Copy + 'static,
{
#[track_caller]
move |ret| {
crate::intrinsics::contract_check_ensures(&ret, cond);
ret
}
cond
}
47 changes: 41 additions & 6 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3450,20 +3450,55 @@ pub const fn contract_checks() -> bool {
///
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
/// returns false.
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
///
/// Note that this function is a no-op during constant evaluation.
#[unstable(feature = "contracts_internals", issue = "128044")]
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[lang = "contract_check_requires"]
#[rustc_intrinsic]
pub fn contract_check_requires<C: Fn() -> bool>(cond: C) {
if contract_checks() && !cond() {
// Emit no unwind panic in case this was a safety requirement.
crate::panicking::panic_nounwind("failed requires check");
}
pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
const_eval_select!(
@capture[C: Fn() -> bool + Copy] { cond: C } :
if const {
// Do nothing
} else {
if contract_checks() && !cond() {
// Emit no unwind panic in case this was a safety requirement.
crate::panicking::panic_nounwind("failed requires check");
}
}
)
}

/// Check if the post-condition `cond` has been met.
///
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
/// returns false.
///
/// Note that this function is a no-op during constant evaluation.
#[cfg(not(bootstrap))]
#[unstable(feature = "contracts_internals", issue = "128044")]
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[lang = "contract_check_ensures"]
#[rustc_intrinsic]
pub const fn contract_check_ensures<Ret, C: Fn(&Ret) -> bool + Copy>(ret: Ret, cond: C) -> Ret {
const_eval_select!(
@capture[Ret, C: Fn(&Ret) -> bool + Copy] { ret: Ret, cond: C } -> Ret :
if const {
// Do nothing
ret
} else {
if contract_checks() && !cond(&ret) {
// Emit no unwind panic in case this was a safety requirement.
crate::panicking::panic_nounwind("failed ensures check");
}
ret
}
)
}

/// This is the old version of contract_check_ensures kept here for bootstrap only.
#[cfg(bootstrap)]
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
#[rustc_intrinsic]
pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) {
Expand Down
1 change: 0 additions & 1 deletion library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@
#![feature(bstr)]
#![feature(bstr_internals)]
#![feature(cfg_match)]
#![feature(closure_track_caller)]
#![feature(const_carrying_mul_add)]
#![feature(const_eval_select)]
#![feature(core_intrinsics)]
Expand Down
13 changes: 12 additions & 1 deletion tests/ui/contracts/contract-attributes-nest.chk_pass.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,16 @@ LL | #![feature(contracts)]
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted
warning: unreachable expression
--> $DIR/contract-attributes-nest.rs:23:1
|
LL | #[core::contracts::ensures(|ret| *ret > 100)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable expression
...
LL | return x.baz + 50;
| ----------------- any code following this expression is unreachable
|
= note: `#[warn(unreachable_code)]` on by default

warning: 2 warnings emitted

1 change: 1 addition & 0 deletions tests/ui/contracts/contract-attributes-nest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#[core::contracts::requires(x.baz > 0)]
#[core::contracts::ensures(|ret| *ret > 100)]
//~^ WARN unreachable expression [unreachable_code]
fn nest(x: Baz) -> i32
{
loop {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,16 @@ LL | #![feature(contracts)]
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted
warning: unreachable expression
--> $DIR/contract-attributes-nest.rs:23:1
|
LL | #[core::contracts::ensures(|ret| *ret > 100)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable expression
...
LL | return x.baz + 50;
| ----------------- any code following this expression is unreachable
|
= note: `#[warn(unreachable_code)]` on by default

warning: 2 warnings emitted

Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,16 @@ LL | #![feature(contracts)]
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted
warning: unreachable expression
--> $DIR/contract-attributes-nest.rs:23:1
|
LL | #[core::contracts::ensures(|ret| *ret > 100)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable expression
...
LL | return x.baz + 50;
| ----------------- any code following this expression is unreachable
|
= note: `#[warn(unreachable_code)]` on by default

warning: 2 warnings emitted

13 changes: 12 additions & 1 deletion tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,16 @@ LL | #![feature(contracts)]
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted
warning: unreachable expression
--> $DIR/contract-attributes-nest.rs:23:1
|
LL | #[core::contracts::ensures(|ret| *ret > 100)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable expression
...
LL | return x.baz + 50;
| ----------------- any code following this expression is unreachable
|
= note: `#[warn(unreachable_code)]` on by default

warning: 2 warnings emitted

11 changes: 11 additions & 0 deletions tests/ui/contracts/contract-const-fn.all_pass.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/contract-const-fn.rs:17:12
|
LL | #![feature(contracts)]
| ^^^^^^^^^
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted

56 changes: 56 additions & 0 deletions tests/ui/contracts/contract-const-fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Check if we can annotate a constant function with contracts.
//!
//! The contract is only checked at runtime, and it will not fail if evaluated statically.
//! This is an existing limitation due to the existing architecture and the lack of constant
//! closures.
//!
//@ revisions: all_pass runtime_fail_pre runtime_fail_post
//
//@ [all_pass] run-pass
//
//@ [runtime_fail_pre] run-fail
//@ [runtime_fail_post] run-fail
//
//@ [all_pass] compile-flags: -Zcontract-checks=yes
//@ [runtime_fail_pre] compile-flags: -Zcontract-checks=yes
//@ [runtime_fail_post] compile-flags: -Zcontract-checks=yes
#![feature(contracts)]
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]

extern crate core;
use core::contracts::*;

#[requires(x < 100)]
const fn less_than_100(x: u8) -> u8 {
x
}

// This is wrong on purpose.
#[ensures(|ret| *ret)]
const fn always_true(b: bool) -> bool {
b
}

const ZERO: u8 = less_than_100(0);
// This is no-op because the contract cannot be checked at compilation time.
const TWO_HUNDRED: u8 = less_than_100(200);

/// Example from <https://github.com/rust-lang/rust/issues/136925>.
#[ensures(move |ret: &u32| *ret > x)]
const fn broken_sum(x: u32, y: u32) -> u32 {
x + y
}

fn main() {
assert_eq!(ZERO, 0);
assert_eq!(TWO_HUNDRED, 200);
assert_eq!(broken_sum(0, 1), 1);
assert_eq!(always_true(true), true);

#[cfg(runtime_fail_post)]
let _ok = always_true(false);

// Runtime check should fail.
#[cfg(runtime_fail_pre)]
let _200 = less_than_100(200);
}
11 changes: 11 additions & 0 deletions tests/ui/contracts/contract-const-fn.runtime_fail_post.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/contract-const-fn.rs:17:12
|
LL | #![feature(contracts)]
| ^^^^^^^^^
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted

11 changes: 11 additions & 0 deletions tests/ui/contracts/contract-const-fn.runtime_fail_pre.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/contract-const-fn.rs:17:12
|
LL | #![feature(contracts)]
| ^^^^^^^^^
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
fn nest(x: Baz) -> i32
contract_requires(|| x.baz > 0)
contract_ensures(|ret| *ret > 100)
//~^ WARN unreachable expression [unreachable_code]
{
loop {
return x.baz + 50;
Expand Down
6 changes: 3 additions & 3 deletions tests/ui/contracts/internal_machinery/contract-intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ fn main() {
#[cfg(any(default, unchk_pass, chk_fail_requires))]
core::intrinsics::contract_check_requires(|| false);

let doubles_to_two = { let old = 2; move |ret| ret + ret == old };
let doubles_to_two = { let old = 2; move |ret: &u32 | ret + ret == old };
// Always pass
core::intrinsics::contract_check_ensures(&1, doubles_to_two);
core::intrinsics::contract_check_ensures(1, doubles_to_two);

// Fail if enabled
#[cfg(any(default, unchk_pass, chk_fail_ensures))]
core::intrinsics::contract_check_ensures(&2, doubles_to_two);
core::intrinsics::contract_check_ensures(2, doubles_to_two);
}
4 changes: 2 additions & 2 deletions tests/ui/contracts/internal_machinery/contract-lang-items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
#![feature(contracts)] // to access core::contracts
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]
#![feature(contracts_internals)] // to access check_requires lang item

#![feature(core_intrinsics)]
fn foo(x: Baz) -> i32 {
let injected_checker = {
core::contracts::build_check_ensures(|ret| *ret > 100)
};

let ret = x.baz + 50;
injected_checker(ret)
core::intrinsics::contract_check_ensures(ret, injected_checker)
}

struct Baz { baz: i32 }
Expand Down