-------------------------------------------------------------------------------
-- (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.
--
--=============================================================================

with SLI;

separate (Sem.Wf_Basic_Declarative_Item.Wf_Basic_Declaration.Wf_Full_Type_Declaration)
procedure Wf_Protected_Type_Declaration
  (Node           : in     STree.SyntaxNode;
   Scope          : in     Dictionary.Scopes;
   Component_Data : in out ComponentManager.ComponentData;
   The_Heap       : in out Heap.HeapRecord)
is
   Protected_Type_Sym              : Dictionary.Symbol;
   Sym                             : Dictionary.Symbol;
   Protected_Scope                 : Dictionary.Scopes;
   Protected_Private_Scope         : Dictionary.Scopes;
   Ident_Node                      : STree.SyntaxNode;
   Protected_Element_Decl_Node     : STree.SyntaxNode;
   Protected_Element_Node          : STree.SyntaxNode;
   Closing_Ident_Node              : STree.SyntaxNode;
   Discriminant_Node               : STree.SyntaxNode;
   Ident_Str                       : LexTokenManager.Lex_String;
   At_Least_One_Operation_Declared : Boolean;
   It                              : STree.Iterator;

   function Get_Discriminant_Node (Protected_Type_Declaration_Node : STree.SyntaxNode) return STree.SyntaxNode
   --# global in STree.Table;
   --# pre Syntax_Node_Type (Protected_Type_Declaration_Node, STree.Table) = SP_Symbols.protected_type_declaration;
   is
      Return_Node : STree.SyntaxNode;
   begin
      Return_Node := Next_Sibling (Current_Node => Child_Node (Current_Node => Protected_Type_Declaration_Node));
      -- ASSUME Return_Node = known_discriminant_part OR protected_definition
      if Syntax_Node_Type (Node => Return_Node) = SP_Symbols.protected_definition then
         -- ASSUME Return_Node = protected_definition
         Return_Node := STree.NullNode;
      elsif Syntax_Node_Type (Node => Return_Node) /= SP_Symbols.known_discriminant_part then
         Return_Node := STree.NullNode;
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Return_Node = known_discriminant_part OR protected_definition in Get_Discriminant_Node");
      end if;
      -- ASSUME Return_Node = known_discriminant_part OR NULL
      SystemErrors.RT_Assert
        (C       => Return_Node = STree.NullNode
           or else Syntax_Node_Type (Node => Return_Node) = SP_Symbols.known_discriminant_part,
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Return_Node = known_discriminant_part OR NULL in Get_Discriminant_Node");
      return Return_Node;
   end Get_Discriminant_Node;

   ----------

   function Get_Protected_Operations_Node (Protected_Type_Declaration_Node : STree.SyntaxNode) return STree.SyntaxNode
   --# global in STree.Table;
   --# pre Syntax_Node_Type (Protected_Type_Declaration_Node, STree.Table) = SP_Symbols.protected_type_declaration;
   --# return Node => Syntax_Node_Type (Node, STree.Table) = SP_Symbols.protected_operation_declaration;
   is
      Current_Node : STree.SyntaxNode;
   begin
      Current_Node :=
        Child_Node
        (Current_Node => Last_Sibling_Of (Start_Node => Child_Node (Current_Node => Protected_Type_Declaration_Node)));
      -- ASSUME Current_Node = protected_operation_declaration
      SystemErrors.RT_Assert
        (C       => Syntax_Node_Type (Node => Current_Node) = SP_Symbols.protected_operation_declaration,
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Current_Node = protected_operation_declaration in Get_Protected_Operations_Node");
      return Current_Node;
   end Get_Protected_Operations_Node;

   ----------

   function Get_Protected_Element_Declaration_Node (Protected_Type_Declaration_Node : STree.SyntaxNode) return STree.SyntaxNode
   --# global in STree.Table;
   --# pre Syntax_Node_Type (Protected_Type_Declaration_Node, STree.Table) = SP_Symbols.protected_type_declaration;
   is
      Return_Node : STree.SyntaxNode;
   begin
      Return_Node :=
        Next_Sibling
        (Current_Node => Get_Protected_Operations_Node (Protected_Type_Declaration_Node => Protected_Type_Declaration_Node));
      -- ASSUME Return_Node = protected_element_declaration OR identifier OR hidden_part
      if Syntax_Node_Type (Node => Return_Node) = SP_Symbols.identifier
        or else Syntax_Node_Type (Node => Return_Node) = SP_Symbols.hidden_part then
         Return_Node := STree.NullNode;
      elsif Syntax_Node_Type (Node => Return_Node) /= SP_Symbols.protected_element_declaration then
         Return_Node := STree.NullNode;
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Return_Node = protected_element_declaration OR identifier OR " &
              "hidden_part in Get_Protected_Element_Declaration_Node");
      end if;
      -- ASSUME Return_Node = protected_element_declaration OR NULL
      SystemErrors.RT_Assert
        (C       => Return_Node = STree.NullNode
           or else Syntax_Node_Type (Node => Return_Node) = SP_Symbols.protected_element_declaration,
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Return_Node = protected_element_declaration OR NULL in Get_Protected_Element_Declaration_Node");
      return Return_Node;
   end Get_Protected_Element_Declaration_Node;

   ----------

   function Get_Closing_Ident_Node (Protected_Type_Declaration_Node : STree.SyntaxNode) return STree.SyntaxNode
   --# global in STree.Table;
   --# pre Syntax_Node_Type (Protected_Type_Declaration_Node, STree.Table) = SP_Symbols.protected_type_declaration;
   is
      Return_Node : STree.SyntaxNode;
   begin
      Return_Node :=
        Last_Sibling_Of
        (Start_Node => Get_Protected_Operations_Node (Protected_Type_Declaration_Node => Protected_Type_Declaration_Node));
      -- ASSUME Return_Node = identifier OR hidden_part
      SystemErrors.RT_Assert
        (C       => Syntax_Node_Type (Node => Return_Node) = SP_Symbols.hidden_part
           or else Syntax_Node_Type (Node => Return_Node) = SP_Symbols.identifier,
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Return_Node = identifier OR hidden_part in Get_Closing_Ident_Node");
      return Return_Node;
   end Get_Closing_Ident_Node;

   ----------

   procedure Wf_Protected_Op_Dec
     (Node           : in     STree.SyntaxNode;
      Scope          : in     Dictionary.Scopes;
      Component_Data : in out ComponentManager.ComponentData;
      The_Heap       : in out Heap.HeapRecord;
      Op_Found       :    out Boolean)
   --# global in     CommandLineData.Content;
   --#        in     ContextManager.Ops.File_Heap;
   --#        in     ContextManager.Ops.Unit_Heap;
   --#        in     ContextManager.Ops.Unit_Stack;
   --#        in out Aggregate_Stack.State;
   --#        in out Dictionary.Dict;
   --#        in out ErrorHandler.Error_Context;
   --#        in out LexTokenManager.State;
   --#        in out SLI.State;
   --#        in out SPARK_IO.File_Sys;
   --#        in out Statistics.TableUsage;
   --#        in out STree.Table;
   --# derives Aggregate_Stack.State,
   --#         Component_Data,
   --#         Dictionary.Dict,
   --#         LexTokenManager.State,
   --#         Statistics.TableUsage,
   --#         STree.Table,
   --#         The_Heap                   from *,
   --#                                         CommandLineData.Content,
   --#                                         Component_Data,
   --#                                         ContextManager.Ops.Unit_Stack,
   --#                                         Dictionary.Dict,
   --#                                         LexTokenManager.State,
   --#                                         Node,
   --#                                         Scope,
   --#                                         STree.Table,
   --#                                         The_Heap &
   --#         ErrorHandler.Error_Context,
   --#         SLI.State,
   --#         SPARK_IO.File_Sys          from CommandLineData.Content,
   --#                                         Component_Data,
   --#                                         ContextManager.Ops.File_Heap,
   --#                                         ContextManager.Ops.Unit_Heap,
   --#                                         ContextManager.Ops.Unit_Stack,
   --#                                         Dictionary.Dict,
   --#                                         ErrorHandler.Error_Context,
   --#                                         LexTokenManager.State,
   --#                                         Node,
   --#                                         Scope,
   --#                                         SLI.State,
   --#                                         SPARK_IO.File_Sys,
   --#                                         STree.Table,
   --#                                         The_Heap &
   --#         Op_Found                   from CommandLineData.Content,
   --#                                         Component_Data,
   --#                                         ContextManager.Ops.Unit_Stack,
   --#                                         Dictionary.Dict,
   --#                                         LexTokenManager.State,
   --#                                         Node,
   --#                                         Scope,
   --#                                         STree.Table,
   --#                                         The_Heap;
   --# pre Syntax_Node_Type (Node, STree.Table) = SP_Symbols.protected_operation_declaration;
   --# post STree.Table = STree.Table~;
      is separate;

   ----------

   procedure Check_Pragma_Validity
     (End_Node_Position  : in LexTokenManager.Token_Position;
      Protected_Type_Sym : in Dictionary.Symbol;
      Scope              : in Dictionary.Scopes)
   --# global in     CommandLineData.Content;
   --#        in     Dictionary.Dict;
   --#        in out ErrorHandler.Error_Context;
   --#        in out LexTokenManager.State;
   --#        in out SPARK_IO.File_Sys;
   --# derives ErrorHandler.Error_Context,
   --#         SPARK_IO.File_Sys          from CommandLineData.Content,
   --#                                         Dictionary.Dict,
   --#                                         End_Node_Position,
   --#                                         ErrorHandler.Error_Context,
   --#                                         LexTokenManager.State,
   --#                                         Protected_Type_Sym,
   --#                                         Scope,
   --#                                         SPARK_IO.File_Sys &
   --#         LexTokenManager.State      from *,
   --#                                         CommandLineData.Content,
   --#                                         Dictionary.Dict,
   --#                                         Protected_Type_Sym;
   is
      Priority_Found, Interrupt_Priority_Found, Attach_Handler_Found : Boolean;
      Unused_Value_Rep                                               : LexTokenManager.Lex_String;
   begin
      Priority_Found           := Dictionary.GetTypeHasPragma (Protected_Type_Sym, Dictionary.Priority);
      Interrupt_Priority_Found := Dictionary.GetTypeHasPragma (Protected_Type_Sym, Dictionary.InterruptPriority);
      Attach_Handler_Found     := Dictionary.GetTypeHasPragma (Protected_Type_Sym, Dictionary.AttachHandler);

      -- There must be either Priority or Interrupt_Priority
      if not (Priority_Found or else Interrupt_Priority_Found) then
         ErrorHandler.Semantic_Error
           (Err_Num   => 876,
            Reference => ErrorHandler.No_Reference,
            Position  => End_Node_Position,
            Id_Str    => LexTokenManager.Null_String);
      end if;

      -- If there is one or more Attach_Handler there must be Interrupt_Priority
      if Attach_Handler_Found then
         if Interrupt_Priority_Found then
            --# accept Flow, 10, Unused_Value_Rep, "Expected ineffective assignment";
            Check_Priority_Range
              (Error_Sym   => Protected_Type_Sym,
               Scope       => Scope,
               Pragma_Kind => Dictionary.AttachHandler,
               Err_Pos     => End_Node_Position,
               Value       => Maths.ValueRep (Dictionary.GetTypePragmaValue (Protected_Type_Sym, Dictionary.InterruptPriority)),
               Value_Rep   => Unused_Value_Rep);
            --# end accept;
         else
            ErrorHandler.Semantic_Error
              (Err_Num   => 878,
               Reference => ErrorHandler.No_Reference,
               Position  => End_Node_Position,
               Id_Str    => LexTokenManager.Null_String);
         end if;
      end if;
      --# accept Flow, 33, Unused_Value_Rep, "Expected to be neither referenced nor exported";
   end Check_Pragma_Validity;

   ----------

   procedure Add_Virtual_Elements (Type_Sym : in Dictionary.Symbol)
   --# global in     ContextManager.Ops.Unit_Stack;
   --#        in     LexTokenManager.State;
   --#        in out Dictionary.Dict;
   --#        in out SPARK_IO.File_Sys;
   --# derives Dictionary.Dict   from *,
   --#                                ContextManager.Ops.Unit_Stack,
   --#                                Type_Sym &
   --#         SPARK_IO.File_Sys from *,
   --#                                ContextManager.Ops.Unit_Stack,
   --#                                Dictionary.Dict,
   --#                                LexTokenManager.State,
   --#                                Type_Sym;
   is
      It : Dictionary.Iterator;
   begin
      It := Dictionary.FirstVirtualElement (Type_Sym);
      while It /= Dictionary.NullIterator loop
         -- Make the virtual element a refinement constituent of the implicit own
         -- variable associated with this protected type.
         Dictionary.AddConstituentSym
           (ConstituentVariable  => Dictionary.CurrentSymbol (It),
            Subject              => Dictionary.GetProtectedTypeOwnVariable (Type_Sym),
            Comp_Unit            => ContextManager.Ops.Current_Unit,
            ConstituentReference => Dictionary.Null_Location);
         It := Dictionary.NextSymbol (It);
      end loop;
   end Add_Virtual_Elements;

   ----------

   procedure Add_Implicit_Interrupt_Stream_Variables
     (Protected_Type_Sym : in Dictionary.Symbol;
      Scope              : in Dictionary.Scopes;
      Error_Node_Pos     : in LexTokenManager.Token_Position)
   --# global in     CommandLineData.Content;
   --#        in     LexTokenManager.State;
   --#        in out Dictionary.Dict;
   --#        in out ErrorHandler.Error_Context;
   --#        in out SPARK_IO.File_Sys;
   --# derives Dictionary.Dict            from *,
   --#                                         CommandLineData.Content,
   --#                                         LexTokenManager.State,
   --#                                         Protected_Type_Sym,
   --#                                         Scope &
   --#         ErrorHandler.Error_Context,
   --#         SPARK_IO.File_Sys          from CommandLineData.Content,
   --#                                         Dictionary.Dict,
   --#                                         ErrorHandler.Error_Context,
   --#                                         Error_Node_Pos,
   --#                                         LexTokenManager.State,
   --#                                         Protected_Type_Sym,
   --#                                         Scope,
   --#                                         SPARK_IO.File_Sys;
   is
      The_Own_Variable : Dictionary.Symbol;
      It               : Dictionary.Iterator;

      procedure Create_Interrupt_Stream_Variables
        (For_PO         : in Dictionary.Symbol;
         Error_Node_Pos : in LexTokenManager.Token_Position)
      --# global in     CommandLineData.Content;
      --#        in     LexTokenManager.State;
      --#        in out Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out SPARK_IO.File_Sys;
      --# derives Dictionary.Dict            from *,
      --#                                         CommandLineData.Content,
      --#                                         For_PO,
      --#                                         LexTokenManager.State &
      --#         ErrorHandler.Error_Context,
      --#         SPARK_IO.File_Sys          from CommandLineData.Content,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         Error_Node_Pos,
      --#                                         For_PO,
      --#                                         LexTokenManager.State,
      --#                                         SPARK_IO.File_Sys;
      is
         It          : Dictionary.Iterator;
         The_Mapping : Dictionary.Symbol;
      begin
         It := Dictionary.FirstInterruptStreamMapping (For_PO);
         while not Dictionary.IsNullIterator (It) loop
            The_Mapping := Dictionary.CurrentSymbol (It);
            Create_Interrupt_Stream_Variable
              (For_PO              => For_PO,
               The_Handler         => Dictionary.GetInterruptStreamMappingHandler (The_Mapping),
               The_Stream_Variable => Dictionary.GetInterruptStreamMappingStream (The_Mapping),
               Error_Node_Pos      => Error_Node_Pos);
            It := Dictionary.NextSymbol (It);
         end loop;
      end Create_Interrupt_Stream_Variables;

   begin -- Add_Implicit_Interrupt_Stream_Variables

      -- Go through all the own variables of this package
      It := Dictionary.FirstOwnVariable (ThePackage => Dictionary.GetRegion (Scope));
      while It /= Dictionary.NullIterator loop
         The_Own_Variable := Dictionary.CurrentSymbol (It);
         -- If the variable is of type Protected_Type_Sym
         if Dictionary.OwnVariableHasType (OwnVariable => The_Own_Variable,
                                           Scope       => Scope)
           and then Dictionary.Types_Are_Equal
           (Left_Symbol        => Dictionary.GetType (The_Own_Variable),
            Right_Symbol       => Protected_Type_Sym,
            Full_Range_Subtype => False) then
            -- Create interrupt stream variables as necessary
            Create_Interrupt_Stream_Variables (For_PO         => The_Own_Variable,
                                               Error_Node_Pos => Error_Node_Pos);
         end if;
         It := Dictionary.NextSymbol (It);
      end loop;
   end Add_Implicit_Interrupt_Stream_Variables;

   ----------

   function Get_Mode (For_Type : in LexTokenManager.Lex_String;
                      Scope    : in Dictionary.Scopes) return Dictionary.Modes
   --# global in Dictionary.Dict;
   --#        in LexTokenManager.State;
   is
      It          : Dictionary.Iterator;
      Own_Var_Sym : Dictionary.Symbol;
      Result      : Dictionary.Modes;
   begin
      Result := Dictionary.DefaultMode;
      -- Go through all the own variables of this package looking for ones
      -- with this type.
      It := Dictionary.FirstOwnVariable (Dictionary.GetRegion (Scope));
      while not Dictionary.IsNullIterator (It) loop
         Own_Var_Sym := Dictionary.CurrentSymbol (It);
         if Dictionary.OwnVariableHasType (OwnVariable => Own_Var_Sym,
                                           Scope       => Scope)
           and then LexTokenManager.Lex_String_Case_Insensitive_Compare
           (Lex_Str1 => For_Type,
            Lex_Str2 => Dictionary.GetSimpleName (Dictionary.GetType (Own_Var_Sym))) =
           LexTokenManager.Str_Eq then
            -- Found an own variable of the type. So use its mode.
            -- Note. If the own variable is moded then there can only be
            -- one instance of that that type. So we can exit when we find
            -- the first one.
            Result := Dictionary.GetOwnVariableMode (Own_Var_Sym);
            exit;
         end if;
         It := Dictionary.NextSymbol (It);
      end loop;
      return Result;
   end Get_Mode;

   procedure Check_Element_Initialization
     (Variable_Declaration_Node : in STree.SyntaxNode;
      Error_Node_Pos            : in LexTokenManager.Token_Position;
      Current_Scope             : in Dictionary.Scopes)
   --# global in     CommandLineData.Content;
   --#        in     Dictionary.Dict;
   --#        in     LexTokenManager.State;
   --#        in     STree.Table;
   --#        in out ErrorHandler.Error_Context;
   --#        in out SPARK_IO.File_Sys;
   --# derives ErrorHandler.Error_Context,
   --#         SPARK_IO.File_Sys          from CommandLineData.Content,
   --#                                         Current_Scope,
   --#                                         Dictionary.Dict,
   --#                                         ErrorHandler.Error_Context,
   --#                                         Error_Node_Pos,
   --#                                         LexTokenManager.State,
   --#                                         SPARK_IO.File_Sys,
   --#                                         STree.Table,
   --#                                         Variable_Declaration_Node;
   --# pre Syntax_Node_Type (Variable_Declaration_Node, STree.Table) = SP_Symbols.variable_declaration;
   is
      It              : STree.Iterator;
      Ident_List_Node : STree.SyntaxNode;
      Ident_Node      : STree.SyntaxNode;
      Ident_Str       : LexTokenManager.Lex_String;
      Type_Node       : STree.SyntaxNode;
      Exp_Node        : STree.SyntaxNode;
      Sym             : Dictionary.Symbol;
   begin
      Ident_List_Node := Child_Node (Current_Node => Variable_Declaration_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 Check_Element_Initialization");

      Type_Node := Next_Sibling (Current_Node => Ident_List_Node);
      -- ASSUME Type_Node = RWaliased OR type_mark
      if Syntax_Node_Type (Node => Type_Node) = SP_Symbols.RWaliased then
         -- ASSUME Type_Node = RWaliased
         Type_Node := Next_Sibling (Current_Node => Type_Node);
      elsif Syntax_Node_Type (Node => Type_Node) /= SP_Symbols.type_mark then
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Type_Node = RWaliased OR type_mark in Check_Element_Initialization");
      end if;
      -- 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 Check_Element_Initialization");

      Exp_Node := Next_Sibling (Current_Node => Type_Node);
      -- ASSUME Exp_Node = expression OR NULL
      if Exp_Node = STree.NullNode then
         -- ASSUME Exp_Node = NULL
         -- Variable(s) ISN'T initialized - we raise an error
         -- at the node corresponsing to the end of the
         -- enclosing protected type declaration, so that the
         -- error may be justified

         -- Variable_Declaration_Node has a list of identifiers below
         -- it, so we need to raise an error for each of them.
         It := Find_First_Node (Node_Kind    => SP_Symbols.identifier,
                                From_Root    => Ident_List_Node,
                                In_Direction => STree.Down);

         while not STree.IsNull (It) loop
            Ident_Node := Get_Node (It => It);
            -- ASSUME Ident_Node = identifier
            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 the protected element had a semantic error in its declaration,
            -- then LookupItem might return NullSymbol, so
            if not Dictionary.Is_Null_Symbol (Sym) then
               ErrorHandler.Usage_Error
                 (Err_Type => ErrorHandler.Uninitialized_Protected_Element,
                  Position => Error_Node_Pos,
                  Var_Sym  => Sym,
                  Scope    => Current_Scope);
            end if;
            It := STree.NextNode (It);
         end loop;
      elsif Syntax_Node_Type (Node => Exp_Node) /= SP_Symbols.expression then
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Exp_Node = expression OR NULL in Check_Element_Initialization");
      end if;
   end Check_Element_Initialization;

begin -- Wf_Protected_Type_Declaration;
   Ident_Node := Child_Node (Current_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_Protected_Type_Declaration");
   Ident_Str := Node_Lex_String (Node => Ident_Node);
   Sym       :=
     Dictionary.LookupItem (Name              => Ident_Str,
                            Scope             => Scope,
                            Context           => Dictionary.ProofContext,
                            Full_Package_Name => False);

   if Dictionary.Is_Null_Symbol (Sym)
     or else (Dictionary.IsTypeMark (Sym)
                and then Dictionary.TypeIsAnnounced (TheType => Sym)
                and then not Dictionary.Is_Declared (Item => Sym)) then

      Discriminant_Node := Get_Discriminant_Node (Protected_Type_Declaration_Node => Node);
      -- ASSUME Discriminant_Node = known_discriminant_part OR NULL

      Closing_Ident_Node := Get_Closing_Ident_Node (Protected_Type_Declaration_Node => Node);
      -- ASSUME Closing_Ident_Node = identifier OR hidden_part
      SystemErrors.RT_Assert
        (C       => Syntax_Node_Type (Node => Closing_Ident_Node) = SP_Symbols.hidden_part
           or else Syntax_Node_Type (Node => Closing_Ident_Node) = SP_Symbols.identifier,
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Closing_Ident_Node = identifier OR hidden_part in Wf_Protected_Type_Declaration");

      if not Dictionary.Is_Null_Symbol (Sym) then
         STree.Set_Node_Lex_String (Sym  => Sym,
                                    Node => Ident_Node);
      end if;

      Dictionary.Add_Protected_Type
        (Name        => Ident_Str,
         Comp_Unit   => ContextManager.Ops.Current_Unit,
         Declaration => Dictionary.Location'(Start_Position => Node_Position (Node => Node),
                                             End_Position   => Node_Position (Node => Node)),
         Scope       => Scope,
         Context     => Dictionary.ProgramContext,
         Mode        => Get_Mode (For_Type => Ident_Str,
                                  Scope    => Scope),
         Constrained => (Syntax_Node_Type (Node => Discriminant_Node) /= SP_Symbols.known_discriminant_part),
         The_Type    => Protected_Type_Sym);
      STree.Add_Node_Symbol (Node => Ident_Node,
                             Sym  => Protected_Type_Sym);
      if ErrorHandler.Generate_SLI then
         SLI.Generate_Xref_Symbol
           (Comp_Unit      => ContextManager.Ops.Current_Unit,
            Parse_Tree     => Ident_Node,
            Symbol         => Protected_Type_Sym,
            Is_Declaration => True);
      end if;

      if Syntax_Node_Type (Node => Discriminant_Node) = SP_Symbols.known_discriminant_part then
         -- ASSUME Discriminant_Node = known_discriminant_part
         Wf_Known_Discriminant_Part (Node               => Discriminant_Node,
                                     Protected_Type_Sym => Protected_Type_Sym,
                                     Scope              => Scope);
      elsif Discriminant_Node /= STree.NullNode then
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Discriminant_Node = known_discriminant_part OR NULL in Wf_Protected_Type_Declaration");
      end if;

      -- wff protected ops
      Protected_Scope := Dictionary.Set_Visibility (The_Visibility => Dictionary.Visible,
                                                    The_Unit       => Protected_Type_Sym);
      Wf_Protected_Op_Dec
        (Node           => Get_Protected_Operations_Node (Protected_Type_Declaration_Node => Node),
         Scope          => Protected_Scope,
         Component_Data => Component_Data,
         The_Heap       => The_Heap,
         Op_Found       => At_Least_One_Operation_Declared);

      -- wff protected elements
      Protected_Element_Decl_Node := Get_Protected_Element_Declaration_Node (Protected_Type_Declaration_Node => Node);
      -- ASSUME Protected_Element_Decl_Node = protected_element_declaration OR NULL
      if Syntax_Node_Type (Node => Protected_Element_Decl_Node) = SP_Symbols.protected_element_declaration then
         -- ASSUME Protected_Element_Decl_Node = protected_element_declaration
         if Get_Mode (For_Type => Ident_Str,
                      Scope    => Scope) /= Dictionary.DefaultMode then
            ErrorHandler.Semantic_Error_Sym
              (Err_Num   => 928,
               Reference => ErrorHandler.No_Reference,
               Position  => Node_Position (Node => Protected_Element_Decl_Node),
               Sym       => Protected_Type_Sym,
               Scope     => Scope);
         else
            -- Element declarations are not hidden so we need to wf them
            -- Grammar ensures there is at least one declaration
            Protected_Private_Scope :=
              Dictionary.Set_Visibility (The_Visibility => Dictionary.Privat,
                                         The_Unit       => Protected_Type_Sym);

            ---------------------------------------------------------------------
            -- We check protected elements in 2 passes.
            --
            -- Pass 1 WFFs each variable_declaration or justification_statement
            --
            -- Pass 2 checks each variable for initialization, raising errors
            --        at the "end PT;" node - this allows for initialization
            --        errors to be justified
            ---------------------------------------------------------------------

            -- Pass 1
            It :=
              Find_First_Node
              (Node_Kind    => SP_Symbols.protected_element,
               From_Root    => Protected_Element_Decl_Node,
               In_Direction => STree.Down);

            while not STree.IsNull (It) loop
               --# assert STree.Table = STree.Table~;
               Protected_Element_Node := Child_Node (Current_Node => Get_Node (It => It));
               -- ASSUME Protected_Element_Node = variable_declaration OR justification_statement
               case Syntax_Node_Type (Node => Protected_Element_Node) is
                  when SP_Symbols.variable_declaration =>
                     -- ASSUME Protected_Element_Node = variable_declaration
                     -- Here we need to distinguish between the scope of the
                     -- enclosing unit and the scope of the declaration, which
                     -- may be different.
                     Wf_Variable_Declaration
                       (Node                 => Protected_Element_Node,
                        Enclosing_Unit_Scope => Scope,
                        Declaration_Scope    => Protected_Private_Scope,
                        The_Heap             => The_Heap);
                  when SP_Symbols.justification_statement =>
                     -- ASSUME Protected_Element_Node = justification_statement
                     Wf_Justification_Statement
                       (Node           => Protected_Element_Node,
                        Scope          => Protected_Private_Scope,
                        Component_Data => Component_Data,
                        The_Heap       => The_Heap);
                  when others =>
                     SystemErrors.Fatal_Error
                       (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                        Msg     => "Expect Protected_Element_Node = variable_declaration OR " &
                          "justification_statement in Wf_Protected_Type_Declaration");
               end case;
               It := STree.NextNode (It);
            end loop;
            -- end of Pass 1

            -- Pass 2
            It :=
              Find_First_Node
              (Node_Kind    => SP_Symbols.protected_element,
               From_Root    => Protected_Element_Decl_Node,
               In_Direction => STree.Down);

            while not STree.IsNull (It) loop
               --# assert STree.Table = STree.Table~;
               Protected_Element_Node := Child_Node (Current_Node => Get_Node (It => It));
               case Syntax_Node_Type (Node => Protected_Element_Node) is
                  when SP_Symbols.variable_declaration =>
                     -- ASSUME Protected_Element_Node = variable_declaration
                     Check_Element_Initialization
                       (Variable_Declaration_Node => Protected_Element_Node,
                        Error_Node_Pos            => Node_Position (Node => Closing_Ident_Node),
                        Current_Scope             => Protected_Private_Scope);
                  when SP_Symbols.justification_statement =>
                     -- ASSUME Protected_Element_Node = justification_statement
                     null;
                  when others =>
                     SystemErrors.Fatal_Error
                       (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                        Msg     => "Expect Protected_Element_Node = variable_declaration OR " &
                          "justification_statement in Wf_Protected_Type_Declaration");
               end case;
               It := STree.NextNode (It);
            end loop;
            -- end of Pass 2

         end if;
      elsif Protected_Element_Decl_Node /= STree.NullNode then
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Protected_Element_Decl_Node = protected_element_declaration OR " &
              "NULL in Wf_Protected_Type_Declaration");
      end if;

      -- Add any virtual elements. The virtual elements are the items in the protects list
      -- for this type. They behave as if they were elements of the protected type and
      -- hence must be made constituents of the implicit own variable associated with the
      -- protected type.
      Add_Virtual_Elements (Type_Sym => Protected_Type_Sym);

      -- This call will creates any interrupt stream variables specified by local own variables
      -- of Protected_Type_Sym.
      Add_Implicit_Interrupt_Stream_Variables
        (Protected_Type_Sym => Protected_Type_Sym,
         Scope              => Scope,
         Error_Node_Pos     => Node_Position (Node => Node));

      -- closing identifier must match initial
      if Syntax_Node_Type (Node => Closing_Ident_Node) = SP_Symbols.identifier then
         -- private part is not hidden so we need to check that the closing identifier is correct
         if LexTokenManager.Lex_String_Case_Insensitive_Compare
           (Lex_Str1 => Ident_Str,
            Lex_Str2 => Node_Lex_String (Node => Closing_Ident_Node)) /=
           LexTokenManager.Str_Eq then
            ErrorHandler.Semantic_Error
              (Err_Num   => 58,
               Reference => ErrorHandler.No_Reference,
               Position  => Node_Position (Node => Closing_Ident_Node),
               Id_Str    => Ident_Str);
         end if;
      else -- must be hidden
         Dictionary.SetProtectedTypeElementsHidden (Protected_Type_Sym);
         ErrorHandler.Hidden_Text
           (Position => Node_Position (Node => Closing_Ident_Node),
            Unit_Str => Ident_Str,
            Unit_Typ => SP_Symbols.protected_type_declaration);
      end if;

      -- protected type must declare at leat one operation
      if not At_Least_One_Operation_Declared then
         ErrorHandler.Semantic_Error
           (Err_Num   => 870,
            Reference => ErrorHandler.No_Reference,
            Position  => Node_Position (Node => Closing_Ident_Node),
            Id_Str    => Ident_Str);
      end if;

      -- there must be a valid combination of pragmas declared in PT
      Check_Pragma_Validity
        (End_Node_Position  => Node_Position (Node => Closing_Ident_Node),
         Protected_Type_Sym => Protected_Type_Sym,
         Scope              => Scope);

   else -- illegal redeclaration
      ErrorHandler.Semantic_Error
        (Err_Num   => 10,
         Reference => ErrorHandler.No_Reference,
         Position  => Node_Position (Node => Ident_Node),
         Id_Str    => Ident_Str);
   end if;
end Wf_Protected_Type_Declaration;
