Version 1.3 of ai05s/ai05-0157-1.txt

Unformatted version of ai05s/ai05-0157-1.txt version 1.3
Other versions for file ai05s/ai05-0157-1.txt

!standard 13.11.2(3)          09-06-27 AI05-0157-1/01
!standard 13.11.2(17)
!class binding interpretation 09-06-02
!status work item 09-06-02
!status received 09-02-15
!priority Low
!difficulty Medium
!qualifier Omission
!subject Calling Unchecked_Deallocation is illegal for zero-sized pools
!summary
Calling an instance of Unchecked_Deallocation is illegal if pool is known to have Storage_Size equal to zero.
We recommend that an instance of Unchecked_Deallocation raise Program_Error if the pool has Storage_Size equal to zero (if it is not illegal).
!question
RM-4.8(5.3/2) says:
5.3/2 {AI95-00366-01} 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.
But there is not a similar rule for Unchecked_Deallocation. That means that Unchecked_Deallocation can be instantiated for such types, and every call to such an Unchecked_Deallocation is guaranteed to be erroneous.
Should such instances be illegal? (No, but calls are.)
!recommendation
(See Summary.)
!wording
Add after 13.11.2(3):
Legality Rules
A call on an instance of Unchecked_Deallocation is illegal if the actual access type of the instance is a 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 actual access type of the instance is a descendant of a formal access type declared within the formal part of the generic unit.
[Editor's note: This is nearly a copy of 4.8(5.3/2).]
Add after 13.11.2(17): (Implementation Advice)
A call on an instance of Unchecked_Deallocation with a non-null access value should raise Program_Error if the actual access type of the instance is a type for which the Storage_Size has been specified to be zero or is defined by the language to be zero.
AARM Note: If the call is not illegal (as in a generic body), we recommend that it raise Program_Error. Since the execution of this call erroneous (any allocator from the pool will have raised Storage_Error, so the non-null access value must have been allocated from a different pool or be a stack-allocated object), we can't require any behavior - anything at all would be a legitimate implementation.
[Editor's note: I don't think we want to raise Program_Error if the access value is null; that case is not erroneous and is defined to do nothing. It seems harmless.
Humm, reading the wording of 13.11.2(16), one could argue that calling an instance of Unchecked_Deallocation with a null access value is always erroneous. It's not clear what "the object" is in that case, but surely it wasn't created by an allocator with Name'Storage_Pool. (Of course, even Adam would be embarassed to actually claim this seriously - no one could doubt the intent.) Do we need to clarify 13.11.2(16) in some way (perhaps an AARM To Be Honest note would be sufficient)?]
!discussion
We had a number of choices here:
(1) Make the instance of Unchecked_Deallocation illegal. But this is weird, because this is a legality check unrelated to the (normal) generic contract. It would be a magic extra check.
A second objection to this choice is that it would make instances of generics illegal if there is merely an instance of Unchecked_Deallocation in the specification. That is quite reasonable, and the instance would not necessarily be called. (Imagine a generic that determines whether or not to call Free based on an additional generic parameter; it would always have to instantiate it.) This would reduce the usability of generics.
(2) Make calls on the instance of Unchecked_Deallocation illegal. This is also a magic check, but at least it doesn't run into issues with generic units.
(3) Raise Program_Error if such a generic instance is detected. This is less weird, but it turns what otherwise would be a legality rule into a runtime check. This also could render some generics unusable (as in case (1)); this could even be a broader problem as the runtime check could cover cases that would not have been statically illegal.
(4) Raise Program_Error on the call of such a generic instance. This also turns a legality rule into a runtime check, but at least it doesn't have the problems with generic units.
(5) Leave calling the instantiation erroneous, but add a permission for an implementation to reject any program that it can detect would have erroneous execution. (One wonders if a similar permission should be allowed for Bounded Errors.) OTOH, this too could cause trouble for any code conditionally compiled. Probably it would be better to follow the rule in 1.1.5(6) (which suggests a warning when the compiler can detect errors).
Since we want this to remain a legality rule, and we do not want to make generic units less useful, we have chosen option (2), along with a recommendation for option (4) if the call is legal but the size is known to be zero.
--!corrigendum 13.11.2(3) --!corrigendum 13.11.2(17)
!ACATS Test
!appendix

From: Bob Duff
Date: Sunday, February 15, 2009  11:17 AM

RM-4.8(5.3/2) says:

  5.3/2 {AI95-00366-01} 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.

But I can't find a similar rule for Unchecked_Deallocation. Seems like a hole,
to me.  Unchecked_Deallocation should be illegal for such types, for the same
reasons as 4.8(5.3/2). Otherwise, every call to such an Unchecked_Deallocation
is guaranteed to be erroneous.

It's a nasty sort of erroneousness, because it will work fine on some
implementations.

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

From: Robert Dewar
Date: Sunday, February 15, 2009  11:24 AM

Not likely, trying to allocate statically allocated stuff will most likely NOT
work and blow up! I agree this is an obvious hole. Bob, you should file this as
a bug if GNAT does not detect this case, and flag it as illegal.

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

From: Bob Duff
Date: Sunday, February 15, 2009  12:22 PM

Sure, but the usual case is to allocate something with "new", then convert it to
another access type.  If you then call U_D using the second access type, and
that has Storage_Size 0, GNAT will just deallocate the object. It won't blow up.

OK, just to be sure, I wrote this:

  with Ada.Text_IO; use Ada.Text_IO;
  with Unchecked_Deallocation;

  procedure Main is

     type A is access all String;
     for A'Storage_Size use 0;
     procedure Free is new Unchecked_Deallocation (String, A);

     type Allocation_Type is access all String;
     X : Allocation_Type := new String'("Hello, world.");

     Y : A := A (X);

  begin
     Free (Y);
     if Y = null then
        Put_Line ("It's null.");
     else
        Put_Line ("It's not null.");
     end if;
  end Main;

It prints "It's null.".  (A more realistic example would have various class-wide
types.)

The compiler warns, though: deallocation from empty storage pool.
So that's not so horrible.

>... I agree this is an obvious hole.
> Bob, you should file this as a bug if GNAT does not detect this  case,
>and flag it as illegal.

Well, the current language doesn't make it illegal (except perhaps via "Robert's
Rule Of Absurdity") and we do give a warning...

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

From: Tucker Taft
Date: Sunday, February 15, 2009  11:38 AM

I don't quite see how you can make it illegal (Unchecked_Deallocation is a
generic, after all), but you could define it to raise Program_Error (or perhaps
to do nothing?).

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

From: Robert Dewar
Date: Sunday, February 15, 2009  11:42 AM

The instantiation should be illegal!

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

From: Tucker Taft
Date: Sunday, February 15, 2009  12:03 PM

That could be a bit of a pain to implement for some. Generics can be renamed,
etc.  At least in our implementation, there is nothing very special about
Unchecked_Deallocation until it is called, at which point we start treating it
specially.

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

From: Robert Dewar
Date: Sunday, February 15, 2009  12:16 PM

How about then the following

a) a clear statement that instantiating UC or calling UC for such a type is
   erroneous.

b) impl permission to make either the instantiation or the call illegal.

Actually it would make some sense to have impl permission that allows the
compiler to treat ANY recognized erroneous construct as illegal!

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

From: Tucker Taft
Date: Sunday, February 15, 2009  12:51 PM

My only concern about this approach is one could imagine a generic that has a
formal access type and declares some kind of "Free" operation implemented using
Unchecked_Deallocation.  The generic can reasonably be instantiated with an
access type with storage size zero, so long as the Free operation is never
called.

> Actually it would make some sense to have impl permission that allows
> the compiler to treat ANY recognized erroneous construct as illegal!

This would seem a natural extension of RM 1.1.5(6).

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

From: Robert Dewar
Date: Sunday, February 15, 2009  1:07 PM

> My only concern about this approach is one could imagine a generic
> that has a formal access type and declares some kind of "Free"
> operation implemented using Unchecked_Deallocation.  The generic can
> reasonably be instantiated with an access type with storage size zero,
> so long as the Free operation is never called.

That's a good argument, so it should be the Free operation that is always
erroneous, and impl permission to consider the call to Free to be illegal ...

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

From: Bob Duff
Date: Sunday, February 15, 2009  12:12 PM

> I don't quite see how you can make it illegal (Unchecked_Deallocation
> is a generic, after all), but you could define it to raise
> Program_Error (or perhaps to do nothing?).

Not sure what you mean -- I'm proposing to make it illegal by adding a Legality
Rule:

    For an instantiation of Unchecked_Deallocation, the generic actual
    parameter for the Name generic formal parameter shall not have a
    Storage_Size that has been specified by a static expression with value
    zero, nor defined by the language to be zero.

Or:

    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 shall
    not be passed to the Name parameter in an instantiation of
    Unchecked_Deallocation.

Plus an AARM note: Including a renaming of Unchecked_Deallocation.

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

From: Bob Duff
Date: Sunday, February 15, 2009  2:10 PM

> >> That could be a bit of a pain to implement for some.
> >> Generics can be renamed, etc.  At least in our implementation,
> >> there is nothing very special about Unchecked_Deallocation until it
> >> is called, at which point we start treating it specially.

I have trouble believing this could be hard to implement in any compiler.
This is from the man who invented coextensions.  ;-)

Seriously, during semantic analysis of an instantiation, you look in your symbol
table, and see if what you've got is called "unchecked_deallocation", and if its
parent is a root library unit called "ada".  Yes, you have to follow renamings
-- big deal.

It's a special-case kludge.  But it's not hard.

> > How about then the following
> >
> > a) a clear statement that instantiating UC or calling UC for such a
> > type is erroneous.
> >
> > b) impl permission to make either the instantiation or the call
> > illegal.

This is getting too complicated for my taste.
I'd rather leave the language alone.
We already have a warning, which is good enough in practise.

> My only concern about this approach is one could imagine a generic
> that has a formal access type and declares some kind of "Free"
> operation implemented using Unchecked_Deallocation.  The generic can
> reasonably be instantiated with an access type with storage size zero,
> so long as the Free operation is never called.

The rule about "new" does not apply to such a generic formal type (I presume,
since otherwise it would be a contract model violation), and my proposed
legality rule should not apply to such a generic formal type.  It only applies
if the Storage_Size is visibly and statically 0.  So I think this is a red
herring.

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

From: Randy Brukardt
Date: Tuesday, February 17, 2009  12:53 AM

> Seriously, during semantic analysis of an instantiation, you look in
> your symbol table, and see if what you've got is called
> "unchecked_deallocation", and if its parent is a root library unit
> called "ada".  Yes, you have to follow renamings
> -- big deal.
>
> It's a special-case kludge.  But it's not hard.

We've been *very* adverse to adding special-case kludges to generic
instantiations. Indeed, I proposed one to support the factory generic
(Generic_Dispatching_Constructor), and we decided instead to add the entire
mechanism to the language outright (abstract formal subprograms) -- which surely
was a lot more work.

I don't much care either way, BTW.

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

From: Robert Dewar
Date: Tuesday, February 17, 2009  11:55 AM

> We've been *very* adverse to adding special-case kludges to generic
> instantiations.

I really think that Unchecked_Deallocation and Unchecked_Conversion are special
cases, they are really fundamental primitives in the language, which just happen
to be spelled as generics, but I don't see that we should let this aversion
cripple the definition in any way. In particular the rules for erroneousness are
already special.

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

Questions? Ask the ACAA Technical Agent