[clang frontend] Add support for the clang attribute NS_NOESCAPE for Objective-C blocks in Sil

Summary:
This attribute is given to parameters of methods that take Objective-C blocks to show that they will be used only in the current context and won't "escape" the context.
We translate it here, with the goal to use it in a new check later. The check is about not using weakSelf in non-escaping blocks, because retain cycles are not possible.

The translation is a bit complex because the annotation comes in the parameter of a method, but in the checker we will need it in the block. So we pass it around in the frontend from the translation of the method call to the translation context and on to the block expression and the block declaration afterwards.

Reviewed By: ngorogiannis

Differential Revision: D19600377

fbshipit-source-id: dd49539bd
master
Dulma Churchill 5 years ago committed by Facebook Github Bot
parent 4f4455b73c
commit 05ea5ec844

@ -49,6 +49,8 @@ type t =
; is_defined: bool (** true if the procedure is defined, and not just declared *)
; is_cpp_noexcept_method: bool (** the procedure is an C++ method annotated with "noexcept" *)
; is_java_synchronized_method: bool (** the procedure is a Java synchronized method *)
; is_no_escape_block: bool
(** The procedure is an Objective-C block that has the NS_NOESCAPE attribute *)
; is_no_return: bool (** the procedure is known not to return *)
; is_specialized: bool (** the procedure is a clone specialized for dynamic dispatch handling *)
; is_synthetic_method: bool (** the procedure is a synthetic method *)
@ -76,6 +78,7 @@ let default translation_unit proc_name =
; is_cpp_noexcept_method= false
; is_defined= false
; is_java_synchronized_method= false
; is_no_escape_block= false
; is_no_return= false
; is_specialized= false
; is_synthetic_method= false
@ -108,6 +111,7 @@ let pp f
; is_defined
; is_cpp_noexcept_method
; is_java_synchronized_method
; is_no_escape_block
; is_no_return
; is_specialized
; is_synthetic_method
@ -150,6 +154,7 @@ let pp f
pp_bool_default ~default:default.is_defined "is_defined" is_defined f () ;
pp_bool_default ~default:default.is_java_synchronized_method "is_java_synchronized_method"
is_java_synchronized_method f () ;
pp_bool_default ~default:default.is_no_escape_block "is_no_escape_block" is_no_escape_block f () ;
pp_bool_default ~default:default.is_no_return "is_no_return" is_no_return f () ;
pp_bool_default ~default:default.is_specialized "is_specialized" is_specialized f () ;
pp_bool_default ~default:default.is_synthetic_method "is_synthetic_method" is_synthetic_method f

@ -33,6 +33,9 @@ type t =
; is_defined: bool (** true if the procedure is defined, and not just declared *)
; is_cpp_noexcept_method: bool (** the procedure is an C++ method annotated with "noexcept" *)
; is_java_synchronized_method: bool (** the procedure is a Java synchronized method *)
; is_no_escape_block: bool
(** The procedure is an Objective-C block that is passed to a method in a position annotated
with NS_NOESCAPE *)
; is_no_return: bool (** the procedure is known not to return *)
; is_specialized: bool (** the procedure is a clone specialized for dynamic dispatch handling *)
; is_synthetic_method: bool (** the procedure is a synthetic method *)

@ -96,8 +96,10 @@ module BuildMethodSignature = struct
|> qual_type_to_sil_type tenv
in
let is_pointer_to_const = CType.is_pointer_to_const qt in
let is_no_escape_block_arg = CAst_utils.is_no_escape_block_arg par in
let annot = CAst_utils.sil_annot_of_type qt in
CMethodSignature.mk_param_type name typ ~is_pointer_to_const ~annot
~is_no_escape_block_arg
| _ ->
raise CFrontend_errors.Invalid_declaration
in
@ -153,7 +155,8 @@ module BuildMethodSignature = struct
else (return_typ, None, return_typ_annot, false)
let method_signature_of_decl qual_type_to_sil_type tenv method_decl ?block_return_type procname =
let method_signature_of_decl qual_type_to_sil_type tenv method_decl ?block_return_type
?(is_no_escape_block = false) procname =
let decl_info = Clang_ast_proj.get_decl_tuple method_decl in
let loc = decl_info.Clang_ast_t.di_source_range in
let ret_type, return_param_typ, ret_typ_annot, has_added_return_param =
@ -181,6 +184,7 @@ module BuildMethodSignature = struct
; method_kind
; is_cpp_virtual
; is_cpp_nothrow
; is_no_escape_block
; is_no_return
; is_variadic
; pointer_to_parent
@ -189,11 +193,12 @@ module BuildMethodSignature = struct
let method_signature_body_of_decl qual_type_to_sil_type tenv method_decl ?block_return_type
procname =
?is_no_escape_block procname =
let body = CMethodProperties.get_method_body method_decl in
let init_list_instrs = CMethodProperties.get_init_list_instrs method_decl in
let ms =
method_signature_of_decl qual_type_to_sil_type tenv method_decl ?block_return_type procname
method_signature_of_decl qual_type_to_sil_type tenv method_decl ?block_return_type
?is_no_escape_block procname
in
(ms, body, init_list_instrs)
end

@ -53,6 +53,7 @@ val method_signature_of_decl :
Tenv.t
-> Clang_ast_t.decl
-> ?block_return_type:Clang_ast_t.qual_type
-> ?is_no_escape_block:bool
-> Procname.t
-> CMethodSignature.t
@ -60,6 +61,7 @@ val method_signature_body_of_decl :
Tenv.t
-> Clang_ast_t.decl
-> ?block_return_type:Clang_ast_t.qual_type
-> ?is_no_escape_block:bool
-> Procname.t
-> CMethodSignature.t
* Clang_ast_t.stmt option

@ -555,6 +555,13 @@ let has_block_attribute decl =
false
(* true if a decl has a NS_NOESCAPE attribute *)
let is_no_escape_block_arg decl =
let has_noescape_attr attr = match attr with `NoEscapeAttr _ -> true | _ -> false in
let attrs = (Clang_ast_proj.get_decl_tuple decl).di_attributes in
List.exists attrs ~f:has_noescape_attr
let is_implicit_decl decl =
let decl_info = Clang_ast_proj.get_decl_tuple decl in
decl_info.Clang_ast_t.di_is_implicit

@ -138,6 +138,8 @@ val get_cxx_virtual_base_classes : Clang_ast_t.decl -> Clang_ast_t.type_ptr list
val is_std_vector : Clang_ast_t.qual_type -> bool
val is_no_escape_block_arg : Clang_ast_t.decl -> bool
val has_block_attribute : Clang_ast_t.decl -> bool
val is_implicit_decl : Clang_ast_t.decl -> bool

@ -75,15 +75,16 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
| None ->
([], None)
in
let procname, block_return_type =
let is_no_escape_block, procname, block_return_type =
match block_data_opt with
| Some {CModule_type.procname; return_type} ->
(procname, Some return_type)
| Some {CModule_type.is_no_escape_block_arg; procname; return_type} ->
(is_no_escape_block_arg, procname, Some return_type)
| _ ->
(CType_decl.CProcname.from_decl ~tenv func_decl, None)
(false, CType_decl.CProcname.from_decl ~tenv func_decl, None)
in
let ms, body_opt, extra_instrs =
CType_decl.method_signature_body_of_decl tenv func_decl ?block_return_type procname
CType_decl.method_signature_body_of_decl tenv func_decl ?block_return_type
~is_no_escape_block procname
in
match body_opt with
| Some body ->

@ -12,10 +12,16 @@ open! IStd
module F = Format
type param_type = {name: Mangled.t; typ: Typ.t; is_pointer_to_const: bool; annot: Annot.Item.t}
type param_type =
{ annot: Annot.Item.t
; is_no_escape_block_arg: bool
; is_pointer_to_const: bool
; name: Mangled.t
; typ: Typ.t }
let mk_param_type ?(is_pointer_to_const = false) ?(annot = Annot.Item.empty) name typ =
{name; typ; is_pointer_to_const; annot}
let mk_param_type ?(is_pointer_to_const = false) ?(annot = Annot.Item.empty)
?(is_no_escape_block_arg = false) name typ =
{name; typ; is_pointer_to_const; annot; is_no_escape_block_arg}
type t =
@ -30,6 +36,7 @@ type t =
; method_kind: ClangMethodKind.t
; is_cpp_virtual: bool
; is_cpp_nothrow: bool
; is_no_escape_block: bool
; is_no_return: bool
; is_variadic: bool
; pointer_to_parent: Clang_ast_t.pointer option
@ -50,8 +57,9 @@ let is_setter {pointer_to_property_opt; params} =
let mk name class_param params ret_type ?(has_added_return_param = false) attributes loc method_kind
?(is_cpp_virtual = false) ?(is_cpp_nothrow = false) ?(is_no_return = false)
?(is_variadic = false) pointer_to_parent pointer_to_property_opt return_param_typ access =
?(is_cpp_virtual = false) ?(is_cpp_nothrow = false) ?(is_no_escape_block = false)
?(is_no_return = false) ?(is_variadic = false) pointer_to_parent pointer_to_property_opt
return_param_typ access =
{ name
; access
; class_param
@ -63,6 +71,7 @@ let mk name class_param params ret_type ?(has_added_return_param = false) attrib
; method_kind
; is_cpp_virtual
; is_cpp_nothrow
; is_no_escape_block
; is_no_return
; is_variadic
; pointer_to_parent
@ -71,9 +80,12 @@ let mk name class_param params ret_type ?(has_added_return_param = false) attrib
let pp fmt ms =
let pp_param fmt {name; typ} = F.fprintf fmt "%a, %a" Mangled.pp name (Typ.pp Pp.text) typ in
Format.fprintf fmt "Method %a [%a]->%a %a"
let pp_param fmt {name; typ; is_no_escape_block_arg} =
F.fprintf fmt "%a, %a (is_no_escape_block=%b)" Mangled.pp name (Typ.pp Pp.text) typ
is_no_escape_block_arg
in
Format.fprintf fmt "Method %a [%a]->%a %a(is_no_escape_block=%b)"
(Pp.of_string ~f:Procname.to_string)
ms.name (Pp.comma_seq pp_param) ms.params (Typ.pp Pp.text) (fst ms.ret_type)
(Pp.of_string ~f:Clang_ast_j.string_of_source_range)
ms.loc
ms.loc ms.is_no_escape_block

@ -10,7 +10,12 @@ open! IStd
(** Define the signature of a method consisting of its name, its arguments, return type, location
and whether its an instance method. *)
type param_type = {name: Mangled.t; typ: Typ.t; is_pointer_to_const: bool; annot: Annot.Item.t}
type param_type =
{ annot: Annot.Item.t
; is_no_escape_block_arg: bool
; is_pointer_to_const: bool
; name: Mangled.t
; typ: Typ.t }
type t =
{ name: Procname.t
@ -24,6 +29,7 @@ type t =
; method_kind: ClangMethodKind.t
; is_cpp_virtual: bool
; is_cpp_nothrow: bool
; is_no_escape_block: bool
; is_no_return: bool
; is_variadic: bool
; pointer_to_parent: Clang_ast_t.pointer option
@ -46,6 +52,7 @@ val mk :
-> ClangMethodKind.t
-> ?is_cpp_virtual:bool
-> ?is_cpp_nothrow:bool
-> ?is_no_escape_block:bool
-> ?is_no_return:bool
-> ?is_variadic:bool
-> Clang_ast_t.pointer option
@ -57,4 +64,9 @@ val mk :
val pp : Format.formatter -> t -> unit
val mk_param_type :
?is_pointer_to_const:bool -> ?annot:Annot.Item.t -> Mangled.t -> Typ.t -> param_type
?is_pointer_to_const:bool
-> ?annot:Annot.Item.t
-> ?is_no_escape_block_arg:bool
-> Mangled.t
-> Typ.t
-> param_type

@ -112,14 +112,13 @@ let get_class_name_method_call_from_receiver_kind context obj_c_message_expr_inf
let get_objc_method_data obj_c_message_expr_info =
let selector = obj_c_message_expr_info.Clang_ast_t.omei_selector in
let pointer = obj_c_message_expr_info.Clang_ast_t.omei_decl_pointer in
match obj_c_message_expr_info.Clang_ast_t.omei_receiver_kind with
| `Instance ->
(selector, pointer, MCVirtual)
(selector, MCVirtual)
| `SuperInstance ->
(selector, pointer, MCNoVirtual)
(selector, MCNoVirtual)
| `Class _ | `SuperClass ->
(selector, pointer, MCStatic)
(selector, MCStatic)
let should_create_procdesc cfg procname ~defined ~set_objc_accessor_attr =
@ -248,6 +247,7 @@ let create_local_procdesc ?(set_objc_accessor_attr = false) trans_unit_ctx cfg t
; is_defined= defined
; is_cpp_noexcept_method= is_cpp_nothrow
; is_biabduction_model= Config.biabduction_models_mode
; is_no_escape_block= ms.CMethodSignature.is_no_escape_block
; is_no_return= ms.CMethodSignature.is_no_return
; is_variadic= ms.CMethodSignature.is_variadic
; sentinel_attr= find_sentinel_attribute ms.CMethodSignature.attributes

@ -42,8 +42,7 @@ val create_external_procdesc :
-> (Typ.t * Typ.t list) option
-> unit
val get_objc_method_data :
Clang_ast_t.obj_c_message_expr_info -> string * Clang_ast_t.pointer option * method_call_type
val get_objc_method_data : Clang_ast_t.obj_c_message_expr_info -> string * method_call_type
val get_class_name_method_call_from_receiver_kind :
CContext.t -> Clang_ast_t.obj_c_message_expr_info -> (Exp.t * Typ.t) list -> Typ.Name.t

@ -10,6 +10,7 @@ open! IStd
type block_data =
{ captured_vars: (Pvar.t * Typ.t) list
; context: CContext.t
; is_no_escape_block_arg: bool
; procname: Procname.t
; return_type: Clang_ast_t.qual_type }

@ -22,25 +22,16 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
1. method is a predefined model
2. method is found by clang's resolution
3. Method is found by our resolution *)
let get_callee_objc_method context obj_c_message_expr_info act_params =
let get_callee_objc_method context obj_c_message_expr_info callee_ms_opt act_params =
let open CContext in
let selector, method_pointer_opt, mc_type =
CMethod_trans.get_objc_method_data obj_c_message_expr_info
in
let selector, mc_type = CMethod_trans.get_objc_method_data obj_c_message_expr_info in
let is_instance = mc_type <> CMethod_trans.MCStatic in
let objc_method_kind = Procname.ObjC_Cpp.objc_method_kind_of_bool is_instance in
let method_kind =
if is_instance then ClangMethodKind.OBJC_INSTANCE else ClangMethodKind.OBJC_CLASS
in
let ms_opt =
match method_pointer_opt with
| Some pointer ->
CMethod_trans.method_signature_of_pointer context.tenv pointer
| None ->
None
in
let proc_name =
match CMethod_trans.get_method_name_from_clang context.tenv ms_opt with
match CMethod_trans.get_method_name_from_clang context.tenv callee_ms_opt with
| Some name ->
name
| None ->
@ -61,7 +52,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
| _ ->
None
in
match (predefined_ms_opt, ms_opt) with
match (predefined_ms_opt, callee_ms_opt) with
| Some ms, _ ->
ignore
(CMethod_trans.create_local_procdesc context.translation_unit_context context.cfg
@ -1254,13 +1245,35 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
else None
and exec_instruction_with_trans_state trans_state_param callee_ms_opt i stmt =
let trans_state_param' =
match callee_ms_opt with
| Some callee_ms when not trans_state_param.is_fst_arg_objc_instance_method_call ->
let ms_param_type_i =
let find_arg j _ = Int.equal i j in
List.findi ~f:find_arg callee_ms.CMethodSignature.params
in
let is_no_escape_block_arg =
match ms_param_type_i with
| Some (_, {CMethodSignature.is_no_escape_block_arg}) ->
is_no_escape_block_arg
| None ->
false
in
{trans_state_param with is_no_escape_block_arg}
| _ ->
trans_state_param
in
exec_with_glvalue_as_reference instruction trans_state_param' stmt
(** If the first argument of the call is self in a static context, remove it as an argument and
change the call from instance to static *)
and objCMessageExpr_deal_with_static_self trans_state_param stmt_list obj_c_message_expr_info =
match stmt_list with
and objCMessageExpr_translate_args trans_state_param args obj_c_message_expr_info callee_ms_opt =
match args with
| stmt :: rest -> (
let param_trans_results =
List.map ~f:(exec_with_glvalue_as_reference instruction trans_state_param) rest
List.mapi ~f:(exec_instruction_with_trans_state trans_state_param callee_ms_opt) rest
in
try
let trans_state_param' =
@ -1268,7 +1281,9 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
{trans_state_param with is_fst_arg_objc_instance_method_call= true}
else {trans_state_param with is_fst_arg_objc_instance_method_call= false}
in
let fst_res_trans = instruction trans_state_param' stmt in
let fst_res_trans =
exec_instruction_with_trans_state trans_state_param' callee_ms_opt 0 stmt
in
(obj_c_message_expr_info, fst_res_trans :: param_trans_results)
with Self.SelfClassException e ->
let pointer = obj_c_message_expr_info.Clang_ast_t.omei_decl_pointer in
@ -1290,9 +1305,17 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let method_type_no_ref = CType_decl.get_type_from_expr_info expr_info context.CContext.tenv in
let method_type = add_reference_if_glvalue method_type_no_ref expr_info in
let trans_state_pri = PriorityNode.try_claim_priority_node trans_state si in
let callee_ms_opt =
match obj_c_message_expr_info.Clang_ast_t.omei_decl_pointer with
| Some pointer ->
CMethod_trans.method_signature_of_pointer context.tenv pointer
| None ->
None
in
let trans_state_param = {trans_state_pri with succ_nodes= []; var_exp_typ= None} in
let obj_c_message_expr_info, res_trans_subexpr_list =
objCMessageExpr_deal_with_static_self trans_state_param stmt_list obj_c_message_expr_info
objCMessageExpr_translate_args trans_state_param stmt_list obj_c_message_expr_info
callee_ms_opt
in
let subexpr_exprs = collect_returns res_trans_subexpr_list in
match
@ -1304,7 +1327,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
| None ->
let procname = Procdesc.get_proc_name context.CContext.procdesc in
let callee_name, method_call_type =
get_callee_objc_method context obj_c_message_expr_info subexpr_exprs
get_callee_objc_method context obj_c_message_expr_info callee_ms_opt subexpr_exprs
in
let res_trans_add_self =
Self.add_self_parameter_for_super_instance si context procname sil_loc
@ -2785,8 +2808,11 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
CVar_decl.captured_vars_from_block_info context stmt_info.Clang_ast_t.si_source_range
block_decl_info.Clang_ast_t.bdi_captured_variables
in
let is_no_escape_block_arg = trans_state.is_no_escape_block_arg in
let res = closure_trans procname captured_vars context stmt_info expr_info in
let block_data = Some {CModule_type.captured_vars; context; procname; return_type} in
let block_data =
Some {CModule_type.captured_vars; context; is_no_escape_block_arg; procname; return_type}
in
F.function_decl context.translation_unit_context context.tenv context.cfg decl block_data ;
res
| _ ->

@ -126,7 +126,10 @@ type trans_state =
; priority: priority_node
; var_exp_typ: (Exp.t * Typ.t) option
; opaque_exp: (Exp.t * Typ.t) option
; is_fst_arg_objc_instance_method_call: bool }
; is_fst_arg_objc_instance_method_call: bool
; is_no_escape_block_arg: bool
(** Current to-be-translated instruction is being passed as argument in a position annotated
with NS_NOESCAPE *) }
let default_trans_state context =
{ context
@ -135,7 +138,8 @@ let default_trans_state context =
; priority= Free
; var_exp_typ= None
; opaque_exp= None
; is_fst_arg_objc_instance_method_call= false }
; is_fst_arg_objc_instance_method_call= false
; is_no_escape_block_arg= false }
type control =

@ -26,7 +26,8 @@ type trans_state =
; priority: priority_node
; var_exp_typ: (Exp.t * Typ.t) option
; opaque_exp: (Exp.t * Typ.t) option
; is_fst_arg_objc_instance_method_call: bool }
; is_fst_arg_objc_instance_method_call: bool
; is_no_escape_block_arg: bool }
val default_trans_state : CContext.t -> trans_state

Loading…
Cancel
Save