From ff7cd86140c33b6d970d79bb03c2191583466239 Mon Sep 17 00:00:00 2001 From: Daiva Naudziuniene Date: Thu, 17 Sep 2020 07:19:50 -0700 Subject: [PATCH] [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 --- infer/src/clang/cFrontend_config.ml | 2 +- infer/src/clang/cFrontend_config.mli | 2 +- infer/src/clang/cFrontend_decl.ml | 38 +++++++++++++------ infer/src/clang/cLocation.ml | 2 +- infer/src/clang/cMethod_trans.ml | 55 ++++++++++++++++++---------- infer/src/clang/cMethod_trans.mli | 10 ++--- infer/src/clang/cTrans.ml | 9 +++-- 7 files changed, 75 insertions(+), 43 deletions(-) diff --git a/infer/src/clang/cFrontend_config.ml b/infer/src/clang/cFrontend_config.ml index 2020ce1fb..bce29b450 100644 --- a/infer/src/clang/cFrontend_config.ml +++ b/infer/src/clang/cFrontend_config.ml @@ -16,7 +16,7 @@ let equal_clang_lang = [%compare.equal: clang_lang] type translation_unit_context = {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 *) diff --git a/infer/src/clang/cFrontend_config.mli b/infer/src/clang/cFrontend_config.mli index 547fa0aee..6cd0eb9fa 100644 --- a/infer/src/clang/cFrontend_config.mli +++ b/infer/src/clang/cFrontend_config.mli @@ -16,7 +16,7 @@ val equal_clang_lang : clang_lang -> clang_lang -> bool type translation_unit_context = {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 *) diff --git a/infer/src/clang/cFrontend_decl.ml b/infer/src/clang/cFrontend_decl.ml index 43e636202..53ccac391 100644 --- a/infer/src/clang/cFrontend_decl.ml +++ b/infer/src/clang/cFrontend_decl.ml @@ -85,8 +85,8 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron with CFrontend_errors.IncorrectAssumption _ -> () - let process_method_decl ?(set_objc_accessor_attr = false) ?(is_destructor = false) trans_unit_ctx - tenv cfg curr_class meth_decl = + let process_method_decl ?(inside_cpp_lambda_expr = false) ?(set_objc_accessor_attr = false) + ?(is_destructor = false) trans_unit_ctx tenv cfg curr_class meth_decl = try let ms, body_opt, extra_instrs = let procname = CType_decl.CProcname.from_decl ~tenv meth_decl in @@ -96,13 +96,23 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron | Some body -> let procname = ms.CMethodSignature.name 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 = if - CMethod_trans.create_local_procdesc ~set_objc_accessor_attr trans_unit_ctx cfg tenv ms - [body] [] + (* Do not translate body for lambda operator() if it comes from call expr rather than lambda expr + 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 - add_method trans_unit_ctx tenv cfg curr_class procname body ms return_param_typ_opt - None extra_instrs ~is_destructor_wrapper + 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 + None extra_instrs ~is_destructor_wrapper in ignore (add_method_if_create_procdesc ms procname ~is_destructor_wrapper:is_destructor) ; if is_destructor then @@ -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 match dec with | 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 _ -> process_method_decl trans_unit_ctx tenv cfg curr_class dec ~is_destructor:true | 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 = - List.iter ~f:(process_one_method_decl 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 ~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 @@ -387,13 +400,16 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron | CXXConstructorDecl (decl_info, _, _, _, _) | CXXConversionDecl (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.*) 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 match class_decl with | (Some (CXXRecordDecl _) | Some (ClassTemplateSpecializationDecl _)) when Config.cxx -> 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 -> L.(debug Capture Verbose) "Methods of %s skipped@\n" diff --git a/infer/src/clang/cLocation.ml b/infer/src/clang/cLocation.ml index 2024f3124..a7815852c 100644 --- a/infer/src/clang/cLocation.ml +++ b/infer/src/clang/cLocation.ml @@ -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 || (Config.cxx && map_file_of equal_header_of_current_source loc_start) || Config.cxx - && decl_trans_context = `Translation + && (decl_trans_context = `Translation || decl_trans_context = `CppLambdaExprTranslation) && translate_on_demand && not Config.testing_mode diff --git a/infer/src/clang/cMethod_trans.ml b/infer/src/clang/cMethod_trans.ml index 69c3f3d13..19822c5de 100644 --- a/infer/src/clang/cMethod_trans.ml +++ b/infer/src/clang/cMethod_trans.ml @@ -121,19 +121,13 @@ let get_objc_method_data obj_c_message_expr_info = (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 | previous_procdesc -> let is_defined_previous = Procdesc.is_defined previous_procdesc in if (defined || set_objc_accessor_attr) && not is_defined_previous then ( Procname.Hash.remove cfg procname ; 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 | exception Caml.Not_found -> true @@ -195,8 +189,8 @@ let find_sentinel_attribute attrs = (** Creates a procedure description. *) -let create_local_procdesc ?(set_objc_accessor_attr = false) ?(update_lambda_captured = false) - trans_unit_ctx cfg tenv ms fbody captured = +let create_local_procdesc ?(set_objc_accessor_attr = false) ?(record_lambda_captured = false) + ?(is_cpp_lambda_call_operator = false) trans_unit_ctx cfg tenv ms fbody captured = let defined = not (List.is_empty fbody) in let proc_name = ms.CMethodSignature.name 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 = List.map ~f:(fun (var, t, mode) -> (Pvar.get_name var, t, mode)) captured 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 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 @@ -225,11 +230,11 @@ let create_local_procdesc ?(set_objc_accessor_attr = false) ?(update_lambda_capt let formals = List.map ~f:(fun ({name; typ} : CMethodSignature.param_type) -> (name, typ)) all_params in - (* Captured variables for blocks are treated as parameters - Captured variables will not be added to formals for lambdas' `operator()` as procdesc for - `operator()` is created before captured variables are translated - *) - let captured_as_formals = List.map ~f:(fun (var, t, _) -> (var, t)) captured_mangled in + (* Captured variables for blocks are treated as parameters, but not for cpp lambdas *) + let captured_as_formals = + if is_cpp_lambda_call_operator then [] + else List.map ~f:(fun (var, t, _) -> (var, t)) captured_mangled + in let formals = captured_as_formals @ formals in let const_formals = 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 proc_attributes = { (ProcAttributes.default translation_unit proc_name) with - ProcAttributes.captured= captured_mangled + ProcAttributes.captured= captured_mangled_not_formals ; formals ; const_formals ; 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_exit_node procdesc exit_node ) 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 ~captured_vars then ( + if should_create_procdesc cfg proc_name ~defined ~set_objc_accessor_attr then ( create_new_procdesc () ; 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. *) @@ -311,7 +328,7 @@ let create_procdesc_with_pointer ?(captured_vars = []) context pointer class_nam ignore (create_local_procdesc context.translation_unit_context context.cfg context.tenv callee_ms [] captured_vars - ~update_lambda_captured:(not (List.is_empty captured_vars))) ; + ~record_lambda_captured:(not (List.is_empty captured_vars))) ; callee_ms.CMethodSignature.name | None -> let callee_name, method_kind = diff --git a/infer/src/clang/cMethod_trans.mli b/infer/src/clang/cMethod_trans.mli index bdb383ccc..960851cae 100644 --- a/infer/src/clang/cMethod_trans.mli +++ b/infer/src/clang/cMethod_trans.mli @@ -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 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 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. *) val create_local_procdesc : ?set_objc_accessor_attr:bool - -> ?update_lambda_captured:bool + -> ?record_lambda_captured:bool + -> ?is_cpp_lambda_call_operator:bool -> CFrontend_config.translation_unit_context -> Cfg.t -> Tenv.t diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 6e0609f6b..27695542c 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -182,11 +182,12 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s 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 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 - `Translation decl ) + decl_context decl ) 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 qual_type = expr_info.Clang_ast_t.ei_qual_type in let context = trans_state.context in - call_translation context lei_lambda_decl ; let procname = Procdesc.get_proc_name context.procdesc in let typ = CType_decl.qual_type_to_sil_type context.tenv qual_type in 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 = CMethod_trans.get_procname_from_cpp_lambda context lei_lambda_decl captured_var_names 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 collect_trans_results context.procdesc ~return:(closure, typ) trans_results