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
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
|