[frontend] Rearrange translation of lambda's body

Summary: Variables captured by reference without initialization are missing dereference in their type inside lambda's body. This causes the translation to miss one dereference. To fix this, we want to add missing reference to the type. However, we first need to make sure that lambdas body is translated after the translation of captured variables.

Reviewed By: jvillard

Differential Revision: D23564099

fbshipit-source-id: 6a2ae053d
master
Daiva Naudziuniene 4 years ago committed by Facebook GitHub Bot
parent 996c1b63e0
commit ff7cd86140

@ -16,7 +16,7 @@ let equal_clang_lang = [%compare.equal: clang_lang]
type translation_unit_context = type translation_unit_context =
{lang: clang_lang; source_file: SourceFile.t; integer_type_widths: Typ.IntegerWidths.t} {lang: clang_lang; source_file: SourceFile.t; integer_type_widths: Typ.IntegerWidths.t}
type decl_trans_context = [`DeclTraversal | `Translation] type decl_trans_context = [`DeclTraversal | `Translation | `CppLambdaExprTranslation]
(** Constants *) (** Constants *)

@ -16,7 +16,7 @@ val equal_clang_lang : clang_lang -> clang_lang -> bool
type translation_unit_context = type translation_unit_context =
{lang: clang_lang; source_file: SourceFile.t; integer_type_widths: Typ.IntegerWidths.t} {lang: clang_lang; source_file: SourceFile.t; integer_type_widths: Typ.IntegerWidths.t}
type decl_trans_context = [`DeclTraversal | `Translation] type decl_trans_context = [`DeclTraversal | `Translation | `CppLambdaExprTranslation]
(** Constants *) (** Constants *)

@ -85,8 +85,8 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
with CFrontend_errors.IncorrectAssumption _ -> () with CFrontend_errors.IncorrectAssumption _ -> ()
let process_method_decl ?(set_objc_accessor_attr = false) ?(is_destructor = false) trans_unit_ctx let process_method_decl ?(inside_cpp_lambda_expr = false) ?(set_objc_accessor_attr = false)
tenv cfg curr_class meth_decl = ?(is_destructor = false) trans_unit_ctx tenv cfg curr_class meth_decl =
try try
let ms, body_opt, extra_instrs = let ms, body_opt, extra_instrs =
let procname = CType_decl.CProcname.from_decl ~tenv meth_decl in let procname = CType_decl.CProcname.from_decl ~tenv meth_decl in
@ -96,11 +96,21 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
| Some body -> | Some body ->
let procname = ms.CMethodSignature.name in let procname = ms.CMethodSignature.name in
let return_param_typ_opt = ms.CMethodSignature.return_param_typ in let return_param_typ_opt = ms.CMethodSignature.return_param_typ in
let is_cpp_lambda_call_operator =
CMethodProperties.is_cpp_lambda_call_operator meth_decl
in
let add_method_if_create_procdesc ms procname ~is_destructor_wrapper = let add_method_if_create_procdesc ms procname ~is_destructor_wrapper =
if if
CMethod_trans.create_local_procdesc ~set_objc_accessor_attr trans_unit_ctx cfg tenv ms (* Do not translate body for lambda operator() if it comes from call expr rather than lambda expr
[body] [] as captured variables will not be translated yet *)
let body_new =
if is_cpp_lambda_call_operator && not inside_cpp_lambda_expr then [] else [body]
in
CMethod_trans.create_local_procdesc ~set_objc_accessor_attr
~is_cpp_lambda_call_operator trans_unit_ctx cfg tenv ms body_new []
then then
if is_cpp_lambda_call_operator && not inside_cpp_lambda_expr then ()
else
add_method trans_unit_ctx tenv cfg curr_class procname body ms return_param_typ_opt add_method trans_unit_ctx tenv cfg curr_class procname body ms return_param_typ_opt
None extra_instrs ~is_destructor_wrapper None extra_instrs ~is_destructor_wrapper
in in
@ -142,11 +152,11 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
() ()
let process_one_method_decl trans_unit_ctx tenv cfg curr_class dec = let process_one_method_decl ~inside_cpp_lambda_expr trans_unit_ctx tenv cfg curr_class dec =
let open Clang_ast_t in let open Clang_ast_t in
match dec with match dec with
| CXXMethodDecl _ | CXXConstructorDecl _ | CXXConversionDecl _ -> | CXXMethodDecl _ | CXXConstructorDecl _ | CXXConversionDecl _ ->
process_method_decl trans_unit_ctx tenv cfg curr_class dec process_method_decl ~inside_cpp_lambda_expr trans_unit_ctx tenv cfg curr_class dec
| CXXDestructorDecl _ -> | CXXDestructorDecl _ ->
process_method_decl trans_unit_ctx tenv cfg curr_class dec ~is_destructor:true process_method_decl trans_unit_ctx tenv cfg curr_class dec ~is_destructor:true
| ObjCMethodDecl _ -> | ObjCMethodDecl _ ->
@ -227,8 +237,11 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
() ()
let process_methods trans_unit_ctx tenv cfg curr_class decl_list = let process_methods ?(inside_cpp_lambda_expr = false) trans_unit_ctx tenv cfg curr_class decl_list
List.iter ~f:(process_one_method_decl trans_unit_ctx tenv cfg curr_class) decl_list =
List.iter
~f:(process_one_method_decl ~inside_cpp_lambda_expr trans_unit_ctx tenv cfg curr_class)
decl_list
(* Here we add an empty dealloc method to every ObjC class if it doesn't have one. Then the implicit (* Here we add an empty dealloc method to every ObjC class if it doesn't have one. Then the implicit
@ -387,13 +400,16 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
| CXXConstructorDecl (decl_info, _, _, _, _) | CXXConstructorDecl (decl_info, _, _, _, _)
| CXXConversionDecl (decl_info, _, _, _, _) | CXXConversionDecl (decl_info, _, _, _, _)
| CXXDestructorDecl (decl_info, _, _, _, _) -> ( | CXXDestructorDecl (decl_info, _, _, _, _) -> (
let inside_cpp_lambda_expr =
match decl_trans_context with `CppLambdaExprTranslation -> true | _ -> false
in
(* di_parent_pointer has pointer to lexical context such as class.*) (* di_parent_pointer has pointer to lexical context such as class.*)
let parent_ptr = Option.value_exn decl_info.Clang_ast_t.di_parent_pointer in let parent_ptr = Option.value_exn decl_info.Clang_ast_t.di_parent_pointer in
let class_decl = CAst_utils.get_decl parent_ptr in let class_decl = CAst_utils.get_decl parent_ptr in
match class_decl with match class_decl with
| (Some (CXXRecordDecl _) | Some (ClassTemplateSpecializationDecl _)) when Config.cxx -> | (Some (CXXRecordDecl _) | Some (ClassTemplateSpecializationDecl _)) when Config.cxx ->
let curr_class = CContext.ContextClsDeclPtr parent_ptr in let curr_class = CContext.ContextClsDeclPtr parent_ptr in
process_methods trans_unit_ctx tenv cfg curr_class [dec] process_methods ~inside_cpp_lambda_expr trans_unit_ctx tenv cfg curr_class [dec]
| Some dec -> | Some dec ->
L.(debug Capture Verbose) L.(debug Capture Verbose)
"Methods of %s skipped@\n" "Methods of %s skipped@\n"

@ -71,7 +71,7 @@ let should_translate translation_unit (loc_start, loc_end) decl_trans_context ~t
|| map_file_of equal_current_source loc_start || map_file_of equal_current_source loc_start
|| (Config.cxx && map_file_of equal_header_of_current_source loc_start) || (Config.cxx && map_file_of equal_header_of_current_source loc_start)
|| Config.cxx || Config.cxx
&& decl_trans_context = `Translation && (decl_trans_context = `Translation || decl_trans_context = `CppLambdaExprTranslation)
&& translate_on_demand && not Config.testing_mode && translate_on_demand && not Config.testing_mode

@ -121,19 +121,13 @@ let get_objc_method_data obj_c_message_expr_info =
(selector, MCStatic) (selector, MCStatic)
let should_create_procdesc ?(captured_vars = []) cfg procname ~defined ~set_objc_accessor_attr = let should_create_procdesc cfg procname ~defined ~set_objc_accessor_attr =
match Procname.Hash.find cfg procname with match Procname.Hash.find cfg procname with
| previous_procdesc -> | previous_procdesc ->
let is_defined_previous = Procdesc.is_defined previous_procdesc in let is_defined_previous = Procdesc.is_defined previous_procdesc in
if (defined || set_objc_accessor_attr) && not is_defined_previous then ( if (defined || set_objc_accessor_attr) && not is_defined_previous then (
Procname.Hash.remove cfg procname ; Procname.Hash.remove cfg procname ;
true ) true )
else if is_defined_previous && not (List.is_empty captured_vars) then (
let new_attributes =
{(Procdesc.get_attributes previous_procdesc) with captured= captured_vars}
in
Procdesc.set_attributes previous_procdesc new_attributes ;
false )
else false else false
| exception Caml.Not_found -> | exception Caml.Not_found ->
true true
@ -195,8 +189,8 @@ let find_sentinel_attribute attrs =
(** Creates a procedure description. *) (** Creates a procedure description. *)
let create_local_procdesc ?(set_objc_accessor_attr = false) ?(update_lambda_captured = false) let create_local_procdesc ?(set_objc_accessor_attr = false) ?(record_lambda_captured = false)
trans_unit_ctx cfg tenv ms fbody captured = ?(is_cpp_lambda_call_operator = false) trans_unit_ctx cfg tenv ms fbody captured =
let defined = not (List.is_empty fbody) in let defined = not (List.is_empty fbody) in
let proc_name = ms.CMethodSignature.name in let proc_name = ms.CMethodSignature.name in
let clang_method_kind = ms.CMethodSignature.method_kind in let clang_method_kind = ms.CMethodSignature.method_kind in
@ -214,6 +208,17 @@ let create_local_procdesc ?(set_objc_accessor_attr = false) ?(update_lambda_capt
let captured_mangled = let captured_mangled =
List.map ~f:(fun (var, t, mode) -> (Pvar.get_name var, t, mode)) captured List.map ~f:(fun (var, t, mode) -> (Pvar.get_name var, t, mode)) captured
in in
(* Retrieve captured variables from procdesc created when translating captured variables in lambda expression *)
(* We want to do this before `should_create_procdesc` is called as it can remove previous procdesc *)
let captured_mangled_not_formals =
if is_cpp_lambda_call_operator then
match Procname.Hash.find cfg proc_name with
| procdesc_prev ->
Procdesc.get_captured procdesc_prev
| exception Caml.Not_found ->
captured_mangled
else captured_mangled
in
let create_new_procdesc () = let create_new_procdesc () =
let all_params = Option.to_list ms.CMethodSignature.class_param @ ms.CMethodSignature.params in let all_params = Option.to_list ms.CMethodSignature.class_param @ ms.CMethodSignature.params in
let has_added_return_param = ms.CMethodSignature.has_added_return_param in let has_added_return_param = ms.CMethodSignature.has_added_return_param in
@ -225,11 +230,11 @@ let create_local_procdesc ?(set_objc_accessor_attr = false) ?(update_lambda_capt
let formals = let formals =
List.map ~f:(fun ({name; typ} : CMethodSignature.param_type) -> (name, typ)) all_params List.map ~f:(fun ({name; typ} : CMethodSignature.param_type) -> (name, typ)) all_params
in in
(* Captured variables for blocks are treated as parameters (* Captured variables for blocks are treated as parameters, but not for cpp lambdas *)
Captured variables will not be added to formals for lambdas' `operator()` as procdesc for let captured_as_formals =
`operator()` is created before captured variables are translated if is_cpp_lambda_call_operator then []
*) else List.map ~f:(fun (var, t, _) -> (var, t)) captured_mangled
let captured_as_formals = List.map ~f:(fun (var, t, _) -> (var, t)) captured_mangled in in
let formals = captured_as_formals @ formals in let formals = captured_as_formals @ formals in
let const_formals = let const_formals =
get_const_params_indices ~shift:(List.length captured_as_formals) all_params get_const_params_indices ~shift:(List.length captured_as_formals) all_params
@ -253,7 +258,7 @@ let create_local_procdesc ?(set_objc_accessor_attr = false) ?(update_lambda_capt
let procdesc = let procdesc =
let proc_attributes = let proc_attributes =
{ (ProcAttributes.default translation_unit proc_name) with { (ProcAttributes.default translation_unit proc_name) with
ProcAttributes.captured= captured_mangled ProcAttributes.captured= captured_mangled_not_formals
; formals ; formals
; const_formals ; const_formals
; has_added_return_param ; has_added_return_param
@ -278,11 +283,23 @@ let create_local_procdesc ?(set_objc_accessor_attr = false) ?(update_lambda_capt
Procdesc.set_start_node procdesc start_node ; Procdesc.set_start_node procdesc start_node ;
Procdesc.set_exit_node procdesc exit_node ) Procdesc.set_exit_node procdesc exit_node )
in in
let captured_vars = if update_lambda_captured then captured_mangled else [] in if should_create_procdesc cfg proc_name ~defined ~set_objc_accessor_attr then (
if should_create_procdesc cfg proc_name ~defined ~set_objc_accessor_attr ~captured_vars then (
create_new_procdesc () ; create_new_procdesc () ;
true ) true )
else false else (
(* If we do not create procdesc when captured variables are translated,
then we want to record captured variables in the previously created procdesc *)
ignore
( if record_lambda_captured then
match Procname.Hash.find cfg proc_name with
| procdesc_prev ->
let new_attributes =
{(Procdesc.get_attributes procdesc_prev) with captured= captured_mangled}
in
Procdesc.set_attributes procdesc_prev new_attributes
| exception Caml.Not_found ->
() ) ;
false )
(** Create a procdesc for objc methods whose signature cannot be found. *) (** Create a procdesc for objc methods whose signature cannot be found. *)
@ -311,7 +328,7 @@ let create_procdesc_with_pointer ?(captured_vars = []) context pointer class_nam
ignore ignore
(create_local_procdesc context.translation_unit_context context.cfg context.tenv callee_ms (create_local_procdesc context.translation_unit_context context.cfg context.tenv callee_ms
[] captured_vars [] captured_vars
~update_lambda_captured:(not (List.is_empty captured_vars))) ; ~record_lambda_captured:(not (List.is_empty captured_vars))) ;
callee_ms.CMethodSignature.name callee_ms.CMethodSignature.name
| None -> | None ->
let callee_name, method_kind = let callee_name, method_kind =

@ -19,19 +19,15 @@ type method_call_type = MCVirtual | MCNoVirtual | MCStatic [@@deriving compare]
val equal_method_call_type : method_call_type -> method_call_type -> bool val equal_method_call_type : method_call_type -> method_call_type -> bool
val should_create_procdesc : val should_create_procdesc :
?captured_vars:(Mangled.t * Typ.typ * Pvar.capture_mode) list Cfg.t -> Procname.t -> defined:bool -> set_objc_accessor_attr:bool -> bool
-> Cfg.t
-> Procname.t
-> defined:bool
-> set_objc_accessor_attr:bool
-> bool
(** Return if a procdesc should be added or not. It returns [false] when the same name of procdesc (** Return if a procdesc should be added or not. It returns [false] when the same name of procdesc
was added previously. [defined] represents if the function body is non-empty. was added previously. [defined] represents if the function body is non-empty.
[set_objc_accessor_attr] represents if the function is a getter/setter in Obj-C. *) [set_objc_accessor_attr] represents if the function is a getter/setter in Obj-C. *)
val create_local_procdesc : val create_local_procdesc :
?set_objc_accessor_attr:bool ?set_objc_accessor_attr:bool
-> ?update_lambda_captured:bool -> ?record_lambda_captured:bool
-> ?is_cpp_lambda_call_operator:bool
-> CFrontend_config.translation_unit_context -> CFrontend_config.translation_unit_context
-> Cfg.t -> Cfg.t
-> Tenv.t -> Tenv.t

@ -182,11 +182,12 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
Ident.NameGenerator.set_current ident_state Ident.NameGenerator.set_current ident_state
let call_translation context decl = let call_translation ?(is_cpp_lambda_expr = false) context decl =
let open CContext in let open CContext in
keep_ident_counter ~f:(fun () -> keep_ident_counter ~f:(fun () ->
let decl_context = if is_cpp_lambda_expr then `CppLambdaExprTranslation else `Translation in
F.translate_one_declaration context.translation_unit_context context.tenv context.cfg F.translate_one_declaration context.translation_unit_context context.tenv context.cfg
`Translation decl ) decl_context decl )
let global_var_decl_translation context decl_ref = let global_var_decl_translation context decl_ref =
@ -3170,7 +3171,6 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let open CContext in let open CContext in
let qual_type = expr_info.Clang_ast_t.ei_qual_type in let qual_type = expr_info.Clang_ast_t.ei_qual_type in
let context = trans_state.context in let context = trans_state.context in
call_translation context lei_lambda_decl ;
let procname = Procdesc.get_proc_name context.procdesc in let procname = Procdesc.get_proc_name context.procdesc in
let typ = CType_decl.qual_type_to_sil_type context.tenv qual_type in let typ = CType_decl.qual_type_to_sil_type context.tenv qual_type in
let get_captured_pvar_typ decl_ref = let get_captured_pvar_typ decl_ref =
@ -3258,6 +3258,9 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let lambda_pname = let lambda_pname =
CMethod_trans.get_procname_from_cpp_lambda context lei_lambda_decl captured_var_names CMethod_trans.get_procname_from_cpp_lambda context lei_lambda_decl captured_var_names
in in
(* We want to translate `operator()` after translating captured variables
so we can correct type of variables captured by reference *)
call_translation ~is_cpp_lambda_expr:true context lei_lambda_decl ;
let closure = Exp.Closure {name= lambda_pname; captured_vars} in let closure = Exp.Closure {name= lambda_pname; captured_vars} in
collect_trans_results context.procdesc ~return:(closure, typ) trans_results collect_trans_results context.procdesc ~return:(closure, typ) trans_results

Loading…
Cancel
Save