Skip to content

Commit ff033ad

Browse files
authored
Represent FnDefs as zero-sized structs instead of Code (rust-lang#1338)
1 parent 762d541 commit ff033ad

File tree

8 files changed

+162
-17
lines changed

8 files changed

+162
-17
lines changed

cprover_bindings/src/goto_program/typ.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,12 @@ impl DatatypeComponent {
185185
}
186186

187187
pub fn field<T: Into<InternedString>>(name: T, typ: Type) -> Self {
188-
// TODO <https://github.com/model-checking/kani/issues/1243>
189-
// assert!(
190-
// Self::typecheck_datatype_field(&typ),
191-
// "Illegal field.\n\tName: {}\n\tType: {:?}",
192-
// name.into(),
193-
// typ
194-
// );
188+
assert!(
189+
Self::typecheck_datatype_field(&typ),
190+
"Illegal field.\n\tName: {}\n\tType: {:?}",
191+
name.into(),
192+
typ
193+
);
195194
let name = name.into();
196195
Field { name, typ }
197196
}

kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ impl<'tcx> GotocCtx<'tcx> {
349349
) -> Expr {
350350
let instance =
351351
Instance::resolve(self.tcx, ty::ParamEnv::reveal_all(), d, substs).unwrap().unwrap();
352-
self.codegen_func_expr(instance, span)
352+
self.codegen_fn_item(instance, span)
353353
}
354354

355355
fn codegen_alloc_pointer(
@@ -627,11 +627,17 @@ impl<'tcx> GotocCtx<'tcx> {
627627
self.find_function(&fname).unwrap().call(vec![init])
628628
}
629629

630-
pub fn codegen_func_expr(&mut self, instance: Instance<'tcx>, span: Option<&Span>) -> Expr {
630+
/// Ensure that the given instance is in the symbol table, returning the symbol.
631+
///
632+
/// FIXME: The function should not have to return the type of the function symbol as well
633+
/// because the symbol should have the type. The problem is that the type in the symbol table
634+
/// sometimes subtly differs from the type that codegen_function_sig returns.
635+
/// This is tracked in <https://github.com/model-checking/kani/issues/1350>.
636+
pub fn codegen_func_symbol(&mut self, instance: Instance<'tcx>) -> (&Symbol, Type) {
631637
let func = self.symbol_name(instance);
632638
let funct = self.codegen_function_sig(self.fn_sig_of_instance(instance).unwrap());
633639
// make sure the functions imported from other modules are in the symbol table
634-
self.ensure(&func, |ctx, _| {
640+
let sym = self.ensure(&func, |ctx, _| {
635641
Symbol::function(
636642
&func,
637643
funct.clone(),
@@ -641,6 +647,40 @@ impl<'tcx> GotocCtx<'tcx> {
641647
)
642648
.with_is_extern(true)
643649
});
644-
Expr::symbol_expression(func, funct).with_location(self.codegen_span_option(span.cloned()))
650+
(sym, funct)
651+
}
652+
653+
/// For a given function instance, generates an expression for the function symbol of type `Code`.
654+
///
655+
/// Note: use `codegen_func_expr_zst` in the general case because GotoC does not allow functions to be used in all contexts
656+
/// (e.g. struct fields).
657+
///
658+
/// For details, see <https://github.com/model-checking/kani/pull/1338>
659+
pub fn codegen_func_expr(&mut self, instance: Instance<'tcx>, span: Option<&Span>) -> Expr {
660+
let (func_symbol, func_typ) = self.codegen_func_symbol(instance);
661+
Expr::symbol_expression(func_symbol.name, func_typ)
662+
.with_location(self.codegen_span_option(span.cloned()))
663+
}
664+
665+
/// For a given function instance, generates a zero-sized dummy symbol of type `Struct`.
666+
///
667+
/// This is often necessary because GotoC does not allow functions to be used in all contexts (e.g. struct fields).
668+
/// For details, see <https://github.com/model-checking/kani/pull/1338>
669+
///
670+
/// Note: use `codegen_func_expr` instead if you want to call the function immediately.
671+
fn codegen_fn_item(&mut self, instance: Instance<'tcx>, span: Option<&Span>) -> Expr {
672+
let (func_symbol, _) = self.codegen_func_symbol(instance);
673+
let func = func_symbol.name;
674+
let fn_struct_ty = self.codegen_fndef_type(instance);
675+
// This zero-sized object that a function name refers to in Rust is globally unique, so we create such a global object.
676+
let fn_singleton_name = format!("{func}::FnDefSingleton");
677+
let fn_singleton = self.ensure_global_var(
678+
&fn_singleton_name,
679+
false,
680+
fn_struct_ty.clone(),
681+
Location::none(),
682+
|_, _| None, // zero-sized, so no initialization necessary
683+
);
684+
fn_singleton.with_location(self.codegen_span_option(span.cloned()))
645685
}
646686
}

kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,18 @@ impl<'tcx> GotocCtx<'tcx> {
696696
t: Ty<'tcx>,
697697
) -> Expr {
698698
match k {
699-
PointerCast::ReifyFnPointer => self.codegen_operand(o).address_of(),
699+
PointerCast::ReifyFnPointer => match self.operand_ty(o).kind() {
700+
ty::FnDef(def_id, substs) => {
701+
let instance =
702+
Instance::resolve(self.tcx, ty::ParamEnv::reveal_all(), *def_id, substs)
703+
.unwrap()
704+
.unwrap();
705+
// We need to handle this case in a special way because `codegen_operand` compiles FnDefs to dummy structs.
706+
// (cf. the function documentation)
707+
self.codegen_func_expr(instance, None).address_of()
708+
}
709+
_ => unreachable!(),
710+
},
700711
PointerCast::UnsafeFnPointer => self.codegen_operand(o),
701712
PointerCast::ClosureFnPointer(_) => {
702713
let dest_typ = self.codegen_ty(t);

kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,9 @@ impl<'tcx> GotocCtx<'tcx> {
431431
| InstanceDef::ReifyShim(..)
432432
| InstanceDef::ClosureOnceShim { .. }
433433
| InstanceDef::CloneShim(..) => {
434-
let func_exp = self.codegen_operand(func);
434+
// We need to handle FnDef items in a special way because `codegen_operand` compiles them to dummy structs.
435+
// (cf. the function documentation)
436+
let func_exp = self.codegen_func_expr(instance, None);
435437
vec![
436438
self.codegen_expr_to_place(destination, func_exp.call(fargs))
437439
.with_location(loc),

kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -611,9 +611,12 @@ impl<'tcx> GotocCtx<'tcx> {
611611
ty::Slice(e) => self.codegen_ty(*e).flexible_array_of(),
612612
ty::Str => Type::c_char().flexible_array_of(),
613613
ty::Ref(_, t, _) | ty::RawPtr(ty::TypeAndMut { ty: t, .. }) => self.codegen_ty_ref(*t),
614-
ty::FnDef(_, _) => {
615-
let sig = self.monomorphize(ty.fn_sig(self.tcx));
616-
self.codegen_function_sig(sig)
614+
ty::FnDef(def_id, substs) => {
615+
let instance =
616+
Instance::resolve(self.tcx, ty::ParamEnv::reveal_all(), *def_id, substs)
617+
.unwrap()
618+
.unwrap();
619+
self.codegen_fndef_type(instance)
617620
}
618621
ty::FnPtr(sig) => self.codegen_function_sig(*sig).to_pointer(),
619622
ty::Closure(_, subst) => self.codegen_ty_closure(ty, subst),
@@ -989,6 +992,22 @@ impl<'tcx> GotocCtx<'tcx> {
989992
}
990993
}
991994

995+
/// Creates a zero-sized struct for a FnDef.
996+
///
997+
/// A FnDef instance in Rust is a zero-sized type, which can be passed around directly, without creating a pointer.
998+
/// (Rust docs: <https://doc.rust-lang.org/reference/types/function-item.html>)
999+
/// To mirror this in GotoC, we create a dummy struct for the function, similarly to what we do for closures.
1000+
///
1001+
/// For details, see <https://github.com/model-checking/kani/pull/1338>
1002+
pub fn codegen_fndef_type(&mut self, instance: Instance<'tcx>) -> Type {
1003+
let func = self.symbol_name(instance);
1004+
self.ensure_struct(
1005+
format!("{func}::FnDefStruct"),
1006+
Some(self.readable_instance_name(instance)),
1007+
|_, _| vec![],
1008+
)
1009+
}
1010+
9921011
/// codegen for struct
9931012
///
9941013
/// they are literally codegen'ed in the corresponding way (except the order of fields might not be preserved)
@@ -1374,7 +1393,6 @@ impl<'tcx> GotocCtx<'tcx> {
13741393
/// Whether a variable of type ty should be ignored as a parameter to a function
13751394
pub fn ignore_var_ty(&self, ty: Ty<'tcx>) -> bool {
13761395
match ty.kind() {
1377-
ty::FnDef(_, _) => true,
13781396
// Ignore variables of the generator type until we add support for
13791397
// them:
13801398
// https://github.com/model-checking/kani/issues/416
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// size_of is not supported yet:
5+
#[kani::proof]
6+
fn assert_fndef_zst() {
7+
assert_eq!(std::mem::size_of_val(&h), 0);
8+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! Examples discovered while debugging issue https://github.com/model-checking/kani/issues/1243
5+
6+
#[kani::proof]
7+
fn example1() {
8+
f(h);
9+
}
10+
11+
fn f(g: impl Fn() -> ()) {
12+
move || g();
13+
}
14+
15+
fn h() {}
16+
17+
#[kani::proof]
18+
fn example2() {
19+
std::iter::empty().try_fold(0, map_try_fold(&mut |x: usize| x, usize::checked_add));
20+
}
21+
22+
fn map_try_fold<'a, T, B, Acc, R>(
23+
f: &'a mut impl FnMut(T) -> B,
24+
mut g: impl FnMut(Acc, B) -> R + 'a,
25+
) -> impl FnMut(Acc, T) -> R + 'a {
26+
move |acc, elt| g(acc, f(elt))
27+
}
28+
29+
#[kani::proof]
30+
fn example3() {
31+
Vec::<String>::new().join("");
32+
}

tests/kani/FunctionSymbols/main.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! Test code generation for FnDef items
5+
6+
#[kani::proof]
7+
fn test_reify_fn_pointer() {
8+
assert!(poly::<usize> as fn() == poly::<usize> as fn());
9+
assert!(poly::<isize> as fn() != poly::<usize> as fn());
10+
}
11+
12+
fn poly<T>() {}
13+
14+
#[kani::proof]
15+
fn test_fn_pointer_call() {
16+
let x: bool = kani::any();
17+
assert_eq!(id(x), x);
18+
assert_eq!((id::<bool> as fn(bool) -> bool)(x), x);
19+
}
20+
21+
fn id<T>(x: T) -> T {
22+
x
23+
}
24+
25+
struct Wrapper<T> {
26+
inner: T,
27+
}
28+
29+
#[kani::proof]
30+
fn test_fn_wrapper() {
31+
let w = Wrapper { inner: id::<bool> };
32+
assert!(w.inner as fn(bool) -> bool == id::<bool> as fn(bool) -> bool);
33+
let x: bool = kani::any();
34+
assert_eq!((w.inner)(x), x);
35+
}

0 commit comments

Comments
 (0)