You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

221 lines
9.3 KiB

(*
* 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
module F = Format
let is_custom_var_pointer pointer = pointer <= 0
let mk_sil_global_var {CFrontend_config.source_file} ?(mk_name = fun _ x -> x) decl_info
named_decl_info var_decl_info qt =
let name_string, simple_name =
CGeneral_utils.get_var_name_mangled decl_info named_decl_info var_decl_info
in
let translation_unit =
match Clang_ast_t.(var_decl_info.vdi_is_extern, var_decl_info.vdi_init_expr) with
| true, None ->
None
| _, None when var_decl_info.Clang_ast_t.vdi_is_static_data_member ->
(* non-const static data member get extern scope unless they are defined out of line here
(in which case vdi_init_expr will not be None) *)
None
| true, Some _
(* "extern" variables with initialisation code are not extern at all, but compilers accept this
*)
| false, _ ->
Some source_file
in
let is_constexpr = var_decl_info.Clang_ast_t.vdi_is_const_expr in
let is_ice = var_decl_info.Clang_ast_t.vdi_is_init_ice in
let is_pod = CGeneral_utils.is_type_pod qt in
let is_static_global =
var_decl_info.Clang_ast_t.vdi_is_global
(* only top-level declarations really have file scope, static field members have a global scope
*)
&& (not var_decl_info.Clang_ast_t.vdi_is_static_data_member)
&& var_decl_info.Clang_ast_t.vdi_is_static
in
let is_const = qt.Clang_ast_t.qt_is_const in
let desugared_type = CAst_utils.get_desugared_type qt.Clang_ast_t.qt_type_ptr in
let is_constant_array =
Option.exists desugared_type ~f:(function Clang_ast_t.ConstantArrayType _ -> true | _ -> false)
in
Pvar.mk_global ~is_constexpr ~is_ice ~is_pod
~is_static_local:var_decl_info.Clang_ast_t.vdi_is_static_local ~is_static_global
~is_constant_array ~is_const ?translation_unit (mk_name name_string simple_name)
let mk_sil_var trans_unit_ctx named_decl_info decl_info_qual_type_opt procname outer_procname =
match decl_info_qual_type_opt with
| Some (decl_info, qt, var_decl_info, should_be_mangled) ->
let name_string, simple_name =
CGeneral_utils.get_var_name_mangled decl_info named_decl_info var_decl_info
in
if var_decl_info.Clang_ast_t.vdi_is_global then
let mk_name =
if var_decl_info.Clang_ast_t.vdi_is_static_local then
Some
(fun name_string _ ->
Mangled.from_string (F.asprintf "%a.%s" Procname.pp outer_procname name_string) )
else None
in
mk_sil_global_var trans_unit_ctx ?mk_name decl_info named_decl_info var_decl_info qt
else if not should_be_mangled then Pvar.mk simple_name procname
else
let start_location = fst decl_info.Clang_ast_t.di_source_range in
let line_opt = start_location.Clang_ast_t.sl_line in
let line_str = match line_opt with Some line -> string_of_int line | None -> "" in
let mangled = Utils.string_crc_hex32 line_str in
let mangled_name = Mangled.mangled name_string mangled in
Pvar.mk mangled_name procname
| None ->
let name_string =
CAst_utils.get_qualified_name named_decl_info |> QualifiedCppName.to_qual_string
in
Pvar.mk (Mangled.from_string name_string) procname
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)
| VarTemplateSpecializationDecl (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
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
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
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