From c7abd82d44d8bcbce6dcffb4fa8b83d94f2c223c Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Thu, 23 Feb 2017 06:17:12 -0800 Subject: [PATCH] [clang] Translate structs on demand Summary: Instead of translating all structs/c++ classes and putting them into type environment, translate ones that are used. It now follows similar mechanism to ondemand function translation. This change should significantly decrease disk space/memory usage to store type environments + small change to fix build Reviewed By: dulmarod Differential Revision: D4597723 fbshipit-source-id: c8b0365 --- facebook-clang-plugins | 2 +- infer/src/IR/Procdesc.re | 4 +- infer/src/clang/CType_decl.ml | 83 +++++++++++++++++-------------- infer/src/clang/cFrontend_decl.ml | 36 +++++++------- 4 files changed, 67 insertions(+), 58 deletions(-) diff --git a/facebook-clang-plugins b/facebook-clang-plugins index 476112cbc..d05b658cc 160000 --- a/facebook-clang-plugins +++ b/facebook-clang-plugins @@ -1 +1 @@ -Subproject commit 476112cbc79aa320afdb4dbf46b79c21432b4717 +Subproject commit d05b658cc7b6d71268225a4d823b79640d83796a diff --git a/infer/src/IR/Procdesc.re b/infer/src/IR/Procdesc.re index 0ecc30d3e..6bc9ee93a 100644 --- a/infer/src/IR/Procdesc.re +++ b/infer/src/IR/Procdesc.re @@ -564,8 +564,8 @@ let pp_variable_list fmt etl => if (List.is_empty etl) { Format.fprintf fmt "None" } else { - IList.iter - (fun (id, ty) => Format.fprintf fmt " %a:%a" Mangled.pp id (Typ.pp_full Pp.text) ty) etl + List.iter + f::(fun (id, ty) => Format.fprintf fmt " %a:%a" Mangled.pp id (Typ.pp_full Pp.text) ty) etl }; let pp_signature fmt pdesc => { diff --git a/infer/src/clang/CType_decl.ml b/infer/src/clang/CType_decl.ml index 72d6f7907..d38a331a3 100644 --- a/infer/src/clang/CType_decl.ml +++ b/infer/src/clang/CType_decl.ml @@ -136,6 +136,17 @@ let get_translate_as_friend_decl decl_list = | _ -> None | exception Not_found -> None +let get_record_definition decl = + let open Clang_ast_t in + match decl with + | ClassTemplateSpecializationDecl + (_, _, _, _, _, _, {rdi_is_complete_definition; rdi_definition_ptr}, _, _) + | CXXRecordDecl (_, _, _, _, _, _, {rdi_is_complete_definition; rdi_definition_ptr}, _) + | RecordDecl (_, _, _, _, _, _, {rdi_is_complete_definition; rdi_definition_ptr}) + when not rdi_is_complete_definition && rdi_definition_ptr <> 0 -> + CAst_utils.get_decl rdi_definition_ptr |> Option.value ~default:decl + | _ -> decl + let rec get_struct_fields tenv decl = let open Clang_ast_t in let decl_list = match decl with @@ -156,56 +167,56 @@ let rec get_struct_fields tenv decl = (* For a record declaration it returns/constructs the type *) and get_record_declaration_type tenv decl = - match get_record_custom_type tenv decl with + let definition_decl = get_record_definition decl in + match get_record_custom_type tenv definition_decl with | Some t -> t - | None -> get_record_declaration_struct_type tenv decl + | None -> get_record_struct_type tenv definition_decl -and get_record_custom_type tenv decl = +and get_record_custom_type tenv definition_decl = let open Clang_ast_t in - match decl with + match definition_decl with | ClassTemplateSpecializationDecl (_, _, _, _, decl_list, _, _, _, _) | CXXRecordDecl (_, _, _, _, decl_list, _, _, _) -> Option.map ~f:(type_ptr_to_sil_type tenv) (get_translate_as_friend_decl decl_list) | _ -> None -and get_record_declaration_struct_type tenv decl = +and get_record_struct_type tenv definition_decl = let open Clang_ast_t in - match decl with + match definition_decl with | ClassTemplateSpecializationDecl (_, _, _, type_ptr, decl_list, _, record_decl_info, _, _) | CXXRecordDecl (_, _, _, type_ptr, decl_list, _, record_decl_info, _) | RecordDecl (_, _, _, type_ptr, decl_list, _, record_decl_info) -> - let csu, name = get_record_name_csu decl in + let csu, name = get_record_name_csu definition_decl in let mangled_name = Mangled.from_string name in - let is_complete_definition = record_decl_info.Clang_ast_t.rdi_is_complete_definition in let sil_typename = Typename.TN_csu (csu, mangled_name) in - let extra_fields = if CTrans_models.is_objc_memory_model_controlled name then - [StructTyp.objc_ref_counter_field] - else [] in - let annots = - if Csu.equal csu (Csu.Class Csu.CPP) then Annot.Class.cpp - else Annot.Item.empty (* No annotations for structs *) in - if is_complete_definition then ( - CAst_utils.update_sil_types_map type_ptr (Typ.Tstruct sil_typename); - let non_statics = get_struct_fields tenv decl in - let fields = CGeneral_utils.append_no_duplicates_fields non_statics extra_fields in - let statics = [] in (* Note: We treat static field same as global variables *) - let methods = get_class_methods name decl_list in (* C++ methods only *) - let supers = get_superclass_list_cpp decl in - ignore (Tenv.mk_struct tenv ~fields ~statics ~methods ~supers ~annots sil_typename); - let sil_type = Typ.Tstruct sil_typename in - CAst_utils.update_sil_types_map type_ptr sil_type; - sil_type - ) else ( - match Tenv.lookup tenv sil_typename with - | Some _ -> Typ.Tstruct sil_typename (* just reuse what is already in tenv *) - | None -> - (* This is first forward declaration seen. Add Tstruct to sil_types_map and struct with - only ref counter field to tenv. Later, when we see the definition, the tenv will be - updated with a new struct including the other fields. *) - ignore (Tenv.mk_struct tenv ~fields:extra_fields sil_typename); - let tvar_type = Typ.Tstruct sil_typename in - CAst_utils.update_sil_types_map type_ptr tvar_type; - tvar_type) + (match Tenv.lookup tenv sil_typename with + | Some _ -> Typ.Tstruct sil_typename (* just reuse what is already in tenv *) + | None -> + let is_complete_definition = record_decl_info.Clang_ast_t.rdi_is_complete_definition in + let extra_fields = if CTrans_models.is_objc_memory_model_controlled name then + [StructTyp.objc_ref_counter_field] + else [] in + let annots = + if Csu.equal csu (Csu.Class Csu.CPP) then Annot.Class.cpp + else Annot.Item.empty (* No annotations for structs *) in + if is_complete_definition then ( + CAst_utils.update_sil_types_map type_ptr (Typ.Tstruct sil_typename); + let non_statics = get_struct_fields tenv definition_decl in + let fields = CGeneral_utils.append_no_duplicates_fields non_statics extra_fields in + let statics = [] in (* Note: We treat static field same as global variables *) + let methods = get_class_methods name decl_list in (* C++ methods only *) + let supers = get_superclass_list_cpp definition_decl in + ignore (Tenv.mk_struct tenv ~fields ~statics ~methods ~supers ~annots sil_typename); + let sil_type = Typ.Tstruct sil_typename in + CAst_utils.update_sil_types_map type_ptr sil_type; + sil_type + ) else ( + (* There is no definition for that struct in whole translation unit. + Put empty struct into tenv to prevent backend problems *) + ignore (Tenv.mk_struct tenv ~fields:extra_fields sil_typename); + let tvar_type = Typ.Tstruct sil_typename in + CAst_utils.update_sil_types_map type_ptr tvar_type; + tvar_type)) | _ -> assert false and add_types_from_decl_to_tenv tenv decl = diff --git a/infer/src/clang/cFrontend_decl.ml b/infer/src/clang/cFrontend_decl.ml index 9d952915b..34b046a44 100644 --- a/infer/src/clang/cFrontend_decl.ml +++ b/infer/src/clang/cFrontend_decl.ml @@ -179,7 +179,7 @@ struct (* each procedure has different scope: start names from id 0 *) Ident.NameGenerator.reset (); - + let translate = translate_one_declaration trans_unit_ctx tenv cg cfg decl_trans_context in (if should_translate_decl trans_unit_ctx dec decl_trans_context then match dec with | FunctionDecl(_, _, _, _) -> @@ -252,26 +252,24 @@ struct ignore (CMethod_trans.create_local_procdesc trans_unit_ctx cfg tenv ms [body] [] false); add_method trans_unit_ctx tenv cg cfg CContext.ContextNoCls procname body None false None [] - + (* Note that C and C++ records are treated the same way + Skip translating implicit struct declarations, unless they have + full definition (which happens with C++ lambdas) *) + | ClassTemplateSpecializationDecl (di, _, _, _, decl_list, _, rdi, _, _) + | CXXRecordDecl (di, _, _, _, decl_list, _, rdi, _) + | RecordDecl (di, _, _, _, decl_list, _, rdi) + when (not di.di_is_implicit) || rdi.rdi_is_complete_definition -> + let is_method_decl decl = match decl with + | CXXMethodDecl _ | CXXConstructorDecl _ | CXXConversionDecl _ + | CXXDestructorDecl _ | FunctionTemplateDecl _ -> + true + | _ -> false in + let method_decls, no_method_decls = IList.partition is_method_decl decl_list in + List.iter ~f:translate no_method_decls; + ignore (CType_decl.add_types_from_decl_to_tenv tenv dec); + List.iter ~f:translate method_decls | _ -> ()); - let translate = translate_one_declaration trans_unit_ctx tenv cg cfg decl_trans_context in match dec with - (* Note that C and C++ records are treated the same way - Skip translating implicit struct declarations, unless they have - full definition (which happens with C++ lambdas) *) - | ClassTemplateSpecializationDecl (di, _, _, _, decl_list, _, rdi, _, _) - | CXXRecordDecl (di, _, _, _, decl_list, _, rdi, _) - | RecordDecl (di, _, _, _, decl_list, _, rdi) - when (not di.di_is_implicit) || rdi.rdi_is_complete_definition -> - let is_method_decl decl = match decl with - | CXXMethodDecl _ | CXXConstructorDecl _ | CXXConversionDecl _ - | CXXDestructorDecl _ | FunctionTemplateDecl _ -> - true - | _ -> false in - let method_decls, no_method_decls = IList.partition is_method_decl decl_list in - List.iter ~f:translate no_method_decls; - ignore (CType_decl.add_types_from_decl_to_tenv tenv dec); - List.iter ~f:translate method_decls | EnumDecl _ -> ignore (CEnum_decl.enum_decl dec) | LinkageSpecDecl (_, decl_list, _) -> Logging.out_debug "ADDING: LinkageSpecDecl decl list@\n";