diff --git a/compiler/rustc_codegen_llvm/src/gotoc/metadata.rs b/compiler/rustc_codegen_llvm/src/gotoc/metadata.rs index 24f0d50ada8c..577fb4153a05 100644 --- a/compiler/rustc_codegen_llvm/src/gotoc/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/gotoc/metadata.rs @@ -255,13 +255,37 @@ impl<'tcx> GotocCtx<'tcx> { self.monomorphize(p.ty(self.current_fn().mir().local_decls(), self.tcx).ty) } - pub fn closure_params(&self, substs: ty::subst::SubstsRef<'tcx>) -> Vec> { - let sig = self.monomorphize(substs.as_closure().sig()); - let args = match sig.skip_binder().inputs()[0].kind() { - ty::Tuple(substs) => substs.iter().map(|s| s.expect_ty()), - _ => unreachable!("this argument of a closure must be a tuple"), - }; - args.collect() + /// Closures expect their last arg untupled at call site, see comment at + /// ty_needs_closure_untupled. + fn sig_with_closure_untupled(&self, sig: ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> { + debug!("sig_with_closure_untupled sig: {:?}", sig); + let fn_sig = sig.skip_binder(); + if let Some((tupe, prev_args)) = fn_sig.inputs().split_last() { + let args: Vec> = match tupe.kind() { + ty::Tuple(substs) => substs.iter().map(|s| s.expect_ty()), + _ => unreachable!("the final argument of a closure must be a tuple"), + } + .collect(); + + // The leading argument should be exactly the environment + assert!(prev_args.len() == 1); + let env = prev_args[0].clone(); + + // Recombine arguments: environment first, then the flattened tuple elements + let recombined_args = iter::once(env).chain(args); + + return ty::Binder::bind_with_vars( + self.tcx.mk_fn_sig( + recombined_args, + fn_sig.output(), + fn_sig.c_variadic, + fn_sig.unsafety, + fn_sig.abi, + ), + sig.bound_vars(), + ); + } + sig } fn closure_sig( @@ -283,30 +307,41 @@ impl<'tcx> GotocCtx<'tcx> { let env_region = ty::ReLateBound(ty::INNERMOST, br); let env_ty = self.tcx.closure_env_ty(def_id, substs, env_region).unwrap(); - // The parameter types are tupled, but we want to have them in a vector - let params = self.closure_params(substs); let sig = sig.skip_binder(); // We build a binder from `sig` where: // * `inputs` contains a sequence with the closure and parameter types // * the rest of attributes are obtained from `sig` - ty::Binder::bind_with_vars( + let sig = ty::Binder::bind_with_vars( self.tcx.mk_fn_sig( - iter::once(env_ty).chain(params.iter().cloned()), + iter::once(env_ty).chain(iter::once(sig.inputs()[0])), sig.output(), sig.c_variadic, sig.unsafety, sig.abi, ), bound_vars, - ) + ); + + // The parameter types are tupled, but we want to have them in a vector + self.sig_with_closure_untupled(sig) } pub fn fn_sig_of_instance(&self, instance: Instance<'tcx>) -> ty::PolyFnSig<'tcx> { let fntyp = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); self.monomorphize(match fntyp.kind() { ty::Closure(def_id, subst) => self.closure_sig(*def_id, subst), - _ => fntyp.fn_sig(self.tcx), + ty::FnPtr(..) | ty::FnDef(..) => { + let sig = fntyp.fn_sig(self.tcx); + // Some virtual calls through a vtable may actually be closures + // or shims that also need the arguments untupled, even though + // the kind of the trait type is not a ty::Closure. + if self.ty_needs_closure_untupled(fntyp) { + return self.sig_with_closure_untupled(sig); + } + sig + } + _ => unreachable!("Can't get function signature of type: {:?}", fntyp), }) } diff --git a/compiler/rustc_codegen_llvm/src/gotoc/mod.rs b/compiler/rustc_codegen_llvm/src/gotoc/mod.rs index a7748efe5c4b..dd461c56c8cf 100644 --- a/compiler/rustc_codegen_llvm/src/gotoc/mod.rs +++ b/compiler/rustc_codegen_llvm/src/gotoc/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use bitflags::_core::any::Any; use cbmc::goto_program::symtab_transformer; -use cbmc::goto_program::{Stmt, Symbol, SymbolTable}; +use cbmc::goto_program::{Expr, Stmt, Symbol, SymbolTable}; use cbmc::{MachineModel, RoundingMode}; use metadata::*; use rustc_codegen_ssa::traits::CodegenBackend; @@ -12,10 +12,10 @@ use rustc_hir::def_id::DefId; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoaderDyn}; use rustc_middle::mir::mono::{CodegenUnit, MonoItem}; -use rustc_middle::mir::{BasicBlock, BasicBlockData, Body, HasLocalDecls}; +use rustc_middle::mir::{BasicBlock, BasicBlockData, Body, HasLocalDecls, Local}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, Instance, TyCtxt}; +use rustc_middle::ty::{self, Instance, TyCtxt, TyS}; use rustc_serialize::json::ToJson; use rustc_session::config::{OutputFilenames, OutputType}; use rustc_session::Session; @@ -135,10 +135,12 @@ impl<'tcx> GotocCtx<'tcx> { let mir = self.current_fn().mir(); let ldecls = mir.local_decls(); ldecls.indices().for_each(|lc| { - let base_name = match self.find_debug_info(&lc) { - None => format!("var_{}", lc.index()), - Some(info) => format!("{}", info.name), - }; + if Some(lc) == mir.spread_arg { + // We have already added this local in the function prelude, so + // skip adding it again here. + return; + } + let base_name = self.codegen_var_base_name(&lc); let name = self.codegen_var_name(&lc); let ldata = &ldecls[lc]; let t = self.monomorphize(ldata.ty); @@ -149,13 +151,96 @@ impl<'tcx> GotocCtx<'tcx> { let sym_e = sym.to_expr(); self.symbol_table.insert(sym); - // declare local variables. 0 represents return value + // Index 0 represents the return value, which does not need to be + // declared in the first block if lc.index() < 1 || lc.index() > mir.arg_count { self.current_fn_mut().push_onto_block(Stmt::decl(sym_e, None, loc)); } }); } + /// MIR functions have a `spread_arg` field that specifies whether the + /// final argument to the function is "spread" at the LLVM/codegen level + /// from a tuple into its individual components. (Used for the "rust- + /// call" ABI, necessary because dynamic trait closure cannot have an + /// argument list in MIR that is both generic and variadic, so Rust + /// allows a generic tuple). + /// + /// If `spread_arg` is Some, then the wrapped value is the local that is + /// to be "spread"/untupled. However, the MIR function body itself expects + /// the tuple instead of the individual components, so we need to generate + /// a function prelude that _retuples_, that is, writes the components + /// back to the tuple local for use in the body. + /// + /// See: + /// https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/Determine.20untupled.20closure.20args.20from.20Instance.3F + fn codegen_function_prelude(&mut self) { + let mir = self.current_fn().mir(); + if mir.spread_arg.is_none() { + // No special tuple argument, no work to be done. + return; + } + let spread_arg = mir.spread_arg.unwrap(); + let spread_data = &mir.local_decls()[spread_arg]; + let loc = self.codegen_span2(&spread_data.source_info.span); + + // When we codegen the function signature elsewhere, we will codegen the + // untupled version. So, the tuple argument itself needs to have a + // symbol declared for it outside of the function signature, we do that + // here. + let tup_typ = self.codegen_ty(self.monomorphize(spread_data.ty)); + let tup_sym = Symbol::variable( + self.codegen_var_name(&spread_arg), + self.codegen_var_base_name(&spread_arg), + tup_typ.clone(), + loc.clone(), + ); + self.symbol_table.insert(tup_sym.clone()); + + // Get the function signature from MIR, _before_ we untuple + let fntyp = self.current_fn().instance().ty(self.tcx, ty::ParamEnv::reveal_all()); + let sig = match fntyp.kind() { + ty::FnPtr(..) | ty::FnDef(..) => fntyp.fn_sig(self.tcx).skip_binder(), + // Closures themselves will have their arguments already untupled, + // see Zulip link above. + ty::Closure(..) => unreachable!( + "Unexpected `spread arg` set for closure, got: {:?}, {:?}", + fntyp, + self.current_fn().readable_name() + ), + _ => unreachable!( + "Expected function type for `spread arg` prelude, got: {:?}, {:?}", + fntyp, + self.current_fn().readable_name() + ), + }; + + // Now that we have the tuple, write the individual component locals + // back to it as a GotoC struct. + let tupe = sig.inputs().last().unwrap(); + let args: Vec<&TyS<'tcx>> = match tupe.kind() { + ty::Tuple(substs) => substs.iter().map(|s| s.expect_ty()).collect(), + _ => unreachable!("a function's spread argument must be a tuple"), + }; + + // Convert each arg to a GotoC expression. + let mut arg_exprs = Vec::new(); + let starting_idx = sig.inputs().len(); + for (arg_i, arg_t) in args.iter().enumerate() { + // The components come at the end, so offset by the untupled length. + let lc = Local::from_usize(arg_i + starting_idx); + let (name, base_name) = self.codegen_spread_arg_name(&lc); + let sym = Symbol::variable(name, base_name, self.codegen_ty(arg_t), loc.clone()); + self.symbol_table.insert(sym.clone()); + arg_exprs.push(sym.to_expr()); + } + + // Finally, combine the expression into a struct. + let tuple_expr = Expr::struct_expr_from_values(tup_typ, arg_exprs, &self.symbol_table) + .with_location(loc.clone()); + self.current_fn_mut().push_onto_block(Stmt::decl(tup_sym.to_expr(), Some(tuple_expr), loc)); + } + /// collect all labels for goto fn codegen_prepare_blocks(&self) -> Vec { self.current_fn().mir().basic_blocks().indices().map(|bb| format!("{:?}", bb)).collect() @@ -202,6 +287,7 @@ impl<'tcx> GotocCtx<'tcx> { self.print_instance(instance, mir); let labels = self.codegen_prepare_blocks(); self.current_fn_mut().set_labels(labels); + self.codegen_function_prelude(); self.codegen_declare_variables(); mir.basic_blocks().iter_enumerated().for_each(|(bb, bbd)| self.codegen_block(bb, bbd)); diff --git a/compiler/rustc_codegen_llvm/src/gotoc/statement.rs b/compiler/rustc_codegen_llvm/src/gotoc/statement.rs index b3d6e661504a..2843af6dfb4b 100644 --- a/compiler/rustc_codegen_llvm/src/gotoc/statement.rs +++ b/compiler/rustc_codegen_llvm/src/gotoc/statement.rs @@ -195,31 +195,50 @@ impl<'tcx> GotocCtx<'tcx> { } } - fn codegen_adjust_arguments(&mut self, fargs: &mut Vec, instance: Instance<'tcx>) { - let ins_ty = self.monomorphize(instance.ty(self.tcx, ty::ParamEnv::reveal_all())); - debug!("codegen_adjust_arguments instance: {:?} ins_ty: {:?}", instance, ins_ty); - if let ty::Closure(_, substs) = ins_ty.kind() { - // a closure takes two argument: - // 0. a struct representing the environment - // 1. a tuple containing the parameters - // - // however, for some reason, Rust decides to generate a function which still - // takes the first argument as the environment struct, but the tuple of parameters - // are flattened as subsequent parameters. - // therefore, we have to project out the corresponding fields when we detect - // an invocation of a closure. - // - // In the case where the closure takes no paramaters, rustc appears to elide the - // second (empty) tuple. So we should only expand the tuple if its there. - if fargs.len() > 1 { - assert_eq!(fargs.len(), 2); - let tupe = fargs.remove(1); - for (i, t) in self.closure_params(substs).iter().enumerate() { - if !t.is_unit() { - // Access the tupled parameters through the `member` operation - let index_param = tupe.clone().member(&i.to_string(), &self.symbol_table); - fargs.push(index_param); + fn codegen_untuple_closure_args( + &mut self, + instance: Instance<'tcx>, + fargs: &mut Vec, + last_mir_arg: Option<&Operand<'tcx>>, + ) { + debug!( + "codegen_untuple_closure_args instance: {:?}, fargs {:?}", + self.readable_instance_name(instance), + fargs + ); + // A closure takes two arguments: + // 0. a struct representing the environment + // 1. a tuple containing the parameters + // + // However, for some reason, Rust decides to generate a function which still + // takes the first argument as the environment struct, but the tuple of parameters + // are flattened as subsequent parameters. + // Therefore, we have to project out the corresponding fields when we detect + // an invocation of a closure. + // + // Note: In some cases, the enviroment struct has type FnDef, so we skip it in + // ignore_var_ty. So the tuple is always the last arg, but it might be in the + // first or the second position. + if fargs.len() > 0 { + let tupe = fargs.remove(fargs.len() - 1); + let tupled_args: Vec = match self.operand_ty(last_mir_arg.unwrap()).kind() { + ty::Tuple(tupled_args) => { + // The tuple needs to be added back for type checking even if empty + if tupled_args.is_empty() { + fargs.push(tupe); + return; } + tupled_args.iter().map(|s| self.codegen_ty(s.expect_ty())).collect() + } + _ => unreachable!("Argument to function with Abi::RustCall is not a tuple"), + }; + + // Unwrap as needed + for (i, t) in tupled_args.iter().enumerate() { + if !t.is_unit() { + // Access the tupled parameters through the `member` operation + let index_param = tupe.clone().member(&i.to_string(), &self.symbol_table); + fargs.push(index_param); } } } @@ -253,7 +272,10 @@ impl<'tcx> GotocCtx<'tcx> { Instance::resolve(self.tcx, ty::ParamEnv::reveal_all(), *defid, subst) .unwrap() .unwrap(); - self.codegen_adjust_arguments(&mut fargs, instance); + + if self.ty_needs_closure_untupled(funct) { + self.codegen_untuple_closure_args(instance, &mut fargs, args.last()); + } if let Some(hk) = self.hooks.hook_applies(self.tcx, instance) { return hk.handle( diff --git a/compiler/rustc_codegen_llvm/src/gotoc/typ.rs b/compiler/rustc_codegen_llvm/src/gotoc/typ.rs index ba704baa8e93..89afa628917c 100644 --- a/compiler/rustc_codegen_llvm/src/gotoc/typ.rs +++ b/compiler/rustc_codegen_llvm/src/gotoc/typ.rs @@ -6,7 +6,7 @@ use super::metadata::GotocCtx; use crate::btree_map; use rustc_ast::ast::Mutability; use rustc_index::vec::IndexVec; -use rustc_middle::mir::{HasLocalDecls, Local}; +use rustc_middle::mir::Local; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::FmtPrinter; use rustc_middle::ty::subst::InternalSubsts; @@ -16,8 +16,10 @@ use rustc_middle::ty::{ use rustc_span; use rustc_span::def_id::DefId; use rustc_target::abi::{ - Abi, FieldsShape, Integer, Layout, LayoutOf, Primitive, TagEncoding, VariantIdx, Variants, + Abi::Vector, FieldsShape, Integer, Layout, LayoutOf, Primitive, TagEncoding, VariantIdx, + Variants, }; +use rustc_target::spec::abi::Abi; use std::collections::BTreeMap; use std::convert::TryInto; use std::fmt::Debug; @@ -120,19 +122,17 @@ impl<'tcx> GotocCtx<'tcx> { instance: Instance<'tcx>, idx: usize, ) -> DatatypeComponent { - // gives a binder with function signature + // Gives a binder with function signature let sig = self.fn_sig_of_instance(instance); - // gives an Irep Pointer object for the signature - let fnptr = self.codegen_dynamic_function_sig(sig).to_pointer(); + // Gives an Irep Pointer object for the signature + let fn_ty = self.codegen_dynamic_function_sig(sig); + let fn_ptr = fn_ty.to_pointer(); // vtable field name, i.e., 3_vol (idx_method) let vtable_field_name = self.vtable_field_name(instance.def_id(), idx); - let ins_ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); - let _layout = self.layout_of(ins_ty); - - Type::datatype_component(&vtable_field_name, fnptr) + Type::datatype_component(&vtable_field_name, fn_ptr) } /// Generates a vtable that looks like this: @@ -513,8 +513,34 @@ impl<'tcx> GotocCtx<'tcx> { self.codegen_struct_fields(flds, layout.layout, 0) } - /// a closure is a struct of all its environments - /// that is, a closure is just a tuple with a unique type identifier, so that Fn related traits + /// A closure in Rust MIR takes two arguments: + /// 0. a struct representing the environment + /// 1. a tuple containing the parameters + /// + /// However, during codegen/lowering from MIR, the 2nd tuple of parameters + /// is flattened into subsequent parameters. + /// + /// Checking whether the type's kind is a closure is insufficient, because + /// a virtual method call through a vtable can have the trait's non-closure + /// type. For example: + /// let p: &dyn Fn(i32) = &|x| assert!(x == 1); + /// p(1); + /// + /// Here, the call p(1) desugars to an MIR trait call Fn::call(&p, (1,)), + /// where the second argument is a tuple. The instance type kind for + /// Fn::call is not a closure, because dynamically, the pointer may be to + /// a function definition instead. We still need to untuple in this case, + /// so we follow the example elsewhere in Rust to use the ABI call type. + /// See `make_call_args` in rmc/compiler/rustc_mir/src/transform/inline.rs + pub fn ty_needs_closure_untupled(&self, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::FnDef(..) | ty::FnPtr(..) => ty.fn_sig(self.tcx).abi() == Abi::RustCall, + _ => unreachable!("Can't treat type as a function: {:?}", ty), + } + } + + /// A closure is a struct of all its environments. That is, a closure is + /// just a tuple with a unique type identifier, so that Fn related traits /// can find its impl. fn codegen_ty_closure(&mut self, t: Ty<'tcx>, substs: ty::subst::SubstsRef<'tcx>) -> Type { self.ensure_struct(&self.ty_mangled_name(t), |ctx, _| { @@ -617,6 +643,7 @@ impl<'tcx> GotocCtx<'tcx> { pub fn codegen_dynamic_function_sig(&mut self, sig: PolyFnSig<'tcx>) -> Type { let sig = self.monomorphize(sig); let sig = self.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig); + let mut is_first = true; let params = sig .inputs() @@ -938,7 +965,7 @@ impl<'tcx> GotocCtx<'tcx> { debug! {"handling simd with layout {:?}", layout}; let (element, size) = match layout { - Abi::Vector { element, count } => (element.clone(), *count), + Vector { element, count } => (element.clone(), *count), _ => unreachable!(), }; @@ -951,7 +978,6 @@ impl<'tcx> GotocCtx<'tcx> { /// the function type of the current instance pub fn fn_typ(&mut self) -> Type { - let mir = self.current_fn().mir(); let sig = self.current_fn().sig(); let sig = self.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig); // we don't call [codegen_function_sig] because we want to get a bit more metainformation. @@ -963,11 +989,21 @@ impl<'tcx> GotocCtx<'tcx> { if self.ignore_var_ty(t) { None } else { - let l = Local::from_usize(i + 1); - let t = *t; - let _ld = &mir.local_decls()[l]; - let ident = self.codegen_var_name(&l); - Some(Type::parameter(Some(ident.to_string()), Some(ident), self.codegen_ty(t))) + let lc = Local::from_usize(i + 1); + let mut ident = self.codegen_var_name(&lc); + + // `spread_arg` indicates that the last argument is tupled + // at the LLVM/codegen level, so we need to declare the indivual + // components as parameters with a special naming convention + // so that we can "retuple" them in the function prelude. + // See: compiler/rustc_codegen_llvm/src/gotoc/mod.rs:codegen_function_prelude + if let Some(spread) = self.current_fn().mir().spread_arg { + if lc.index() >= spread.index() { + let (name, _) = self.codegen_spread_arg_name(&lc); + ident = name; + } + } + Some(Type::parameter(Some(ident.to_string()), Some(ident), self.codegen_ty(*t))) } }) .collect(); diff --git a/compiler/rustc_codegen_llvm/src/gotoc/utils.rs b/compiler/rustc_codegen_llvm/src/gotoc/utils.rs index ded9cc97385d..e1945f4aca99 100644 --- a/compiler/rustc_codegen_llvm/src/gotoc/utils.rs +++ b/compiler/rustc_codegen_llvm/src/gotoc/utils.rs @@ -25,6 +25,23 @@ impl<'tcx> GotocCtx<'tcx> { } } + pub fn codegen_var_base_name(&self, l: &Local) -> String { + match self.find_debug_info(l) { + None => format!("var_{}", l.index()), + Some(info) => format!("{}", info.name), + } + } + + // Special naming conventions for parameters that are spread from a tuple + // into its individual components at the LLVM level, see comment at + // compiler/rustc_codegen_llvm/src/gotoc/mod.rs:codegen_function_prelude + pub fn codegen_spread_arg_name(&self, l: &Local) -> (String, String) { + let fname = self.current_fn().name(); + let base_name = format!("spread{:?}", l); + let name = format!("{}::1::{}", fname, base_name); + (name, base_name) + } + pub fn find_debug_info(&self, l: &Local) -> Option<&VarDebugInfo<'tcx>> { self.current_fn().mir().var_debug_info.iter().find(|info| match info.value { VarDebugInfoContents::Place(p) => p.local == *l && p.projection.len() == 0, diff --git a/src/test/cbmc/DynTrait/boxed_closure.rs b/src/test/cbmc/DynTrait/boxed_closure.rs index 6f2e6042fbfd..8ef183cbbe8f 100644 --- a/src/test/cbmc/DynTrait/boxed_closure.rs +++ b/src/test/cbmc/DynTrait/boxed_closure.rs @@ -5,8 +5,11 @@ fn main() { // Create a boxed once-callable closure - let f: Box = Box::new(|x| assert!(x == 1)); + let f: Box = Box::new(|x, y| { + assert!(x == 1.0); + assert!(y == 2); + }); // Call it - f(1); + f(1.0, 2); } diff --git a/src/test/cbmc/DynTrait/dyn_fn_param.rs b/src/test/cbmc/DynTrait/dyn_fn_param.rs index 51aa618616e0..6dfc62c6bfa9 100644 --- a/src/test/cbmc/DynTrait/dyn_fn_param.rs +++ b/src/test/cbmc/DynTrait/dyn_fn_param.rs @@ -1,13 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR MIT -//rmc-flags: --no-memory-safety-checks - -// We use `--no-memory-safety-checks` in this test to avoid getting -// a verification failure: -// [pointer_dereference.14] invalid function pointer: FAILURE -// Tracking issue: https://github.com/model-checking/rmc/issues/307 - // Check that we can pass a dyn function pointer to a stand alone // function definition diff --git a/src/test/cbmc/DynTrait/nested_closures_fixme.rs b/src/test/cbmc/DynTrait/nested_closures.rs similarity index 64% rename from src/test/cbmc/DynTrait/nested_closures_fixme.rs rename to src/test/cbmc/DynTrait/nested_closures.rs index a4f11ad085c8..70375214f783 100644 --- a/src/test/cbmc/DynTrait/nested_closures_fixme.rs +++ b/src/test/cbmc/DynTrait/nested_closures.rs @@ -4,26 +4,30 @@ // Check that we can codegen various nesting structures of boxes and // pointer to closures. -// FIXME: several cases fail because we need to "retuple" closures, -// see: https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/Determine.20untupled.20closure.20args.20from.20Instance.3F - fn main() { // Create a nested boxed once-callable closure let f: Box> = Box::new(Box::new(|x| assert!(x == 1))); f(1); // Create a pointer to a closure - let g = |y| assert!(y == 2); - let p: &dyn Fn(i32) = &g; - p(2); + let g = |x: f32, y: i32| { + assert!(x == 1.0); + assert!(y == 2) + }; + let p: &dyn Fn(f32, i32) = &g; + p(1.0, 2); // Additional level of pointer nesting - let q: &dyn Fn(i32) = &p; - q(2); + let q: &dyn Fn(f32, i32) = &p; + q(1.0, 2); // Create a boxed pointer to a closure - let r: Box<&dyn Fn(i32)> = Box::new(&g); - r(2); + let r: Box<&dyn Fn(f32, i32, bool)> = Box::new(&|x: f32, y: i32, z: bool| { + assert!(x == 1.0); + assert!(y == 2); + assert!(z); + }); + r(1.0, 2, true); // Another boxed box let s: Box> = Box::new(Box::new(|x| assert!(x == 3))); diff --git a/src/test/cbmc/DynTrait/nested_closures_fail.rs b/src/test/cbmc/DynTrait/nested_closures_fail.rs new file mode 100644 index 000000000000..2b7a6ee36ed6 --- /dev/null +++ b/src/test/cbmc/DynTrait/nested_closures_fail.rs @@ -0,0 +1,45 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Check that we can codegen various nesting structures of boxes and +// pointer to closures. Conditions negated for negative test. + +// rmc-verify-fail + +include!("../../rmc-prelude.rs"); + +fn main() { + // Create a nested boxed once-callable closure + let f: Box> = + Box::new(Box::new(|x| __VERIFIER_expect_fail(x != 1, "wrong int"))); + f(1); + + // Create a pointer to a closure + let g = |x: f32, y: i32| { + __VERIFIER_expect_fail(x != 1.0, "wrong float"); + __VERIFIER_expect_fail(y != 2, "wrong int") + }; + let p: &dyn Fn(f32, i32) = &g; + p(1.0, 2); + + // Additional level of pointer nesting + let q: &dyn Fn(f32, i32) = &p; + q(1.0, 2); + + // Create a boxed pointer to a closure + let r: Box<&dyn Fn(f32, i32, bool)> = Box::new(&|x: f32, y: i32, z: bool| { + __VERIFIER_expect_fail(x != 1.0, "wrong float"); + __VERIFIER_expect_fail(y != 2, "wrong int"); + __VERIFIER_expect_fail(!z, "wrong bool"); + }); + r(1.0, 2, true); + + // Another boxed box + let s: Box> = + Box::new(Box::new(|x| __VERIFIER_expect_fail(x != 3, "wrong int"))); + s(3); + + // A pointer to the boxed box + let t: &Box> = &s; + t(3); +}