[siof] take POD into account

Summary:
SIOF is only for interactions between objects of non-POD types. Previously the
checker was also reporting for POD types.

Reviewed By: akotulski

Differential Revision: D4197620

fbshipit-source-id: 7c56571
master
Jules Villard 8 years ago committed by Facebook Github Bot
parent 05c72f510a
commit 17179d4275

@ -1 +1 @@
Subproject commit 248d630ea8a64a01ca56ccb87f97acd707610660
Subproject commit 18904710494629ff27a5350d715f3a2a228970b6

@ -26,7 +26,7 @@ type pvar_kind =
| Abduced_retvar Procname.t Location.t /** synthetic variable to represent return value */
| Abduced_ref_param Procname.t t Location.t
/** synthetic variable to represent param passed by reference */
| Global_var (DB.source_file, bool) /** global variable: translation unit + is it compile constant? */
| Global_var (DB.source_file, bool, bool) /** global variable: translation unit + is it compile constant? + is it POD? */
| Seed_var /** variable used to store the initial value of formal parameters */
/** Names for program variables. */
and t = {pv_name: Mangled.t, pv_kind: pvar_kind};
@ -62,12 +62,17 @@ let rec pvar_kind_compare k1 k2 =>
}
| (Abduced_ref_param _, _) => (-1)
| (_, Abduced_ref_param _) => 1
| (Global_var (f1, b1), Global_var (f2, b2)) =>
| (Global_var (f1, const1, pod1), Global_var (f2, const2, pod2)) =>
let n = DB.source_file_compare f1 f2;
if (n != 0) {
n
} else {
bool_compare b1 b2
let n = bool_compare const1 const2;
if (n != 0) {
n
} else {
bool_compare pod1 pod2
}
}
| (Global_var _, _) => (-1)
| (_, Global_var _) => 1
@ -111,9 +116,21 @@ let rec _pp f pv => {
} else {
F.fprintf f "%a$%a%a|abducedRefParam" Procname.pp n Location.pp l Mangled.pp name
}
| Global_var (fname, b) =>
| Global_var (fname, is_const, is_pod) =>
F.fprintf
f "#GB<%s%s>$%a" (DB.source_file_to_string fname) (if b {"|const"} else {""}) Mangled.pp name
f
"#GB<%s%s%s>$%a"
(DB.source_file_to_string fname)
(if is_const {"|const"} else {""})
(
if (not is_pod) {
"|!pod"
} else {
""
}
)
Mangled.pp
name
| Seed_var => F.fprintf f "old_%a" Mangled.pp name
}
};
@ -327,9 +344,9 @@ let mk_callee (name: Mangled.t) (proc_name: Procname.t) :t => {
/** create a global variable with the given name */
let mk_global is_constexpr::is_constexpr=false (name: Mangled.t) fname :t => {
let mk_global is_constexpr::is_constexpr=false is_pod::is_pod=true (name: Mangled.t) fname :t => {
pv_name: name,
pv_kind: Global_var (fname, is_constexpr)
pv_kind: Global_var (fname, is_constexpr, is_pod)
};
@ -354,16 +371,22 @@ let mk_abduced_ref_param (proc_name: Procname.t) (pv: t) (loc: Location.t) :t =>
let get_source_file pvar =>
switch pvar.pv_kind {
| Global_var (f, _) => Some f
| Global_var (f, _, _) => Some f
| _ => None
};
let is_compile_constant pvar =>
switch pvar.pv_kind {
| Global_var (_, b) => b
| Global_var (_, b, _) => b
| _ => false
};
let is_pod pvar =>
switch pvar.pv_kind {
| Global_var (_, _, b) => b
| _ => true
};
let get_initializer_pname {pv_name, pv_kind} =>
switch pv_kind {
| Global_var _ =>

@ -104,7 +104,7 @@ let mk_callee: Mangled.t => Procname.t => t;
/** create a global variable with the given name */
let mk_global: is_constexpr::bool? => Mangled.t => DB.source_file => t;
let mk_global: is_constexpr::bool? => is_pod::bool? => Mangled.t => DB.source_file => t;
/** create a fresh temporary variable local to procedure [pname]. for use in the frontends only! */
@ -139,10 +139,16 @@ let to_string: t => string;
let get_source_file: t => option DB.source_file;
/** Is the variable's value a compile-time constant? Always [false] for non-globals. */
/** Is the variable's value a compile-time constant? Always (potentially incorrectly) returns
[false] for non-globals. */
let is_compile_constant: t => bool;
/** Is the variable's type a "Plain Old Data" type (C++)? Always (potentially incorrectly) returns
[true] for non-globals. */
let is_pod: t => bool;
/** Get the procname of the initializer function for the given global variable */
let get_initializer_pname: t => option Procname.t;

@ -27,20 +27,11 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
module Domain = SiofDomain
type extras = ProcData.no_extras
let is_semantically_compile_constant tenv pdesc pv =
match Pvar.get_initializer_pname pv with
| Some pname -> (
match Summary.read_summary tenv pdesc pname with
| Some (Domain.NonBottom _) -> false
| Some Domain.Bottom | None -> true
)
| None -> true
let get_globals tenv astate pdesc loc e =
let get_globals astate loc e =
let is_dangerous_global pv =
Pvar.is_global pv
&& not (Pvar.is_compile_constant pv
|| is_semantically_compile_constant tenv pdesc pv) in
&& not (Pvar.is_pod pv)
&& not (Pvar.is_compile_constant pv) in
let globals = Exp.get_vars e |> snd |> IList.filter is_dangerous_global in
if globals = [] then
Domain.Bottom
@ -55,9 +46,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
globals in
Domain.NonBottom globals_trace
let add_params_globals astate tenv pdesc loc params =
let add_params_globals astate loc params =
IList.map fst params
|> IList.map (fun e -> get_globals tenv astate pdesc loc e)
|> IList.map (fun e -> get_globals astate loc e)
|> IList.fold_left Domain.join astate
let at_least_bottom =
@ -67,7 +58,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Load (_, exp, _, loc)
| Store (_, _, exp, loc)
| Prune (exp, loc, _, _) ->
Domain.join astate (get_globals tenv astate pdesc loc exp)
Domain.join astate (get_globals astate loc exp)
| Call (_, Const (Cfun callee_pname), params, loc, _) ->
let callsite = CallSite.make callee_pname loc in
let callee_globals =
@ -76,13 +67,13 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
Domain.NonBottom (SiofTrace.with_callsite trace callsite)
| _ ->
Domain.Bottom in
add_params_globals astate tenv pdesc loc params
add_params_globals astate loc params
|> Domain.join callee_globals
|>
(* make sure it's not Bottom: we made a function call so this needs initialization *)
at_least_bottom
| Call (_, _, params, loc, _) ->
add_params_globals astate tenv pdesc loc params
add_params_globals astate loc params
|>
(* make sure it's not Bottom: we made a function call so this needs initialization *)
at_least_bottom
@ -98,7 +89,19 @@ module Analyzer =
module Interprocedural = Analyzer.Interprocedural (Summary)
let is_foreign tu_opt v =
let is_orig_file f = match tu_opt with
| Some orig_file ->
let orig_path = DB.source_file_to_abs_path orig_file in
string_equal orig_path (DB.source_file_to_abs_path f)
| None -> assert false in
Option.map_default (fun f -> not (is_orig_file f)) false (Pvar.get_source_file v)
let report_siof tenv trace pdesc gname loc =
let tu_opt =
let attrs = Procdesc.get_attributes pdesc in
attrs.ProcAttributes.translation_unit in
let trace_of_pname pname =
match Summary.read_summary tenv pdesc pname with
| Some (SiofDomain.NonBottom summary) -> summary
@ -129,32 +132,27 @@ let report_siof tenv trace pdesc gname loc =
let report_one_path ((_, path) as sink_path) =
let final_sink = fst (IList.hd path) in
let description =
F.asprintf
"The initializer of %s accesses global variables in another translation unit: %a"
gname
pp_sink final_sink in
let ltr = trace_of_error sink_path in
let caller_pname = Procdesc.get_proc_name pdesc in
let msg = Localise.to_string Localise.static_initialization_order_fiasco in
let exn = Exceptions.Checkers (msg, Localise.verbatim_desc description) in
Reporting.log_error caller_pname ~loc ~ltr exn in
if is_foreign tu_opt (SiofTrace.Sink.kind final_sink) then (
let description =
F.asprintf
"The initializer of %s accesses global variables in another translation unit: %a"
gname
pp_sink final_sink in
let ltr = trace_of_error sink_path in
let caller_pname = Procdesc.get_proc_name pdesc in
let msg = Localise.to_string Localise.static_initialization_order_fiasco in
let exn = Exceptions.Checkers (msg, Localise.verbatim_desc description) in
Reporting.log_error caller_pname ~loc ~ltr exn
); in
IList.iter report_one_path (SiofTrace.get_reportable_sink_paths trace ~trace_of_pname)
let siof_check tenv pdesc gname = function
| Some (SiofDomain.NonBottom post) ->
let attrs = Procdesc.get_attributes pdesc in
let is_orig_file f = match attrs.ProcAttributes.translation_unit with
| Some orig_file ->
let orig_path = DB.source_file_to_abs_path orig_file in
string_equal orig_path (DB.source_file_to_abs_path f)
| None -> false in
let is_foreign v = Option.map_default
(fun f -> not (is_orig_file f)) false (Pvar.get_source_file v) in
let foreign_global_sinks =
SiofTrace.Sinks.filter
(fun sink -> is_foreign (SiofTrace.Sink.kind sink))
(fun sink -> is_foreign attrs.ProcAttributes.translation_unit (SiofTrace.Sink.kind sink))
(SiofTrace.sinks post) in
if not (SiofTrace.Sinks.is_empty foreign_global_sinks)
then report_siof tenv post pdesc gname attrs.ProcAttributes.loc;

@ -221,14 +221,14 @@ struct
| Some dec ->
Logging.out "Methods of %s skipped\n" (Ast_utils.string_of_decl dec)
| None -> ())
| VarDecl (decl_info, named_decl_info, _, ({ vdi_is_global; vdi_init_expr } as vdi))
| VarDecl (decl_info, named_decl_info, qt, ({ vdi_is_global; vdi_init_expr } as vdi))
when vdi_is_global && Option.is_some vdi_init_expr ->
(* create a fake procedure that initializes the global variable so that the variable
initializer can be analyzed by the backend (eg, the SIOF checker) *)
let procname =
(* create the corresponding global variable to get the right pname for its
initializer *)
let global = General_utils.mk_sil_global_var trans_unit_ctx named_decl_info vdi in
let global = General_utils.mk_sil_global_var trans_unit_ctx named_decl_info vdi qt in
(* safe to Option.get because it's a global *)
Option.get (Pvar.get_initializer_pname global) in
let ms = CMethod_signature.make_ms procname [] Ast_expressions.create_void_type

@ -781,20 +781,33 @@ struct
name_string, mangled
let mk_sil_global_var {CFrontend_config.source_file} ?(mk_name=fun _ x -> x)
named_decl_info var_decl_info =
named_decl_info var_decl_info qt =
let name_string, simple_name = get_var_name_mangled named_decl_info var_decl_info in
let translation_unit =
match var_decl_info.Clang_ast_t.vdi_storage_class with
| Some "extern" ->
match (var_decl_info.Clang_ast_t.vdi_storage_class,
var_decl_info.Clang_ast_t.vdi_init_expr) with
| Some "extern", None ->
(* some compilers simply disregard "extern" when the global is given some initialisation
code, which is why we make sure that [vdi_init_expr] is None here... *)
DB.source_file_empty
| _ ->
source_file in
let is_constexpr = var_decl_info.Clang_ast_t.vdi_is_const_expr in
Pvar.mk_global ~is_constexpr (mk_name name_string simple_name) translation_unit
let is_pod =
Ast_utils.get_desugared_type qt.Clang_ast_t.qt_type_ptr
|> Option.map_default (function
| Clang_ast_t.RecordType(_, decl_ptr) -> Ast_utils.get_decl decl_ptr
| _ -> None) None
|> Option.map_default (function
| Clang_ast_t.CXXRecordDecl(_, _, _, _, _, _, _, {xrdi_is_pod})
| Clang_ast_t.ClassTemplateSpecializationDecl(_, _, _, _, _, _, _, {xrdi_is_pod}, _) ->
xrdi_is_pod
| _ -> true) true in
Pvar.mk_global ~is_constexpr ~is_pod (mk_name name_string simple_name) translation_unit
let mk_sil_var trans_unit_ctx named_decl_info decl_info_type_ptr_opt procname outer_procname =
match decl_info_type_ptr_opt with
| Some (decl_info, _, var_decl_info, should_be_mangled) ->
| Some (decl_info, qt, var_decl_info, should_be_mangled) ->
let name_string, simple_name = get_var_name_mangled named_decl_info var_decl_info in
if var_decl_info.Clang_ast_t.vdi_is_global then
let mk_name =
@ -802,7 +815,7 @@ struct
Some (fun name_string _ ->
Mangled.from_string ((Procname.to_string outer_procname) ^ "_" ^ name_string))
else None in
mk_sil_global_var trans_unit_ctx ?mk_name named_decl_info var_decl_info
mk_sil_global_var trans_unit_ctx ?mk_name 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

@ -247,7 +247,7 @@ sig
val mk_sil_global_var : CFrontend_config.translation_unit_context ->
?mk_name:(string -> Mangled.t -> Mangled.t) ->
Clang_ast_t.named_decl_info -> Clang_ast_t.var_decl_info -> Pvar.t
Clang_ast_t.named_decl_info -> Clang_ast_t.var_decl_info -> Clang_ast_t.qual_type -> Pvar.t
val mk_sil_var : CFrontend_config.translation_unit_context -> Clang_ast_t.named_decl_info ->
var_info option -> Procname.t -> Procname.t -> Pvar.t

@ -19,7 +19,8 @@ SOURCES = \
siof/pod_across_translation_units-1.cpp \
siof/pod_across_translation_units-2.cpp \
siof/pod_same_translation_unit.cpp \
siof/siof_across_translation_units-1.cpp \
siof/siof_across_translation_units-2.cpp \
siof/siof.cpp \
siof/siof_templated.cpp \
siof/siof_different_tu.cpp \
include $(TESTS_DIR)/clang.make

@ -1,4 +1,10 @@
siof/const_use.cpp, __infer_globals_initializer_use_u, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of use_u,access to u]
siof/pod_across_translation_units-1.cpp, __infer_globals_initializer_x, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of x,call to baz,call to bar,call to foo,access to y]
siof/siof_across_translation_units-1.cpp, __infer_globals_initializer_another_global_object, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_global_object,access to another_global_object]
siof/siof_across_translation_units-1.cpp, __infer_globals_initializer_another_global_object, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_global_object,call to SomeOtherObject_SomeOtherObject,access to global_object]
siof/siof.cpp, __infer_globals_initializer_X::static_pod_accesses_non_pod, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of X::static_pod_accesses_non_pod,call to access_to_non_pod,access to global_object2]
siof/siof.cpp, __infer_globals_initializer_another_global_object, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_global_object,call to SomeOtherNonPODObject_SomeOtherNonPODObject,access to extern_global_object]
siof/siof.cpp, __infer_globals_initializer_another_global_object2, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_global_object2,call to access_to_non_pod,access to global_object2]
siof/siof.cpp, __infer_globals_initializer_another_global_object3, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_global_object3,call to access_to_templated_non_pod,access to global_object3]
siof/siof.cpp, __infer_globals_initializer_initWithGlobal, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of initWithGlobal,call to getGlobalNonPOD,access to global_object2]
siof/siof.cpp, __infer_globals_initializer_initWithStatic, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of initWithStatic,call to getFunctionStaticNonPOD,access to getFunctionStaticNonPOD_instance]
siof/siof.cpp, __infer_globals_initializer_pod_accesses_non_pod, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of pod_accesses_non_pod,call to access_to_non_pod,access to global_object2]
siof/siof_templated.cpp, __infer_globals_initializer_another_templated_global_object, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_templated_global_object,call to SomeOtherTemplatedNonPODObject<_Bool>_SomeOtherTemplatedNonPODObject,access to extern_global_object]
siof/siof_templated.cpp, __infer_globals_initializer_another_templated_global_object2, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_templated_global_object2,call to access_to_non_pod,access to global_object2]
siof/siof_templated.cpp, __infer_globals_initializer_another_templated_global_object3, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_templated_global_object3,call to access_to_templated_non_pod,access to global_object3]

@ -0,0 +1,43 @@
/*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "siof_types.h"
extern SomeNonPODObject extern_global_object;
SomeNonPODObject global_object;
extern int access_to_non_pod();
struct SomeOtherNonPODObject {
SomeOtherNonPODObject() {
global_object.some_method(); // OK, same translation unit
extern_global_object.some_method(); // bad, different translation unit
};
SomeOtherNonPODObject(int i) {
global_object.some_method(); // OK, same translation unit
};
};
SomeOtherNonPODObject another_global_object; // SIOF!
SomeOtherNonPODObject another_global_object2(access_to_non_pod()); // SIOF!
SomeOtherNonPODObject another_global_object3(
access_to_templated_non_pod()); // SIOF!
SomeOtherNonPODObject another_global_object4(42); // OK
int pod_accesses_non_pod = access_to_non_pod(); // SIOF!
struct X {
static int static_pod_accesses_non_pod;
};
int X::static_pod_accesses_non_pod = access_to_non_pod(); // SIOF!
SomeNonPODObject initWithStatic = getFunctionStaticNonPOD(); // OK
SomeNonPODObject initWithGlobal = getGlobalNonPOD(); // SIOF!

@ -1,19 +0,0 @@
/*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
// This file exists only so that the SIOF checkers sees global_object
// being initialized via a method call. The SIOF checker could be
// improved to know that all non-POD types require initialization in
// C++.
struct SomeObject {
void some_method();
};
SomeObject global_object;

@ -0,0 +1,28 @@
/*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "siof_types.h"
SomeNonPODObject global_object2;
int access_to_non_pod() {
global_object2.some_method();
return 5;
}
SomeTemplatedNonPODObject<int> global_object3;
int access_to_templated_non_pod() { return global_object3.some_method(); }
SomeNonPODObject& getFunctionStaticNonPOD() {
static SomeNonPODObject instance;
return instance;
}
SomeNonPODObject& getGlobalNonPOD() { return global_object2; }

@ -0,0 +1,33 @@
/*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "siof_types.h"
extern SomeTemplatedNonPODObject<int> extern_global_object;
SomeTemplatedNonPODObject<int> global_object;
template <typename T>
struct SomeOtherTemplatedNonPODObject {
SomeOtherTemplatedNonPODObject() {
global_object.some_method(); // OK, same translation unit
extern_global_object.some_method(); // bad, different translation unit
};
SomeOtherTemplatedNonPODObject(int i) {
global_object.some_method(); // OK, same translation unit
};
};
SomeOtherTemplatedNonPODObject<bool> another_templated_global_object; // SIOF!
SomeOtherTemplatedNonPODObject<bool> another_templated_global_object2(
access_to_non_pod()); // SIOF!
SomeOtherTemplatedNonPODObject<bool> another_templated_global_object3(
access_to_templated_non_pod()); // SIOF!
SomeOtherTemplatedNonPODObject<bool> another_templated_global_object4(42); // OK

@ -7,15 +7,16 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
struct SomeObject {
void some_method();
struct SomeNonPODObject {
virtual void some_method();
};
extern SomeObject global_object;
struct SomeOtherObject {
SomeOtherObject() { global_object.some_method(); };
template<typename T>
struct SomeTemplatedNonPODObject {
virtual T some_method();
};
// BAD: report SIOF here
SomeOtherObject another_global_object;
int access_to_templated_non_pod();
int access_to_non_pod();
SomeNonPODObject& getFunctionStaticNonPOD();
SomeNonPODObject& getGlobalNonPOD();

@ -1,6 +1,6 @@
/* @generated */
digraph iCFG {
"__infer_globals_initializer_global.bdc08c089842ce08b974b22a75daf78e_3" [label="3: DeclStmt \n _fun_X_X(&#GB<globals/global_const1.cpp>$global:class X *) [line 13]\n " shape="box"]
"__infer_globals_initializer_global.bdc08c089842ce08b974b22a75daf78e_3" [label="3: DeclStmt \n _fun_X_X(&#GB<globals/global_const1.cpp|!pod>$global:class X *) [line 13]\n " shape="box"]
"__infer_globals_initializer_global.bdc08c089842ce08b974b22a75daf78e_3" -> "__infer_globals_initializer_global.bdc08c089842ce08b974b22a75daf78e_2" ;
@ -18,7 +18,7 @@ digraph iCFG {
"X_X{_ZN1XC1ERKS_}.abc525d74d1815a6e1a874d1ed502de3_1" -> "X_X{_ZN1XC1ERKS_}.abc525d74d1815a6e1a874d1ed502de3_2" ;
"test{d41d8cd98f00b204e9800998ecf8427e_Z4testv}.8c08101fe48ee96867ff8578442d10bc_3" [label="3: Return Stmt \n n$0=*&__return_param:class X * [line 15]\n _fun_X_X(&#GB<globals/global_const1.cpp>$global:class X *) [line 13]\n _fun_X_X(n$0:class X *,&#GB<globals/global_const1.cpp>$global:class X &) [line 15]\n " shape="box"]
"test{d41d8cd98f00b204e9800998ecf8427e_Z4testv}.8c08101fe48ee96867ff8578442d10bc_3" [label="3: Return Stmt \n n$0=*&__return_param:class X * [line 15]\n _fun_X_X(&#GB<globals/global_const1.cpp|!pod>$global:class X *) [line 13]\n _fun_X_X(n$0:class X *,&#GB<globals/global_const1.cpp|!pod>$global:class X &) [line 15]\n " shape="box"]
"test{d41d8cd98f00b204e9800998ecf8427e_Z4testv}.8c08101fe48ee96867ff8578442d10bc_3" -> "test{d41d8cd98f00b204e9800998ecf8427e_Z4testv}.8c08101fe48ee96867ff8578442d10bc_2" ;

@ -18,7 +18,7 @@ digraph iCFG {
"foo::Rectangle_Rectangle{_ZN3foo9RectangleC1Ev}.994e34698d49402781f481c8d7fa0e03_1" -> "foo::Rectangle_Rectangle{_ZN3foo9RectangleC1Ev}.994e34698d49402781f481c8d7fa0e03_2" ;
"__infer_globals_initializer_bar::rect.e5e9061ca63212fdc2fd329df6c073de_3" [label="3: DeclStmt \n _fun_bar::Rectangle_Rectangle(&#GB<shared/namespace/namespace.cpp>$bar::rect:class bar::Rectangle *) [line 38]\n " shape="box"]
"__infer_globals_initializer_bar::rect.e5e9061ca63212fdc2fd329df6c073de_3" [label="3: DeclStmt \n _fun_bar::Rectangle_Rectangle(&#GB<shared/namespace/namespace.cpp|!pod>$bar::rect:class bar::Rectangle *) [line 38]\n " shape="box"]
"__infer_globals_initializer_bar::rect.e5e9061ca63212fdc2fd329df6c073de_3" -> "__infer_globals_initializer_bar::rect.e5e9061ca63212fdc2fd329df6c073de_2" ;

Loading…
Cancel
Save