diff mbox

[Ada] Class-wide preconditions in dispatching calls.

Message ID 20170427133839.GA51667@adacore.com
State New
Headers show

Commit Message

Arnaud Charlet April 27, 2017, 1:38 p.m. UTC
This patch is a partial implementation of the semantics mandated in AI12-0195
concerning class-wide preconditions on dispatching calls: the precondition
that applies is that of the denoted subprogram entity, not that of the body
that is actually executed.

Tested in ACATS test C611A03

Tested on x86_64-pc-linux-gnu, committed on trunk

2017-04-27  Ed Schonberg  <schonberg@adacore.com>

	* exp_disp.adb (Build_Class_Wide_Check): New subsidiary
	of Expand_Dispatching_Call. If the denoted subprogram has a
	class-wide precondition, this is the only precondition that
	applies to the call, rather that the class-wide preconditions
	that may apply to the body that is executed. (This is specified
	in AI12-0195).
diff mbox

Patch

Index: exp_disp.adb
===================================================================
--- exp_disp.adb	(revision 247320)
+++ exp_disp.adb	(working copy)
@@ -58,6 +58,7 @@ 
 with Sem_Type; use Sem_Type;
 with Sem_Util; use Sem_Util;
 with Sinfo;    use Sinfo;
+with Sinput;   use Sinput;
 with Snames;   use Snames;
 with Stand;    use Stand;
 with Stringt;  use Stringt;
@@ -649,11 +650,112 @@ 
       Eq_Prim_Op      : Entity_Id := Empty;
       Controlling_Tag : Node_Id;
 
+      procedure Build_Class_Wide_Check;
+      --  If the denoted subprogram has a class-wide precondition, generate
+      --  a check using that precondition before the dispatching call, because
+      --  this is the only class-wide precondition that applies to the call.
+
       function New_Value (From : Node_Id) return Node_Id;
       --  From is the original Expression. New_Value is equivalent to a call
       --  to Duplicate_Subexpr with an explicit dereference when From is an
       --  access parameter.
 
+      ----------------------------
+      -- Build_Class_Wide_Check --
+      ----------------------------
+
+      procedure Build_Class_Wide_Check is
+         Prec    : Node_Id;
+         Cond    : Node_Id;
+         Msg     : Node_Id;
+         Str_Loc : constant String := Build_Location_String (Loc);
+
+         function Replace_Formals (N : Node_Id) return Traverse_Result;
+         --  Replace occurrences of the formals of the subprogram by the
+         --  corresponding actuals in the call, given that this check is
+         --  performed outside of the body of the subprogram.
+
+         ---------------------
+         -- Replace_Formals --
+         ---------------------
+
+         function Replace_Formals (N : Node_Id) return Traverse_Result is
+         begin
+            if Is_Entity_Name (N)
+              and then Present (Entity (N))
+              and then Is_Formal (Entity (N))
+            then
+               declare
+                  A : Node_Id;
+                  F : Entity_Id;
+
+               begin
+                  F := First_Formal (Subp);
+                  A := First_Actual (Call_Node);
+                  while Present (F) loop
+                     if F = Entity (N) then
+                        Rewrite (N, New_Copy_Tree (A));
+                        exit;
+                     end if;
+                     Next_Formal (F);
+                     Next_Actual (A);
+                  end loop;
+               end;
+            end if;
+
+            return OK;
+         end Replace_Formals;
+
+         procedure Update is new Traverse_Proc (Replace_Formals);
+      begin
+
+         --  Locate class-wide precondition, if any
+
+         if Present (Contract (Subp))
+           and then Present (Pre_Post_Conditions (Contract (Subp)))
+         then
+            Prec := Pre_Post_Conditions (Contract (Subp));
+
+            while Present (Prec) loop
+               exit when Pragma_Name (Prec) = Name_Precondition
+                 and then Class_Present (Prec);
+               Prec := Next_Pragma (Prec);
+            end loop;
+
+            if No (Prec) then
+               return;
+            end if;
+
+            --  The expression for the precondition is analyzed within the
+            --  generated pragma. The message text is the last parameter
+            --  of the generated pragma, indicating source of precondition.
+
+            Cond := New_Copy_Tree
+              (Expression (First (Pragma_Argument_Associations (Prec))));
+            Update (Cond);
+
+            --  Build message indicating the failed precondition and the
+            --  dispatching call that caused it.
+
+            Msg := Expression (Last (Pragma_Argument_Associations (Prec)));
+            Name_Len := 0;
+            Append (Global_Name_Buffer, Strval (Msg));
+            Append (Global_Name_Buffer, " in dispatching call at ");
+            Append (Global_Name_Buffer, Str_Loc);
+            Msg := Make_String_Literal (Loc, Name_Buffer (1 .. Name_Len));
+
+            Insert_Action (Call_Node,
+               Make_If_Statement (Loc,
+                  Condition => Make_Op_Not (Loc, Cond),
+                  Then_Statements => New_List (
+                     Make_Procedure_Call_Statement (Loc,
+                       Name                   =>
+                         New_Occurrence_Of
+                           (RTE (RE_Raise_Assert_Failure), Loc),
+                       Parameter_Associations => New_List (Msg)))));
+         end if;
+      end Build_Class_Wide_Check;
+
       ---------------
       -- New_Value --
       ---------------
@@ -714,6 +816,8 @@ 
          Subp := Alias (Subp);
       end if;
 
+      Build_Class_Wide_Check;
+
       --  Definition of the class-wide type and the tagged type
 
       --  If the controlling argument is itself a tag rather than a tagged
@@ -1174,7 +1278,7 @@ 
       if not Tagged_Type_Expansion then
          return;
 
-      --  A static conversion to an interface type that is not classwide is
+      --  A static conversion to an interface type that is not class-wide is
       --  curious but legal if the interface operation is a null procedure.
       --  If the operation is abstract it will be rejected later.
 
@@ -1190,7 +1294,7 @@ 
 
       if not Is_Static then
 
-         --  Give error if configurable run time and Displace not available
+         --  Give error if configurable run-time and Displace not available
 
          if not RTE_Available (RE_Displace) then
             Error_Msg_CRT ("dynamic interface conversion", N);