This patch adds a warning on certain occurrences of Subprogram'Access that
could cause an access-before-elaboration error.

The following example should give a warning when compiled with the -gnatw.f
switch:

% gcc -c -gnatw.f -gnatwe elab_acc.adb
elab_acc.ads:4:31: warning: Access attribute of "F" before body seen
elab_acc.ads:4:31: warning: possible Program_Error on later references
%

package Elab_Acc is
   function F return Integer;
   type Funcy is access function return Integer;
   F_Ptr : constant Funcy := F'Access;
   --  Calls to F_Ptr.all before F's body can cause an
   --  access-before-elaboration error.

end Elab_Acc;

package body Elab_Acc is

   function F return Integer is
   begin
      return 123;
   end F;

end Elab_Acc;

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

2015-01-07  Bob Duff  <d...@adacore.com>

        * sem_elab.adb (Check_Internal_Call_Continue): Give a warning
        for P'Access, where P is a subprogram in the same package as
        the P'Access, and the P'Access is evaluated at elaboration
        time, and occurs before the body of P. For example, "X : T :=
        P'Access;" would allow a subsequent call to X.all to be an
        access-before-elaboration error; hence the warning. This warning
        is enabled by the -gnatw.f switch.
        * opt.ads (Warn_On_Elab_Access): New flag for warning switch.
        * warnsw.adb (Set_Dot_Warning_Switch): Set Warn_On_Elab_Access.
        * gnat_ugn.texi: Document the new warning.

Index: gnat_ugn.texi
===================================================================
--- gnat_ugn.texi       (revision 219252)
+++ gnat_ugn.texi       (working copy)
@@ -5048,6 +5048,23 @@
 effect of warning on unreferenced entities other than subprogram
 formals.
 
+@item -gnatw.f
+@emph{Activate warnings on suspicious subprogram 'Access.}
+@cindex @option{-gnatw.f} (@command{gcc})
+This switch causes a warning to be generated if @code{P'Access} occurs
+in the same package where subprogram P is declared, and the
+@code{P'Access} is evaluated at elaboration time, and occurs before
+the body of P has been elaborated. For example, if we have
+@code{X : T := P'Access;}, then if X.all is subsequently called before
+the body of P is elaborated, it could cause
+access-before-elaboration. The default is that these warnings are not
+generated.
+
+@item -gnatw.F
+@emph{Suppress warnings on suspicious subprogram 'Access.}
+@cindex @option{-gnatw.F} (@command{gcc})
+This switch suppresses warnings for suspicious subprogram 'Access.
+
 @item -gnatwg
 @emph{Activate warnings on unrecognized pragmas.}
 @cindex @option{-gnatwg} (@command{gcc})
Index: warnsw.adb
===================================================================
--- warnsw.adb  (revision 219191)
+++ warnsw.adb  (working copy)
@@ -326,6 +326,12 @@
          when 'e' =>
             All_Warnings (True);
 
+         when 'f' =>
+            Warn_On_Elab_Access                 := True;
+
+         when 'F' =>
+            Warn_On_Elab_Access                 := False;
+
          when 'g' =>
             Set_GNAT_Mode_Warnings;
 
Index: sem_elab.adb
===================================================================
--- sem_elab.adb        (revision 219191)
+++ sem_elab.adb        (working copy)
@@ -1990,10 +1990,21 @@
       Inst_Case : constant Boolean := Nkind (N) in N_Generic_Instantiation;
 
    begin
-      --  If not function or procedure call or instantiation, then ignore
-      --  call (this happens in some error cases and rewriting cases).
+      --  For P'Access, we want to warn if the -gnatw.f switch is set, and the
+      --  node comes from source.
 
-      if not Nkind_In (N, N_Function_Call, N_Procedure_Call_Statement)
+      if Nkind (N) = N_Attribute_Reference and then
+        (not Warn_On_Elab_Access or else not Comes_From_Source (N))
+      then
+         return;
+
+      --  If not function or procedure call, instantiation, or 'Access, then
+      --  ignore call (this happens in some error cases and rewriting cases).
+
+      elsif not Nkind_In
+               (N, N_Function_Call,
+                   N_Procedure_Call_Statement,
+                   N_Attribute_Reference)
         and then not Inst_Case
       then
          return;
@@ -2001,7 +2012,7 @@
       --  Nothing to do if this is a call or instantiation that has already
       --  been found to be a sure ABE.
 
-      elsif ABE_Is_Certain (N) then
+      elsif Nkind (N) /= N_Attribute_Reference and then ABE_Is_Certain (N) then
          return;
 
       --  Nothing to do if errors already detected (avoid cascaded errors)
@@ -2323,7 +2334,7 @@
       --  Not that special case, warning and dynamic check is required
 
       --  If we have nothing in the call stack, then this is at the outer
-      --  level, and the ABE is bound to occur.
+      --  level, and the ABE is bound to occur, unless it's a 'Access.
 
       if Elab_Call.Last = 0 then
          Error_Msg_Warn := SPARK_Mode /= On;
@@ -2331,13 +2342,19 @@
          if Inst_Case then
             Error_Msg_NE
               ("cannot instantiate& before body seen<<", N, Orig_Ent);
+         elsif Nkind (N) /= N_Attribute_Reference then
+            Error_Msg_NE
+              ("cannot call& before body seen<<", N, Orig_Ent);
          else
             Error_Msg_NE
-              ("cannot call& before body seen<<", N, Orig_Ent);
+              ("Access attribute of & before body seen<<", N, Orig_Ent);
+            Error_Msg_N ("\possible Program_Error on later references<", N);
          end if;
 
-         Error_Msg_N ("\Program_Error [<<", N);
-         Insert_Elab_Check (N);
+         if Nkind (N) /= N_Attribute_Reference then
+            Error_Msg_N ("\Program_Error [<<", N);
+            Insert_Elab_Check (N);
+         end if;
 
       --  Call is not at outer level
 
Index: opt.ads
===================================================================
--- opt.ads     (revision 219280)
+++ opt.ads     (working copy)
@@ -1669,6 +1669,13 @@
    --  Set to True to generate warnings for suspicious use of export or
    --  import pragmas. Modified by use of -gnatwx/X.
 
+   Warn_On_Elab_Access : Boolean := False;
+   --  GNAT
+   --  Set to True to generate warnings for P'Access in the case where
+   --  subprogram P is in the same package as the P'Access, and the P'Access is
+   --  evaluated at package elaboration time, and occurs before the body of P
+   --  has been elaborated.
+
    Warn_On_Hiding : Boolean := False;
    --  GNAT
    --  Set to True to generate warnings if a declared entity hides another

Reply via email to