Issue with unbounded type declarations and constants

Hi,
I don’t understand how limitations on type and subtype constraints work. I used to think instanciation of unbounded arrays were forbidden, but not the declaration of type, except for constants whole value is known at compile-time, as is the case below.

  pragma Ada_2022;
pragma Extensions_Allowed (On);
with Ada.Text_IO; use Ada.Text_IO;

procedure DISPLAY_INITIALS is
   type Letters is array (1..8) of String;
   M : constant Letters :=
     ["*             *",
      "**           **",
      "* *         * *",
      "*  *       *  *",
      "*   *     *   *",
      "*    *   *    *",
      "*     * *     *",
      "*      *      *"];
   S : constant Letters :=
     ["***************",
      "*             *",
      "*             *",
      "*             *",
      "***************",
      "              *",
      "              *",
      "***************"];
begin
      for LINE of Letters'[for I in Letters'range => S(I) & M(I)] loop
         PUT_LINE(LINE);
      end loop;
end;

I could use add a constraint on the number of column, but then the aggregate at the end would fail.
The only solution I found is

  type Big_Letters is array (1..8) of String(1..33);
   type Letters is array (1..8) of String(1..15);

and then:

for LINE of Big_Letters'[for I in Letters'range => S(I) & "   " & M(I)] loop

But it’s so clunky. Can’t the compiler create bounded anonymous subtypes for the constants and aggregate, considering they are all static variables ?
Why is defining the Letters type without constraints an issue, but an unbounded subtype of String isn’t ?
And I’m not sure I get why I need to qualify the aggregate at all while I usually don’t for say, a sequence of Integer.

Your declaration of Letters is not valid Ada. Either your compiler has an error, or you’re using a compiler for another language that looks a lot like Ada. pragma Extensions_Allowed (On); implies the latter.

pragma-extensions-allowed is a implementation defined pragma of GNAT.

https://docs.adacore.com/gnat_rm-docs/html/gnat_rm/gnat_rm/implementation_defined_pragmas.html#pragma-extensions-allowed

Of course it’s Ada… compiler is GNAT 13.3.0.
Ah ok, so declarations of arrays of unconstrained components are not allowed. Same for records I suppose ?
And am I right that

Vector : Integer_Array := [for J in 1 … 5 => J * 2];

is valid, but the following

for Line of [for I in 1..4 => I] loop
  null;
end loop

isn’t without a qualified expression ?

if it helps:

Vector : Integer_Array := [for J in 1 … 5 => J * 2];

Doesn’t require a qualified expression because it is being assigned to a variable with a known type (Integer_Array), so it knows exactly how to formulate an object from that array aggregate.

for Line of [for I in 1..4 => I] loop
  null;
end loop

There are no types here. Line doesn’t have a type and the array aggregate doesn’t have a type. What type is the compiler supposed to infer here? A 4 byte integer, an 8 byte integer, an integer only in the range of 1…4 or an integer with a much broader range that contains 1…4? Something needs to indicate the expected type for this to work. Ada 2022 adds some syntax to help:

for Line : Integer of [for I in 1..4 => I] loop
  null;
end loop

But it is a new feature and GNAT still has issues unless you do the qualified expression for the aggregate. I don’t know if this is a compiler bug with the new feature or a language bug, but with they type qualification of Line, that should technically be enough to determine the type. Maybe worth generating a question to Adacore to see if intended or a bug and if intended what is the thought process / language req?

Yes, you’re right. This follows from the syntax rules:

The former is an object declaration, and takes an expression:

object_declaration ::=
defining_identifier_list : [aliased ] [constant ] subtype_indication [:= expression]
[aspect_specification];

Whereas the latter is a loop statement where the iterator specification takes a name.

iterator_specification ::=
defining_identifier [: loop_parameter_subtype_indication] in [reverse ] iterator_ name
[iterator_filter]
| defining_identifier [: loop_parameter_subtype_indication] of [reverse ] iterable_ name
[iterator_filter]

A name can be a qualified expression, but not an expression direclty

name ::=
direct_name | explicit_dereference
| indexed_component | slice
| selected_component | attribute_reference
| type_conversion | function_call
| character_literal | qualified_expression
| generalized_reference | generalized_indexing
| target_name

2 Likes

Ok, I think I got it now, you made sense.
However, according to you the following should not be rejected, even though it is

for LINE : String of [for I in Letters'range => S(I) & M(I)] loop
  PUT_LINE(LINE);
 end loop;

The type of Line and the type of the aggregate, and their boundaries, are obvious and inferable. I get display_initials.adb:26:27: error: name expected. Should I use gnat’s github, or is someone here in better position to tell them ?

Yes. Though keep in mind that I may just be naive in thinking that is ok, but my first impression is that should be enough to allow type inference. GNAT has a bugzilla interface through GCC

If you have a github account, you can maybe make an issue or send a message on their github for it.

Alternately you can maybe send an email to Adacore support to ask. I was trying to find the email for that, but can’t seem to locate it (it’s been years since I used it). Maybe someone here has it handy.