-------------------------------------------------------------------------------
-- (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 (FlowAnalyser.FlowAnalyse.AnalyseRelations)
procedure CheckDependencies is
   IntersectionSeq, DependencyCol, RhoCol, RhoRow : SeqAlgebra.Seq;
   MemberOfInitVars, MemberOfExports              : SeqAlgebra.MemberOfSeq;
   InitVarRep, ExpVarRep                          : Natural;
   InitVar, ExportVar                             : Dictionary.Symbol;
   ExportLeaves                                   : SeqAlgebra.Seq;
   MemberOfExportLeaves                           : SeqAlgebra.MemberOfSeq;
   ExportLeafRep                                  : Natural;
   LeafRhoCol                                     : SeqAlgebra.Seq;
   NewError                                       : Natural;
   MemberOfDependencyCol                          : SeqAlgebra.MemberOfSeq;
   DepRep                                         : Natural;
   DepSym                                         : Dictionary.Symbol;
   DepLeaves                                      : SeqAlgebra.Seq;
   MemberOfRhoCol                                 : SeqAlgebra.MemberOfSeq;
   RhoSym                                         : Dictionary.Symbol;
   RhoRep                                         : Natural;
   RootRhoRep                                     : Natural;
   PreservedVars                                  : SeqAlgebra.Seq;
   PreservedExportLeaves                          : SeqAlgebra.Seq;

   function IsNonLeafRecord (Sym : Dictionary.Symbol) return Boolean
   --# global in ComponentData;
   --#        in Dictionary.Dict;
   is
   begin
      return Dictionary.Is_Variable (Sym)
        and then Dictionary.TypeIsRecord (Dictionary.GetType (Sym))
        and then ComponentManager.HasChildren (ComponentData, ComponentManager.GetComponentNode (ComponentData, Sym));
   end IsNonLeafRecord;

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

   function EmptyIntersection (A, B : SeqAlgebra.Seq) return Boolean
   --# global in TheHeap;
   is
      Empty              : Boolean := True;
      M, N               : SeqAlgebra.MemberOfSeq;
      ValueOfM, ValueOfN : Natural;
   begin
      M := SeqAlgebra.FirstMember (TheHeap, A);
      N := SeqAlgebra.FirstMember (TheHeap, B);
      loop
         exit when SeqAlgebra.IsNullMember (M) or SeqAlgebra.IsNullMember (N);
         ValueOfM := SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                                 M        => M);
         ValueOfN := SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                                 M        => N);
         if ValueOfM < ValueOfN then
            M := SeqAlgebra.NextMember (TheHeap, M);
         elsif ValueOfM > ValueOfN then
            N := SeqAlgebra.NextMember (TheHeap, N);
         else --equal values, intersection is not empty
            Empty := False;
         end if;
         exit when not Empty;
      end loop;
      return Empty;
   end EmptyIntersection;

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

begin --CheckDependencies

   -- check effectiveness of initializations of package own variables;
   MemberOfInitVars := SeqAlgebra.FirstMember (TheHeap, SeqOfInitVars);
   while not SeqAlgebra.IsNullMember (MemberOfInitVars) loop
      InitVarRep := SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                                M        => MemberOfInitVars);
      InitVar    := Dictionary.ConvertSymbolRef (ExaminerConstants.RefType (InitVarRep));
      RelationAlgebra.RowExtraction (TheHeap, IFA_Stack.Top (S).Rho, InitVarRep, RhoRow);
      SeqAlgebra.Intersection (TheHeap, RhoRow, ExpSeqOfExports, IntersectionSeq);
      if SeqAlgebra.IsEmptySeq (TheHeap, IntersectionSeq) then
         if Dictionary.IsSubcomponent (InitVar) then
            ComponentErrors.CreateError
              (TheErrorHeap,
               TheHeap,
               ComponentErrors.Dependency,
               ErrorHandler.Dependency_Err_Type'Pos (ErrorHandler.Ineff_Init),
               EndPosition,
               Dictionary.NullSymbol,
               --to get
               NewError);
            ComponentManager.AddError
              (TheHeap,
               TheErrorHeap,
               ComponentData,
               ComponentManager.GetComponentNode (ComponentData, InitVar),
               NewError);
         else
            ErrorHandler.Dependency_Error
              (Err_Type       => ErrorHandler.Ineff_Init,
               Position       => EndPosition,
               Import_Var_Sym => InitVar,
               Export_Var_Sym => Dictionary.NullSymbol,
               Scope          => Scope);
         end if;
      end if;
      SeqAlgebra.DisposeOfSeq (TheHeap, RhoRow);
      SeqAlgebra.DisposeOfSeq (TheHeap, IntersectionSeq);
      MemberOfInitVars := SeqAlgebra.NextMember (TheHeap, MemberOfInitVars);
   end loop;

   --calculate preserved vars for use in implicit record self-dependency
   SeqAlgebra.Complement (TheHeap, IFA_Stack.Top (S).AllVars, IFA_Stack.Top (S).UnPreservedVars,
                          --to get
                          PreservedVars);

   MemberOfExports := SeqAlgebra.FirstMember (TheHeap, SeqOfExports);
   while not SeqAlgebra.IsNullMember (MemberOfExports) loop
      ExpVarRep := SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                               M        => MemberOfExports);
      ExportVar := Dictionary.ConvertSymbolRef (ExaminerConstants.RefType (ExpVarRep));

      -- Do not process any dependency stuff associated with implicit exports of mode in
      if Dictionary.GetOwnVariableOrConstituentMode (ExportVar) /= Dictionary.InMode
        and then
        -- nor anything to do with exporting of the "data sink" null variable
        not Dictionary.Is_Null_Variable (ExportVar)
        and then
        -- nor updates of implicit in streams associated with protected state
        not Dictionary.IsImplicitInStream (ExportVar) then

         RelationAlgebra.ColExtraction
           (TheHeap,
            DependencyRelation,
            SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                        M        => MemberOfExports),
            --to get
            DependencyCol);
         if IsNonLeafRecord (ExportVar) then       -- 9.3.2 (3) of S.P0466.53.9
            SeqAlgebra.CreateSeq (TheHeap, RhoCol);
            ComponentManager.GetLeaves
              (TheHeap,
               ComponentData,
               ComponentManager.GetComponentNode (ComponentData, ExportVar),
               --to get
               ExportLeaves);
            MemberOfExportLeaves := SeqAlgebra.FirstMember (TheHeap, ExportLeaves);
            while not SeqAlgebra.IsNullMember (MemberOfExportLeaves) loop
               ExportLeafRep := SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                                            M        => MemberOfExportLeaves);
               RelationAlgebra.ColExtraction (TheHeap, IFA_Stack.Top (S).Rho, ExportLeafRep,
                                              --to get
                                              LeafRhoCol);
               SeqAlgebra.AugmentSeq (TheHeap, RhoCol,
                                      --with
                                      LeafRhoCol);
               SeqAlgebra.DisposeOfSeq (TheHeap, LeafRhoCol);

               MemberOfExportLeaves := SeqAlgebra.NextMember (TheHeap, MemberOfExportLeaves);
            end loop;

            --added this to handle implicit dependency of record on itself
            SeqAlgebra.Intersection (TheHeap, PreservedVars, ExportLeaves,
                                     --to get
                                     PreservedExportLeaves);
            SeqAlgebra.AugmentSeq (TheHeap, RhoCol,
                                   --with
                                   PreservedExportLeaves);
         else
            RelationAlgebra.ColExtraction (TheHeap, IFA_Stack.Top (S).Rho, ExpVarRep,
                                           --to get
                                           RhoCol);
         end if;

         MemberOfDependencyCol := SeqAlgebra.FirstMember (TheHeap, DependencyCol);
         -- If the spec says "derives Y from X" then there should be an entry in
         -- the Rho relation for this dependency. If not then flow error 50 is raised.
         -- This check is not performed in data-flow analysis mode, or if flow=auto
         -- and there was no derives annotation, because in those cases the 'specified'
         -- dependency will be a synthesised 'all exports from all imports'.
         --
         -- It *is* possible to have a package symbol here, hence the guards.
         -- Don't check calculated rho against specified rho if there was no derives
         -- annotation, unless the subprogram is a function, in which case we can check
         -- it, even in data-flow mode.
         while ((Dictionary.IsFunction (SubprogSym)
                   or else Dictionary.IsProcedure (SubprogSym)
                   or else Dictionary.IsTaskType (SubprogSym))
                and then Dictionary.GetHasDerivesAnnotation (SubprogSym))
           and then not SeqAlgebra.IsNullMember (MemberOfDependencyCol) loop
            DepRep := SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                                  M        => MemberOfDependencyCol);
            DepSym := Dictionary.ConvertSymbolRef (ExaminerConstants.RefType (DepRep));

            if IsNonLeafRecord (DepSym) then
               ComponentManager.GetLeaves
                 (TheHeap,
                  ComponentData,
                  ComponentManager.GetComponentNode (ComponentData, DepSym),
                  --to get
                  DepLeaves);
               if EmptyIntersection (DepLeaves, RhoCol) then
                  ErrorHandler.Dependency_Error
                    (Err_Type       => ErrorHandler.Not_Used,
                     Position       => EndPosition,
                     Import_Var_Sym => DepSym,
                     Export_Var_Sym => ExportVar,
                     Scope          => Scope);
               end if;
            else
               if not SeqAlgebra.IsMember (TheHeap, RhoCol, DepRep) then
                  ErrorHandler.Dependency_Error
                    (Err_Type       => ErrorHandler.Not_Used,
                     Position       => EndPosition,
                     Import_Var_Sym => DepSym,
                     Export_Var_Sym => ExportVar,
                     Scope          => Scope);
               end if;
            end if;
            MemberOfDependencyCol := SeqAlgebra.NextMember (TheHeap, MemberOfDependencyCol);
         end loop;

         -- start of 9.3.3 of S.P0468.53.9
         SeqAlgebra.Reduction (TheHeap, RhoCol, SeqOfInitVars);
         MemberOfRhoCol := SeqAlgebra.FirstMember (TheHeap, RhoCol);
         while not SeqAlgebra.IsNullMember (MemberOfRhoCol) loop
            RhoRep := SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                                  M        => MemberOfRhoCol);
            RhoSym := Dictionary.ConvertSymbolRef (ExaminerConstants.RefType (RhoRep));
            --add guard to prevent dependencies on OUT streams being reported
            if Dictionary.GetOwnVariableOrConstituentMode (Dictionary.GetMostEnclosingObject (RhoSym)) /=
              Dictionary.OutMode then
               if Dictionary.IsSubcomponent (RhoSym) then
                  RootRhoRep :=
                    Natural (Dictionary.SymbolRef
                               (ComponentManager.GetName
                                  (ComponentData,
                                   ComponentManager.GetRoot
                                     (ComponentData,
                                      ComponentManager.GetComponentNode (ComponentData, RhoSym)))));
                  if not SeqAlgebra.IsMember (TheHeap, DependencyCol, RootRhoRep) then
                     if SeqAlgebra.IsMember (TheHeap, SeqOfImports, RootRhoRep) then
                        ComponentErrors.CreateError
                          (TheErrorHeap,
                           TheHeap,
                           ComponentErrors.Dependency,
                           ErrorHandler.Dependency_Err_Type'Pos (ErrorHandler.May_Be_Used),
                           EndPosition,
                           ExportVar,
                           --to get
                           NewError);
                     else
                        ComponentErrors.CreateError
                          (TheErrorHeap,
                           TheHeap,
                           ComponentErrors.Dependency,
                           ErrorHandler.Dependency_Err_Type'Pos (ErrorHandler.Uninitialised),
                           EndPosition,
                           ExportVar,
                           --to get
                           NewError);
                     end if;
                     ComponentManager.AddError
                       (TheHeap,
                        TheErrorHeap,
                        ComponentData,
                        ComponentManager.GetComponentNode (ComponentData, RhoSym),
                        NewError);
                  end if;

               else --not a record or an entire record
                  if not SeqAlgebra.IsMember (TheHeap, DependencyCol, RhoRep) then
                     if SeqAlgebra.IsMember (TheHeap, SeqOfImports, RhoRep) then

                        if Dictionary.RelationViolatesInfoFlowPolicy (ExportVar, RhoSym) then
                           ErrorHandler.Dependency_Error
                             (Err_Type       => ErrorHandler.May_Be_Integrity_Violation,
                              Position       => EndPosition,
                              Import_Var_Sym => RhoSym,
                              Export_Var_Sym => ExportVar,
                              Scope          => Scope);
                        else
                           ErrorHandler.Dependency_Error
                             (Err_Type       => ErrorHandler.May_Be_Used,
                              Position       => EndPosition,
                              Import_Var_Sym => RhoSym,
                              Export_Var_Sym => ExportVar,
                              Scope          => Scope);
                        end if;

                     else
                        ErrorHandler.Dependency_Error
                          (Err_Type       => ErrorHandler.Uninitialised,
                           Position       => EndPosition,
                           Import_Var_Sym => RhoSym,
                           Export_Var_Sym => ExportVar,
                           Scope          => Scope);
                     end if;
                  end if;
               end if;
            end if;
            MemberOfRhoCol := SeqAlgebra.NextMember (TheHeap, MemberOfRhoCol);
         end loop;

         SeqAlgebra.DisposeOfSeq (TheHeap, DependencyCol);
         SeqAlgebra.DisposeOfSeq (TheHeap, RhoCol);
      end if; -- ignoring exports of mode in and exports that are null variables (data sinks)
      MemberOfExports := SeqAlgebra.NextMember (TheHeap, MemberOfExports);
   end loop;
   SeqAlgebra.DisposeOfSeq (TheHeap, PreservedVars);
end CheckDependencies;
