-------------------------------------------------------------------------------
-- (C) Altran Praxis Limited
-------------------------------------------------------------------------------
--
-- The SPARK toolset 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. The SPARK toolset is distributed in the hope that it will be
-- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY 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 the SPARK toolset; see file
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
-- the license.
--
--=============================================================================

separate (Sem.Dependency_Relation)
procedure Create_Full_Subprog_Dependency
  (Node_Pos    : in     LexTokenManager.Token_Position;
   Subprog_Sym : in     Dictionary.Symbol;
   Abstraction : in     Dictionary.Abstractions;
   The_Heap    : in out Heap.HeapRecord)
is

   Imports_In_Relation, Exports_In_Relation : SeqAlgebra.Seq;

   -------------------------------

   procedure Add_To_Full_Dependency
     (Sym                                      : in     Dictionary.Symbol;
      Mode                                     : in     Dictionary.Modes;
      Imports_In_Relation, Exports_In_Relation : in     SeqAlgebra.Seq;
      The_Heap                                 : in out Heap.HeapRecord)
   --# global in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         The_Heap              from *,
   --#                                    Exports_In_Relation,
   --#                                    Imports_In_Relation,
   --#                                    Mode,
   --#                                    Sym,
   --#                                    The_Heap;
   is

      procedure Add_Export
        (Sym                 : in     Dictionary.Symbol;
         Exports_In_Relation : in     SeqAlgebra.Seq;
         The_Heap            : in out Heap.HeapRecord)
      --# global in out Statistics.TableUsage;
      --# derives Statistics.TableUsage,
      --#         The_Heap              from *,
      --#                                    Exports_In_Relation,
      --#                                    Sym,
      --#                                    The_Heap;
      is
      begin
         SeqAlgebra.AddMember (The_Heap, Exports_In_Relation, Natural (Dictionary.SymbolRef (Sym)));
      end Add_Export;

      ---------------

      procedure Add_Import
        (Sym                 : in     Dictionary.Symbol;
         Imports_In_Relation : in     SeqAlgebra.Seq;
         The_Heap            : in out Heap.HeapRecord)
      --# global in out Statistics.TableUsage;
      --# derives Statistics.TableUsage,
      --#         The_Heap              from *,
      --#                                    Imports_In_Relation,
      --#                                    Sym,
      --#                                    The_Heap;
      is
      begin
         SeqAlgebra.AddMember (The_Heap, Imports_In_Relation, Natural (Dictionary.SymbolRef (Sym)));
      end Add_Import;

   begin -- Add_To_Full_Dependency
      case Mode is
         when Dictionary.InMode =>
            Add_Import (Sym                 => Sym,
                        Imports_In_Relation => Imports_In_Relation,
                        The_Heap            => The_Heap);
         when Dictionary.OutMode =>
            Add_Export (Sym                 => Sym,
                        Exports_In_Relation => Exports_In_Relation,
                        The_Heap            => The_Heap);
         when Dictionary.InOutMode =>
            Add_Import (Sym                 => Sym,
                        Imports_In_Relation => Imports_In_Relation,
                        The_Heap            => The_Heap);
            Add_Export (Sym                 => Sym,
                        Exports_In_Relation => Exports_In_Relation,
                        The_Heap            => The_Heap);
         when Dictionary.DefaultMode =>
            -- can't occur for global
            Add_Import (Sym                 => Sym,
                        Imports_In_Relation => Imports_In_Relation,
                        The_Heap            => The_Heap);
         when Dictionary.InvalidMode =>
            -- can't ocur
            null;
      end case;
   end Add_To_Full_Dependency;

   --------------------------------------------------

   procedure Add_Parameters_To_Import_Export_Lists
     (Subprog_Sym                              : in     Dictionary.Symbol;
      Imports_In_Relation, Exports_In_Relation : in     SeqAlgebra.Seq;
      The_Heap                                 : in out Heap.HeapRecord)
   --# global in     Dictionary.Dict;
   --#        in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         The_Heap              from *,
   --#                                    Dictionary.Dict,
   --#                                    Exports_In_Relation,
   --#                                    Imports_In_Relation,
   --#                                    Subprog_Sym,
   --#                                    The_Heap;
   is
      It  : Dictionary.Iterator;
      Sym : Dictionary.Symbol;
   begin
      It := Dictionary.FirstSubprogramParameter (Subprog_Sym);
      while not Dictionary.IsNullIterator (It) loop
         Sym := Dictionary.CurrentSymbol (It);
         Add_To_Full_Dependency
           (Sym                 => Sym,
            Mode                => Dictionary.GetSubprogramParameterMode (Sym),
            Imports_In_Relation => Imports_In_Relation,
            Exports_In_Relation => Exports_In_Relation,
            The_Heap            => The_Heap);
         It := Dictionary.NextSymbol (It);
      end loop;
   end Add_Parameters_To_Import_Export_Lists;

   --------------------------------------------------

   procedure Add_Globals_To_Import_Export_Lists
     (Abstraction                              : in     Dictionary.Abstractions;
      Subprog_Sym                              : in     Dictionary.Symbol;
      Imports_In_Relation, Exports_In_Relation : in     SeqAlgebra.Seq;
      The_Heap                                 : in out Heap.HeapRecord)
   --# global in     Dictionary.Dict;
   --#        in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         The_Heap              from *,
   --#                                    Abstraction,
   --#                                    Dictionary.Dict,
   --#                                    Exports_In_Relation,
   --#                                    Imports_In_Relation,
   --#                                    Subprog_Sym,
   --#                                    The_Heap;
   is
      It  : Dictionary.Iterator;
      Sym : Dictionary.Symbol;
   begin
      It := Dictionary.FirstGlobalVariable (Abstraction, Subprog_Sym);
      while not Dictionary.IsNullIterator (It) loop
         Sym := Dictionary.CurrentSymbol (It);
         Add_To_Full_Dependency
           (Sym                 => Sym,
            Mode                => Dictionary.GetGlobalMode (Abstraction, Subprog_Sym, Sym),
            Imports_In_Relation => Imports_In_Relation,
            Exports_In_Relation => Exports_In_Relation,
            The_Heap            => The_Heap);
         It := Dictionary.NextSymbol (It);
      end loop;
   end Add_Globals_To_Import_Export_Lists;

   --------------------------------------------------

   procedure Add_Exports_To_Dictionary
     (The_Heap            : in Heap.HeapRecord;
      Node_Pos            : in LexTokenManager.Token_Position;
      Subprog_Sym         : in Dictionary.Symbol;
      Abstraction         : in Dictionary.Abstractions;
      Exports_In_Relation : in SeqAlgebra.Seq)
   --# global in     LexTokenManager.State;
   --#        in out Dictionary.Dict;
   --#        in out SPARK_IO.File_Sys;
   --# derives Dictionary.Dict   from *,
   --#                                Abstraction,
   --#                                Exports_In_Relation,
   --#                                Subprog_Sym,
   --#                                The_Heap &
   --#         SPARK_IO.File_Sys from *,
   --#                                Abstraction,
   --#                                Dictionary.Dict,
   --#                                Exports_In_Relation,
   --#                                LexTokenManager.State,
   --#                                Node_Pos,
   --#                                Subprog_Sym,
   --#                                The_Heap;
   is
      Member : SeqAlgebra.MemberOfSeq;
   begin
      Member := SeqAlgebra.FirstMember (The_Heap, Exports_In_Relation);
      while not SeqAlgebra.IsNullMember (Member) loop
         Dictionary.AddExport
           (Abstraction     => Abstraction,
            TheProcedure    => Subprog_Sym,
            TheExport       => Dictionary.ConvertSymbolRef
              (ExaminerConstants.RefType (SeqAlgebra.Value_Of_Member (The_Heap => The_Heap,
                                                                      M        => Member))),
            ExportReference => Dictionary.Location'(Start_Position => Node_Pos,
                                                    End_Position   => Node_Pos),
            Annotation      => Dictionary.Location'(Start_Position => Node_Pos,
                                                    End_Position   => Node_Pos));
         Member := SeqAlgebra.NextMember (The_Heap, Member);
      end loop;
   end Add_Exports_To_Dictionary;

   procedure Possibly_Add_Null_Export
     (Node_Pos                                 : in     LexTokenManager.Token_Position;
      Abstraction                              : in     Dictionary.Abstractions;
      Imports_In_Relation, Exports_In_Relation : in     SeqAlgebra.Seq;
      Subprog_Sym                              : in     Dictionary.Symbol;
      The_Heap                                 : in out Heap.HeapRecord)
   --# global in     ContextManager.Ops.Unit_Stack;
   --#        in     LexTokenManager.State;
   --#        in out Dictionary.Dict;
   --#        in out SPARK_IO.File_Sys;
   --#        in out Statistics.TableUsage;
   --# derives Dictionary.Dict       from *,
   --#                                    Abstraction,
   --#                                    ContextManager.Ops.Unit_Stack,
   --#                                    Imports_In_Relation,
   --#                                    Node_Pos,
   --#                                    Subprog_Sym,
   --#                                    The_Heap &
   --#         SPARK_IO.File_Sys     from *,
   --#                                    Abstraction,
   --#                                    ContextManager.Ops.Unit_Stack,
   --#                                    Dictionary.Dict,
   --#                                    Imports_In_Relation,
   --#                                    LexTokenManager.State,
   --#                                    Node_Pos,
   --#                                    Subprog_Sym,
   --#                                    The_Heap &
   --#         Statistics.TableUsage,
   --#         The_Heap              from *,
   --#                                    Abstraction,
   --#                                    ContextManager.Ops.Unit_Stack,
   --#                                    Dictionary.Dict,
   --#                                    Exports_In_Relation,
   --#                                    Imports_In_Relation,
   --#                                    Node_Pos,
   --#                                    Subprog_Sym,
   --#                                    The_Heap;
   is
      Import                                 : SeqAlgebra.MemberOfSeq;
      External_Variable_Found_In_Import_List : Boolean := False;
      Global_Variable_Sym                    : Dictionary.Symbol;
   begin
      -- check whether any of the imports are external variables
      Import := SeqAlgebra.FirstMember (The_Heap, Imports_In_Relation);
      while not SeqAlgebra.IsNullMember (Import) loop
         if Dictionary.GetOwnVariableOrConstituentMode
           (Dictionary.ConvertSymbolRef
              (ExaminerConstants.RefType (SeqAlgebra.Value_Of_Member (The_Heap => The_Heap,
                                                                      M        => Import)))) /=
           Dictionary.DefaultMode then
            External_Variable_Found_In_Import_List := True;
            exit;
         end if;
         Import := SeqAlgebra.NextMember (The_Heap, Import);
      end loop;
      -- if NONE of the imports are external variables then we need to create a suitable
      -- export by adding the NullVariable to the export set.
      if not External_Variable_Found_In_Import_List then
         -- We must first make it a global of the subprogram
         --# accept Flow, 10, Global_Variable_Sym, "Expected ineffective assignment to OK";
         Dictionary.AddGlobalVariable
           (Abstraction         => Abstraction,
            Subprogram          => Subprog_Sym,
            Variable            => Dictionary.GetNullVariable,
            Mode                => Dictionary.OutMode,
            PrefixNeeded        => False,
            Comp_Unit           => ContextManager.Ops.Current_Unit,
            VariableReference   => Dictionary.Location'(Start_Position => Node_Pos,
                                                        End_Position   => Node_Pos),
            Global_Variable_Sym => Global_Variable_Sym);
         --# end accept;
         -- and then we can add it to the export set.  Call to CreateFullDerives will do the rest
         SeqAlgebra.AddMember (The_Heap, Exports_In_Relation, Natural (Dictionary.SymbolRef (Dictionary.GetNullVariable)));
      end if;
      --# accept Flow, 33, Global_Variable_Sym, "Expected to be neither referenced nor exported";
   end Possibly_Add_Null_Export;

begin -- Create_Full_Subprog_Dependency

   -- Use parameter and global modes to create set of imports and set of exports
   SeqAlgebra.CreateSeq (The_Heap, Imports_In_Relation);
   SeqAlgebra.CreateSeq (The_Heap, Exports_In_Relation);
   Add_Parameters_To_Import_Export_Lists
     (Subprog_Sym         => Subprog_Sym,
      Imports_In_Relation => Imports_In_Relation,
      Exports_In_Relation => Exports_In_Relation,
      The_Heap            => The_Heap);
   Add_Globals_To_Import_Export_Lists
     (Abstraction         => Abstraction,
      Subprog_Sym         => Subprog_Sym,
      Imports_In_Relation => Imports_In_Relation,
      Exports_In_Relation => Exports_In_Relation,
      The_Heap            => The_Heap);
   -- If the above calls to populate the set of imports and exports results in an
   -- empty sequence of exports but a non-empty sequence of imports then we need
   -- to do a further check.
   -- Nothing needs to be done if the imports include external (stream) variables
   -- because AddDerivesStreamEffects below will  make any imported streams into
   -- an export as well (in order to model stream side-effects.
   -- We only need to do something if all the imports are "ordinary"
   -- variables.
   -- In that case only we make the set of exports into {NullVariable}.
   if SeqAlgebra.IsEmptySeq (The_Heap, Exports_In_Relation)
     and then not SeqAlgebra.IsEmptySeq (The_Heap, Imports_In_Relation) then
      Possibly_Add_Null_Export
        (Node_Pos            => Node_Pos,
         Abstraction         => Abstraction,
         Imports_In_Relation => Imports_In_Relation,
         Exports_In_Relation => Exports_In_Relation,
         Subprog_Sym         => Subprog_Sym,
         The_Heap            => The_Heap);
   end if;
   Add_Exports_To_Dictionary
     (The_Heap            => The_Heap,
      Node_Pos            => Node_Pos,
      Subprog_Sym         => Subprog_Sym,
      Abstraction         => Abstraction,
      Exports_In_Relation => Exports_In_Relation);
   Dictionary.AddDependencyRelation
     (Abstraction,
      Subprog_Sym,
      Dictionary.Location'(Start_Position => Node_Pos,
                           End_Position   => Node_Pos));
   -- create "closure" dependency relation, all exports from all imports
   Create_Full_Dependency
     (Node_Pos    => Node_Pos,
      Subprog_Sym => Subprog_Sym,
      Abstraction => Abstraction,
      Import_List => Imports_In_Relation,
      Export_List => Exports_In_Relation,
      The_Heap    => The_Heap);

   SeqAlgebra.DisposeOfSeq (The_Heap, Imports_In_Relation);
   SeqAlgebra.DisposeOfSeq (The_Heap, Exports_In_Relation);

   Sem.Add_Derives_Stream_Effects (Node_Pos    => Node_Pos,
                                   Subprog_Sym => Subprog_Sym,
                                   Abstraction => Abstraction);

   if Abstraction = Dictionary.IsRefined then
      Check_Derives_Consistency (Subprog_Sym => Subprog_Sym,
                                 Position    => Node_Pos,
                                 The_Heap    => The_Heap);
   end if;
end Create_Full_Subprog_Dependency;
