AI22-0130-1

!standard A.18.29(2/5)                                    25-04-18  AI22-0130-1/03

!standard A.18.29(3/3)

!standard A.18.29(4/3)

!standard A.18.31(2/5)

!standard A.18.31(3/3)

!standard A.18.31(4/3)

!class Binding Interpretation 25-03-06

!status Amendment 1-2022  25-03-21

!status WG9 Approved 25-07-18

!status ARG Approved  13-0-0  25-03-19

!status work item 25-03-06

!status received 25-03-06

!assigned author Steve Baird

!submitter Stephen Baird

!priority Low

!difficulty Easy

!qualifier Omission

!subject Capacity of Bounded Queues

!summary

Changes to the predefined generics Bounded_Synchronized_Queues.Queue and Bounded_Priority_Queues.Queues are needed to address Storage_Error problems.

!issue

Default discriminant values are defined for the discriminated types Bounded_Synchronized_Queues.Queue and Bounded_Priority_Queues.Queues,

but an unconstrained component (as opposed to a top-level object) of either of these types is effectively required to be enormous. Typically this means that declaring (or allocating) an object having such a component will raise Storage_Error.

!recommendation

Add a new generic formal parameter to each of the two generics, Max_Capacity, which is then used in declaring a subtype which, in turn, is used as the subtype of the Capacity discriminant of the type Queue (instead of using Count_Type). The new parameters each default to Count_Type’Last; if defaulted, the current behavior is preserved. But specifying a smaller value provides a way to work around the aforementioned Storage_Error problems.

!wording

Modify A.18.29(2/5):

with System;
with Ada.Containers.Synchronized_Queue_Interfaces;
generic
   with package Queue_Interfaces is
      new Ada.Containers.Synchronized_Queue_Interfaces (<>);
   Default_Capacity : Count_Type;
   Default_Ceiling  : System.Any_Priority := System.Priority'Last;
   {Max_Capacity : Count_Type := Count_Type'Last;}
package Ada.Containers.Bounded_Synchronized_Queues
   with Preelaborate,
        Nonblocking, Global => in out synchronized is

Modify A.18.31(2/5):

with System;
with Ada.Containers.Synchronized_Queue_Interfaces;
generic
   with package Queue_Interfaces is
      new Ada.Containers.Synchronized_Queue_Interfaces (<>);
   type Queue_Priority is private;
   with function Get_Priority
     (Element : Queue_Interfaces.Element_Type)
        return Queue_Priority is <>;
   with function Before
     (Left, Right : Queue_Priority) return Boolean is <>;
   Default_Capacity : Count_Type;
   Default_Ceiling  : System.Any_Priority := System.Priority'Last;
   {Max_Capacity : Count_Type := Count_Type'Last;}
package Ada.Containers.Bounded_Priority_Queues
   with Preelaborate,
        Nonblocking, Global => in out synchronized is

 

Add after A.18.29(3/3) and A.18.31(3/3):

 subtype Bounded_Capacity is
Count_Type range Count_Type'First .. Max_Capacity;

Modify A.18.29(4/3) and A.18.31(4/3):

 protected type Queue
        (Capacity : {Bounded_Capacity}[Count_Type] := Default_Capacity;
         Ceiling  : System.Any_Priority := Default_Ceiling)
           with Priority => Ceiling is
        new Queue_Interfaces.Queue with

!discussion

The current specification for Bounded_Synchronized_Queues was based on an incorrect  assumption that if a discriminated protected type has static default discriminant values, then those default values can be treated like a constraint for purposes of computing how much storage is needed for an unconstrained component of that discriminated type (for example, in determining the component size for an array type whose element subtype is an unconstrained subtype of the protected type).

That assumption has since been shown to be incorrect (although an analogous assumption about unconstrained stand-alone objects of the discriminated type is valid). An implementation that relies on this incorrect assumption will typically be unable to correctly evaluate an aggregate of that array type:

  (1 .. 3 => Make (My_Default_Capacity_Value + 1))

Here the Make function uses an extended return statement to return a result whose Capacity_Discriminant is equal to the argument of the function.

 

So, contrary to what was believed when that specification was written, the specification effectively forces many "conventional" Ada implementations to raise Storage_Error when elaborating something like:

package My_SQ_Instance is new

     Ada.Containers.Synchronized_Queue_Interfaces (Job_Type);

package My_BSQ_Instance is new

     Ada.Containers.Bounded_Synchronized_Queues

        (My_SQ_Instance, Default_Capacity => 4);

type R is record

    Q : My_BSQ_Instance.Queue;

end record;

Obj1 : R := (Q => Make (10)); -- Raises Storage_Error (Make as above)

Obj2 : R;                     -- Also raises Storage_Error

 

This is undesirable. To address this problem, a new generic formal parameter Max_Capacity is added and then used to declare a more tightly constrained subtype for the Capacity discriminant. Users who are not encountering any problems with the old specification do not

need to make any changes in their code. Users who are encountering problems (such as raising Storage_Error in a case like the preceding example) can specify smaller Max_Capacity values in their instantiations.

Everything said above about Bounded_Synchronized_Queues also applies to Bounded_Priority_Queues.

This change relies on an assumption that the implementation will take statically known discriminant subtype information into account in determining the size of an unconstrained discriminated subtype, but it will not make matters any worse in the (unlikely) case that this assumption does not hold.

!example

package My_SQ_Instance is new

     Ada.Containers.Synchronized_Queue_Interfaces (Job_Type);

package My_BSQ_Instance is new

     Ada.Containers.Bounded_Synchronized_Queues

        (My_SQ_Instance, Default_Capacity => 4, Max_Capacity => 20);

type R is record

    Q : My_BSQ_Instance.Queue;

end record;

Obj1 : R := (Q => Make (10)); -- OK.

Obj2 : R;                     -- OK.

With the use of the Max_Capacity parameter, no implementation should raise Storage_Error for these object declarations.

!corrigendum A.18.29(2/5)

@drepl

@xcode{@b{with} System;

@b{with} Ada.Containers.Synchronized_Queue_Interfaces;

@b{generic}

   @b{with package} Queue_Interfaces @b{is}

      @b{new} Ada.Containers.Synchronized_Queue_Interfaces (<>);

   Default_Capacity : Count_Type;

   Default_Ceiling  : System.Any_Priority := System.Priority'Last;

@b{package} Ada.Containers.Bounded_Synchronized_Queues

   @b{with} Preelaborate,

        Nonblocking, Global => @b{in out synchronized is}}

@dby

@xcode{@b{with} System;

@b{with} Ada.Containers.Synchronized_Queue_Interfaces;

@b{generic}

   @b{with package} Queue_Interfaces @b{is}

      @b{new} Ada.Containers.Synchronized_Queue_Interfaces (<>);

   Default_Capacity : Count_Type;

   Default_Ceiling  : System.Any_Priority := System.Priority'Last;

   Max_Capacity : Count_Type := Count_Type'Last;

@b{package} Ada.Containers.Bounded_Synchronized_Queues

   @b{with} Preelaborate,

         Nonblocking, Global => @b{in out synchronized is}}

!corrigendum A.18.29(3/3)

@dinsa

@xcode{   @b{package} Implementation @b{is}

      ...  -- @ft{@i{not specified by the language}}

   @b{end} Implementation;}

@dinst

@xcode{   @b{subtype} Bounded_Capacity @b{is}

      Count_Type @b{range} Count_Type'First .. Max_Capacity;}

!corrigendum A.18.29(4/3)

@drepl

@xcode{   @b{protected type} Queue

        (Capacity : Count_Type := Default_Capacity;

         Ceiling  : System.Any_Priority := Default_Ceiling)

           @b{with} Priority => Ceiling @b{is}

        @b{new} Queue_Interfaces.Queue @b{with}}

@dby

@xcode{   @b{protected type} Queue

        (Capacity : Bounded_Capacity := Default_Capacity;

         Ceiling  : System.Any_Priority := Default_Ceiling)

           @b{with} Priority => Ceiling @b{is}

        @b{new} Queue_Interfaces.Queue @b{with}}

!corrigendum A.18.31(2/5)

@drepl

@xcode{@b{with} System;

@b{with} Ada.Containers.Synchronized_Queue_Interfaces;

@b{generic}

   @b{[with package} Queue_Interfaces @b{is}

      @b{new} Ada.Containers.Synchronized_Queue_Interfaces (<>);

   @b{type} Queue_Priority @b{is private};

   @b{with function} Get_Priority

      (Element : Queue_Interfaces.Element_Type) @b{return} Queue_Priority @b{is} <>;

  @b{with function} Before

       (Left, Right : Queue_Priority) @b{return} Boolean @b{is} <>;

   Default_Capacity : Count_Type;

   Default_Ceiling  : System.Any_Priority := System.Priority'Last;

   @b{package} Ada.Containers.Bounded_Priority_Queues

   @b{with} Preelaborate,

        Nonblocking, Global => @b{in out synchronized is}}

@dby

@xcode{@b{with} System;

@b{with} Ada.Containers.Synchronized_Queue_Interfaces;

@b{generic}

   @b{with package} Queue_Interfaces @b{is}

      @b{new} Ada.Containers.Synchronized_Queue_Interfaces (<>);

   @b{type} Queue_Priority @b{is private};

   @b{with function} Get_Priority

     (Element : Queue_Interfaces.Element_Type) @b{return} Queue_Priority @b{is} <>;

   @b{with function} Before

     (Left, Right : Queue_Priority) @b{return} Boolean @b{is} <>;

   Default_Capacity : Count_Type;

   Default_Ceiling  : System.Any_Priority := System.Priority'Last;

   Max_Capacity : Count_Type := Count_Type'Last;

   @b{package} Ada.Containers.Bounded_Priority_Queues

   @b{with} Preelaborate,

        Nonblocking, Global => @b{in out synchronized is}}

!corrigendum A.18.31(3/3)

@dinsa

@xcode{   @b{package} Implementation @b{is}

      ... -- @ft{@i{not specified by the language}}

   @b{end} Implementation;}

@dinst

@xcode{   @b{subtype} Bounded_Capacity @b{is}

      Count_Type @b{range} Count_Type'First .. Max_Capacity;}

!corrigendum A.18.31(4/3)

@drepl

@xcode{   @b{protected type} Queue

        (Capacity : Count_Type := Default_Capacity;

         Ceiling  : System.Any_Priority := Default_Ceiling)

            @b{with} Priority => Ceiling @b{is}

        @b{new} Queue_Interfaces.Queue @b{with}}

@dby

@xcode{   @b{protected type} Queue

        (Capacity : Bounded_Capacity := Default_Capacity;

         Ceiling  : System.Any_Priority := Default_Ceiling)

           @b{with} Priority => Ceiling @b{is}

       @b{new} Queue_Interfaces.Queue @b{with}}

!ACATS test

An ACATS C-test similar to the example would be reasonable. The test could declare instances of both generics. A test that the “(1 .. 3 => …)” aggregate mentioned above is handled correctly might also be appropriate.

!appendix

The original issue was raised in Github Issue #111 (https://github.com/Ada-Rapporteur-Group/User-Community-Input/issues/111).