------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                             E X P _ P R A G                              --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--          Copyright (C) 1992-2024, Free Software Foundation, Inc.         --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT 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 GNAT; see file COPYING3.  If not, go to --
-- http://www.gnu.org/licenses for a complete copy of the license.          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

with Atree;          use Atree;
with Casing;         use Casing;
with Checks;         use Checks;
with Debug;          use Debug;
with Einfo;          use Einfo;
with Einfo.Entities; use Einfo.Entities;
with Einfo.Utils;    use Einfo.Utils;
with Elists;         use Elists;
with Errout;         use Errout;
with Exp_Ch11;       use Exp_Ch11;
with Exp_Util;       use Exp_Util;
with Inline;         use Inline;
with Lib;            use Lib;
with Namet;          use Namet;
with Nlists;         use Nlists;
with Nmake;          use Nmake;
with Opt;            use Opt;
with Restrict;       use Restrict;
with Rident;         use Rident;
with Rtsfind;        use Rtsfind;
with Sem;            use Sem;
with Sem_Aux;        use Sem_Aux;
with Sem_Ch8;        use Sem_Ch8;
with Sem_Prag;       use Sem_Prag;
with Sem_Util;       use Sem_Util;
with Sinfo;          use Sinfo;
with Sinfo.Nodes;    use Sinfo.Nodes;
with Sinfo.Utils;    use Sinfo.Utils;
with Sinput;         use Sinput;
with Snames;         use Snames;
with Stringt;        use Stringt;
with Stand;          use Stand;
with Tbuild;         use Tbuild;
with Uintp;          use Uintp;
with Validsw;        use Validsw;
with Warnsw;         use Warnsw;

package body Exp_Prag is

   -----------------------
   -- Local Subprograms --
   -----------------------

   function Arg_N (N : Node_Id; Arg_Number : Positive) return Node_Id;
   --  Obtain specified pragma argument expression

   procedure Expand_Pragma_Abort_Defer             (N : Node_Id);
   procedure Expand_Pragma_Check                   (N : Node_Id);
   procedure Expand_Pragma_Common_Object           (N : Node_Id);
   procedure Expand_Pragma_CUDA_Execute            (N : Node_Id);
   procedure Expand_Pragma_Import_Or_Interface     (N : Node_Id);
   procedure Expand_Pragma_Inspection_Point        (N : Node_Id);
   procedure Expand_Pragma_Interrupt_Priority      (N : Node_Id);
   procedure Expand_Pragma_Loop_Variant            (N : Node_Id);
   procedure Expand_Pragma_Psect_Object            (N : Node_Id);
   procedure Expand_Pragma_Relative_Deadline       (N : Node_Id);
   procedure Expand_Pragma_Suppress_Initialization (N : Node_Id);

   procedure Undo_Initialization (Def_Id : Entity_Id; N : Node_Id);
   --  This procedure is used to undo initialization already done for Def_Id,
   --  which is always an E_Variable, in response to the occurrence of the
   --  pragma N, a pragma Interface, Import, or Suppress_Initialization. In all
   --  these cases we want no initialization to occur, but we have already done
   --  the initialization by the time we see the pragma, so we have to undo it.

   -----------
   -- Arg_N --
   -----------

   function Arg_N (N : Node_Id; Arg_Number : Positive) return Node_Id is
      Arg : Node_Id := First (Pragma_Argument_Associations (N));
   begin
      if No (Arg) then
         return Empty;
      end if;

      for J in 2 .. Arg_Number loop
         Next (Arg);
         if No (Arg) then
            return Empty;
         end if;
      end loop;

      if Present (Arg) then
         return Get_Pragma_Arg (Arg);
      else
         return Empty;
      end if;
   end Arg_N;

   ---------------------
   -- Expand_N_Pragma --
   ---------------------

   procedure Expand_N_Pragma (N : Node_Id) is
      Pname   : constant Name_Id   := Pragma_Name (N);
      Prag_Id : constant Pragma_Id := Get_Pragma_Id (Pname);

   begin
      --  Suppress the expansion of an ignored assertion pragma. Such a pragma
      --  should not be transformed into a null statement because:
      --
      --    * The pragma may be part of the rep item chain of a type, in which
      --      case rewriting it will destroy the chain.
      --
      --    * The analysis of the pragma may involve two parts (see routines
      --      Analyze_xxx_In_Decl_Part). The second part of the analysis will
      --      not happen if the pragma is rewritten.

      if Assertion_Expression_Pragma (Prag_Id) and then Is_Ignored (N) then
         return;

      --  Rewrite the pragma into a null statement when it is ignored using
      --  pragma Ignore_Pragma, or denotes Default_Scalar_Storage_Order and
      --  compilation switch -gnatI is in effect.

      elsif Should_Ignore_Pragma_Sem (N)
        or else (Prag_Id = Pragma_Default_Scalar_Storage_Order
                  and then Ignore_Rep_Clauses)
      then
         Rewrite (N, Make_Null_Statement (Sloc (N)));
         return;
      end if;

      case Prag_Id is

         --  Pragmas requiring special expander action

         when Pragma_Abort_Defer =>
            Expand_Pragma_Abort_Defer (N);

         when Pragma_Check =>
            Expand_Pragma_Check (N);

         when Pragma_Common_Object =>
            Expand_Pragma_Common_Object (N);

         when Pragma_CUDA_Execute =>
            Expand_Pragma_CUDA_Execute (N);

         when Pragma_Import =>
            Expand_Pragma_Import_Or_Interface (N);

         when Pragma_Inspection_Point =>
            Expand_Pragma_Inspection_Point (N);

         when Pragma_Interface =>
            Expand_Pragma_Import_Or_Interface (N);

         when Pragma_Interrupt_Priority =>
            Expand_Pragma_Interrupt_Priority (N);

         when Pragma_Loop_Variant =>
            Expand_Pragma_Loop_Variant (N);

         when Pragma_Psect_Object =>
            Expand_Pragma_Psect_Object (N);

         when Pragma_Relative_Deadline =>
            Expand_Pragma_Relative_Deadline (N);

         when Pragma_Suppress_Initialization =>
            Expand_Pragma_Suppress_Initialization (N);

         --  All other pragmas need no expander action (includes
         --  Unknown_Pragma).

         when others => null;
      end case;
   end Expand_N_Pragma;

   -------------------------------
   -- Expand_Pragma_Abort_Defer --
   -------------------------------

   --  An Abort_Defer pragma appears as the first statement in a handled
   --  statement sequence (right after the begin). It defers aborts for
   --  the entire statement sequence, but not for any declarations or
   --  handlers (if any) associated with this statement sequence.

   --  The transformation is to transform

   --    pragma Abort_Defer;
   --    statements;

   --  into

   --    begin
   --       Abort_Defer.all;
   --       statements
   --    exception
   --       when all others =>
   --          Abort_Undefer.all;
   --          raise;
   --    at end
   --       Abort_Undefer_Direct;
   --    end;

   procedure Expand_Pragma_Abort_Defer (N : Node_Id) is
   begin
      --  Abort_Defer has no useful effect if Abort's are not allowed

      if not Abort_Allowed then
         return;
      end if;

      --  Normal case where abort is possible

      declare
         Loc  : constant Source_Ptr := Sloc (N);
         Stm  : Node_Id;
         Stms : List_Id;
         HSS  : Node_Id;
         Blk  : constant Entity_Id :=
                  New_Internal_Entity (E_Block, Current_Scope, Sloc (N), 'B');
         AUD  : constant Entity_Id := RTE (RE_Abort_Undefer_Direct);

      begin
         Stms := New_List (Build_Runtime_Call (Loc, RE_Abort_Defer));
         loop
            Stm := Remove_Next (N);
            exit when No (Stm);
            Append (Stm, Stms);
         end loop;

         HSS :=
           Make_Handled_Sequence_Of_Statements (Loc,
             Statements  => Stms,
             At_End_Proc => New_Occurrence_Of (AUD, Loc));

         --  Present the Abort_Undefer_Direct function to the backend so that
         --  it can inline the call to the function.

         Add_Inlined_Body (AUD, N);

         Rewrite (N,
           Make_Block_Statement (Loc, Handled_Statement_Sequence => HSS));

         Set_Scope (Blk, Current_Scope);
         Set_Etype (Blk, Standard_Void_Type);
         Set_Identifier (N, New_Occurrence_Of (Blk, Sloc (N)));
         Expand_At_End_Handler (HSS, Blk);
         Analyze (N);
      end;
   end Expand_Pragma_Abort_Defer;

   -------------------------------------
   -- Expand_Pragma_Always_Terminates --
   -------------------------------------

   procedure Expand_Pragma_Always_Terminates (Prag : Node_Id) is
      pragma Unreferenced (Prag);
   begin
      null;
   end Expand_Pragma_Always_Terminates;

   --------------------------
   -- Expand_Pragma_Check --
   --------------------------

   procedure Expand_Pragma_Check (N : Node_Id) is
      procedure Replace_Discriminals_Of_Protected_Op (Expr : Node_Id);
      --  Discriminants of the enclosing protected object may be referenced
      --  in the expression of a precondition of a protected operation.
      --  In the body of the operation these references must be replaced by
      --  the discriminal created for them, which are renamings of the
      --  discriminants of the object that is the target of the operation.
      --  This replacement is done by visibility when the references appear
      --  in the subprogram body, but in the case of a condition which appears
      --  on the specification of the subprogram it has be done separately
      --  because the condition has been replaced by a Check pragma and
      --  analyzed earlier, before the creation of the discriminal renaming
      --  declarations that are added to the subprogram body.

      function Make_Failure_Message
        (Nam  : Name_Id;
         Cond : Node_Id)
         return Node_Id;
      --  Build node with a string literal of a message for check Nam with
      --  condition Cond failing at runtime.

      --------------------------
      -- Make_Failure_Message --
      --------------------------

      function Make_Failure_Message
        (Nam  : Name_Id;
         Cond : Node_Id)
         return Node_Id
      is
         Loc     : constant Source_Ptr := Sloc (First_Node (Cond));
         Loc_Str : constant String := Build_Location_String (Loc);

      begin
         Name_Len := 0;

         --  For Assert, we just use the location

         if Nam = Name_Assert then
            null;

         --  For predicate, we generate the string "predicate failed at yyy".
         --  We prefer all lower case for predicate.

         elsif Nam = Name_Predicate then
            Add_Str_To_Name_Buffer ("predicate failed at ");

         --  For special case of Precondition/Postcondition the string is
         --  "failed xx from yy" where xx is precondition/postcondition in all
         --  lower case. The reason for this different wording is that the
         --  failure is not at the point of occurrence of the pragma, unlike
         --  the other Check cases.

         elsif Nam in Name_Pre
                    | Name_Precondition
                    | Name_Post
                    | Name_Postcondition
         then
            Add_Str_To_Name_Buffer ("failed ");

            --  Enhance information for inherited pragmas

            if Comes_From_Inherited_Pragma (Loc) then
               Add_Str_To_Name_Buffer ("inherited ");
            end if;

            if Nam = Name_Pre then
               Get_Name_String_And_Append (Name_Precondition);
            elsif Nam = Name_Post then
               Get_Name_String_And_Append (Name_Postcondition);
            else
               Get_Name_String_And_Append (Nam);
            end if;

            Add_Str_To_Name_Buffer (" from ");

         --  For special case of Invariant, the string is "failed invariant
         --  from yy", to be consistent with the string that is generated for
         --  the aspect case (the code later on checks for this specific string
         --  to modify it in some cases, so this is functionally important).

         elsif Nam = Name_Invariant then
            Add_Str_To_Name_Buffer ("failed invariant from ");

         --  For all other checks, the string is "xxx failed at yyy"
         --  where xxx is the check name with appropriate casing.

         else
            Get_Name_String (Nam);
            Set_Casing (Identifier_Casing (Source_Index (Current_Sem_Unit)));
            Add_Str_To_Name_Buffer (" failed at ");
         end if;

         --  In all cases, add location string

         Add_Str_To_Name_Buffer (Loc_Str);

         --  Build the message

         return Make_String_Literal (Loc, Name_Buffer (1 .. Name_Len));
      end Make_Failure_Message;

      ------------------------------------------
      -- Replace_Discriminals_Of_Protected_Op --
      ------------------------------------------

      procedure Replace_Discriminals_Of_Protected_Op (Expr : Node_Id) is
         function Find_Corresponding_Discriminal
           (E : Entity_Id) return Entity_Id;
         --  Find the local entity that renames a discriminant of the enclosing
         --  protected type, and has a matching name.

         function Replace_Discr_Ref (N : Node_Id) return Traverse_Result;
         --  Replace a reference to a discriminant of the original protected
         --  type by the local renaming declaration of the discriminant of
         --  the target object.

         ------------------------------------
         -- Find_Corresponding_Discriminal --
         ------------------------------------

         function Find_Corresponding_Discriminal
           (E : Entity_Id) return Entity_Id
         is
            R : Entity_Id;

         begin
            R := First_Entity (Current_Scope);

            while Present (R) loop
               if Nkind (Parent (R)) = N_Object_Renaming_Declaration
                 and then Present (Discriminal_Link (R))
                 and then Chars (Discriminal_Link (R)) = Chars (E)
               then
                  return R;
               end if;

               Next_Entity (R);
            end loop;

            return Empty;
         end Find_Corresponding_Discriminal;

         -----------------------
         -- Replace_Discr_Ref --
         -----------------------

         function Replace_Discr_Ref (N : Node_Id) return Traverse_Result is
            R : Entity_Id;

         begin
            if Is_Entity_Name (N)
              and then Present (Discriminal_Link (Entity (N)))
            then
               R := Find_Corresponding_Discriminal (Entity (N));
               Rewrite (N, New_Occurrence_Of (R, Sloc (N)));
            end if;

            return OK;
         end Replace_Discr_Ref;

         procedure Replace_Discriminant_References is
           new Traverse_Proc (Replace_Discr_Ref);

      --  Start of processing for Replace_Discriminals_Of_Protected_Op

      begin
         Replace_Discriminant_References (Expr);
      end Replace_Discriminals_Of_Protected_Op;

      --  Local variables

      Cond : constant Node_Id := Arg_N (N, 2);
      Nam  : constant Name_Id := Chars (Arg_N (N, 1));
      Msg  : Node_Id;

      Loc : constant Source_Ptr := Sloc (First_Node (Cond));
      --  Source location used in the case of a failed assertion: point to the
      --  failing condition, not Loc. Note that the source location of the
      --  expression is not usually the best choice here, because it points to
      --  the location of the topmost tree node, which may be an operator in
      --  the middle of the source text of the expression. For example, it gets
      --  located on the last AND keyword in a chain of boolean expressions
      --  AND'ed together. It is best to put the message on the first character
      --  of the condition, which is the effect of the First_Node call here.
      --  This source location is used to build the default exception message,
      --  and also as the sloc of the call to the runtime subprogram raising
      --  Assert_Failure, so that coverage analysis tools can relate the
      --  call to the failed check.

   --  Start of processing for Expand_Pragma_Check

   begin
      --  Nothing to do if pragma is ignored

      if Is_Ignored (N) then
         return;
      end if;

      --  Since this check is active, rewrite the pragma into a corresponding
      --  if statement, and then analyze the statement.

      --  The normal case expansion transforms:

      --    pragma Check (name, condition [,message]);

      --  into

      --    if not condition then
      --       System.Assertions.Raise_Assert_Failure (Str);
      --    end if;

      --  where Str is the message if one is present, or the default of
      --  name failed at file:line if no message is given (the "name failed
      --  at" is omitted for name = Assertion, since it is redundant, given
      --  that the name of the exception is Assert_Failure.)

      --  Also, instead of "XXX failed at", we generate slightly
      --  different messages for some of the contract assertions (see
      --  code below for details).

      --  An alternative expansion is used when the No_Exception_Propagation
      --  restriction is active and there is a local Assert_Failure handler.
      --  This is not a common combination of circumstances, but it occurs in
      --  the context of Aunit and the zero footprint profile. In this case we
      --  generate:

      --    if not condition then
      --       raise Assert_Failure;
      --    end if;

      --  This will then be transformed into a goto, and the local handler will
      --  be able to handle the assert error (which would not be the case if a
      --  call is made to the Raise_Assert_Failure procedure).

      --  We also generate the direct raise if the Suppress_Exception_Locations
      --  is active, since we don't want to generate messages in this case.

      --  Note that the reason we do not always generate a direct raise is that
      --  the form in which the procedure is called allows for more efficient
      --  breakpointing of assertion errors.

      --  Generate the appropriate if statement. Note that we consider this to
      --  be an explicit conditional in the source, not an implicit if, so we
      --  do not call Make_Implicit_If_Statement. Note also that we wrap the
      --  raise statement in a block statement so that, if the condition is
      --  evaluated at compile time to False, then the rewriting of the if
      --  statement will not involve the raise but the block statement, and
      --  thus not leave a dangling reference to the raise statement in the
      --  Local_Raise_Statements list of the handler.

      --  Case where we generate a direct raise

      if ((Debug_Flag_Dot_G
            or else Restriction_Active (No_Exception_Propagation))
           and then Present (Find_Local_Handler (RTE (RE_Assert_Failure), N)))
        or else (Opt.Exception_Locations_Suppressed and then No (Arg_N (N, 3)))
      then
         Rewrite (N,
           Make_If_Statement (Loc,
             Condition       => Make_Op_Not (Loc, Right_Opnd => Cond),
             Then_Statements => New_List (
               Make_Block_Statement (Loc,
                 Handled_Statement_Sequence =>
                   Make_Handled_Sequence_Of_Statements (Loc,
                     Statements => New_List (
                       Make_Raise_Statement (Loc,
                         Name =>
                           New_Occurrence_Of (RTE (RE_Assert_Failure),
                                                                   Loc))))))));

         Set_Comes_From_Check_Or_Contract (N);

      --  Case where we call the procedure

      else
         --  For a precondition, replace references to discriminants of a
         --  protected type with the local discriminals.

         if Is_Protected_Type (Scope (Current_Scope))
           and then Has_Discriminants (Scope (Current_Scope))
           and then From_Aspect_Specification (N)
         then
            Replace_Discriminals_Of_Protected_Op (Cond);
         end if;

         --  Now rewrite as an if statement

         declare
            function Make_Elsif_Check (Conj : Node_Id) return Node_Id;
            --  Create an elsif part that checks a conjunct expression Conj and
            --  emits a message with the exact location when the check fails.

            ----------------------
            -- Make_Elsif_Check --
            ----------------------

            function Make_Elsif_Check (Conj : Node_Id) return Node_Id is
            begin
               return
                 Make_Elsif_Part (Loc,
                   Condition       =>
                     Make_Op_Not (Loc,
                       Relocate_Node (Conj)),
                   Then_Statements =>
                     New_List (
                       Make_Procedure_Call_Statement (Loc,
                         Name                   =>
                           New_Occurrence_Of
                             (RTE (RE_Raise_Assert_Failure), Loc),
                       Parameter_Associations =>
                         New_List (
                           Make_Failure_Message (Nam, Conj)))));
            end Make_Elsif_Check;

            Conjunct : Node_Id := Cond;
            Elsifs   : List_Id := No_List;

         begin
            --  If we have a message given, use it

            if Present (Arg_N (N, 3)) then
               Msg := Get_Pragma_Arg (Arg_N (N, 3));

            --  If check is for a Pre/Post expression of the form "A and then
            --  B", then we split condition into separate conjuncts with
            --  messages pointing to their exact locations, i.e.:
            --
            --    if not A then
            --       Raise_Assert_Failure ("failed pre/post from [sloc of A]");
            --    elsif not B then then
            --       Raise_Assert_Failure ("failed pre/post from [sloc of B]");
            --    end if;
            --
            --  This makes it easier to debug a failed complex contract.

            else
               if Nam in Name_Pre
                       | Name_Precondition
                       | Name_Post
                       | Name_Postcondition
               then
                  while Nkind (Conjunct) = N_And_Then loop
                     Prepend_New_To (Elsifs,
                       Make_Elsif_Check (Right_Opnd (Conjunct)));
                     Conjunct := Left_Opnd (Conjunct);
                  end loop;
               end if;

               Msg := Make_Failure_Message (Nam, Conjunct);
            end if;

            Rewrite (N,
              Make_If_Statement (Loc,
                Condition       =>
                  Make_Op_Not (Loc, Relocate_Node (Conjunct)),
                Then_Statements =>
                  New_List (
                    Make_Procedure_Call_Statement (Loc,
                      Name                   =>
                        New_Occurrence_Of
                          (RTE (RE_Raise_Assert_Failure), Loc),
                    Parameter_Associations =>
                      New_List (Msg))),
                Elsif_Parts     => Elsifs));

            Set_Comes_From_Check_Or_Contract (N);
         end;
      end if;

      Analyze (N);

      --  If new condition is always false, give a warning

      if Warn_On_Assertion_Failure
        and then Nkind (N) = N_Procedure_Call_Statement
        and then Is_RTE (Entity (Name (N)), RE_Raise_Assert_Failure)
      then
         --  If original condition was a Standard.False, we assume that this is
         --  indeed intended to raise assert error and no warning is required.

         if Is_Entity_Name (Original_Node (Cond))
           and then Entity (Original_Node (Cond)) = Standard_False
         then
            null;

         --  For Subprogram_Variant suppress the warning altogether, because
         --  for mutually recursive subprograms with multiple variant clauses
         --  some of the clauses might have expressions that are only meant for
         --  verification and would always fail when executed.

         elsif Nam = Name_Subprogram_Variant then
            null;
         elsif Nam = Name_Assert then
            Error_Msg_N ("?.a?assertion will fail at run time", N);
         else
            Error_Msg_N ("?.a?check will fail at run time", N);
         end if;
      end if;
   end Expand_Pragma_Check;

   ---------------------------------
   -- Expand_Pragma_Common_Object --
   ---------------------------------

   --  Use a machine attribute to replicate semantic effect in DEC Ada

   --    pragma Machine_Attribute (intern_name, "common_object", extern_name);

   --  For now we do nothing with the size attribute ???

   --  Note: Psect_Object shares this processing

   procedure Expand_Pragma_Common_Object (N : Node_Id) is
      Loc : constant Source_Ptr := Sloc (N);

      Internal : constant Node_Id := Arg_N (N, 1);
      External : constant Node_Id := Arg_N (N, 2);

      Psect : Node_Id;
      --  Psect value upper cased as string literal

      Iloc : constant Source_Ptr := Sloc (Internal);
      Eloc : constant Source_Ptr := Sloc (External);
      Ploc : Source_Ptr;

   begin
      --  Acquire Psect value and fold to upper case

      if Present (External) then
         if Nkind (External) = N_String_Literal then
            String_To_Name_Buffer (Strval (External));
         else
            Get_Name_String (Chars (External));
         end if;

         Set_Casing (All_Upper_Case);

         Psect :=
           Make_String_Literal (Eloc, Strval => String_From_Name_Buffer);

      else
         Get_Name_String (Chars (Internal));
         Set_Casing (All_Upper_Case);
         Psect :=
           Make_String_Literal (Iloc, Strval => String_From_Name_Buffer);
      end if;

      Ploc := Sloc (Psect);

      --  Insert the pragma

      Insert_After_And_Analyze (N,
        Make_Pragma (Loc,
          Chars                        => Name_Machine_Attribute,
          Pragma_Argument_Associations => New_List (
            Make_Pragma_Argument_Association (Iloc,
              Expression => New_Copy_Tree (Internal)),
            Make_Pragma_Argument_Association (Eloc,
              Expression =>
                Make_String_Literal (Sloc => Ploc, Strval => "common_object")),
            Make_Pragma_Argument_Association (Ploc,
              Expression => New_Copy_Tree (Psect)))));
   end Expand_Pragma_Common_Object;

   --------------------------------
   -- Expand_Pragma_CUDA_Execute --
   --------------------------------

   --  Pragma CUDA_Execute is expanded in the following manner:

   --  Original Code

   --    pragma CUDA_Execute (My_Proc (X, Y), Blocks, Grids, Mem, Stream)

   --  Expanded Code

   --    declare
   --      Blocks_Id : CUDA.Vector_Types.Dim3 := Blocks;
   --      Grids_Id  : CUDA.Vector_Types.Dim3 := Grids;
   --      Mem_Id    : Integer := <Mem or 0>;
   --      Stream_Id : CUDA.Driver_Types.Stream_T := <Stream or null>;
   --      X_Id      : <Type of X> := X;
   --      Y_Id      : <Type of Y> := Y;
   --      Arg_Id    : Array (1..2) of System.Address :=
   --        (X'Address,_Id Y'Address);_Id
   --    begin
   --      CUDA.Internal.Push_Call_Configuration (
   --        Grids_Id,
   --        Blocks_Id,
   --        Mem_Id,
   --        Stream_Id);
   --      CUDA.Internal.Pop_Call_Configuration (
   --        Grids_Id'address,
   --        Blocks_Id'address,
   --        Mem_Id'address,
   --        Stream_Id'address),
   --      CUDA.Internal.Launch_Kernel (
   --        My_Proc'Address,
   --        Blocks_Id,
   --        Grids_Id,
   --        Arg_Id'Address,
   --        Mem_Id,
   --        Stream_Id);
   --    end;

   procedure Expand_Pragma_CUDA_Execute (N : Node_Id) is

      Loc : constant Source_Ptr := Sloc (N);

      procedure Append_Copies
        (Params : List_Id;
         Decls  : List_Id;
         Copies : Elist_Id);
      --  For each parameter in list Params, create an object declaration of
      --  the following form:
      --
      --    Copy_Id : Param_Typ := Param_Val;
      --
      --  Param_Typ is the type of the parameter. Param_Val is the initial
      --  value of the parameter. The declarations are stored in Decls, the
      --  entities of the new objects are collected in list Copies.

      function Build_Dim3_Declaration
        (Decl_Id  : Entity_Id;
         Init_Val : Node_Id) return Node_Id;
      --  Build an object declaration of the form
      --
      --    Decl_Id : CUDA.Internal.Dim3 := Val;
      --
      --  Val depends on the nature of Init_Val, as follows:
      --
      --    * If Init_Val is of type CUDA.Vector_Types.Dim3, then Val has the
      --      following form:
      --
      --        (Interfaces.C.Unsigned (Val.X),
      --         Interfaces.C.Unsigned (Val.Y),
      --         Interfaces.C.Unsigned (Val.Z))
      --
      --    * If Init_Val is a single Integer, Val has the following form:
      --
      --        (Interfaces.C.Unsigned (Init_Val),
      --         Interfaces.C.Unsigned (1),
      --         Interfaces.C.Unsigned (1))
      --
      --    * If Init_Val is an aggregate of three values, Val has the
      --      following form:
      --
      --        (Interfaces.C.Unsigned (Val_1),
      --         Interfaces.C.Unsigned (Val_2),
      --         Interfaces.C.Unsigned (Val_3))

      function Build_Kernel_Args_Declaration
        (Kernel_Arg : Entity_Id;
         Var_Ids    : Elist_Id) return Node_Id;
      --  Given a list of variables, return an object declaration of the
      --  following form:
      --
      --    Kernel_Arg : ... := (Var_1'Address, ..., Var_N'Address);

      function Build_Launch_Kernel_Call
        (Proc       : Entity_Id;
         Grid_Dims  : Entity_Id;
         Block_Dims : Entity_Id;
         Kernel_Arg : Entity_Id;
         Memory     : Entity_Id;
         Stream     : Entity_Id) return Node_Id;
      --  Builds and returns a call to CUDA.Internal.Launch_Kernel using the
      --  given arguments. Proc is the entity of the procedure passed to the
      --  CUDA_Execute pragma. Grid_Dims and Block_Dims are entities of the
      --  generated declarations that hold the kernel's dimensions. Args is the
      --  entity of the temporary array that holds the arguments of the kernel.
      --  Memory and Stream are the entities of the temporaries that hold the
      --  fourth and fith arguments of CUDA_Execute or their default values.

      function Build_Shared_Memory_Declaration
        (Decl_Id  : Entity_Id;
         Init_Val : Node_Id) return Node_Id;
      --  Builds a declaration the Defining_Identifier of which is Decl_Id, the
      --  type of which is inferred from CUDA.Internal.Launch_Kernel and the
      --  value of which is Init_Val if present or null if not.

      function Build_Simple_Declaration_With_Default
        (Decl_Id     : Entity_Id;
         Init_Val    : Node_Id;
         Typ         : Node_Id;
         Default_Val : Node_Id) return Node_Id;
      --  Build a declaration the Defining_Identifier of which is Decl_Id, the
      --  Object_Definition of which is Typ, the value of which is Init_Val if
      --  present or Default otherwise.

      function Build_Stream_Declaration
        (Decl_Id  : Entity_Id;
         Init_Val : Node_Id) return Node_Id;
      --  Build a declaration the Defining_Identifier of which is Decl_Id, the
      --  type of which is Integer, the value of which is Init_Val if present
      --  and 0 otherwise.

      function Etype_Or_Dim3 (N : Node_Id) return Node_Id;
      --  If N is an aggregate whose type is unknown, return a new occurrence
      --  of the public Dim3 type. Otherwise, return a new occurrence of N's
      --  type.

      function Get_Nth_Arg_Type
         (Subprogram : Entity_Id;
          N          : Positive) return Entity_Id;
      --  Returns the type of the Nth argument of Subprogram

      function To_Addresses (Elmts : Elist_Id) return List_Id;
      --  Returns a new list containing each element of Elmts wrapped in an
      --  'address attribute reference. When passed No_Elist, returns an empty
      --  list.

      -------------------
      -- Append_Copies --
      -------------------

      procedure Append_Copies
        (Params : List_Id;
         Decls  : List_Id;
         Copies : Elist_Id)
      is
         Copy  : Entity_Id;
         Param : Node_Id;
         Expr  : Node_Id;
      begin
         Param := First (Params);
         while Present (Param) loop
            Copy := Make_Temporary (Loc, 'C');

            if Nkind (Param) = N_Parameter_Association then
               Expr := Explicit_Actual_Parameter (Param);
            else
               Expr := Param;
            end if;

            Append_To (Decls,
               Make_Object_Declaration (Loc,
                 Defining_Identifier => Copy,
                 Object_Definition   => New_Occurrence_Of (Etype (Expr), Loc),
                 Expression          => New_Copy_Tree (Expr)));

            Append_Elmt (Copy, Copies);
            Next (Param);
         end loop;
      end Append_Copies;

      ----------------------------
      -- Build_Dim3_Declaration --
      ----------------------------

      function Build_Dim3_Declaration
        (Decl_Id  : Entity_Id;
         Init_Val : Node_Id) return Node_Id
      is
         --  Expressions for each component of the returned Dim3
         Dim_X : Node_Id;
         Dim_Y : Node_Id;
         Dim_Z : Node_Id;

         --  Type of CUDA.Internal.Dim3 - inferred from
         --  RE_Push_Call_Configuration to avoid needing changes in GNAT when
         --  the CUDA bindings change (this happens frequently).
         Internal_Dim3 : constant Entity_Id :=
           Get_Nth_Arg_Type (RTE (RE_Push_Call_Configuration), 1);

         --  Entities for each component of external and internal Dim3
         First_Component  : Entity_Id := First_Entity (RTE (RE_Dim3));
         Second_Component : Entity_Id := Next_Entity (First_Component);
         Third_Component  : Entity_Id := Next_Entity (Second_Component);

      begin
         --  Sem_prag.adb ensured that Init_Val is either a Dim3, an aggregate
         --  of three Any_Integers or Any_Integer.

         --  If Init_Val is a Dim3, use each of its components

         if Etype (Init_Val) = RTE (RE_Dim3) then
            Dim_X := Make_Selected_Component (Loc,
              Prefix        => New_Occurrence_Of (Entity (Init_Val), Loc),
              Selector_Name => New_Occurrence_Of (First_Component, Loc));

            Dim_Y := Make_Selected_Component (Loc,
              Prefix        => New_Occurrence_Of (Entity (Init_Val), Loc),
              Selector_Name => New_Occurrence_Of (Second_Component, Loc));

            Dim_Z := Make_Selected_Component (Loc,
              Prefix        => New_Occurrence_Of (Entity (Init_Val), Loc),
              Selector_Name => New_Occurrence_Of (Third_Component, Loc));
         else
            --  If Init_Val is an aggregate, use each of its arguments

            if Nkind (Init_Val) = N_Aggregate then
               Dim_X := First (Expressions (Init_Val));
               Dim_Y := Next (Dim_X);
               Dim_Z := Next (Dim_Y);

            --  Otherwise, we know it is an integer and the rest defaults to 1

            else
               Dim_X := Init_Val;
               Dim_Y := Make_Integer_Literal (Loc, 1);
               Dim_Z := Make_Integer_Literal (Loc, 1);
            end if;
         end if;

         First_Component  := First_Entity (Internal_Dim3);
         Second_Component := Next_Entity (First_Component);
         Third_Component  := Next_Entity (Second_Component);

         --  Finally return the CUDA.Internal.Dim3 declaration with an
         --  aggregate initialization expression.

         return Make_Object_Declaration (Loc,
            Defining_Identifier => Decl_Id,
            Object_Definition   => New_Occurrence_Of (Internal_Dim3, Loc),
            Expression          => Make_Aggregate (Loc,
              Expressions => New_List (
                 Make_Type_Conversion (Loc,
                   Subtype_Mark =>
                     New_Occurrence_Of (Etype (First_Component), Loc),
                   Expression   => New_Copy_Tree (Dim_X)),
                 Make_Type_Conversion (Loc,
                   Subtype_Mark =>
                     New_Occurrence_Of (Etype (Second_Component), Loc),
                   Expression   => New_Copy_Tree (Dim_Y)),
                 Make_Type_Conversion (Loc,
                   Subtype_Mark =>
                     New_Occurrence_Of (Etype (Third_Component), Loc),
                   Expression   => New_Copy_Tree (Dim_Z)))));
      end Build_Dim3_Declaration;

      -----------------------------------
      -- Build_Kernel_Args_Declaration --
      -----------------------------------

      function Build_Kernel_Args_Declaration
        (Kernel_Arg : Entity_Id;
         Var_Ids    : Elist_Id) return Node_Id
      is
         Vals : constant List_Id := To_Addresses (Var_Ids);
      begin
         return
           Make_Object_Declaration (Loc,
             Defining_Identifier => Kernel_Arg,
             Object_Definition   =>
               Make_Constrained_Array_Definition (Loc,
                 Discrete_Subtype_Definitions => New_List (
                   Make_Range (Loc,
                     Low_Bound  => Make_Integer_Literal (Loc, 1),
                     High_Bound =>
                       Make_Integer_Literal (Loc, List_Length (Vals)))),
                 Component_Definition         =>
                   Make_Component_Definition (Loc,
                     Subtype_Indication =>
                       New_Occurrence_Of (Etype (RTE (RE_Address)), Loc))),
             Expression          => Make_Aggregate (Loc, Vals));
      end Build_Kernel_Args_Declaration;

      -------------------------------
      --  Build_Launch_Kernel_Call --
      -------------------------------

      function Build_Launch_Kernel_Call
        (Proc       : Entity_Id;
         Grid_Dims  : Entity_Id;
         Block_Dims : Entity_Id;
         Kernel_Arg : Entity_Id;
         Memory     : Entity_Id;
         Stream     : Entity_Id) return Node_Id is
      begin
         return
           Make_Procedure_Call_Statement (Loc,
             Name                   =>
               New_Occurrence_Of (RTE (RE_Launch_Kernel), Loc),
             Parameter_Associations => New_List (
               Make_Attribute_Reference (Loc,
                 Prefix         => New_Occurrence_Of (Proc, Loc),
                 Attribute_Name => Name_Address),
               New_Occurrence_Of (Grid_Dims, Loc),
               New_Occurrence_Of (Block_Dims, Loc),
               Make_Attribute_Reference (Loc,
                 Prefix         => New_Occurrence_Of (Kernel_Arg, Loc),
                 Attribute_Name => Name_Address),
               New_Occurrence_Of (Memory, Loc),
               New_Occurrence_Of (Stream, Loc)));
      end Build_Launch_Kernel_Call;

      -------------------------------------
      -- Build_Shared_Memory_Declaration --
      -------------------------------------

      function Build_Shared_Memory_Declaration
        (Decl_Id  : Entity_Id;
         Init_Val : Node_Id) return Node_Id
      is
      begin
         return Build_Simple_Declaration_With_Default
           (Decl_Id     => Decl_Id,
            Init_Val    => Init_Val,
            Typ         =>
              New_Occurrence_Of
                (Get_Nth_Arg_Type (RTE (RE_Launch_Kernel), 5), Loc),
            Default_Val => Make_Integer_Literal (Loc, 0));
      end Build_Shared_Memory_Declaration;

      -------------------------------------------
      -- Build_Simple_Declaration_With_Default --
      -------------------------------------------

      function Build_Simple_Declaration_With_Default
        (Decl_Id     : Entity_Id;
         Init_Val    : Node_Id;
         Typ         : Node_Id;
         Default_Val : Node_Id) return Node_Id
      is
         Value : Node_Id := Init_Val;
      begin
         if No (Value) then
            Value := Default_Val;
         end if;

         return Make_Object_Declaration (Loc,
           Defining_Identifier => Decl_Id,
           Object_Definition   => Typ,
           Expression          => Value);
      end Build_Simple_Declaration_With_Default;

      ------------------------------
      -- Build_Stream_Declaration --
      ------------------------------

      function Build_Stream_Declaration
        (Decl_Id  : Entity_Id;
         Init_Val : Node_Id) return Node_Id
      is
      begin
         return Build_Simple_Declaration_With_Default
           (Decl_Id     => Decl_Id,
            Init_Val    => Init_Val,
            Typ         =>
              New_Occurrence_Of
                (Get_Nth_Arg_Type (RTE (RE_Launch_Kernel), 6), Loc),
            Default_Val => Make_Null (Loc));
      end Build_Stream_Declaration;

      -------------------
      -- Etype_Or_Dim3 --
      -------------------

      function Etype_Or_Dim3 (N : Node_Id) return Node_Id is
      begin
         if Nkind (N) = N_Aggregate and then Is_Composite_Type (Etype (N)) then
            return New_Occurrence_Of (RTE (RE_Dim3), Sloc (N));
         end if;

         return New_Occurrence_Of (Etype (N), Loc);
      end Etype_Or_Dim3;

      ----------------------
      -- Get_Nth_Arg_Type --
      ----------------------

      function Get_Nth_Arg_Type
         (Subprogram : Entity_Id;
          N          : Positive) return Entity_Id
      is
         Argument : Entity_Id := First_Entity (Subprogram);
      begin
         for J in 2 .. N loop
            Next_Entity (Argument);
         end loop;

         return Etype (Argument);
      end Get_Nth_Arg_Type;

      ------------------
      -- To_Addresses --
      ------------------

      function To_Addresses (Elmts : Elist_Id) return List_Id is
         Result : constant List_Id := New_List;
         Elmt   : Elmt_Id;
      begin
         if No (Elmts) then
            return Result;
         end if;

         Elmt := First_Elmt (Elmts);
         while Present (Elmt) loop
            Append_To (Result,
              Make_Attribute_Reference (Loc,
                Prefix         => New_Occurrence_Of (Node (Elmt), Loc),
                Attribute_Name => Name_Address));
            Next_Elmt (Elmt);
         end loop;

         return Result;
      end To_Addresses;

      --  Local variables

      --  Pragma arguments

      Procedure_Call   : constant Node_Id := Get_Pragma_Arg (Arg_N (N, 1));
      Grid_Dimensions  : constant Node_Id := Get_Pragma_Arg (Arg_N (N, 2));
      Block_Dimensions : constant Node_Id := Get_Pragma_Arg (Arg_N (N, 3));
      Shared_Memory    : constant Node_Id := Get_Pragma_Arg (Arg_N (N, 4));
      CUDA_Stream      : constant Node_Id := Get_Pragma_Arg (Arg_N (N, 5));

      --  Entities of objects that will be overwritten by calls to cuda runtime
      Grids_Id  : constant Entity_Id := Make_Temporary (Loc, 'C');
      Blocks_Id : constant Entity_Id := Make_Temporary (Loc, 'C');
      Memory_Id : constant Entity_Id := Make_Temporary (Loc, 'C');
      Stream_Id : constant Entity_Id := Make_Temporary (Loc, 'C');

      --  Entities of objects that capture the value of pragma arguments
      Temp_Grid  : constant Entity_Id := Make_Temporary (Loc, 'C');
      Temp_Block : constant Entity_Id := Make_Temporary (Loc, 'C');

      --  Declarations for temporary block and grids. These needs to be stored
      --  in temporary declarations as the expressions will need to be
      --  referenced multiple times but could have side effects.
      Temp_Grid_Decl : constant Node_Id := Make_Object_Declaration (Loc,
        Defining_Identifier => Temp_Grid,
        Object_Definition   => Etype_Or_Dim3 (Grid_Dimensions),
        Expression          => Grid_Dimensions);
      Temp_Block_Decl : constant Node_Id := Make_Object_Declaration (Loc,
        Defining_Identifier => Temp_Block,
        Object_Definition   => Etype_Or_Dim3 (Block_Dimensions),
        Expression          => Block_Dimensions);

      --  List holding the entities of the copies of Procedure_Call's arguments

      Kernel_Arg_Copies : constant Elist_Id := New_Elmt_List;

      --  Entity of the array that contains the address of each of the kernel's
      --  arguments.

      Kernel_Args_Id : constant Entity_Id := Make_Temporary (Loc, 'C');

      --  Calls to the CUDA runtime API.

      Launch_Kernel_Call : Node_Id;
      Pop_Call           : Node_Id;
      Push_Call          : Node_Id;

      --  Declaration of all temporaries required for CUDA API Calls

      Blk_Decls  : constant List_Id := New_List;

   --  Start of processing for CUDA_Execute

   begin
      --  Append temporary declarations

      Append_To (Blk_Decls, Temp_Grid_Decl);
      Analyze (Temp_Grid_Decl);

      Append_To (Blk_Decls, Temp_Block_Decl);
      Analyze (Temp_Block_Decl);

      --  Build parameter declarations for CUDA API calls

      Append_To
        (Blk_Decls,
         Build_Dim3_Declaration
           (Grids_Id, New_Occurrence_Of (Temp_Grid, Loc)));

      Append_To
        (Blk_Decls,
         Build_Dim3_Declaration
           (Blocks_Id, New_Occurrence_Of (Temp_Block, Loc)));

      Append_To
        (Blk_Decls,
         Build_Shared_Memory_Declaration (Memory_Id, Shared_Memory));

      Append_To
        (Blk_Decls, Build_Stream_Declaration (Stream_Id, CUDA_Stream));

      Append_Copies
        (Parameter_Associations (Procedure_Call),
         Blk_Decls,
         Kernel_Arg_Copies);

      Append_To
        (Blk_Decls,
         Build_Kernel_Args_Declaration
           (Kernel_Args_Id, Kernel_Arg_Copies));

      --  Build calls to the CUDA API

      Push_Call :=
         Make_Procedure_Call_Statement (Loc,
           Name                   =>
             New_Occurrence_Of (RTE (RE_Push_Call_Configuration), Loc),
           Parameter_Associations => New_List (
             New_Occurrence_Of (Grids_Id, Loc),
             New_Occurrence_Of (Blocks_Id, Loc),
             New_Occurrence_Of (Memory_Id, Loc),
             New_Occurrence_Of (Stream_Id, Loc)));

      Pop_Call :=
        Make_Procedure_Call_Statement (Loc,
          Name                   =>
            New_Occurrence_Of (RTE (RE_Pop_Call_Configuration), Loc),
          Parameter_Associations => To_Addresses
            (New_Elmt_List
              (Grids_Id,
               Blocks_Id,
               Memory_Id,
               Stream_Id)));

      Launch_Kernel_Call := Build_Launch_Kernel_Call
        (Proc       => Entity (Name (Procedure_Call)),
         Grid_Dims  => Grids_Id,
         Block_Dims => Blocks_Id,
         Kernel_Arg => Kernel_Args_Id,
         Memory     => Memory_Id,
         Stream     => Stream_Id);

      --  Finally make the block that holds declarations and calls

      Rewrite (N,
        Make_Block_Statement (Loc,
          Declarations               => Blk_Decls,
          Handled_Statement_Sequence =>
            Make_Handled_Sequence_Of_Statements (Loc,
             Statements => New_List (
               Push_Call,
               Pop_Call,
               Launch_Kernel_Call))));
      Analyze (N);
   end Expand_Pragma_CUDA_Execute;

   ----------------------------------
   -- Expand_Pragma_Contract_Cases --
   ----------------------------------

   --  Pragma Contract_Cases is expanded in the following manner:

   --    subprogram S is
   --       Count    : Natural := 0;
   --       Flag_1   : Boolean := False;
   --       . . .
   --       Flag_N   : Boolean := False;
   --       Flag_N+1 : Boolean := False;  --  when "others" present
   --       Pref_1   : ...;
   --       . . .
   --       Pref_M   : ...;

   --       <preconditions (if any)>

   --       --  Evaluate all case guards

   --       if Case_Guard_1 then
   --          Flag_1 := True;
   --          Count  := Count + 1;
   --       end if;
   --       . . .
   --       if Case_Guard_N then
   --          Flag_N := True;
   --          Count  := Count + 1;
   --       end if;

   --       --  Emit errors depending on the number of case guards that
   --       --  evaluated to True.

   --       if Count = 0 then
   --          raise Assertion_Error with "xxx contract cases incomplete";
   --            <or>
   --          Flag_N+1 := True;  --  when "others" present

   --       elsif Count > 1 then
   --          declare
   --             Str0 : constant String :=
   --                      "contract cases overlap for subprogram ABC";
   --             Str1 : constant String :=
   --                      (if Flag_1 then
   --                         Str0 & "case guard at xxx evaluates to True"
   --                       else Str0);
   --             StrN : constant String :=
   --                      (if Flag_N then
   --                         StrN-1 & "case guard at xxx evaluates to True"
   --                       else StrN-1);
   --          begin
   --             raise Assertion_Error with StrN;
   --          end;
   --       end if;

   --       --  Evaluate all attribute 'Old prefixes found in the selected
   --       --  consequence.

   --       if Flag_1 then
   --          Pref_1 := <prefix of 'Old found in Consequence_1>
   --       . . .
   --       elsif Flag_N then
   --          Pref_M := <prefix of 'Old found in Consequence_N>
   --       end if;

   --       procedure _Postconditions is
   --       begin
   --          <postconditions (if any)>

   --          if Flag_1 and then not Consequence_1 then
   --             raise Assertion_Error with "failed contract case at xxx";
   --          end if;
   --          . . .
   --          if Flag_N[+1] and then not Consequence_N[+1] then
   --             raise Assertion_Error with "failed contract case at xxx";
   --          end if;
   --       end _Postconditions;
   --    begin
   --       . . .
   --    end S;

   procedure Expand_Pragma_Contract_Cases
     (CCs     : Node_Id;
      Subp_Id : Entity_Id;
      Decls   : List_Id;
      Stmts   : in out List_Id)
   is
      Loc : constant Source_Ptr := Sloc (CCs);

      procedure Case_Guard_Error
        (Decls     : List_Id;
         Flag      : Entity_Id;
         Error_Loc : Source_Ptr;
         Msg       : in out Entity_Id);
      --  Given a declarative list Decls, status flag Flag, the location of the
      --  error and a string Msg, construct the following check:
      --    Msg : constant String :=
      --            (if Flag then
      --                Msg & "case guard at Error_Loc evaluates to True"
      --             else Msg);
      --  The resulting code is added to Decls

      procedure Consequence_Error
        (Checks : in out Node_Id;
         Flag   : Entity_Id;
         Conseq : Node_Id);
      --  Given an if statement Checks, status flag Flag and a consequence
      --  Conseq, construct the following check:
      --    [els]if Flag and then not Conseq then
      --       raise Assertion_Error
      --         with "failed contract case at Sloc (Conseq)";
      --    [end if;]
      --  The resulting code is added to Checks

      function Declaration_Of (Id : Entity_Id) return Node_Id;
      --  Given the entity Id of a boolean flag, generate:
      --    Id : Boolean := False;

      procedure Expand_Attributes_In_Consequence
        (Decls  : List_Id;
         Evals  : in out Node_Id;
         Flag   : Entity_Id;
         Conseq : Node_Id);
      --  Perform specialized expansion of all attribute 'Old references found
      --  in consequence Conseq such that at runtime only prefixes coming from
      --  the selected consequence are evaluated. Similarly expand attribute
      --  'Result references by replacing them with identifier _result which
      --  resolves to the sole formal parameter of procedure _Postconditions.
      --  Any temporaries generated in the process are added to declarations
      --  Decls. Evals is a complex if statement tasked with the evaluation of
      --  all prefixes coming from a single selected consequence. Flag is the
      --  corresponding case guard flag. Conseq is the consequence expression.

      function Increment (Id : Entity_Id) return Node_Id;
      --  Given the entity Id of a numerical variable, generate:
      --    Id := Id + 1;

      function Set (Id : Entity_Id) return Node_Id;
      --  Given the entity Id of a boolean variable, generate:
      --    Id := True;

      ----------------------
      -- Case_Guard_Error --
      ----------------------

      procedure Case_Guard_Error
        (Decls     : List_Id;
         Flag      : Entity_Id;
         Error_Loc : Source_Ptr;
         Msg       : in out Entity_Id)
      is
         New_Line : constant Character := Character'Val (10);
         New_Msg  : constant Entity_Id := Make_Temporary (Loc, 'S');

      begin
         Start_String;
         Store_String_Char  (New_Line);
         Store_String_Chars ("  case guard at ");
         Store_String_Chars (Build_Location_String (Error_Loc));
         Store_String_Chars (" evaluates to True");

         --  Generate:
         --    New_Msg : constant String :=
         --      (if Flag then
         --          Msg & "case guard at Error_Loc evaluates to True"
         --       else Msg);

         Append_To (Decls,
           Make_Object_Declaration (Loc,
             Defining_Identifier => New_Msg,
             Constant_Present    => True,
             Object_Definition   => New_Occurrence_Of (Standard_String, Loc),
             Expression          =>
               Make_If_Expression (Loc,
                 Expressions => New_List (
                   New_Occurrence_Of (Flag, Loc),

                   Make_Op_Concat (Loc,
                     Left_Opnd  => New_Occurrence_Of (Msg, Loc),
                     Right_Opnd => Make_String_Literal (Loc, End_String)),

                   New_Occurrence_Of (Msg, Loc)))));

         Msg := New_Msg;
      end Case_Guard_Error;

      -----------------------
      -- Consequence_Error --
      -----------------------

      procedure Consequence_Error
        (Checks : in out Node_Id;
         Flag   : Entity_Id;
         Conseq : Node_Id)
      is
         Cond  : Node_Id;
         Error : Node_Id;

      begin
         --  Generate:
         --    Flag and then not Conseq

         Cond :=
           Make_And_Then (Loc,
             Left_Opnd  => New_Occurrence_Of (Flag, Loc),
             Right_Opnd =>
               Make_Op_Not (Loc,
                 Right_Opnd => Relocate_Node (Conseq)));

         --  Generate:
         --    raise Assertion_Error
         --      with "failed contract case at Sloc (Conseq)";

         Start_String;
         Store_String_Chars ("failed contract case at ");
         Store_String_Chars (Build_Location_String (Sloc (Conseq)));

         Error :=
           Make_Procedure_Call_Statement (Loc,
             Name                   =>
               New_Occurrence_Of (RTE (RE_Raise_Assert_Failure), Loc),
             Parameter_Associations => New_List (
               Make_String_Literal (Loc, End_String)));

         if No (Checks) then
            Checks :=
              Make_Implicit_If_Statement (CCs,
                Condition       => Cond,
                Then_Statements => New_List (Error));

            Set_Comes_From_Check_Or_Contract (Checks);

         else
            if No (Elsif_Parts (Checks)) then
               Set_Elsif_Parts (Checks, New_List);
            end if;

            Append_To (Elsif_Parts (Checks),
              Make_Elsif_Part (Loc,
                Condition       => Cond,
                Then_Statements => New_List (Error)));
         end if;
      end Consequence_Error;

      --------------------
      -- Declaration_Of --
      --------------------

      function Declaration_Of (Id : Entity_Id) return Node_Id is
      begin
         return
           Make_Object_Declaration (Loc,
             Defining_Identifier => Id,
             Object_Definition   => New_Occurrence_Of (Standard_Boolean, Loc),
             Expression          => New_Occurrence_Of (Standard_False, Loc));
      end Declaration_Of;

      --------------------------------------
      -- Expand_Attributes_In_Consequence --
      --------------------------------------

      procedure Expand_Attributes_In_Consequence
        (Decls  : List_Id;
         Evals  : in out Node_Id;
         Flag   : Entity_Id;
         Conseq : Node_Id)
      is
         Eval_Stmts : List_Id := No_List;
         --  The evaluation sequence expressed as assignment statements of all
         --  prefixes of attribute 'Old found in the current consequence.

         function Expand_Attributes (N : Node_Id) return Traverse_Result;
         --  Determine whether an arbitrary node denotes attribute 'Old or
         --  'Result and if it does, perform all expansion-related actions.

         -----------------------
         -- Expand_Attributes --
         -----------------------

         function Expand_Attributes (N : Node_Id) return Traverse_Result is
            Decl     : Node_Id;
            Pref     : Node_Id;
            Temp     : Entity_Id;
            Indirect : Boolean := False;

            use Sem_Util.Old_Attr_Util.Indirect_Temps;

            procedure Append_For_Indirect_Temp
              (N : Node_Id; Is_Eval_Stmt : Boolean);

            --  Append either a declaration (which is to be elaborated
            --  unconditionally) or an evaluation statement (which is
            --  to be executed conditionally).

            -------------------------------
            --  Append_For_Indirect_Temp --
            -------------------------------

            procedure Append_For_Indirect_Temp
              (N : Node_Id; Is_Eval_Stmt : Boolean)
            is
            begin
               if Is_Eval_Stmt then
                  Append_To (Eval_Stmts, N);
               else
                  Prepend_To (Decls, N);
                  --  This use of Prepend (as opposed to Append) is why
                  --  we have the Append_Decls_In_Reverse_Order parameter.
               end if;
            end Append_For_Indirect_Temp;

            procedure Declare_Indirect_Temporary is new
              Declare_Indirect_Temp (
                Append_Item                   => Append_For_Indirect_Temp,
                Append_Decls_In_Reverse_Order => True);

         --  Start of processing for Expand_Attributes

         begin
            --  Attribute 'Old

            if Is_Attribute_Old (N) then
               Pref := Prefix (N);

               Indirect := Indirect_Temp_Needed (Etype (Pref));

               if Indirect then
                  if No (Eval_Stmts) then
                     Eval_Stmts := New_List;
                  end if;

                  Declare_Indirect_Temporary
                    (Attr_Prefix   => Pref,
                     Indirect_Temp => Temp);

               --  Declare a temporary of the prefix type with no explicit
               --  initial value. If the appropriate contract case is selected
               --  at run time, then the temporary will be initialized via an
               --  assignment statement.

               else
                  Temp := Make_Temporary (Loc, 'T', Pref);
                  Set_Etype (Temp, Etype (Pref));

                  --  Generate a temporary to capture the value of the prefix:
                  --    Temp : <Pref type>;

                  Decl :=
                    Make_Object_Declaration (Loc,
                      Defining_Identifier => Temp,
                      Object_Definition   =>
                        New_Occurrence_Of (Etype (Pref), Loc));

                  --  Place that temporary at the beginning of declarations, to
                  --  prevent anomalies in the GNATprove flow-analysis pass in
                  --  the precondition procedure that follows.

                  Prepend_To (Decls, Decl);

                  --  Initially Temp is uninitialized (which is required for
                  --  correctness if default initialization might have side
                  --  effects). Assign prefix value to temp on Eval_Statement
                  --  list, so assignment will be executed conditionally.

                  Mutate_Ekind (Temp, E_Variable);
                  Set_Suppress_Initialization (Temp);
                  Analyze (Decl);

                  if No (Eval_Stmts) then
                     Eval_Stmts := New_List;
                  end if;

                  Append_To (Eval_Stmts,
                    Make_Assignment_Statement (Loc,
                      Name       => New_Occurrence_Of (Temp, Loc),
                      Expression => Pref));
               end if;

               --  Mark the temporary as coming from a 'Old reference

               if Present (Temp) then
                  Set_Stores_Attribute_Old_Prefix (Temp);
               end if;

               --  Ensure that the prefix is valid

               if Validity_Checks_On and then Validity_Check_Operands then
                  Ensure_Valid (Pref);
               end if;

               --  Replace the original attribute 'Old by a reference to the
               --  generated temporary.

               if Indirect then
                  Rewrite (N,
                    Indirect_Temp_Value
                      (Temp => Temp, Typ => Etype (Pref), Loc => Loc));
               else
                  Rewrite (N, New_Occurrence_Of (Temp, Loc));
               end if;

            --  Attribute 'Result

            elsif Is_Attribute_Result (N) then
               Rewrite (N, Make_Identifier (Loc, Name_uResult));
            end if;

            return OK;
         end Expand_Attributes;

         procedure Expand_Attributes_In is
           new Traverse_Proc (Expand_Attributes);

      --  Start of processing for Expand_Attributes_In_Consequence

      begin
         --  Inspect the consequence and expand any attribute 'Old and 'Result
         --  references found within.

         Expand_Attributes_In (Conseq);

         --  The consequence does not contain any attribute 'Old references

         if No (Eval_Stmts) then
            return;
         end if;

         --  Augment the machinery to trigger the evaluation of all prefixes
         --  found in the step above. If Eval is empty, then this is the first
         --  consequence to yield expansion of 'Old. Generate:

         --    if Flag then
         --       <evaluation statements>
         --    end if;

         if No (Evals) then
            Evals :=
              Make_Implicit_If_Statement (CCs,
                Condition       => New_Occurrence_Of (Flag, Loc),
                Then_Statements => Eval_Stmts);

            Set_Comes_From_Check_Or_Contract (Evals);

         --  Otherwise generate:
         --    elsif Flag then
         --       <evaluation statements>
         --    end if;

         else
            if No (Elsif_Parts (Evals)) then
               Set_Elsif_Parts (Evals, New_List);
            end if;

            Append_To (Elsif_Parts (Evals),
              Make_Elsif_Part (Loc,
                Condition       => New_Occurrence_Of (Flag, Loc),
                Then_Statements => Eval_Stmts));
         end if;
      end Expand_Attributes_In_Consequence;

      ---------------
      -- Increment --
      ---------------

      function Increment (Id : Entity_Id) return Node_Id is
      begin
         return
           Make_Assignment_Statement (Loc,
             Name       => New_Occurrence_Of (Id, Loc),
             Expression =>
               Make_Op_Add (Loc,
                 Left_Opnd  => New_Occurrence_Of (Id, Loc),
                 Right_Opnd => Make_Integer_Literal (Loc, 1)));
      end Increment;

      ---------
      -- Set --
      ---------

      function Set (Id : Entity_Id) return Node_Id is
      begin
         return
           Make_Assignment_Statement (Loc,
             Name       => New_Occurrence_Of (Id, Loc),
             Expression => New_Occurrence_Of (Standard_True, Loc));
      end Set;

      --  Local variables

      Aggr : constant Node_Id :=
               Expression (First (Pragma_Argument_Associations (CCs)));

      Case_Guard    : Node_Id;
      CG_Checks     : Node_Id;
      CG_Stmts      : List_Id;
      Conseq        : Node_Id;
      Conseq_Checks : Node_Id   := Empty;
      Count         : Entity_Id;
      Count_Decl    : Node_Id;
      Error_Decls   : List_Id := No_List; -- init to avoid warning
      Flag          : Entity_Id;
      Flag_Decl     : Node_Id;
      If_Stmt       : Node_Id;
      Msg_Str       : Entity_Id := Empty;
      Multiple_PCs  : Boolean;
      Old_Evals     : Node_Id   := Empty;
      Others_Decl   : Node_Id;
      Others_Flag   : Entity_Id := Empty;
      Post_Case     : Node_Id;

   --  Start of processing for Expand_Pragma_Contract_Cases

   begin
      --  Do nothing if pragma is not enabled. If pragma is disabled, it has
      --  already been rewritten as a Null statement.

      if Is_Ignored (CCs) then
         return;

      --  Guard against malformed contract cases

      elsif Nkind (Aggr) /= N_Aggregate then
         return;
      end if;

      --  The expansion of contract cases is quite distributed as it produces
      --  various statements to evaluate the case guards and consequences. To
      --  preserve the original context, set the Is_Assertion_Expr flag. This
      --  aids the Ghost legality checks when verifying the placement of a
      --  reference to a Ghost entity.

      In_Assertion_Expr := In_Assertion_Expr + 1;

      Multiple_PCs := List_Length (Component_Associations (Aggr)) > 1;

      --  Create the counter which tracks the number of case guards that
      --  evaluate to True.

      --    Count : Natural := 0;

      Count := Make_Temporary (Loc, 'C');
      Count_Decl :=
        Make_Object_Declaration (Loc,
          Defining_Identifier => Count,
          Object_Definition   => New_Occurrence_Of (Standard_Natural, Loc),
          Expression          => Make_Integer_Literal (Loc, 0));

      Prepend_To (Decls, Count_Decl);
      Analyze (Count_Decl);

      --  Create the base error message for multiple overlapping case guards

      --    Msg_Str : constant String :=
      --                "contract cases overlap for subprogram Subp_Id";

      if Multiple_PCs then
         Msg_Str := Make_Temporary (Loc, 'S');

         Start_String;
         Store_String_Chars ("contract cases overlap for subprogram ");
         Store_String_Chars (Get_Name_String (Chars (Subp_Id)));

         Error_Decls := New_List (
           Make_Object_Declaration (Loc,
             Defining_Identifier => Msg_Str,
             Constant_Present    => True,
             Object_Definition   => New_Occurrence_Of (Standard_String, Loc),
             Expression          => Make_String_Literal (Loc, End_String)));
      end if;

      --  Process individual post cases

      Post_Case := First (Component_Associations (Aggr));
      while Present (Post_Case) loop
         Case_Guard := First (Choices (Post_Case));
         Conseq     := Expression (Post_Case);

         --  The "others" choice requires special processing

         if Nkind (Case_Guard) = N_Others_Choice then
            Others_Flag := Make_Temporary (Loc, 'F');
            Others_Decl := Declaration_Of (Others_Flag);

            Prepend_To (Decls, Others_Decl);
            Analyze (Others_Decl);

            --  Check possible overlap between a case guard and "others"

            if Multiple_PCs and Exception_Extra_Info then
               Case_Guard_Error
                 (Decls     => Error_Decls,
                  Flag      => Others_Flag,
                  Error_Loc => Sloc (Case_Guard),
                  Msg       => Msg_Str);
            end if;

            --  Inspect the consequence and perform special expansion of any
            --  attribute 'Old and 'Result references found within.

            Expand_Attributes_In_Consequence
              (Decls  => Decls,
               Evals  => Old_Evals,
               Flag   => Others_Flag,
               Conseq => Conseq);

            --  Check the corresponding consequence of "others"

            Consequence_Error
              (Checks => Conseq_Checks,
               Flag   => Others_Flag,
               Conseq => Conseq);

         --  Regular post case

         else
            --  Create the flag which tracks the state of its associated case
            --  guard.

            Flag := Make_Temporary (Loc, 'F');
            Flag_Decl := Declaration_Of (Flag);

            Prepend_To (Decls, Flag_Decl);
            Analyze (Flag_Decl);

            --  The flag is set when the case guard is evaluated to True
            --    if Case_Guard then
            --       Flag  := True;
            --       Count := Count + 1;
            --    end if;

            If_Stmt :=
              Make_Implicit_If_Statement (CCs,
                Condition       => Relocate_Node (Case_Guard),
                Then_Statements => New_List (
                  Set (Flag),
                  Increment (Count)));

            Set_Comes_From_Check_Or_Contract (If_Stmt);

            Append_To (Decls, If_Stmt);
            Analyze (If_Stmt);

            --  Check whether this case guard overlaps with another one

            if Multiple_PCs and Exception_Extra_Info then
               Case_Guard_Error
                 (Decls     => Error_Decls,
                  Flag      => Flag,
                  Error_Loc => Sloc (Case_Guard),
                  Msg       => Msg_Str);
            end if;

            --  Inspect the consequence and perform special expansion of any
            --  attribute 'Old and 'Result references found within.

            Expand_Attributes_In_Consequence
              (Decls  => Decls,
               Evals  => Old_Evals,
               Flag   => Flag,
               Conseq => Conseq);

            --  The corresponding consequence of the case guard which evaluated
            --  to True must hold on exit from the subprogram.

            Consequence_Error
              (Checks => Conseq_Checks,
               Flag   => Flag,
               Conseq => Conseq);
         end if;

         Next (Post_Case);
      end loop;

      --  Raise Assertion_Error when none of the case guards evaluate to True.
      --  The only exception is when we have "others", in which case there is
      --  no error because "others" acts as a default True.

      --  Generate:
      --    Flag := True;

      if Present (Others_Flag) then
         CG_Stmts := New_List (Set (Others_Flag));

      --  Generate:
      --    raise Assertion_Error with "xxx contract cases incomplete";

      else
         Start_String;
         Store_String_Chars (Build_Location_String (Loc));
         Store_String_Chars (" contract cases incomplete");

         CG_Stmts := New_List (
           Make_Procedure_Call_Statement (Loc,
             Name                   =>
               New_Occurrence_Of (RTE (RE_Raise_Assert_Failure), Loc),
             Parameter_Associations => New_List (
               Make_String_Literal (Loc, End_String))));
      end if;

      CG_Checks :=
        Make_Implicit_If_Statement (CCs,
          Condition       =>
            Make_Op_Eq (Loc,
              Left_Opnd  => New_Occurrence_Of (Count, Loc),
              Right_Opnd => Make_Integer_Literal (Loc, 0)),
          Then_Statements => CG_Stmts);

      Set_Comes_From_Check_Or_Contract (CG_Checks);

      --  Detect a possible failure due to several case guards evaluating to
      --  True.

      --  Generate:
      --    elsif Count > 0 then
      --       declare
      --          <Error_Decls>
      --       begin
      --          raise Assertion_Error with <Msg_Str>;
      --    end if;

      if Multiple_PCs then
         Set_Elsif_Parts (CG_Checks, New_List (
           Make_Elsif_Part (Loc,
             Condition       =>
               Make_Op_Gt (Loc,
                 Left_Opnd  => New_Occurrence_Of (Count, Loc),
                 Right_Opnd => Make_Integer_Literal (Loc, 1)),

             Then_Statements => New_List (
               Make_Block_Statement (Loc,
                 Declarations               => Error_Decls,
                 Handled_Statement_Sequence =>
                   Make_Handled_Sequence_Of_Statements (Loc,
                     Statements => New_List (
                       Make_Procedure_Call_Statement (Loc,
                         Name                   =>
                           New_Occurrence_Of
                             (RTE (RE_Raise_Assert_Failure), Loc),
                         Parameter_Associations => New_List (
                           New_Occurrence_Of (Msg_Str, Loc))))))))));
      end if;

      --  Append the checks, but do not analyze them at this point, because
      --  contracts get potentially expanded as part of a wrapper which gets
      --  fully analyzed once it is fully formed.

      Append_To (Decls, CG_Checks);

      --  Once all case guards are evaluated and checked, evaluate any prefixes
      --  of attribute 'Old founds in the selected consequence.

      if Present (Old_Evals) then
         Append_To (Decls, Old_Evals);
      end if;

      --  Raise Assertion_Error when the corresponding consequence of a case
      --  guard that evaluated to True fails.

      Append_New_To (Stmts, Conseq_Checks);

      In_Assertion_Expr := In_Assertion_Expr - 1;
   end Expand_Pragma_Contract_Cases;

   -------------------------------------
   -- Expand_Pragma_Exceptional_Cases --
   -------------------------------------

   --  Aspect Exceptional_Cases shoule be expanded in the following manner:

   --  Original declaration

   --     procedure P (...) with
   --        Exceptional_Cases =>
   --           (Exp_1 => True,
   --            Exp_2 => Post_4);

   --  Expanded body

   --     procedure P (...) is
   --     begin
   --        --  normal body of of P
   --        declare
   --        ...
   --        end;
   --
   --     exception
   --        when Exp1 =>
   --           pragma Assert (True);
   --           raise;
   --        when E : Exp2 =>
   --           pragma Assert (Post_4);
   --           raise;
   --        when others =>
   --           pragma Assert (False);
   --           raise;
   --     end P;

   procedure Expand_Pragma_Exceptional_Cases (Prag : Node_Id) is
   begin
      --  Currently we don't expand this pragma

      Rewrite (Prag, Make_Null_Statement (Sloc (Prag)));
   end Expand_Pragma_Exceptional_Cases;

   ---------------------------------------
   -- Expand_Pragma_Import_Or_Interface --
   ---------------------------------------

   procedure Expand_Pragma_Import_Or_Interface (N : Node_Id) is
      Def_Id : Entity_Id;

   begin
      --  In Relaxed_RM_Semantics, support old Ada 83 style:
      --  pragma Import (Entity, "external name");

      if Relaxed_RM_Semantics
        and then List_Length (Pragma_Argument_Associations (N)) = 2
        and then Pragma_Name (N) = Name_Import
        and then Nkind (Arg_N (N, 2)) = N_String_Literal
      then
         Def_Id := Entity (Arg_N (N, 1));
      else
         Def_Id := Entity (Arg_N (N, 2));
      end if;

      --  Variable case (we have to undo any initialization already done)

      if Ekind (Def_Id) = E_Variable then
         Undo_Initialization (Def_Id, N);

      --  Case of exception with convention C++

      elsif Ekind (Def_Id) = E_Exception
        and then Convention (Def_Id) = Convention_CPP
      then
         --  Import a C++ convention

         declare
            Loc          : constant Source_Ptr := Sloc (N);
            Rtti_Name    : constant Node_Id    := Arg_N (N, 3);
            Dum          : constant Entity_Id  := Make_Temporary (Loc, 'D');
            Exdata       : List_Id;
            Lang_Char    : Node_Id;
            Foreign_Data : Node_Id;

         begin
            Exdata := Component_Associations (Expression (Parent (Def_Id)));

            Lang_Char := Next (First (Exdata));

            --  Change the one-character language designator to 'C'

            Rewrite (Expression (Lang_Char),
              Make_Character_Literal (Loc,
                Chars              => Name_uC,
                Char_Literal_Value => UI_From_CC (Get_Char_Code ('C'))));
            Analyze (Expression (Lang_Char));

            --  Change the value of Foreign_Data

            Foreign_Data := Next (Next (Next (Next (Lang_Char))));

            Insert_Actions (Def_Id, New_List (
              Make_Object_Declaration (Loc,
                Defining_Identifier => Dum,
                Object_Definition   =>
                  New_Occurrence_Of (Standard_Character, Loc)),

              Make_Pragma (Loc,
                Chars                        => Name_Import,
                Pragma_Argument_Associations => New_List (
                  Make_Pragma_Argument_Association (Loc,
                    Expression => Make_Identifier (Loc, Name_Ada)),

                  Make_Pragma_Argument_Association (Loc,
                    Expression => Make_Identifier (Loc, Chars (Dum))),

                  Make_Pragma_Argument_Association (Loc,
                    Chars      => Name_External_Name,
                    Expression => Relocate_Node (Rtti_Name))))));

            Rewrite (Expression (Foreign_Data),
              OK_Convert_To (Standard_Address,
                Make_Attribute_Reference (Loc,
                  Prefix         => Make_Identifier (Loc, Chars (Dum)),
                  Attribute_Name => Name_Address)));
            Analyze (Expression (Foreign_Data));
         end;

      --  No special expansion required for any other case

      else
         null;
      end if;
   end Expand_Pragma_Import_Or_Interface;

   -------------------------------------
   -- Expand_Pragma_Initial_Condition --
   -------------------------------------

   procedure Expand_Pragma_Initial_Condition
     (Pack_Id : Entity_Id;
      N       : Node_Id)
   is
      procedure Extract_Package_Body_Lists
        (Pack_Body : Node_Id;
         Body_List : out List_Id;
         Call_List : out List_Id;
         Spec_List : out List_Id);
      --  Obtain the various declarative and statement lists of package body
      --  Pack_Body needed to insert the initial condition procedure and the
      --  call to it. The lists are as follows:
      --
      --    * Body_List - used to insert the initial condition procedure body
      --
      --    * Call_List - used to insert the call to the initial condition
      --      procedure.
      --
      --    * Spec_List - used to insert the initial condition procedure spec

      procedure Extract_Package_Declaration_Lists
        (Pack_Decl : Node_Id;
         Body_List : out List_Id;
         Call_List : out List_Id;
         Spec_List : out List_Id);
      --  Obtain the various declarative lists of package declaration Pack_Decl
      --  needed to insert the initial condition procedure and the call to it.
      --  The lists are as follows:
      --
      --    * Body_List - used to insert the initial condition procedure body
      --
      --    * Call_List - used to insert the call to the initial condition
      --      procedure.
      --
      --    * Spec_List - used to insert the initial condition procedure spec

      --------------------------------
      -- Extract_Package_Body_Lists --
      --------------------------------

      procedure Extract_Package_Body_Lists
        (Pack_Body : Node_Id;
         Body_List : out List_Id;
         Call_List : out List_Id;
         Spec_List : out List_Id)
      is
         Pack_Spec : constant Entity_Id := Corresponding_Spec (Pack_Body);

         Dummy_1 : List_Id;
         Dummy_2 : List_Id;
         HSS     : Node_Id;

      begin
         pragma Assert (Present (Pack_Spec));

         --  The different parts of the invariant procedure are inserted as
         --  follows:

         --    package Pack is       package body Pack is
         --       <IC spec>             <IC body>
         --    private               begin
         --       ...                   <IC call>
         --    end Pack;             end Pack;

         --  The initial condition procedure spec is inserted in the visible
         --  declaration of the corresponding package spec.

         Extract_Package_Declaration_Lists
           (Pack_Decl => Unit_Declaration_Node (Pack_Spec),
            Body_List => Dummy_1,
            Call_List => Dummy_2,
            Spec_List => Spec_List);

         --  The initial condition procedure body is added to the declarations
         --  of the package body.

         Body_List := Declarations (Pack_Body);

         if No (Body_List) then
            Body_List := New_List;
            Set_Declarations (Pack_Body, Body_List);
         end if;

         --  The call to the initial condition procedure is inserted in the
         --  statements of the package body.

         HSS := Handled_Statement_Sequence (Pack_Body);

         if No (HSS) then
            HSS :=
              Make_Handled_Sequence_Of_Statements (Sloc (Pack_Body),
                Statements => New_List);
            Set_Handled_Statement_Sequence (Pack_Body, HSS);
         end if;

         Call_List := Statements (HSS);
      end Extract_Package_Body_Lists;

      ---------------------------------------
      -- Extract_Package_Declaration_Lists --
      ---------------------------------------

      procedure Extract_Package_Declaration_Lists
        (Pack_Decl : Node_Id;
         Body_List : out List_Id;
         Call_List : out List_Id;
         Spec_List : out List_Id)
      is
         Pack_Spec : constant Node_Id := Specification (Pack_Decl);

      begin
         --  The different parts of the invariant procedure are inserted as
         --  follows:

         --    package Pack is
         --       <IC spec>
         --       <IC body>
         --    private
         --       <IC call>
         --    end Pack;

         --  The initial condition procedure spec and body are inserted in the
         --  visible declarations of the package spec.

         Body_List := Visible_Declarations (Pack_Spec);

         if No (Body_List) then
            Body_List := New_List;
            Set_Visible_Declarations (Pack_Spec, Body_List);
         end if;

         Spec_List := Body_List;

         --  The call to the initial procedure is inserted in the private
         --  declarations of the package spec.

         Call_List := Private_Declarations (Pack_Spec);

         if No (Call_List) then
            Call_List := New_List;
            Set_Private_Declarations (Pack_Spec, Call_List);
         end if;
      end Extract_Package_Declaration_Lists;

      --  Local variables

      IC_Prag : constant Node_Id :=
                  Get_Pragma (Pack_Id, Pragma_Initial_Condition);

      Body_List    : List_Id;
      Call         : Node_Id;
      Call_List    : List_Id;
      Call_Loc     : Source_Ptr;
      Expr         : Node_Id;
      Loc          : Source_Ptr;
      Proc_Body    : Node_Id;
      Proc_Body_Id : Entity_Id;
      Proc_Decl    : Node_Id;
      Proc_Id      : Entity_Id;
      Spec_List    : List_Id;

   --  Start of processing for Expand_Pragma_Initial_Condition

   begin
      --  Nothing to do when the package is not subject to an Initial_Condition
      --  pragma.

      if No (IC_Prag) then
         return;
      end if;

      Expr := Get_Pragma_Arg (First (Pragma_Argument_Associations (IC_Prag)));
      Loc  := Sloc (IC_Prag);

      --  Nothing to do when the pragma is ignored because its semantics are
      --  suppressed.

      if Is_Ignored (IC_Prag) then
         return;

      --  Nothing to do when the pragma or its argument are illegal because
      --  there is no valid expression to check.

      elsif Error_Posted (IC_Prag) or else Error_Posted (Expr) then
         return;
      end if;

      --  Obtain the various lists of the context where the individual pieces
      --  of the initial condition procedure are to be inserted.

      if Nkind (N) = N_Package_Body then
         Extract_Package_Body_Lists
           (Pack_Body => N,
            Body_List => Body_List,
            Call_List => Call_List,
            Spec_List => Spec_List);

      elsif Nkind (N) = N_Package_Declaration then
         Extract_Package_Declaration_Lists
           (Pack_Decl => N,
            Body_List => Body_List,
            Call_List => Call_List,
            Spec_List => Spec_List);

      --  This routine should not be used on anything other than packages

      else
         pragma Assert (False);
         return;
      end if;

      Proc_Id :=
        Make_Defining_Identifier (Loc,
          Chars => New_External_Name (Chars (Pack_Id), "Initial_Condition"));

      Mutate_Ekind                       (Proc_Id, E_Procedure);
      Set_Is_Initial_Condition_Procedure (Proc_Id);

      --  Generate:
      --    procedure <Pack_Id>Initial_Condition;

      Proc_Decl :=
        Make_Subprogram_Declaration (Loc,
          Make_Procedure_Specification (Loc,
            Defining_Unit_Name => Proc_Id));

      Append_To (Spec_List, Proc_Decl);

      --  The initial condition procedure requires debug info when initial
      --  condition is subject to Source Coverage Obligations.

      if Generate_SCO then
         Set_Debug_Info_Needed (Proc_Id);
      end if;

      --  Generate:
      --    procedure <Pack_Id>Initial_Condition is
      --    begin
      --       pragma Check (Initial_Condition, <Expr>);
      --    end <Pack_Id>Initial_Condition;

      Proc_Body :=
        Make_Subprogram_Body (Loc,
          Specification              =>
            Copy_Subprogram_Spec (Specification (Proc_Decl)),
          Declarations               => Empty_List,
          Handled_Statement_Sequence =>
            Make_Handled_Sequence_Of_Statements (Loc,
              Statements => New_List (
                Make_Pragma (Loc,
                  Chars                        => Name_Check,
                  Pragma_Argument_Associations => New_List (
                    Make_Pragma_Argument_Association (Loc,
                      Expression =>
                        Make_Identifier (Loc, Name_Initial_Condition)),
                    Make_Pragma_Argument_Association (Loc,
                      Expression => New_Copy_Tree (Expr)))))));

      Append_To (Body_List, Proc_Body);

      --  The initial condition procedure requires debug info when initial
      --  condition is subject to Source Coverage Obligations.

      Proc_Body_Id := Defining_Entity (Proc_Body);

      if Generate_SCO then
         Set_Debug_Info_Needed (Proc_Body_Id);
      end if;

      --  The location of the initial condition procedure call must be as close
      --  as possible to the intended semantic location of the check because
      --  the ABE mechanism relies heavily on accurate locations.

      Call_Loc := End_Keyword_Location (N);

      --  Generate:
      --    <Pack_Id>Initial_Condition;

      Call :=
        Make_Procedure_Call_Statement (Call_Loc,
          Name => New_Occurrence_Of (Proc_Id, Call_Loc));

      Append_To (Call_List, Call);

      Analyze (Proc_Decl);
      Analyze (Proc_Body);
      Analyze (Call);
   end Expand_Pragma_Initial_Condition;

   ------------------------------------
   -- Expand_Pragma_Inspection_Point --
   ------------------------------------

   --  If no argument is given, then we supply a default argument list that
   --  includes all objects declared at the source level in all subprograms
   --  that enclose the inspection point pragma.

   procedure Expand_Pragma_Inspection_Point (N : Node_Id) is
      Loc : constant Source_Ptr := Sloc (N);

      A          : List_Id;
      Assoc      : Node_Id;
      Faulty_Arg : Node_Id := Empty;
      E          : Entity_Id;
      S          : Entity_Id;

   begin
      if No (Pragma_Argument_Associations (N)) then
         A := New_List;
         S := Current_Scope;

         while S /= Standard_Standard loop
            E := First_Entity (S);
            while Present (E) loop
               if Comes_From_Source (E)
                 and then Is_Object (E)
                 and then not Is_Entry_Formal (E)
                 and then not Is_Formal_Object (E)
                 and then Ekind (E) /= E_Component
                 and then Ekind (E) /= E_Discriminant
               then
                  Append_To (A,
                    Make_Pragma_Argument_Association (Loc,
                      Expression => New_Occurrence_Of (E, Loc)));
               end if;

               Next_Entity (E);
            end loop;

            S := Scope (S);
         end loop;

         Set_Pragma_Argument_Associations (N, A);
      end if;

      --  Process the arguments of the pragma

      Assoc := First (Pragma_Argument_Associations (N));
      while Present (Assoc) loop
         --  The back end may need to take the address of the object

         Set_Address_Taken (Entity (Expression (Assoc)));

         --  If any of the objects have a freeze node, it must appear before
         --  pragma Inspection_Point, otherwise the entity won't be elaborated
         --  when Gigi processes the pragma.

         if Has_Delayed_Freeze (Entity (Expression (Assoc)))
           and then not Is_Frozen (Entity (Expression (Assoc)))
         then
            Error_Msg_NE
              ("??inspection point references unfrozen object &",
               Assoc,
               Entity (Expression (Assoc)));
            Faulty_Arg := Assoc;
         end if;

         Next (Assoc);
      end loop;

      --  When the above requirement isn't met, turn the pragma into a no-op

      if Present (Faulty_Arg) then
         Error_Msg_Sloc := Sloc (Faulty_Arg);
         Error_Msg_N ("\pragma Inspection_Point # will be ignored",
           Faulty_Arg);

         --  We can't just remove the pragma from the tree as it might be
         --  iterated over by the caller. Turn it into a null statement
         --  instead.

         Rewrite (N, Make_Null_Statement (Loc));
      end if;
   end Expand_Pragma_Inspection_Point;

   --------------------------------------
   -- Expand_Pragma_Interrupt_Priority --
   --------------------------------------

   --  Supply default argument if none exists (System.Interrupt_Priority'Last)

   procedure Expand_Pragma_Interrupt_Priority (N : Node_Id) is
      Loc : constant Source_Ptr := Sloc (N);
   begin
      if No (Pragma_Argument_Associations (N)) then
         Set_Pragma_Argument_Associations (N, New_List (
           Make_Pragma_Argument_Association (Loc,
             Expression =>
               Make_Attribute_Reference (Loc,
                 Prefix         =>
                   New_Occurrence_Of (RTE (RE_Interrupt_Priority), Loc),
                 Attribute_Name => Name_Last))));
      end if;
   end Expand_Pragma_Interrupt_Priority;

   --------------------------------
   -- Expand_Pragma_Loop_Variant --
   --------------------------------

   --  Pragma Loop_Variant is expanded in the following manner:

   --  Original code

   --     for | while ... loop
   --        <preceding source statements>
   --        pragma Loop_Variant
   --                 (Increases => Incr_Expr,
   --                  Decreases => Decr_Expr);
   --        <succeeding source statements>
   --     end loop;

   --  Expanded code

   --     Curr_1 : <type of Incr_Expr>;
   --     Curr_2 : <type of Decr_Expr>;
   --     Old_1  : <type of Incr_Expr>;
   --     Old_2  : <type of Decr_Expr>;
   --     Flag   : Boolean := False;

   --     for | while ... loop
   --        <preceding source statements>

   --        if Flag then
   --           Old_1 := Curr_1;
   --           Old_2 := Curr_2;
   --        end if;

   --        Curr_1 := <Incr_Expr>;
   --        Curr_2 := <Decr_Expr>;

   --        if Flag then
   --           if Curr_1 /= Old_1 then
   --              pragma Check (Loop_Variant, Curr_1 > Old_1);
   --           else
   --              pragma Check (Loop_Variant, Curr_2 < Old_2);
   --           end if;
   --        else
   --           Flag := True;
   --        end if;

   --        <succeeding source statements>
   --     end loop;

   procedure Expand_Pragma_Loop_Variant (N : Node_Id) is
      Loc      : constant Source_Ptr := Sloc (N);
      Last_Var : constant Node_Id    :=
                   Last (Pragma_Argument_Associations (N));

      Curr_Assign : List_Id   := No_List;
      Flag_Id     : Entity_Id := Empty;
      If_Stmt     : Node_Id   := Empty;
      Old_Assign  : List_Id   := No_List;
      Loop_Scop   : Entity_Id;
      Loop_Stmt   : Node_Id;
      Variant     : Node_Id;

      procedure Process_Variant (Variant : Node_Id; Is_Last : Boolean);
      --  Process a single increasing / decreasing termination variant. Flag
      --  Is_Last should be set when processing the last variant.

      ---------------------
      -- Process_Variant --
      ---------------------

      procedure Process_Variant (Variant : Node_Id; Is_Last : Boolean) is
         Expr     : constant Node_Id    := Expression (Variant);
         Expr_Typ : constant Entity_Id  := Etype (Expr);
         Loc      : constant Source_Ptr := Sloc (Expr);
         Loop_Loc : constant Source_Ptr := Sloc (Loop_Stmt);
         Curr_Id  : Entity_Id;
         Old_Id   : Entity_Id;
         Prag     : Node_Id;

      begin
         --  All temporaries generated in this routine must be inserted before
         --  the related loop statement. Ensure that the proper scope is on the
         --  stack when analyzing the temporaries. Note that we also use the
         --  Sloc of the related loop.

         Push_Scope (Scope (Loop_Scop));

         --  Step 1: Create the declaration of the flag which controls the
         --  behavior of the assertion on the first iteration of the loop.

         if No (Flag_Id) then

            --  Generate:
            --    Flag : Boolean := False;

            Flag_Id := Make_Temporary (Loop_Loc, 'F');

            Insert_Action (Loop_Stmt,
              Make_Object_Declaration (Loop_Loc,
                Defining_Identifier => Flag_Id,
                Object_Definition   =>
                  New_Occurrence_Of (Standard_Boolean, Loop_Loc),
                Expression          =>
                  New_Occurrence_Of (Standard_False, Loop_Loc)));

            --  Prevent an unwanted optimization where the Current_Value of
            --  the flag eliminates the if statement which stores the variant
            --  values coming from the previous iteration.

            --     Flag : Boolean := False;
            --     loop
            --        if Flag then         --  condition rewritten to False
            --           Old_N := Curr_N;  --  and if statement eliminated
            --        end if;
            --        . . .
            --        Flag := True;
            --     end loop;

            Set_Current_Value (Flag_Id, Empty);
         end if;

         --  Step 2: Create the temporaries which store the old and current
         --  values of the associated expression.

         --  Generate:
         --    Curr : <type of Expr>;

         Curr_Id := Make_Temporary (Loc, 'C');

         Insert_Action (Loop_Stmt,
           Make_Object_Declaration (Loop_Loc,
             Defining_Identifier => Curr_Id,
             Object_Definition   => New_Occurrence_Of (Expr_Typ, Loop_Loc)));

         --  Generate:
         --    Old : <type of Expr>;

         Old_Id := Make_Temporary (Loc, 'P');

         Insert_Action (Loop_Stmt,
           Make_Object_Declaration (Loop_Loc,
             Defining_Identifier => Old_Id,
             Object_Definition   => New_Occurrence_Of (Expr_Typ, Loop_Loc)));

         --  Restore original scope after all temporaries have been analyzed

         Pop_Scope;

         --  Step 3: Store value of the expression from the previous iteration

         --  Generate:
         --    Old := Curr;

         Append_New_To (Old_Assign,
           Make_Assignment_Statement (Loc,
             Name       => New_Occurrence_Of (Old_Id, Loc),
             Expression => New_Occurrence_Of (Curr_Id, Loc)));

         --  Step 4: Store the current value of the expression

         --  Generate:
         --    Curr := <Expr>;

         Append_New_To (Curr_Assign,
           Make_Assignment_Statement (Loc,
             Name       => New_Occurrence_Of (Curr_Id, Loc),
             Expression => Relocate_Node (Expr)));

         --  Step 5: Create corresponding assertion to verify change of value

         --  Generate:
         --    pragma Check (Loop_Variant, Curr <|> Old);

         Prag :=
           Make_Pragma (Loc,
             Chars                        => Name_Check,
             Pragma_Argument_Associations => New_List (
               Make_Pragma_Argument_Association (Loc,
                 Expression => Make_Identifier (Loc, Name_Loop_Variant)),
               Make_Pragma_Argument_Association (Loc,
                 Expression =>
                   Make_Variant_Comparison (Loc,
                     Mode     => Chars (Variant),
                     Typ      => Expr_Typ,
                     Curr_Val => New_Occurrence_Of (Curr_Id, Loc),
                     Old_Val  => New_Occurrence_Of (Old_Id, Loc)))));

         --  Generate:
         --    if Curr /= Old then
         --       <Prag>;

         if No (If_Stmt) then

            --  When there is just one termination variant, do not compare the
            --  old and current value for equality, just check the pragma.

            if Is_Last then
               If_Stmt := Prag;
            else
               If_Stmt :=
                 Make_If_Statement (Loc,
                   Condition       =>
                     Make_Op_Ne (Loc,
                       Left_Opnd  => New_Occurrence_Of (Curr_Id, Loc),
                       Right_Opnd => New_Occurrence_Of (Old_Id, Loc)),
                   Then_Statements => New_List (Prag));
            end if;

         --  Generate:
         --    else
         --       <Prag>;
         --    end if;

         elsif Is_Last then
            Set_Else_Statements (If_Stmt, New_List (Prag));

         --  Generate:
         --    elsif Curr /= Old then
         --       <Prag>;

         else
            if Elsif_Parts (If_Stmt) = No_List then
               Set_Elsif_Parts (If_Stmt, New_List);
            end if;

            Append_To (Elsif_Parts (If_Stmt),
              Make_Elsif_Part (Loc,
                Condition       =>
                  Make_Op_Ne (Loc,
                    Left_Opnd  => New_Occurrence_Of (Curr_Id, Loc),
                    Right_Opnd => New_Occurrence_Of (Old_Id, Loc)),
                Then_Statements => New_List (Prag)));
         end if;
      end Process_Variant;

   --  Start of processing for Expand_Pragma_Loop_Variant

   begin
      --  If pragma is not enabled, rewrite as Null statement. If pragma is
      --  disabled, it has already been rewritten as a Null statement.
      --
      --  Likewise, ignore structural variants for execution.
      --
      --  Also do this in CodePeer mode, because the expanded code is too
      --  complicated for CodePeer to analyse.

      if Is_Ignored (N)
        or else Chars (Last_Var) = Name_Structural
        or else CodePeer_Mode
      then
         Rewrite (N, Make_Null_Statement (Loc));
         Analyze (N);
         return;
      end if;

      --  The expansion of Loop_Variant is quite distributed as it produces
      --  various statements to capture and compare the arguments. To preserve
      --  the original context, set the Is_Assertion_Expr flag. This aids the
      --  Ghost legality checks when verifying the placement of a reference to
      --  a Ghost entity.

      In_Assertion_Expr := In_Assertion_Expr + 1;

      --  Locate the enclosing loop for which this assertion applies. In the
      --  case of Ada 2012 array iteration, we might be dealing with nested
      --  loops. Only the outermost loop has an identifier.

      Loop_Stmt := N;
      while Present (Loop_Stmt) loop
         if Nkind (Loop_Stmt) = N_Loop_Statement
           and then Present (Identifier (Loop_Stmt))
         then
            exit;
         end if;

         Loop_Stmt := Parent (Loop_Stmt);
      end loop;

      Loop_Scop := Entity (Identifier (Loop_Stmt));

      --  Create the circuitry which verifies individual variants

      Variant := First (Pragma_Argument_Associations (N));
      while Present (Variant) loop
         Process_Variant (Variant, Is_Last => Variant = Last_Var);
         Next (Variant);
      end loop;

      --  Construct the segment which stores the old values of all expressions.
      --  Generate:
      --    if Flag then
      --       <Old_Assign>
      --    end if;

      Insert_Action (N,
        Make_If_Statement (Loc,
          Condition       => New_Occurrence_Of (Flag_Id, Loc),
          Then_Statements => Old_Assign));

      --  Update the values of all expressions

      Insert_Actions (N, Curr_Assign);

      --  Add the assertion circuitry to test all changes in expressions.
      --  Generate:
      --    if Flag then
      --       <If_Stmt>
      --    else
      --       Flag := True;
      --    end if;

      Insert_Action (N,
        Make_If_Statement (Loc,
          Condition       => New_Occurrence_Of (Flag_Id, Loc),
          Then_Statements => New_List (If_Stmt),
          Else_Statements => New_List (
            Make_Assignment_Statement (Loc,
              Name       => New_Occurrence_Of (Flag_Id, Loc),
              Expression => New_Occurrence_Of (Standard_True, Loc)))));

      --  Note: the pragma has been completely transformed into a sequence of
      --  corresponding declarations and statements. We leave it in the tree
      --  for documentation purposes. It will be ignored by the backend.

      In_Assertion_Expr := In_Assertion_Expr - 1;
   end Expand_Pragma_Loop_Variant;

   --------------------------------
   -- Expand_Pragma_Psect_Object --
   --------------------------------

   --  Convert to Common_Object, and expand the resulting pragma

   procedure Expand_Pragma_Psect_Object (N : Node_Id)
     renames Expand_Pragma_Common_Object;

   -------------------------------------
   -- Expand_Pragma_Relative_Deadline --
   -------------------------------------

   procedure Expand_Pragma_Relative_Deadline (N : Node_Id) is
      P    : constant Node_Id    := Parent (N);
      Loc  : constant Source_Ptr := Sloc (N);

   begin
      --  Expand the pragma only in the case of the main subprogram. For tasks
      --  the expansion is done in exp_ch9. Generate a call to Set_Deadline
      --  at Clock plus the relative deadline specified in the pragma. Time
      --  values are translated into Duration to allow for non-private
      --  addition operation.

      if Nkind (P) = N_Subprogram_Body then
         Rewrite
           (N,
            Make_Procedure_Call_Statement (Loc,
              Name => New_Occurrence_Of (RTE (RE_Set_Deadline), Loc),
              Parameter_Associations => New_List (
                Unchecked_Convert_To (RTE (RO_RT_Time),
                  Make_Op_Add (Loc,
                    Left_Opnd  =>
                      Make_Function_Call (Loc,
                        New_Occurrence_Of (RTE (RO_RT_To_Duration), Loc),
                        New_List
                          (Make_Function_Call
                             (Loc, New_Occurrence_Of (RTE (RE_Clock), Loc)))),
                    Right_Opnd  =>
                      Unchecked_Convert_To (
                        Standard_Duration,
                        Arg_N (N, 1)))))));

         Analyze (N);
      end if;
   end Expand_Pragma_Relative_Deadline;

   --------------------------------------
   -- Expand_Pragma_Subprogram_Variant --
   --------------------------------------

   --  Aspect Subprogram_Variant is expanded in the following manner:

   --  Original code

   --     procedure Proc (Param : T) with
   --        with Variant (Increases => Incr_Expr,
   --                      Decreases => Decr_Expr)
   --        <declarations>
   --     is
   --        <source statements>
   --        Proc (New_Param_Value);
   --     end Proc;

   --  Expanded code

   --     procedure Proc (Param : T) is
   --        Old_Incr : constant <type of Incr_Expr> := <Incr_Expr>;
   --        Old_Decr : constant <type of Decr_Expr> := <Decr_Expr> ;
   --
   --        procedure Variants (Param : T);
   --
   --        procedure Variants (Param : T) is
   --           Curr_Incr : constant <type of Incr_Expr> := <Incr_Expr>;
   --           Curr_Decr : constant <type of Decr_Expr> := <Decr_Expr>;
   --        begin
   --           if Curr_Incr /= Old_Incr then
   --              pragma Check (Variant, Curr_Incr > Old_Incr);
   --           else
   --              pragma Check (Variant, Curr_Decr < Old_Decr);
   --           end if;
   --        end Variants;
   --
   --        <declarations>
   --     begin
   --        <source statements>
   --        Variants (New_Param_Value);
   --        Proc (New_Param_Value);
   --     end Proc;

   procedure Expand_Pragma_Subprogram_Variant
     (Prag       : Node_Id;
      Subp_Id    : Entity_Id;
      Body_Decls : List_Id)
   is
      Curr_Decls : List_Id;
      If_Stmt    : Node_Id := Empty;

      function Formal_Param_Map
        (Old_Subp : Entity_Id;
         New_Subp : Entity_Id) return Elist_Id;
      --  Given two subprogram entities Old_Subp and New_Subp with the same
      --  number of formal parameters return a list of the form:
      --
      --    old formal 1
      --    new formal 1
      --    old formal 2
      --    new formal 2
      --    ...
      --
      --  as required by New_Copy_Tree to replace references to formal
      --  parameters of Old_Subp with references to formal parameters of
      --  New_Subp.

      procedure Process_Variant
        (Variant    : Node_Id;
         Formal_Map : Elist_Id;
         Prev_Decl  : in out Node_Id;
         Is_Last    : Boolean);
      --  Process a single increasing / decreasing termination variant given by
      --  a component association Variant. Formal_Map is a list of formal
      --  parameters of the annotated subprogram and of the internal procedure
      --  that verifies the variant in the format required by New_Copy_Tree.
      --  The Old_... object created by this routine will be appended after
      --  Prev_Decl and is stored in this parameter for a next call to this
      --  routine. Is_Last is True when there are no more variants to process.

      ----------------------
      -- Formal_Param_Map --
      ----------------------

      function Formal_Param_Map
        (Old_Subp : Entity_Id;
         New_Subp : Entity_Id) return Elist_Id
      is
         Old_Formal : Entity_Id := First_Formal (Old_Subp);
         New_Formal : Entity_Id := First_Formal (New_Subp);

         Param_Map : Elist_Id;
      begin
         if Present (Old_Formal) then
            Param_Map := New_Elmt_List;
            while Present (Old_Formal) and then Present (New_Formal) loop
               Append_Elmt (Old_Formal,  Param_Map);
               Append_Elmt (New_Formal, Param_Map);

               Next_Formal (Old_Formal);
               Next_Formal (New_Formal);
            end loop;

            return Param_Map;
         else
            return No_Elist;
         end if;
      end Formal_Param_Map;

      ---------------------
      -- Process_Variant --
      ---------------------

      procedure Process_Variant
        (Variant    : Node_Id;
         Formal_Map : Elist_Id;
         Prev_Decl  : in out Node_Id;
         Is_Last    : Boolean)
      is
         Expr     : constant Node_Id    := Expression (Variant);
         Expr_Typ : constant Entity_Id  := Etype (Expr);
         Loc      : constant Source_Ptr := Sloc (Expr);

         Old_Id    : Entity_Id;
         Old_Decl  : Node_Id;
         Curr_Id   : Entity_Id;
         Curr_Decl : Node_Id;
         Prag      : Node_Id;

      begin
         --  Create temporaries that store the old values of the associated
         --  expression.

         --  Generate:
         --    Old : constant <type of Expr> := <Expr>;

         Old_Id := Make_Temporary (Loc, 'P');

         Old_Decl :=
           Make_Object_Declaration (Loc,
             Defining_Identifier => Old_Id,
             Constant_Present    => True,
             Object_Definition   => New_Occurrence_Of (Expr_Typ, Loc),
             Expression          => New_Copy_Tree (Expr));

         Insert_After_And_Analyze (Prev_Decl, Old_Decl);

         Prev_Decl := Old_Decl;

         --  Generate:
         --    Curr : constant <type of Expr> := <Expr>;

         Curr_Id := Make_Temporary (Loc, 'C');

         Curr_Decl :=
           Make_Object_Declaration (Loc,
             Defining_Identifier => Curr_Id,
             Constant_Present    => True,
             Object_Definition   => New_Occurrence_Of (Expr_Typ, Loc),
             Expression          =>
               New_Copy_Tree (Expr, Map => Formal_Map));

         Append (Curr_Decl, Curr_Decls);

         --  Generate:
         --    pragma Check (Variant, Curr <|> Old);

         Prag :=
           Make_Pragma (Loc,
             Chars                        => Name_Check,
             Pragma_Argument_Associations => New_List (
               Make_Pragma_Argument_Association (Loc,
                 Expression =>
                   Make_Identifier (Loc,
                     Name_Subprogram_Variant)),
               Make_Pragma_Argument_Association (Loc,
                 Expression =>
                   Make_Variant_Comparison (Loc,
                     Mode     => Chars (First (Choices (Variant))),
                     Typ      => Expr_Typ,
                     Curr_Val => New_Occurrence_Of (Curr_Id, Loc),
                     Old_Val  => New_Occurrence_Of (Old_Id, Loc)))));

         --  Generate:
         --    if Curr /= Old then
         --       <Prag>;

         if No (If_Stmt) then

            --  When there is just one termination variant, do not compare
            --  the old and current value for equality, just check the
            --  pragma.

            if Is_Last then
               If_Stmt := Prag;
            else
               If_Stmt :=
                 Make_If_Statement (Loc,
                   Condition       =>
                     Make_Op_Ne (Loc,
                       Left_Opnd  => New_Occurrence_Of (Curr_Id, Loc),
                       Right_Opnd => New_Occurrence_Of (Old_Id, Loc)),
                   Then_Statements => New_List (Prag));
            end if;

            --  Generate:
            --    else
            --       <Prag>;
            --    end if;

         elsif Is_Last then
            Set_Else_Statements (If_Stmt, New_List (Prag));

            --  Generate:
            --    elsif Curr /= Old then
            --       <Prag>;

         else
            if Elsif_Parts (If_Stmt) = No_List then
               Set_Elsif_Parts (If_Stmt, New_List);
            end if;

            Append_To (Elsif_Parts (If_Stmt),
              Make_Elsif_Part (Loc,
              Condition       =>
              Make_Op_Ne (Loc,
                Left_Opnd  => New_Occurrence_Of (Curr_Id, Loc),
                Right_Opnd => New_Occurrence_Of (Old_Id, Loc)),
              Then_Statements => New_List (Prag)));
         end if;
      end Process_Variant;

      --  Local variables

      Loc : constant Source_Ptr := Sloc (Prag);

      Aggr         : constant Node_Id :=
        Expression (First (Pragma_Argument_Associations (Prag)));
      Formal_Map   : Elist_Id;
      Last         : Node_Id;
      Last_Variant : constant Node_Id :=
        Nlists.Last (Component_Associations (Aggr));
      Proc_Bod     : Node_Id;
      Proc_Decl    : Node_Id;
      Proc_Id      : Entity_Id;
      Proc_Spec    : Node_Id;
      Variant      : Node_Id;

   begin
      --  Do nothing if pragma is not present or is disabled.
      --  Also ignore structural variants for execution.

      if Is_Ignored (Prag)
        or else Chars (Nlists.Last (Choices (Last_Variant))) = Name_Structural
      then
         return;
      end if;

      --  The expansion of Subprogram Variant is quite distributed as it
      --  produces various statements to capture and compare the arguments.
      --  To preserve the original context, set the Is_Assertion_Expr flag.
      --  This aids the Ghost legality checks when verifying the placement
      --  of a reference to a Ghost entity.

      In_Assertion_Expr := In_Assertion_Expr + 1;

      --  Create declaration of the procedure that compares values of the
      --  variant expressions captured at the start of subprogram with their
      --  values at the recursive call of the subprogram.

      Proc_Id := Make_Defining_Identifier (Loc, Name_uVariants);

      Proc_Spec :=
        Make_Procedure_Specification
          (Loc,
           Defining_Unit_Name       => Proc_Id,
           Parameter_Specifications => Copy_Parameter_List (Subp_Id));

      Proc_Decl :=
        Make_Subprogram_Declaration (Loc, Proc_Spec);

      Insert_Before_First_Source_Declaration (Proc_Decl, Body_Decls);
      Analyze (Proc_Decl);

      --  Create a mapping between formals of the annotated subprogram (which
      --  are used to compute values of the variant expression at the start of
      --  subprogram) and formals of the internal procedure (which are used to
      --  compute values of of the variant expression at the recursive call).

      Formal_Map :=
        Formal_Param_Map (Old_Subp => Subp_Id, New_Subp => Proc_Id);

      --  Process invidual increasing / decreasing variants

      Last         := Proc_Decl;
      Curr_Decls   := New_List;

      Variant := First (Component_Associations (Aggr));
      while Present (Variant) loop
         Process_Variant
           (Variant    => Variant,
            Formal_Map => Formal_Map,
            Prev_Decl  => Last,
            Is_Last    => Variant = Last_Variant);
         Next (Variant);
      end loop;

      --  Create a subprogram body with declarations of objects that capture
      --  the current values of variant expressions at a recursive call and an
      --  if-then-else statement that compares current with old values.

      Proc_Bod :=
        Make_Subprogram_Body (Loc,
          Specification              =>
            Copy_Subprogram_Spec (Proc_Spec),
          Declarations               => Curr_Decls,
          Handled_Statement_Sequence =>
            Make_Handled_Sequence_Of_Statements (Loc,
              Statements => New_List (If_Stmt),
              End_Label  => Make_Identifier (Loc, Chars (Proc_Id))));

      Insert_After_And_Analyze (Last, Proc_Bod);

      --  Restore assertion context

      In_Assertion_Expr := In_Assertion_Expr - 1;

      --  Rewrite the aspect expression, which is no longer needed, with
      --  a reference to the procedure that has just been created. We will
      --  generate a call to this procedure at each recursive call of the
      --  subprogram that has been annotated with Subprogram_Variant.

      Rewrite (Aggr, New_Occurrence_Of (Proc_Id, Loc));
   end Expand_Pragma_Subprogram_Variant;

   -------------------------------------------
   -- Expand_Pragma_Suppress_Initialization --
   -------------------------------------------

   procedure Expand_Pragma_Suppress_Initialization (N : Node_Id) is
      Def_Id : constant Entity_Id  := Entity (Arg_N (N, 1));

   begin
      --  Variable case (we have to undo any initialization already done)

      if Ekind (Def_Id) = E_Variable then
         Undo_Initialization (Def_Id, N);
      end if;
   end Expand_Pragma_Suppress_Initialization;

   -------------------------
   -- Undo_Initialization --
   -------------------------

   procedure Undo_Initialization (Def_Id : Entity_Id; N : Node_Id) is
      Init_Call : Node_Id;

   begin
      --  When applied to a variable, the default initialization must not be
      --  done. As it is already done when the pragma is found, we just get rid
      --  of the call to the initialization procedure which followed the object
      --  declaration. The call is inserted after the declaration, but validity
      --  checks may also have been inserted and thus the initialization call
      --  does not necessarily appear immediately after the object declaration.

      --  We can't use the freezing mechanism for this purpose, since we have
      --  to elaborate the initialization expression when it is first seen (so
      --  this elaboration cannot be deferred to the freeze point).

      --  Find and remove generated initialization call for object, if any

      Init_Call := Remove_Init_Call (Def_Id, Rep_Clause => N);

      --  Any default initialization expression should be removed (e.g.
      --  null defaults for access objects, zero initialization of packed
      --  bit arrays). Imported objects aren't allowed to have explicit
      --  initialization, so the expression must have been generated by
      --  the compiler.

      if No (Init_Call) and then Present (Expression (Parent (Def_Id))) then
         Set_Expression (Parent (Def_Id), Empty);
      end if;

      --  The object may not have any initialization, but in the presence of
      --  Initialize_Scalars code is inserted after then declaration, which
      --  must now be removed as well. The code carries the same source
      --  location as the declaration itself.

      if Initialize_Scalars and then Is_Array_Type (Etype (Def_Id)) then
         declare
            Init : Node_Id;
            Nxt  : Node_Id;
         begin
            Init := Next (Parent (Def_Id));
            while not Comes_From_Source (Init)
              and then Sloc (Init) = Sloc (Def_Id)
            loop
               Nxt := Next (Init);
               Remove (Init);
               Init := Nxt;
            end loop;
         end;
      end if;
   end Undo_Initialization;

end Exp_Prag;