-------------------------------------------------------------------------------
-- (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 (Dictionary)
procedure Instantiate_Subprogram_Parameters
  (Actual_Subprogram : in RawDict.Subprogram_Info_Ref;
   Comp_Unit         : in ContextManager.UnitDescriptors;
   Declaration       : in Location) is

   function Substitute_Type
     (Possibly_Generic_Type : RawDict.Type_Info_Ref;
      The_Subprogram        : RawDict.Subprogram_Info_Ref)
     return                  RawDict.Type_Info_Ref
   --# global in Dict;
   is
      Result : RawDict.Type_Info_Ref;
   begin
      if RawDict.Get_Type_Discriminant (Type_Mark => Possibly_Generic_Type) = Generic_Type_Item then
         Result :=
           Actual_Of_Generic_Formal_Type (The_Generic_Formal_Type => Possibly_Generic_Type,
                                          Actual_Subprogram       => The_Subprogram,
                                          Not_Found_Expected      => True);
      else
         Result := Possibly_Generic_Type;
      end if;
      return Result;
   end Substitute_Type;

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

   procedure Substitute_Parameters (Actual_Subprogram : in RawDict.Subprogram_Info_Ref)
   --# global in     LexTokenManager.State;
   --#        in out Dict;
   --#        in out SPARK_IO.File_Sys;
   --# derives Dict              from *,
   --#                                Actual_Subprogram &
   --#         SPARK_IO.File_Sys from *,
   --#                                Actual_Subprogram,
   --#                                Dict,
   --#                                LexTokenManager.State;
   is
      Generic_Parameter             : RawDict.Subprogram_Parameter_Info_Ref;
      Type_Reference, Specification : LexTokenManager.Token_Position;
   begin
      Generic_Parameter :=
        RawDict.Get_Subprogram_First_Parameter
        (The_Subprogram => RawDict.Get_Subprogram_Instantiation_Of (The_Subprogram => Actual_Subprogram));
      while Generic_Parameter /= RawDict.Null_Subprogram_Parameter_Info_Ref loop
         Type_Reference :=
           RawDict.Get_Symbol_Location
           (RawDict.Get_Type_Symbol (RawDict.Get_Subprogram_Parameter_Type (The_Subprogram_Parameter => Generic_Parameter)));
         Specification  := RawDict.Get_Symbol_Location (RawDict.Get_Subprogram_Parameter_Symbol (Generic_Parameter));
         -- create new parameter for instantiation
         Add_Subprogram_Parameter
           (Name           => RawDict.Get_Subprogram_Parameter_Name (The_Subprogram_Parameter => Generic_Parameter),
            The_Subprogram => Actual_Subprogram,
            Type_Mark      => Substitute_Type
              (Possibly_Generic_Type => RawDict.Get_Subprogram_Parameter_Type (The_Subprogram_Parameter => Generic_Parameter),
               The_Subprogram        => Actual_Subprogram),
            Type_Reference => Location'(Start_Position => Type_Reference,
                                        End_Position   => Type_Reference),
            Mode           => RawDict.Get_Subprogram_Parameter_Mode (The_Subprogram_Parameter => Generic_Parameter),
            Comp_Unit      => RawDict.Get_Symbol_Compilation_Unit (RawDict.Get_Subprogram_Parameter_Symbol (Generic_Parameter)),
            Specification  => Location'(Start_Position => Specification,
                                        End_Position   => Specification));
         -- move on to next parameter
         Generic_Parameter := RawDict.Get_Next_Subprogram_Parameter (The_Subprogram_Parameter => Generic_Parameter);
      end loop;
   end Substitute_Parameters;

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

   procedure Substitute_Globals (Actual_Subprogram : in RawDict.Subprogram_Info_Ref;
                                 Abstraction       : in Abstractions)
   --# global in     LexTokenManager.State;
   --#        in out Dict;
   --#        in out SPARK_IO.File_Sys;
   --# derives Dict              from *,
   --#                                Abstraction,
   --#                                Actual_Subprogram &
   --#         SPARK_IO.File_Sys from *,
   --#                                Abstraction,
   --#                                Actual_Subprogram,
   --#                                Dict,
   --#                                LexTokenManager.State;
   is
      Generic_Global_Variable : RawDict.Global_Variable_Info_Ref;
      Actual_Global_Variable  : RawDict.Global_Variable_Info_Ref;
      Specification           : LexTokenManager.Token_Position;
   begin
      Generic_Global_Variable :=
        RawDict.Get_Subprogram_First_Global_Variable
        (The_Subprogram => RawDict.Get_Subprogram_Instantiation_Of (The_Subprogram => Actual_Subprogram),
         Abstraction    => Abstraction);
      while Generic_Global_Variable /= RawDict.Null_Global_Variable_Info_Ref loop
         Specification := RawDict.Get_Symbol_Location (RawDict.Get_Global_Variable_Symbol (Generic_Global_Variable));
         --# accept Flow, 10, Actual_Global_Variable, "Expected ineffective assignment to OK";
         Add_Subprogram_Global_Variable
           (Abstraction         => Abstraction,
            The_Subprogram      => Actual_Subprogram,
            The_Variable        => RawDict.Get_Global_Variable_Variable (The_Global_Variable => Generic_Global_Variable),
            Mode                => RawDict.Get_Global_Variable_Mode (The_Global_Variable => Generic_Global_Variable),
            Prefix_Needed       => RawDict.Get_Global_Variable_Prefix_Needed (The_Global_Variable => Generic_Global_Variable),
            Comp_Unit           => RawDict.Get_Symbol_Compilation_Unit (RawDict.Get_Global_Variable_Symbol (Generic_Global_Variable)),
            Variable_Reference  => Location'(Start_Position => Specification,
                                             End_Position   => Specification),
            The_Global_Variable => Actual_Global_Variable);
         --# end accept;
         Generic_Global_Variable := RawDict.Get_Next_Global_Variable (The_Global_Variable => Generic_Global_Variable);
      end loop;
      --# accept Flow, 33, Actual_Global_Variable, "Expected to be neither referenced nor exported";
   end Substitute_Globals;

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

   procedure Substitute_Derives
     (Actual_Subprogram : in RawDict.Subprogram_Info_Ref;
      Comp_Unit         : in ContextManager.UnitDescriptors)
   --# global in     LexTokenManager.State;
   --#        in out Dict;
   --#        in out SPARK_IO.File_Sys;
   --# derives Dict              from *,
   --#                                Actual_Subprogram,
   --#                                Comp_Unit &
   --#         SPARK_IO.File_Sys from *,
   --#                                Actual_Subprogram,
   --#                                Comp_Unit,
   --#                                Dict,
   --#                                LexTokenManager.State;
   is
      Export_It              : Iterator;
      Dependency_It          : Iterator;
      The_Generic_Export_Sym : Symbol;
      The_Actual_Export_Sym  : Symbol;
      The_Import_Sym         : Symbol;
      Generic_Subprogram     : RawDict.Subprogram_Info_Ref;
   begin
      Generic_Subprogram := RawDict.Get_Subprogram_Instantiation_Of (The_Subprogram => Actual_Subprogram);
      Export_It          := First_Subprogram_Export (The_Subprogram => Generic_Subprogram,
                                                     Abstraction    => IsAbstract);
      while not IsNullIterator (Export_It) loop
         The_Generic_Export_Sym := CurrentSymbol (Export_It);
         case RawDict.GetSymbolDiscriminant (The_Generic_Export_Sym) is
            when Variable_Symbol =>
               Add_Subprogram_Export_Variable
                 (The_Subprogram   => Actual_Subprogram,
                  Abstraction      => IsAbstract,
                  The_Export       => RawDict.Get_Variable_Info_Ref (Item => The_Generic_Export_Sym),
                  Export_Reference => Null_Location,
                  Annotation       => Null_Location);
               The_Actual_Export_Sym := The_Generic_Export_Sym;
            when Subprogram_Parameter_Symbol =>
               Add_Subprogram_Export_Parameter
                 (The_Subprogram   => Actual_Subprogram,
                  Abstraction      => IsAbstract,
                  The_Export       => Actual_Of_Generic_Parameter
                    (The_Subprogram_Parameter => RawDict.Get_Subprogram_Parameter_Info_Ref (Item => The_Generic_Export_Sym),
                     Actual_Subprogram        => Actual_Subprogram),
                  Export_Reference => Null_Location,
                  Annotation       => Null_Location);
               The_Actual_Export_Sym :=
                 RawDict.Get_Subprogram_Parameter_Symbol
                 (Actual_Of_Generic_Parameter
                    (The_Subprogram_Parameter => RawDict.Get_Subprogram_Parameter_Info_Ref (Item => The_Generic_Export_Sym),
                     Actual_Subprogram        => Actual_Subprogram));
            when others =>
               The_Actual_Export_Sym := NullSymbol;
               SystemErrors.Fatal_Error
                 (Sys_Err => SystemErrors.Invalid_Symbol_Table,
                  Msg     => "in Dictionary.Instantiate_Subprogram_Parameters.Substitute_Derives");
         end case;
         Dependency_It :=
           First_Subprogram_Dependency
           (The_Subprogram => Generic_Subprogram,
            Abstraction    => IsAbstract,
            The_Export     => The_Generic_Export_Sym);
         while not IsNullIterator (Dependency_It) loop
            The_Import_Sym := CurrentSymbol (Dependency_It);
            case RawDict.GetSymbolDiscriminant (The_Import_Sym) is
               when Variable_Symbol =>
                  null;
               when Subprogram_Parameter_Symbol =>
                  The_Import_Sym :=
                    RawDict.Get_Subprogram_Parameter_Symbol
                    (Actual_Of_Generic_Parameter
                       (The_Subprogram_Parameter => RawDict.Get_Subprogram_Parameter_Info_Ref (The_Import_Sym),
                        Actual_Subprogram        => Actual_Subprogram));
               when others =>
                  SystemErrors.Fatal_Error
                    (Sys_Err => SystemErrors.Invalid_Symbol_Table,
                     Msg     => "in Dictionary.Instantiate_Subprogram_Parameters.Substitute_Derives");
            end case;
            Add_Subprogram_Dependency
              (Abstraction      => IsAbstract,
               Comp_Unit        => Comp_Unit,
               The_Subprogram   => Actual_Subprogram,
               The_Export       => The_Actual_Export_Sym,
               The_Import       => The_Import_Sym,
               Import_Reference => Null_Location);
            Dependency_It := NextSymbol (Dependency_It);
         end loop;
         Export_It := NextSymbol (Export_It);
      end loop;
   end Substitute_Derives;

begin -- Instantiate_Subprogram_Parameters
   if RawDict.Get_Subprogram_Return_Type
     (The_Subprogram => RawDict.Get_Subprogram_Instantiation_Of (The_Subprogram => Actual_Subprogram)) /=
     RawDict.Null_Type_Info_Ref then
      Add_Return_Type
        (The_Function   => Actual_Subprogram,
         Type_Mark      => Substitute_Type
           (Possibly_Generic_Type => RawDict.Get_Subprogram_Return_Type
              (The_Subprogram => RawDict.Get_Subprogram_Instantiation_Of (The_Subprogram => Actual_Subprogram)),
            The_Subprogram        => Actual_Subprogram),
         Comp_Unit      => Comp_Unit,
         Type_Reference => Declaration);
   end if;
   Substitute_Parameters (Actual_Subprogram => Actual_Subprogram);

   Substitute_Globals (Actual_Subprogram => Actual_Subprogram,
                       Abstraction       => IsAbstract);

   Substitute_Derives (Actual_Subprogram => Actual_Subprogram,
                       Comp_Unit         => Comp_Unit);
end Instantiate_Subprogram_Parameters;
