Skip to content

Commit a82bad4

Browse files
authored
Fix ICEs due to mismatched arguments (rust-lang#2994)
Use FnAbi instead of function signature when generating code for function types. Properly check the `PassMode::Ignore`. For foreign functions, instead of ignoring the declaration type, cast the arguments and return value. For now, we also ignore the caller location, since we don't currently support tracking caller location. This change makes it easier for us to do so. We might want to wait for this issue to get fixed so we can easily add support using stable APIs: rust-lang/project-stable-mir#62 Resolves rust-lang#2260 Resolves rust-lang#2312 Resolves rust-lang#1365 Resolves rust-lang#1350
1 parent 409a83d commit a82bad4

File tree

10 files changed

+164
-261
lines changed

10 files changed

+164
-261
lines changed

cprover_bindings/src/goto_program/expr.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,13 +508,13 @@ impl Expr {
508508

509509
/// `(typ) self`.
510510
pub fn cast_to(self, typ: Type) -> Self {
511-
assert!(self.can_cast_to(&typ), "Can't cast\n\n{self:?} ({:?})\n\n{typ:?}", self.typ);
512511
if self.typ == typ {
513512
self
514513
} else if typ.is_bool() {
515514
let zero = self.typ.zero();
516515
self.neq(zero)
517516
} else {
517+
assert!(self.can_cast_to(&typ), "Can't cast\n\n{self:?} ({:?})\n\n{typ:?}", self.typ);
518518
expr!(Typecast(self), typ)
519519
}
520520
}
@@ -688,7 +688,8 @@ impl Expr {
688688
pub fn call(self, arguments: Vec<Expr>) -> Self {
689689
assert!(
690690
Expr::typecheck_call(&self, &arguments),
691-
"Function call does not type check:\nfunc: {self:?}\nargs: {arguments:?}"
691+
"Function call does not type check:\nfunc params: {:?}\nargs: {arguments:?}",
692+
self.typ().parameters().unwrap_or(&vec![])
692693
);
693694
let typ = self.typ().return_type().unwrap().clone();
694695
expr!(FunctionCall { function: self, arguments }, typ)

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

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ use std::collections::HashSet;
1111

1212
use crate::codegen_cprover_gotoc::codegen::PropertyClass;
1313
use crate::codegen_cprover_gotoc::GotocCtx;
14-
use crate::kani_middle;
14+
use crate::unwrap_or_return_codegen_unimplemented_stmt;
1515
use cbmc::goto_program::{Expr, Location, Stmt, Symbol, Type};
1616
use cbmc::{InternString, InternedString};
1717
use lazy_static::lazy_static;
18-
use rustc_smir::rustc_internal;
19-
use rustc_target::abi::call::Conv;
18+
use stable_mir::abi::{CallConvention, PassMode};
2019
use stable_mir::mir::mono::Instance;
20+
use stable_mir::mir::Place;
21+
use stable_mir::ty::{RigidTy, TyKind};
2122
use stable_mir::CrateDef;
2223
use tracing::{debug, trace};
2324

@@ -48,14 +49,12 @@ impl<'tcx> GotocCtx<'tcx> {
4849
/// handled later.
4950
pub fn codegen_foreign_fn(&mut self, instance: Instance) -> &Symbol {
5051
debug!(?instance, "codegen_foreign_function");
51-
let instance_internal = rustc_internal::internal(instance);
5252
let fn_name = self.symbol_name_stable(instance).intern();
5353
if self.symbol_table.contains(fn_name) {
5454
// Symbol has been added (either a built-in CBMC function or a Rust allocation function).
5555
self.symbol_table.lookup(fn_name).unwrap()
5656
} else if RUST_ALLOC_FNS.contains(&fn_name)
57-
|| (self.is_cffi_enabled()
58-
&& kani_middle::fn_abi(self.tcx, instance_internal).conv == Conv::C)
57+
|| (self.is_cffi_enabled() && instance.fn_abi().unwrap().conv == CallConvention::C)
5958
{
6059
// Add a Rust alloc lib function as is declared by core.
6160
// When C-FFI feature is enabled, we just trust the rust declaration.
@@ -84,6 +83,40 @@ impl<'tcx> GotocCtx<'tcx> {
8483
}
8584
}
8685

86+
/// Generate a function call to a foreign function by potentially casting arguments and return value, since
87+
/// the external function definition may not match exactly its Rust declaration.
88+
/// See <https://github.com/model-checking/kani/issues/1350#issuecomment-1192036619> for more details.
89+
pub fn codegen_foreign_call(
90+
&mut self,
91+
fn_expr: Expr,
92+
args: Vec<Expr>,
93+
ret_place: &Place,
94+
loc: Location,
95+
) -> Stmt {
96+
let expected_args = fn_expr
97+
.typ()
98+
.parameters()
99+
.unwrap()
100+
.iter()
101+
.zip(args)
102+
.map(|(param, arg)| arg.cast_to(param.typ().clone()))
103+
.collect::<Vec<_>>();
104+
let call_expr = fn_expr.call(expected_args);
105+
106+
let ret_kind = self.place_ty_stable(ret_place).kind();
107+
if ret_kind.is_unit() || matches!(ret_kind, TyKind::RigidTy(RigidTy::Never)) {
108+
call_expr.as_stmt(loc)
109+
} else {
110+
let ret_expr = unwrap_or_return_codegen_unimplemented_stmt!(
111+
self,
112+
self.codegen_place_stable(ret_place)
113+
)
114+
.goto_expr;
115+
let ret_type = ret_expr.typ().clone();
116+
ret_expr.assign(call_expr.cast_to(ret_type), loc)
117+
}
118+
}
119+
87120
/// Checks whether C-FFI has been enabled or not.
88121
/// When enabled, we blindly encode the function type as is.
89122
fn is_cffi_enabled(&self) -> bool {
@@ -102,24 +135,24 @@ impl<'tcx> GotocCtx<'tcx> {
102135
/// Generate type for the given foreign instance.
103136
fn codegen_ffi_type(&mut self, instance: Instance) -> Type {
104137
let fn_name = self.symbol_name_stable(instance);
105-
let fn_abi = kani_middle::fn_abi(self.tcx, rustc_internal::internal(instance));
138+
let fn_abi = instance.fn_abi().unwrap();
106139
let loc = self.codegen_span_stable(instance.def.span());
107140
let params = fn_abi
108141
.args
109142
.iter()
110143
.enumerate()
111-
.filter(|&(_, arg)| (!arg.is_ignore()))
144+
.filter(|&(_, arg)| (arg.mode != PassMode::Ignore))
112145
.map(|(idx, arg)| {
113146
let arg_name = format!("{fn_name}::param_{idx}");
114147
let base_name = format!("param_{idx}");
115-
let arg_type = self.codegen_ty(arg.layout.ty);
148+
let arg_type = self.codegen_ty_stable(arg.ty);
116149
let sym = Symbol::variable(&arg_name, &base_name, arg_type.clone(), loc)
117150
.with_is_parameter(true);
118151
self.symbol_table.insert(sym);
119152
arg_type.as_parameter(Some(arg_name.into()), Some(base_name.into()))
120153
})
121154
.collect();
122-
let ret_type = self.codegen_ty(fn_abi.ret.layout.ty);
155+
let ret_type = self.codegen_ty_stable(fn_abi.ret.ty);
123156

124157
if fn_abi.c_variadic {
125158
Type::variadic_code(params, ret_type)
@@ -140,9 +173,9 @@ impl<'tcx> GotocCtx<'tcx> {
140173
let entry = self.unsupported_constructs.entry("foreign function".into()).or_default();
141174
entry.push(loc);
142175

143-
let call_conv = kani_middle::fn_abi(self.tcx, rustc_internal::internal(instance)).conv;
176+
let call_conv = instance.fn_abi().unwrap().conv;
144177
let msg = format!("call to foreign \"{call_conv:?}\" function `{fn_name}`");
145-
let url = if call_conv == Conv::C {
178+
let url = if call_conv == CallConvention::C {
146179
"https://github.com/model-checking/kani/issues/2423"
147180
} else {
148181
"https://github.com/model-checking/kani/issues/new/choose"

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ impl<'tcx> GotocCtx<'tcx> {
210210
self.ensure(&self.symbol_name_stable(instance), |ctx, fname| {
211211
Symbol::function(
212212
fname,
213-
ctx.fn_typ(&body),
213+
ctx.fn_typ(instance, &body),
214214
None,
215215
instance.name(),
216216
ctx.codegen_span_stable(instance.def.span()),

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

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -574,13 +574,7 @@ impl<'tcx> GotocCtx<'tcx> {
574574
}
575575

576576
/// Ensure that the given instance is in the symbol table, returning the symbol.
577-
///
578-
/// FIXME: The function should not have to return the type of the function symbol as well
579-
/// because the symbol should have the type. The problem is that the type in the symbol table
580-
/// sometimes subtly differs from the type that codegen_function_sig returns.
581-
/// This is tracked in <https://github.com/model-checking/kani/issues/1350>.
582-
fn codegen_func_symbol(&mut self, instance: Instance) -> (&Symbol, Type) {
583-
let funct = self.codegen_function_sig_stable(self.fn_sig_of_instance_stable(instance));
577+
fn codegen_func_symbol(&mut self, instance: Instance) -> &Symbol {
584578
let sym = if instance.is_foreign_item() {
585579
// Get the symbol that represents a foreign instance.
586580
self.codegen_foreign_fn(instance)
@@ -592,7 +586,7 @@ impl<'tcx> GotocCtx<'tcx> {
592586
.lookup(&func)
593587
.unwrap_or_else(|| panic!("Function `{func}` should've been declared before usage"))
594588
};
595-
(sym, funct)
589+
sym
596590
}
597591

598592
/// Generate a goto expression that references the function identified by `instance`.
@@ -601,8 +595,8 @@ impl<'tcx> GotocCtx<'tcx> {
601595
///
602596
/// This should not be used where Rust expects a "function item" (See `codegen_fn_item`)
603597
pub fn codegen_func_expr(&mut self, instance: Instance, span: Option<Span>) -> Expr {
604-
let (func_symbol, func_typ) = self.codegen_func_symbol(instance);
605-
Expr::symbol_expression(func_symbol.name, func_typ)
598+
let func_symbol = self.codegen_func_symbol(instance);
599+
Expr::symbol_expression(func_symbol.name, func_symbol.typ.clone())
606600
.with_location(self.codegen_span_option_stable(span))
607601
}
608602

@@ -612,7 +606,7 @@ impl<'tcx> GotocCtx<'tcx> {
612606
/// This is the Rust "function item". See <https://doc.rust-lang.org/reference/types/function-item.html>
613607
/// This is not the function pointer, for that use `codegen_func_expr`.
614608
fn codegen_fn_item(&mut self, instance: Instance, span: Option<Span>) -> Expr {
615-
let (func_symbol, _) = self.codegen_func_symbol(instance);
609+
let func_symbol = self.codegen_func_symbol(instance);
616610
let mangled_name = func_symbol.name;
617611
let fn_item_struct_ty = self.codegen_fndef_type_stable(instance);
618612
// This zero-sized object that a function name refers to in Rust is globally unique, so we create such a global object.

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ impl<'tcx> GotocCtx<'tcx> {
135135
"https://github.com/model-checking/kani/issues/692",
136136
),
137137
TerminatorKind::Return => {
138-
let rty = self.current_fn().sig().output();
138+
let rty = self.current_fn().instance_stable().fn_abi().unwrap().ret.ty;
139139
if rty.kind().is_unit() {
140140
self.codegen_ret_unit()
141141
} else {
@@ -145,7 +145,8 @@ impl<'tcx> GotocCtx<'tcx> {
145145
self.codegen_place_stable(&place)
146146
)
147147
.goto_expr;
148-
if self.place_ty_stable(&place).kind().is_bool() {
148+
assert_eq!(rty, self.place_ty_stable(&place), "Unexpected return type");
149+
if rty.kind().is_bool() {
149150
place_expr.cast_to(Type::c_bool()).ret(loc)
150151
} else {
151152
place_expr.ret(loc)
@@ -555,10 +556,17 @@ impl<'tcx> GotocCtx<'tcx> {
555556
// We need to handle FnDef items in a special way because `codegen_operand` compiles them to dummy structs.
556557
// (cf. the function documentation)
557558
let func_exp = self.codegen_func_expr(instance, None);
558-
vec![
559-
self.codegen_expr_to_place_stable(destination, func_exp.call(fargs))
559+
if instance.is_foreign_item() {
560+
vec![self.codegen_foreign_call(func_exp, fargs, destination, loc)]
561+
} else {
562+
vec![
563+
self.codegen_expr_to_place_stable(
564+
destination,
565+
func_exp.call(fargs),
566+
)
560567
.with_location(loc),
561-
]
568+
]
569+
}
562570
}
563571
};
564572
stmts.push(self.codegen_end_call(*target, loc));

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use crate::codegen_cprover_gotoc::GotocCtx;
1010
use cbmc::goto_program::Type;
1111
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
12-
use rustc_middle::ty::{self};
1312
use rustc_smir::rustc_internal;
1413
use stable_mir::mir::mono::Instance;
1514
use stable_mir::mir::{Local, Operand, Place, Rvalue};
@@ -53,13 +52,6 @@ impl<'tcx> GotocCtx<'tcx> {
5352
)
5453
}
5554

56-
pub fn fn_sig_of_instance_stable(&self, instance: Instance) -> FnSig {
57-
let fn_sig = self.fn_sig_of_instance(rustc_internal::internal(instance));
58-
let fn_sig =
59-
self.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), fn_sig);
60-
rustc_internal::stable(fn_sig)
61-
}
62-
6355
pub fn use_fat_pointer_stable(&self, pointer_ty: Ty) -> bool {
6456
self.use_fat_pointer(rustc_internal::internal(pointer_ty))
6557
}

0 commit comments

Comments
 (0)