Version 1.10 of ai12s/ai12-0038-1.txt

Unformatted version of ai12s/ai12-0038-1.txt version 1.10
Other versions for file ai12s/ai12-0038-1.txt

!standard E.2.1(7/1)          14-06-23 AI12-0038-1/04
!standard E.2.1(8/1)
!class binding interpretation 12-11-28
!status Corrigendum 2015 12-12-31
!status work item 13-01-04
!status ARG Approved 5-0-4 12-12-09
!status work item 12-11-28
!status received 12-06-23
!priority Low
!difficulty Medium
!subject Shared_Passive package restrictions
!summary
Shared_Passive restrictions have to be adjusted because declared-pure packages now allow the declarations of access types.
[Editor's note: These changes were included in the draft Standard when they were initially approved. We're no longer certain that they are correct, so they have much more risk of change.]
!question
Shared passive packages are allowed to depend on declared-pure packages. In Ada 2005, access type declarations were allowed in declared-pure packages, but the implications of this were not accounted for in the restrictions on shared passive packages. Should we add new restrictions directly on shared passive packages to account for this change? (Yes)
!recommendation
Shared passive packages are not allowed to declare access-to-class-wide types, but there is nothing preventing them from referencing an access-to-class-wide type declared in a declared-pure package. We propose that when an access type from a declared-pure package is used in a shared-passive package, it is treated as though it were declared in the shared passive package as far as the special accessibility check given in E.2.1(8).
!wording
Modify E.2.1(7/1):
* it shall not contain a library-level declaration of an access type that designates a class-wide type, {or a type with a part that is of a} task type, or protected type with entry_declarations.
Modify E.2.1(8):
Notwithstanding the definition of accessibility given in 3.10.2, the declaration of a library unit P1 is not accessible from within the declarative region of a shared passive library unit P2, unless the shared passive library unit P2 depends semantically on P1. {Furthermore, for the purposes of accessibility checking, when an access type that is declared within a declared-pure package is used as part of a library-level declaration in a shared-passive package, it is as though the access type were declared in the shared-passive package.}
!discussion
The accessibility rule of E.2.1(8) is intended to prevent pointers being created in a shared-passive package that point "back" into the data area of some particular active partition. Unfortunately, this doesn't work if the access type is declared in something other than a shared-passive package, in particular, in a declared-pure package. Hence, when an access type from a declared-pure package is used in a shared-passive package, it "inherits" the accessibility rules of the referencing shared-passive package.
!corrigendum E.2.1(7/1)
Replace the paragraph:
by:
!corrigendum E.2.1(8)
Replace the paragraph:
Notwithstanding the definition of accessibility given in 3.10.2, the declaration of a library unit P1 is not accessible from within the declarative region of a shared passive library unit P2, unless the shared passive library unit P2 depends semantically on P1.
by:
Notwithstanding the definition of accessibility given in 3.10.2, the declaration of a library unit P1 is not accessible from within the declarative region of a shared passive library unit P2, unless the shared passive library unit P2 depends semantically on P1. Furthermore, for the purposes of accessibility checking, when an access type that is declared within a declared-pure package is used as part of a library-level declaration in a shared-passive package, it is as though the access type were declared in the shared-passive package.
!ACATS test
An ACATS B-Test should be created to check that the new rule is actually enforced.
!ASIS
No ASIS impact.
!appendix

From: Tucker Taft
Sent: Thursday, November 29, 2012  8:49 AM

I was tasked with splitting off an AI from AI12-0031, to deal with issues associated
with shared-passive packages and their interaction with declared-pure packages.
The basic problem is that in Ada 2005 we starting allowing access types to be declared
in declared-pure packages, but didn't adjust the rules for shared-passive packages to
account for that change.

So here is a proposed new AI that tries to do that.

[This is version /01 of this AI.]

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

From: Randy Brukardt
Sent: Thursday, November 29, 2012  1:27 PM

...
> So here is a proposed new AI that tries to do that.

I fear the Mayans are right about an impending apocalypse, as the point when Tucker is
among the first to finish most of his homework is roughly as likely as me winning the
Powerball lottery. :-)

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

From: Randy Brukardt
Sent: Thursday, November 29, 2012  8:12 PM

The first sentence of the !discussion says:

The accessibility rule of E.2.3(8) is intended to prevent pointers being created in a
shared-passive package that poing "back" into the data area of some particular active
partition.

"poing" is a new term for Ada, even informally. :-) I presume you mean "pointing", right?

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

From: Randy Brukardt
Sent: Thursday, November 29, 2012  8:15 PM

Upon further reflection, you probably meant "point" (no "ing").

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

From: Tucker Taft
Sent: Thursday, November 29, 2012  8:16 PM

> "poing" is a new term for Ada, even informally. :-) I presume you mean
> "pointing", right?

Oops.  I meant "point".

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

From: Randy Brukardt
Sent: Monday, December 31, 2012  6:08 PM

For AI12-0038-1, we approved the following wording:

it shall not contain a library-level declaration of an access type that
designates a class-wide type, task type, or protected type with
entry_declarations{; further, it shall not contain a library-level declaration
that includes a name that denotes a subtype with a part having an access type
that is declared within a declared-pure package}.

So far so good. Then, the first draft of the minutes on the meeting go on to
say:

A non-limited private type that has stream attributes and an access component
could be legal in a declared-pure package (as the type would then have external
streaming). If the package also declares a function that is passed an access
value and returns that type could cause trouble by storing the parameter value
into the component and then returning that to be stored in an object of the
shared-passive partition. No solution is obvious: a solution would seem to
require making referencing any non-limited private types illegal which seems way
over the top. We're going to ignore this problem.

Say what?? Did we really decide this? (Probably, as 4 people abstained,
presumably because of this hole.)

The problem here is that we can't get away with completely ignoring this "hole".
If we ignore it, we're saying that compilers have to make it work. But that
makes no sense; if we're going to require it to work in some (obscure) case,
then we surely don't need E.2.1(7-8) to prevent it in most cases. Even if we
can't figure out an appropriate rule, we need to let compilers off the hook
somehow.

Specifically, we either need a Legality Rule, some sort of run-time check, or it
has to be erroneous.

A Legality Rule would be easy if we could break privacy, but we can't.
Probably the best solution would to attack the function that is required to
cause a problem (as it has to return the type, and take a parameter with a part
of an access type). But there probably are more forms of this (for instance, a
procedure with an out parameter of the type), so such a solution is likely to be
fragile.

I suppose we could have a To-Be-Honest AARM note that implementations need to
break privacy to make the check described by the wording given above. (That
seems to be OK since Mr. Private is retired. ;-) I would hope that is a last
resort, however.

A run-time check might happen here anyway (we're talking about passing an access
parameter in most of the scenarios), but adding overhead for this is not
appealing. And figuring out the interactions of E.2.1(8) and the algorithms
described for AI12-0016-1 sound like torture.

There is some argument for making it erroneous; I suspect that there are other
ways to get an access value of some other partition (via chapter 13 means, at
least), and those are probably going to be erroneous (at least we hope so!). A
small expansion isn't going to be a major problem.

Another option would be to make assigning a non-limited private type that has
part of an access type declared in a declared-pure package a bounded error
inside of a shared-passive package. Either it works (no one will want to
implement that) or it raises Program_Error. This check (being at runtime) can
break privacy, but in actual fact, it would always be detected at compile-time.
(And presumably would have an associated warning.) [Since the package is
preelaborated, a dangerous value can only be introduced by a later assignment,
as no constructor functions are allowed -- I think.] Or perhaps just do this for
the declaration of such an object at library-level in a shared passive package.

Should we reopen this AI to get *some* solution to this problem?? (I have no
idea what to write in the AI to explain why we're ignoring this hole.) Is there
any better ideas of a solution?

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

From: Brad Moore
Sent: Friday, January  4, 2013  2:43 PM

...
> A Legality Rule would be easy if we could break privacy, but we can't.
> Probably the best solution would to attack the function that is
> required to cause a problem (as it has to return the type, and take a
> parameter with a part of an access type). But there probably are more
> forms of this (for instance, a procedure with an out parameter of the
> type), so such a solution is likely to be fragile.

There seems to be a myriad of ways to do this sort of thing other than returning
a type that has an access component. Consider:

private with Ada.Streams;

package Pure_P is
   pragma Pure;

   type T is private with Preelaborable_Initialization;

   type Access_T is access all T with Storage_Size => 0;

   procedure Add (X : in out T;
                  Val : Integer;
                  Next : access T);  -- Anonymous Access

   procedure Add2 (X : in out T;
                  Val : Integer;
                  Next : Access_T);  -- Access Type

   procedure Add3 (X : in out T;
                   Val : Integer;
                   Next : aliased in out T);  -- Aliased

   function Value (X : T) return Integer;

private

   type Array_Of_Access_T is array (1 .. 10) of Access_T;
   type Array_Of_Anon_Access_T is array (1 .. 10) of access T;

   type T is record
      Val : Integer := 0;
      Count : Integer := 0;
      Count2 : Integer := 0;
      List : Array_Of_Anon_Access_T;
      List2 : Array_Of_Access_T;
   end record;

   procedure Read
     (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
      Item : out T);
   for T'Read use Read;

   procedure Write
     (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
      Item : T);
   for T'Write use Write;

end Pure_P;

package body Pure_P is

   procedure Add
     (X : in out T;
      Val : Integer;
      Next : access T)
   is
   begin
      X.Count := X.Count + 1;
      X.List (X.Count) := Next;
      Next.Val := Val;
   end Add;


   procedure Add2
     (X : in out T;
      Val : Integer;
      Next : Access_T)
   is
   begin
      X.Count2 := X.Count2 + 1;
      X.List2 (X.Count2) := Next;
      Next.Val := Val;
   end Add2;

   procedure Add3 (X : in out T;
                   Val : Integer;
                   Next : aliased in out T) is
   begin
      X.Count2 := X.Count2 + 1;
      X.List2 (X.Count2) := Next'Unchecked_Access;
      Next.Val := Val;
   end Add3;

   procedure Read
     (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
      Item : out T) is
   begin
      Integer'Read (Stream, Item.Count);
      for I in 1 .. Item.Count loop
         ...
      end loop;

      Integer'Read (Stream, Item.Count2);
      for I in 1 .. Item.Count2 loop
         ...
      end loop;

   end Read;

   function Value (X : T) return Integer is
   begin
      return X.Val;
   end Value;

   procedure Write
     (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
      Item : T) is
   begin
      Integer'Write (Stream, Item.Count);
      for I in 1 .. Item.Count loop
         ...
      end loop;

      Integer'Write (Stream, Item.Count2);
      for I in 1 .. Item.Count2 loop
         ...
      end loop;
   end Write;

end Pure_P;

with Pure_P;
package Normal_P is
   X, Y : aliased Pure_P.T;
end Normal_P;

with Pure_P;

package Passive_P is
   pragma Shared_Passive;
   X : Pure_P.T;
end Passive_P;

with Normal_P;
with Pure_P;
with Passive_P;
procedure Main is
   X, Y : aliased Pure_P.T;
begin
   Pure_P.Add (X    => Normal_P.X,
               Val  => 1,
               Next => Normal_P.Y'Access);  -- Store anonymous access to Normal variables in Shared package

   Pure_P.Add2 (X    => Passive_P.X,
               Val  => 1,
               Next => X'Unchecked_Access);  -- Store named access to local variables in Shared package

   Pure_P.Add3 (X    => Passive_P.X,
                Val  => 1,
                Next => Y);                   -- Store access to aliased variable in Shared package
end Main;

The proposed wording of this AI breaks privacy in these examples.

Could a solution to prevent privacy breaking involve a new aspect similar to the
way Preelaborable_Initialization works that says that the full view of a
private type does not contain parts that involve access types?

Perhaps No_Access_Components as in...

package Pure_P is
   pragma Pure;

   type T is private with Preelaborable_Initialization, No_Access_Components;

   ...

private

   ...
end Pure_P;

Then the wording for Shared Passive units could be tweaked to only allow non
limited private types that have this aspect.

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

From: Tucker Taft
Sent: Friday, January  4, 2013  3:10 PM

We don't need to protect against examples using Unchecked_Access.

I would say make it a bounded error if there is no use of Unchecked_Access.

If Unchecked_Access is used, then as usual execution can become erroneous if the
designated object disappears and someone dereferences the access value stored in
the passive partition.

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

From: Randy Brukardt
Sent: Friday, January  4, 2013  7:03 PM

> We don't need to protect against examples using Unchecked_Access.

Right. The "original" rule that prevents these problems is an accessibility
check, and the whole point of 'Unchecked_Access is to turn off accessibility
checks.

> I would say make it a bounded error if there is no use of
> Unchecked_Access.

The advantage of a Bounded Error is that it is a runtime check, and thus can
break privacy (or more accurately, privacy is not considered). The question is,
exactly what is checked?

The rule I suggested was that the *assignment* of an object that has an access
part declared in a pure unit is a bounded error. I suppose that would also have
to cover reference parameter passing (I forgot about this in my original
message; I was thinking assignment covered all parameter passing, which is
wrong). But the idea is that the check is on the assignment or parameter passing
of an object declared in a shared passive package.

I don't see how it could be on the access value or object passed in, unless we
want to insist on full run-time accessibility checking in this case. (In which
case, I don't think it needs to be a bounded error, it just has to be checked at
run-time.) But that seems like it would have distributed overhead.

> If Unchecked_Access is used, then as usual execution can become
> erroneous if the designated object disappears and someone dereferences
> the access value stored in the passive partition.

Right.

But in any case my original question was whether we need to reopen the AI to
determine a solution. If we do that, then there is plenty more time for the AI
author (that would be Tucker) to figure out a solution (we don't have to have it
now).

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

From: Tucker Taft
Sent: Sunday, June  9, 2013  3:23 PM

I came up with a possible simple fix for this one.  I propose that whenever a
reference is made to an access type declared in a declared-pure package is used
within a library-level declaration of a shared-passive package, that it
effectively "inherits" the special accessibility rule of access types declared
in shared-passive packages.  I didn't work out all of the implications, but I
think this is approximately right... [This is version /03 of the AI.]

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

Questions? Ask the ACAA Technical Agent