-
Notifications
You must be signed in to change notification settings - Fork 144
Add support to casting closure to function pointer #2124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d4af992
f088d23
c2c3ab9
f498c56
dc159af
b7a554f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,9 +33,8 @@ impl<'tcx> GotocCtx<'tcx> { | |
| StatementKind::Assign(box (l, r)) => { | ||
| let lty = self.place_ty(l); | ||
| let rty = self.rvalue_ty(r); | ||
| let llayout = self.layout_of(lty); | ||
| // we ignore assignment for all zero size types | ||
| if llayout.is_zst() { | ||
| if self.is_zst(lty) { | ||
|
celinval marked this conversation as resolved.
|
||
| Stmt::skip(location) | ||
| } else if lty.is_fn_ptr() && rty.is_fn() && !rty.is_fn_ptr() { | ||
| // implicit address of a function pointer, e.g. | ||
|
|
@@ -402,10 +401,19 @@ impl<'tcx> GotocCtx<'tcx> { | |
| } | ||
| } | ||
|
|
||
| /// As part of **calling** a function (closure actually), we may need to un-tuple arguments. | ||
| /// As part of **calling** a function (or closure), we may need to un-tuple arguments. | ||
| /// | ||
| /// See [GotocCtx::ty_needs_closure_untupled] | ||
| fn codegen_untuple_closure_args( | ||
| /// This function will replace the last `fargs` argument by its un-tupled version. | ||
| /// | ||
| /// Some context: A closure / shim takes two arguments: | ||
| /// 0. a struct (or a pointer to) representing the environment | ||
| /// 1. a tuple containing the parameters (if not empty) | ||
| /// | ||
| /// However, Rust generates a function where the tuple of parameters are flattened | ||
| /// as subsequent parameters. | ||
| /// | ||
| /// See [GotocCtx::ty_needs_untupled_args] for more details. | ||
| fn codegen_untupled_args( | ||
| &mut self, | ||
| instance: Instance<'tcx>, | ||
| fargs: &mut Vec<Expr>, | ||
|
|
@@ -416,32 +424,22 @@ impl<'tcx> GotocCtx<'tcx> { | |
| 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 environment 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. | ||
| // Note 2: For empty closures, the only argument needed is the environment struct. | ||
| if !fargs.is_empty() { | ||
| let tuple_ty = self.operand_ty(last_mir_arg.unwrap()); | ||
| if self.is_zst(tuple_ty) { | ||
| // Don't pass anything if all tuple elements are ZST. | ||
| // ZST arguments are ignored. | ||
| return; | ||
| } | ||
| let tupe = fargs.remove(fargs.len() - 1); | ||
| let tupled_args: Vec<Type> = match self.operand_ty(last_mir_arg.unwrap()).kind() { | ||
| ty::Tuple(tupled_args) => tupled_args.iter().map(|s| self.codegen_ty(s)).collect(), | ||
| _ => unreachable!("Argument to function with Abi::RustCall is not a tuple"), | ||
| }; | ||
|
|
||
| // Unwrap as needed | ||
| for (i, _) in tupled_args.iter().enumerate() { | ||
| // Access the tupled parameters through the `member` operation | ||
| let index_param = tupe.clone().member(&i.to_string(), &self.symbol_table); | ||
| fargs.push(index_param); | ||
| if let ty::Tuple(tupled_args) = tuple_ty.kind() { | ||
| for (idx, arg_ty) in tupled_args.iter().enumerate() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this ordering deterministic?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't dive deep into that since we already had this assumption in this code. I just merged the two loops together. |
||
| if !self.is_zst(arg_ty) { | ||
| // Access the tupled parameters through the `member` operation | ||
| let idx_expr = tupe.clone().member(&idx.to_string(), &self.symbol_table); | ||
| fargs.push(idx_expr); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -459,16 +457,30 @@ impl<'tcx> GotocCtx<'tcx> { | |
| /// Generate Goto-C for each argument to a function call. | ||
| /// | ||
| /// N.B. public only because instrinsics use this directly, too. | ||
| pub(crate) fn codegen_funcall_args(&mut self, args: &[Operand<'tcx>]) -> Vec<Expr> { | ||
| args.iter() | ||
| .map(|o| { | ||
| if self.operand_ty(o).is_bool() { | ||
| self.codegen_operand(o).cast_to(Type::c_bool()) | ||
| /// When `skip_zst` is set to `true`, the return value will not include any argument that is ZST. | ||
| /// This is used because we ignore ZST arguments, except for intrinsics. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why can't we ignore them for instrinsics?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because our intrinsics implementation have assumptions about the number of parameters. BTW, I think this is also true for other backends. |
||
| pub(crate) fn codegen_funcall_args( | ||
| &mut self, | ||
| args: &[Operand<'tcx>], | ||
| skip_zst: bool, | ||
|
celinval marked this conversation as resolved.
|
||
| ) -> Vec<Expr> { | ||
| let fargs = args | ||
| .iter() | ||
| .filter_map(|o| { | ||
| let op_ty = self.operand_ty(o); | ||
| if op_ty.is_bool() { | ||
| Some(self.codegen_operand(o).cast_to(Type::c_bool())) | ||
| } else if !self.is_zst(op_ty) || !skip_zst { | ||
| Some(self.codegen_operand(o)) | ||
| } else { | ||
| self.codegen_operand(o) | ||
| // We ignore ZST types. | ||
| debug!(arg=?o, "codegen_funcall_args ignore"); | ||
| None | ||
| } | ||
| }) | ||
| .collect() | ||
| .collect(); | ||
| debug!(?fargs, "codegen_funcall_args"); | ||
| fargs | ||
| } | ||
|
|
||
| /// Generates Goto-C for a MIR [TerminatorKind::Call] statement. | ||
|
|
@@ -498,16 +510,17 @@ impl<'tcx> GotocCtx<'tcx> { | |
|
|
||
| let loc = self.codegen_span(&span); | ||
| let funct = self.operand_ty(func); | ||
| let mut fargs = self.codegen_funcall_args(args); | ||
| let mut fargs = self.codegen_funcall_args(args, true); | ||
| match &funct.kind() { | ||
| ty::FnDef(defid, subst) => { | ||
| let instance = | ||
| Instance::resolve(self.tcx, ty::ParamEnv::reveal_all(), *defid, subst) | ||
| .unwrap() | ||
| .unwrap(); | ||
|
|
||
| if self.ty_needs_closure_untupled(funct) { | ||
| self.codegen_untuple_closure_args(instance, &mut fargs, args.last()); | ||
| // TODO(celina): Move this check to be inside codegen_funcall_args. | ||
| if self.ty_needs_untupled_args(funct) { | ||
| self.codegen_untupled_args(instance, &mut fargs, args.last()); | ||
| } | ||
|
|
||
| if let Some(hk) = self.hooks.hook_applies(self.tcx, instance) { | ||
|
|
@@ -608,6 +621,7 @@ impl<'tcx> GotocCtx<'tcx> { | |
| ) -> Vec<Stmt> { | ||
| let vtable_field_name = self.vtable_field_name(def_id, idx); | ||
| trace!(?self_ty, ?place, ?vtable_field_name, "codegen_virtual_funcall"); | ||
| debug!(?fargs, "codegen_virtual_funcall"); | ||
|
|
||
| let trait_fat_ptr = self.extract_ptr(fargs[0].clone(), self_ty); | ||
| assert!( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is
tyvstypea standard convention to distinguish between MIR and GOTO?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a standard convention. I tend to used it often when I need to use both in the same function though.
I think it's much better than using
tfor both though. I think we should avoid shadowing variables, especially when they represent different things, not just a different state of same thing.Do you have any other suggestion?