-------------------------------------------------------------------------------
-- (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)
procedure Check_Announced_Types_Declared
  (Pack_Sym : in Dictionary.Symbol;
   Scope    : in Dictionary.Scopes;
   Node_Pos : in LexTokenManager.Token_Position)
is

   Type_List : Dictionary.Iterator;
   The_Type  : Dictionary.Symbol;

   function Type_Is_Protected (The_Type : Dictionary.Symbol;
                               Pack_Sym : Dictionary.Symbol) return Boolean
   --# global in Dictionary.Dict;
   --#        in Scope;
   is
      -- This function is necessary because at this point the type symbol is
      -- simply a placeholder generated from the type announcement and contains
      -- no information as to what the type is. So we must iterate over the own
      -- variables looking for this type and then we can deduce whether the
      -- type is protected.
      Own_List    : Dictionary.Iterator;
      The_Own_Var : Dictionary.Symbol;
      Result      : Boolean := False;
   begin
      Own_List := Dictionary.FirstOwnVariable (Pack_Sym);
      while not Dictionary.IsNullIterator (Own_List) loop
         The_Own_Var := Dictionary.CurrentSymbol (Own_List);
         if Dictionary.OwnVariableHasType (The_Own_Var, Scope)
           and then Dictionary.Types_Are_Equal
           (Left_Symbol        => Dictionary.GetType (The_Own_Var),
            Right_Symbol       => The_Type,
            Full_Range_Subtype => False)
           and then Dictionary.HasValidPriorityProperty (The_Own_Var) then
            Result := True;
            exit;
         end if;
         Own_List := Dictionary.NextSymbol (Own_List);
      end loop;
      return Result;
   end Type_Is_Protected;

   function Type_Is_Task (The_Type : Dictionary.Symbol;
                          Pack_Sym : Dictionary.Symbol) return Boolean
   --# global in Dictionary.Dict;
   is
      -- As above but looking through the own tasks.
      Own_List     : Dictionary.Iterator;
      The_Own_Task : Dictionary.Symbol;
      Result       : Boolean := False;
   begin
      Own_List := Dictionary.FirstOwnTask (Pack_Sym);
      while not Dictionary.IsNullIterator (Own_List) loop
         The_Own_Task := Dictionary.CurrentSymbol (Own_List);
         if Dictionary.Types_Are_Equal
           (Left_Symbol        => Dictionary.GetType (The_Own_Task),
            Right_Symbol       => The_Type,
            Full_Range_Subtype => False) then
            Result := True;
            exit;
         end if;
         Own_List := Dictionary.NextSymbol (Own_List);
      end loop;
      return Result;
   end Type_Is_Task;

begin -- Check_Announced_Types_Declared
   Type_List := Dictionary.First_Undeclared_Type (The_Package => Pack_Sym);
   while not Dictionary.IsNullIterator (Type_List) loop
      The_Type := Dictionary.CurrentSymbol (Type_List);

      -- Are we in the package specification?

      --# accept Flow, 41, "Expected stable expression";
      if Dictionary.Get_Visibility (Scope => Scope) = Dictionary.Visible then
         --# end accept;
         if Type_Is_Protected (The_Type => The_Type,
                               Pack_Sym => Pack_Sym)
           or else Type_Is_Task (The_Type => The_Type,
                                 Pack_Sym => Pack_Sym) then

            ErrorHandler.Semantic_Error
              (Err_Num   => 325,
               Reference => ErrorHandler.No_Reference,
               Position  => Node_Pos,
               Id_Str    => Dictionary.GetSimpleName (The_Type));
         end if;

      else
         -- We're in the package body..
         if not Dictionary.TypeIsPrivate (TheType => Dictionary.CurrentSymbol (Type_List))
           and then not Type_Is_Protected (The_Type => The_Type,
                                           Pack_Sym => Pack_Sym)
           and then not Type_Is_Task (The_Type => The_Type,
                                      Pack_Sym => Pack_Sym) then
            ErrorHandler.Semantic_Error
              (Err_Num   => 325,
               Reference => ErrorHandler.No_Reference,
               Position  => Node_Pos,
               Id_Str    => Dictionary.GetSimpleName (The_Type));
         end if;
      end if;

      Type_List := Dictionary.NextSymbol (Type_List);
   end loop;
end Check_Announced_Types_Declared;
