Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 29 additions & 2 deletions kani-compiler/src/kani_middle/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,42 @@ use crate::kani_middle::transform::stubs::{ExternFnStubPass, FnStubPass};
use crate::kani_queries::QueryDb;
use automatic::{AutomaticArbitraryPass, AutomaticHarnessPass};
use dump_mir_pass::DumpMirPass;
use rustc_middle::ty::TyCtxt;
use rustc_hir::def::DefKind;
use rustc_middle::ty::{EarlyBinder, TyCtxt, TypingEnv};
use rustc_public::mir::Body;
use rustc_public::mir::mono::{Instance, MonoItem};
use rustc_public::rustc_internal;
use std::collections::HashMap;
use std::fmt::Debug;

use crate::kani_middle::transform::rustc_intrinsics::RustcIntrinsicsPass;
pub use internal_mir::RustcInternalMir;

/// Build the `Body` for an instance whose body `rustc_public` does not surface.
///
/// This currently only handles ADT constructors: rust-lang/rust stopped encoding
/// *optimized* MIR for constructors (`instance_mir` uses `mir_for_ctfe` for them),
/// which makes `rustc_public::Instance::body()` return `None` for constructors
/// imported from other crates even though rustc can still build their (trivial)
/// shim body. We reconstruct it here the same way `rustc_public`'s `BodyBuilder`
/// would: fetch the instance MIR and monomorphize it, then convert to the stable
/// representation. Any other bodyless instance is a genuine error and panics.
fn build_missing_body(tcx: TyCtxt, instance: Instance) -> Body {
let internal_instance = rustc_internal::internal(tcx, instance);
assert!(
matches!(tcx.def_kind(internal_instance.def_id()), DefKind::Ctor(..)),
"expected a body for instance `{}`",
instance.name()
);
let internal_body = tcx.instance_mir(internal_instance.def).clone();
let mono_body = internal_instance.instantiate_mir_and_normalize_erasing_regions(
tcx,
TypingEnv::fully_monomorphized(),
EarlyBinder::bind(internal_body),
);
rustc_internal::stable(&mono_body)
}

mod automatic;
pub(crate) mod body;
mod check_uninit;
Expand Down Expand Up @@ -118,7 +145,7 @@ impl BodyTransformation {
.entry(instance)
.or_insert_with(|| {
// Transform and add to the cache if there's no existing entry.
let mut body = instance.body().unwrap();
let mut body = instance.body().unwrap_or_else(|| build_missing_body(tcx, instance));
Comment thread
tautschnig marked this conversation as resolved.

let mut modified = false;
for pass in self.stub_passes.iter_mut().chain(self.inst_passes.iter_mut()) {
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
# SPDX-License-Identifier: Apache-2.0 OR MIT

[toolchain]
channel = "nightly-2026-02-05"
channel = "nightly-2026-02-06"
components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"]
30 changes: 30 additions & 0 deletions tests/kani/FunctionCall/ctor_as_fn_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Checks that ADT constructors defined in other crates can be used as function
//! values. As of nightly-2026-02-06, rustc no longer encodes optimized MIR for
//! ADT constructors, so their bodies are unavailable through
//! `rustc_public::Instance::body()` for constructors imported from other crates
//! and Kani must reconstruct them (see `BodyTransformation::body`). Passing
//! `Some` or `std::num::Wrapping` to `Iterator::map` exercises exactly that
//! path; a local constructor is included for contrast.

struct Local(u8);

#[kani::proof]
fn check_cross_crate_ctor_as_fn_value() {
let x: u8 = kani::any();
// `Some` is `Option`'s constructor, defined in `core`.
let v = std::iter::once(x).map(Some).next().unwrap();
assert_eq!(v, Some(x));
// A tuple-struct constructor defined in `core` (re-exported by `std`).
let w = std::iter::once(x).map(std::num::Wrapping).next().unwrap();
assert_eq!(w.0, x);
}

#[kani::proof]
fn check_local_ctor_as_fn_value() {
let x: u8 = kani::any();
let l = std::iter::once(x).map(Local).next().unwrap();
assert_eq!(l.0, x);
}
Loading