diff mbox

[Ada] Handling of SPARK aspects/pragmas on subprogram body stubs

Message ID 20140225150544.GA16318@adacore.com
State New
Headers show

Commit Message

Arnaud Charlet Feb. 25, 2014, 3:05 p.m. UTC
This patch reimplements the support for SPARK aspects/pragmas that apply to a
subprogram body stub and implements a missing rule which forbids the placement
of refinement annotations in subunits.

------------
-- Source --
------------

--  error.ads

package Error
  with SPARK_Mode     => On,
       Abstract_State => State
is
   procedure Spec_Stub_Body_1
     with Global => (In_Out => State);

   procedure Spec_Stub_Body_2
     with Global  => (In_Out => State),
          Depends => (State  => State);

   procedure Spec_Stub_Body_3
     with Global  => (In_Out => State),
          Depends => (State  => State);

   procedure Spec_Stub_Body_4
     with Global  => (In_Out => State),
          Depends => (State  => State);

   procedure Spec_Stub_Body_5
     with Global  => (In_Out => State),
          Depends => (State  => State);
end Error;

--  error.adb

package body Error
  with SPARK_Mode    => On,
       Refined_State => (State => (A, B))
is
   A : Integer := 1;
   B : Integer := 2;

   procedure Spec_Stub_Body_1 is separate
     with Depends => (A => B);  --  error
   --  Depends must appear on the spec (first declaration)

   procedure Spec_Stub_Body_2 is separate
     with Refined_Global => (In_Out => (A, B));
   --  Refined_Depends must appear on the stub (second declaration)

   procedure Spec_Stub_Body_3 is separate;
   --  Refined_Global and Refined_Depends must appear on the stub (second
   --  declaration).

   procedure Spec_Stub_Body_4 is separate
     with Refined_Global  => (In_Out  => (A, "error")),
          Refined_Depends => ("error" => B);
   --  Refined_Global and Refined_Depends are placed properly, but malformed

   procedure Spec_Stub_Body_5 is separate
     with Refined_Global  => (In_Out  => (A, "error")),
          Refined_Depends => ("error" => B);
   --  Refined_Global and Refined_Depends are placed properly, but malformed. A
   --  proper body is also missing.

   procedure Stub_Body is separate
     with Global  => (In_Out => (A, B)),
          Depends => (A => B);
   --  Refined_Global and Refined_Depends apply to a body whose spec (the
   --  stub) is not visible.
end Error;

--  error-spec_stub_body_1.adb

separate (Error)

procedure Spec_Stub_Body_1 is begin null; end Spec_Stub_Body_1;

--  error-spec_stub_body_2.adb

separate (Error)

procedure Spec_Stub_Body_2
  with Refined_Depends => (A => B)  --  error
is begin null; end Spec_Stub_Body_2;

--  error-spec_stub_body_3.adb

separate (Error)

procedure Spec_Stub_Body_3
  with Refined_Global  => (In_Out => (A, B)),  --  error
       Refined_Depends => (A => B)             --  error
is begin null; end Spec_Stub_Body_3;

--  error-spec_stub_body_4.adb

separate (Error)

procedure Spec_Stub_Body_4 is begin null; end Spec_Stub_Body_4;

----------------------------
-- Compilation and output --
----------------------------

$ gcc -c error.adb
error.adb:9:11: aspect specification must appear in subprogram declaration
error.adb:25:04: warning: subunit "Error.Spec_Stub_Body_5" in file
  "error-spec_stub_body_5.adb" not found
error-spec_stub_body_2.adb:4:08: aspect "Refined_Depends" cannot apply to a
  subunit
error-spec_stub_body_3.adb:4:08: aspect "Refined_Global" cannot apply to a
  subunit
error-spec_stub_body_3.adb:5:08: aspect "Refined_Depends" cannot apply to a
  subunit
error-stub_body.adb:4:08: aspect "Refined_Global" cannot apply to a subunit
error-stub_body.adb:5:08: aspect "Refined_Depends" cannot apply to a subunit

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

2014-02-25  Hristian Kirtchev  <kirtchev@adacore.com>

	* exp_ch6.adb (Add_Or_Save_Precondition): New routine.
	(Collect_Body_Postconditions_In_Decls): New routine.
	(Collect_Body_Postconditions_Of_Kind): Factor out code. Handle
	postcondition aspects or pragmas that appear on a subprogram
	body stub.
	(Collect_Spec_Preconditions): Factor out code. Handle
	precondition aspects or pragmas that appear on a subprogram
	body stub.
	* sem_ch6.adb (Analyze_Subprogram_Body_Helper): The analysis of
	aspects that apply to a subprogram body stub is no longer delayed,
	the aspects are analyzed on the spot.
	(SPARK_Aspect_Error):
	Aspects that apply to a subprogram declaration cannot appear in
	a subunit.
	* sem_ch10.adb Remove with and use clause for Sem_Ch13.
	(Analyze_Proper_Body): Add local variable Comp_Unit. Unum
	is now a local variable. Code cleanup. Analysis related to
	the aspects of a subprogram body stub is now carried out by
	Analyze_Subprogram_Body_Helper. Do not propagate the aspects
	and/or pragmas of a subprogram body stub to the proper body
	as this is no longer needed. Do not analyze the aspects of a
	subprogram stub when the corresponding source unit is missing.
	(Analyze_Protected_Body_Stub): Flag the illegal use of aspects
	on a stub.
	(Analyze_Task_Body_Stub): Flag the illegal use of
	aspects on a stub.
	(Optional_Subunit): Add local variable Unum.
	* sem_ch13.adb (Insert_Delayed_Pragma): Do not analyze a generated
	pragma when it applies to a subprogram body stub.
	* sem_prag.adb (Analyze_Pragma): Pragmas Contract_Cases,
	Depends and Global can now apply to a subprogram body stub as
	long as it acts as its own spec.
	(Analyze_Refined_Pragma):
	Code reformatting. Refinement pragmas cannot apply to a subunit.
diff mbox

Patch

Index: sem_ch10.adb
===================================================================
--- sem_ch10.adb	(revision 208131)
+++ sem_ch10.adb	(working copy)
@@ -53,7 +53,6 @@ 
 with Sem_Ch6;  use Sem_Ch6;
 with Sem_Ch7;  use Sem_Ch7;
 with Sem_Ch8;  use Sem_Ch8;
-with Sem_Ch13; use Sem_Ch13;
 with Sem_Dist; use Sem_Dist;
 with Sem_Prag; use Sem_Prag;
 with Sem_Util; use Sem_Util;
@@ -1558,7 +1557,6 @@ 
 
    procedure Analyze_Proper_Body (N : Node_Id; Nam : Entity_Id) is
       Subunit_Name : constant Unit_Name_Type := Get_Unit_Name (N);
-      Unum         : Unit_Number_Type;
 
       procedure Optional_Subunit;
       --  This procedure is called when the main unit is a stub, or when we
@@ -1573,6 +1571,7 @@ 
 
       procedure Optional_Subunit is
          Comp_Unit : Node_Id;
+         Unum      : Unit_Number_Type;
 
       begin
          --  Try to load subunit, but ignore any errors that occur during the
@@ -1633,7 +1632,8 @@ 
 
       --  Local variables
 
-      Stub_Id : Entity_Id;
+      Comp_Unit : Node_Id;
+      Unum      : Unit_Number_Type;
 
    --  Start of processing for Analyze_Proper_Body
 
@@ -1787,86 +1787,45 @@ 
                   Write_Eol;
                end if;
 
-               declare
-                  Comp_Unit : constant Node_Id := Cunit (Unum);
-                  Prop_Body : Node_Id;
+               Comp_Unit := Cunit (Unum);
 
-               begin
-                  --  Check for child unit instead of subunit
+               --  Check for child unit instead of subunit
 
-                  if Nkind (Unit (Comp_Unit)) /= N_Subunit then
-                     Error_Msg_N
-                       ("expected SEPARATE subunit, found child unit",
-                        Cunit_Entity (Unum));
+               if Nkind (Unit (Comp_Unit)) /= N_Subunit then
+                  Error_Msg_N
+                    ("expected SEPARATE subunit, found child unit",
+                     Cunit_Entity (Unum));
 
-                  --  OK, we have a subunit
+               --  OK, we have a subunit
 
-                  else
-                     Prop_Body := Proper_Body (Unit (Comp_Unit));
+               else
+                  Set_Corresponding_Stub (Unit (Comp_Unit), N);
+                  Set_Library_Unit (N, Comp_Unit);
 
-                     --  Set corresponding stub (even if errors)
+                  --  We update the version. Although we are not technically
+                  --  semantically dependent on the subunit, given our approach
+                  --  of macro substitution of subunits, it makes sense to
+                  --  include it in the version identification.
 
-                     Set_Corresponding_Stub (Unit (Comp_Unit), N);
+                  Version_Update (Cunit (Main_Unit), Comp_Unit);
 
-                     --  Collect SCO information for loaded subunit if we are
-                     --  in the main unit.
+                  --  Collect SCO information for loaded subunit if we are in
+                  --  the main unit.
 
-                     if Generate_SCO
-                       and then
-                         In_Extended_Main_Source_Unit
-                           (Cunit_Entity (Current_Sem_Unit))
-                     then
-                        SCO_Record (Unum);
-                     end if;
-
-                     --  Propagate all aspect specifications associated with
-                     --  the stub to the proper body.
-
-                     Move_Or_Merge_Aspects (From => N, To => Prop_Body);
-
-                     --  Move all source pragmas that follow the body stub and
-                     --  apply to it to the declarations of the proper body.
-
-                     if Nkind (N) = N_Subprogram_Body_Stub then
-                        Relocate_Pragmas_To_Body (N, Target_Body => Prop_Body);
-                     end if;
-
-                     --  Analyze the unit if semantics active
-
-                     if not Fatal_Error (Unum) or else Try_Semantics then
-                        Analyze_Subunit (Comp_Unit);
-                     end if;
-
-                     --  Set the library unit pointer in any case
-
-                     Set_Library_Unit (N, Comp_Unit);
-
-                     --  We update the version. Although we are not technically
-                     --  semantically dependent on the subunit, given our
-                     --  approach of macro substitution of subunits, it makes
-                     --  sense to include it in the version identification.
-
-                     Version_Update (Cunit (Main_Unit), Comp_Unit);
+                  if Generate_SCO
+                    and then
+                      In_Extended_Main_Source_Unit
+                        (Cunit_Entity (Current_Sem_Unit))
+                  then
+                     SCO_Record (Unum);
                   end if;
-               end;
 
-            --  The unit which should contain the proper subprogram body does
-            --  not exist. Analyze the aspect specifications of the stub (if
-            --  any).
+                  --  Analyze the unit if semantics active
 
-            elsif Nkind (N) = N_Subprogram_Body_Stub
-              and then Has_Aspects (N)
-            then
-               Stub_Id := Defining_Unit_Name (Specification (N));
-
-               --  Restore the proper visibility of the stub and its formals
-
-               Push_Scope (Stub_Id);
-               Install_Formals (Stub_Id);
-
-               Analyze_Aspect_Specifications (N, Stub_Id);
-
-               Pop_Scope;
+                  if not Fatal_Error (Unum) or else Try_Semantics then
+                     Analyze_Subunit (Comp_Unit);
+                  end if;
+               end if;
             end if;
          end if;
 
@@ -1901,6 +1860,17 @@ 
          Error_Msg_N ("missing specification for Protected body", N);
 
       else
+         --  Currently there are no language-defined aspects that can apply to
+         --  a protected body stub. Issue an error and remove the aspects to
+         --  prevent cascaded errors.
+
+         if Has_Aspects (N) then
+            Error_Msg_N
+              ("aspects on protected bodies are not allowed",
+               First (Aspect_Specifications (N)));
+            Remove_Aspects (N);
+         end if;
+
          Set_Scope (Defining_Entity (N), Current_Scope);
          Set_Has_Completion (Etype (Nam));
          Set_Corresponding_Spec_Of_Stub (N, Nam);
@@ -2351,7 +2321,19 @@ 
 
       if No (Nam) or else not Is_Task_Type (Etype (Nam)) then
          Error_Msg_N ("missing specification for task body", N);
+
       else
+         --  Currently there are no language-defined aspects that can apply to
+         --  a task body stub. Issue an error and remove the aspects to prevent
+         --  cascaded errors.
+
+         if Has_Aspects (N) then
+            Error_Msg_N
+              ("aspects on task bodies are not allowed",
+               First (Aspect_Specifications (N)));
+            Remove_Aspects (N);
+         end if;
+
          Set_Scope (Defining_Entity (N), Current_Scope);
          Generate_Reference (Nam, Defining_Identifier (N), 'b');
          Set_Corresponding_Spec_Of_Stub (N, Nam);
Index: sem_prag.adb
===================================================================
--- sem_prag.adb	(revision 208131)
+++ sem_prag.adb	(working copy)
@@ -3601,40 +3601,42 @@ 
 
          Body_Decl := Find_Related_Subprogram_Or_Body (N, Do_Checks => True);
 
-         if not Nkind_In (Body_Decl, N_Subprogram_Body,
-                                     N_Subprogram_Body_Stub)
-         then
-            Pragma_Misplaced;
-            return;
-         end if;
+         --  Extract the entities of the spec and body
 
-         Body_Id := Defining_Entity (Body_Decl);
+         if Nkind (Body_Decl) = N_Subprogram_Body then
+            Body_Id := Defining_Entity (Body_Decl);
+            Spec_Id := Corresponding_Spec (Body_Decl);
 
-         --  The body [stub] must not act as a spec, in other words it has to
-         --  be paired with a corresponding spec.
+         elsif Nkind (Body_Decl) = N_Subprogram_Body_Stub then
+            Body_Id := Defining_Entity (Body_Decl);
+            Spec_Id := Corresponding_Spec_Of_Stub (Body_Decl);
 
-         if Nkind (Body_Decl) = N_Subprogram_Body then
-            Spec_Id := Corresponding_Spec (Body_Decl);
          else
-            Spec_Id := Corresponding_Spec_Of_Stub (Body_Decl);
+            Pragma_Misplaced;
+            return;
          end if;
 
+         --  The pragma must apply to the second declaration of a subprogram.
+         --  In other words, the body [stub] cannot acts as a spec.
+
          if No (Spec_Id) then
             Error_Pragma ("pragma % cannot apply to a stand alone body");
             return;
+
+         --  Catch the case where the subprogram body is a subunit and acts as
+         --  the third declaration of the subprogram.
+
+         elsif Nkind (Parent (Body_Decl)) = N_Subunit then
+            Error_Pragma ("pragma % cannot apply to a subunit");
+            return;
          end if;
 
-         --  The pragma may only apply to the body [stub] of a subprogram
+         --  The pragma can only apply to the body [stub] of a subprogram
          --  declared in the visible part of a package. Retrieve the context of
          --  the subprogram declaration.
 
          Spec_Decl := Parent (Parent (Spec_Id));
 
-         pragma Assert
-           (Nkind_In (Spec_Decl, N_Abstract_Subprogram_Declaration,
-                                 N_Generic_Subprogram_Declaration,
-                                 N_Subprogram_Declaration));
-
          if Nkind (Parent (Spec_Decl)) /= N_Package_Specification then
             Error_Pragma
               ("pragma % must apply to the body of a subprogram declared in a "
@@ -12445,10 +12447,24 @@ 
             Subp_Decl :=
               Find_Related_Subprogram_Or_Body (N, Do_Checks => True);
 
-            if Nkind (Subp_Decl) /= N_Subprogram_Declaration
-              and then (Nkind (Subp_Decl) /= N_Subprogram_Body
-                         or else not Acts_As_Spec (Subp_Decl))
+            if Nkind (Subp_Decl) = N_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;
+
+            else
                Pragma_Misplaced;
                return;
             end if;
@@ -12969,10 +12985,24 @@ 
             Subp_Decl :=
               Find_Related_Subprogram_Or_Body (N, Do_Checks => True);
 
-            if Nkind (Subp_Decl) /= N_Subprogram_Declaration
-              and then (Nkind (Subp_Decl) /= N_Subprogram_Body
-                          or else not Acts_As_Spec (Subp_Decl))
+            if Nkind (Subp_Decl) = N_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;
+
+            else
                Pragma_Misplaced;
                return;
             end if;
@@ -14239,10 +14269,24 @@ 
             Subp_Decl :=
               Find_Related_Subprogram_Or_Body (N, Do_Checks => True);
 
-            if Nkind (Subp_Decl) /= N_Subprogram_Declaration
-              and then (Nkind (Subp_Decl) /= N_Subprogram_Body
-                          or else not Acts_As_Spec (Subp_Decl))
+            if Nkind (Subp_Decl) = N_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;
+
+            else
                Pragma_Misplaced;
                return;
             end if;
Index: exp_ch6.adb
===================================================================
--- exp_ch6.adb	(revision 208067)
+++ exp_ch6.adb	(working copy)
@@ -8671,58 +8671,100 @@ 
 
       procedure Collect_Body_Postconditions (Stmts : in out List_Id) is
          procedure Collect_Body_Postconditions_Of_Kind (Post_Nam : Name_Id);
-         --  Process postconditions of a particular kind denoted by Post_Nam
+         --  Process all postconditions of a particular kind denoted by
+         --  Post_Nam.
 
          -----------------------------------------
          -- Collect_Body_Postconditions_Of_Kind --
          -----------------------------------------
 
          procedure Collect_Body_Postconditions_Of_Kind (Post_Nam : Name_Id) is
-            Check_Prag : Node_Id;
-            Decl       : Node_Id;
+            procedure Collect_Body_Postconditions_In_Decls
+              (First_Decl : Node_Id);
+            --  Process all postconditions found in a declarative list starting
+            --  with declaration First_Decl.
 
-         begin
-            pragma Assert (Nam_In (Post_Nam, Name_Postcondition,
-                                             Name_Refined_Post));
+            ------------------------------------------
+            -- Collect_Body_Postconditions_In_Decls --
+            ------------------------------------------
 
-            --  Inspect the declarations of the subprogram body looking for a
-            --  pragma that matches the desired name.
+            procedure Collect_Body_Postconditions_In_Decls
+              (First_Decl : Node_Id)
+            is
+               Check_Prag : Node_Id;
+               Decl       : Node_Id;
 
-            Decl := First (Declarations (N));
-            while Present (Decl) loop
-               if Nkind (Decl) = N_Pragma then
-                  if Pragma_Name (Decl) = Post_Nam then
-                     Analyze (Decl);
-                     Check_Prag := Build_Pragma_Check_Equivalent (Decl);
+            begin
+               --  Inspect the declarative list looking for a pragma that
+               --  matches the desired name.
 
-                     if Expander_Active then
-                        Append_Enabled_Item
-                          (Item => Check_Prag,
-                           List => Stmts);
+               Decl := First_Decl;
+               while Present (Decl) loop
 
-                     --  When analyzing a generic unit, save the pragma for
-                     --  later.
+                  --  Note that non-matching pragmas are skipped
 
-                     else
-                        Prepend_To_Declarations (Check_Prag);
+                  if Nkind (Decl) = N_Pragma then
+                     if Pragma_Name (Decl) = Post_Nam then
+                        if not Analyzed (Decl) then
+                           Analyze (Decl);
+                        end if;
+
+                        Check_Prag := Build_Pragma_Check_Equivalent (Decl);
+
+                        if Expander_Active then
+                           Append_Enabled_Item
+                             (Item => Check_Prag,
+                              List => Stmts);
+
+                        --  When analyzing a generic unit, save the pragma for
+                        --  later.
+
+                        else
+                           Prepend_To_Declarations (Check_Prag);
+                        end if;
                      end if;
+
+                  --  Skip internally generated code
+
+                  elsif not Comes_From_Source (Decl) then
+                     null;
+
+                  --  Postcondition pragmas are usually grouped together. There
+                  --  is no need to inspect the whole declarative list.
+
+                  else
+                     exit;
                   end if;
 
-               --  Skip internally generated code
+                  Next (Decl);
+               end loop;
+            end Collect_Body_Postconditions_In_Decls;
 
-               elsif not Comes_From_Source (Decl) then
-                  null;
+            --  Local variables
 
-               --  Postconditions in bodies are usually grouped at the top of
-               --  the declarations. There is no point in inspecting the whole
-               --  source list.
+            Unit_Decl : constant Node_Id := Parent (N);
 
-               else
-                  exit;
-               end if;
+         --  Start of processing for Collect_Body_Postconditions_Of_Kind
 
-               Next (Decl);
-            end loop;
+         begin
+            pragma Assert (Nam_In (Post_Nam, Name_Postcondition,
+                                             Name_Refined_Post));
+
+            --  Inspect the declarations of the subprogram body looking for a
+            --  pragma that matches the desired name.
+
+            Collect_Body_Postconditions_In_Decls
+              (First_Decl => First (Declarations (N)));
+
+            --  The subprogram body being processed is actually the proper body
+            --  of a stub with a corresponding spec. The subprogram stub may
+            --  carry a postcondition pragma in which case it must be taken
+            --  into account. The pragma appears after the stub.
+
+            if Present (Spec_Id) and then Nkind (Unit_Decl) = N_Subunit then
+               Collect_Body_Postconditions_In_Decls
+                 (First_Decl => Next (Corresponding_Stub (Unit_Decl)));
+            end if;
          end Collect_Body_Postconditions_Of_Kind;
 
       --  Start of processing for Collect_Body_Postconditions
@@ -8808,11 +8850,45 @@ 
       --------------------------------
 
       procedure Collect_Spec_Preconditions (Subp_Id : Entity_Id) is
+         Class_Pre : Node_Id := Empty;
+         --  The sole class-wide precondition pragma that applies to the
+         --  subprogram.
+
+         procedure Add_Or_Save_Precondition (Prag : Node_Id);
+         --  Save a class-wide precondition or add a regulat precondition to
+         --  the declarative list of the body.
+
          procedure Merge_Preconditions (From : Node_Id; Into : Node_Id);
          --  Merge two class-wide preconditions by "or else"-ing them. The
          --  changes are accumulated in parameter Into. Update the error
          --  message of Into.
 
+         ------------------------------
+         -- Add_Or_Save_Precondition --
+         ------------------------------
+
+         procedure Add_Or_Save_Precondition (Prag : Node_Id) is
+            Check_Prag : Node_Id;
+
+         begin
+            Check_Prag := Build_Pragma_Check_Equivalent (Prag);
+
+            --  Save the sole class-wide precondition (if any) for the next
+            --  step where it will be merged with inherited preconditions.
+
+            if Class_Present (Prag) then
+               pragma Assert (No (Class_Pre));
+               Class_Pre := Check_Prag;
+
+            --  Accumulate the corresponding Check pragmas to the top of the
+            --  declarations. Prepending the items ensures that they will be
+            --  evaluated in their original order.
+
+            else
+               Prepend_To_Declarations (Check_Prag);
+            end if;
+         end Add_Or_Save_Precondition;
+
          -------------------------
          -- Merge_Preconditions --
          -------------------------
@@ -8889,8 +8965,9 @@ 
 
          Inher_Subps   : constant Subprogram_List :=
                            Inherited_Subprograms (Subp_Id);
+         Subp_Decl     : constant Node_Id := Parent (Parent (Subp_Id));
          Check_Prag    : Node_Id;
-         Class_Pre     : Node_Id := Empty;
+         Decl          : Node_Id;
          Inher_Subp_Id : Entity_Id;
          Prag          : Node_Id;
 
@@ -8902,25 +8979,49 @@ 
          Prag := Pre_Post_Conditions (Contract (Subp_Id));
          while Present (Prag) loop
             if Pragma_Name (Prag) = Name_Precondition then
-               Check_Prag := Build_Pragma_Check_Equivalent (Prag);
+               Add_Or_Save_Precondition (Prag);
+            end if;
 
-               --  Save the sole class-wide precondition (if any) for the next
-               --  step where it will be merged with inherited preconditions.
+            Prag := Next_Pragma (Prag);
+         end loop;
 
-               if Class_Present (Prag) then
-                  Class_Pre := Check_Prag;
+         --  The subprogram declaration being processed is actually a body
+         --  stub. The stub may carry a precondition pragma in which case it
+         --  must be taken into account. The pragma appears after the stub.
 
-               --  Accumulate the corresponding Check pragmas to the top of the
-               --  declarations. Prepending the items ensures that they will
-               --  be evaluated in their original order.
+         if Nkind (Subp_Decl) = N_Subprogram_Body_Stub then
 
+            --  Inspect the declarations following the body stub
+
+            Decl := Next (Subp_Decl);
+            while Present (Decl) loop
+
+               --  Note that non-matching pragmas are skipped
+
+               if Nkind (Decl) = N_Pragma then
+                  if Pragma_Name (Decl) = Name_Precondition then
+                     if not Analyzed (Decl) then
+                        Analyze (Decl);
+                     end if;
+
+                     Add_Or_Save_Precondition (Decl);
+                  end if;
+
+               --  Skip internally generated code
+
+               elsif not Comes_From_Source (Decl) then
+                  null;
+
+               --  Preconditions are usually grouped together. There is no need
+               --  to inspect the whole declarative list.
+
                else
-                  Prepend_To_Declarations (Check_Prag);
+                  exit;
                end if;
-            end if;
 
-            Prag := Next_Pragma (Prag);
-         end loop;
+               Next (Decl);
+            end loop;
+         end if;
 
          --  Process the contracts of all inherited subprograms, looking for
          --  class-wide preconditions.
Index: sem_ch6.adb
===================================================================
--- sem_ch6.adb	(revision 208128)
+++ sem_ch6.adb	(working copy)
@@ -2485,9 +2485,18 @@ 
 
             if Has_Aspect (Spec_Id, Asp_Id) then
                Error_Msg_Name_1 := Asp_Nam;
-               Error_Msg_Name_2 := Ref_Nam;
-               Error_Msg_N ("aspect % should be %", Asp);
 
+               --  Subunits cannot carry aspects that apply to a subprogram
+               --  declaration.
+
+               if Nkind (Parent (N)) = N_Subunit then
+                  Error_Msg_N ("aspect % cannot apply to a subunit", Asp);
+
+               else
+                  Error_Msg_Name_2 := Ref_Nam;
+                  Error_Msg_N ("aspect % should be %", Asp);
+               end if;
+
             --  Otherwise the aspect must appear in the spec, not in the body:
 
             --    procedure P;
@@ -2912,28 +2921,16 @@ 
          end if;
       end if;
 
-      --  Language-defined aspects cannot appear in a subprogram body [stub] if
-      --  the subprogram has a separate spec. Certainly implementation-defined
-      --  aspects are allowed to appear (per Aspects_On_Body_Of_Stub_OK).
+      --  Language-defined aspects cannot appear on a subprogram body [stub] if
+      --  the subprogram has a spec. Certain implementation-defined aspects are
+      --  allowed to break this rule (see table Aspect_On_Body_Or_Stub_OK).
 
       if Has_Aspects (N) then
          if Present (Spec_Id)
            and then not Aspects_On_Body_Or_Stub_OK (N)
-
-            --  Do not emit an error on a subprogram body stub that act as
-            --  its own spec.
-
-           and then Nkind (Parent (Parent (Spec_Id))) /= N_Subprogram_Body_Stub
          then
             Diagnose_Misplaced_Aspect_Specifications;
 
-         --  Delay the analysis of aspect specifications that apply to a body
-         --  stub until the proper body is analyzed. If the corresponding body
-         --  is missing, the aspects are still analyzed in Analyze_Proper_Body.
-
-         elsif Nkind (N) in N_Body_Stub then
-            null;
-
          else
             Analyze_Aspect_Specifications (N, Body_Id);
          end if;
Index: sem_ch13.adb
===================================================================
--- sem_ch13.adb	(revision 208087)
+++ sem_ch13.adb	(working copy)
@@ -1234,14 +1234,6 @@ 
 
          else
             Insert_After (N, Prag);
-
-            --  Analyze the pragma before analyzing the proper body of a stub.
-            --  This ensures that the pragma will appear on the proper contract
-            --  list (see N_Contract).
-
-            if Nkind (N) = N_Subprogram_Body_Stub then
-               Analyze (Prag);
-            end if;
          end if;
       end Insert_Delayed_Pragma;