!standard 13.11.2(3) 09-06-02 AI05-0157-1/00 !class binding interpretation 09-06-02 !status work item 09-06-02 !status received 09-02-15 !priority Low !difficulty Medium !qualifier Omission !subject !summary ** TBD ** !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? !recommendation (See Summary.) !wording ** TBD ** !discussion We seem to have 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. (2) 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. If it is a runtime check on cases that aren't currently detected by the legality rule, it could have a negative impact on generics that instantiate but do not call (in some cases) Unchecked_Deallocation. (Imagine a generic that determines whether or not to call Free based on an additional generic parameter; it would always have to instantiate it.) (3) Raise Program_Error on the *call* of such a generic instance. (4) 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). --!corrigendum 13.1(23) !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. ****************************************************************