[frontend,pulse] Avoid dereference of C struct

Summary:
This diff avoids dereference of C struct, in its frontend and its semantics of Pulse. In SIL, C
struct is not first-class value, thus dereferencing on it does not make sense.

Reviewed By: ezgicicek

Differential Revision: D27953258

fbshipit-source-id: 348d56338
master
Sungkeun Cho 4 years ago committed by Facebook GitHub Bot
parent fdd051afb1
commit 2886e849da

@ -560,11 +560,16 @@ let cast_operation ?objc_bridge_cast_kind cast_kind ((exp, typ) as exp_typ) cast
| `BitCast | `IntegralCast | `IntegralToBoolean -> | `BitCast | `IntegralCast | `IntegralToBoolean ->
(* This is treated as a nop by returning the same expressions exps*) (* This is treated as a nop by returning the same expressions exps*)
([], (exp, cast_typ)) ([], (exp, cast_typ))
| `LValueToRValue -> | `LValueToRValue -> (
match typ with
| {Typ.desc= Tstruct _} ->
(* Avoid dereference of C struct on cast *)
([], exp_typ)
| _ ->
(* Takes an LValue and allow it to use it as RValue. *) (* Takes an LValue and allow it to use it as RValue. *)
(* So we assign the LValue to a temp and we pass it to the parent.*) (* So we assign the LValue to a temp and we pass it to the parent.*)
let instr, deref_exp = dereference_var_sil (exp, cast_typ) sil_loc in let instr, deref_exp = dereference_var_sil (exp, cast_typ) sil_loc in
([instr], (deref_exp, cast_typ)) ([instr], (deref_exp, cast_typ)) )
| `NullToPointer -> | `NullToPointer ->
if Exp.is_zero exp then ([], (Exp.null, cast_typ)) else ([], (exp, cast_typ)) if Exp.is_zero exp then ([], (Exp.null, cast_typ)) else ([], (exp, cast_typ))
| `ToVoid -> | `ToVoid ->

@ -198,9 +198,9 @@ let call_aux tenv caller_proc_desc call_loc callee_pname ret actuals callee_proc
in in
let captured_vars = let captured_vars =
Procdesc.get_captured callee_proc_desc Procdesc.get_captured callee_proc_desc
|> List.map ~f:(fun {CapturedVar.name; capture_mode} -> |> List.map ~f:(fun {CapturedVar.name; capture_mode; typ} ->
let pvar = Pvar.mk name callee_pname in let pvar = Pvar.mk name callee_pname in
(Var.of_pvar pvar, capture_mode) ) (Var.of_pvar pvar, capture_mode, typ) )
in in
let<*> astate, captured_vars_with_actuals = let<*> astate, captured_vars_with_actuals =
match actuals with match actuals with

@ -205,13 +205,22 @@ let rec materialize_pre_from_address callee_proc_name call_location ~pre ~addr_p
~addr_hist_caller:addr_hist_dest_caller call_state ) ) ~addr_hist_caller:addr_hist_dest_caller call_state ) )
let deref_non_c_struct addr typ astate =
match typ.Typ.desc with
| Tstruct _ ->
Some addr
| _ ->
BaseMemory.find_edge_opt addr Dereference astate |> Option.map ~f:fst
(** materialize subgraph of [pre] rooted at the address represented by a [formal] parameter that has (** materialize subgraph of [pre] rooted at the address represented by a [formal] parameter that has
been instantiated with the corresponding [actual] into the current state [call_state.astate] *) been instantiated with the corresponding [actual] into the current state [call_state.astate] *)
let materialize_pre_from_actual callee_proc_name call_location ~pre ~formal ~actual call_state = let materialize_pre_from_actual callee_proc_name call_location ~pre ~formal ~actual:(actual, typ)
call_state =
L.d_printfln "Materializing PRE from [%a <- %a]" Var.pp formal AbstractValue.pp (fst actual) ; L.d_printfln "Materializing PRE from [%a <- %a]" Var.pp formal AbstractValue.pp (fst actual) ;
(let open IOption.Let_syntax in (let open IOption.Let_syntax in
let* addr_formal_pre, _ = BaseStack.find_opt formal pre.BaseDomain.stack in let* addr_formal_pre, _ = BaseStack.find_opt formal pre.BaseDomain.stack in
let+ formal_pre, _ = BaseMemory.find_edge_opt addr_formal_pre Dereference pre.BaseDomain.heap in let+ formal_pre = deref_non_c_struct addr_formal_pre typ pre.BaseDomain.heap in
materialize_pre_from_address callee_proc_name call_location ~pre ~addr_pre:formal_pre materialize_pre_from_address callee_proc_name call_location ~pre ~addr_pre:formal_pre
~addr_hist_caller:actual call_state) ~addr_hist_caller:actual call_state)
|> function Some result -> result | None -> Ok call_state |> function Some result -> result | None -> Ok call_state
@ -236,7 +245,7 @@ let materialize_pre_for_parameters callee_proc_name call_location pre_post ~form
call [materialize_pre_from] on them. Give up if calling the function introduces aliasing. call [materialize_pre_from] on them. Give up if calling the function introduces aliasing.
*) *)
match match
IList.fold2_result formals actuals ~init:call_state ~f:(fun call_state formal (actual, _) -> IList.fold2_result formals actuals ~init:call_state ~f:(fun call_state formal actual ->
materialize_pre_from_actual callee_proc_name call_location materialize_pre_from_actual callee_proc_name call_location
~pre:(pre_post.AbductiveDomain.pre :> BaseDomain.t) ~pre:(pre_post.AbductiveDomain.pre :> BaseDomain.t)
~formal ~actual call_state ) ~formal ~actual call_state )
@ -468,15 +477,16 @@ let rec record_post_for_address callee_proc_name call_loc ({AbductiveDomain.pre;
~addr_hist_caller:addr_hist_curr_dest call_state ) ) ~addr_hist_caller:addr_hist_curr_dest call_state ) )
let record_post_for_actual callee_proc_name call_loc pre_post ~formal ~actual call_state = let record_post_for_actual callee_proc_name call_loc pre_post ~formal ~actual:(actual, typ)
call_state =
L.d_printfln_escaped "Recording POST from [%a] <-> %a" Var.pp formal AbstractValue.pp (fst actual) ; L.d_printfln_escaped "Recording POST from [%a] <-> %a" Var.pp formal AbstractValue.pp (fst actual) ;
match match
let open IOption.Let_syntax in let open IOption.Let_syntax in
let* addr_formal_pre, _ = let* addr_formal_pre, _ =
BaseStack.find_opt formal (pre_post.AbductiveDomain.pre :> BaseDomain.t).BaseDomain.stack BaseStack.find_opt formal (pre_post.AbductiveDomain.pre :> BaseDomain.t).BaseDomain.stack
in in
let+ formal_pre, _ = let+ formal_pre =
BaseMemory.find_edge_opt addr_formal_pre Dereference deref_non_c_struct addr_formal_pre typ
(pre_post.AbductiveDomain.pre :> BaseDomain.t).BaseDomain.heap (pre_post.AbductiveDomain.pre :> BaseDomain.t).BaseDomain.heap
in in
record_post_for_address callee_proc_name call_loc pre_post ~addr_callee:formal_pre record_post_for_address callee_proc_name call_loc pre_post ~addr_callee:formal_pre
@ -525,7 +535,7 @@ let apply_post_for_parameters callee_proc_name call_location pre_post ~formals ~
between pre and post since it's unreliable, eg replace value read in pre with same value in between pre and post since it's unreliable, eg replace value read in pre with same value in
post but nuke other fields in the meantime? is that possible?). *) post but nuke other fields in the meantime? is that possible?). *)
match match
List.fold2 formals actuals ~init:call_state ~f:(fun call_state formal (actual, _) -> List.fold2 formals actuals ~init:call_state ~f:(fun call_state formal actual ->
record_post_for_actual callee_proc_name call_location pre_post ~formal ~actual call_state ) record_post_for_actual callee_proc_name call_location pre_post ~formal ~actual call_state )
with with
| Unequal_lengths -> | Unequal_lengths ->

@ -14,7 +14,7 @@ val apply_prepost :
-> Procname.t -> Procname.t
-> Location.t -> Location.t
-> callee_prepost:AbductiveDomain.t -> callee_prepost:AbductiveDomain.t
-> captured_vars_with_actuals:(Var.t * (AbstractValue.t * ValueHistory.t)) list -> captured_vars_with_actuals:(Var.t * ((AbstractValue.t * ValueHistory.t) * Typ.t)) list
-> formals:Var.t list -> formals:Var.t list
-> actuals:((AbstractValue.t * ValueHistory.t) * Typ.t) list -> actuals:((AbstractValue.t * ValueHistory.t) * Typ.t) list
-> AbductiveDomain.t -> AbductiveDomain.t

@ -561,12 +561,12 @@ let get_captured_actuals location ~captured_vars ~actual_closure astate =
let* astate, this_value_addr = eval_access Read location actual_closure Dereference astate in let* astate, this_value_addr = eval_access Read location actual_closure Dereference astate in
let+ _, astate, captured_vars_with_actuals = let+ _, astate, captured_vars_with_actuals =
List.fold_result captured_vars ~init:(0, astate, []) List.fold_result captured_vars ~init:(0, astate, [])
~f:(fun (id, astate, captured) (var, mode) -> ~f:(fun (id, astate, captured) (var, mode, typ) ->
let+ astate, captured_actual = let+ astate, captured_actual =
eval_access Read location this_value_addr eval_access Read location this_value_addr
(FieldAccess (Closures.mk_fake_field ~id mode)) (FieldAccess (Closures.mk_fake_field ~id mode))
astate astate
in in
(id + 1, astate, (var, captured_actual) :: captured) ) (id + 1, astate, (var, (captured_actual, typ)) :: captured) )
in in
(astate, captured_vars_with_actuals) (astate, captured_vars_with_actuals)

@ -204,7 +204,7 @@ val check_address_escape :
val get_captured_actuals : val get_captured_actuals :
Location.t Location.t
-> captured_vars:(Var.t * Pvar.capture_mode) list -> captured_vars:(Var.t * Pvar.capture_mode * Typ.t) list
-> actual_closure:AbstractValue.t * ValueHistory.t -> actual_closure:AbstractValue.t * ValueHistory.t
-> t -> t
-> (t * (Var.t * (AbstractValue.t * ValueHistory.t)) list) AccessResult.t -> (t * (Var.t * ((AbstractValue.t * ValueHistory.t) * Typ.t)) list) AccessResult.t

@ -70,7 +70,6 @@ codetoanalyze/cpp/pulse/optional.cpp, inside_try_catch_FP, 3, OPTIONAL_EMPTY_ACC
codetoanalyze/cpp/pulse/optional.cpp, none_copy_bad, 3, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),passed as argument to `folly::Optional::Optional(folly::Optional<Value> arg)` (modelled),return from call to `folly::Optional::Optional(folly::Optional<Value> arg)` (modelled),invalid access occurs here] codetoanalyze/cpp/pulse/optional.cpp, none_copy_bad, 3, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),passed as argument to `folly::Optional::Optional(folly::Optional<Value> arg)` (modelled),return from call to `folly::Optional::Optional(folly::Optional<Value> arg)` (modelled),invalid access occurs here]
codetoanalyze/cpp/pulse/optional.cpp, none_no_check_bad, 2, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),invalid access occurs here] codetoanalyze/cpp/pulse/optional.cpp, none_no_check_bad, 2, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),invalid access occurs here]
codetoanalyze/cpp/pulse/optional.cpp, not_none_check_value_ok_FP, 5, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),invalid access occurs here] codetoanalyze/cpp/pulse/optional.cpp, not_none_check_value_ok_FP, 5, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),invalid access occurs here]
codetoanalyze/cpp/pulse/optional.cpp, operator_arrow_bad, 0, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),when calling `emplace` here,parameter `state` of emplace,passed as argument to `folly::Optional::operator->()` (modelled),return from call to `folly::Optional::operator->()` (modelled),invalid access occurs here]
codetoanalyze/cpp/pulse/optional.cpp, std_assign2_bad, 4, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::operator=(None)` (modelled),return from call to `std::optional::operator=(None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::operator=(None)` (modelled),return from call to `std::optional::operator=(None)` (modelled),invalid access occurs here] codetoanalyze/cpp/pulse/optional.cpp, std_assign2_bad, 4, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::operator=(None)` (modelled),return from call to `std::optional::operator=(None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::operator=(None)` (modelled),return from call to `std::optional::operator=(None)` (modelled),invalid access occurs here]
codetoanalyze/cpp/pulse/optional.cpp, std_assign_bad, 5, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),passed as argument to `std::optional::operator=(std::optional<Value> arg)` (modelled),return from call to `std::optional::operator=(std::optional<Value> arg)` (modelled),invalid access occurs here] codetoanalyze/cpp/pulse/optional.cpp, std_assign_bad, 5, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),passed as argument to `std::optional::operator=(std::optional<Value> arg)` (modelled),return from call to `std::optional::operator=(std::optional<Value> arg)` (modelled),invalid access occurs here]
codetoanalyze/cpp/pulse/optional.cpp, std_none_copy_bad, 3, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),passed as argument to `std::optional::optional(std::optional<Value> arg)` (modelled),return from call to `std::optional::optional(std::optional<Value> arg)` (modelled),invalid access occurs here] codetoanalyze/cpp/pulse/optional.cpp, std_none_copy_bad, 3, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),passed as argument to `std::optional::optional(std::optional<Value> arg)` (modelled),return from call to `std::optional::optional(std::optional<Value> arg)` (modelled),invalid access occurs here]

@ -148,7 +148,8 @@ void emplace(folly::Optional<State> state) {
auto pos = state->vec.begin(); auto pos = state->vec.begin();
} }
void operator_arrow_bad() { emplace(folly::none); } /* There is a bug in the frontend T89443328 */
void operator_arrow_bad_FN() { emplace(folly::none); }
void get_pointer_check_none_check_ok() { void get_pointer_check_none_check_ok() {
folly::Optional<int> foo{folly::none}; folly::Optional<int> foo{folly::none};

@ -15,7 +15,7 @@ digraph cfg {
"getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_4" -> "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_2" ; "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_4" -> "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_2" ;
"getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_5" [label="5: Message Call: setS: \n n$8=*&target:A* [line 27, column 3]\n n$7=*&my_struct:my_struct [line 27, column 14]\n n$9=_fun_A.setS:(n$8:A*,n$7:my_struct) [line 27, column 10]\n " shape="box"] "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_5" [label="5: Message Call: setS: \n n$7=*&target:A* [line 27, column 3]\n n$8=_fun_A.setS:(n$7:A*,&my_struct:my_struct) [line 27, column 10]\n " shape="box"]
"getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_5" -> "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_3" ; "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_5" -> "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_3" ;

@ -9,5 +9,6 @@ codetoanalyze/objc/pulse/MemoryLeaks.m, call_cfrelease_interproc_leak_ok_FP, 2,
codetoanalyze/objc/pulse/MemoryLeaksInBlocks.m, block_captured_var_leak_bad, 6, MEMORY_LEAK, no_bucket, ERROR, [allocation part of the trace starts here,allocated by call to `malloc_no_fail` (modelled),allocation part of the trace ends here,memory becomes unreachable here] codetoanalyze/objc/pulse/MemoryLeaksInBlocks.m, block_captured_var_leak_bad, 6, MEMORY_LEAK, no_bucket, ERROR, [allocation part of the trace starts here,allocated by call to `malloc_no_fail` (modelled),allocation part of the trace ends here,memory becomes unreachable here]
codetoanalyze/objc/pulse/NPEBlocks.m, Singleton.dispatch_once_no_npe_good_FP, 6, NULLPTR_DEREFERENCE, no_bucket, ERROR, [source of the null value part of the trace starts here,assigned,is the null pointer,null pointer dereference part of the trace starts here,assigned,invalid access occurs here] codetoanalyze/objc/pulse/NPEBlocks.m, Singleton.dispatch_once_no_npe_good_FP, 6, NULLPTR_DEREFERENCE, no_bucket, ERROR, [source of the null value part of the trace starts here,assigned,is the null pointer,null pointer dereference part of the trace starts here,assigned,invalid access occurs here]
codetoanalyze/objc/pulse/NPEBlocks.m, captured_npe_bad, 5, NULLPTR_DEREFERENCE, no_bucket, ERROR, [source of the null value part of the trace starts here,assigned,is the null pointer,null pointer dereference part of the trace starts here,assigned,when calling `objc_blockcaptured_npe_bad_3` here,parameter `x` of objc_blockcaptured_npe_bad_3,invalid access occurs here] codetoanalyze/objc/pulse/NPEBlocks.m, captured_npe_bad, 5, NULLPTR_DEREFERENCE, no_bucket, ERROR, [source of the null value part of the trace starts here,assigned,is the null pointer,null pointer dereference part of the trace starts here,assigned,when calling `objc_blockcaptured_npe_bad_3` here,parameter `x` of objc_blockcaptured_npe_bad_3,invalid access occurs here]
codetoanalyze/objc/pulse/uninit.m, Uninit.call_setter_c_struct_bad, 3, PULSE_UNINITIALIZED_VALUE, no_bucket, ERROR, [struct field address `x` created,when calling `Uninit.setS:` here,parameter `s` of Uninit.setS:,read to uninitialized value occurs here]
codetoanalyze/objc/pulse/use_after_free.m, PulseTest.use_after_free_simple_in_objc_method_bad:, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of PulseTest.use_after_free_simple_in_objc_method_bad:,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of PulseTest.use_after_free_simple_in_objc_method_bad:,invalid access occurs here] codetoanalyze/objc/pulse/use_after_free.m, PulseTest.use_after_free_simple_in_objc_method_bad:, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of PulseTest.use_after_free_simple_in_objc_method_bad:,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of PulseTest.use_after_free_simple_in_objc_method_bad:,invalid access occurs here]
codetoanalyze/objc/pulse/use_after_free.m, use_after_free_simple_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of use_after_free_simple_bad,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of use_after_free_simple_bad,invalid access occurs here] codetoanalyze/objc/pulse/use_after_free.m, use_after_free_simple_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of use_after_free_simple_bad,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of use_after_free_simple_bad,invalid access occurs here]

@ -130,10 +130,9 @@ dispatch_queue_t queue;
return [Uninit getter_c_struct:obj]; return [Uninit getter_c_struct:obj];
} }
/* FN due to the frontend is incorrect when passing C struct value as a function + (void)call_setter_c_struct_bad {
paraemeter when calling C struct setter. */
+ (void)call_setter_c_struct_bad_FN:(Uninit*)obj {
struct my_struct s; struct my_struct s;
Uninit* obj = [Uninit new];
obj.s = s; // setter is called obj.s = s; // setter is called
} }

Loading…
Cancel
Save