Version 1.17 of ais/ai-00366.txt

Unformatted version of ais/ai-00366.txt version 1.17
Other versions for file ais/ai-00366.txt

!standard 10.2.1(16)          05-10-11 AI95-00366/11
!standard 3.2(6)
!standard 3.8(9)
!standard 4.8(5)
!standard 10.2.1(17)
!standard 10.2.1(18)
!standard 13.13.2(36/1)
!standard E.2.2(8)
!standard E.2.2(14/1)
!standard E.2.2(17/1)
!standard E.2.3(14/1)
!class amendment 03-12-14
!status Amendment 200Y 04-12-02
!status ARG Approved 10-0-0 04-11-20
!status work item 03-12-14
!status received 03-12-14
!priority Medium
!difficulty Easy
!subject More liberal rules for Pure units
!summary
Pure units allow access-to-subprogram types and access-to-object types for which no storage pool is created. Furthermore, declarations in generic formal parts do not affect the purity of units.
Various places in the RM, notably the semantics of remote types and remote call interfaces in E.2.2 and E.2.3, are fixed to preserve their intended semantics, given the above change. For this purpose, we introduce the concept of types supporting external streaming in 13.13.2.
A missing definition of the term "part of a type" is added in 3.2.
!problem
In pure units access-to-subprogram types and access-to-object types, for which no storage pool is created, are presently prohibited. There is no technical reason for the restriction and many packages that should be pure can not be declared pure.
Furthermore, there is no technical reason for including declarations in generic formal parts for assessing whether a unit is pure.
Independently, a definition for the term "part of a type" is missing in the RM.
!proposal
Allowing access types in pure packages requires changes in the description of pragma Pure, and also of the remote type and remote interface semantics of the language to preserve the prohibition of a semantic dependence on access types that do not support external streaming.
(For details, see wording.)
!wording
Replace 3.2(6):
The term subcomponent is used in this International Standard in place of the term component to indicate either a component, or a component of another subcomponent. Where other subcomponents are excluded, the term component is used instead. Similarly, a part of an object or value is used to mean the whole object or value, or any set of its subcomponents.
by:
The term subcomponent is used in this International Standard in place of the term component to indicate either a component, or a component of another subcomponent. Where other subcomponents are excluded, the term component is used instead. Similarly, a part of an object or value is used to mean the whole object or value, or any set of its subcomponents. The terms component, subcomponent, and part are also applied to a type meaning the component, subcomponent, or part of objects and values of the type.
Note for Corrigendum: "component" is incorrectly italicised in 3.8(9).
Added after 4.8(5):
An allocator shall not be of an access type for which the Storage_Size has been specified by a static expression with value zero or is defined by the language to be zero. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit. This rule does not apply in the body of a generic unit or within a body declared within the declarative region of a generic unit, if the type of the allocator is a descendant of a formal access type declared within the formal part of the generic unit.
AARM NOTE: The last sentence covers the case of children of generics, and formal access types of formal packages of the generic unit.
Replace 10.2.1(16)
A pure library_item is a preelaborable library_item that does not contain the declaration of any variable or named access type, except within a subprogram, generic subprogram, task unit, or protected unit.
by:
Static Semantics
A pure library_item is a preelaborable library_item whose elaboration does not perform any of the following actions:
* the elaboration of a variable declaration;
* the evaluation of an allocator of an access-to-variable type; for the purposes of this rule, the partial view of a type is presumed to have non-visible components whose default initialization evaluates such an allocator;
QUESTION: Can we instead require that a type with preelaborable initialization not evaluate an allocator of an access- to-variable type as part of its default initialization? in Ada 95, types with preelaborable initialization were allowed to have access discriminants with a default being an allocator.
* the elaboration of the declaration of a named access-to-variable type unless the Storage_Size of the type has been specified by a static expression with value zero or is defined by the language to be zero;
* the elaboration of the declaration of a named access-to-constant type for which the Storage_Size has been specified by an expression other than a static expression with value zero.
AARM NOTE: We allow access-to-constant types so long as there is no user-specified non-zero Storage_Size; if there were a user-specified non-zero Storage_Size restricting the size of the storage pool, allocators would be problematic since the package is supposedly "stateless", and the allocated size count for the storage pool would represent state.
The Storage_Size for an anonymous access-to-variable type declared at library level in a library unit that is declared pure is defined to be zero.
Legality Rules
Replace 10.2.1(17)
A pragma Pure is used to declare that a library unit is pure. If a pragma Pure applies to a library unit, then its compilation units shall be pure, and they shall depend semantically only on compilation units of other library units that are declared pure.
by: A pragma Pure is used to declare that a library unit is pure. if a pragma Pure applies to a library unit, then its compilation units shall be pure, and they shall depend semantically only on compilation units of other library units that are declared pure. Furthermore, the full view of any nonlimited partial view declared in the visible part of the library unit that has any available stream attributes shall support external streaming (see 13.13.2).
Replace 10.2.1(18)
If a library unit is declared pure, then the implementation is permitted to omit a call on a library-level subprogram of the library unit if the results are not needed after the call. Similarly, it may omit such a call and simply reuse the results produced by an earlier call on the same subprogram, provided that none of the parameters are of a limited type, and the addresses and values of all by-reference actual parameters, and the values of all by-copy-in actual parameters, are the same as they were at the earlier call. This permission applies even if the subprogram produces other side effects when called.
by:
if a library unit is declared pure, then the implementation is permitted to omit a call on a library-level subprogram of the library unit if the results are not needed after the call. in addition, the implementation may omit a call on such a subprogram and simply reuse the results produced by an earlier call on the same subprogram, provided that none of the parameters nor any object accessible via access values from the parameters are of a limited type, and the addresses and values of all by-reference actual parameters, the values of all by-copy-in actual parameters, and the values of all objects accessible via access values from the parameters, are the same as they were at the earlier call. This permission applies even if the subprogram produces other side effects when called.
Add after 13.13.2(36/1):
A type is said to support external streaming if Read and Write attributes are provided for sending values of such a type between active partitions, with Write marshalling the representation, and Read unmarshalling the representation.
A limited type supports external streaming only if it has available Read and Write attributes. A type with a part that is of an access type supports external streaming only if that access type or the type of some part that includes the access type component, has Read and Write attributes that have been specified via an attribute_definition_clause, and that attribute_definition_clause is visible. [An anonymous access type does not support external streaming.] All other types support external streaming.
Add after the above an AARM note:
A limited type with a part that is of an access type needs to satisfy both rules.
Replace E.2.2(8):
if the full view of a type declared in the visible part of the library unit has a part that is of a non-remote access type, then that access type, or the type of some part that includes the access type subcomponent, shall have user-specified Read and Write attributes.
by
the full view of each type declared in the visible part of the library unit that has any available stream attributes shall support external streaming (see 13.13.2).
Replace E.2.2(14/1):
The primitive subprograms of the corresponding specific limited private type shall only have access parameters if they are controlling formal parameters; each non-controlling formal parameter shall have either a nonlimited type or a type with Read and Write attributes specified via an attribute_definition_clause;
by
The primitive subprograms of the corresponding specific limited private type shall only have access parameters if they are controlling formal parameters; each non-controlling formal parameter shall support external streaming (see 13.13.2);
Replace E.2.2(17/1)
The Storage_Pool and Storage_Size attributes are not defined for remote access-to-class-wide types; the expected type for an allocator shall not be a remote access-to-class-wide type; a remote access-to-class-wide type shall not be an actual parameter for a generic formal access type.
by
The Storage_Pool attribute is not defined for a remote access-to-class-wide type; the expected type for an allocator shall not be a remote access-to-class-wide type. A remote access-to-class-wide type shall not be an actual parameter for a generic formal access type. The Storage_Size attribute of a remote access-to-class-wide type yields 0; it is not allowed in an attribute_definition_clause.
Change E.2.3(14/1) from
* it shall not be, nor shall its visible part contain, a subprogram (or access-to-subprogram) declaration whose profile has an access parameter, or a formal parameter of a limited type unless that limited type has user-specified Read and Write attributes;
to
* it shall not be, nor shall its visible part contain, a subprogram (or access-to-subprogram) declaration whose profile has [an access parameter or] a parameter of a type that does not support external streaming (see 13.13.2);
Replace AARM E.2.3(11.a)
Reason: We disallow the declaration of task and protected types, since calling an entry or a protected subprogram implicitly passes an object of a limited type (the target task or protected object). We disallow other limited types since we require that such types have user-defined Read and Write attributes, but we certainly don't want the Read and Write attributes themselves to involve remote calls (thereby defeating their purpose of marshalling the value for remote calls).
by
Reason: We disallow the declaration of task and protected types, since calling an entry or a protected subprogram implicitly passes an object of a limited type (the target task or protected object). We disallow other limited types since we require that such types have available Read and Write attributes, but we certainly don't want the Read and Write attributes themselves to involve remote calls (thereby defeating their purpose of marshalling the value for remote calls).
!discussion
Pure implies that a unit has no state. Access-to-object types are associated with storage pools, which constitute state. However, access-to-subprogram types have no such implications. Hence they should not be excluded.
Beyond access-to-subprogram types, some packages have a need for an access-to-object type, without a need for a storage pool for the type, however. Setting the Storage_Size of the type to zero implies that no storage pool (and hence no state) needs to be created. The wording is intentionally formulated to allow for easy static checking whether the Storage_Size is zero.
Declarations in generic formal parts do not establish state and hence should be added to the enumerated exclusions of the restriction on the presence of certain declarations. (State comes with the instantiation of the respective generic units.)
The proposed !wording reflects these observations and removes the unnecessary restrictions.
Allowing access types in Pure units means that the permission of 10.2.1(18) needs to be revised. The intent of the permission is that calls can be omitted only if it is safe (ignoring possible side-effects) because the parameters are the same. When access types are included, it is necessary to include the designated values in this check; otherwise, the designated values could be changed and the call still omitted. For instance, we do not want to allow the omission of calls on a Pure random number generator!
Furthermore, the semantics of Remote Types and Remote Call Invocation library units (E.2.2 and E.2.3) need to be adjusted to disallow dependence on an access type in a pure unit, for which user-defined Read and Write attributes have not been specified. To simplify wording, we introduce the concept of types supporting external streaming in 13.13.2 und use this more intuitive term throughout Section E wherever user-defined Read and Write attributes are required.
To avoid privacy issues, we add a rule that ensures that all private types declared in the visible part of Pure units (on which Remote Types or Remote Call Invocation units may depend) support streaming.
!example
!corrigendum 3.2(6)
Replace the paragraph:
The term subcomponent is used in this International Standard in place of the term component to indicate either a component, or a component of another subcomponent. Where other subcomponents are excluded, the term component is used instead. Similarly, a part of an object or value is used to mean the whole object or value, or any set of its subcomponents.
by:
The term subcomponent is used in this International Standard in place of the term component to indicate either a component, or a component of another subcomponent. Where other subcomponents are excluded, the term component is used instead. Similarly, a part of an object or value is used to mean the whole object or value, or any set of its subcomponents. The terms component, subcomponent, and part are also applied to a type meaning the component, subcomponent, or part of objects and values of the type.
!corrigendum 3.8(9)
Replace the paragraph:
Each component_declaration declares a component of the record type. Besides components declared by component_declarations, the components of a record type include any components declared by discriminant_specifications of the record type declaration. The identifiers of all components of a record type shall be distinct.
by:
Each component_declaration declares a component of the record type. Besides components declared by component_declarations, the components of a record type include any components declared by discriminant_specifications of the record type declaration. The identifiers of all components of a record type shall be distinct.
!corrigendum 4.8(5)
Replace the paragraph:
If the type of the allocator is an access-to-constant type, the allocator shall be an initialized allocator. If the designated type is limited, the allocator shall be an uninitialized allocator.
by:
An allocator shall not be of an access type for which the Storage_Size has been specified by a static expression with value zero or is defined by the language to be zero. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit. This rule does not apply in the body of a generic unit or within a body declared within the declarative region of a generic unit, if the type of the allocator is a descendant of a formal access type declared within the formal part of the generic unit.
!corrigendum 10.2.1(16)
Replace the paragraph:
Legality Rules
A pure library_item is a preelaborable library_item that does not contain the declaration of any variable or named access type, except within a subprogram, generic subprogram, task unit, or protected unit.
by:
Static Semantics
A pure library_item is a preelaborable library_item whose elaboration does not perform any of the following actions:
The Storage_Size for an anonymous access-to-variable type declared at library level in a library unit that is declared pure is defined to be zero.
Legality Rules
!corrigendum 10.2.1(17)
Replace the paragraph:
A pragma Pure is used to declare that a library unit is pure. If a pragma Pure applies to a library unit, then its compilation units shall be pure, and they shall depend semantically only on compilation units of other library units that are declared pure.
by:
A pragma Pure is used to declare that a library unit is pure. If a pragma Pure applies to a library unit, then its compilation units shall be pure, and they shall depend semantically only on compilation units of other library units that are declared pure. Furthermore, the full view of any partial view declared in the visible part of the library unit that has any available stream attributes shall support external streaming (see 13.13.2).
!corrigendum 10.2.1(18)
Replace the paragraph:
If a library unit is declared pure, then the implementation is permitted to omit a call on a library-level subprogram of the library unit if the results are not needed after the call. Similarly, it may omit such a call and simply reuse the results produced by an earlier call on the same subprogram, provided that none of the parameters are of a limited type, and the addresses and values of all by-reference actual parameters, and the values of all by-copy-in actual parameters, are the same as they were at the earlier call. This permission applies even if the subprogram produces other side effects when called.
by:
If a library unit is declared pure, then the implementation is permitted to omit a call on a library-level subprogram of the library unit if the results are not needed after the call. In addition, the implementation may omit a call on such a subprogram and simply reuse the results produced by an earlier call on the same subprogram, provided that none of the parameters nor any object accessible via access values from the parameters are of a limited type, and the addresses and values of all by-reference actual parameters, the values of all by-copy-in actual parameters, and the values of all objects accessible via access values from the parameters, are the same as they were at the earlier call. This permission applies even if the subprogram produces other side effects when called.
!corrigendum 13.13.2(36/1)
Insert after the paragraph:
The stream-oriented attributes may be specified for any type via an attribute_definition_clause. All nonlimited types have default implementations for these operations. An attribute_reference for one of these attributes is illegal if the type is limited, unless the attribute has been specified by an attribute_definition_clause or (for a type extension) the attribute has been specified for an ancestor type. For an attribute_definition_clause specifying one of these attributes, the subtype of the Item parameter shall be the base subtype if scalar, and the first subtype otherwise. The same rule applies to the result of the Input function.
the new paragraphs:
A type is said to support external streaming if Read and Write attributes are provided for sending values of such a type between active partitions, with Write marshalling the representation, and Read unmarshalling the representation.
A limited type supports external streaming only if it has available Read and Write attributes. A type with a part that is of an access type supports external streaming only if that access type or the type of some part that includes the access type component, has Read and Write attributes that have been specified via an attribute_definition_clause, and that attribute_definition_clause is visible. An anonymous access type does not support external streaming. All other types support external streaming.
!corrigendum E.2.2(8)
Replace the paragraph:
by:
!corrigendum E.2.2(14/1)
Replace the paragraph:
by:
!corrigendum E.2.2(17/1)
Replace the paragraph:
by:
!corrigendum E.2.3(14/1)
Replace the paragraph:
by:
!ACATS test
An ACATS test checking that such types are allowed in Pure units should be constructed. An ACATS B-Test checking that a unit with a private access part and no streaming operations is rejected should be constructed.
!appendix

!topic Generic formal access types in Pure packages
!reference 10.2.1(16)
!from Adam Beneschan 11-27-02
!discussion


Is it the intent of 10.2.1(16) that a Pure package cannot declare a
nested generic with a generic formal access type?

Specifically, I believe the RM makes these two package declarations
illegal:

-- (1)
package pak1 is
   pragma Pure;
   generic
      type T1 is private;
      type T2 is access all T1;
   package genpack is
      procedure proc (x : in T2);
   end genpack;
end pak1;

-- (2)
package pak1 is
   pragma Pure;
   generic
      type T1 is private;
      type T2 is access all T1;
   procedure proc (x : in T2);
end pak1;

10.2.1(16) says, "A pure library_item is a preelaborable library_item
that does not contain the declaration of any variable or named access
type, except within a subprogram, generic subprogram, task unit, or
protected unit".  Both of these packages contain declarations of named
access types named T2.  I can't find anything in the RM that would
make declarations of generic formal types *not* fall under this rule;
the declaration of a generic formal type is clearly a "declaration",
and from everything I can tell, a generic formal type is a "type".
(In case (2), although the type declaration is within a
generic_subprogram_declaration, I don't think this is the same thing
as being "within a ... generic subprogram".)

However, my impression (which may be off base) is that the rule is
intended to prevent Pure packages from declaring new access types, and
a generic formal type doesn't fit this description.  So should
10.2.1(16) be amended to exclude generic formal access types?

By the way, GNAT accepts both of the above examples.

****************************************************************

From: Christoph Grein
Sent: Wednesday, November 27, 2002  11:48 PM

Also Rational Apex 3.2.0b accepts them.

****************************************************************

From: Pascal Leroy
Sent: Thursday, November 28, 2002   3:12 AM

I think that the two examples should be legal, and that the wording in
10.2.1(16) is lousy.  It would probably be good to fix it, although I would see
this as a low priority issue.

****************************************************************

From: Randy Brukardt
Sent: Tuesday, February 3, 2004  5:16 PM

In the context of AI-362, I was thinking about how to get any sort of
logging/debugging into a Preelaborated unit.

The problem is that once a preelaborated unit has that pragma applied, you can
no longer with any I/O. Without I/O, you can't have any logging for faults in a
fielded system (if the system dies, it probably won't get to dump an in-memory
log, making it useless for field debugging - you probably can't run a debugger
on the target system - think about NASA's rovers...)

I realized that something that my spam filter's code does provides a solution
(assuming some up-front work):

  package Something is
    pragma Preelaborate (Something);
    type Logger_Access is access procedure (Text : in String);

    procedure Do_Something (....; Logger : Logger_Access := null);
  end Something;

Then, a call to Do_Something can include a logger, even if the logger couldn't
be withed by the Preelaborated unit:
    Do_Something (...., Logger => Ada.Text_IO.Put_Line'Access);

If you do this consistently, with each preelaborated routine passing the logger
on through any calls, you can pretty much log anything you need in a
preelaborated unit. At the cost of an extra parameter to everything, of course.
(You could use a global variable as well, but that's UGLY!)

That's OK for preelaborated units, since that is mainly about library-level
elaboration: which necessarily has been completed before any call can be made.

However, AI-366 proposes to allow access-to-subprogram and
access-to-object-with-empty-pool in pure units. This means that it would be
trivial to pass an impure function to a Pure function, and thus make the
results vary even if the parameters are the same (as per the permission of
10.2.1(18)):

     package Impure is
          pragma Pure (Impure);
          type Random_Access is function return Float;

          function Use_Random (Random : in Random_Access) return Natural;

     end Impure;

     A := Use_Random (Random'Access);
     B := Use_Random (Random'Access);

Clearly, the calls to Use_Random have the same values for a by-copy parameter,
but A and B are unlikely to have the same values. Yet the compiler can omit the
second call.

Indeed, this permission allows me to write a "Pure" random number generator:

    package Pure_Random is
         pragma Pure (Pure_Random);
         type Generator is private;
         type Generator_Access is access all Generator;
         for Generator_Access'Storage_Size use 0;

         function Random (Gen : in Generator_Access) return Float;
    end Pure_Random;

    Gen : aliased Pure_Random.Generator;

    A := Pure_Random.Random (Gen'Access);
    B := Pure_Random.Random (Gen'Access);

Clearly, the permission of 10.2.1(18) applies to these calls, and yet it
clearly must not!

So it appears that AI-366 is incomplete, in that it should address this issue
somehow.

We'd really rather not allow such functions as Pure -- but that's the current
state; we could vote this AI No Action, but that's hardly responsive to the
problem.

One possibility is to do as the GNAT Pure_Function pragma, which is to say that
pragma Pure is an assertion that any functions always return the (logically)
same results and have no significant side-effects (without checking those
assertions). In which case, we can leave the permission alone.

But others have suggested that the permission of 10.2.1(18) is flawed anywhy
(that discussion was filed in AI-290 about pragma Pure_Function). Perhaps the
permission can be 'patched up' by including the values of access-to-object
parameters, and excluding the permission if there are any access-to-subprogram
parameters. (That's probably a bit too strong, but I'd rather err on the side
of correctness rather than optimization...)

****************************************************************

From: Tucker Taft
Sent: Tuesday, February 3, 2004  8:08 PM

If we allow "pure" access types, then clearly the
values of the variables accessible via an access value
must be included in 10.2.1(18).  The variables accessible
via an access-to-subprogram value include any variables
visible to the designated subprogram.  The variables
accessible via an access-to-object value include the
designated object, and any variables that can be reached
from there via following access values.

****************************************************************

From: Pascal Leroy
Sent: Wednesday, February 4, 2004  7:12 AM

I don't see that there is anything new here.  You can do the same
currently if Random has an access parameter, because 10.2.1(16) only
talks about named access types.

I believe that the rules that 10.2.1(18) specifies for by-reference
types should also apply to access types,.

Actually I would be in favor of deleting 10.2.1(18) altogether, as it's
one of these rules (like 11.6) which effectively says "forget the rest
of this book, we don't really mean it".  But I guess that's a different
discussion.

****************************************************************

From: Tucker Taft
Sent: Wednesday, February 4, 2004  7:16 AM

Yes, that's a very different discussion ;-).

****************************************************************

From: Robert Dewar
Sent: Wednesday, February 4, 2004  7:36 AM

Remember that you can always do anything by doing raw import
rather than WITH.

It is really nothing to get worried about that this sort of
thing can be done.

Pure does NOT mean that a function is pure in the formal
sense, just that the compiler can make assumptions about
deleting identical calls, and deleting useless calls.

****************************************************************

From: Robert Dewar
Sent: Wednesday, February 4, 2004  7:41 AM

Pascal Leroy wrote:

> Actually I would be in favor of deleting 10.2.1(18) altogether, as it's
> one of these rules (like 11.6) which effectively says "forget the rest
> of this book, we don't really mean it".  But I guess that's a different
> discussion.

This would be an unacceptable change! The optimziation of pure functions
can be quite critical to efficiency in some applications, and certainly
other languages have this capability.

Of course removing it from the language would merely mean we had to
invent a new pragma for an implementation (actually GNAT already does
that with Pure_Function, which allows a function to be declared pure
for optimization purposes without the whole package being Pure --
which carries so much irrelevant baggage along with it.

Yes, you can worry from a theoretical point of view that 10.2.1(18)
in conjunction with nasty stuff, e.g. calling unknown foreign
language routines, can result in problems, but I don't think that's
a major issue in practice.

****************************************************************

From: Robert Dewar
Sent: Wednesday, February 4, 2004  7:50 AM

Randy Brukardt wrote:

> In the context of AI-362, I was thinking about how to get any sort of
> logging/debugging into a Preelaborated unit.
>
> The problem is that once a preelaborated unit has that pragma applied, you
> can no longer with any I/O. Without I/O, you can't have any logging for
> faults in a fielded system (if the system dies, it probably won't get to
> dump an in-memory log, making it useless for field debugging - you probably
> can't run a debugger on the target system - think about NASA's rovers...)

Well of course you can do I/O, you just can't use the (somewhat dubious
in any case) predefined Ada I/O packages. In GNAT, we provide GNAT.IO
which is a useful simple subset of Text_IO, and the documentation in
g-io.ads says:

--  A simple text I/O package that can be used for simple I/O functions
--  in user programs as required. This package is also preelaborated,
--  unlike Text_IO, and can thus be with'ed by preelaborated library
--  units.

The implementation is quite straightforward, using pragma Import
on standard C library functions.

****************************************************************

From: Robert A. Duff
Sent: Wednesday, February 4, 2004  8:32 AM

> The problem is that once a preelaborated unit has that pragma applied, you
> can no longer with any I/O.

I do debugging/tracing output in pragma-Pure and pragma-Preelab packages
all the time.  I have a debug package containing things like Put, and
Put is declared ``pragma Import(Ada, Put, "Impure_Put");''.  Impure_Put
does the dirty work -- it is not with'ed by the debug package.  The
debug package has pragma Pure, so can be used anywhere.

****************************************************************

From: Robert Dewar
Sent: Wednesday, February 4, 2004  8:48 AM

Pure seems wrong for that. If you have

    while not EOF loop ... end loop;

then the compiler can (and most likely will) pull the test for
EOF out of the loop. It is even OK in:

    put ("-----");
    put ("-----");

to eliminate the second call.

It seems conceptually wrong to have an I/O package be pure, though
of course it can be programmed and may be useful in practice. It
seems fine for an I/O package to be preelaborable.

****************************************************************

From: Robert A. Duff
Sent: Wednesday, February 4, 2004  9:22 AM

> It seems conceptually wrong to have an I/O package be pure, ...

Yes, it is.  I do it anyway, because in some cases it's useful for
debugging.  Of course, I have to keep in mind that the compiler can
transform the code in various ways that might affect the actual
printouts.

>...though
> of course it can be programmed and may be useful in practice.

Interestingly, my previous message just happened to be number 666 in my
mailbox -- maybe that's a sign of how evil that package is.  ;-)

****************************************************************

From: Randy Brukardt
Sent: Wednesday, February 4, 2004  9:49 PM

Robert Dewar said:
...
> Remember that you can always do anything by doing raw import
> rather than WITH.

Of course. But the design of Pure seems to imply that if you just write
straight Ada code, the optimizer won't do anything bad to you.

> It is really nothing to get worried about that this sort of
> thing can be done.
>
> Pure does NOT mean that a function is pure in the formal
> sense, just that the compiler can make assumptions about
> deleting identical calls, and deleting useless calls.

If this is the case, then there is no need for all of the restrictions on
Pure units. We should just make the restrictions the same as Preelaborate,
and say user-beware. (That doesn't seem very much in keeping with the Ada
philosophy to me, but this is a weird case.)

****************************************************************

From: Randy Brukardt
Sent: Wednesday, February 4, 2004  10:01 PM

Robert Dewar wrote:

...
> Well of course you can do I/O, you just can't use the (somewhat dubious
> in any case) predefined Ada I/O packages. In GNAT, we provide GNAT.IO
> which is a useful simple subset of Text_IO, and the documentation in
> g-io.ads says:

Well, I don't want to have to use implementation-defined stuff to make
logging (which will remain in the application for its entire lifetime) work.
That locks me into a specific compiler.

If this is so easy to implement, then it seems that we should look at
requiring some of the predefined IO to be preelaborated. Then there is no
more problem. At least one implementer has objected to doing that, which is
what led to the musings.

Bob said:

> I do debugging/tracing output in pragma-Pure and pragma-Preelab packages
> all the time.  I have a debug package containing things like Put, and
> Put is declared ``pragma Import(Ada, Put, "Impure_Put");''.  Impure_Put
> does the dirty work -- it is not with'ed by the debug package.  The
> debug package has pragma Pure, so can be used anywhere.

That's also not portable (although more so); most Janus/Ada compilers
standard mode is "no Import/Export". That allows "one button" program
creation (no bizarre system linkers needed); everything is in the Ada
program.

Of course, we support Import/Export in "clunky program creation mode", but I
use that only when access to actual foreign code is needed.

Pragma Import should be prohibited in Pure units anyway (I realize that
isn't going to happen, it would break too many existing kludges).

****************************************************************

From: Pascal Leroy
Sent: Thursday, February 5, 2004  3:49 AM

Randy replied to Robert:

> > Remember that you can always do anything by doing raw import rather
> > than WITH.
>
> Of course. But the design of Pure seems to imply that if you
> just write straight Ada code, the optimizer won't do anything
> bad to you.

I agree with Randy.  You can do all sorts of nasty things with low-level
programming and interfacing with other languages, but the rules in
10.2.1 seem to be designed so that the načve programmer is not
unwittingly bitten by the permission of 10.2.1(18).  It just happens
that access types must be taken into account here.

****************************************************************

From: Robert Dewar
Sent: Thursday, February 5, 2004  4:14 AM

Randy Brukardt wrote:

> If this is so easy to implement, then it seems that we should look at
> requiring some of the predefined IO to be preelaborated. Then there is no
> more problem. At least one implementer has objected to doing that, which is
> what led to the musings.

How about a subset of Text_IO that removes a lot of the junk (e.g. page
marks, line counting etc), handles just strings and characters (can
always use 'Image) and is Preelaborable.

****************************************************************

From: Robert I. Eachus
Sent: Thursday, February 5, 2004  8:30 AM

> How about a subset of Text_IO that removes a lot of the junk (e.g. page
> marks, line counting etc), handles just strings and characters (can
> always use 'Image) and is Preelaborable.

Excellent idea.  Call it Simple_IO or something, include put for
Integer, Float, and possibly Duration, plus Long_ and Short_ variants if
supported by the compiler.  Hmmm.  No.  Feature creap. We could include
the Short_ and Long_ types in the spec, and in Ada 0Y we should probably
just bite the bullet and require that Short_Integer, Integer, and
Long_Integer be supported along with Short_Float, Float, and Long_Float
in standard.  (They can be renames for compilers that only support two
(or one) type)  But that is a different AI.

For Simple_IO. A better idea would be to have child generics for other
integer, real, enumeration types, and non-Latin1 character types.  Yes
that will require instantiation inside subprograms if you want to use
them in pre-elaboratable units.  But I don't see that as a significant
issue.

****************************************************************

From: Robert Dewar
Sent: Thursday, February 5, 2004  7:29 PM

Why burden it with all this junk. It is fine to use 'Image for numeric
stuff, have Simple_IO just do string stuff (have a look at GNAT.IO), or
better I will give the spec here. This has proved very useful:

--  A simple preelaborable subset of Text_IO capabilities

--  A simple text I/O package that can be used for simple I/O functions in
--  user programs as required. This package is also preelaborated, unlike
--  Text_IO, and can thus be with'ed by preelaborated library units.

--  Note that Data_Error is not raised by these subprograms for bad data.
--  If such checks are needed then the regular Text_IO package must be used.

package GNAT.IO is
pragma Preelaborate (IO);

    type File_Type is limited private;
    --  Specifies file to be used (the only possibilities are Standard_Output
    --  and Standard_Error). There is no Create or Open facility that would
    --  allow more general use of file names.

    function Standard_Output return File_Type;
    function Standard_Error  return File_Type;
    --  These functions are the only way to get File_Type values

    procedure Get (X : out Integer);
    procedure Get (C : out Character);
    procedure Get_Line (Item : out String; Last : out Natural);
    --  These routines always read from Standard_Input

    procedure Put (File : File_Type; X : Integer);
    procedure Put (X : Integer);
    --  Output integer to specified file, or to current output file, same
    --  output as if Ada.Text_IO.Integer_IO had been instantiated for Integer.

    procedure Put (File : File_Type; C : Character);
    procedure Put (C : Character);
    --  Output character to specified file, or to current output file

    procedure Put (File : File_Type; S : String);
    procedure Put (S : String);
    --  Output string to specified file, or to current output file

    procedure Put_Line (File : File_Type; S : String);
    procedure Put_Line (S : String);
    --  Output string followed by new line to specified file, or to
    --  current output file.

    procedure New_Line (File : File_Type; Spacing : Positive := 1);
    procedure New_Line (Spacing : Positive := 1);
    --  Output new line character to specified file, or to current output file

    procedure Set_Output (File : File_Type);
    --  Set current output file, default is Standard_Output if no call to
    --  Set_Output is made.

private
    type File_Type is (Stdout, Stderr);
    --  Stdout = Standard_Output, Stderr = Standard_Error

    pragma Inline (Standard_Error);
    pragma Inline (Standard_Output);

end GNAT.IO;

****************************************************************

From: Randy Brukardt
Sent: Thursday, February 5, 2004  6:29 PM

Robert Dewar wrote, replying to me:

> How about a subset of Text_IO that removes a lot of the junk (e.g. page
> marks, line counting etc), handles just strings and characters (can
> always use 'Image) and is Preelaborable.

I have no objection to that, although it seems like a tough sell to me (why
do we need yet another I/O library?).

My preference would be to make Text_IO and Stream_IO preelaborated (because
these have relatively simple dependence on the underlying system), leaving
the rest alone.

But I don't see that any library that allows file I/O (for logging) is going
to easily go around the objections of existing implementers. All of our I/O
is built on top of a single package (called System.Basic_IO), and if it is
too hard to make that preelaborated, it seems unlikely that it would be much
easier to implement a subset of it that is preelaborated. I believe that
this is a common implementation (Pascal indicated that Rational's is
organized the same way). And if you can make that preelaborated, making
Stream_IO and Text_IO preelaborated is pretty simple.

The only thing that causes trouble with making Text_IO preelaborated in
Janus/Ada is handling Current/Standard_Input/Output. While we could dispense
with redirection, we can hardly eliminate the concept of Standard_Output
from Simple_IO.

****************************************************************

From: Robert I. Eachus
Sent: Friday, February 6, 2004  5:39 PM

Robert Dewar wrote:

> Why burden it with all this junk. It is fine to use 'Image for numeric
> stuff, have Simple_IO just do string stuff (have a look at GNAT.IO), or
> better I will give the spec here. This has proved very useful:

I argee with Robert Dewar.  That's what the "No. Feature Creep." in the
message was saying.  But the edited message as RBKD answered it is not
how it looked to me.  (It may just have been where the line breaks fell...)

To be clear, I thought I was seconding Robert Dewar's suggestion, and
noting that any support for other types in Standard could be pushed to
child packages if a vendor wanted to include such support.

I was also serious that in Ada 0Y we should at least require
Long_Integer and Long_Float.  Implementors who actually have only one
base integer or floating point type could just put in a renames. There
is little or no hardship for implementors there.  But I am writing more
and more code that uses Long_Float by default.  I've never had to modify
any of it to compile on a system that didn't support Long_Float, even
though such a modification is also trivial.  If everyone is supporting
Long_Float, why don't we say so?

****************************************************************

From: Randy Brukardt
Sent: Friday, February 6, 2004  5:40 PM

Robert Dewar said:

> Why burden it with all this junk. It is fine to use 'Image for numeric
> stuff, have Simple_IO just do string stuff (have a look at GNAT.IO), or
> better I will give the spec here. This has proved very useful:

I totally agree about the string-only stuff. I've been making do with a
single logger procedure, after all.

But the spec. you provided don't allow output to files. That won't do.
Virtually all of the programs I work on these days either run unattended
(web server, spam filter) or are GUI programs. In neither case, can you use
Standard_Output (the unattended programs because there is no screen, and the
logging is a permanent function used for debugging of production errors; the
GUI programs because having a standard output window on the screen can
interfere with the behavior that you are trying to debug -- or it is
unreadable, because it is underneath the program's windows).

So I think the package needs Open, Create, Close, and Delete. (But not
necessarily Name or Form or Reset, as those tend to make the implementation
harder). And I'd leave out Set_Output; it complicates things when you have
files and you certainly can work around not having it.

But the other operations are just about right.

****************************************************************

From: Robert Dewar
Sent: Friday, February 6, 2004  5:44 PM

I agree that Long_Float should be unconditionally supported.

****************************************************************

From: Robert A. Duff
Sent: Thursday, February 5, 2004  8:24 AM

Randy said:

> Robert Dewar wrote:
> ...
> > Well of course you can do I/O, you just can't use the (somewhat dubious
> > in any case) predefined Ada I/O packages. In GNAT, we provide GNAT.IO
> > which is a useful simple subset of Text_IO, and the documentation in
> > g-io.ads says:
>
> Well, I don't want to have to use implementation-defined stuff to make
> logging (which will remain in the application for its entire lifetime) work.
> That locks me into a specific compiler.

Agreed.

> If this is so easy to implement, then it seems that we should look at
> requiring some of the predefined IO to be preelaborated. Then there is no
> more problem. At least one implementer has objected to doing that, which is
> what led to the musings.
>
> Bob said:
>
> > I do debugging/tracing output in pragma-Pure and pragma-Preelab packages
> > all the time.  I have a debug package containing things like Put, and
> > Put is declared ``pragma Import(Ada, Put, "Impure_Put");''.  Impure_Put
> > does the dirty work -- it is not with'ed by the debug package.  The
> > debug package has pragma Pure, so can be used anywhere.
>
> That's also not portable (although more so); ...

I think it *is* portable.  Convention "Ada" and the relevant pragmas are
not optional features of the language.  You don't need any special
linker options to get the impure part included -- just put a "with" of
the impure version of the package somewhere in the program.

By the way, although the semantics of my impure/pure cheat is somewhat
dubious, as Robert Dewar pointed out, in practise it's no worse than
what happens with an interactive debugger -- if the compiler has chosen
to stir the code around, the debugger gets confused and sometimes lies.
That's a pain, but getting a debugger to tell the truth about optimized
code is a rather difficult trick.  Pragma-Pure-related optimizations are
but one example.

Another standard trick is to use Import(Ada) to suppress default
initialization.  This is specified in RM-B.1(38), and mentioned in
AARM-13.3(12.c) and B.1(38.a).

>... most Janus/Ada compilers
> standard mode is "no Import/Export".

If you mean "pragma Import(Ada) is illegal in this mode, then you can
make that the *default* mode if you like, but I don't think you can call
it "standard". ;-)  If you're talking about extra linker options, then
fine -- but my ugly hack needs no extra linker options.

>... That allows "one button" program
> creation (no bizarre system linkers needed); everything is in the Ada
> program.
>
> Of course, we support Import/Export in "clunky program creation mode", but I
> use that only when access to actual foreign code is needed.
>
> Pragma Import should be prohibited in Pure units anyway (I realize that
> isn't going to happen, it would break too many existing kludges).

Over My Dead Body.  It would break *my* kludge!  (I'm sure I'm not the
only one who's thought of it.)

Anyway, it could be useful to import a pure C function into a
pragma-Pure package -- nothing wrong with that.  Of course, if the C
function is impure, you might get a surprise, but that's no different
than what happens if you get the parameter types and modes wrong.
In general, import is a programmer-beware feature, and we should not
place restrictions on it.  I say the same about machine code -- if your
machine has a "cosine" instruction, there's nothing wrong with using it
in a pragma-Pure place.

****************************************************************

From: Randy Brukardt
Sent: Thursday, February 5, 2004  7:34 PM

...
> > Bob said:
> >
> > > I do debugging/tracing output in pragma-Pure and pragma-Preelab packages
> > > all the time.  I have a debug package containing things like Put, and
> > > Put is declared ``pragma Import(Ada, Put, "Impure_Put");''. Impure_Put
> > > does the dirty work -- it is not with'ed by the debug package.  The
> > > debug package has pragma Pure, so can be used anywhere.
> >
> > That's also not portable (although more so); ...
>
> I think it *is* portable.  Convention "Ada" and the relevant pragmas are
> not optional features of the language.

I agree with that. But...

> You don't need any special
> linker options to get the impure part included -- just put a "with" of
> the impure version of the package somewhere in the program.

I don't agree with this. Not the inclusion part, but the notion that it
ought to work. You're declaring external code, and if your toolchain doesn't
support external code, then it isn't going to work even if all of the code
is actually in this program.

Keep in mind that I'm only interested (for the purposes of this discussion)
in what happens in an all-Ada program. Once you start bringing C and hardware
into the equation, all bets are off anyway.

...
> Another standard trick is to use Import(Ada) to suppress default
> initialization.  This is specified in RM-B.1(38), and mentioned in
> AARM-13.3(12.c) and B.1(38.a).

We support that, of course. I believe that there is special code to suppress
the linker symbol in such a case - if there wasn't, the program could never
link (because there is nothing that you are actually linking to).

But note that in this case you're not really using Import at all, because the
object still resides here, it's just not default initialized. (If you had the
object reside somewhere else, you couldn't use the feature in an all-Ada
program: how would you suppress default initialization at the location of
the actual object? Well, you'd have to apply Import again, and you'd have an
infinite regress.)

> >... most Janus/Ada compilers
> > standard mode is "no Import/Export".
>
> If you mean "pragma Import(Ada) is illegal in this mode, then you can
> make that the *default* mode if you like, but I don't think you can call
> it "standard". ;-)

No. You just can't link the program; it contains external references, and
the standard linker doesn't support external references.

> If you're talking about extra linker options, then
> fine -- but my ugly hack needs no extra linker options.

No again; you have to use a different toolchain. That's the problem.

Our compiler design is all-Ada top to bottom. The linker is of our own
design, and creates a executable directly (no other programs run). This is
the standard mode. That's not negotiable; we control the entire toolchain,
and that's a requirement if you're staking your business on it.

At a (much) later date, we added support for Import/Export. That was done
using a special linker that outputs the entire Ada program as one giant
system object file (complete with any Import/Export linker symbols), and
then is given to the system linker.

Now, the problem is you (Bob) are suggesting two uses for pragma Import:

    * To link to code somewhere else within the Ada program; and
    * To link to code external to the Ada program.

The fact that the convention is "Ada" doesn't really change this; you still
want to be to link to other, separate Ada (or assembler!) subsystems using
the system linker.

The problem is that there is no way to tell those apart; but they are using
*different* linkers. The "solution" is simply to assume that you are linking
to code external to the program (that works on internal code by accident);
but that implies that you are using the "secondary" toolchain. And it also
implies very substandard code (all imports call through thunks so that the
actual external reference occurs in only one place).

Even if, by some miracle, you could figure out which was which, it still
would be impossible, because the Ada linker does not use link names. It
simply uses address offsets, which of course change every time a change (or
even recompile with different switches) occurs to a spec. You can't put them
into "pragma Import" code because the only way to find out the right address
is to with the appropriate package -- which defeats the purpose of the
Import.

The beauty of Ada is that you *don't* have to depend on unreliable
code/tools from a whole bunch of unreliable vendors (just one unreliable Ada
vendor!) - you can do it *all* in Ada. Now, it obvious that sometimes you
need to access C, but (on Windows, at least) it's a lot rarer than people
make out. (I believe that you can run any Claw GUI program and almost any
Win32 program without any use of imported C code in the executable. Of
course, you're using the C code that makes up the Windows kernel - AdaOS
doesn't exist yet - but you need no Microsoft tools or code to create
running programs.)

We of course validated all of our compilers in the standard linker; we used
the alternate chain only on C interface programs. (With the exception of
Windows, for which the standard linker didn't exist when we validated.) I
would be *very* upset if that did not remain true.

****************************************************************

From: Robert Dewar
Sent: Friday, February 6, 2004  4:12 PM

Randy Brukardt wrote:
> Our compiler design is all-Ada top to bottom. The linker is of our own
> design, and creates a executable directly (no other programs run). This is
> the standard mode. That's not negotiable; we control the entire toolchain,
> and that's a requirement if you're staking your business on it.

An exceedingly odd claim. Any real Ada compiler must depend on
proprietary tool chains to be any use at all in my view. Anyway
for sure this is off topic. I must say I agree with Bob Duff's view
here that pragma Import Ada must work as he expects.

> Now, the problem is you (Bob) are suggesting two uses for pragma Import:
>
>     * To link to code somewhere else within the Ada program; and
>     * To link to code external to the Ada program.
>
> The fact that the convention is "Ada" doesn't really change this; you still
> want to be to link to other, separate Ada (or assembler!) subsystems using
> the system linker.

Yes, both are reasonable, both should work

> The problem is that there is no way to tell those apart; but they are using
> *different* linkers. The "solution" is simply to assume that you are linking
> to code external to the program (that works on internal code by accident);
> but that implies that you are using the "secondary" toolchain. And it also
> implies very substandard code (all imports call through thunks so that the
> actual external reference occurs in only one place).

That's an artifact of your peculiar linking approach. The Ada design
clearly anticipates a conventional approach to linking.

> Even if, by some miracle, you could figure out which was which, it still
> would be impossible, because the Ada linker does not use link names. It
> simply uses address offsets, which of course change every time a change (or
> even recompile with different switches) occurs to a spec. You can't put them
> into "pragma Import" code because the only way to find out the right address
> is to with the appropriate package -- which defeats the purpose of the
> Import.

Well as I say, I think you have chosen a model that makes it very
hard for you to do Import/Export correctly.

> The beauty of Ada is that you *don't* have to depend on unreliable
> code/tools from a whole bunch of unreliable vendors (just one unreliable Ada
> vendor!) - you can do it *all* in Ada. Now, it obvious that sometimes you
> need to access C, but (on Windows, at least) it's a lot rarer than people
> make out. (I believe that you can run any Claw GUI program and almost any
> Win32 program without any use of imported C code in the executable. Of
> course, you're using the C code that makes up the Windows kernel - AdaOS
> doesn't exist yet - but you need no Microsoft tools or code to create
> running programs.)

I don't buy this claim. I think your implementation (linking) approach
is fundamentally flawed, and the difficulties you are having with
Import/Export (Ada) are just a symptom of this.

> We of course validated all of our compilers in the standard linker; we used
> the alternate chain only on C interface programs. (With the exception of
> Windows, for which the standard linker didn't exist when we validated.) I
> would be *very* upset if that did not remain true.

I don't see any basis for upset. I suspect that virtually all Ada 95
compilers do Import/Export Ada the way Bob expects it to work, and in
fact it is a handy way of avoiding certain elaboration problems (very
low level and junky, but sometimes you need that).

Quite a few of the DEC tests use this capability, so ACT was actually
contractually obligated to get this working (of course it worked out
of the box, if you use a standard linking approach, it will).

I think the approach Randy describes is suitable for Ada 83, but not
for Ada 95. That is, I agree with him that it seems very hard to make
this approach work properly.

Perhaps we should indeed discuss this in the ARG, and decide what is
and what is not supposed to work in this regard.

****************************************************************

From: Randy Brukardt
Sent: Friday, February 6, 2004  5:28 PM

Robert Dewar wrote:

> Randy Brukardt wrote:
> > Our compiler design is all-Ada top to bottom. The linker is of our own
> > design, and creates a executable directly (no other programs run). This is
> > the standard mode. That's not negotiable; we control the entire toolchain,
> > and that's a requirement if you're staking your business on it.
>
> An exceedingly odd claim. Any real Ada compiler must depend on
> proprietary tool chains to be any use at all in my view.

Why? It wasn't necessary for CP/M and MS-DOS, it isn't necessary for Windows
(you can access all of Win32 and any DLL without using any Microsoft or
other tool), and it certainly isn't necessary for bare machines. The only
place that I'm familiar with that it *is* necessary is on Unix/Linux
systems - and I consider that a design fault in those systems. (It is
actually possible to build a full Ada compiler on Intel ABI machines without
using any stuff from any Unix - our SCO compiler did exactly that - but I
wouldn't do it again because too much stuff is implemented in the C
libraries rathe in the Kernel.)

Indeed, I don't see any Microsoft tools used in the Windows version of GNAT.
You can use it out of the box to make programs for Windows without getting
anything else. So what proprietary tools do you need?

...
> > The beauty of Ada is that you *don't* have to depend on unreliable
> > code/tools from a whole bunch of unreliable vendors (just one unreliable Ada
> > vendor!) - you can do it *all* in Ada. Now, it obvious that sometimes you
> > need to access C, but (on Windows, at least) it's a lot rarer than people
> > make out. (I believe that you can run any Claw GUI program and almost any
> > Win32 program without any use of imported C code in the executable. Of
> > course, you're using the C code that makes up the Windows kernel - AdaOS
> > doesn't exist yet - but you need no Microsoft tools or code to create
> > running programs.)
>
> I don't buy this claim. I think your implementation (linking) approach
> is fundamentally flawed, and the difficulties you are having with
> Import/Export (Ada) are just a symptom of this.

Fine. I want an all-Ada world as much as possible. I realize the current
state of the world makes that impractical most of the time. But I don't
believe for a minute that the standard insists on anything that would make
it impossible to have an all-Ada world. If we cannot at least strive for a
more perfect world

> > We of course validated all of our compilers in the standard linker; we used
> > the alternate chain only on C interface programs. (With the exception of
> > Windows, for which the standard linker didn't exist when we validated.) I
> > would be *very* upset if that did not remain true.
>
> I don't see any basis for upset. I suspect that virtually all Ada 95
> compilers do Import/Export Ada the way Bob expects it to work, and in
> fact it is a handy way of avoiding certain elaboration problems (very
> low level and junky, but sometimes you need that).

It is exceedingly bad practice to have dependencies within an Ada program
not documented in the context clause. I see absolutely no reason to
encourage that - even if, as a practical matter, you'll need to once in a
great while do something exceedingly bad. (In which case, you can use the
alternative toolchain set up for that purpose. Just don't ask us to validate
it - once you're depending on unknown external code, there is essentially
nothing we can do to insure correctness.) Any rep. clause pointing inside of
the Ada code itself is a terrible idea. [I know people do this as a
workaround for "in out" function parameters, for example, but I find that
horrific -- fix the root problem (using the wrong mode on the parameter),
don't stand on your head. And lobby to get the language fixed...]

> I think the approach Randy describes is suitable for Ada 83, but not
> for Ada 95. That is, I agree with him that it seems very hard to make
> this approach work properly.

In general, approaches that worked for Ada 83 were supposed to continue
working in Ada 95. We explicitly pointed out where we expected that *not* to
be true. I would consider anything in the standard that make requirements to
the contrary to fall under the Dewar rule "the standard never says anything
silly".

...
> Perhaps we should indeed discuss this in the ARG, and decide what is
> and what is not supposed to work in this regard.

I think that this would be a particularly bad use of the ARG's time. I can't
imagine anything good that would come out of such a discussion - any
conclusion is going to be considered very bad by someone, and I certainly
don't hear users complaining about this issue.

****************************************************************

From: Robert Dewar
Sent: Friday, February 6, 2004  6:38 PM

Randy Brukardt wrote:

>>An exceedingly odd claim. Any real Ada compiler must depend on
>>proprietary tool chains to be any use at all in my view.
>
> Why? It wasn't necessary for CP/M and MS-DOS,

Most people would not consider those operating systems, and you
were talking about Ada 83, not Ada 95.

  it isn't necessary for Windows

You are arguing in a circular manner. You argue that the language
allows your approach, which then validates the approach, which you
then use to invalidate an important feature of the language (export
import Ada).

>>Perhaps we should indeed discuss this in the ARG, and decide what is
>>and what is not supposed to work in this regard.

> I think that this would be a particularly bad use of the ARG's time. I can't
> imagine anything good that would come out of such a discussion - any
> conclusion is going to be considered very bad by someone, and I certainly
> don't hear users complaining about this issue.

You might be right, I think the major complete language Ada 95 compilers
do in fact fully implement pragma import/export for Ada anyway.

****************************************************************

From: Randy Brukardt
Sent: Friday, February 6, 2004  6:57 PM

Robert Dewar:
>   it isn't necessary for Windows
>
> You are arguing in a circular manner. You argue that the language
> allows your approach, which then validates the approach, which you
> then use to invalidate an important feature of the language (export
> import Ada).

Well, clearly the problem is the "important feature of the language".

I view Import/Export Ada to be something of no particular value that
naturally fell out of the other definitions (sort of like non-binary modular
types). It's for external interfacing to other Ada subsystems: a very rare
event. Some people have found a way to abuse this capability in order to
end-run Ada's protections.

> >>Perhaps we should indeed discuss this in the ARG, and decide what is
> >>and what is not supposed to work in this regard.
>
> > I think that this would be a particularly bad use of the ARG's time. I
can't
> > imagine anything good that would come out of such a discussion - any
> > conclusion is going to be considered very bad by someone, and I
certainly
> > don't hear users complaining about this issue.
>
> You might be right, I think the major complete language Ada 95 compilers
> do in fact fully implement pragma import/export for Ada anyway.

Certainly Janus/Ada does: but only if you use the alternative toolchain. And
we don't provide or support the external parts (i.e. the ones from
Microsoft) needed for that. And that's not a problem for customers: if
you're going to link C code, you need a C compiler anyway. But I don't want
to have to make Ada-only customers have to purchase full-blown development
systems from elsewhere, and I don't want our validations depending on tools
that we don't provide or support.

****************************************************************

From: Robert Dewar
Sent: Friday, February 6, 2004  7:17 PM

Randy Brukardt wrote:

> Some people have found a way to abuse this capability in order to
> end-run Ada's protections.

Sounds like you are saying you don't like the feature. OK, everyone
has that privilege, but it is part of the language, and its ability
to bypass in a very deliberate way protections that are in the language
is useful, just as address overlays are useful. In any case, the
feature is there, it must be implemented whether you like it or not :-)

> Certainly Janus/Ada does: but only if you use the alternative toolchain. And
> we don't provide or support the external parts (i.e. the ones from
> Microsoft) needed for that.

OK that's fine.

> And that's not a problem for customers: if
> you're going to link C code, you need a C compiler anyway. But I don't want
> to have to make Ada-only customers have to purchase full-blown development
> systems from elsewhere, and I don't want our validations depending on tools
> that we don't provide or support.

Well to validate import/export, you need external compilers anyway,
so I don't know what you are talking about here. The ACATS tests
have non-Ada in them.

It seems perfectly fine to tell your users that to use pragma
Import/Export Ada they need the alternative toolchain.

I do think the ACATS tests should test Import/Export Ada, since
this is definitely a feature that is used, and I consider it an
important feature.

****************************************************************

From: Randy Brukardt
Sent: Friday, February 6, 2004  7:55 PM

Robert Dewar:

> Sounds like you are saying you don't like the feature. OK, everyone
> has that privilege, but it is part of the language, and its ability
> to bypass in a very deliberate way protections that are in the language
> is useful, just as address overlays are useful.

Another mis-feature that ought to be restricted as much as possible. As with
Imports, the danger  is creating an overlay by accident. I'd prefer users
that have to put "pragma Warranty_Void();" on any address overlays before
they'd work. :-)

...
> Well to validate import/export, you need external compilers anyway,
> so I don't know what you are talking about here. The ACATS tests
> have non-Ada in them.

We used "special options" (which actually was a completely different set of
programs) for the Interface C tests. The reason was the need to link foreign
language code. That reason would not apply to Import/Export Ada.

> It seems perfectly fine to tell your users that to use pragma
> Import/Export Ada they need the alternative toolchain.
>
> I do think the ACATS tests should test Import/Export Ada, since
> this is definitely a feature that is used, and I consider it an
> important feature.

Well, given the budget of the ACAA, and the feeling of vendors, I doubt that
there is going to be many (if any) new ACATS tests on Ada other than those
steming from the Corrigendum and (soon) the Amendment. There are lots of
core rules (supposedly 30%, although I think the actual number of testable
ones is more like 10%) that matter to users (read-only access to protected
components from functions, for one example) that are untested - it's hard to
imagine how to choose what ought to be tested. Vendors don't want hundreds
of new tests.

****************************************************************

From: Robert Dewar
Sent: Friday, February 6, 2004  3:40 PM

Making Text_IO preelaborated is far too big a change, it might result in
a need for a radical reimplementation. There is simply nothing to justify
such a huge change.

****************************************************************

From: Pascal Leroy
Sent: Tuesday, March 16, 2004  7:30 AM

A quick note following the discussion of AI 366 at the last meeting:
imported objects should be excluded from the restrictions in 10.2.1(9),
as no initialization takes place for these objects (B.1(38)).  This
change would make it possible to write preelaborable units that
reference objects exported from the runtime system.  Not that any user
would want to do that, but implementers might want to do it, for
instance to implement some of the "Alan packages".

****************************************************************

From: Robert Dewar
Sent: Saturday, March 20, 2004  7:54 PM

Well I am dubious about the idea of semantics that are useful
only for implementors, but if this is the case, implementors
can do anything they like anyway within the run time library,
since it does not have to be in Ada at all.

****************************************************************

From: Robert A. Duff
Sent: Sunday, March 21, 2004  11:35 AM

True, but why force implementors to have (another?) special case in
their compilers, of the form "if we're compiling the run-time system
then ... else ... end if;"?

Anyway, I'm not sure I believe that the proposed rule is useful only for
implementors.  Pascal pointed out one usage (which is for implementors),
but the feature in question is harmless and natural, and therefore might
be useful for anybody.

The wording of 10.1.2(9) talks about a "default-initialized object",
so it seems pretty silly to forbid imported objects, since they
are not, in fact, initialized by default.

****************************************************************

From: Erhard Ploedereder
Sent: Sunday, April 18, 2004  9:47 AM

Editorial on the minutes: 10.1.2(18) -> 10.2.1(18) twice, in the discussion
of AI-366.

More importantly...I have significant problems with Pascal's notion that
imported objects should be allowed in pure units. While they do not need
elaboration, they are accessible state. If they were allowed, I could write
a "pure" random-number generator by merely hiding the state behind the
import. The optimizing permissions of 10.2.1(18) would then allow an
implementation to always return the same initial number.

This does not make sense to me. State is state, imported or not. Am I wrong ?

****************************************************************

From: Pascal Leroy
Sent: Sunday, April 18, 2004  9:58 AM

I was proposing (I think) of allowing them in *preelaborated* units.  There
are currently restrictions on what objects you can declare in a
preelaborated unit.  I was proposing to allow imported objects because they
have essentially no initialization.

I realize that the AI is currently only talking about *pure* units, but I
was thinking that, while you are in the vicinity, you might be willing to
fix another oddity regarding categorization.

****************************************************************

From: Tucker Taft
Sent: Sunday, April 18, 2004  10:29 AM

Note that it is permissible to call Import'ed subprograms
in a pure unit, and I hope you don't decide to close
*that* loophole.  We use it frequently as a way to sneak
debugging I/O into pure units.  Obviously it is "caveat
emptor" and the compiler would be allowed to eliminate
calls on such imported subprograms if they satisfied the
requirements, so it is inherently non-portable.  But
that seems fine for this kind of back door.

****************************************************************

From: Robert A. Duff
Sent: Sunday, April 18, 2004  10:45 AM

I agree with that.

However, I also have a more general comment: Even when one is not doing
evil deeds like the above, the philosophy of Import should always be
that the *programmer* is in charge of making sure the other-language
code obeys the rules.  We should never forbid Import just because you
*might* use it to bypass normal rules.

For example, I want to be able to call pure C functions from pragma Pure
packages.  Of course, if I lie about the purity, all bets are off.

****************************************************************

From: Robert Dewar
Sent: Sunday, April 18, 2004  11:13 AM

Right, this is such a huge "loop hole" that no other
objections based on loop hole worries are serious :-)

Indeed it is this loop hole that makes it necessary
to have the IA that says that you can treat functions
in pure units as being pure when in fact you know
no such thing from the categorization itself.

****************************************************************

From: Robert Dewar
Sent: Sunday, April 18, 2004  11:18 AM

> However, I also have a more general comment: Even when one is not doing
> evil deeds like the above, the philosophy of Import should always be
> that the *programmer* is in charge of making sure the other-language
> code obeys the rules.  We should never forbid Import just because you
> *might* use it to bypass normal rules.

I disagree, it is perfectly legitimate to "disobey" the rules. For
example, suppose you have a sqrt function which you want the compiler
to treat as pure, but you want to instrument it to see how often it
is called with an argument that is less than some number. It is just
fine to have the "pure" function call an imported function (which
can be in Ada in a non-pure unit) that increments a counter.

> For example, I want to be able to call pure C functions from pragma Pure
> packages.  Of course, if I lie about the purity, all bets are off.

No, not all bets. It is simply that the implementation permission
applies for deleting calls with identical arguments. No bets, and
nothing uncomfortable. Just a well defined consequence of designating
the unit as pure.

Now you might argue that the scenario I give above for square
root should be achievable without such subterfuge. Perhaps, but
the status quo is quite acceptable, and extending what is allowed
is quite reasonable.

****************************************************************

From: Erhard Ploedereder
Sent: Wednesday, April 21, 2004  1:54 PM

To summarize what I have heard...

a) Imported Variables are to be allowed for Preelaborable/ated units.  (Makes sense.)
b) Imported subprograms are allowed in Pure units. (Sure. They are. No problem.)

I am not so sure what I heard about imported variables. The discussion got
railroaded to the unendangered species of imported subprograms.

I maintain that lying, as may be the case for b), should not be o.k. in the
presence of immediate and manifest evidence that "it ain't so" (and caveat
emptor in the absence of such evidence). Imported variables say "it ain't so"
to me and hence no pragma Pure, please, in their presence.

Silence implies consent :-)

****************************************************************

From: Robert A. Duff
Sent: Wednesday, April 21, 2004  2:55 PM

Silence.

****************************************************************

From: Tucker Taft
Sent: Wednesday, April 21, 2004  3:42 PM

I see no reason to allow library-level variables in
a pure package, imported or otherwise.

****************************************************************

From: Pascal Leroy
Sent: Thursday, April 22, 2004  2:10 AM

That's exactly right.  I never asked for variables in pure units.

****************************************************************

From: Randy Brukardt
Sent: Friday, April 23, 2004  9:04 PM

I had homework to pool implementers about making System Pure. In researching
that, I found that AI-126 (the old categorization AI) said that System was
not to be made Remote_Types because "...transportation of type Address
[between partitions] makes no sense." Pure packages are, of course, allowed
to be depended on in the interfaces of distributed programs. So making
System Pure would effectively make it a Remote_Types package, and allow the
transportation of System.Address.

I was just going to mention this in in the context of AI-362, but then I got
a response from Tucker: "...type Address is an access type (visibly it is a
private type). If we have the notion of a "pure" access type..." This set
off alarm bells!!

The entire clause E.2 (including subclauses) is written assuming that Pure
packages do not contain access types. A primary reason for having
Remote_Types packages appears to be so that we can apply a requirement for
user-defined Read and Write attributes to access types in the visible
specification of such packages. If we are allowing access types in Pure
units, we are essentially allowing the existence of access types that (a)
can be transported between partitions; and (b) are not necessarily set up to
provide such access.

I don't have any brilliant ideas of how to circumvent this problem. The best
I can come up with would be to require pragma Remote_Types on Pure packages
containing access types before they can be depended on in a distributed
system. But that makes a mess out of the hierarchy.

Considering that there is only one vendor supporting Annex E, it would be
aggravating to have to abandon AIs 362 and 366 because of Annex E
concerns -- but it would be unfair to that vendor and its users to simply
ignore Annex E.

****************************************************************

From: Tucker Taft
Sent: Friday, April 23, 2004  9:41 PM

There seem two possible solutions:

1) Require that a Pure package containing an access type
    to *also* have a Remote_Types pragma applied to it if
    it is to be depended-on in a distributed program

or

2) Require that an access type declared in a Pure package
    satisfy the requirements of the Remote_Types pragma, namely
    that it include overridings of the Stream attributes.

Either would seem to solve the problem.  The latter solution
has the advantage of not requiring a change to annex E,
while the former has the advantage of preventing System from
being used in a distributed system, while still being Pure.

I have a slight preference for (1), because equating Pure with
"safe for use in distribution" has been a bit of a pain anyway.
This would establish the principle that Pure is only part of
the requirement.  The more important requirements are those
associated with Remote_Types.  So long as a Pure package has
no access types, then it automatically qualifies as Remote_Types.
Also, providing a bogus overriding of Address'Read/Address'Write
in package System just to satisfy Remote_Types requirements seems
a bit weird, and potentially misleading.

****************************************************************

From: Robert I. Eachus
Sent: Saturday, April 24, 2004  12:20 AM

I have a strong preference for (3) Recognize that not all Pure packages
are appropriate for sharing between partitions, and fix the problem of
Pure being considered a categorization pragma in E.2.   This would
require that we go through the current Pure packages and decide which,
if any, categorization pragmas should be required for them.  Any other
fix results in the distributed annex tail wagging the real-time systems dog.

And yes, I know that some embedded systems are actually distributed
systems, I've worked on several such.  Even in that environment, Pure
has a lot more to do with elaboration and other real-time issues than
with distribution.  It just happens that most Pure units are in effect,
shared passive units as well.  I like the idea of making the operators
for Address pure.  But I agree that comparing Address values from
different partitions, even if they are running on the same hardware, is
nonsense.  (And no implementor should be required to do anything with
such a call.)  So System cannot be required to be shared passive, and
shouldn't even be a remote types package.  If we take this approach,
implementors can be allowed to apply a categorization pragma to System,
but it would be required to be Pure.

How much burden would this put on current users of Annex E?  Some
packages now declared Pure would need pragma Shared_Passive added.
Doing that for all current (non-system) packages currently used in a
distributed program should be fairly trivial.  Of course the implementor
of such a program could take advantage of the new orthogonality, but
that is not a upward compatibility issue.

****************************************************************

From: Robert Dewar
Sent: Saturday, April 24, 2004  5:54 AM

In GNAT System is already Pure, as permitted by the RM. Are
there really Ada 95 compilers that do NOT take advantage
of this permission.

I find the arguments about addresses bogus. Addresses
are nothing to do with access types.

****************************************************************

From: Robert Dewar
Sent: Saturday, April 24, 2004  6:00 AM

I must say I don't see any reason not to pass addresses between
partitions, addresses are simply private types with very limited
use if you think of uses that are clearly defined and portable.

****************************************************************

From: Robert A. Duff
Sent: Saturday, April 24, 2004  7:55 AM

> I had homework to pool implementers about making System Pure. In researching
> that, I found that AI-126 (the old categorization AI) said that System was
> not to be made Remote_Types because "...transportation of type Address
> [between partitions] makes no sense."

Sounds like a bogus reason, to me.  Lots of things having to do with
Address "make no sense", but it's not our job to legislate against
them.  Forbidding useful stuff ("with System;" on a pragma-pure package)
in order to prevent useless stuff is not good language design.

Whether or not it makes sense to transport addresses across partitions
is entirely implementation dependent.  (It *could* make sense -- some
distributed systems have shared memory.)

Our compiler (SofCheck's AdaMagic) has pragma Pure in package System,
except in the Java-byte-code target.  And I know GNAT has a pragma-Pure
System.  This has not caused the sky to fall, so far.

So I'm in favor of requiring System to be Pure.  Why didn't we do that
in the first place?  The AARM doesn't say.

----------------

At first, I was confused by Randy's note -- I didn't notice the subject,
and I thought the issue was the above-mentioned Address issue.
Apparently Robert Dewar had the same confusion.  I eventually figured
out that the *main* issue is AI-366, which says that pragma-Pure
packages can contain access types.  That does seem like a problem, and I
agree with the solution suggested by both Randy and Tuck.  Randy said:

> The best
> I can come up with would be to require pragma Remote_Types on Pure packages
> containing access types before they can be depended on in a distributed
> system.

That's a kludge, but disconnecting Pure from Remote_Types (which is what
we should have done in the first place) is not upward compatible.

I don't like Tucker's other suggestion of requiring me to write useless
attribute subprograms in non-distributed programs.

****************************************************************

From: Robert Dewar
Sent: Saturday, April 24, 2004  8:14 AM

> So I'm in favor of requiring System to be Pure.  Why didn't we do that
> in the first place?  The AARM doesn't say.

I 100% agree, and I agree with all Bob's reasoning here.

The great strength of Ada is that in normal usage it is safe (like Java,
an unlike C++) but there are escape hatches that allow you to do what
you want in specialized circumstances (unlike Java).

****************************************************************

From: Robert I. Eachus
Sent: Saturday, April 24, 2004  11:57 AM

>Whether or not it makes sense to transport addresses across partitions
>is entirely implementation dependent.  (It *could* make sense -- some
>distributed systems have shared memory.)

Transporting addresses across partitions may make sense, and I am not
adverse to allowing it.  Requiring a lot of extra cruft in the final
compiled program to support it if the user has no intention of using
it?  THAT is a problem.  If I were implementing 'Read and 'Write for
Addresses on a system that supported virtual to physical address
conversions, I would expect that to be done either right by default, or
not at all so users who need the functionality can add it.  As I see it
that is the issue with transporting Address values across partitions.
(I could make a simliar argument for passing Address values between CPUs
without shared memory, but I hope no one would implement that by default.)

>>The best
>>I can come up with would be to require pragma Remote_Types on Pure packages
>>containing access types before they can be depended on in a distributed
>>system.
>
>That's a kludge, but disconnecting Pure from Remote_Types (which is what
>we should have done in the first place) is not upward compatible.

I agree that it is not purely upward compatible, but I just don't see
the incompatibility as justifying such a big wart.  IF we require
categorization pragmas on Pure units that are shared between partitions,
it should only affect user defined pure packages that are currently used
in distributed programs.  And the fix that can be done in a porting tool
is a simple text replacement.  Change "pragma Pure;" to "pragma Pure;
pragma Shared Passive;"

The advantage to fixing the problem right is more than making E.2 easier
to write.  Now it becomes natural to allow access types in pure
packages, and those not using the distributed annex immediately have
additional programming power.  If anyone that wants to use such a pure
unit in a distributed environment, he can use the correct categorization
pragma.  Much better for all concerned than saying that a pure package
is shared passive unless it declares an access type somewhere.  That
looks like a pretty nasty contract model violation.  What happens if
there is such a type in the private part?  What about pure packages that
depend on other pure packages that define an access type?

****************************************************************

From: Erhard Ploedereder
Sent: Monday, May 17, 2004  5:20 PM

Context: I was asked to add words to AI-366 so that imported objects are
preelaborable.

Now I am confused. 99.9% of imported objects are preelaborable today, as far
as I can tell from the RM.  The only relevant sentence that I could find is
10.2.1(9) -- and I claim that almost all imported objects are not of a type
cited here:
---- << preelaborabe units may not include... >>
* The creation of a default-initialized object (including a component) of a
descendant of a private type, private extension, controlled type, task type,
or protected type with entry_declarations; similarly the evaluation of an
extension_aggregate with an ancestor subtype_mark denoting a subtype of such
a type.
----

o.k., our imported object could be "of a descendent of a private type", but
as far as the rest goes, really very very unlikely.

I propose to do nothing here.

If you feel different, please comment also on the re-write below:
------
* The creation of a default-initialized object (including a component) of a
descendant of a private type, private extension, controlled type, task type,
or protected type with entry_declarations, unless a pragma Import applies to
the object (see B.1); similarly the evaluation of an
extension_aggregate with an ancestor subtype_mark denoting a subtype of such
a type.
------

(Incidently: this is the only occurrence of the term "default-initialized"
 in the normative part of the RM; one more in a note of 3.8. Not exactly a
 well defined term :-) )

****************************************************************

From: Gary Dismukes
Sent: Monday, May 17, 2004  7:16 PM

> If you feel different, please comment also on the re-write below:

I agree, there doesn't seem a need to change anything here, and
adding special treatment in 10.2.1(9) for imported objects doesn't
seem worthwhile.

Since Pascal raised this issue, I guess he'll need to speak up if he
thinks there's still an issue that needs to be addressed for imported
objects in preelaborated packages.

> (Incidently: this is the only occurrence of the term "default-initialized"
>  in the normative part of the RM; one more in a note of 3.8. Not exactly a
>  well defined term :-) )

Note that AI-161 modifies 10.2.1(9) so that it's framed in terms of
the new notion of types that have "preelaborable initialization".

****************************************************************

From: Pascal Leroy
Sent: Tuesday, May 18, 2004  1:49 AM

> I propose to do nothing here.

Having proposed this change you won't be surprised to hear that I disagree.

> * The creation of a default-initialized object (including a component) of a
> descendant of a private type, private extension, controlled type, task type,
> or protected type with entry_declarations, unless a pragma Import applies to
> the object (see B.1); similarly the evaluation of an
> extension_aggregate with an ancestor subtype_mark denoting a subtype of such
> a type.

Sounds good to me.

> (Incidently: this is the only occurrence of the term "default-initialized"
>  in the normative part of the RM; one more in a note of 3.8. Not exactly a
> well defined term :-) )

I believe (but I didn't check) that this was clarified/discussed in the AI
that defines pragma Preelaborable_Initialization.

****************************************************************

From: Erhard Ploedereder
Sent: Tuesday, May 18, 2004  6:45 AM

Pascal wrote wrt AI-366:
> Having proposed this change you won't be surprised to hear that I disagree.

Having looked now at the wording of AI-161 (and B.1(38), which states that
an imported object is not created -- under a "notwithstanding" rule),
all imported objects are preelaborable today and under AI-161.

We need not change anything. Hence, consider my "unless clause" for 10.2.1(9)
withdrawn from consideration.

It might be worth a Note or an AARM annotations to spell out this
ramification for imported objects. I leave that to the author of AI-161.

****************************************************************

From: Tucker Taft
Sent: Thursday, May 27, 2004  6:43 PM

> Replace 10.2.1(18) with

> If a library unit is declared pure, then the implementation is
> permitted to omit a call on a library-level subprogram of the library
> unit if the results are not needed after the call.  In addition, the
> implementation may omit a call on such a function and simply reuse the
> results produced by an earlier call on the same function, provided
> that none of the parameters nor any object accessible via access
> values from the parameters are of a limited type, and the addresses
> and values of all by-reference actual parameters, the values of all
> by-copy-in actual parameters, and the values of all objects accessible
> via access values from the parameters, are the same as they were at
> the earlier call. This permission applies even if the subprogram
> produces other side effects when called.

The uses of the word "function" in the above paragraph should be
changed to "subprogram."

****************************************************************

From: Randy Brukardt
Sent: Thursday, May 27, 2004  7:05 PM

The !discussion should say something about the reasons for revising 10.2.1(18).
Therefore, I added the following to the !discussion:

Allowing access types in Pure units means that the permission of 10.2.1(18)
needs to be revised. The intent of the permission is that calls can be omitted
only if it is safe (ignoring possible side-effects) because the parameters are
the same. When access types are included, it is necessary to include the
designated values in this check; otherwise, the designated values could be
changed and the call still omitted. For instance, we do not want to allow the
omission of calls on a Pure random number generator!

****************************************************************

From: Erhard Ploedereder
Sent: Monday, August 30, 2004  5:53 PM


I tried fairly hard to figure out the right wording for AI-366 in accordance
with the minutes of the last meeting, where it was decided that we should
require Read/Write for private types in pure packages that are or include
access types, and to require same for the visible types if used in RCI
packages.

Rather than incorporating the results in the AI directly, I give you the
proposed wording separately for discussion at the next meeting. See my
comments why I did this.

Erhard

----------------------------

Add after the revised 10.2.1(16):

  If the full view of a private type declared in the visible part of the
  library unit is a non-remote access type (see E.2.2) or has a part that is of a
  non-remote access type, then that access type, or the type of some part
  that includes the access type subcomponent, shall have user-specified Read and Write
  attributes.

<<< Comments:
1. the "non-remote" part seems necessary, since remote type
   packages can be pure and for remote access types E.2.2(8) poses the
   requirement already on all access types, not just the private ones.
2. E.2.2(17/1) has me seriously worried, since the expanded rule for "pure"
   requires STORAGE_SIZE to be 0, but E.2.2 (17/1) says that the attributes
   are not defined at all for the interesting remote access types. Probably that
   para would need a change, too.
3. The terminology of E.2.2(8) - inherited by this rule and the one below -
   of talking about "views of types having a part that is of an access type"
   is particularly mysterious to me. Does "type X is access Integer" have a
   part that is of an access type ? I guess not, but in that case E.2.2
   does not require user-defined Read/Write attributes on remote access
   types. Interesting and surprising (to me).
   >>>


Change E.2.3(14.1) from

* it shall not be, nor shall its visible part contain, a subprogram
  (or access-to-subprogram) declaration whose profile has an access
  parameter, or a formal parameter of a limited type unless that
  limited type has user-specified Read and Write attributes;

to

* it shall not be, nor shall its visible part contain, a subprogram
  (or access-to-subprogram) declaration whose profile has an access
  parameter, or a formal parameter of a limited type unless that
  limited type has user-specified Read and Write attributes, or a
  formal parameter of a type that is a non-remote access type or
  has a part that is of a non-remote access type, unless that access
  type, or the type of some part that includes the access type
  subcomponent, has user-specified Read and Write attributes.

<<< Comments: I find these rules WAY TOO complicated; I don't fully
understand them myself and I am not sure by any degree that they are
correct and complete. Admittedly, the whole streaming area is not my
forte, so it may seem simpler to you. In any case, these wordings need
close scrutiny by better streaming experts.
>>>

****************************************************************

From: Randy Brukardt
Sent: Monday, August 30, 2004  8:51 PM

> 3. The terminology of E.2.2(8) - inherited by this rule and the one
below -
>    of talking about "views of types having a part that is of an access
type"
>    is particularly mysterious to me. Does "type X is access Integer" have
a
>    part that is of an access type ? I guess not, but in that case E.2.2
>    does not require user-defined Read/Write attributes on remote access
>    types. Interesting and surprising (to me).
>    >>>

"Part" is of course a technical term, defined in 3.2(6) [I found this
definition by looking in the index, as Bob always used to say]. "Part"
includes the whole object or value as well as any subcomponents. If you
don't want to include the object itself, you say "subcomponent" (which was
not done here).

Therefore, it's obvious that "type C is access Integer" has a part that is
of an access type, and the rule applies.

...
>* it shall not be, nor shall its visible part contain, a subprogram
>  (or access-to-subprogram) declaration whose profile has an access
>  parameter, or a formal parameter of a limited type unless that
>  limited type has user-specified Read and Write attributes, or a
>  formal parameter of a type that is a non-remote access type or
>  has a part that is of a non-remote access type, unless that access
>  type, or the type of some part that includes the access type
>  subcomponent, has user-specified Read and Write attributes.

As noted above, the "that is a non-remote access type" is redundant, as it
is covered by "part". So the wording can be simplified to:

* it shall not be, nor shall its visible part contain, a subprogram
  (or access-to-subprogram) declaration whose profile has an access
  parameter, or a formal parameter of a limited type unless that
  limited type has user-specified Read and Write attributes, or a
  formal parameter of a type that has a part that is of a non-remote
  access type, unless the type of some part that includes the access
  type has user-specified Read and Write attributes.

I'm not going to claim that this is a lot more understandable, but less
wording is usually better...

****************************************************************

From: Randy Brukardt
Sent: Monday, September 20, 2004  9:51 PM

The version of AI-366 that Erhard distributed at the meeting includes the
wording:

    A type is said to support external streaming if Read and Write attributes
    are available that provide for sending values of such a type between active
    partitions, with Write marshalling the representation, and Read
    unmarshalling the representation.

    A type with a part that is of an access type supports external streaming
    only if that access type or the type of some part that includes the access
    type component, has user-defined Read and Write attributes. [An anonymous
    access type does not support external streaming.] A limited type supports
    external streaming only if it has user-defined Read and Write attributes.
    All other types support external streaming.

This isn't quite correct. The corrigendum defines inherited and/or constructed
attributes in some cases. AI-195 corrects this so that a limited type's
attributes are "available" in various cases. (In these cases, the stream
attribute has a defined result.) Clearly, there is no reason to disallow Annex
E uses whenever there is a stream attribute defined for a type. Thus, the
wording should use "available".

"A limited type supports external streaming only if it has available Read
and Write attributes."

The use of "only if" in the above wording is clever, but I think an AARM note
is required to note that "only if" means that a limited type with an access
type part must satisify both parts of the rule. It's certainly not obvious on
casual reading.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, July 20, 2005  8:26 PM

In York, we decided to add rules to ban most forms of allocators from Pure
units, as they represent a form of state. That includes a general rule
making allocators of an access type with Storage_Size 0 illegal.

Tucker was nice enough (or is that crazy enough) to develop wording for
this. One of the rules he proposes has a question after it:

Replace 10.2.1(16) by:

    A pure library_item is a preelaborable library_item whose elaboration
    does not perform any of the following actions:

      ...

      * the evaluation of an allocator of an access-to-variable type;
        for the purposes of this rule, the partial view of a type
        is presumed to have non-visible components whose default
        initialization evaluates such an allocator;

          QUESTION: Can we instead require that a type with preelaborable
          initialization not evaluate an allocator of an access-
          to-variable type as part of its default initialization?
          In Ada 95, types with preelaborable initialization were
          allowed to have access discriminants with a default
          being an allocator.

      ...

It may not be really clear from this wording, but this is a fairly serious
rule, as it prevents the use of most partial views to declare objects in
pure packages. We had a similar problem with preelaborated packages, which
was solved with pragma Preelaborable_Initialization (whew!). In York, we had
no stomach for defining a pragma Pure_Initialization (there are quite a few
rules needed). So, Tucker just banned everything that might be a problem.

Tucker's question is suggesting that we might instead make this a
requirement of a type with preelaborable initialization. In that case, the
rule doesn't disallow as many harmless cases, but it spreads the
incompatibility to preelaborable units as well.

I don't quite understand Tucker's statement: "In Ada 95, types with
preelaborable initialization were allowed to have access discriminants with
a default being an allocator.", because I don't see anything in Ada 95 that
disallows using an allocator as the default initialization of any component.
An allocator is not a name (syntactically), so 10.2.1(8) doesn't apply.
Whether a subprogram call is involved (for the default pool) could be argued
forever, and in any case, there is no difference between anonymous and named
types for the evaluation of allocators.

So it seems to me that either allocators were allowed everywhere, or
nowhere, in default expressions for types used to declare objects in
preelaborable units. For the moment, I'll assume that allocators are allowed
(otherwise, the whole question is moot, and we should just ban them
explicitly and be done with it).

Anyway, that's a side issue. The important question is whether we want to
ban allocators of access-to-variable types in types with preelaborable
initialization. This would appear to be somewhat incompatible, but also very
consistent with the intent of preelaboration to avoid runtime actions. (I
can't imagine that the default pool for an access-to-variable type could be
evaluated at compile-time.)

Here's an example:

    package Pre is
        pragma Preelaborate (Pre);

        type Acc is access all Integer;

        type Rec is record
            Comp : Acc := new Integer'(1);
        end record;

        Obj : Rec;

        type Priv is private;
        pragma Preelaboration_Initialization(Priv);
    private
        type Priv is new Rec;
    end Pre;

Obj would be illegal by the proposed rule, because Rec would not have
preelaborable initialization, as Comp's default initialization involves an
allocator for an access-to-variable type. Similarly, (and more important),
Priv's full declaration would be illegal because it doesn't have
preelaborable initialization.

Note that the above is presumed to be legal in Ada 95 (other than the
pragma, which won't be recognized).

So, is this an incompatibility or not? If it is, can we stand this
incompatibility? Should we stand this incompatibility?

Humm. I have to wonder if we need this rule at all. I don't see how such an
allocator could be evaluated, given the other rules (old and new).

The primary concern that we're trying to prevent is something like:

    package Pure1 is
       pragma Pure(Pure1);

       type Something (D : access Integer) is record
          Comp : Boolean;
       end record;

       subtype Funny is Something (D => new Integer'(0));
    end Pure1;

Funny is clearly at library level, which means that the accessibility of D
is library level. So the rule that says that library-level anonymous access
types in a pure unit have storage size 0 would seem to apply, making the
allocator illegal.

I can't think of any other way to get an allocator at the library level of a
pure unit. (You could declare the subtype inside of a component declaration,
but the semantics would be the same.)

So I'm not sure why we need the rule above. Even if there is something wrong
with my analysis, we'd be better off just banning library-level subtypes
like the above in pure units, rather than trying to craft a rule which
breaks other things:

      * the elaboration of a subtype_indication which evaluates an
allocator;

Any partial view in a Pure package necessarily has its full view elaborated;
the above rule would catch problems. Any partial view used in a pure package
would necessarily come from a pure package. And since no object declarations
for composite types are allowed in a Pure unit [variables aren't allowed,
and constants need an initializer which necessarily would not be allowed -
composite types aren't static], I don't think that you can cause default
initialization of a partial view anyway. (I've tried for the last half-hour
to come up with an example...)

If I'm right here, then there is no reason to mess with preelaborable
initialization (unless we want to), because you can't do anything "pure"
with a pure partial view anyway. And thus we don't need to worry about what
goes on inside of them.

OK, everyone, tell me I'm wrong. :-) And what we should do here.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, July 20, 2005  8:56 PM

I should have more carefully read the meeting notes before posting the
above. The problem case comes from <> in aggregates. As such, it doesn't
provide a compatibility problem, because it is new. It also can happen in
extension aggregates, but Ada 95 did not allow the use of a private type as
the ancestor of an extension aggregate in a preelaborated unit (of which a
pure unit is a subset). So the private case causes no compatibility
problems.

The case in question would look something like:

    package Pure1 is
       pragma Pure(Pure1);

       type Something (D : access Integer) is record
          Comp : Boolean;
       end record;

       type Priv is tagged private;
       pragma Preelaborable_Initialization (Priv);
    private
       type Priv is tagged record
           A_Comp : Something (D => new Integer'(0));
       end Priv;
    end Pure1;

    with Pure1;
    package Pure2 is
        pragma Pure(Pure2);
        type New_Type is new Pure1.Priv with null record;
        C : constant New_Type := (Pure1.Priv with null record);
    end Pure2;

We need the declaration of C to be illegal because of the allocator in Priv.
The rule Tucker suggested made all such uses of private types illegal in
pure units. While we could do better, the lack of a compatibility issue (and
the existence of one if it is extended to preelaborable units) seems to
suggest that it is best to use Tucker's rule and move on. (We could allow
more in the future if it proves desirable.)

Comments?

****************************************************************

From: Tucker Taft
Sent: Wednesday, July 20, 2005  11:14 PM

It does seem better to keep the rule relatively
simple; and as you say, we could be more liberal
in the future.

So when you say "Tucker's rule" you mean the
one which bans (from pure units) library-level
constant declarations that make use
of default initialization of a private type, even if
that private type has preelaborable initialization.

Although that rule is not really "simple" it
is simpler than trying to impose additional
requirements on types with preelaborable initialization.

****************************************************************

From: Randy Brukardt
Sent: Thursday, July 21, 2005  11:22 AM

> So when you say "Tucker's rule" you mean the
> one which bans (from pure units) library-level
> constant declarations that make use
> of default initialization of a private type, even if
> that private type has preelaborable initialization.

Yes that's what I meant (the rule I quoted in my first, erroneous message).

> Although that rule is not really "simple" it
> is simpler than trying to impose additional
> requirements on types with preelaborable initialization.

Yes, and more compatible.

****************************************************************


Questions? Ask the ACAA Technical Agent