[Ada] Support for new aspect Subprogram_Variant on recursive subprograms
authorPiotr Trojanek <trojanek@adacore.com>
Mon, 13 Jul 2020 10:42:18 +0000 (12:42 +0200)
committerPierre-Marie de Rodat <derodat@adacore.com>
Tue, 20 Oct 2020 07:21:28 +0000 (03:21 -0400)
gcc/ada/

* aspects.ads: Introduce Subprogram_Variant aspect with the
following properties: GNAT-specific, with mandatory expression,
not a representation aspect, never delayed.
* contracts.adb (Expand_Subprogram_Contract): Mention new aspect
in the comment.
(Add_Contract_Item): Support addition of pragma
Subprogram_Variant to N_Contract node.
(Analyze_Entry_Or_Subprogram_Contract): Mention new aspect in
the comment; add pragma Subprogram_Variant to N_Contract node.
(Build_Postconditions_Procedure): Adapt call to
Insert_Before_First_Source_Declaration, which is now reused in
expansion of new aspect.
(Process_Contract_Cases_For): Also process Subprogram_Variant,
which is stored in N_Contract node together with Contract_Cases.
* contracts.ads (Analyze_Entry_Or_Subprogram_Contract): Mention
new aspect in the comment.
(Analyze_Entry_Or_Subprogram_Body_Contract): Likewise.
* einfo.adb (Get_Pragma): Support retrieval of new pragma.
* einfo.ads (Get_Pragma): Likewise.
* exp_ch6.adb (Check_Subprogram_Variant): New routine for
emitting call to check Subprogram_Variant expressions at run
time.
(Expand_Call_Helper): Check Subprogram_Variant expressions at
recursive calls.
* exp_prag.adb (Make_Op): Moved from expansion of pragma
Loop_Variant to Exp_Util, so it is now reused for expansion of
pragma Subprogram_Variant.
(Process_Variant): Adapt call to Make_Op after moving it to
Exp_Util.
(Expand_Pragma_Subprogram_Variant): New routine.
* exp_prag.ads (Expand_Pragma_Subprogram_Variant): Likewise.
* exp_util.adb (Make_Variant_Comparison): Moved from Exp_Prag
(see above).
* exp_util.ads (Make_Variant_Comparison): Likewise.
* inline.adb (Remove_Aspects_And_Pragmas): Handle aspect/pragma
Subprogram_Variant just like similar contracts.
* par-prag.adb (Prag): Likewise.
* sem.adb (Insert_Before_First_Source_Declaration): Moved from
Contracts (see above).
* sem.ads (Insert_Before_First_Source_Declaration): Likewise.
* sem_ch12.adb: Mention new aspect in the comment about
"Implementation of Generic Contracts", just like similar aspects
are mentioned there.
* sem_ch13.adb (Insert_Pragma): Mention new aspect in the
comment, because this routine is now used for Subprogram_Variant
just like for other similar aspects.
(Analyze_Aspect_Specifications): Mention new aspect in comments;
it is handled just like aspect Contract_Cases.
(Check_Aspect_At_Freeze_Point): Do not expect aspect
Subprogram_Variant just like we don't expect aspect
Contract_Cases.
* sem_prag.adb (Ensure_Aggregate_Form): Now also used for pragma
Subprogram_Variant, so update comment.
(Analyze_Pragma): Add initial checks for pragma
Subprogram_Variant.
(Analyze_Subprogram_Variant_In_Decl_Part): New routine with
secondary checks on the new pragma.
(Sig_Flags): Handle references within pragma Subprogram_Variant
expression just like references in similar pragma
Contract_Cases.
(Is_Valid_Assertion_Kind): Handle Subprogram_Variant just like
other similar contracts.
* sem_prag.ads (Analyze_Subprogram_Variant_In_Decl_Part): New
routine.
* sem_res.adb (Same_Or_Aliased_Subprograms): Moved to Sem_Util,
so it can be reused for detection of recursive calls where
Subprogram_Variant needs to be verified.
* sem_util.adb (Is_Subprogram_Contract_Annotation): Handle new
Subprogram_Variant annotation just like other similar
annotations.
(Same_Or_Aliased_Subprograms): Moved from Sem_Res (see above).
* sem_util.ads (Is_Subprogram_Contract_Annotation): Mention new
aspect in the comment.
(Same_Or_Aliased_Subprograms): Moved from Sem_Res (see above).
* sinfo.ads (N_Contract): Document handling of
Subprogram_Variant.
* snames.ads-tmpl: Add name for the internally generated
procedure with checks for Subprogram_Variant expression, name
for the new aspect and new pragma corresponding to aspect
Subprogram_Variant.

23 files changed:
gcc/ada/aspects.ads
gcc/ada/contracts.adb
gcc/ada/contracts.ads
gcc/ada/einfo.adb
gcc/ada/einfo.ads
gcc/ada/exp_ch6.adb
gcc/ada/exp_prag.adb
gcc/ada/exp_prag.ads
gcc/ada/exp_util.adb
gcc/ada/exp_util.ads
gcc/ada/inline.adb
gcc/ada/par-prag.adb
gcc/ada/sem.adb
gcc/ada/sem.ads
gcc/ada/sem_ch12.adb
gcc/ada/sem_ch13.adb
gcc/ada/sem_prag.adb
gcc/ada/sem_prag.ads
gcc/ada/sem_res.adb
gcc/ada/sem_util.adb
gcc/ada/sem_util.ads
gcc/ada/sinfo.ads
gcc/ada/snames.ads-tmpl

index 72812ff26146c6a959cfff212fad9c4190e96f15..d8931004fda15896e799c9a43bc329d1ea0d548f 100644 (file)
@@ -153,6 +153,7 @@ package Aspects is
       Aspect_Storage_Size,
       Aspect_Stream_Size,
       Aspect_String_Literal,
+      Aspect_Subprogram_Variant,            -- GNAT
       Aspect_Suppress,
       Aspect_Synchronization,
       Aspect_Test_Case,                     -- GNAT
@@ -426,6 +427,7 @@ package Aspects is
       Aspect_Storage_Size               => Expression,
       Aspect_Stream_Size                => Expression,
       Aspect_String_Literal             => Name,
+      Aspect_Subprogram_Variant         => Expression,
       Aspect_Suppress                   => Name,
       Aspect_Synchronization            => Name,
       Aspect_Test_Case                  => Expression,
@@ -526,6 +528,7 @@ package Aspects is
       Aspect_Storage_Size                 => True,
       Aspect_Stream_Size                  => True,
       Aspect_String_Literal               => False,
+      Aspect_Subprogram_Variant           => False,
       Aspect_Suppress                     => False,
       Aspect_Synchronization              => False,
       Aspect_Test_Case                    => False,
@@ -700,6 +703,7 @@ package Aspects is
       Aspect_Storage_Size                 => Name_Storage_Size,
       Aspect_Stream_Size                  => Name_Stream_Size,
       Aspect_String_Literal               => Name_String_Literal,
+      Aspect_Subprogram_Variant           => Name_Subprogram_Variant,
       Aspect_Suppress                     => Name_Suppress,
       Aspect_Suppress_Debug_Info          => Name_Suppress_Debug_Info,
       Aspect_Suppress_Initialization      => Name_Suppress_Initialization,
@@ -955,6 +959,7 @@ package Aspects is
       Aspect_Relaxed_Initialization       => Never_Delay,
       Aspect_SPARK_Mode                   => Never_Delay,
       Aspect_Static                       => Never_Delay,
+      Aspect_Subprogram_Variant           => Never_Delay,
       Aspect_Synchronization              => Never_Delay,
       Aspect_Test_Case                    => Never_Delay,
       Aspect_Unimplemented                => Never_Delay,
index 666a57ff574c112aa093662aceea07f85c561de7..e633e19c1d84822e7c078903fff753f7683340b8 100644 (file)
@@ -69,8 +69,8 @@ package body Contracts is
    procedure Expand_Subprogram_Contract (Body_Id : Entity_Id);
    --  Expand the contracts of a subprogram body and its correspoding spec (if
    --  any). This routine processes all [refined] pre- and postconditions as
-   --  well as Contract_Cases, invariants and predicates. Body_Id denotes the
-   --  entity of the subprogram body.
+   --  well as Contract_Cases, Subprogram_Variant, invariants and predicates.
+   --  Body_Id denotes the entity of the subprogram body.
 
    -----------------------
    -- Add_Contract_Item --
@@ -200,7 +200,10 @@ package body Contracts is
          then
             Add_Classification;
 
-         elsif Prag_Nam in Name_Contract_Cases | Name_Test_Case then
+         elsif Prag_Nam in Name_Contract_Cases
+                         | Name_Subprogram_Variant
+                         | Name_Test_Case
+         then
             Add_Contract_Test_Case;
 
          elsif Prag_Nam in Name_Postcondition | Name_Precondition then
@@ -550,8 +553,8 @@ package body Contracts is
       end if;
 
       --  Deal with preconditions, [refined] postconditions, Contract_Cases,
-      --  invariants and predicates associated with body and its spec. Do not
-      --  expand the contract of subprogram body stubs.
+      --  Subprogram_Variant, invariants and predicates associated with body
+      --  and its spec. Do not expand the contract of subprogram body stubs.
 
       if Nkind (Body_Decl) = N_Subprogram_Body then
          Expand_Subprogram_Contract (Body_Id);
@@ -686,6 +689,10 @@ package body Contracts is
                else
                   Analyze_Contract_Cases_In_Decl_Part (Prag, Freeze_Id);
                end if;
+
+            elsif Prag_Nam = Name_Subprogram_Variant then
+               Analyze_Subprogram_Variant_In_Decl_Part (Prag);
+
             else
                pragma Assert (Prag_Nam = Name_Test_Case);
                Analyze_Test_Case_In_Decl_Part (Prag);
@@ -1941,49 +1948,6 @@ package body Contracts is
          Stmts   : List_Id;
          Result  : Entity_Id)
       is
-         procedure Insert_Before_First_Source_Declaration (Stmt : Node_Id);
-         --  Insert node Stmt before the first source declaration of the
-         --  related subprogram's body. If no such declaration exists, Stmt
-         --  becomes the last declaration.
-
-         --------------------------------------------
-         -- Insert_Before_First_Source_Declaration --
-         --------------------------------------------
-
-         procedure Insert_Before_First_Source_Declaration (Stmt : Node_Id) is
-            Decls : constant List_Id := Declarations (Body_Decl);
-            Decl  : Node_Id;
-
-         begin
-            --  Inspect the declarations of the related subprogram body looking
-            --  for the first source declaration.
-
-            if Present (Decls) then
-               Decl := First (Decls);
-               while Present (Decl) loop
-                  if Comes_From_Source (Decl) then
-                     Insert_Before (Decl, Stmt);
-                     return;
-                  end if;
-
-                  Next (Decl);
-               end loop;
-
-               --  If we get there, then the subprogram body lacks any source
-               --  declarations. The body of _Postconditions now acts as the
-               --  last declaration.
-
-               Append (Stmt, Decls);
-
-            --  Ensure that the body has a declaration list
-
-            else
-               Set_Declarations (Body_Decl, New_List (Stmt));
-            end if;
-         end Insert_Before_First_Source_Declaration;
-
-         --  Local variables
-
          Loc       : constant Source_Ptr := Sloc (Body_Decl);
          Params    : List_Id := No_List;
          Proc_Bod  : Node_Id;
@@ -1991,8 +1955,6 @@ package body Contracts is
          Proc_Id   : Entity_Id;
          Proc_Spec : Node_Id;
 
-      --  Start of processing for Build_Postconditions_Procedure
-
       begin
          --  Nothing to do if there are no actions to check on exit
 
@@ -2051,7 +2013,8 @@ package body Contracts is
          --  order reference. The body of _Postconditions must be placed after
          --  the declaration of Temp to preserve correct visibility.
 
-         Insert_Before_First_Source_Declaration (Proc_Decl);
+         Insert_Before_First_Source_Declaration
+           (Proc_Decl, Declarations (Body_Decl));
          Analyze (Proc_Decl);
 
          --  Set an explicit End_Label to override the sloc of the implicit
@@ -2092,14 +2055,20 @@ package body Contracts is
             if Present (Items) then
                Prag := Contract_Test_Cases (Items);
                while Present (Prag) loop
-                  if Pragma_Name (Prag) = Name_Contract_Cases
-                    and then Is_Checked (Prag)
-                  then
-                     Expand_Pragma_Contract_Cases
-                       (CCs     => Prag,
-                        Subp_Id => Subp_Id,
-                        Decls   => Declarations (Body_Decl),
-                        Stmts   => Stmts);
+                  if Is_Checked (Prag) then
+                     if Pragma_Name (Prag) = Name_Contract_Cases then
+                        Expand_Pragma_Contract_Cases
+                          (CCs     => Prag,
+                           Subp_Id => Subp_Id,
+                           Decls   => Declarations (Body_Decl),
+                           Stmts   => Stmts);
+
+                     elsif Pragma_Name (Prag) = Name_Subprogram_Variant then
+                        Expand_Pragma_Subprogram_Variant
+                          (Prag       => Prag,
+                           Subp_Id    => Subp_Id,
+                           Body_Decls => Declarations (Body_Decl));
+                     end if;
                   end if;
 
                   Prag := Next_Pragma (Prag);
index 9e7b95569fc28ee6f9123f0ac372f6a228e94e1f..4782ef59cb2c78fc95a9e225343886eb69d32e13 100644 (file)
@@ -69,15 +69,16 @@ package Contracts is
    --  subprogram body Body_Id as if they appeared at the end of a declarative
    --  region. Pragmas in question are:
    --
-   --    Contract_Cases   (stand alone subprogram body)
-   --    Depends          (stand alone subprogram body)
-   --    Global           (stand alone subprogram body)
-   --    Postcondition    (stand alone subprogram body)
-   --    Precondition     (stand alone subprogram body)
+   --    Contract_Cases     (stand alone subprogram body)
+   --    Depends            (stand alone subprogram body)
+   --    Global             (stand alone subprogram body)
+   --    Postcondition      (stand alone subprogram body)
+   --    Precondition       (stand alone subprogram body)
    --    Refined_Depends
    --    Refined_Global
    --    Refined_Post
-   --    Test_Case        (stand alone subprogram body)
+   --    Subprogram_Variant (stand alone subprogram body)
+   --    Test_Case          (stand alone subprogram body)
 
    procedure Analyze_Entry_Or_Subprogram_Contract
      (Subp_Id   : Entity_Id;
@@ -91,6 +92,7 @@ package Contracts is
    --    Global
    --    Postcondition
    --    Precondition
+   --    Subprogram_Variant
    --    Test_Case
    --
    --  Freeze_Id is the entity of a [generic] package body or a [generic]
index 4e41a726fac14c1f16ac4c59222caceb526b21c5..f1cdb19656381967aef3edaffa5fe0865458019d 100644 (file)
@@ -7660,10 +7660,11 @@ package body Einfo is
                  Id = Pragma_Refined_State              or else
                  Id = Pragma_Volatile_Function;
 
-      --  Contract / test case pragmas
+      --  Contract / subprogram variant / test case pragmas
 
       Is_CTC : constant Boolean :=
                   Id = Pragma_Contract_Cases            or else
+                  Id = Pragma_Subprogram_Variant        or else
                   Id = Pragma_Test_Case;
 
       --  Pre / postcondition pragmas
index 765d0f40e4ab8e06eca0b6e1ba8415b72363aff1..c60c070224c828c0e59165e5543b5643cae9e644 100644 (file)
@@ -8521,6 +8521,7 @@ package Einfo is
    --    Refined_Global
    --    Refined_Post
    --    Refined_State
+   --    Subprogram_Variant
    --    Test_Case
    --    Volatile_Function
 
index cb85901fcba13b59a4d801179d3e68c40b178395..5d8e48bcc5c8803cfe009460a232fbb5704e2634 100644 (file)
@@ -2800,6 +2800,10 @@ package body Exp_Ch6 is
       --  though useless predicate checks will be generally removed by
       --  back-end optimizations.
 
+      procedure Check_Subprogram_Variant;
+      --  Emit a call to the internally generated procedure with checks for
+      --  aspect Subprogrgram_Variant, if present and enabled.
+
       function Inherited_From_Formal (S : Entity_Id) return Entity_Id;
       --  Within an instance, a type derived from an untagged formal derived
       --  type inherits from the original parent, not from the actual. The
@@ -3058,6 +3062,37 @@ package body Exp_Ch6 is
          end if;
       end Can_Fold_Predicate_Call;
 
+      ------------------------------
+      -- Check_Subprogram_Variant --
+      ------------------------------
+
+      procedure Check_Subprogram_Variant is
+         Variant_Prag : constant Node_Id :=
+           Get_Pragma (Current_Scope, Pragma_Subprogram_Variant);
+
+         Variant_Proc : Entity_Id;
+
+      begin
+         if Present (Variant_Prag) and then Is_Checked (Variant_Prag) then
+
+            --  Analysis of the pragma rewrites its argument with a reference
+            --  to the internally generated procedure.
+
+            Variant_Proc :=
+              Entity
+                (Expression
+                   (First
+                      (Pragma_Argument_Associations (Variant_Prag))));
+
+            Insert_Action (Call_Node,
+              Make_Procedure_Call_Statement (Loc,
+                 Name                   =>
+                   New_Occurrence_Of (Variant_Proc, Loc),
+                 Parameter_Associations =>
+                   New_Copy_List (Parameter_Associations (Call_Node))));
+         end if;
+      end Check_Subprogram_Variant;
+
       ---------------------------
       -- Inherited_From_Formal --
       ---------------------------
@@ -4650,6 +4685,18 @@ package body Exp_Ch6 is
 
       Expand_Actuals (Call_Node, Subp, Post_Call);
 
+      --  If it is a recursive call then call the internal procedure that
+      --  verifies Subprogram_Variant contract (if present and enabled).
+      --  Detecting calls to subprogram aliases is necessary for recursive
+      --  calls in instances of generic subprograms, where the renaming of
+      --  the current subprogram is called.
+
+      if Is_Subprogram (Subp)
+        and then Same_Or_Aliased_Subprograms (Subp, Current_Scope)
+      then
+         Check_Subprogram_Variant;
+      end if;
+
       --  Verify that the actuals do not share storage. This check must be done
       --  on the caller side rather that inside the subprogram to avoid issues
       --  of parameter passing.
index b0ee2337ef824065de7b19ab5ff0c0833901cc95..f4b15fa251cdb3328a607e69d79a67747087daa6 100644 (file)
@@ -2321,32 +2321,6 @@ package body Exp_Prag is
       ---------------------
 
       procedure Process_Variant (Variant : Node_Id; Is_Last : Boolean) is
-         function Make_Op
-           (Loc      : Source_Ptr;
-            Curr_Val : Node_Id;
-            Old_Val  : Node_Id) return Node_Id;
-         --  Generate a comparison between Curr_Val and Old_Val depending on
-         --  the change mode (Increases / Decreases) of the variant.
-
-         -------------
-         -- Make_Op --
-         -------------
-
-         function Make_Op
-           (Loc      : Source_Ptr;
-            Curr_Val : Node_Id;
-            Old_Val  : Node_Id) return Node_Id
-         is
-         begin
-            if Chars (Variant) = Name_Increases then
-               return Make_Op_Gt (Loc, Curr_Val, Old_Val);
-            else pragma Assert (Chars (Variant) = Name_Decreases);
-               return Make_Op_Lt (Loc, Curr_Val, Old_Val);
-            end if;
-         end Make_Op;
-
-         --  Local variables
-
          Expr     : constant Node_Id    := Expression (Variant);
          Expr_Typ : constant Entity_Id  := Etype (Expr);
          Loc      : constant Source_Ptr := Sloc (Expr);
@@ -2355,8 +2329,6 @@ package body Exp_Prag is
          Old_Id   : Entity_Id;
          Prag     : Node_Id;
 
-      --  Start of processing for Process_Variant
-
       begin
          --  All temporaries generated in this routine must be inserted before
          --  the related loop statement. Ensure that the proper scope is on the
@@ -2467,7 +2439,8 @@ package body Exp_Prag is
                  Expression => Make_Identifier (Loc, Name_Loop_Variant)),
                Make_Pragma_Argument_Association (Loc,
                  Expression =>
-                   Make_Op (Loc,
+                   Make_Variant_Comparison (Loc,
+                     Mode     => Chars (Variant),
                      Curr_Val => New_Occurrence_Of (Curr_Id, Loc),
                      Old_Val  => New_Occurrence_Of (Old_Id, Loc)))));
 
@@ -2650,6 +2623,338 @@ package body Exp_Prag is
       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    : Node_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))),
+                     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         : Node_Id;
+      Formal_Map   : Elist_Id;
+      Last         : Node_Id;
+      Last_Variant : Node_Id;
+      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
+
+      if Is_Ignored (Prag) then
+         return;
+      end if;
+
+      Aggr := Expression (First (Pragma_Argument_Associations (Prag)));
+
+      --  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;
+      Last_Variant := Nlists.Last (Component_Associations (Aggr));
+
+      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 --
    -------------------------------------------
index 9957b219e78a0b4586c8220cb1ee4c02b669f83e..59f0d6a4f2fe41850630c0f318452be2d3ed4d44 100644 (file)
@@ -49,4 +49,15 @@ package Exp_Prag is
    --  applies to package Pack_Id. N denotes the related package spec or
    --  body.
 
+   procedure Expand_Pragma_Subprogram_Variant
+     (Prag       : Node_Id;
+      Subp_Id    : Entity_Id;
+      Body_Decls : List_Id);
+   --  Given pragma Subprogram_Variant Prag, create the circuitry needed
+   --  to evaluate variant expressions at the subprogram entry and at the
+   --  recursive call. Subp_Id is the related subprogram for which the pragma
+   --  applies and Body_Decls are its body declarations. On exit, the argument
+   --  of Prag is replaced with a reference to procedure with checks for the
+   --  variant expressions.
+
 end Exp_Prag;
index 269c9e05f50929cf8eef17cc7e5ad5bfe9262fb5..508381763d338972377dbaca20e2d533651af17c 100644 (file)
@@ -9930,6 +9930,24 @@ package body Exp_Util is
               Constraints => List_Constr));
    end Make_Subtype_From_Expr;
 
+   -----------------------------
+   -- Make_Variant_Comparison --
+   -----------------------------
+
+   function Make_Variant_Comparison
+     (Loc      : Source_Ptr;
+      Mode     : Name_Id;
+      Curr_Val : Node_Id;
+      Old_Val  : Node_Id) return Node_Id
+   is
+   begin
+      if Mode = Name_Increases then
+         return Make_Op_Gt (Loc, Curr_Val, Old_Val);
+      else pragma Assert (Mode = Name_Decreases);
+         return Make_Op_Lt (Loc, Curr_Val, Old_Val);
+      end if;
+   end Make_Variant_Comparison;
+
    ---------------
    -- Map_Types --
    ---------------
index b3073bc49e9c9e13bd3d9d113a4f9b6b05505081..cb288bd0c0e6a4f29f55ab8f28e63aea44ffe7fe 100644 (file)
@@ -910,6 +910,15 @@ package Exp_Util is
    --  wide type. Set Related_Id to request an external name for the subtype
    --  rather than an internal temporary.
 
+   function Make_Variant_Comparison
+     (Loc      : Source_Ptr;
+      Mode     : Name_Id;
+      Curr_Val : Node_Id;
+      Old_Val  : Node_Id) return Node_Id;
+   --  Subsidiary to the expansion of pragmas Loop_Variant and
+   --  Subprogram_Variant. Generate a comparison between Curr_Val and Old_Val
+   --  depending on the variant mode (Increases / Decreases).
+
    procedure Map_Types (Parent_Type : Entity_Id; Derived_Type : Entity_Id);
    --  Establish the following mapping between the attributes of tagged parent
    --  type Parent_Type and tagged derived type Derived_Type.
index f1c94cd21aa3f6656bf5a8c00bd22167634e3730..b4d56b620a6c7d87c696505525f628769da58087 100644 (file)
@@ -317,6 +317,7 @@ package body Inline is
    --    Refined_Global
    --    Refined_Depends
    --    Refined_Post
+   --    Subprogram_Variant
    --    Test_Case
    --    Unmodified
    --    Unreferenced
@@ -5119,6 +5120,7 @@ package body Inline is
                                         | Name_Refined_Global
                                         | Name_Refined_Depends
                                         | Name_Refined_Post
+                                        | Name_Subprogram_Variant
                                         | Name_Test_Case
                                         | Name_Unmodified
                                         | Name_Unreferenced
index 120aab5d7da24fa58426762fb000044ac7b06507..5783c33d2dbdc0765aba5b3a7dca44c1d2176ec5 100644 (file)
@@ -1496,6 +1496,7 @@ begin
          | Pragma_Storage_Unit
          | Pragma_Stream_Convert
          | Pragma_Subtitle
+         | Pragma_Subprogram_Variant
          | Pragma_Suppress
          | Pragma_Suppress_Debug_Info
          | Pragma_Suppress_Exception_Locations
index 4429b6bb52db3c0458599a7b0e3ab75b162a381a..7a67a4371448892a671c511622efbcf9dafac214 100644 (file)
@@ -1193,6 +1193,38 @@ package body Sem is
       end if;
    end Insert_Before_And_Analyze;
 
+   --------------------------------------------
+   -- Insert_Before_First_Source_Declaration --
+   --------------------------------------------
+
+   procedure Insert_Before_First_Source_Declaration
+     (Stmt  : Node_Id;
+      Decls : List_Id)
+   is
+      Decl : Node_Id;
+   begin
+      --  Inspect the declarations of the related subprogram body looking for
+      --  the first source declaration.
+
+      pragma Assert (Present (Decls));
+
+      Decl := First (Decls);
+      while Present (Decl) loop
+         if Comes_From_Source (Decl) then
+            Insert_Before (Decl, Stmt);
+            return;
+         end if;
+
+         Next (Decl);
+      end loop;
+
+      --  If we get there, then the subprogram body lacks any source
+      --  declarations. The body of _Postconditions now acts as the
+      --  last declaration.
+
+      Append (Stmt, Decls);
+   end Insert_Before_First_Source_Declaration;
+
    -----------------------------------
    -- Insert_List_After_And_Analyze --
    -----------------------------------
index f320b32d9959c6026cfa98f2b30032fc4f83c0cc..6003997e89d644143c728215cb5aa15a78a9b835 100644 (file)
@@ -671,6 +671,13 @@ package Sem is
    --  Suppress argument is present, then the analysis is done with the
    --  specified check suppressed (can be All_Checks to suppress all checks).
 
+   procedure Insert_Before_First_Source_Declaration
+     (Stmt  : Node_Id;
+      Decls : List_Id);
+   --  Insert node Stmt before the first source declaration of the related
+   --  subprogram's body. If no such declaration exists, Stmt becomes the last
+   --  declaration.
+
    function External_Ref_In_Generic (E : Entity_Id) return Boolean;
    --  Return True if we are in the context of a generic and E is
    --  external (more global) to it.
index 5f44e813b2fd11d00b6584f9a1dca8c2c88e44d2..4fbb6e56b68eda57328d1ada990b418446c4eff9 100644 (file)
@@ -270,6 +270,7 @@ package body Sem_Ch12 is
    --                                Refined_Depends
    --                                Refined_Global
    --                                Refined_Post
+   --                                Subprogram_Variant
    --                                Test_Case
 
    --  Most package contract annotations utilize forward references to classify
index 8f02d07acb782d15758e9d18edb75ff38769eb78..63ca3211f4367fbe8c67f99ebc2a57a1fa28d410 100644 (file)
@@ -1544,6 +1544,7 @@ package body Sem_Ch13 is
       --    Refined_Global
       --    Refined_State
       --    SPARK_Mode
+      --    Subprogram_Variant
       --    Warnings
       --  Insert pragma Prag such that it mimics the placement of a source
       --  pragma of the same kind. Flag Is_Generic should be set when the
@@ -1764,10 +1765,10 @@ package body Sem_Ch13 is
       --  analyzed right now.
 
       --  Note that there is a special handling for Pre, Post, Test_Case,
-      --  Contract_Cases aspects. In these cases, we do not have to worry
-      --  about delay issues, since the pragmas themselves deal with delay
-      --  of visibility for the expression analysis. Thus, we just insert
-      --  the pragma after the node N.
+      --  Contract_Cases and Subprogram_Variant aspects. In these cases, we do
+      --  not have to worry about delay issues, since the pragmas themselves
+      --  deal with delay of visibility for the expression analysis. Thus, we
+      --  just insert the pragma after the node N.
 
       --  Loop through aspects
 
@@ -4192,8 +4193,8 @@ package body Sem_Ch13 is
 
                --  Case 4: Aspects requiring special handling
 
-               --  Pre/Post/Test_Case/Contract_Cases whose corresponding
-               --  pragmas take care of the delay.
+               --  Pre/Post/Test_Case/Contract_Cases/Subprogram_Variant whose
+               --  corresponding pragmas take care of the delay.
 
                --  Pre/Post
 
@@ -4402,6 +4403,19 @@ package body Sem_Ch13 is
                   Insert_Pragma (Aitem);
                   goto Continue;
 
+               --  Subprogram_Variant
+
+               when Aspect_Subprogram_Variant =>
+                  Make_Aitem_Pragma
+                    (Pragma_Argument_Associations => New_List (
+                       Make_Pragma_Argument_Association (Loc,
+                         Expression => Relocate_Node (Expr))),
+                     Pragma_Name                  => Nam);
+
+                  Decorate (Aspect, Aitem);
+                  Insert_Pragma (Aitem);
+                  goto Continue;
+
                --  Case 5: Special handling for aspects with an optional
                --  boolean argument.
 
@@ -10855,6 +10869,7 @@ package body Sem_Ch13 is
             | Aspect_Refined_State
             | Aspect_Relaxed_Initialization
             | Aspect_SPARK_Mode
+            | Aspect_Subprogram_Variant
             | Aspect_Test_Case
             | Aspect_Unimplemented
             | Aspect_Volatile_Function
index 33a3f7aecba612fbe41663e8f087e2018aeb27c8..67b3d35f4fd2062d25be51b6300a0ac63f11c6ed 100644 (file)
@@ -4070,10 +4070,10 @@ package body Sem_Prag is
       procedure Ensure_Aggregate_Form (Arg : Node_Id);
       --  Subsidiary routine to the processing of pragmas Abstract_State,
       --  Contract_Cases, Depends, Global, Initializes, Refined_Depends,
-      --  Refined_Global and Refined_State. Transform argument Arg into
-      --  an aggregate if not one already. N_Null is never transformed.
-      --  Arg may denote an aspect specification or a pragma argument
-      --  association.
+      --  Refined_Global, Refined_State and Subprogram_Variant. Transform
+      --  argument Arg into an aggregate if not one already. N_Null is never
+      --  transformed. Arg may denote an aspect specification or a pragma
+      --  argument association.
 
       procedure Error_Pragma (Msg : String);
       pragma No_Return (Error_Pragma);
@@ -23898,6 +23898,139 @@ package body Sem_Prag is
             end if;
          end Style_Checks;
 
+         ------------------------
+         -- Subprogram_Variant --
+         ------------------------
+
+         --  pragma Subprogram_Variant ( SUBPROGRAM_VARIANT_ITEM
+         --                           {, SUBPROGRAM_VARIANT_ITEM } );
+
+         --  SUBPROGRAM_VARIANT_ITEM ::=
+         --    CHANGE_DIRECTION => discrete_EXPRESSION
+
+         --  CHANGE_DIRECTION ::= Increases | Decreases
+
+         --  Characteristics:
+
+         --    * Analysis - The annotation undergoes initial checks to verify
+         --    the legal placement and context. Secondary checks preanalyze the
+         --    expressions in:
+
+         --       Analyze_Subprogram_Variant_In_Decl_Part
+
+         --    * Expansion - The annotation is expanded during the expansion of
+         --    the related subprogram [body] contract as performed in:
+
+         --       Expand_Subprogram_Contract
+
+         --    * Template - The annotation utilizes the generic template of the
+         --    related subprogram [body] when it is:
+
+         --       aspect on subprogram declaration
+         --       aspect on stand-alone subprogram body
+         --       pragma on stand-alone subprogram body
+
+         --    The annotation must prepare its own template when it is:
+
+         --       pragma on subprogram declaration
+
+         --    * Globals - Capture of global references must occur after full
+         --    analysis.
+
+         --    * Instance - The annotation is instantiated automatically when
+         --    the related generic subprogram [body] is instantiated except for
+         --    the "pragma on subprogram declaration" case. In that scenario
+         --    the annotation must instantiate itself.
+
+         when Pragma_Subprogram_Variant => Subprogram_Variant : declare
+            Spec_Id   : Entity_Id;
+            Subp_Decl : Node_Id;
+            Subp_Spec : Node_Id;
+
+         begin
+            GNAT_Pragma;
+            Check_No_Identifiers;
+            Check_Arg_Count (1);
+
+            --  Ensure the proper placement of the pragma. Subprogram_Variant
+            --  must be associated with a subprogram declaration or a body that
+            --  acts as a spec.
+
+            Subp_Decl :=
+              Find_Related_Declaration_Or_Body (N, Do_Checks => True);
+
+            --  Generic subprogram
+
+            if Nkind (Subp_Decl) = N_Generic_Subprogram_Declaration then
+               null;
+
+            --  Body acts as spec
+
+            elsif Nkind (Subp_Decl) = N_Subprogram_Body
+              and then No (Corresponding_Spec (Subp_Decl))
+            then
+               null;
+
+            --  Body stub acts as spec
+
+            elsif Nkind (Subp_Decl) = N_Subprogram_Body_Stub
+              and then No (Corresponding_Spec_Of_Stub (Subp_Decl))
+            then
+               null;
+
+            --  Subprogram
+
+            elsif Nkind (Subp_Decl) = N_Subprogram_Declaration then
+               Subp_Spec := Specification (Subp_Decl);
+
+               --  Pragma Subprogram_Variant is forbidden on null procedures,
+               --  as this may lead to potential ambiguities in behavior when
+               --  interface null procedures are involved. Also, it just
+               --  wouldn't make sense, because null procedure is not
+               --  recursive.
+
+               if Nkind (Subp_Spec) = N_Procedure_Specification
+                 and then Null_Present (Subp_Spec)
+               then
+                  Error_Msg_N (Fix_Error
+                    ("pragma % cannot apply to null procedure"), N);
+                  return;
+               end if;
+
+            else
+               Pragma_Misplaced;
+               return;
+            end if;
+
+            Spec_Id := Unique_Defining_Entity (Subp_Decl);
+
+            --  A pragma that applies to a Ghost entity becomes Ghost for the
+            --  purposes of legality checks and removal of ignored Ghost code.
+
+            Mark_Ghost_Pragma (N, Spec_Id);
+            Ensure_Aggregate_Form (Get_Argument (N, Spec_Id));
+
+            --  Chain the pragma on the contract for further processing by
+            --  Analyze_Contract_Cases_In_Decl_Part.
+
+            Add_Contract_Item (N, Defining_Entity (Subp_Decl));
+
+            --  Fully analyze the pragma when it appears inside a subprogram
+            --  body because it cannot benefit from forward references.
+
+            if Nkind (Subp_Decl) in N_Subprogram_Body
+                                  | N_Subprogram_Body_Stub
+            then
+               --  The legality checks of pragma Subprogram_Variant are
+               --  affected by the SPARK mode in effect and the volatility
+               --  of the context. Analyze all pragmas in a specific order.
+
+               Analyze_If_Present (Pragma_SPARK_Mode);
+               Analyze_If_Present (Pragma_Volatile_Function);
+               Analyze_Subprogram_Variant_In_Decl_Part (N);
+            end if;
+         end Subprogram_Variant;
+
          --------------
          -- Subtitle --
          --------------
@@ -28918,6 +29051,152 @@ package body Sem_Prag is
       Set_Is_Analyzed_Pragma (N);
    end Analyze_Refined_State_In_Decl_Part;
 
+   ---------------------------------------------
+   -- Analyze_Subprogram_Variant_In_Decl_Part --
+   ---------------------------------------------
+
+   --  WARNING: This routine manages Ghost regions. Return statements must be
+   --  replaced by gotos which jump to the end of the routine and restore the
+   --  Ghost mode.
+
+   procedure Analyze_Subprogram_Variant_In_Decl_Part
+     (N         : Node_Id;
+      Freeze_Id : Entity_Id := Empty)
+   is
+      Subp_Decl : constant Node_Id   := Find_Related_Declaration_Or_Body (N);
+      Spec_Id   : constant Entity_Id := Unique_Defining_Entity (Subp_Decl);
+
+      procedure Analyze_Variant (Variant : Node_Id);
+      --  Verify the legality of a single contract case
+
+      ---------------------
+      -- Analyze_Variant --
+      ---------------------
+
+      procedure Analyze_Variant (Variant : Node_Id) is
+         Direction       : Node_Id;
+         Expr            : Node_Id;
+         Errors          : Nat;
+         Extra_Direction : Node_Id;
+
+      begin
+         if Nkind (Variant) /= N_Component_Association then
+            Error_Msg_N ("wrong syntax in subprogram variant", Variant);
+            return;
+         end if;
+
+         Direction := First (Choices (Variant));
+         Expr      := Expression (Variant);
+
+         --  Each variant must have exactly one direction
+
+         Extra_Direction := Next (Direction);
+
+         if Present (Extra_Direction) then
+            Error_Msg_N
+              ("subprogram variant case must have exactly one direction",
+               Extra_Direction);
+         end if;
+
+         --  Check placement of OTHERS if available (SPARK RM 6.1.3(1))
+
+         if Nkind (Direction) = N_Identifier then
+            if Chars (Direction) /= Name_Decreases
+                 and then
+               Chars (Direction) /= Name_Increases
+            then
+               Error_Msg_N ("wrong direction", Direction);
+            end if;
+         else
+            Error_Msg_N ("wrong syntax", Direction);
+         end if;
+
+         Errors := Serious_Errors_Detected;
+         Preanalyze_Assert_Expression (Expr, Any_Discrete);
+
+         --  Emit a clarification message when the variant expression
+         --  contains at least one undefined reference, possibly due
+         --  to contract freezing.
+
+         if Errors /= Serious_Errors_Detected
+           and then Present (Freeze_Id)
+           and then Has_Undefined_Reference (Expr)
+         then
+            Contract_Freeze_Error (Spec_Id, Freeze_Id);
+         end if;
+      end Analyze_Variant;
+
+      --  Local variables
+
+      Variants : constant Node_Id := Expression (Get_Argument (N, Spec_Id));
+
+      Saved_GM  : constant Ghost_Mode_Type := Ghost_Mode;
+      Saved_IGR : constant Node_Id         := Ignored_Ghost_Region;
+      --  Save the Ghost-related attributes to restore on exit
+
+      Variant       : Node_Id;
+      Restore_Scope : Boolean := False;
+
+   --  Start of processing for Analyze_Subprogram_Variant_In_Decl_Part
+
+   begin
+      --  Do not analyze the pragma multiple times
+
+      if Is_Analyzed_Pragma (N) then
+         return;
+      end if;
+
+      --  Set the Ghost mode in effect from the pragma. Due to the delayed
+      --  analysis of the pragma, the Ghost mode at point of declaration and
+      --  point of analysis may not necessarily be the same. Use the mode in
+      --  effect at the point of declaration.
+
+      Set_Ghost_Mode (N);
+
+      --  Single and multiple contract cases must appear in aggregate form. If
+      --  this is not the case, then either the parser of the analysis of the
+      --  pragma failed to produce an aggregate.
+
+      pragma Assert (Nkind (Variants) = N_Aggregate);
+
+      if Present (Component_Associations (Variants)) then
+
+         --  Ensure that the formal parameters are visible when analyzing all
+         --  clauses. This falls out of the general rule of aspects pertaining
+         --  to subprogram declarations.
+
+         if not In_Open_Scopes (Spec_Id) then
+            Restore_Scope := True;
+            Push_Scope (Spec_Id);
+
+            if Is_Generic_Subprogram (Spec_Id) then
+               Install_Generic_Formals (Spec_Id);
+            else
+               Install_Formals (Spec_Id);
+            end if;
+         end if;
+
+         Variant := First (Component_Associations (Variants));
+         while Present (Variant) loop
+            Analyze_Variant (Variant);
+            Next (Variant);
+         end loop;
+
+         if Restore_Scope then
+            End_Scope;
+         end if;
+
+      --  Otherwise the pragma is illegal
+
+      else
+         Error_Msg_N ("wrong syntax for subprogram variant", N);
+      end if;
+
+      Set_Is_Analyzed_Pragma (N);
+
+      Restore_Ghost_Region (Saved_GM, Saved_IGR);
+   end Analyze_Subprogram_Variant_In_Decl_Part;
+
    ------------------------------------
    -- Analyze_Test_Case_In_Decl_Part --
    ------------------------------------
@@ -30983,6 +31262,7 @@ package body Sem_Prag is
       Pragma_Storage_Unit                   =>  0,
       Pragma_Stream_Convert                 =>  0,
       Pragma_Style_Checks                   =>  0,
+      Pragma_Subprogram_Variant             => -1,
       Pragma_Subtitle                       =>  0,
       Pragma_Suppress                       =>  0,
       Pragma_Suppress_All                   =>  0,
@@ -31274,6 +31554,7 @@ package body Sem_Prag is
             | Name_Predicate
             | Name_Refined_Post
             | Name_Statement_Assertions
+            | Name_Subprogram_Variant
          =>
             return True;
 
index 460fc9ce4774ba1d0a43d75dd1063baa8539bfbc..fd7a0cdc29255d52cb31299418d92519a3e99781 100644 (file)
@@ -265,6 +265,13 @@ package Sem_Prag is
    --  the entity of [generic] package body or [generic] subprogram body which
    --  caused "freezing" of the related contract where the pragma resides.
 
+   procedure Analyze_Subprogram_Variant_In_Decl_Part
+     (N         : Node_Id;
+      Freeze_Id : Entity_Id := Empty);
+   --  Perform full analysis of delayed pragma Subprogram_Variant. Freeze_Id is
+   --  the entity of [generic] package body or [generic] subprogram body which
+   --  caused "freezing" of the related contract where the pragma resides.
+
    procedure Analyze_Test_Case_In_Decl_Part (N : Node_Id);
    --  Perform preanalysis of pragma Test_Case
 
index 1ca62ecaddca221e6aea75f8b59c84f7d1bcbb3e..ae7c5b7ac893f60b4ac8672dcbb56723de302315 100644 (file)
@@ -6124,27 +6124,6 @@ package body Sem_Res is
    ------------------
 
    procedure Resolve_Call (N : Node_Id; Typ : Entity_Id) is
-      function Same_Or_Aliased_Subprograms
-        (S : Entity_Id;
-         E : Entity_Id) return Boolean;
-      --  Returns True if the subprogram entity S is the same as E or else
-      --  S is an alias of E.
-
-      ---------------------------------
-      -- Same_Or_Aliased_Subprograms --
-      ---------------------------------
-
-      function Same_Or_Aliased_Subprograms
-        (S : Entity_Id;
-         E : Entity_Id) return Boolean
-      is
-         Subp_Alias : constant Entity_Id := Alias (S);
-      begin
-         return S = E or else (Present (Subp_Alias) and then Subp_Alias = E);
-      end Same_Or_Aliased_Subprograms;
-
-      --  Local variables
-
       Loc      : constant Source_Ptr := Sloc (N);
       Subp     : constant Node_Id    := Name (N);
       Body_Id  : Entity_Id;
@@ -6157,8 +6136,6 @@ package body Sem_Res is
       Rtype    : Entity_Id;
       Scop     : Entity_Id;
 
-   --  Start of processing for Resolve_Call
-
    begin
       --  Preserve relevant elaboration-related attributes of the context which
       --  are no longer available or very expensive to recompute once analysis,
index d9a96c6cfccbb28960482bf55c48816229dc1c8c..085ec69473b67ed992f2948917102ea9742aed07 100644 (file)
@@ -19037,6 +19037,7 @@ package body Sem_Util is
         or else Nam = Name_Refined_Depends
         or else Nam = Name_Refined_Global
         or else Nam = Name_Refined_Post
+        or else Nam = Name_Subprogram_Variant
         or else Nam = Name_Test_Case;
    end Is_Subprogram_Contract_Annotation;
 
@@ -26242,6 +26243,19 @@ package body Sem_Util is
       end if;
    end Same_Object;
 
+   ---------------------------------
+   -- Same_Or_Aliased_Subprograms --
+   ---------------------------------
+
+   function Same_Or_Aliased_Subprograms
+     (S : Entity_Id;
+      E : Entity_Id) return Boolean
+   is
+      Subp_Alias : constant Entity_Id := Alias (S);
+   begin
+      return S = E or else (Present (Subp_Alias) and then Subp_Alias = E);
+   end Same_Or_Aliased_Subprograms;
+
    ---------------
    -- Same_Type --
    ---------------
index 19116947a31a5443d5387ca211c3bb9732c1cac6..181c657d7289e41c48dd7760d3696c3d6de2b2c1 100644 (file)
@@ -2148,6 +2148,7 @@ package Sem_Util is
    --    Refined_Depends
    --    Refined_Global
    --    Refined_Post
+   --    Subprogram_Variant
    --    Test_Case
 
    function Is_Subprogram_Stub_Without_Prior_Declaration
@@ -2824,6 +2825,12 @@ package Sem_Util is
    --  mean that different objects are designated, just that this could not
    --  be reliably determined at compile time.
 
+   function Same_Or_Aliased_Subprograms
+     (S : Entity_Id;
+      E : Entity_Id) return Boolean;
+   --  Returns True if the subprogram entity S is the same as E or else S is an
+   --  alias of E.
+
    function Same_Type (T1, T2 : Entity_Id) return Boolean;
    --  Determines if T1 and T2 represent exactly the same type. Two types
    --  are the same if they are identical, or if one is an unconstrained
index 2583f9144b165699ffa3abbf733315a11677f98b..231ebb586abb247c578a5a0f1c3768cb629e99cb 100644 (file)
@@ -7945,8 +7945,8 @@ package Sinfo is
       --  operation) are also in this list.
 
       --  Contract_Test_Cases contains a collection of pragmas that correspond
-      --  to aspects/pragmas Contract_Cases and Test_Case. The ordering in the
-      --  list is in LIFO fashion.
+      --  to aspects/pragmas Contract_Cases, Test_Case and Subprogram_Variant.
+      --  The ordering in the list is in LIFO fashion.
 
       --  Classifications contains pragmas that either declare, categorize, or
       --  establish dependencies between subprogram or package inputs and
index 65cc9d5a3eb8c09c2809b1c22f114dda58b18233..8d457b50097868811a06fd6490a8e9204272e5d2 100644 (file)
@@ -196,6 +196,7 @@ package Snames is
    Name_uTask_Name                     : constant Name_Id := N + $;
    Name_uTrace_Sp                      : constant Name_Id := N + $;
    Name_uType_Invariant                : constant Name_Id := N + $;
+   Name_uVariants                      : constant Name_Id := N + $;
 
    --  Names of predefined primitives used in the expansion of dispatching
    --  requeue and select statements, Abort, 'Callable and 'Terminated.
@@ -668,6 +669,7 @@ package Snames is
    Name_Source_Reference               : constant Name_Id := N + $; -- GNAT
    Name_Static_Elaboration_Desired     : constant Name_Id := N + $; -- GNAT
    Name_Stream_Convert                 : constant Name_Id := N + $; -- GNAT
+   Name_Subprogram_Variant             : constant Name_Id := N + $; -- GNAT
    Name_Subtitle                       : constant Name_Id := N + $; -- GNAT
    Name_Suppress_All                   : constant Name_Id := N + $; -- GNAT
    Name_Suppress_Debug_Info            : constant Name_Id := N + $; -- GNAT
@@ -2102,6 +2104,7 @@ package Snames is
       Pragma_Source_Reference,
       Pragma_Static_Elaboration_Desired,
       Pragma_Stream_Convert,
+      Pragma_Subprogram_Variant,
       Pragma_Subtitle,
       Pragma_Suppress_All,
       Pragma_Suppress_Debug_Info,