-------------------------------------------------------------------------------
-- (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.Wf_Package_Declaration.Wf_Package_Specification.Wf_Visible)
procedure Wf_Deferred (Node          : in STree.SyntaxNode;
                       Pack_Sym      : in Dictionary.Symbol;
                       Current_Scope : in Dictionary.Scopes) is
   Ident_List_Node, Type_Node, Next_Node : STree.SyntaxNode;
   It                                    : STree.Iterator;
   Ident_Str                             : LexTokenManager.Lex_String;
   Type_Sym, The_Constant                : Dictionary.Symbol;

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

   procedure Wf_Local_Private_Type
     (Node          : in     STree.SyntaxNode;
      Current_Scope : in     Dictionary.Scopes;
      Type_Sym      :    out Dictionary.Symbol)
   --# global in     CommandLineData.Content;
   --#        in     Dictionary.Dict;
   --#        in     LexTokenManager.State;
   --#        in out ErrorHandler.Error_Context;
   --#        in out SPARK_IO.File_Sys;
   --#        in out STree.Table;
   --# derives ErrorHandler.Error_Context,
   --#         SPARK_IO.File_Sys          from CommandLineData.Content,
   --#                                         Current_Scope,
   --#                                         Dictionary.Dict,
   --#                                         ErrorHandler.Error_Context,
   --#                                         LexTokenManager.State,
   --#                                         Node,
   --#                                         SPARK_IO.File_Sys,
   --#                                         STree.Table &
   --#         STree.Table,
   --#         Type_Sym                   from CommandLineData.Content,
   --#                                         Current_Scope,
   --#                                         Dictionary.Dict,
   --#                                         LexTokenManager.State,
   --#                                         Node,
   --#                                         STree.Table;
   --# pre Syntax_Node_Type (Node, STree.Table) = SP_Symbols.type_mark;
   --# post STree.Table = STree.Table~;
   is
      Sym        : Dictionary.Symbol;
      Ident_Node : STree.SyntaxNode;
      Ident_Str  : LexTokenManager.Lex_String;
   begin
      Ident_Node := Last_Child_Of (Start_Node => Node);
      -- ASSUME Ident_Node = identifier
      SystemErrors.RT_Assert
        (C       => Syntax_Node_Type (Node => Ident_Node) = SP_Symbols.identifier,
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Ident_Node = identifier in Wf_Local_Private_Type");

      Ident_Str := Node_Lex_String (Node => Ident_Node);
      Sym       :=
        Dictionary.LookupItem
        (Name              => Ident_Str,
         Scope             => Current_Scope,
         Context           => Dictionary.ProgramContext,
         Full_Package_Name => False);
      if Dictionary.Is_Null_Symbol (Sym) then -- not declared or visible
         Type_Sym := Dictionary.GetUnknownTypeMark;
         ErrorHandler.Semantic_Error
           (Err_Num   => 141,
            Reference => ErrorHandler.No_Reference,
            Position  => Node_Position (Node => Ident_Node),
            Id_Str    => Ident_Str);
      elsif Dictionary.IsType (Sym) and then Dictionary.TypeIsPrivate (TheType => Sym) then -- ok if not dotted
         if Next_Sibling (Child_Node (Child_Node (Node))) = STree.NullNode then -- no selector on type mark
            STree.Set_Node_Lex_String (Sym  => Sym,
                                       Node => Ident_Node);
            Type_Sym := Sym;
         else -- shouldn't have selector on private type!
            Type_Sym := Dictionary.GetUnknownTypeMark;
            ErrorHandler.Semantic_Error
              (Err_Num   => 9,
               Reference => ErrorHandler.No_Reference,
               Position  => Node_Position (Node => Ident_Node),
               Id_Str    => Ident_Str);
         end if;
      else -- not a local private type
         Type_Sym := Dictionary.GetUnknownTypeMark;
         ErrorHandler.Semantic_Error
           (Err_Num   => 89,
            Reference => 12,
            Position  => Node_Position (Node => Ident_Node),
            Id_Str    => Ident_Str);
      end if;
   end Wf_Local_Private_Type;

begin -- Wf_Deferred
   Ident_List_Node := Child_Node (Current_Node => Node);
   -- ASSUME Ident_List_Node = identifier_list
   SystemErrors.RT_Assert
     (C       => Syntax_Node_Type (Node => Ident_List_Node) = SP_Symbols.identifier_list,
      Sys_Err => SystemErrors.Invalid_Syntax_Tree,
      Msg     => "Expect Ident_List_Node = identifier_list in Wf_Deferred");

   Type_Node := Next_Sibling (Current_Node => Ident_List_Node);
   -- ASSUME Type_Node = type_mark
   SystemErrors.RT_Assert
     (C       => Syntax_Node_Type (Node => Type_Node) = SP_Symbols.type_mark,
      Sys_Err => SystemErrors.Invalid_Syntax_Tree,
      Msg     => "Expect Type_Node = type_mark in Wf_Deferred");

   case CommandLineData.Content.Language_Profile is
      when CommandLineData.SPARK83 =>
         -- Can only be a private type in SPARK83
         Wf_Local_Private_Type (Node          => Type_Node,
                                Current_Scope => Current_Scope,
                                Type_Sym      => Type_Sym);
      when CommandLineData.SPARK95_Onwards =>
         -- Any type allowed in 95 onwards
         Wf_Type_Mark
           (Node          => Type_Node,
            Current_Scope => Current_Scope,
            Context       => Dictionary.ProgramContext,
            Type_Sym      => Type_Sym);
   end case;

   It := Find_First_Node (Node_Kind    => SP_Symbols.identifier,
                          From_Root    => Ident_List_Node,
                          In_Direction => STree.Down);
   while not STree.IsNull (It) loop
      Next_Node := Get_Node (It => It);
      --# assert STree.Table = STree.Table~ and
      --#   Syntax_Node_Type (Next_Node, STree.Table) = SP_Symbols.identifier and
      --#   Next_Node = Get_Node (It);
      Ident_Str := Node_Lex_String (Node => Next_Node);
      if Dictionary.IsDefined
        (Name              => Ident_Str,
         Scope             => Current_Scope,
         Context           => Dictionary.ProofContext,
         Full_Package_Name => False) then
         ErrorHandler.Semantic_Error
           (Err_Num   => 10,
            Reference => ErrorHandler.No_Reference,
            Position  => Node_Position (Node => Next_Node),
            Id_Str    => Ident_Str);
      elsif Dictionary.IsPredefinedSuspensionObjectType (Type_Sym) or Dictionary.IsProtectedTypeMark (Type_Sym) then
         ErrorHandler.Semantic_Error
           (Err_Num   => 903,
            Reference => ErrorHandler.No_Reference,
            Position  => Node_Position (Node => Type_Node),
            Id_Str    => LexTokenManager.Null_String);
      else
         Dictionary.Add_Deferred_Constant
           (Name           => Ident_Str,
            Type_Mark      => Type_Sym,
            Type_Reference => Dictionary.Location'(Start_Position => Node_Position (Node => Type_Node),
                                                   End_Position   => Node_Position (Node => Type_Node)),
            Comp_Unit      => ContextManager.Ops.Current_Unit,
            Declaration    => Dictionary.Location'(Start_Position => Node_Position (Node => Next_Node),
                                                   End_Position   => Node_Position (Node => Next_Node)),
            The_Package    => Pack_Sym,
            TheConstant    => The_Constant);
         STree.Add_Node_Symbol (Node => Next_Node,
                                Sym  => The_Constant);
      end if;
      It := STree.NextNode (It);
   end loop;
end Wf_Deferred;
