[frontend] Handle struct copy in C

Summary:
When there was an assignment of C struct, `x = y;`, it was translated to the statements of load and store.

```
n$0 = *y
*x = n$0
```

However, this is incorrect in Sil, because a struct is not a value that can be assigned to registers.  This diff fixes the translation as assignments of each field values :

```
n$0 = *y.field1
*x.field1 = n$0
n$0 = *y.field2
*x.field2 = n$0
...
```

It copies field values of C structs on:

* assign statement
* return statement
* declarations.

It supports nested structs.

Reviewed By: jvillard

Differential Revision: D25952894

fbshipit-source-id: 355f8db9c
master
Sungkeun Cho 4 years ago committed by Facebook GitHub Bot
parent e11b1b49b3
commit 1bce54aaf3

@ -987,6 +987,25 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
mk_trans_result (array_exp, typ) control
and struct_copy tenv loc e1 e2 typ struct_name =
let rec struct_copy_helper e1 e2 typ struct_name rev_acc =
let {Struct.fields} = Option.value_exn (Tenv.lookup tenv struct_name) in
List.fold fields ~init:rev_acc ~f:(fun rev_acc (field_name, field_typ, _) ->
let mk_field e = Exp.Lfield (e, field_name, typ) in
let e1 = mk_field e1 in
let e2 = mk_field e2 in
match field_typ.Typ.desc with
| Tstruct (CStruct _ as struct_name) ->
struct_copy_helper e1 e2 field_typ struct_name rev_acc
| _ ->
let id = Ident.create_fresh Ident.knormal in
Sil.Store {e1; root_typ= field_typ; typ= field_typ; e2= Exp.Var id; loc}
:: Sil.Load {id; e= e2; root_typ= field_typ; typ= field_typ; loc}
:: rev_acc )
in
if Exp.equal e1 e2 then [] else struct_copy_helper e1 e2 typ struct_name [] |> List.rev
and binaryOperator_trans trans_state binary_operator_info stmt_info expr_info stmt_list =
L.debug Capture Verbose " BinaryOperator '%a' "
(Pp.of_string ~f:Clang_ast_j.string_of_binary_operator_kind)
@ -1003,8 +1022,21 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let res_typ =
CType_decl.qual_type_to_sil_type context.CContext.tenv expr_info.Clang_ast_t.ei_qual_type
in
match stmt_list with
| [s1; s2] ->
match (stmt_list, res_typ.desc, binary_operator_info.Clang_ast_t.boi_kind) with
| ( [s1; Clang_ast_t.ImplicitCastExpr (_, [s2], _, _)]
, Tstruct (CStruct _ as struct_name)
, `Assign ) ->
let res_trans_e1, res_trans_e2 =
(instruction trans_state' s1, instruction trans_state' s2)
in
let instrs =
struct_copy context.CContext.tenv sil_loc (fst res_trans_e1.return)
(fst res_trans_e2.return) res_typ struct_name
in
[res_trans_e1.control; res_trans_e2.control; {empty_control with instrs}]
|> PriorityNode.compute_controls_to_parent trans_state_pri sil_loc node_name stmt_info
|> mk_trans_result res_trans_e1.return
| [s1; s2], _, _ ->
(* Assumption: We expect precisely 2 stmt corresponding to the 2 operands*)
(* NOTE: we create a node only if required. In that case this node *)
(* becomes the successor of the nodes that may be created when *)
@ -1061,7 +1093,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
PriorityNode.compute_controls_to_parent trans_state_pri sil_loc node_name stmt_info
all_res_trans
|> mk_trans_result return
| _ ->
| _, _, _ ->
(* Binary operator should have two operands *)
assert false
@ -2512,7 +2544,14 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
and init_expr_trans_aux trans_state var_exp_typ var_stmt_info init_expr =
(* For init expr, translate how to compute it and assign to the var *)
let var_exp, _var_typ = var_exp_typ in
let var_exp, var_typ = var_exp_typ in
let cstruct_name_opt =
match var_typ.Typ.desc with
| Tstruct (CStruct _ as struct_name) ->
Some struct_name
| _ ->
None
in
let context = trans_state.context in
let sil_loc =
CLocation.location_of_stmt_info context.translation_unit_context.source_file var_stmt_info
@ -2523,6 +2562,14 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let res_trans_ie =
let trans_state' = {trans_state_pri with succ_nodes= []; var_exp_typ= Some var_exp_typ} in
let instruction' = exec_with_glvalue_as_reference instruction in
let init_expr =
match init_expr with
| Clang_ast_t.ImplicitCastExpr (_, [init_expr], _, _) when Option.is_some cstruct_name_opt
->
init_expr
| _ ->
init_expr
in
exec_with_block_priority_exception instruction' trans_state' init_expr var_stmt_info
in
L.debug Capture Verbose "init expr result: %a@\n" pp_control res_trans_ie.control ;
@ -2544,10 +2591,14 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let sil_e1', ie_typ = res_trans_ie.return in
L.debug Capture Verbose "Sub-expr did not initialize %a, initializing with %a@\n" Exp.pp
var_exp Exp.pp sil_e1' ;
Some
{ empty_control with
instrs=
[Sil.Store {e1= var_exp; root_typ= ie_typ; typ= ie_typ; e2= sil_e1'; loc= sil_loc}] }
let instrs =
match cstruct_name_opt with
| Some struct_name ->
struct_copy context.CContext.tenv sil_loc var_exp sil_e1' var_typ struct_name
| None ->
[Sil.Store {e1= var_exp; root_typ= ie_typ; typ= ie_typ; e2= sil_e1'; loc= sil_loc}]
in
Some {empty_control with instrs}
in
let all_res_trans = res_trans_ie.control :: Option.to_list assign_trans_control_opt in
L.debug Capture Verbose "sending init_expr_trans results to parent@\n" ;
@ -2876,72 +2927,77 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
~exn:[] ;
ret_node
in
match stmt_list with
| [stmt] ->
(* return exp; *)
let ret_type = Procdesc.get_ret_type procdesc in
let ret_exp, ret_typ, var_control =
match context.CContext.return_param_typ with
| Some ret_param_typ ->
let name = CFrontend_config.return_param in
let pvar = Pvar.mk (Mangled.from_string name) procname in
let id = Ident.create_fresh Ident.knormal in
let instr =
Sil.Load
{id; e= Exp.Lvar pvar; root_typ= ret_param_typ; typ= ret_param_typ; loc= sil_loc}
in
let ret_typ =
match ret_param_typ.desc with Typ.Tptr (t, _) -> t | _ -> assert false
in
(Exp.Var id, ret_typ, Some {empty_control with instrs= [instr]})
| None ->
(Exp.Lvar (Procdesc.get_ret_var procdesc), ret_type, None)
in
let trans_state' =
{trans_state_pri with succ_nodes= []; var_exp_typ= Some (ret_exp, ret_typ)}
in
L.debug Capture Verbose "Evaluating sub-expr of return@\n" ;
let res_trans_stmt = instruction trans_state' stmt in
L.debug Capture Verbose "Done evaluating sub-expr of return@\n" ;
let controls =
let var_control =
Option.map var_control ~f:(fun control ->
(* force node creation to place the load instruction [var_control] before the
translation of the sub-expr *)
let trans_state =
PriorityNode.force_claim_priority_node trans_state_pri stmt_info
in
PriorityNode.compute_control_to_parent trans_state sil_loc ReturnStmt stmt_info
control )
in
PriorityNode.compute_controls_to_parent trans_state' sil_loc ReturnStmt stmt_info
(Option.to_list var_control @ [res_trans_stmt.control])
in
let ret_instrs =
if List.exists ~f:(Exp.equal ret_exp) res_trans_stmt.control.initd_exps then []
else
let sil_expr, _ = res_trans_stmt.return in
[Sil.Store {e1= ret_exp; root_typ= ret_type; typ= ret_typ; e2= sil_expr; loc= sil_loc}]
in
let ret_node = mk_ret_node ret_instrs in
L.debug Capture Verbose "Created return node %a with instrs [%a]@\n" Procdesc.Node.pp
ret_node
(Pp.seq ~sep:";" (Sil.pp_instr ~print_types:false Pp.text))
ret_instrs ;
assert (List.is_empty controls.instrs) ;
List.iter controls.leaf_nodes ~f:(fun leaf ->
Procdesc.set_succs leaf ~normal:(Some [ret_node]) ~exn:None ) ;
let ret_control =
{empty_control with root_nodes= [ret_node]; leaf_nodes= [ret_node]; instrs= []}
let return_stmt stmt ~mk_ret_instrs =
let ret_typ_of_pdesc = Procdesc.get_ret_type procdesc in
let ret_exp, ret_typ, var_control =
match context.CContext.return_param_typ with
| Some ret_param_typ ->
let name = CFrontend_config.return_param in
let pvar = Pvar.mk (Mangled.from_string name) procname in
let id = Ident.create_fresh Ident.knormal in
let instr =
Sil.Load
{id; e= Exp.Lvar pvar; root_typ= ret_param_typ; typ= ret_param_typ; loc= sil_loc}
in
let ret_typ =
match ret_param_typ.desc with Typ.Tptr (t, _) -> t | _ -> assert false
in
(Exp.Var id, ret_typ, Some {empty_control with instrs= [instr]})
| None ->
(Exp.Lvar (Procdesc.get_ret_var procdesc), ret_typ_of_pdesc, None)
in
let trans_state' =
{trans_state_pri with succ_nodes= []; var_exp_typ= Some (ret_exp, ret_typ)}
in
L.debug Capture Verbose "Evaluating sub-expr of return@\n" ;
let res_trans_stmt = instruction trans_state' stmt in
L.debug Capture Verbose "Done evaluating sub-expr of return@\n" ;
let controls =
let var_control =
Option.map var_control ~f:(fun control ->
(* force node creation to place the load instruction [var_control] before the
translation of the sub-expr *)
let trans_state = PriorityNode.force_claim_priority_node trans_state_pri stmt_info in
PriorityNode.compute_control_to_parent trans_state sil_loc ReturnStmt stmt_info
control )
in
PriorityNode.compute_controls_to_parent trans_state_pri sil_loc ReturnStmt stmt_info
[controls; ret_control]
|> mk_trans_result res_trans_stmt.return
| [] ->
PriorityNode.compute_controls_to_parent trans_state' sil_loc ReturnStmt stmt_info
(Option.to_list var_control @ [res_trans_stmt.control])
in
let ret_instrs = mk_ret_instrs ret_exp ret_typ_of_pdesc ret_typ res_trans_stmt in
let ret_node = mk_ret_node ret_instrs in
L.debug Capture Verbose "Created return node %a with instrs [%a]@\n" Procdesc.Node.pp ret_node
(Pp.seq ~sep:";" (Sil.pp_instr ~print_types:false Pp.text))
ret_instrs ;
assert (List.is_empty controls.instrs) ;
List.iter controls.leaf_nodes ~f:(fun leaf ->
Procdesc.set_succs leaf ~normal:(Some [ret_node]) ~exn:None ) ;
let ret_control =
{empty_control with root_nodes= [ret_node]; leaf_nodes= [ret_node]; instrs= []}
in
PriorityNode.compute_controls_to_parent trans_state_pri sil_loc ReturnStmt stmt_info
[controls; ret_control]
|> mk_trans_result res_trans_stmt.return
in
match (stmt_list, context.CContext.return_param_typ) with
| ( ([Clang_ast_t.ImplicitCastExpr (_, [stmt], _, _)] | [stmt])
, Some {desc= Tptr ({desc= Tstruct (CStruct _ as struct_name)}, _)} ) ->
(* return (exp:struct); *)
return_stmt stmt ~mk_ret_instrs:(fun ret_exp _root_typ ret_typ res_trans_stmt ->
struct_copy context.CContext.tenv sil_loc ret_exp (fst res_trans_stmt.return) ret_typ
struct_name )
| [stmt], _ ->
(* return exp; *)
return_stmt stmt ~mk_ret_instrs:(fun ret_exp root_typ ret_typ res_trans_stmt ->
if List.exists ~f:(Exp.equal ret_exp) res_trans_stmt.control.initd_exps then []
else
let sil_expr, _ = res_trans_stmt.return in
[Sil.Store {e1= ret_exp; root_typ; typ= ret_typ; e2= sil_expr; loc= sil_loc}] )
| [], _ ->
(* return; *)
let ret_node = mk_ret_node [] in
mk_trans_result (mk_fresh_void_exp_typ ()) {empty_control with root_nodes= [ret_node]}
| _ ->
| _, _ ->
assert false

@ -297,6 +297,12 @@ codetoanalyze/c/bufferoverrun/sizeof.c, eval_sizeof_bad, 1, CONDITION_ALWAYS_TRU
codetoanalyze/c/bufferoverrun/sizeof.c, eval_sizeof_bad, 4, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 1 Size: 0]
codetoanalyze/c/bufferoverrun/sizeof.c, static_stride_bad, 5, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here]
codetoanalyze/c/bufferoverrun/sizeof.c, static_stride_bad, 7, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 1 Size: 0]
codetoanalyze/c/bufferoverrun/struct_copy.c, nested_struct_copy_Bad, 7, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Offset trace>,Assignment,Assignment,<Length trace>,Array declaration,Array access: Offset: 5 Size: 3]
codetoanalyze/c/bufferoverrun/struct_copy.c, struct_copy_Bad, 6, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Offset trace>,Assignment,Assignment,<Length trace>,Array declaration,Array access: Offset: 5 Size: 3]
codetoanalyze/c/bufferoverrun/struct_copy.c, struct_copy_decl_Bad, 6, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Offset trace>,Assignment,Assignment,<Length trace>,Array declaration,Array access: Offset: 5 Size: 3]
codetoanalyze/c/bufferoverrun/struct_copy.c, struct_copy_decl_by_function_Bad, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Offset trace>,Call,Assignment,Assignment,<Length trace>,Array declaration,Array access: Offset: 5 Size: 3]
codetoanalyze/c/bufferoverrun/struct_copy.c, struct_copy_from_function_call_Bad, 4, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Offset trace>,Call,Assignment,Assignment,<Length trace>,Array declaration,Array access: Offset: 5 Size: 3]
codetoanalyze/c/bufferoverrun/struct_copy.c, struct_copy_from_wrapper_call_Bad, 4, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Offset trace>,Call,Call,Assignment,Assignment,<Length trace>,Array declaration,Array access: Offset: 5 Size: 3]
codetoanalyze/c/bufferoverrun/trivial.c, differentiate_array_info_Bad, 8, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Assignment,Array access: Offset: 5 Size: 5]
codetoanalyze/c/bufferoverrun/trivial.c, differentiate_array_info_Bad, 8, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Assignment,Array access: Offset: 10 (⇐ 5 + 5) Size: 10]
codetoanalyze/c/bufferoverrun/trivial.c, malloc_zero_Bad, 2, INFERBO_ALLOC_IS_ZERO, no_bucket, ERROR, [Allocation: Length: 0]

@ -0,0 +1,123 @@
/*
* 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.
*/
struct s {
int a;
int b;
};
void struct_copy_Ok() {
int a[5];
struct s x, y;
x.a = 3;
x.b = 5;
y = x;
a[y.a] = 0;
}
void struct_copy_Bad() {
int a[3];
struct s x, y;
x.a = 3;
x.b = 5;
y = x;
a[y.b] = 0;
}
struct t {
struct s s;
int c;
};
void nested_struct_copy_Ok() {
int a[7];
struct t x, y;
x.s.a = 3;
x.s.b = 5;
x.c = 7;
y = x;
a[y.s.a] = 0;
}
void nested_struct_copy_Bad() {
int a[3];
struct t x, y;
x.s.a = 3;
x.s.b = 5;
x.c = 7;
y = x;
a[y.s.b] = 0;
}
struct s get_struct() {
struct s x;
x.a = 3;
x.b = 5;
return x;
}
void struct_copy_from_function_call_Ok() {
int a[5];
struct s x;
x = get_struct();
a[x.a] = 0;
}
void struct_copy_from_function_call_Bad() {
int a[3];
struct s x;
x = get_struct();
a[x.b] = 0;
}
struct s get_struct_wrapper() {
return get_struct();
}
void struct_copy_from_wrapper_call_Ok() {
int a[5];
struct s x;
x = get_struct_wrapper();
a[x.a] = 0;
}
void struct_copy_from_wrapper_call_Bad() {
int a[3];
struct s x;
x = get_struct_wrapper();
a[x.b] = 0;
}
void struct_copy_decl_Ok() {
int a[5];
struct s x;
x.a = 3;
x.b = 5;
struct s y = x;
a[y.a] = 0;
}
void struct_copy_decl_Bad() {
int a[3];
struct s x;
x.a = 3;
x.b = 5;
struct s y = x;
a[y.b] = 0;
}
void struct_copy_decl_by_function_Ok() {
int a[5];
struct s x = get_struct();
a[x.a] = 0;
}
void struct_copy_decl_by_function_Bad() {
int a[3];
struct s x = get_struct();
a[x.b] = 0;
}

@ -30,7 +30,7 @@ digraph cfg {
"init_with_compound_literal.745ef6cf3c32f7f18974c2c4fc6a8c9c_4" -> "init_with_compound_literal.745ef6cf3c32f7f18974c2c4fc6a8c9c_2" ;
"init_with_compound_literal.745ef6cf3c32f7f18974c2c4fc6a8c9c_5" [label="5: DeclStmt \n VARIABLE_DECLARED(p:point); [line 16, column 3]\n *&p.x:int=32 [line 16, column 34]\n *&p.y:int=52 [line 16, column 34]\n n$1=*&p:point [line 16, column 20]\n " shape="box"]
"init_with_compound_literal.745ef6cf3c32f7f18974c2c4fc6a8c9c_5" [label="5: DeclStmt \n VARIABLE_DECLARED(p:point); [line 16, column 3]\n *&p.x:int=32 [line 16, column 34]\n *&p.y:int=52 [line 16, column 34]\n " shape="box"]
"init_with_compound_literal.745ef6cf3c32f7f18974c2c4fc6a8c9c_5" -> "init_with_compound_literal.745ef6cf3c32f7f18974c2c4fc6a8c9c_3" ;

@ -30,7 +30,7 @@ digraph cfg {
"foo.acbd18db4cc2f85cedef654fccc4a4d8_3" -> "foo.acbd18db4cc2f85cedef654fccc4a4d8_2" ;
"implicit_expr_set_correctly.dcfe49f71ad24e86323cbad97b1a70fe_1" [label="1: Start implicit_expr_set_correctly\nFormals: \nLocals: imageDrawRect:rect \n " color=yellow style=filled]
"implicit_expr_set_correctly.dcfe49f71ad24e86323cbad97b1a70fe_1" [label="1: Start implicit_expr_set_correctly\nFormals: \nLocals: 0$?%__sil_tmpSIL_compound_literal__n$1:rect imageDrawRect:rect \n " color=yellow style=filled]
"implicit_expr_set_correctly.dcfe49f71ad24e86323cbad97b1a70fe_1" -> "implicit_expr_set_correctly.dcfe49f71ad24e86323cbad97b1a70fe_5" ;
@ -45,7 +45,7 @@ digraph cfg {
"implicit_expr_set_correctly.dcfe49f71ad24e86323cbad97b1a70fe_4" -> "implicit_expr_set_correctly.dcfe49f71ad24e86323cbad97b1a70fe_2" ;
"implicit_expr_set_correctly.dcfe49f71ad24e86323cbad97b1a70fe_5" [label="5: BinaryOperatorStmt: Assign \n *&imageDrawRect.origin.x.a:int=0 [line 56, column 35]\n *&imageDrawRect.origin.x.b:int=0 [line 56, column 35]\n *&imageDrawRect.origin.y:int=0 [line 56, column 35]\n *&imageDrawRect.z:int=0 [line 56, column 35]\n *&imageDrawRect.size:int=5 [line 56, column 25]\n n$1=*&imageDrawRect:rect [line 56, column 19]\n " shape="box"]
"implicit_expr_set_correctly.dcfe49f71ad24e86323cbad97b1a70fe_5" [label="5: BinaryOperatorStmt: Assign \n *&0$?%__sil_tmpSIL_compound_literal__n$1.origin.x.a:int=0 [line 56, column 35]\n *&0$?%__sil_tmpSIL_compound_literal__n$1.origin.x.b:int=0 [line 56, column 35]\n *&0$?%__sil_tmpSIL_compound_literal__n$1.origin.y:int=0 [line 56, column 35]\n *&0$?%__sil_tmpSIL_compound_literal__n$1.z:int=0 [line 56, column 35]\n *&0$?%__sil_tmpSIL_compound_literal__n$1.size:int=5 [line 56, column 25]\n n$2=*&0$?%__sil_tmpSIL_compound_literal__n$1.origin.x.a:int [line 56, column 3]\n *&imageDrawRect.origin.x.a:int=n$2 [line 56, column 3]\n n$3=*&0$?%__sil_tmpSIL_compound_literal__n$1.origin.x.b:int [line 56, column 3]\n *&imageDrawRect.origin.x.b:int=n$3 [line 56, column 3]\n n$4=*&0$?%__sil_tmpSIL_compound_literal__n$1.origin.y:int [line 56, column 3]\n *&imageDrawRect.origin.y:int=n$4 [line 56, column 3]\n n$5=*&0$?%__sil_tmpSIL_compound_literal__n$1.z:int [line 56, column 3]\n *&imageDrawRect.z:int=n$5 [line 56, column 3]\n n$6=*&0$?%__sil_tmpSIL_compound_literal__n$1.size:int [line 56, column 3]\n *&imageDrawRect.size:int=n$6 [line 56, column 3]\n " shape="box"]
"implicit_expr_set_correctly.dcfe49f71ad24e86323cbad97b1a70fe_5" -> "implicit_expr_set_correctly.dcfe49f71ad24e86323cbad97b1a70fe_3" ;
@ -60,7 +60,7 @@ digraph cfg {
"main.fad58de7366495db4650cfefac2fcd61_3" -> "main.fad58de7366495db4650cfefac2fcd61_2" ;
"point_coords_set_correctly.3abf7d8dcf379339f0fa9b69df909b28_1" [label="1: Start point_coords_set_correctly\nFormals: p:Point*\nLocals: \n " color=yellow style=filled]
"point_coords_set_correctly.3abf7d8dcf379339f0fa9b69df909b28_1" [label="1: Start point_coords_set_correctly\nFormals: p:Point*\nLocals: 0$?%__sil_tmpSIL_compound_literal__n$2:Point \n " color=yellow style=filled]
"point_coords_set_correctly.3abf7d8dcf379339f0fa9b69df909b28_1" -> "point_coords_set_correctly.3abf7d8dcf379339f0fa9b69df909b28_5" ;
@ -75,7 +75,7 @@ digraph cfg {
"point_coords_set_correctly.3abf7d8dcf379339f0fa9b69df909b28_4" -> "point_coords_set_correctly.3abf7d8dcf379339f0fa9b69df909b28_2" ;
"point_coords_set_correctly.3abf7d8dcf379339f0fa9b69df909b28_5" [label="5: BinaryOperatorStmt: Assign \n n$2=*&p:Point* [line 18, column 4]\n *n$2.x:int=4 [line 18, column 15]\n *n$2.y:int=5 [line 18, column 15]\n n$3=*n$2:Point [line 18, column 8]\n " shape="box"]
"point_coords_set_correctly.3abf7d8dcf379339f0fa9b69df909b28_5" [label="5: BinaryOperatorStmt: Assign \n n$3=*&p:Point* [line 18, column 4]\n *&0$?%__sil_tmpSIL_compound_literal__n$2.x:int=4 [line 18, column 15]\n *&0$?%__sil_tmpSIL_compound_literal__n$2.y:int=5 [line 18, column 15]\n n$4=*&0$?%__sil_tmpSIL_compound_literal__n$2.x:int [line 18, column 3]\n *n$3.x:int=n$4 [line 18, column 3]\n n$5=*&0$?%__sil_tmpSIL_compound_literal__n$2.y:int [line 18, column 3]\n *n$3.y:int=n$5 [line 18, column 3]\n " shape="box"]
"point_coords_set_correctly.3abf7d8dcf379339f0fa9b69df909b28_5" -> "point_coords_set_correctly.3abf7d8dcf379339f0fa9b69df909b28_3" ;

@ -22,7 +22,7 @@ digraph cfg {
"struct_init_test.b3909a459f16e15611cc425c52c74b0c_3" -> "struct_init_test.b3909a459f16e15611cc425c52c74b0c_4" ;
"struct_init_test.b3909a459f16e15611cc425c52c74b0c_4" [label="4: Return Stmt \n *n$0.a:double=-1 [line 27, column 29]\n *n$0.b:double=0 [line 27, column 29]\n *n$0.c:double=-0 [line 27, column 29]\n *n$0.d:double=-1 [line 27, column 29]\n *n$0.tx:double=0. [line 27, column 62]\n *n$0.ty:double=0. [line 27, column 62]\n n$1=*n$0:CGAffineTransform [line 27, column 10]\n " shape="box"]
"struct_init_test.b3909a459f16e15611cc425c52c74b0c_4" [label="4: Return Stmt \n *n$0.a:double=-1 [line 27, column 29]\n *n$0.b:double=0 [line 27, column 29]\n *n$0.c:double=-0 [line 27, column 29]\n *n$0.d:double=-1 [line 27, column 29]\n *n$0.tx:double=0. [line 27, column 62]\n *n$0.ty:double=0. [line 27, column 62]\n " shape="box"]
"struct_init_test.b3909a459f16e15611cc425c52c74b0c_4" -> "struct_init_test.b3909a459f16e15611cc425c52c74b0c_5" ;

Loading…
Cancel
Save