(* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. *) open! IStd (** Process variable declarations by saving them as local or global variables. *) (** Computes the local variables of a function or method to be added to the procdesc *) let is_custom_var_pointer pointer = pointer <= 0 let sil_var_of_decl context var_decl procname = let outer_procname = CContext.get_outer_procname context in let trans_unit_ctx = context.CContext.translation_unit_context in let open Clang_ast_t in match var_decl with | VarDecl (decl_info, name_info, qual_type, var_decl_info) -> let shoud_be_mangled = not (is_custom_var_pointer decl_info.Clang_ast_t.di_pointer) in let var_decl_details = Some (decl_info, qual_type, var_decl_info, shoud_be_mangled) in CGeneral_utils.mk_sil_var trans_unit_ctx name_info var_decl_details procname outer_procname | ParmVarDecl (decl_info, name_info, qual_type, var_decl_info) -> let var_decl_details = Some (decl_info, qual_type, var_decl_info, false) in CGeneral_utils.mk_sil_var trans_unit_ctx name_info var_decl_details procname outer_procname | _ -> assert false let sil_var_of_decl_ref context source_range decl_ref procname = let name = match decl_ref.Clang_ast_t.dr_name with Some name_info -> name_info | None -> assert false in match decl_ref.Clang_ast_t.dr_kind with | `ImplicitParam -> let outer_procname = CContext.get_outer_procname context in let trans_unit_ctx = context.CContext.translation_unit_context in CGeneral_utils.mk_sil_var trans_unit_ctx name None procname outer_procname | _ -> ( let pointer = decl_ref.Clang_ast_t.dr_decl_pointer in if is_custom_var_pointer pointer then Pvar.mk (Mangled.from_string name.Clang_ast_t.ni_name) procname else match CAst_utils.get_decl pointer with | Some var_decl -> sil_var_of_decl context var_decl procname | None -> (* FIXME(t21762295) *) CFrontend_errors.incorrect_assumption __POS__ source_range "pointer '%d' for var decl not found. The var decl was: %a" pointer (Pp.of_string ~f:Clang_ast_j.string_of_decl_ref) decl_ref ) let has_block_attribute decl_info = let open Clang_ast_t in List.exists decl_info.di_attributes ~f:(fun attr -> match attr with `BlocksAttr _ -> true | _ -> false ) let add_var_to_locals procdesc var_decl typ pvar = let open Clang_ast_t in match var_decl with | VarDecl (decl_info, _, _, vdi) -> if not vdi.Clang_ast_t.vdi_is_global then let modify_in_block = has_block_attribute decl_info in let is_constexpr = vdi.Clang_ast_t.vdi_is_const_expr || (Typ.is_const typ.Typ.quals && vdi.Clang_ast_t.vdi_is_init_expr_cxx11_constant) in let is_declared_unused = List.exists decl_info.di_attributes ~f:(function `UnusedAttr _ -> true | _ -> false) in let var_data : ProcAttributes.var_data = {name= Pvar.get_name pvar; typ; modify_in_block; is_constexpr; is_declared_unused} in Procdesc.append_locals procdesc [var_data] | _ -> assert false (* The context here is of the method that contains the block *) let sil_var_of_captured_var context source_range procname decl_ref = let is_block_inside_objc_class_method = CContext.is_objc_class_method context in let var_opt = match decl_ref with | {Clang_ast_t.dr_name= Some {Clang_ast_t.ni_name}} -> (* In Objective-C class methods, self is not the standard self instance, since in this context we don't have an instance. Instead it is used to get the class of the method. We translate this variables in a different way than normal, we don't treat them as variables in Sil, instead we remove them and get the class directly in the frontend. For that reason, we shouldn't add them as captured variables of blocks, since they don't appear anywhere else in the translation. *) if is_block_inside_objc_class_method && String.equal ni_name CFrontend_config.self then None else Some (sil_var_of_decl_ref context source_range decl_ref procname) | _ -> assert false in let typ_opt = CType_decl.type_of_captured_var context.CContext.tenv ~is_block_inside_objc_class_method decl_ref in match (var_opt, typ_opt) with | Some var, Some typ -> let modify_in_block = match CAst_utils.get_decl decl_ref.Clang_ast_t.dr_decl_pointer with | Some (VarDecl (decl_info, _, _, _)) -> has_block_attribute decl_info | _ -> false in Some (var, typ, modify_in_block) | None, None -> None | _ -> Logging.die InternalError "Not possible case, captured variable and its type should both be available or not at %s" (Clang_ast_j.string_of_source_range source_range) (* Returns a list of captured variables as sil variables. *) let captured_vars_from_block_info context source_range captured_vars = let procname = Procdesc.get_proc_name context.CContext.procdesc in let cv_decl_ref_list = List.map ~f:(fun cv -> Option.value_exn cv.Clang_ast_t.bcv_variable) captured_vars in List.filter_map ~f:(sil_var_of_captured_var context source_range procname) cv_decl_ref_list let mk_temp_sil_var procdesc ~name = let procname = Procdesc.get_proc_name procdesc in Pvar.mk_tmp name procname let mk_temp_sil_var_for_expr context ~name ~clang_pointer expr_info = match Caml.Hashtbl.find_opt context.CContext.temporary_names clang_pointer with | Some pvar_typ -> pvar_typ | None -> let qual_type = expr_info.Clang_ast_t.ei_qual_type in let typ = CType_decl.qual_type_to_sil_type context.CContext.tenv qual_type in let pvar_typ = (mk_temp_sil_var context.CContext.procdesc ~name, typ) in Caml.Hashtbl.add context.CContext.temporary_names clang_pointer pvar_typ ; pvar_typ let materialize_cpp_temporary context stmt_info expr_info = (* the type we get here is a 'best effort' type - the translation may decide to use different type later *) mk_temp_sil_var_for_expr context ~name:Pvar.materialized_cpp_temporary ~clang_pointer:stmt_info.Clang_ast_t.si_pointer expr_info