!standard 10.2.1(5-9) 09-06-01 AC95-00173/01 !class Amendment 09-06-01 !status received no action 09-06-01 !status received 09-02-27 !subject Preelaboration rules need loosening? !summary !appendix !topic Preelaboration rules need loosening? !reference 10.2.1(5-9) !from Adam Beneschan 09-02-27 !discussion I'm looking at a GNAT implementation of Ada.Exceptions, in a-except.ads. I think it's from 2007 (I don't know if there's a later version around). The interesting parts of this are: package Ada.Exceptions is pragma Preelaborate_05; private subtype Code_Loc is System.Address; Null_Loc : constant Code_Loc := System.Null_Address; end Ada.Exceptions; Preelaborate_05 is GNAT's own pragma, I realize, but the definition of Ada.Exceptions indicates that the package must be preelaborable (11.4.1(2)), so I think we can assume that the package is supposed to be preelaborable. But it doesn't look like it follows the rules for that. 10.2.1(8) says that one of the things that makes a construct non-preelaborable is the evaluation of a _primary_ that is the _name_ of an object, unless the _name_ is a static expression (or statically denotes a discriminant...). Here, the elaboration of the constant declaration causes the evaluation of System.Null_Address, which is the name of a constant object in System; and it's not a static expression since it's a deferred constant (since GNAT follows the Implementation Advice that System.Address is a private type). (AARM 4.9(24.a)) So I think this declaration makes Ada.Exceptions non-preelaborable. (The full view of Null_Occurrence also includes a component whose elaboration requires evaluating System.Null_Address.) My point isn't to say "Gotcha" about GNAT doing something illegal, but to whine that what they're doing seems like a reasonable thing to do in a preelaborable package, but it appears that the rules don't allow this. I don't know how this would be fixable, except to provide a pragma that a programmer could apply to a deferred constant saying "This will be a static constant, or a preelaborable constant", or something like that, and then requiring that the full constant be static or preelaborable if this pragma is used. Then 10.2.1(8) would allow the evaluation of names that denote such constants. (Please note that in the case of System.Null_Address, the full constant declaration in GNAT's system package is a static expression, since it's 0; but if System.Address is a two-component record, as in a case I have to deal with, Null_Address could not be a static constant since it's not a scalar. So a rule that depends on something being a "static constant" may not be good enough.) **************************************************************** From: Adam Beneschan Date: Friday, February 27, 2009 1:55 PM Actually, on further reflection, is there any reason for 10.2.1(8) not to allow a name that denotes *any* constant? Any constant object that is named must be declared in another preelaborable package, and thus its value must satisfy the rules in 10.2.1(5-9). So it seems that, at least, if a constant's (full) declaration is preelaborable, and another object declaration uses that constant's value as its own initial value, or as a subcomponent of the initial value, then that shouldn't prevent the second object declaration from preelaborable also. (I realize that the constant name could be evaluated in other contexts besides as an initial value or subcomponent of an initial value of an object, and maybe those cause problems---I don't know.) **************************************************************** From: Randy Brukardt Date: Friday, May 22, 2009 8:59 PM ... > Actually, on further reflection, is there any reason for > 10.2.1(8) not to allow a name that denotes *any* constant? > Any constant object that is named must be declared in another > preelaborable package, and thus its value must satisfy the rules in > 10.2.1(5-9). So it seems that, at least, if a constant's (full) > declaration is preelaborable, and another object declaration uses that > constant's value as its own initial value, or as a subcomponent of the > initial value, then that shouldn't prevent the second object > declaration from preelaborable also. (I realize that the constant > name could be evaluated in other contexts besides as an initial value > or subcomponent of an initial value of an object, and maybe those > cause problems---I don't know.) Well, not all constants are really compile-time constant (see AI05-0054-2), so any of those that aren't really constant would have to be excluded. While the technique of getting a writable access via Initialize is not allowed - user-defined Initialize is not preelaboratable initialization (so controlled parts probably don't need a special exception), the Rosen trick does not appear to be prevented by preelaboration rules. That is, an immutably limited type can have preelaborable initialization. But any parts of a constant object that have immutably limited types would have to exclude the object from being used in preelaboration (since the values that they could have could not be known for certain at compile-time). That of course brings up a privacy issue - the fact that there are (or are not) components of an immutably limited type is not (in general) known for a deferred constant. Probably the best fix would be to allow pragma Preelaboratable_Initialization to be applied to constants as well. But of course that is a fairly substantial change, so I'm not sure it is worth it. **************************************************************** From: Adam Beneschan Date: Wednesday, June 3, 2009 7:01 PM I'm not convinced this is a real concern. To reconstruct the context (since both of us took some time to respond), I had a thought that 10.2.1(8) could be modified so that a preelaborable construct could refer to a _name_ that denotes any constant (in addition to allowing names that denote static expressions or discriminants of enclosing types). Your response, if I understand correctly, is that this won't work because a constant could have a type with an access discriminant and still have preelaborable initialization; and since this allows the Rosen trick to be used, it's not guaranteed that if a construct refers to a constant, the compiler would know the value of the constant at compile time. A more concrete example: package Pack1 is pragma Preelaborate(Pack1); type Typ1 is limited record ... some component that uses Typ1'access as a discriminant F1 : Integer; end record; Const1 : constant Typ1 := ; end Pack1; with Pack1; package Pack2 is pragma Preelaborate(Pack2); type Typ2 is limited record ... F2 : Integer; ... end record; Const2 : constant Typ2 := (..., F2 => Pack1.Const1.F1, ...); end Pack2; My "proposal" (still just a thought) would be to allow the reference to Pack1.Const1 in the preelaborable package. You're saying that the compiler can't know for certain what the value of Pack1.Const1.F1 is since it could have been modified via the Rosen trick. Am I understanding you corrctly? If I am, I think this is wrong, because I don't see any way that the constant could be modified. Any elaboration that takes place before the evaluation of Const2's initial expression must be in a preelaborable package; and since elaborations in preelaborable packages may not contain statements or subprogram calls (other than to static functions), I don't see how it's possible for any code that uses the Rosen trick to be executed. So it appears to me that, even though later code *could* possibly change the value of Const1.F1---assuming this is doable legally, which I haven't looked into---it isn't possible to change Const1.F1 before it's used to initialize Const2, so the compiler *does* know what the value is. If there's some more complex example that breaks this, I can't think of one. In any event, the fact that GNAT's runtime tried to use this illegal construct (not caught by their compiler) is evidence that it would be useful to have *some* way to do this legally, and I'm hopeful that the idea isn't sunk by some obscure and probably pathological example. P.S. Constants that have Import applied to them could be a problem, though. **************************************************************** From: Randy Brukardt Date: Monday, June 8, 2009 10:04 PM For the record, it wasn't sunk by an obscure example. It simply was judged "not important enough". ****************************************************************