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 commit is contained in:
Dawid Lachowicz 2025-07-25 07:50:46 +01:00
parent 5b9007bfc3
commit e4ead0ec70
No known key found for this signature in database
GPG key ID: C3DF0179A96A684D
18 changed files with 461 additions and 120 deletions

View file

@ -0,0 +1,260 @@
use crate::LoweringContext;
impl<'a, 'hir> LoweringContext<'a, 'hir> {
pub(super) fn lower_contract(
&mut self,
body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>,
contract: &rustc_ast::FnContract,
) -> rustc_hir::Expr<'hir> {
match (&contract.requires, &contract.ensures) {
(Some(req), Some(ens)) => {
// Lower the fn contract, which turns:
//
// { body }
//
// into:
//
// {
// let __postcond = if contracts_checks() {
// contract_check_requires(PRECOND);
// Some(|ret_val| POSTCOND)
// } else {
// None
// };
// contract_check_ensures(__postcond, { body })
// }
let precond = self.lower_precond(req);
let postcond_checker = self.lower_postcond_checker(ens);
let contract_check =
self.lower_contract_check_with_postcond(Some(precond), postcond_checker);
let wrapped_body =
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
self.expr_block(wrapped_body)
}
(None, Some(ens)) => {
// Lower the fn contract, which turns:
//
// { body }
//
// into:
//
// {
// let __postcond = if contracts_check() {
// Some(|ret_val| POSTCOND)
// } else {
// None
// };
// __postcond({ body })
// }
let postcond_checker = self.lower_postcond_checker(ens);
let contract_check =
self.lower_contract_check_with_postcond(None, postcond_checker);
let wrapped_body =
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
self.expr_block(wrapped_body)
}
(Some(req), None) => {
// Lower the fn contract, which turns:
//
// { body }
//
// into:
//
// {
// if contracts_check() {
// contract_requires(PRECOND);
// }
// body
// }
let precond = self.lower_precond(req);
let precond_check = self.lower_contract_check_just_precond(precond);
let body = self.arena.alloc(body(self));
// Flatten the body into precond check, then body.
let wrapped_body = self.block_all(
body.span,
self.arena.alloc_from_iter([precond_check].into_iter()),
Some(body),
);
self.expr_block(wrapped_body)
}
(None, None) => body(self),
}
}
/// Lower the precondition check intrinsic.
fn lower_precond(&mut self, req: &Box<rustc_ast::Expr>) -> rustc_hir::Stmt<'hir> {
let lowered_req = self.lower_expr_mut(&req);
let req_span = self.mark_span_with_reason(
rustc_span::DesugaringKind::Contract,
lowered_req.span,
None,
);
let precond = self.expr_call_lang_item_fn_mut(
req_span,
rustc_hir::LangItem::ContractCheckRequires,
&*arena_vec![self; lowered_req],
);
self.stmt_expr(req.span, precond)
}
fn lower_postcond_checker(
&mut self,
ens: &Box<rustc_ast::Expr>,
) -> &'hir rustc_hir::Expr<'hir> {
let ens_span = self.lower_span(ens.span);
let ens_span =
self.mark_span_with_reason(rustc_span::DesugaringKind::Contract, ens_span, None);
let lowered_ens = self.lower_expr_mut(&ens);
self.expr_call_lang_item_fn(
ens_span,
rustc_hir::LangItem::ContractBuildCheckEnsures,
&*arena_vec![self; lowered_ens],
)
}
fn lower_contract_check_just_precond(
&mut self,
precond: rustc_hir::Stmt<'hir>,
) -> rustc_hir::Stmt<'hir> {
let stmts = self.arena.alloc_from_iter([precond].into_iter());
let then_block_stmts = self.block_all(precond.span, stmts, None);
let then_block = self.arena.alloc(self.expr_block(&then_block_stmts));
let precond_check = rustc_hir::ExprKind::If(
self.expr_call_lang_item_fn(
precond.span,
rustc_hir::LangItem::ContractChecks,
Default::default(),
),
then_block,
None,
);
let precond_check = self.expr(precond.span, precond_check);
self.stmt_expr(precond.span, precond_check)
}
fn lower_contract_check_with_postcond(
&mut self,
precond: Option<rustc_hir::Stmt<'hir>>,
postcond_checker: &'hir rustc_hir::Expr<'hir>,
) -> &'hir rustc_hir::Expr<'hir> {
let stmts = self.arena.alloc_from_iter(precond.into_iter());
let span = match precond {
Some(precond) => precond.span,
None => postcond_checker.span,
};
let postcond_checker = self.arena.alloc(self.expr_enum_variant_lang_item(
postcond_checker.span,
rustc_hir::lang_items::LangItem::OptionSome,
&*arena_vec![self; *postcond_checker],
));
let then_block_stmts = self.block_all(span, stmts, Some(postcond_checker));
let then_block = self.arena.alloc(self.expr_block(&then_block_stmts));
let none_expr = self.arena.alloc(self.expr_enum_variant_lang_item(
postcond_checker.span,
rustc_hir::lang_items::LangItem::OptionNone,
Default::default(),
));
let else_block = self.block_expr(none_expr);
let else_block = self.arena.alloc(self.expr_block(else_block));
let contract_check = rustc_hir::ExprKind::If(
self.expr_call_lang_item_fn(
span,
rustc_hir::LangItem::ContractChecks,
Default::default(),
),
then_block,
Some(else_block),
);
self.arena.alloc(self.expr(span, contract_check))
}
fn wrap_body_with_contract_check(
&mut self,
body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>,
contract_check: &'hir rustc_hir::Expr<'hir>,
postcond_span: rustc_span::Span,
) -> &'hir rustc_hir::Block<'hir> {
let check_ident: rustc_span::Ident =
rustc_span::Ident::from_str_and_span("__ensures_checker", postcond_span);
let (check_hir_id, postcond_decl) = {
// Set up the postcondition `let` statement.
let (checker_pat, check_hir_id) = self.pat_ident_binding_mode_mut(
postcond_span,
check_ident,
rustc_hir::BindingMode::NONE,
);
(
check_hir_id,
self.stmt_let_pat(
None,
postcond_span,
Some(contract_check),
self.arena.alloc(checker_pat),
rustc_hir::LocalSource::Contract,
),
)
};
// Install contract_ensures so we will intercept `return` statements,
// then lower the body.
self.contract_ensures = Some((postcond_span, check_ident, check_hir_id));
let body = self.arena.alloc(body(self));
// Finally, inject an ensures check on the implicit return of the body.
let body = self.inject_ensures_check(body, postcond_span, check_ident, check_hir_id);
// Flatten the body into precond, then postcond, then wrapped body.
let wrapped_body = self.block_all(
body.span,
self.arena.alloc_from_iter([postcond_decl].into_iter()),
Some(body),
);
wrapped_body
}
/// Create an `ExprKind::Ret` that is optionally wrapped by a call to check
/// a contract ensures clause, if it exists.
pub(super) fn checked_return(
&mut self,
opt_expr: Option<&'hir rustc_hir::Expr<'hir>>,
) -> rustc_hir::ExprKind<'hir> {
let checked_ret =
if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures {
let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span));
Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id))
} else {
opt_expr
};
rustc_hir::ExprKind::Ret(checked_ret)
}
/// Wraps an expression with a call to the ensures check before it gets returned.
pub(super) fn inject_ensures_check(
&mut self,
expr: &'hir rustc_hir::Expr<'hir>,
span: rustc_span::Span,
cond_ident: rustc_span::Ident,
cond_hir_id: rustc_hir::HirId,
) -> &'hir rustc_hir::Expr<'hir> {
let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id);
let call_expr = self.expr_call_lang_item_fn_mut(
span,
rustc_hir::LangItem::ContractCheckEnsures,
arena_vec![self; *cond_fn, *expr],
);
self.arena.alloc(call_expr)
}
}

View file

@ -383,36 +383,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}
/// Create an `ExprKind::Ret` that is optionally wrapped by a call to check
/// a contract ensures clause, if it exists.
fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> {
let checked_ret =
if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures {
let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span));
Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id))
} else {
opt_expr
};
hir::ExprKind::Ret(checked_ret)
}
/// Wraps an expression with a call to the ensures check before it gets returned.
pub(crate) fn inject_ensures_check(
&mut self,
expr: &'hir hir::Expr<'hir>,
span: Span,
cond_ident: Ident,
cond_hir_id: HirId,
) -> &'hir hir::Expr<'hir> {
let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id);
let call_expr = self.expr_call_lang_item_fn_mut(
span,
hir::LangItem::ContractCheckEnsures,
arena_vec![self; *cond_fn, *expr],
);
self.arena.alloc(call_expr)
}
pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock {
self.with_new_scopes(c.value.span, |this| {
let def_id = this.local_def_id(c.id);
@ -2120,7 +2090,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e))
}
fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> {
pub(super) fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> {
self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[])))
}
@ -2161,6 +2131,43 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr(span, hir::ExprKind::Call(e, args))
}
pub(super) fn expr_struct(
&mut self,
span: Span,
path: &'hir hir::QPath<'hir>,
fields: &'hir [hir::ExprField<'hir>],
) -> hir::Expr<'hir> {
self.expr(span, hir::ExprKind::Struct(path, fields, rustc_hir::StructTailExpr::None))
}
pub(super) fn expr_enum_variant(
&mut self,
span: Span,
path: &'hir hir::QPath<'hir>,
fields: &'hir [hir::Expr<'hir>],
) -> hir::Expr<'hir> {
let fields = self.arena.alloc_from_iter(fields.into_iter().enumerate().map(|(i, f)| {
hir::ExprField {
hir_id: self.next_id(),
ident: Ident::from_str(&i.to_string()),
expr: f,
span: f.span,
is_shorthand: false,
}
}));
self.expr_struct(span, path, fields)
}
pub(super) fn expr_enum_variant_lang_item(
&mut self,
span: Span,
lang_item: hir::LangItem,
fields: &'hir [hir::Expr<'hir>],
) -> hir::Expr<'hir> {
let path = self.arena.alloc(self.lang_item_path(span, lang_item));
self.expr_enum_variant(span, path, fields)
}
pub(super) fn expr_call(
&mut self,
span: Span,
@ -2189,8 +2196,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.arena.alloc(self.expr_call_lang_item_fn_mut(span, lang_item, args))
}
fn expr_lang_item_path(&mut self, span: Span, lang_item: hir::LangItem) -> hir::Expr<'hir> {
self.expr(span, hir::ExprKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span))))
pub(super) fn expr_lang_item_path(
&mut self,
span: Span,
lang_item: hir::LangItem,
) -> hir::Expr<'hir> {
let path = self.lang_item_path(span, lang_item);
self.expr(span, hir::ExprKind::Path(path))
}
pub(super) fn lang_item_path(
&mut self,
span: Span,
lang_item: hir::LangItem,
) -> hir::QPath<'hir> {
hir::QPath::LangItem(lang_item, self.lower_span(span))
}
/// `<LangItem>::name`

View file

@ -1214,76 +1214,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
let params =
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
// Optionally lower the fn contract, which turns:
//
// { body }
//
// into:
//
// { contract_requires(PRECOND); let __postcond = |ret_val| POSTCOND; postcond({ body }) }
// Optionally lower the fn contract
if let Some(contract) = contract {
let precond = if let Some(req) = &contract.requires {
// Lower the precondition check intrinsic.
let lowered_req = this.lower_expr_mut(&req);
let req_span = this.mark_span_with_reason(
DesugaringKind::Contract,
lowered_req.span,
None,
);
let precond = this.expr_call_lang_item_fn_mut(
req_span,
hir::LangItem::ContractCheckRequires,
&*arena_vec![this; lowered_req],
);
Some(this.stmt_expr(req.span, precond))
} else {
None
};
let (postcond, body) = if let Some(ens) = &contract.ensures {
let ens_span = this.lower_span(ens.span);
let ens_span =
this.mark_span_with_reason(DesugaringKind::Contract, ens_span, None);
// Set up the postcondition `let` statement.
let check_ident: Ident =
Ident::from_str_and_span("__ensures_checker", ens_span);
let (checker_pat, check_hir_id) = this.pat_ident_binding_mode_mut(
ens_span,
check_ident,
hir::BindingMode::NONE,
);
let lowered_ens = this.lower_expr_mut(&ens);
let postcond_checker = this.expr_call_lang_item_fn(
ens_span,
hir::LangItem::ContractBuildCheckEnsures,
&*arena_vec![this; lowered_ens],
);
let postcond = this.stmt_let_pat(
None,
ens_span,
Some(postcond_checker),
this.arena.alloc(checker_pat),
hir::LocalSource::Contract,
);
// Install contract_ensures so we will intercept `return` statements,
// then lower the body.
this.contract_ensures = Some((ens_span, check_ident, check_hir_id));
let body = this.arena.alloc(body(this));
// Finally, inject an ensures check on the implicit return of the body.
let body = this.inject_ensures_check(body, ens_span, check_ident, check_hir_id);
(Some(postcond), body)
} else {
let body = &*this.arena.alloc(body(this));
(None, body)
};
// Flatten the body into precond, then postcond, then wrapped body.
let wrapped_body = this.block_all(
body.span,
this.arena.alloc_from_iter([precond, postcond].into_iter().flatten()),
Some(body),
);
(params, this.expr_block(wrapped_body))
(params, this.lower_contract(body, contract))
} else {
(params, body(this))
}

View file

@ -77,6 +77,7 @@ macro_rules! arena_vec {
mod asm;
mod block;
mod contract;
mod delegation;
mod errors;
mod expr;

View file

@ -429,6 +429,7 @@ language_item_table! {
// Experimental lang items for implementing contract pre- and post-condition checking.
ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None;
ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None;
ContractChecks, sym::contract_checks, contract_checks_fn, Target::Fn, GenericRequirement::None;
// Experimental lang items for `MCP: Low level components for async drop`(https://github.com/rust-lang/compiler-team/issues/727)
DefaultTrait4, sym::default_trait4, default_trait4_trait, Target::Trait, GenericRequirement::None;

View file

@ -651,7 +651,9 @@ pub(crate) fn check_intrinsic_type(
sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool),
// contract_check_requires::<C>(C) -> bool, where C: impl Fn() -> bool
sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.unit),
sym::contract_check_ensures => (2, 0, vec![param(0), param(1)], param(1)),
sym::contract_check_ensures => {
(2, 0, vec![Ty::new_option(tcx, param(0)), param(1)], param(1))
}
sym::simd_eq | sym::simd_ne | sym::simd_lt | sym::simd_le | sym::simd_gt | sym::simd_ge => {
(2, 0, vec![param(0), param(0)], param(1))

View file

@ -903,6 +903,12 @@ impl<'tcx> Ty<'tcx> {
Ty::new_generic_adt(tcx, def_id, ty)
}
#[inline]
pub fn new_option(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
let def_id = tcx.require_lang_item(LangItem::Option, DUMMY_SP);
Ty::new_generic_adt(tcx, def_id, ty)
}
#[inline]
pub fn new_maybe_uninit(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
let def_id = tcx.require_lang_item(LangItem::MaybeUninit, DUMMY_SP);

View file

@ -2637,9 +2637,10 @@ pub const unsafe fn const_make_global(ptr: *mut u8) -> *const u8 {
/// of not prematurely committing at compile-time to whether contract
/// checking is turned on, so that we can specify contracts in libstd
/// and let an end user opt into turning them on.
#[rustc_const_unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[inline(always)]
#[lang = "contract_checks"]
#[rustc_intrinsic]
pub const fn contract_checks() -> bool {
// FIXME: should this be `false` or `cfg!(contract_checks)`?
@ -2668,7 +2669,7 @@ pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
if const {
// Do nothing
} else {
if contract_checks() && !cond() {
if !cond() {
// Emit no unwind panic in case this was a safety requirement.
crate::panicking::panic_nounwind("failed requires check");
}
@ -2681,6 +2682,8 @@ pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
/// returns false.
///
/// If `cond` is `None`, then no postcondition checking is performed.
///
/// Note that this function is a no-op during constant evaluation.
#[unstable(feature = "contracts_internals", issue = "128044")]
// Similar to `contract_check_requires`, we need to use the user-facing
@ -2689,16 +2692,24 @@ pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[lang = "contract_check_ensures"]
#[rustc_intrinsic]
pub const fn contract_check_ensures<C: Fn(&Ret) -> bool + Copy, Ret>(cond: C, ret: Ret) -> Ret {
pub const fn contract_check_ensures<C: Fn(&Ret) -> bool + Copy, Ret>(
cond: Option<C>,
ret: Ret,
) -> Ret {
const_eval_select!(
@capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: C, ret: Ret } -> Ret :
@capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: Option<C>, ret: Ret } -> 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");
match cond {
crate::option::Option::Some(cond) => {
if !cond(&ret) {
// Emit no unwind panic in case this was a safety requirement.
crate::panicking::panic_nounwind("failed ensures check");
}
},
crate::option::Option::None => {},
}
ret
}

View file

@ -0,0 +1,17 @@
//@ run-pass
#![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::ensures;
#[ensures({*x = 0; |_ret| true})]
fn buggy_add(x: &mut u32, y: u32) {
*x = *x + y;
}
fn main() {
let mut x = 10;
buggy_add(&mut x, 100);
assert_eq!(x, 110);
}

View file

@ -0,0 +1,11 @@
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/contracts-disabled-side-effect-ensures.rs:2: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

View file

@ -0,0 +1,16 @@
//@ 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::ensures;
#[ensures()]
//~^ ERROR expected a `Fn(&_)` closure, found `()` [E0277]
fn foo(x: u32) -> u32 {
x * 2
}
fn main() {
foo(1);
}

View file

@ -0,0 +1,25 @@
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/empty-ensures.rs:2: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
error[E0277]: expected a `Fn(&_)` closure, found `()`
--> $DIR/empty-ensures.rs:8:1
|
LL | #[ensures()]
| ^^^^^^^^^^^^
| |
| expected an `Fn(&_)` closure, found `()`
| required by a bound introduced by this call
|
= help: the trait `for<'a> Fn(&'a _)` is not implemented for `()`
note: required by a bound in `build_check_ensures`
--> $SRC_DIR/core/src/contracts.rs:LL:COL
error: aborting due to 1 previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,18 @@
//@ dont-require-annotations: NOTE
//@ 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;
#[requires()]
//~^ ERROR mismatched types [E0308]
//~| NOTE expected `bool`, found `()`
fn foo(x: u32) -> u32 {
x * 2
}
fn main() {
foo(1);
}

View file

@ -0,0 +1,18 @@
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/empty-requires.rs:3: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
error[E0308]: mismatched types
--> $DIR/empty-requires.rs:9:1
|
LL | #[requires()]
| ^^^^^^^^^^^^^ expected `bool`, found `()`
error: aborting due to 1 previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0308`.

View file

@ -22,15 +22,15 @@ fn main() {
// always pass
core::intrinsics::contract_check_requires(|| true);
// fail if enabled
#[cfg(any(default, unchk_pass, chk_fail_requires))]
// always fail
#[cfg(any(chk_fail_requires))]
core::intrinsics::contract_check_requires(|| false);
let doubles_to_two = { let old = 2; move |ret: &u32 | ret + ret == old };
// Always pass
core::intrinsics::contract_check_ensures(doubles_to_two, 1);
core::intrinsics::contract_check_ensures(Some(doubles_to_two), 1);
// Fail if enabled
#[cfg(any(default, unchk_pass, chk_fail_ensures))]
core::intrinsics::contract_check_ensures(doubles_to_two, 2);
// always fail
#[cfg(any(chk_fail_ensures))]
core::intrinsics::contract_check_ensures(Some(doubles_to_two), 2);
}

View file

@ -17,8 +17,10 @@
#![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 injected_checker = if core::intrinsics::contract_checks() {
Some(core::contracts::build_check_ensures(|ret| *ret > 100))
} else {
None
};
let ret = x.baz + 50;

View file

@ -6,7 +6,7 @@ fn main() {
//~^ ERROR use of unstable library feature `contracts_internals`
core::intrinsics::contract_check_requires(|| true);
//~^ ERROR use of unstable library feature `contracts_internals`
core::intrinsics::contract_check_ensures( |_|true, &1);
core::intrinsics::contract_check_ensures(Some(|_: &&u32| true), &1);
//~^ ERROR use of unstable library feature `contracts_internals`
core::contracts::build_check_ensures(|_: &()| true);

View file

@ -41,7 +41,7 @@ LL | core::intrinsics::contract_check_requires(|| true);
error[E0658]: use of unstable library feature `contracts_internals`
--> $DIR/internal-feature-gating.rs:9:5
|
LL | core::intrinsics::contract_check_ensures( |_|true, &1);
LL | core::intrinsics::contract_check_ensures(Some(|_: &&u32| true), &1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information