------------------------------------------------------------------------------
--                                                                          --
--                            GNAT2WHY COMPONENTS                           --
--                                                                          --
--                           F L O W _ T Y P E S                            --
--                                                                          --
--                                 S p e c                                  --
--                                                                          --
--              Copyright (C) 2013-2025, Capgemini Engineering              --
--                                                                          --
-- gnat2why is  free  software;  you can redistribute  it and/or  modify it --
-- under terms of the  GNU General Public License as published  by the Free --
-- Software  Foundation;  either version 3,  or (at your option)  any later --
-- version.  gnat2why is distributed  in the hope that  it will be  useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of  MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public --
-- License for  more details.  You should have  received  a copy of the GNU --
-- General  Public License  distributed with  gnat2why;  see file COPYING3. --
-- If not,  go to  http://www.gnu.org/licenses  for a complete  copy of the --
-- license.                                                                 --
--                                                                          --
------------------------------------------------------------------------------

--  This package deals with common types used in flow analysis, in particular
--  Flow_Id and V_Attributes.

with Ada.Containers;
with Ada.Containers.Hashed_Maps;
with Ada.Containers.Hashed_Sets;
with Ada.Containers.Ordered_Sets;
with Atree;             use Atree;
with Common_Containers; use Common_Containers;
with Einfo.Entities;    use Einfo.Entities;
with Graphs;
with Sinfo.Nodes;       use Sinfo.Nodes;
with SPARK_Util;        use SPARK_Util;
with Types;             use Types;

package Flow_Types is

   type Analyzed_Subject_Kind is (Kind_Subprogram, Kind_Task, Kind_Package);
   --  The different kinds of things we will analyze

   ----------------------------------------------------------------------
   --  Flow_Id
   --
   --  Represents an instance of a Node or Entity that is involved in flow
   --  analysis. A reference to just the Entity_Id or Node_Id is not
   --  sufficient, though, in a few cases:
   --    1) Initial and Final values need to be differentiated from "Normal"
   --       use of an entity.
   --    2) Individual record field(s) need to be modelled in addition to the
   --       "entire variable" represented by the Entity_Id.
   --    3) Entities referenced as a result of global-frame-computataion but
   --       are NOT in the AST are represented by a "magic string" rather than
   --       an Entity or Node ID.
   ----------------------------------------------------------------------

   type Param_Mode is
     (Mode_Invalid, Mode_Proof, Mode_In, Mode_In_Out, Mode_Out);
   pragma Ordered (Param_Mode);

   subtype In_Global_Modes is Param_Mode range Mode_Proof .. Mode_In;

   subtype Initialized_Global_Modes is
     Param_Mode range Mode_Proof .. Mode_In_Out;

   subtype Exported_Global_Modes is Param_Mode range Mode_In_Out .. Mode_Out;

   type Edge_Colours is
     (EC_Default,
      --  Control-flow dependencies

      EC_Barrier,
      --  For the `wait' edges for barriers. They introduce
      --  control dependence, but are otherwise not
      --  traversible.

      EC_Abend,
      --  For abnormal termination we need to add an edge,
      --  but it should not be traversed for the purpose of
      --  producing the DDG.

      EC_Inf,
      --  For infinite loops we do add an edge, but we do
      --  not want to traverse it for the purpose of finding
      --  dead code.

      EC_DDG,
      --  Data dependencies

      EC_TDG
      --  Transitive call dependencies
     );
   --  ??? by convention type name are in singular (e.g. Kind not Kinds)

   type Flow_Id_Kind is
     (Null_Value,
      --  No reference or any entity or node

      Direct_Mapping,
      --  Direct reference to Entity or Node in the AST

      Record_Field,
      --  Reference to list of record field(s) as well as
      --  whole variable entity in the AST.

      Synthetic_Null_Export,
      --  The null export (to capture effects, such as
      --  timing, outside of SPARK).

      Magic_String
      --  Entity not in AST, so referred to by a String
     );

   type Flow_Id_Variant is
     (Normal_Use,
      --  Normal usage of the identifier

      Initial_Value,
      Final_Value,
      --  For the 'initial and 'final vertices

      Initial_Grouping,
      Final_Grouping,
      --  For the tree of record components

      In_View,
      Out_View
      --  For the procedure call parameter vertices
     );
   pragma Ordered (Flow_Id_Variant);

   type Variable_Facet_T is
     (Normal_Part,
      Private_Part,    --  for private types
      Extension_Part,  --  for tagged types
      The_Tag,         --  for tagged types
      The_Bounds       --  for unconstrained objects
     );
   --  Not all things can be represented by just X. For example a discriminated
   --  private type might need X'Private_Part and X.D. Most Flow_Id objects
   --  will describe the Normal_Part.
   --
   --  Flo's note on naming: I did want to use the name "aspect", but this is
   --  perhaps asking for confusion; hence I went for "facet".

   subtype Initial_Or_Final_Variant is
     Flow_Id_Variant range Initial_Value .. Final_Value;

   subtype Parameter_Variant is Flow_Id_Variant range In_View .. Out_View;

   type Corresponding_Grouping_Map is
     array (Initial_Or_Final_Variant) of Flow_Id_Variant;

   Corresponding_Grouping : constant Corresponding_Grouping_Map :=
     (Initial_Value => Initial_Grouping, Final_Value => Final_Grouping);

   type Flow_Id (Kind : Flow_Id_Kind := Null_Value) is record
      Variant : Flow_Id_Variant;
      --  In theory this doesn't have to be part of a Null_Value id, but there
      --  are many checks for Foo.Variant throughout flow analysis and it will
      --  be quite tedious to prefix each of them with Present (Foo).

      case Kind is
         when Direct_Mapping | Record_Field =>
            Node  : Node_Or_Entity_Id;
            Facet : Variable_Facet_T; --  only used for records and types

            case Kind is
               when Record_Field =>
                  Component : Entity_Vectors.Vector;

               when others =>
                  null;
            end case;

         when Magic_String =>
            Name : Entity_Name;

         when others =>
            null;
      end case;
   end record;

   ----------------
   -- Flow_Scope --
   ----------------

   --  The scopes we care about in flow analysis are restricted to packages,
   --  protected objects and task objects.
   --
   --  Variables or subprograms can be declared or defined in:
   --    * package's visible part
   --    * package's private part
   --    * package's body
   --    * protected object's visible
   --    * protected object's private part
   --    * protected object's body
   --    * task object's visible
   --    * task object's body
   --
   --  Therefore flow scope is an entity + visible|private|body

   type Any_Declarative_Part is
     (Null_Part, Visible_Part, Private_Part, Body_Part);
   pragma Ordered (Any_Declarative_Part);

   subtype Declarative_Part is
     Any_Declarative_Part range Visible_Part .. Body_Part;

   subtype Scope_Id is Entity_Id
   with
     Dynamic_Predicate =>
       (if Present (Scope_Id)
        then
          Ekind (Scope_Id)
          in E_Function
           | E_Procedure
           | E_Package
           | E_Protected_Type
           | E_Task_Type
           | Entry_Kind
           | Generic_Unit_Kind);
   --  Type that defines a subset of legal entities for the use in Flow_Scope

   type Flow_Scope is record
      Ent  : Scope_Id;
      Part : Any_Declarative_Part;
   end record
   with
     Dynamic_Predicate =>
       (Flow_Scope.Ent = Empty) = (Flow_Scope.Part = Null_Part);
   --  Note: conceptually this is a discrimated record type, but discriminating
   --  on Scope_Id (which is an Entity_Id with a predicate) is troublesome.

   Null_Flow_Scope : constant Flow_Scope := (Empty, Null_Part);

   subtype Global_Set is Node_Sets.Set
   with Dynamic_Predicate => (for all E of Global_Set => Is_Global_Entity (E));

   type Global_Nodes is record
      Proof_Ins : Global_Set;
      Inputs    : Global_Set;
      Outputs   : Global_Set;
   end record
   with
     Dynamic_Predicate =>
       (for all G of Global_Nodes.Proof_Ins =>
          not Global_Nodes.Inputs.Contains (G)
          and then not Global_Nodes.Outputs.Contains (G));
   --  ??? should it be an array then we could remove some repeated code

   type Global_Names is record
      Proof_Ins : Name_Sets.Set;          --  Flow/User
      Inputs    : Name_Sets.Set;          --  Flow/Frontend/User
      Outputs   : Name_Sets.Set;          --  Flow/Frontend/User
   end record
   with
     Dynamic_Predicate =>
       (for all G of Global_Names.Proof_Ins =>
          not Global_Names.Inputs.Contains (G)
          and then not Global_Names.Outputs.Contains (G));

   type Type_Aspect is (No_Aspect, DIC);

   function "=" (Left, Right : Flow_Id) return Boolean;
   --  Equality for Flow_Id

   function "<" (Left, Right : Flow_Id) return Boolean;
   --  Ordering for Flow_Id

   Null_Flow_Id : constant Flow_Id :=
     Flow_Id'(Kind => Null_Value, Variant => Normal_Use);

   Null_Export_Flow_Id : constant Flow_Id :=
     Flow_Id'(Kind => Synthetic_Null_Export, Variant => Normal_Use);

   function Hash (N : Flow_Id) return Ada.Containers.Hash_Type;
   --  Hash function for flow ids. The idea is that a direct mapping the node N
   --  will return the same hash as a magic string mapping to node N.

   function Present (F : Flow_Id) return Boolean
   is (F.Kind /= Null_Value);
   --  Returns True iff F is not null

   function Synthetic (F : Flow_Id) return Boolean
   is (F.Kind = Synthetic_Null_Export);
   --  Returns True iff F is a synthesised Flow_Id

   function Direct_Mapping_Id
     (N       : Node_Or_Entity_Id;
      Variant : Flow_Id_Variant := Normal_Use;
      Facet   : Variable_Facet_T := Normal_Part) return Flow_Id
   with Pre => Present (N);
   --  Create a Flow_Id for the given node or entity

   function Get_Direct_Mapping_Id (F : Flow_Id) return Node_Id
   with
     Pre  => F.Kind in Direct_Mapping | Record_Field,
     Post => Present (Get_Direct_Mapping_Id'Result);
   --  Given a direct mapping Flow_Id, return the associated node or entity. In
   --  case of a record field, return the entire variable.

   function Record_Field_Id (N : Node_Id) return Flow_Id
   with Pre => Present (N) and then Nkind (N) = N_Selected_Component;
   --  Create a Flow_Id for the given record field

   function Add_Component (F : Flow_Id; Comp : Entity_Id) return Flow_Id
   with
     Pre  =>
       F.Kind in Direct_Mapping | Record_Field
       and then
         (Is_Part_Of_Concurrent_Object (Comp)
          or else Is_Unique_Component (Comp))
       and then F.Facet = Normal_Part,
     Post => Add_Component'Result.Kind = Record_Field;
   --  Returns the same Flow_Id, but accessed with the given component

   function Magic_String_Id
     (S : Entity_Name; Variant : Flow_Id_Variant := Normal_Use) return Flow_Id;
   --  Create a Flow_Id for the given magic string.

   function Belongs_To_Concurrent_Type (F : Flow_Id) return Boolean;
   --  @param F is the Flow_Id which will be checked
   --  @return True iff F represents a discriminant, component, or Part_Of
   --     concurrent type

   function Encapsulating_State (F : Flow_Id) return Flow_Id
   with
     Pre =>
       F.Kind in Direct_Mapping | Record_Field | Magic_String
       and then Is_Constituent (F);
   --  Returns the encapsulating state of F

   function Is_Discriminant (F : Flow_Id) return Boolean
   with Pre => Present (F);
   --  @param F is the Flow_Id which will be checked
   --  @return True iff the given Flow_Id is discriminant (this includes
   --    discriminants for protected types and tasks).

   function Is_Input_Discriminant (F : Flow_Id) return Boolean;
   --  @param F is the Flow_Id which will be checked
   --  @return True iff the given Flow_Id is discriminant of the object F
   --     itself and not of its subcomponents

   function Is_Private_Part (F : Flow_Id) return Boolean
   is (F.Kind in Direct_Mapping | Record_Field
       and then F.Facet = Private_Part);
   --  Returns True if the given Flow_Id represents the hidden part of a record
   --  (used when something is private and we don't have visibility).

   function Is_Entire_Variable (F : Flow_Id) return Boolean
   is (case F.Kind is
         when Direct_Mapping                       =>
           F.Facet = Normal_Part and then Nkind (F.Node) in N_Entity,
         when Synthetic_Null_Export | Magic_String => True,
         when Null_Value | Record_Field            => False);
   --  Returns True iff the given flow id represents an entire variable (or the
   --  magic null export, or a magic string).

   function Is_Extension (F : Flow_Id) return Boolean
   is (F.Kind in Direct_Mapping | Record_Field
       and then F.Facet = Extension_Part);
   --  Returns True if the given Flow_Id represents the extension part of a
   --  record.

   function Is_Record_Tag (F : Flow_Id) return Boolean
   is (F.Kind in Direct_Mapping | Record_Field and then F.Facet = The_Tag);
   --  Returns True if the given Flow_Id represents the tag of a classwide type

   function Is_Bound (F : Flow_Id) return Boolean
   is (F.Kind in Direct_Mapping | Record_Field and then F.Facet = The_Bounds)
   with Post => (if Is_Bound'Result then F.Kind = Direct_Mapping);
   --  Returns True if the given Flow_Id represents a bound

   function Is_Volatile (F : Flow_Id) return Boolean;
   --  Returns True if the given Flow_Id is volatile in any way

   function Has_Async_Readers (F : Flow_Id) return Boolean
   with Post => (if Has_Async_Readers'Result then Is_Volatile (F));
   --  Checks if F has async readers

   function Has_Async_Writers (F : Flow_Id) return Boolean
   with Post => (if Has_Async_Writers'Result then Is_Volatile (F));
   --  Checks if F has async writers

   function Has_Effective_Reads (F : Flow_Id) return Boolean
   with Post => (if Has_Effective_Reads'Result then Has_Async_Writers (F));
   --  Checks if reads of F are always effective

   function Has_Effective_Writes (F : Flow_Id) return Boolean
   with Post => (if Has_Effective_Writes'Result then Has_Async_Readers (F));
   --  Checks if writes to F are always effective

   function Is_Volatile_For_Reading (F : Flow_Id) return Boolean
   is (Is_Volatile (F)
       and then (Has_Async_Writers (F) or else Has_Effective_Reads (F)));
   --  Returns True if the given Flow_Id is effectively volatile for reading

   function Is_Synchronized (F : Flow_Id) return Boolean;
   --  Returns True if the global object F is synchronized

   function Is_Abstract_State (F : Flow_Id) return Boolean;
   --  Checks if F is an abstract state

   function Is_Constituent (F : Flow_Id) return Boolean;
   --  Checks if F is a constituent of an abstract state

   function Is_Implicit_Constituent (F : Flow_Id) return Boolean;
   --  Checks if F is a constituent of an implicit abstract state that
   --  implicitly represents an object hidden inside a package.

   function Is_Function_Entity (F : Flow_Id) return Boolean;
   --  Checks if F is a function entity (and thus used to capture the
   --  function's return value).

   function Is_Internal (F : Flow_Id) return Boolean
   with Pre => Present (F), Ghost;
   --  Checks if F represents an internal entity

   function Change_Variant
     (F : Flow_Id; Variant : Flow_Id_Variant) return Flow_Id
   with Pre => Present (F);
   --  Returns a copy of the given Flow_Id, but with a modified variant

   function Parent_Record (F : Flow_Id) return Flow_Id
   with
     Pre  =>
       (F.Kind = Record_Field
        or else (F.Kind = Direct_Mapping and then F.Facet /= Normal_Part))
       and then
         F.Variant
         in Initial_Grouping | Initial_Value | Final_Grouping | Final_Value,
     Post =>
       Parent_Record'Result.Kind in Direct_Mapping | Record_Field
       and then Parent_Record'Result.Facet = Normal_Part
       and then Parent_Record'Result.Variant = F.Variant;
   --  Return the parent record for the given record field. If given the
   --  hidden fields of a record, returns the visible part (i.e. clears the
   --  hidden_part flag before moving up the component list). If given a
   --  constituent of a protected object then the protected object is returned.

   function Entire_Variable (F : Flow_Id) return Flow_Id
   with
     Pre  => Present (F),
     Post => Is_Entire_Variable (Entire_Variable'Result);
   --  Returns the entire variable represented by F

   procedure Sprint_Flow_Id (F : Flow_Id);
   --  Debug procedure to print the given flow id, similar to Sprint_Node

   procedure Print_Flow_Id (F : Flow_Id);
   --  Debug procedure to print the flow id with more information (such as kind
   --  and variant) attached.

   function Pretty_Print (EN : Entity_Name) return String;
   --  Pretty-print an entity name by substituting double underscores with a
   --  single dot and converting strings to mixed case, e.g. "pkg__a_state"
   --  will become "Pkg.A_State".

   function Flow_Id_To_String
     (F : Flow_Id; Pretty : Boolean := False) return String
   with Pre => Is_Easily_Printable (F);
   --  Convert a flow id to a human readable string. This is used mainly
   --  for emitting error messages, where we want entities represented by
   --  Magic_String to be pretty-printed (so Pretty should be True) and for
   --  the graphviz/debug output where we prefer Magic_String to be evident
   --  (so Pretty should be False).

   function Is_Easily_Printable (F : Flow_Id) return Boolean
   with Ghost;
   --  Check if F can be printed without resorting to Sprint

   package Flow_Graphs is new
     Graphs
       (Vertex_Key   => Flow_Id,
        Key_Hash     => Hash,
        Edge_Colours => Edge_Colours,
        Null_Key     => Null_Flow_Id,
        Test_Key     => "=");

   ----------------------------------------------------------------------
   --  Types based on Flow_Id
   ----------------------------------------------------------------------

   package Flow_Id_Sets is new
     Ada.Containers.Hashed_Sets
       (Element_Type        => Flow_Id,
        Hash                => Hash,
        Equivalent_Elements => "=",
        "="                 => "=");

   package Flow_Id_Maps is new
     Ada.Containers.Hashed_Maps
       (Key_Type        => Flow_Id,
        Element_Type    => Flow_Id_Sets.Set,
        Hash            => Hash,
        Equivalent_Keys => "=",
        "="             => Flow_Id_Sets."=");

   package Flow_Id_Surjection is new
     Ada.Containers.Hashed_Maps
       (Key_Type        => Flow_Id,
        Element_Type    => Flow_Id,
        Hash            => Hash,
        Equivalent_Keys => "=",
        "="             => "=");

   package Ordered_Flow_Id_Sets is new
     Ada.Containers.Ordered_Sets
       (Element_Type => Flow_Id,
        "<"          => "<",
        "="          => "=");

   type Global_Flow_Ids is record
      Proof_Ins : Flow_Id_Sets.Set;
      Inputs    : Flow_Id_Sets.Set;
      Outputs   : Flow_Id_Sets.Set;
   end record
   with
     Predicate =>
       (for all Proof_In in Proof_Ins.Iterate =>
          not Inputs.Contains
                (Flow_Id_Sets.Constant_Reference (Proof_Ins, Proof_In))
          and then
            not Outputs.Contains
                  (Flow_Id_Sets.Constant_Reference (Proof_Ins, Proof_In)));
   --  ??? Iteration with cursors and not elements works around some GNAT bug.
   --  The explicit call to Constant_Reference works around another bug, where
   --  GNAT can't recognize the predicated record component as an implicit call
   --  in a prefix notation.

   function To_Ordered_Flow_Id_Set
     (S : Flow_Id_Sets.Set) return Ordered_Flow_Id_Sets.Set;
   --  Convert a hashed flow id set into an ordered flow id set

   function To_Entire_Variables (S : Flow_Id_Sets.Set) return Flow_Id_Sets.Set
   with
     Post =>
       (for all X of To_Entire_Variables'Result => X.Kind /= Record_Field);
   --  Convert a set containing flattened records into a set containing only
   --  entire variables.

   function To_Name (F : Flow_Id) return Entity_Name;
   --  Convert a flow id to an entity name. Any record fields are changed into
   --  entire variables.

   function To_Node_Set (S : Flow_Id_Sets.Set) return Node_Sets.Set
   with Pre => (for all F of S => F.Kind = Direct_Mapping);
   --  Convert a simple Flow_Id set to a node set

   function To_Flow_Id_Set
     (S : Node_Sets.Set; View : Flow_Id_Variant := Normal_Use)
      return Flow_Id_Sets.Set
   with
     Post => (for all F of To_Flow_Id_Set'Result => F.Kind = Direct_Mapping);
   --  Convert a node set to a Flow_Id set

   function Change_Variant
     (FS : Flow_Id_Sets.Set; Variant : Flow_Id_Variant)
      return Flow_Id_Sets.Set;
   --  Returns a copy of the given flow id set, but with a modified variant

   ----------------------------------------------------------------------
   --  V_Attributes
   ----------------------------------------------------------------------

   --  If you change this type, please also update Print_Graph_Vertex in Flow

   type Pretty_Print_Kind_T is
     (Pretty_Print_Null,
      Pretty_Print_Declaration,
      Pretty_Print_DIC,
      Pretty_Print_Package,
      Pretty_Print_Folded_Function_Check,
      Pretty_Print_Loop_Init,
      Pretty_Print_Record_Field,
      Pretty_Print_Entry_Barrier,
      Pretty_Print_Reclaim,
      Pretty_Print_Call_Exception,
      Pretty_Print_Param_Havoc,
      Pretty_Print_Param_Scrub,
      Pretty_Print_Program_Exit);

   type Subprogram_Call is record
      N : Node_Id;    --  node of the subprogram call
      E : Entity_Id;  --  entity of the called subprogram
   end record;
   --  For subprogram calls (and other nodes that we handle similarly), we
   --  store both the subprogram call node and the called entity, because the
   --  node itself is needed to detect dispatching calls and entity cannot be
   --  uniquely determined by the node itself, e.g. for implicit calls related
   --  to iterator_specifications. Also, it is just convenient to have the
   --  entity available as a record component.
   --
   --  Note: it is tempting to use the call node as a location of error
   --  messages, but it would give confusing location for call occuring in type
   --  predicates, which are traversed when they apply to membership tests.

   function Hash (SC : Subprogram_Call) return Ada.Containers.Hash_Type;
   --  Hash routine for subprogram calls

   package Call_Sets is new
     Ada.Containers.Hashed_Sets
       (Element_Type        => Subprogram_Call,
        Hash                => Hash,
        Equivalent_Elements => "=");
   --  Container for subprogram calls. Currently, it must allow efficient
   --  adding of the same element (for calls occuring in predicate expressions)
   --  and relatively efficient membership test for procedure and entries (for
   --  determining whether they are called definitely).

   type V_Attributes is record
      Is_Null_Node : Boolean;
      --  Set for auxiliary nodes which can be removed, such as early returns
      --  or null statements.

      Is_Program_Node : Boolean;
      --  Set for all vertices which both
      --     - trace directly to an element in the AST,
      --     - they are constructs which could be ineffective
      --
      --  Setting this attribute enables the following analyses which would not
      --  normally be performed:
      --     * ineffective_statements
      --
      --  It should be noted that most vertices we construct will have this set
      --  to true.

      Is_Dead_Path : Boolean;
      --  True for paths that are dead

      Is_Original_Program_Node : Boolean;
      --  True for pruned vertices that were originally program nodes

      In_Nested_Package : Boolean;
      --  True for vertices created for constructs in nested packages

      Is_Path_Copy : Boolean;
      --  True for vertices on internal copies of finally statements

      Is_Exceptional_Branch : Boolean;
      --  True for nodes which lead *into* an exceptional path (see below), but
      --  are not part of the path itself.

      Is_Exceptional_Path : Boolean;
      --  True for all nodes on exceptional execution paths, i.e. paths leading
      --  to raise statements, statically false assertions and calls to
      --  subprograms with pragma No_Return. We tend to exclude these from
      --  analysis and sanity checking.

      Is_Assertion : Boolean;
      --  True if this vertex represents an assertion expression

      Is_Package_Initialization : Boolean;
      --  True if this vertex represents a package initialization

      Is_Default_Init : Boolean;
      --  True if this vertex represents a default initialization

      Is_Loop_Entry : Boolean;
      --  True if this vertex represents a loop entry assignment. For each
      --  variable where we use 'Loop_Entry we have one of these at the top of
      --  the actual loop.

      Is_Initialized : Boolean;
      --  True if an initial value is either imported (in or in out) or
      --  otherwise initialized.

      Is_Global : Boolean;
      --  True if the imported or exported variable is a global

      Is_Import : Boolean;
      --  True if the given initial value is a parameter or global of the
      --  analysed subprogram.

      Is_Export : Boolean;
      --  True if the given final-use variable is actually relevant to a
      --  subprogram's exports (out parameter or global out).

      Mode : Param_Mode;
      --  Set for initial and final use vertices which are parameters or
      --  globals.

      Is_Constant : Boolean;
      --  True if this value may not be updated

      Is_Callsite : Boolean;
      --  True if the vertex represents a subprogram call

      Is_Parameter : Boolean;
      --  True if this vertex models an argument to a procedure call

      Is_Global_Parameter : Boolean;
      --  True if this vertex models a global for a procedure or function call

      Is_Implicit_Parameter : Boolean;
      --  True if this vertex models an implicit formal parameter of a
      --  subprogram.

      Is_Call_Exception : Boolean;
      --  True if this vertex represents an exception raised within a called
      --  subprogram.

      Is_Param_Havoc : Boolean;
      --  True if this vertex represents havocing parameters of a call to
      --  subprogram with Exceptional_Cases which are passed neither by-copy
      --  nor by-reference and thus their value becomes unspecified when the
      --  call raises an exception.

      Is_Neverending : Boolean;
      --  True if this vertex models a loop that is detected as potentially
      --  nonreturning.

      Object_Declarations : Node_Sets.Set;
      --  Objects whose 'Initial and 'Final vertices were created together with
      --  this vertex and should be cleared when this vertex is found to be a
      --  dead code.

      Execution : Execution_Kind_T;
      --  Determines how we should treat edges from this vertex. Most nodes
      --  will have Normal_Execution set here.

      Perform_IPFA : Boolean;
      --  True if the dependencies for this callsite should be filled in using
      --  interprocedural flow analysis.

      Callee : Entity_Id;
      --  For nodes where Is_Callsite is True; denotes the called entity
      --
      --  Note: this could be recovered from the vertex key, but it is
      --  convenient to store it directly.

      Call_Vertex : Flow_Graphs.Vertex_Id;
      --  For parameter vertices it denotes the corresponding call vertex

      Parameter_Actual : Flow_Id;
      Parameter_Formal : Flow_Id;
      --  For nodes where Is_Parameter is true, this keeps track of which
      --  parameter this is. This is also quite useful for pretty-printing.
      --  For nodes with Is_Global_Parameter only Parameter_Formal is set.

      Default_Init_Var : Flow_Id;
      Default_Init_Val : Node_Id;
      --  For default initializations (Is_Default_Init) this pair records which
      --  variable has a default value (Var) and what it is (Val).

      Variables_Defined : Flow_Id_Sets.Set;
      Variables_Used    : Flow_Id_Sets.Set;
      Variables_Read    : Flow_Id_Sets.Set;
      --  For producing the DDG

      Variables_Explicitly_Used : Flow_Id_Sets.Set;
      --  Similar to Variables_Used, but does not include the implicit
      --  self-dependency for partial record and array updates.

      Volatiles_Read    : Flow_Id_Sets.Set;
      Volatiles_Written : Flow_Id_Sets.Set;
      --  Again, for producing the DDG. These are implied updates due to reads
      --  of volatiles where reads are effective.

      Subprogram_Calls : Call_Sets.Set;
      --  The set of all subprograms (functions and procedures) called; think
      --  of this as Variables_Used, but for subprogram calls.

      Indirect_Calls : Node_Sets.Set;
      --  The set of all nodes that can have indirect calls, such as calls to
      --  equality or predicate functions.

      Loops : Node_Sets.Set;
      --  Which loops are we a member of (identified by loop name/label). For
      --  loop stability analysis.

      First_Field : Flow_Graphs.Vertex_Id;
      --  For vertices coming from declarations and assignments of record
      --  objects, it points to the first vertex in the CFG that represents
      --  the sequence of vertices with variable useds and variables defined
      --  for every record component.

      Record_RHS : Flow_Graphs.Vertex_Id;
      --  Vertex with a variables used on the RHS of a record assignment

      Error_Location : Node_Or_Entity_Id;
      --  If we have an error involving this vertex, raise it here

      Aux_Node : Node_Or_Entity_Id;
      --  The meaning of this depends on the kind of vertex these attributes
      --  are attached to.
      --
      --     * E_Return_Statement : for the implicit extended return
      --       returns this keeps track of the actual variable we return.

      Warnings_Off : Boolean;
      --  True if we don't want to emit warnings about unreachable code on
      --  this vertex.

      Pretty_Print_Kind : Pretty_Print_Kind_T;
      --  Some extra information which we use when deciding how to pretty print
      --  the vertex in --flow-debug mode.
   end record;

   Null_Attributes : constant V_Attributes :=
     V_Attributes'
       (Is_Null_Node              => False,
        In_Nested_Package         => False,
        Is_Path_Copy              => False,
        Is_Program_Node           => False,
        Is_Original_Program_Node  => False,
        Is_Dead_Path              => False,
        Is_Exceptional_Branch     => False,
        Is_Exceptional_Path       => False,
        Is_Assertion              => False,
        Is_Package_Initialization => False,
        Is_Default_Init           => False,
        Is_Loop_Entry             => False,
        Is_Initialized            => False,
        Is_Global                 => False,
        Is_Import                 => False,
        Is_Export                 => False,
        Mode                      => Mode_Invalid,
        Is_Constant               => False,
        Is_Callsite               => False,
        Is_Parameter              => False,
        Is_Global_Parameter       => False,
        Is_Implicit_Parameter     => False,
        Is_Call_Exception         => False,
        Is_Param_Havoc            => False,
        Is_Neverending            => False,
        Object_Declarations       => Node_Sets.Empty_Set,
        Execution                 => Normal_Execution,
        Perform_IPFA              => False,
        Callee                    => Empty,
        Call_Vertex               => Flow_Graphs.Null_Vertex,
        Parameter_Actual          => Null_Flow_Id,
        Parameter_Formal          => Null_Flow_Id,
        Default_Init_Var          => Null_Flow_Id,
        Default_Init_Val          => Empty,
        Variables_Defined         => Flow_Id_Sets.Empty_Set,
        Variables_Used            => Flow_Id_Sets.Empty_Set,
        Variables_Read            => Flow_Id_Sets.Empty_Set,
        Variables_Explicitly_Used => Flow_Id_Sets.Empty_Set,
        Volatiles_Read            => Flow_Id_Sets.Empty_Set,
        Volatiles_Written         => Flow_Id_Sets.Empty_Set,
        Subprogram_Calls          => Call_Sets.Empty_Set,
        Indirect_Calls            => Node_Sets.Empty_Set,
        Loops                     => Node_Sets.Empty_Set,
        First_Field               => Flow_Graphs.Null_Vertex,
        Record_RHS                => Flow_Graphs.Null_Vertex,
        Error_Location            => Empty,
        Aux_Node                  => Empty,
        Warnings_Off              => False,
        Pretty_Print_Kind         => Pretty_Print_Null);

   Null_Node_Attributes : constant V_Attributes :=
     (Null_Attributes
      with delta Is_Null_Node => True, Is_Program_Node => True);

   type Reference_Kind is (Inputs, Proof_Ins, Null_Deps);
   --  Modes for queries about variables referenced in a given expression. For
   --  example, when quering a call to function annotated like this:
   --
   --    function F return Integer
   --    with Global  => (Input => (A, B), Proof_In => C),
   --         Depends => (F'Result => A,
   --                     null     => B);
   --
   --  when routine Get_Variables is called it will return the following:
   --
   --    Inputs    => {A}
   --    Proof_Ins => {C}
   --    Null_Deps => {B}
   --
   --  Results in modes Inputs and Proof_Ins are come from straightforward
   --  references to variables; results in mode Null_Deps come from calls to
   --  subprograms with "null => ..." dependency clauses, 'Update expressions,
   --  delay statements, etc.

end Flow_Types;
