Fix unreachable expression warning

Invert the order that we pass the arguments to the
`contract_check_ensures` function to avoid the warning when the tail
of the function is unreachable.

Note that the call itself is also unreachable, but we have already
handled that case by ignoring unreachable call for contract calls.
This commit is contained in:
Celina G. Val
2025-04-07 17:42:08 -07:00
parent b9754f9e7b
commit 3feac59b79
16 changed files with 39 additions and 66 deletions

View File

@@ -401,11 +401,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
cond_hir_id: HirId, cond_hir_id: HirId,
) -> &'hir hir::Expr<'hir> { ) -> &'hir hir::Expr<'hir> {
let cond_fn = self.expr_ident(span, cond_ident, cond_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);
let call_expr = self.expr_call_lang_item_fn_mut( let call_expr = self.expr_call_lang_item_fn_mut(
span, span,
hir::LangItem::ContractCheckEnsures, hir::LangItem::ContractCheckEnsures,
arena_vec![self; *expr, *cond_fn], arena_vec![self; *cond_fn, *expr],
); );
self.arena.alloc(call_expr) self.arena.alloc(call_expr)
} }

View File

@@ -1209,8 +1209,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
let precond = if let Some(req) = &contract.requires { let precond = if let Some(req) = &contract.requires {
// Lower the precondition check intrinsic. // Lower the precondition check intrinsic.
let lowered_req = this.lower_expr_mut(&req); 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( let precond = this.expr_call_lang_item_fn_mut(
req.span, req_span,
hir::LangItem::ContractCheckRequires, hir::LangItem::ContractCheckRequires,
&*arena_vec![this; lowered_req], &*arena_vec![this; lowered_req],
); );
@@ -1220,6 +1225,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
}; };
let (postcond, body) = if let Some(ens) = &contract.ensures { let (postcond, body) = if let Some(ens) = &contract.ensures {
let ens_span = this.lower_span(ens.span); 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. // Set up the postcondition `let` statement.
let check_ident: Ident = let check_ident: Ident =
Ident::from_str_and_span("__ensures_checker", ens_span); Ident::from_str_and_span("__ensures_checker", ens_span);

View File

@@ -236,7 +236,7 @@ pub fn check_intrinsic_type(
// where C: for<'a> Fn(&'a Ret) -> bool, // where C: for<'a> Fn(&'a Ret) -> bool,
// //
// so: two type params, 0 lifetime param, 0 const params, two inputs, no return // 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) (2, 0, 0, vec![param(0), param(1)], param(1), hir::Safety::Safe)
} else { } else {
let safety = intrinsic_operation_unsafety(tcx, intrinsic_id); let safety = intrinsic_operation_unsafety(tcx, intrinsic_id);
let (n_tps, n_cts, inputs, output) = match intrinsic_name { let (n_tps, n_cts, inputs, output) = match intrinsic_name {

View File

@@ -2,15 +2,20 @@
pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires}; pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires};
/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` /// This is an identity function used as part of the desugaring of the `#[ensures]` attribute.
/// 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. /// This is an existing hack to allow users to omit the type of the return value in their ensures
/// attribute.
///
/// Ideally, rustc should be able to generate the type annotation.
/// The existing lowering logic makes it rather hard to add the explicit type annotation,
/// while the function call is fairly straight forward.
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] #[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
// Similar to `contract_check_requires`, we need to use the user-facing
// `contracts` feature rather than the perma-unstable `contracts_internals`.
// Const-checking doesn't honor allow internal unstable logic used by contract expansion.
#[rustc_const_unstable(feature = "contracts", issue = "128044")] #[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[lang = "contract_build_check_ensures"] #[lang = "contract_build_check_ensures"]
#[track_caller]
pub const fn build_check_ensures<Ret, C>(cond: C) -> C pub const fn build_check_ensures<Ret, C>(cond: C) -> C
where where
C: Fn(&Ret) -> bool + Copy + 'static, C: Fn(&Ret) -> bool + Copy + 'static,

View File

@@ -3453,6 +3453,10 @@ pub const fn contract_checks() -> bool {
/// ///
/// Note that this function is a no-op during constant evaluation. /// Note that this function is a no-op during constant evaluation.
#[unstable(feature = "contracts_internals", issue = "128044")] #[unstable(feature = "contracts_internals", issue = "128044")]
// Calls to this function get inserted by an AST expansion pass, which uses the equivalent of
// `#[allow_internal_unstable]` to allow using `contracts_internals` functions. Const-checking
// doesn't honor `#[allow_internal_unstable]`, so for the const feature gate we use the user-facing
// `contracts` feature rather than the perma-unstable `contracts_internals`
#[rustc_const_unstable(feature = "contracts", issue = "128044")] #[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[lang = "contract_check_requires"] #[lang = "contract_check_requires"]
#[rustc_intrinsic] #[rustc_intrinsic]
@@ -3478,12 +3482,15 @@ pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
/// Note that this function is a no-op during constant evaluation. /// Note that this function is a no-op during constant evaluation.
#[cfg(not(bootstrap))] #[cfg(not(bootstrap))]
#[unstable(feature = "contracts_internals", issue = "128044")] #[unstable(feature = "contracts_internals", issue = "128044")]
// Similar to `contract_check_requires`, we need to use the user-facing
// `contracts` feature rather than the perma-unstable `contracts_internals`.
// Const-checking doesn't honor allow internal unstable logic used by contract expansion.
#[rustc_const_unstable(feature = "contracts", issue = "128044")] #[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[lang = "contract_check_ensures"] #[lang = "contract_check_ensures"]
#[rustc_intrinsic] #[rustc_intrinsic]
pub const fn contract_check_ensures<Ret, C: Fn(&Ret) -> bool + Copy>(ret: Ret, cond: C) -> Ret { pub const fn contract_check_ensures<C: Fn(&Ret) -> bool + Copy, Ret>(cond: C, ret: Ret) -> Ret {
const_eval_select!( const_eval_select!(
@capture[Ret, C: Fn(&Ret) -> bool + Copy] { ret: Ret, cond: C } -> Ret : @capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: C, ret: Ret } -> Ret :
if const { if const {
// Do nothing // Do nothing
ret ret

View File

@@ -7,16 +7,5 @@ LL | #![feature(contracts)]
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information = note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default = note: `#[warn(incomplete_features)]` on by default
warning: unreachable expression warning: 1 warning emitted
--> $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

View File

@@ -21,7 +21,6 @@
#[core::contracts::requires(x.baz > 0)] #[core::contracts::requires(x.baz > 0)]
#[core::contracts::ensures(|ret| *ret > 100)] #[core::contracts::ensures(|ret| *ret > 100)]
//~^ WARN unreachable expression [unreachable_code]
fn nest(x: Baz) -> i32 fn nest(x: Baz) -> i32
{ {
loop { loop {

View File

@@ -7,16 +7,5 @@ LL | #![feature(contracts)]
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information = note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default = note: `#[warn(incomplete_features)]` on by default
warning: unreachable expression warning: 1 warning emitted
--> $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

View File

@@ -7,16 +7,5 @@ LL | #![feature(contracts)]
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information = note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default = note: `#[warn(incomplete_features)]` on by default
warning: unreachable expression warning: 1 warning emitted
--> $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

View File

@@ -7,16 +7,5 @@ LL | #![feature(contracts)]
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information = note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= note: `#[warn(incomplete_features)]` on by default = note: `#[warn(incomplete_features)]` on by default
warning: unreachable expression warning: 1 warning emitted
--> $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

View File

@@ -16,6 +16,7 @@ LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz
| | within this `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}` | | within this `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}`
| | this tail expression is of type `{closure@contract-captures-via-closure-noncopy.rs:12:42}` | | this tail expression is of type `{closure@contract-captures-via-closure-noncopy.rs:12:42}`
| unsatisfied trait bound | unsatisfied trait bound
| required by a bound introduced by this call
| |
= help: within `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}`, the trait `std::marker::Copy` is not implemented for `Baz` = help: within `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}`, the trait `std::marker::Copy` is not implemented for `Baz`
note: required because it's used within this closure note: required because it's used within this closure

View File

@@ -21,7 +21,6 @@
fn nest(x: Baz) -> i32 fn nest(x: Baz) -> i32
contract_requires(|| x.baz > 0) contract_requires(|| x.baz > 0)
contract_ensures(|ret| *ret > 100) contract_ensures(|ret| *ret > 100)
//~^ WARN unreachable expression [unreachable_code]
{ {
loop { loop {
return x.baz + 50; return x.baz + 50;

View File

@@ -28,9 +28,9 @@ fn main() {
let doubles_to_two = { let old = 2; move |ret: &u32 | ret + ret == old }; let doubles_to_two = { let old = 2; move |ret: &u32 | ret + ret == old };
// Always pass // Always pass
core::intrinsics::contract_check_ensures(1, doubles_to_two); core::intrinsics::contract_check_ensures(doubles_to_two, 1);
// Fail if enabled // Fail if enabled
#[cfg(any(default, unchk_pass, chk_fail_ensures))] #[cfg(any(default, unchk_pass, chk_fail_ensures))]
core::intrinsics::contract_check_ensures(2, doubles_to_two); core::intrinsics::contract_check_ensures(doubles_to_two, 2);
} }

View File

@@ -22,7 +22,7 @@ fn foo(x: Baz) -> i32 {
}; };
let ret = x.baz + 50; let ret = x.baz + 50;
core::intrinsics::contract_check_ensures(ret, injected_checker) core::intrinsics::contract_check_ensures(injected_checker, ret)
} }
struct Baz { baz: i32 } struct Baz { baz: i32 }

View File

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