-------------------------------------------------------------------------------
-- (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 CheckExpressions is
   Expn, ZeroStableExpn, OneStableExpn, OtherStableExpn, Variable : SeqAlgebra.MemberOfSeq;
   ExpnNmbr, VarNmbr                                              : Natural;
   VarSym                                                         : Dictionary.Symbol;
   Intersecn, LambdaCol, MuRow, ThetaCol, ThetaTildeCol           : SeqAlgebra.Seq;

   procedure AddRecordComponentError
     (ErrClass : in ComponentErrors.ErrorClass;
      ErrVal   : in Natural;
      Position : in LexTokenManager.Token_Position;
      Sym      : in Dictionary.Symbol)
   --# global in     ComponentData;
   --#        in out Statistics.TableUsage;
   --#        in out TheErrorHeap;
   --#        in out TheHeap;
   --# derives Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    ComponentData,
   --#                                    ErrClass,
   --#                                    ErrVal,
   --#                                    Position,
   --#                                    Sym,
   --#                                    TheErrorHeap,
   --#                                    TheHeap &
   --#         TheErrorHeap          from *,
   --#                                    ErrClass,
   --#                                    ErrVal,
   --#                                    Position,
   --#                                    TheHeap;
   is
      NewError : Natural;
   begin
      ComponentErrors.CreateError (TheErrorHeap, TheHeap, ErrClass, ErrVal, Position, Dictionary.NullSymbol,
                                   --to get
                                   NewError);

      ComponentManager.AddError
        (TheHeap,
         TheErrorHeap,
         ComponentData,
         ComponentManager.GetComponentNode (ComponentData, Sym),
         NewError);
   end AddRecordComponentError;

begin -- CheckExpressions
   Expn            := SeqAlgebra.FirstMember (TheHeap, IFA_Stack.Top (S).SeqOfExpns);
   ZeroStableExpn  := SeqAlgebra.FirstMember (TheHeap, ZeroStableExpnSeq);
   OneStableExpn   := SeqAlgebra.FirstMember (TheHeap, OneStableExpnSeq);
   OtherStableExpn := SeqAlgebra.FirstMember (TheHeap, OtherStableExpnSeq);
   while not SeqAlgebra.IsNullMember (Expn) loop
      --# assert True;
      ExpnNmbr := SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                              M        => Expn);
      if KindDictionary (ExpnNmbr) /= ControlVarAssignment and then KindDictionary (ExpnNmbr) /= ModellingStmt then
         RelationAlgebra.ColExtraction (TheHeap, IFA_Stack.Top (S).Theta, ExpnNmbr, ThetaCol);
         RelationAlgebra.ColExtraction (TheHeap, IFA_Stack.Top (S).ThetaTilde, ExpnNmbr, ThetaTildeCol);
         SeqAlgebra.Reduction (TheHeap, ThetaCol, SeqOfInitVars);
         SeqAlgebra.Reduction (TheHeap, ThetaCol, ExpSeqOfImports);
         Variable := SeqAlgebra.FirstMember (TheHeap, ThetaCol);
         while not SeqAlgebra.IsNullMember (Variable) loop
            DataFlowErrorFoundLocal := True; -- signal presence of data flow error to caller
                                             --# assert True;
            VarNmbr                 := SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                                                   M        => Variable);
            VarSym                  := Dictionary.ConvertSymbolRef (ExaminerConstants.RefType (VarNmbr));
            if SeqAlgebra.IsMember (TheHeap, ThetaTildeCol, VarNmbr) then
               --# accept Flow, 41, "Expected stable expression";
               case KindDictionary (ExpnNmbr) is
                  when ComplexAssignment | FieldUpdateByProc =>
                     --# end accept;
                     if Dictionary.IsSubcomponent (VarSym) then
                        AddRecordComponentError
                          (ComponentErrors.DataFlow,
                           ErrorHandler.Data_Flow_Err_Type'Pos (ErrorHandler.Stmt_Undefined),
                           STree.Node_Position (Node => StmtLocations (ExpnNmbr)),
                           VarSym);
                     else
                        ErrorHandler.Data_Flow_Error
                          (Err_Type => ErrorHandler.Stmt_Undefined,
                           Position => STree.Node_Position (Node => StmtLocations (ExpnNmbr)),
                           Var_Sym  => VarSym,
                           Scope    => Scope);
                     end if;
                  when others =>
                     if Dictionary.IsSubcomponent (VarSym) then
                        AddRecordComponentError
                          (ComponentErrors.DataFlow,
                           ErrorHandler.Data_Flow_Err_Type'Pos (ErrorHandler.Expn_Undefined),
                           STree.Node_Position (Node => ExpnLocations (ExpnNmbr)),
                           VarSym);
                     else
                        ErrorHandler.Data_Flow_Error
                          (Err_Type => ErrorHandler.Expn_Undefined,
                           Position => STree.Node_Position (Node => ExpnLocations (ExpnNmbr)),
                           Var_Sym  => VarSym,
                           Scope    => Scope);
                     end if;
               end case;
            else
               --# accept Flow, 41, "Expected stable expression";
               case KindDictionary (ExpnNmbr) is
                  when ComplexAssignment | FieldUpdateByProc =>
                     --# end accept;
                     if Dictionary.IsSubcomponent (VarSym) then
                        AddRecordComponentError
                          (ComponentErrors.DataFlow,
                           ErrorHandler.Data_Flow_Err_Type'Pos (ErrorHandler.Stmt_May_Be_Undefined),
                           STree.Node_Position (Node => StmtLocations (ExpnNmbr)),
                           VarSym);
                     else
                        ErrorHandler.Data_Flow_Error
                          (Err_Type => ErrorHandler.Stmt_May_Be_Undefined,
                           Position => STree.Node_Position (Node => StmtLocations (ExpnNmbr)),
                           Var_Sym  => VarSym,
                           Scope    => Scope);
                     end if;
                  when others =>
                     if Dictionary.IsSubcomponent (VarSym) then
                        AddRecordComponentError
                          (ComponentErrors.DataFlow,
                           ErrorHandler.Data_Flow_Err_Type'Pos (ErrorHandler.Expn_May_Be_Undefined),
                           STree.Node_Position (Node => ExpnLocations (ExpnNmbr)),
                           VarSym);
                     else
                        ErrorHandler.Data_Flow_Error
                          (Err_Type => ErrorHandler.Expn_May_Be_Undefined,
                           Position => STree.Node_Position (Node => ExpnLocations (ExpnNmbr)),
                           Var_Sym  => VarSym,
                           Scope    => Scope);
                     end if;
               end case;
            end if;
            Variable := SeqAlgebra.NextMember (TheHeap, Variable);
         end loop;
         SeqAlgebra.DisposeOfSeq (TheHeap, ThetaCol);
         SeqAlgebra.DisposeOfSeq (TheHeap, ThetaTildeCol);

         --# assert True;

         RelationAlgebra.RowExtraction (TheHeap, IFA_Stack.Top (S).Mu, ExpnNmbr, MuRow);
         SeqAlgebra.Intersection (TheHeap, MuRow, ExpSeqOfExports, Intersecn);
         if SeqAlgebra.IsEmptySeq (TheHeap, Intersecn) then
            VarSym := ParamDictionary (ExpnNmbr);
            if not Dictionary.Is_Null_Variable (VarSym) then -- don't report
               case KindDictionary (ExpnNmbr) is
                  when ComplexAssignment | FieldUpdateByProc =>
                     if Dictionary.IsSubcomponent (VarSym) then
                        if KindDictionary (ExpnNmbr) = ComplexAssignment then
                           AddRecordComponentError
                             (ComponentErrors.IneffectiveStmt,
                              0,
                              STree.Node_Position (Node => StmtLocations (ExpnNmbr)),
                              VarSym);
                        else --must be a record field directly assigned by proc call
                           AddRecordComponentError
                             (ComponentErrors.IneffectiveFieldAssignment,
                              0,
                              STree.Node_Position (Node => StmtLocations (ExpnNmbr)),
                              VarSym);
                        end if;
                     else
                        ErrorHandler.Ineffective_Stmt
                          (STree.Node_Position (Node => StmtLocations (ExpnNmbr)),
                           ParamDictionary (ExpnNmbr),
                           Scope);
                     end if;

                  when Initialization =>
                     --# accept Flow, 41, "Expected stable expression";
                     if Dictionary.Is_Subprogram (SubprogSym) then
                        --# end accept;
                        if Dictionary.IsSubcomponent (VarSym) then
                           AddRecordComponentError
                             (ComponentErrors.Dependency,
                              ErrorHandler.Dependency_Err_Type'Pos (ErrorHandler.Ineff_Local_Init),
                              STree.Node_Position
                                (Node => STree.RefToNode
                                   (Dictionary.GetVariableExpNode
                                      (ComponentManager.GetName
                                         (ComponentData,
                                          ComponentManager.GetRoot
                                            (ComponentData,
                                             ComponentManager.GetComponentNode (ComponentData, VarSym)))))),
                              VarSym);
                        else
                           ErrorHandler.Dependency_Error
                             (Err_Type       => ErrorHandler.Ineff_Local_Init,
                              Position       => STree.Node_Position
                                (Node => STree.RefToNode (Dictionary.GetVariableExpNode (ParamDictionary (ExpnNmbr)))),
                              Import_Var_Sym => VarSym,
                              Export_Var_Sym => Dictionary.NullSymbol,
                              Scope          => Scope);
                        end if;
                     else --must be package
                        if Dictionary.IsSubcomponent (VarSym) then
                           AddRecordComponentError
                             (ComponentErrors.Dependency,
                              ErrorHandler.Dependency_Err_Type'Pos (ErrorHandler.Ineff_Local_Init),
                              EndPosition,
                              VarSym);
                        else
                           ErrorHandler.Dependency_Error
                             (Err_Type       => ErrorHandler.Ineff_Local_Init,
                              Position       => EndPosition,
                              Import_Var_Sym => VarSym,
                              Export_Var_Sym => Dictionary.NullSymbol,
                              Scope          => Scope);
                        end if;
                     end if;

                  when others =>
                     ErrorHandler.Ineffective_Stmt
                       (Position => STree.Node_Position (Node => StmtLocations (ExpnNmbr)),
                        Var_Sym  => Dictionary.NullSymbol,
                        Scope    => Scope);
               end case;
            end if; -- null variable
         end if;
         SeqAlgebra.DisposeOfSeq (TheHeap, Intersecn);
         SeqAlgebra.DisposeOfSeq (TheHeap, MuRow);

         --# assert True;
         if (KindDictionary (ExpnNmbr) = ForkExpn) and not SeqAlgebra.IsMember (TheHeap, InnerExpns, ExpnNmbr) then
            RelationAlgebra.ColExtraction (TheHeap, IFA_Stack.Top (S).Lambda, ExpnNmbr, LambdaCol);
            SeqAlgebra.Intersection (TheHeap, LambdaCol, ExpSeqOfImports, Intersecn);
            if SeqAlgebra.IsEmptySeq (TheHeap, Intersecn) then
               ErrorHandler.Data_Flow_Error
                 (Err_Type => ErrorHandler.Invariant_Exp,
                  Position => STree.Node_Position (Node => ExpnLocations (ExpnNmbr)),
                  Var_Sym  => Dictionary.NullSymbol,
                  Scope    => Scope);
            end if;
            SeqAlgebra.DisposeOfSeq (TheHeap, LambdaCol);
            SeqAlgebra.DisposeOfSeq (TheHeap, Intersecn);
         end if;

         --# assert True;
         if not SeqAlgebra.IsNullMember (ZeroStableExpn) then
            if SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                           M        => ZeroStableExpn) = ExpnNmbr then
               if (KindDictionary (ExpnNmbr) = ExitExpn) then
                  ErrorHandler.Stability_Error
                    (Err_Type        => ErrorHandler.Stable_Exit_Cond,
                     Position        => STree.Node_Position (Node => ExpnLocations (ExpnNmbr)),
                     Stability_Index => ErrorHandler.Index_Zero);
               elsif (KindDictionary (ExpnNmbr) = ForkExpn) then
                  ErrorHandler.Stability_Error
                    (Err_Type        => ErrorHandler.Stable_Fork_Cond,
                     Position        => STree.Node_Position (Node => ExpnLocations (ExpnNmbr)),
                     Stability_Index => ErrorHandler.Index_Zero);
               end if;
               -- 898
               -- we also have a kind of stable exit in the case of a DefaultExitExpression
               -- inserted into infinite loops at the "End_of_loop" node so as to provide
               -- a syntactic exit point for the benefit of the flow analyser.  Since a
               -- DefaultExitExpression is neither an ExitExpn or a ForkExpn, it is ignored
               -- by the preceding if statement and no error is reported.

               ZeroStableExpn := SeqAlgebra.NextMember (TheHeap, ZeroStableExpn);
            end if;
         end if;

         --# assert True;
         if not SeqAlgebra.IsNullMember (OneStableExpn) then
            if SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                           M        => OneStableExpn) = ExpnNmbr then
               if (KindDictionary (ExpnNmbr) = ExitExpn) then
                  ErrorHandler.Stability_Error
                    (Err_Type        => ErrorHandler.Stable_Exit_Cond,
                     Position        => STree.Node_Position (Node => ExpnLocations (ExpnNmbr)),
                     Stability_Index => ErrorHandler.Index_One);
               elsif (KindDictionary (ExpnNmbr) = ForkExpn) then
                  ErrorHandler.Stability_Error
                    (Err_Type        => ErrorHandler.Stable_Fork_Cond,
                     Position        => STree.Node_Position (Node => ExpnLocations (ExpnNmbr)),
                     Stability_Index => ErrorHandler.Index_One);
               end if;
               OneStableExpn := SeqAlgebra.NextMember (TheHeap, OneStableExpn);
            end if;
         end if;

         --# assert True;
         if not SeqAlgebra.IsNullMember (OtherStableExpn) then
            if SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                           M        => OtherStableExpn) = ExpnNmbr then
               if (KindDictionary (ExpnNmbr) = ExitExpn) then
                  ErrorHandler.Stability_Error
                    (Err_Type        => ErrorHandler.Stable_Exit_Cond,
                     Position        => STree.Node_Position (Node => ExpnLocations (ExpnNmbr)),
                     Stability_Index => ErrorHandler.Larger_Index);
               elsif (KindDictionary (ExpnNmbr) = ForkExpn) then
                  ErrorHandler.Stability_Error
                    (Err_Type        => ErrorHandler.Stable_Fork_Cond,
                     Position        => STree.Node_Position (Node => ExpnLocations (ExpnNmbr)),
                     Stability_Index => ErrorHandler.Larger_Index);
               end if;
               OtherStableExpn := SeqAlgebra.NextMember (TheHeap, OtherStableExpn);
            end if;
         end if;
      end if;
      Expn := SeqAlgebra.NextMember (TheHeap, Expn);
   end loop;
end CheckExpressions;
