This patch handles properly Ada 2012 loops whose iterator specification is
a qualified expression whose type is derived from an iterator type.

Executing

   gnatmake -q ice_primes_2.adb
   ice_primes_2

must yield:

 3
 5
 7
 11
 13
 17
 19
 23
 29
 31

---
with Ada.Text_IO;
with F552A00_Prime_Numbers; use F552A00_Prime_Numbers;
procedure ICE_Primes_2 is
   It : constant Prime_Number_Set
     := Prime_Number_Set'(Max_Value => 32);
begin
   for P in It
   loop
      Ada.Text_IO.Put_Line (P'Img);
   end loop;
end ICE_Primes_2;
---
with Ada.Iterator_Interfaces;
with Ada.Strings.Unbounded; use Ada;

package F552A00_Prime_Numbers is

   --  This package defines a simple Iterator Type that represents a
   --  set of prime numbers from 1 to N.

   function Is_Prime (Value : Natural) return Boolean;

   package Prime_Number_Iterator is new Ada.Iterator_Interfaces (
      Cursor      => Natural,
      Has_Element => Is_Prime);

   type Prime_Number_Set (Max_Value : Natural) is new
     Prime_Number_Iterator.Forward_Iterator with null record;
   --  A Prime_Number_Set represents all the prime numbers between
   --  1 and Max_Value. Two is not considered to be a prime number.
   --  Max_Value may or may not be a prime number

   overriding function First (Object : Prime_Number_Set) return Natural;

   overriding function Next
     (Object : Prime_Number_Set;
      Value  : Natural)
      return   Natural;

   function Iterate
     (Set  : Prime_Number_Set)
      return Prime_Number_Iterator.Forward_Iterator'Class;

   TC_Call_History : Strings.Unbounded.Unbounded_String;
   --
   --  A string capturing the call sequence to the above subprogams.
   --  The following gets appended to the history for the above calls;
   --      Iterate  => I
   --      First    => 1
   --      Next     => N( nn)        where nn is the next prime number
   --      Is_Prime => H:{T|F}( nn)  H is the Has_Element function
   --                                  T means Has_Element returned True
   --                                  F means Has_Element returns False
   --                                  nn is the current prime number

end F552A00_Prime_Numbers;
---
with Ada.Numerics.Elementary_Functions; use Ada.Numerics.Elementary_Functions;
package body F552A00_Prime_Numbers is

   Disable_History : Boolean := False;
   --  Used to disable call history for calls to Is_Prime that are internal
   --  and not called as a result of loop iteration.

   function First (Object : Prime_Number_Set) return Natural is
   begin
      --  The first prime number is 3, unless the Prime_Number_Set only
      --  goes up to 2 or less, in which case a non-prime number is returned
      --  since the set does not contain a prime number.

      Ada.Strings.Unbounded.Append (Source => TC_Call_History,
                                    New_Item => '1');
      return (if Object.Max_Value >= 3 then 3 else Object.Max_Value);
   end First;

   function Next
     (Object : Prime_Number_Set;
      Value  : Natural)
      return   Natural is
   begin

      --  Disable logging of calls to Is_Prime inside this loop, since these
      --  calls are not directly related to iterator types.
      Disable_History := True;

      for I in Value + 1 .. Object.Max_Value loop
         if Is_Prime (I) then
            Ada.Strings.Unbounded.Append
              (Source   => TC_Call_History,
               New_Item => "N(" & Integer'Image (I) & ')');
            Disable_History := False;
            return I;
         end if;
      end loop;
      Disable_History := False;

      Ada.Strings.Unbounded.Append
        (Source   => TC_Call_History,
         New_Item => "N(" & Integer'Image (Value + 1) & ')');

      return Value + 1;
   end Next;

   function Is_Prime (Value : Natural) return Boolean is
   begin
      for I in 2 .. Integer (Sqrt (Float (Value))) loop
         if Value mod I = 0 then

            --  Not a prime number
            if not Disable_History then
               Ada.Strings.Unbounded.Append
                 (Source   => TC_Call_History,
                  New_Item => "H:F(" & Integer'Image (Value) & ')');
            end if;

            return False;
         end if;
      end loop;

      --  Is a prime number if the value is > 2.
      if not Disable_History then
         Ada.Strings.Unbounded.Append
           (Source   => TC_Call_History,
            New_Item => "H:" &
                        (if Value > 2 then "T(" else "F(") &
                        Integer'Image (Value) &
                        ')');
      end if;

      return (Value > 2);
   end Is_Prime;

   function Iterate
     (Set  : Prime_Number_Set)
      return Prime_Number_Iterator.Forward_Iterator'Class is
   begin
      Ada.Strings.Unbounded.Append (Source => TC_Call_History,
                                    New_Item => 'I');
      return Result : Prime_Number_Set (Set.Max_Value) do
         null;
      end return;
   end Iterate;

end F552A00_Prime_Numbers;

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

2016-04-18  Ed Schonberg  <schonb...@adacore.com>

        * sem_ch5.adb (Get_Cursor_Type): If iterator type is a derived
        type, the cursor is declared in the scope of the parent type.
        (Analyze_Parameter_Specification): A qualified expression with an
        iterator type indicates an iteration over a container (explicit
        or implicit).

Index: sem_ch5.adb
===================================================================
--- sem_ch5.adb (revision 235093)
+++ sem_ch5.adb (working copy)
@@ -1795,7 +1795,15 @@
          Ent : Entity_Id;
 
       begin
-         Ent := First_Entity (Scope (Typ));
+         --  If iterator type is derived, the cursor is declared in the scope
+         --  of the parent type.
+
+         if Is_Derived_Type (Typ) then
+            Ent := First_Entity (Scope (Etype (Typ)));
+         else
+            Ent := First_Entity (Scope (Typ));
+         end if;
+
          while Present (Ent) loop
             exit when Chars (Ent) = Name_Cursor;
             Next_Entity (Ent);
@@ -2747,8 +2755,9 @@
 
          --  a)  a function call,
          --  b)  an identifier that is not a type,
-         --  c)  an attribute reference 'Old (within a postcondition)
-         --  d)  an unchecked conversion
+         --  c)  an attribute reference 'Old (within a postcondition),
+         --  d)  an unchecked conversion or a qualified expression with
+         --      the proper iterator type.
 
          --  then it is an iteration over a container. It was classified as
          --  a loop specification by the parser, and must be rewritten now
@@ -2758,13 +2767,19 @@
          --  conversion is always an object.
 
          if Nkind (DS_Copy) = N_Function_Call
+
            or else (Is_Entity_Name (DS_Copy)
                      and then not Is_Type (Entity (DS_Copy)))
+
            or else (Nkind (DS_Copy) = N_Attribute_Reference
                      and then Nam_In (Attribute_Name (DS_Copy),
-                                      Name_Old, Name_Loop_Entry))
+                                      Name_Loop_Entry, Name_Old))
+
+           or else Has_Aspect (Etype (DS_Copy), Aspect_Iterable)
+
            or else Nkind (DS_Copy) = N_Unchecked_Type_Conversion
-           or else Has_Aspect (Etype (DS_Copy), Aspect_Iterable)
+           or else (Nkind (DS_Copy) = N_Qualified_Expression
+                     and then Is_Iterator (Etype (DS_Copy)))
          then
             --  This is an iterator specification. Rewrite it as such and
             --  analyze it to capture function calls that may require
@@ -3138,11 +3153,13 @@
                Set_Parent (DS_Copy, Parent (DS));
                Preanalyze_Range (DS_Copy);
 
-               --  Check for a call to Iterate ()
+               --  Check for a call to Iterate () or an expression with
+               --  an iterator type.
 
                return
-                 Nkind (DS_Copy) = N_Function_Call
-                   and then Needs_Finalization (Etype (DS_Copy));
+                 (Nkind (DS_Copy) = N_Function_Call
+                   and then Needs_Finalization (Etype (DS_Copy)))
+                 or else Is_Iterator (Etype (DS_Copy));
             end;
          end if;
       end Is_Container_Iterator;

Reply via email to