I tried this code:
fn hook(x: &i32) {
assert!(*x == 2);
}
enum Hook {
Default,
Custom(Box<dyn Fn(&i32) + Send + Sync + 'static>),
}
impl Hook {
fn into_box(self) -> Box<dyn Fn(&i32) + Send + Sync + 'static> {
match self {
Hook::Default => Box::new(hook),
Hook::Custom(hook) => hook,
}
}
}
#[kani::proof]
fn check() {
Hook::Default.into_box()(&2);
}
using the following command line invocation:
kani box_fn_item.rs --only-codegen
with Kani version: 0.67.0
I expected to see this happen: codegen succeeds, as it does for Box::new(|x: &i32| ...).
Instead, this happened:
kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs:234:9:
assertion failed: component.typ().is_pointer() || component.typ().is_rust_fat_ptr(&self.symbol_table)
and, with that assertion relaxed, the underlying mismatch:
cprover_bindings/src/goto_program/expr.rs:1049:9:
Error in struct_expr; value type does not match field type.
[Field { name: "pointer", typ: StructTag("tag-_62165824...") }]
[Expr { value: AddressOf(... hook::FnDefSingleton ...),
typ: Pointer { StructTag(... hook::FnDefStruct) } }]
RAW_PTR_FROM_BOX walks Box -> Unique -> NonNull -> pointer, but NonNull's pointer field holds a pattern_type!(*const T is !null), which is codegenned as a struct of its own, so the chain stops one level above the raw pointer. codegen_ptr_in_wrappers already documents and rebuilds that chain for DynMetadata; the Box path was not updated to match. The trigger is boxing a function item rather than a closure, which is why tests/kani/DynTrait/boxed_closure.rs passes.
Surfaced by std::panicking::Hook::into_box in an autoharness --list --std ./library run over the standard library on nightly-2026-08-21, which it aborts.
I tried this code:
using the following command line invocation:
with Kani version: 0.67.0
I expected to see this happen: codegen succeeds, as it does for
Box::new(|x: &i32| ...).Instead, this happened:
and, with that assertion relaxed, the underlying mismatch:
RAW_PTR_FROM_BOXwalksBox->Unique->NonNull-> pointer, butNonNull'spointerfield holds apattern_type!(*const T is !null), which is codegenned as a struct of its own, so the chain stops one level above the raw pointer.codegen_ptr_in_wrappersalready documents and rebuilds that chain forDynMetadata; theBoxpath was not updated to match. The trigger is boxing a function item rather than a closure, which is whytests/kani/DynTrait/boxed_closure.rspasses.Surfaced by
std::panicking::Hook::into_boxin anautoharness --list --std ./libraryrun over the standard library on nightly-2026-08-21, which it aborts.