!standard 13.4(10) 18-09-05 AI12-0237-1/04 !standard 13.4(11/3) !class Amendment 17-09-07 !status Amendment 1-2012 18-07-05 !status WG9 Approved 16-10-22 !status ARG Approved 9-0-4 (By Letter Ballot) 18-08-13 !status work item 18-07-11 !status ARG Approved 6-0-1 18-06-24 !status work item 17-09-07 !status received 17-08-08 !priority Very_Low !difficulty Easy !subject Getting the representation of an enumeration value !summary Attributes Enum_Rep and Enum_Val are added. !problem It is not possible to determine the representation (as opposed to the position number) of an enumeration literal of a generic formal type. While Unchecked_Conversion can be used, in order for this to be portable the Size of the type has to match that of the result type, and that is not possible in general within a generic unit. (A Size clause has to be static, and the Size of a generic formal type is not static.) !proposal Add functional attributes Enum_Rep and Enum_Val for setting and querying representation values. !wording [Editor's note: I put these attributes into 13.4, since they only make sense in the context of an enumeration representation clause. We could alternatively put them into 3.5.5, but that doesn't make sense given that the attributes are about the representation of a value rather than its semantics.] Add after 13.4(10): Static Semantics For every discrete subtype S, the following attributes are defined: S'Enum_Rep S'Enum_Rep denotes a function with the following specification: function S'Enum_Rep (Arg : S'Base) return universal_integer This function returns the representation value of the value of Arg, as a value of type universal_integer. The *representation value* is the internal code specified in an enumeration representation clause, if any, for the type corresponding to the value of Arg, and otherwise is the position number of the value. S'Enum_Val S'Enum_Val denotes a function with the following specification: function S'Enum_Val (Arg : universal_integer) return S'Base This function returns a value of the type of S whose representation value equals the value of Arg. For the evaluation of a call on S'Enum_Val, if there is no value in the base range of its type with the given representation value, Constraint_Error is raised. AARM Reason: We define these on all discrete types so that they can be used inside of a generic unit on a subtype of a generic formal discrete type. They're not useful on integer types (they have the same effect as S'Pos and S'Val). Modify 13.4(11/3): NOTES 13 [Unchecked_Conversion]{Attribute Enum_Rep} may be used to query the internal codes used for an enumeration type{; attribute Enum_Val may be used to convert from an internal code to an enumeration value}. The {other} attributes of the type, such as Succ, Pred, and Pos, are unaffected by the enumeration_representation_clause. For example, Pos always returns the position number, not the internal integer code that might have been specified in an enumeration_representation_clause. !discussion Outside of a generic unit, the following type and instantiations are roughly equivalent to the attributes defined here (we're assuming that Enum is a static subtype, so that Size is static): type Rep is range -(2**(Enum'Size-1)) .. (2**(Enum'Size-1)-1 with Size => Enum'Size; function Enum_Rep is new Ada.Unchecked_Conversion (Source => Enum, Target => Rep); function Enum_Val is new Ada.Unchecked_Conversion (Source => Rep, Target => Enum); The Unchecked_Conversions have the additional advantage that an unsigned representation can be used by replacing type Rep with: type Rep is mod 2**Enum'Size; As noted in the !problem, this solution doesn't work if Enum'Size is not static. Outside of a generic, Enum'Base'Size should always be static, so that can be used instead. However, inside of a generic, Formal'Size is never static, so this solution cannot be used portably. (A particular implementation might allow differing sizes for Unchecked_Conversion in this case, as the result in such a case is implementation-defined, but this cannot be depended upon.) --- Note that within a generic unit, Enum_Rep and Enum_Val are not static. Therefore, the "universal_integer" parts of those attributes are evaluated using type root_integer (by the preference rules in 8.6(29) and the patch added by AI12-0227-1 to 4.4(10)). Thus, if the representation is outside of the range of root_integer, Constraint_Error might be raised. For example, if the range of root_integer is -2**31 .. 2**31-1, then the following code: type Big_Enum is (Min, Max); for Big_Enum use (Min => 0, Max => 2**32-1); generic type Disc is (<>); package Gen is procedure Check_It (Obj : in Disc); end Gen; package body Gen is procedure Check_It (Obj : in Disc) is begin if Disc'Enum_Rep (Obj) >= 2 ** 31 then -- (1) ... end Check_It; end Gen; package Inst is new Gen (Big_Enum); Inst.Check_It (Max); Here, the expression marked by (1) will be evaluated using the relational operator of type Root_Integer. Since the value of Disc'Enum_Rep (Obj) is outside of the base range of that type, Constraint_Error can be raised. (Note that the same effect can occur when the attribute is non-static outside of a generic. Is this the reason that we never defined these attributes??) --- These attributes were considered at the very start of the Ada 2005 development and rejected at that time. Here are the relevant minutes from the September 1999 meeting: `Enum_Rep The current workaround uses unchecked_conversion to get at the physical representation of enumeration values, but can run into difficulty in trying to get the right size of the result and runs counter to some style rules. Is there a real need for this capability? Fifteen years of Ada comments with no comment about the lack of this capability seems rather strong evidence to the contrary. There was only mild support for this capability, only 5 votes for it. Discussion moved to the appropriate name. Since it should also apply to discrete values, its name seems too narrow, although it will be most applied to enumeration values. `Rep would seem more appropriate. Other than completeness in analogy to the `Val and `Pos attributes, there's been no request/real need for it. !corrigendum 13.4(10) @dinsa @xbullet for boolean types.> @dinss @s8<@i> For every discrete subtype S, the following attributes are defined: @xhang<@xterm S'Enum_Rep denotes a function with the following specification:> @xcode< @b S'Enum_Rep (Arg : S'Base) @b @i> @xindent. The @i is the internal code specified in an enumeration representation clause, if any, for the type corresponding to the value of Arg, and otherwise is the position number of the value.> @xhang<@xterm S'Enum_Val denotes a function with the following specification:> @xcode< @b S'Enum_Val (Arg : @i) @b S'Base> @xindent !corrigendum 13.4(11/3) @drepl @xindent<@s9<13 Unchecked_Conversion may be used to query the internal codes used for an enumeration type. The attributes of the type, such as Succ, Pred, and Pos, are unaffected by the @fa. For example, Pos always returns the position number, @i the internal integer code that might have been specified in an @fa.>> @dby @xindent<@s9<13 Attribute Enum_Rep may be used to query the internal codes used for an enumeration type; attribute Enum_Val may be used to convert from an internal code to an enumeration value. The other attributes of the type, such as Succ, Pred, and Pos, are unaffected by the @fa. For example, Pos always returns the position number, @i the internal integer code that might have been specified in an @fa.>> !ASIS No ASIS effect. !ACATS test An ACATS C-Test is needed to check that the new capabilities are supported. !appendix !topic Unchecked conversion for enumerations !reference Ada 2012 RM !from Victor Porton 17-08-08 !keywords enumeration type, unchecked conversion, Unchecked_Converstion !discussion Suppose E is an enumeration type. Suppose all representation values of E are in an integer type R. In this case there should work unchecked conversion between these two types (in both directions) converting between integers and corresponding representations, even if the representation sizes of E and R types are different. It is useful for instance when interfacing with C. *************************************************************** From: Martin Dowie Sent: Tuesday, August 8, 2017 10:55 AM I think implementations are free to support his already (http://www.ada-auth.org/standards/rm12_w_tc1/html/RM-13-9.html) but I’m not sure it crops up often enough to warrant the effort of a language change. *************************************************************** From: Randy Brukardt Sent: Tuesday, August 8, 2017 4:47 PM ... > In this case there should work unchecked conversion between these two > types (in both directions) converting between integers and > corresponding representations, even if the representation sizes of E > and R types are different. You can always do this in two steps, effectively adding at most a single line to the code, so it's unclear whether it is important enough to change. (Also see below.) Specifically, there always is an integer type (signed or modular) the same size as the enumeration type which can represent all of the representation values. If you don't have one the right size handy, it is easy enough to declare it at the same point as the instance of Unchecked_Conversion. Then you can convert (with a regular type conversion) value to the type that you need. For instance, if E has size 8 and has all positive representations (that fit in size 8), then: function Get_Code (V : in E) return Integer is type Result is mod 2**8 with Size => 8; function Convert is new Ada.Unchecked_Conversion (E, Result); -- Same sizes, so well-defined. begin return Integer(Convert (V)); end Get_Code; Indeed, you could declare Result using E'Size (assuming that E is not a generic formal type), meaning that the only assumption that you are making is that all of the representation values are non-negative (the most likely case): type Result is mod 2**E'Size with Size => E'Size; You can do the same thing using a signed type if the representation values have negative values: type Result is range -(2**E'Size) .. (2**E'Size)-1 with Size => E'Size; Note that you (the programmer) have to decide whether the result is signed or unsigned. But Unchecked_Conversion should never be doing a sign extension (it is supposed to be a straight bit conversion), so it wouldn't work properly for a signed conversion unless the sizes match. Ergo, I don't think (in that case) it would do what you want anyway, even if defined as you ask. *************************************************************** From: Jeffrey R. Carter Sent: Wednesday, August 9, 2017 11:04 AM > You can always do this in two steps, effectively adding at most a > single line to the code, so it's unclear whether it is important enough to > change. (Also see below.) Right, and people have been doing this since Ada 83, so obviously the ARG feels no need to eliminate that intermediate type. But if they did, I'd hope that, rather than screwing with Unchecked_Conversion, they'd create an attribute function ('Representation, for example) that returned universal_integer. And perhaps a reverse attribute function ('From_Representation?) that took any integer type. *************************************************************** From: Randy Brukardt Sent: Thursday, July 5, 2018 7:37 PM For the record: The existing note 13.4(11/3), which now immediately follows the definition of attribute Enum_Rep, says: Unchecked_Conversion may be used to query the internal codes used for an enumeration type. The attributes of the type, such as Succ, Pred, and Pos, are unaffected by the enumeration_representation_clause. For example, Pos always returns the position number, not the internal integer code that might have been specified in an enumeration_representation_clause. Obviously, the first sentence is silly given the preceding definition. It should say something like: Attribute Enum_Rep may be used to query the internal codes used for an enumeration type; attribute Enum_Val may be used to convert from an internal code to a enumeration value. The other attributes of the type, such as Succ, Pred, and Pos, are unaffected by the enumeration_representation_clause. For example, Pos always returns the position number, not the internal integer code that might have been specified in an enumeration_representation_clause. I've made this change as part of the AI; consider it my editorial review for the AI. *************************************************************** From: Steve Baird Sent: Thursday, July 5, 2018 7:45 PM Looks good to me. *************************************************************** From: Randy Brukardt Sent: Thursday, July 5, 2018 8:01 PM Thanks, but my replacement included: "... to a enumeration value. ..." Someone is surely is going to complain about the missing "n" here. :-) [Should be "an enumeration value."] *************************************************************** From: Tucker Taft Sent: Friday, July 6, 2018 4:15 PM I thought we agreed on the names "Enum_Rep_Pos" and "Enum_Rep_Val" or "To_Enum_Rep" and "From_Enum_Rep" or some such pair. I don't believe we ended up with "Enum_Rep" or "Enum_Val" by themselves. ************************************************************** From: Randy Brukardt Sent: Friday, July 6, 2018 4:29 PM I have no notes that suggest we ever agreed on any names (there was no straw poll, nor any statement of a choice, just a list of suggestions), but I was waiting for the minutes review to worry about that. (It's easy to change names!). Worst case, we can take a straw poll here. ************************************************************** From: Steve Baird Sent: Friday, July 6, 2018 4:43 PM Now that Tuck mentions it, my recollection is also that we agreed on To_Enum_Rep and From_Enum_Rep . I don't have my (hand written) Lisbon notes with me to confirm this. ************************************************************** From: Bob Duff Sent: Friday, July 6, 2018 5:07 PM Why are we making gratuitous changes to what GNAT already calls it (Enum_Rep)? ************************************************************** From: Randy Brukardt Sent: Friday, July 6, 2018 5:11 PM What does GNAT call the reverse operation? That was the sticking point, I believe. ************************************************************** From: Steve Baird Sent: Friday, July 6, 2018 5:43 PM That's also my recollection. I found my Lisbon notes and they say "approved 6-0-1 with names From_Enum_Rep, To_Enum_Rep". ************************************************************** From: Bob Duff Sent: Friday, July 6, 2018 5:43 PM > What does GNAT call the reverse operation? That was the sticking > point, I believe. I don't think GNAT has the reverse operation, which seems a bit odd. I'm a little grouchy about this because I spent a lot of time recently trying to figure out why setting assertion policy to "ignore" wasn't working in some obscure case. Well, aspect clauses get turned into pragmas, and Ada-standard-named pragmas get turned into equivalent GNAT-named pragmas (which existed before the pragmas were standardized). Tracing through levels of tree transformations, to find where it actually does something, and that something is just "Set_Ignored(N)". All for hysterical raisins. ;-) Final bug fix was to add about 20 characters to the compiler FE. ************************************************************** From: Randy Brukardt Sent: Friday, July 6, 2018 10:11 PM > I found my Lisbon notes and they say > "approved 6-0-1 with names From_Enum_Rep, To_Enum_Rep". Thanks. I was going to change the names when I realized that I have no idea which name goes with which attribute. That suggests that these aren't very good choices for names! (And I have absolutely no recollection of choosing those names - or any names for that matter. I believe this was the absolute last AI we worked on, so we may not have been quite as thorough as we usually are.) The issue for me is that there is nothing "From" or "To" here: an object of such an enumeration type *has* a representation. All the primary attribute is doing is retrieving it. There's no conversion here, but "To" and "From" imply one. For me, that means that a name like Get_Enum_Rep would make more sense. (Note that this operation is essentially a no-op, the only thing it might do is change the size of the value.) The reverse operation also doesn't do a conversion, but it does do a check that the value is in fact a representation for the enumeration. Unfortunately, the opposite of Get doesn't make any sense for this operation; it's more like "Check_Enum_Rep". Maybe the impossibility of naming the operation is why GNAT doesn't have one?? (It was very hard to describe, as well, making a distinction between a value and its representation sounds like dancing on the head of a pin.) If we must view these as some sort of conversion, we really need to be clearer about what we're starting with and going to. By analogy with Pos and Val, these are conversions between "Val" and "Rep". So one pair of possibilities would be "Rep_From_Val" and "Val_From_Rep", or perhaps "Enum_Rep_From_Val" and "Enum_Val_From_Rep". Of course, we could use a shorter version similar to 'Val and 'Pos themselves, like "Enum_Rep" and "Enum_Val" ;-). It does appear that trying to make the names symetric only leads to lousy names. I currently would lean toward "Enum_Rep" (since it is obvious what it is retrieving) and "Enum_Rep_To_Val" (since it is *not* obvious what it is setting), but these perhaps are too similar. Anyway, if I'm the only one that is confused by these names, I can hold my nose and proceed. Otherwise, we'll either have to reopen the AI or come up with some other process to choose some names. Thoughts?? ************************************************************** From: Jean-Pierre Rosen Sent: Saturday, July 7, 2018 12:45 AM > The issue for me is that there is nothing "From" or "To" here: an > object of such an enumeration type *has* a representation. All the > primary attribute is doing is retrieving it. There's no conversion here, but > "To" and "From" imply one. These attributes take a value of an enumeration type and return an integer type (or the opposite). This looks furiously like a conversion to me... ************************************************************* From: Randy Brukardt Sent: Saturday, July 7, 2018 2:51 AM If it's a conversion to an integer, then it can't be a conversion "to" a "rep". A representation is the encoding of an object, an enumeration object in this case. (Indeed, 13.1(7/2) defines it to be a "certain number of bits".) It already has that encoding, you can't convert *to* it. (Well, you could consider the reverse operation to do that, but that's the reverse of the intent as I understand it.) Moreover, 13.4 never describes what an enumeration representation clause sets as a "representation". It always calls them "internal codes". The wording describing a "representation value" here was stuck in solely to justify this attribute's name -- but all it really does is add confusion. The entire definition of these aspects requires linguistic gymnastics, because it has to try to differentiate between the value of an enumeration type and a value of the representation of a value of an enumeration type. This leads to far too many "values". It would be better to completely rewrite all of this to match the rest of 13.4 and talk about "internal codes". But even then, one doesn't convert *to* an internal code, one *retrieves* an internal code. In any case, fixing this requires far more work than this feature deserves. I wrote this AI originally with the assumption that it would be killed, not with the intent that we'd actually try to define it. Anyway, I call for a Letter Ballot on this AI. I believe it should be reconsidered and/or killed. YMMV. ************************************************************* From: Bob Duff Sent: Saturday, July 7, 2018 6:38 AM >...like "Enum_Rep" and > "Enum_Val" ;-). Looks good to me. ************************************************************* From: Bob Duff Sent: Saturday, July 7, 2018 9:33 AM Actually, what I said earlier was wrong. GNAT does have both attributes, and they are called Enum_Rep and Enum_Val. I would object to doing anything differently than what GNAT does with these, unless there's a really good reason. Here's the doc: Attribute Enum_Rep ================== .. index:: Representation of enums .. index:: Enum_Rep For every enumeration subtype ``S``, ``S'Enum_Rep`` denotes a function with the following spec: .. code-block:: ada function S'Enum_Rep (Arg : S'Base) return ; It is also allowable to apply ``Enum_Rep`` directly to an object of an enumeration type or to a non-overloaded enumeration literal. In this case ``S'Enum_Rep`` is equivalent to ``typ'Enum_Rep(S)`` where ``typ`` is the type of the enumeration literal or object. The function returns the representation value for the given enumeration value. This will be equal to value of the ``Pos`` attribute in the absence of an enumeration representation clause. This is a static attribute (i.e.,:the result is static if the argument is static). ``S'Enum_Rep`` can also be used with integer types and objects, in which case it simply returns the integer value. The reason for this is to allow it to be used for ``(<>)`` discrete formal arguments in a generic unit that can be instantiated with either enumeration types or integer types. Note that if ``Enum_Rep`` is used on a modular type whose upper bound exceeds the upper bound of the largest signed integer type, and the argument is a variable, so that the universal integer calculation is done at run time, then the call to ``Enum_Rep`` may raise ``Constraint_Error``. Attribute Enum_Val ================== .. index:: Representation of enums .. index:: Enum_Val For every enumeration subtype ``S``, ``S'Enum_Val`` denotes a function with the following spec: .. code-block:: ada function S'Enum_Val (Arg : ) return S'Base; The function returns the enumeration value whose representation matches the argument, or raises Constraint_Error if no enumeration literal of the type has the matching value. This will be equal to value of the ``Val`` attribute in the absence of an enumeration representation clause. This is a static attribute (i.e., the result is static if the argument is static). ************************************************************* From: Jeff Cousins Sent: Saturday, July 7, 2018 1:46 PM >That's also my recollection. > > I found my Lisbon notes and they say > "approved 6-0-1 with names From_Enum_Rep, To_Enum_Rep". Ditto. I think I was the abstention. It was the last AI that we discussed, maybe we were getting tired and/or suffering from the lack of coffee. ************************************************************* From: Steve Baird Sent: Wednesday, July 11, 2018 5:57 PM When we chose the attribute names To_Enum_Rep and From_Enum_Rep in Lisbon, we were under the incorrect impression that GNAT did not already define an attribute corresponding to From_Enum_Rep. GNAT users are already using the names Enum_Rep and Enum_Val for these attributes. Bob has argued (and I agree) that there is value in not choosing different names unless we are convinced that the new names are substantially better in some way. In trying to write up the Lisbon decision, Randy has found the names To_Enum_Rep and From_Enum_Rep to be confusing. This suggests that perhaps the "substantially better" criterion has not been met. So I'd like to suggest that the ARG reconsider its decision and use the attribute names Enum_Rep and Enum_Val instead (with no other changes to the AI). Since Bob and Randy have indicated in private communications that they would also support this, Randy will be posting a call for a letter ballot. ************************************************************* From: Edward Fish Sent: Thursday, July 12, 2018 12:43 PM Question: Can we have overloaded Attribute-names? If we can, then we could simply define: S'Enumeration( Value : S'Base ) return universal_integer and S'Enumeration( Value : universal_integer ) return S'Base And simply let the type of parameter select which actual-attribute to use, thereby making the "to" and "from" implicit-in-the-name but explicit-in-the-call/-usage; this seems to me to be the best way to keep the cognitive overhead of naming to a minimum. ************************************************************* From: Tucker Taft Sent: Thursday, July 12, 2018 1:04 PM Enum_Rep and Enum_Val work for me. [Aside: The whole area of enumeration rep clauses is confusing, IMHO, and I feel like Ada probably got it wrong to begin with. Arrays indexed by holey enums should probably not have been allowed, and 'Pos/'Val were probably a mistake. But exactly how they should have worked is not completely obvious either!] ************************************************************* From: Jeff Cousins Sent: Thursday, July 12, 2018 1:46 PM It's somewhat inconsistent that one can have arrays indexed by holey enums but not arrays indexed by subtypes with predicates, not even static ones. ************************************************************* From: Arnaud Charlet Sent: Thursday, July 12, 2018 1:20 PM > Enum_Rep and Enum_Val work for me. Agreed, these are sensible names, so there's no reason to create an incompatibility with GNAT's choice and users here. > [Aside: The whole area of enumeration rep clauses is confusing, IMHO, > and I feel like Ada probably got it wrong to begin with. Arrays > indexed by holey enums should probably not have been allowed, and > 'Pos/'Val were probably a mistake. But exactly how they should have > worked is not completely obvious either!] Right. ************************************************************* From: Bob Duff Sent: Thursday, July 12, 2018 1:34 PM > > Enum_Rep and Enum_Val work for me. > > Agreed, these are sensible names, so there's no reason to create an > incompatibility with GNAT's choice and users here. Strongly agree. But there is one thing I discussed with Steve: The AI allows both attributes on generic formal discrete types, but GNAT only allows that for Enum_Rep. I suggest we fix GNAT to allow it for Enum_Val as well (whether or not ARG approves the AI being discussed). ************************************************************* From: Randy Brukardt Sent: Thursday, July 12, 2018 2:42 PM The AI wording is "for any discrete type". So these are also allowed on integer types, including generic integer and modular types. They're not useful in any of these circumstances (and I'll try to avoid testing such uses in the ACATS), but they are allowed. ************************************************************* From: Randy Brukardt Sent: Thursday, July 12, 2018 2:48 PM > Question: Can we have overloaded Attribute-names? Not really, but since these denote functions, the functions can be overloaded. So the effect is the same. > If we can, then we could simply define: > S'Enumeration( Value : S'Base ) return universal_integer and > S'Enumeration( Value : universal_integer ) return S'Base > > And simply let the type of parameter select which actual-attribute to > use, thereby making the "to" and "from" > implicit-in-the-name but explicit-in-the-call/-usage; this seems to me > to be the best way to keep the cognitive overhead of naming to a > minimum. But this doesn't work with the current definition, 'cause S is "any discrete type", and that would be ambiguous with universal_integer in many (useless) cases. Perhaps it would work if S is defined more tightly, but I don't see a lot of value to changing the GNAT names (even though they're suboptimal) -- this (enumeration_representation_clauses) is a rarely used feature as it is. ************************************************************* From: Randy Brukardt Sent: Thursday, July 12, 2018 2:56 PM > It's somewhat inconsistent that one can have arrays indexed by holey > enums but not arrays indexed by subtypes with predicates, not even > static ones. True, but there is an obvious implementation for arrays indexed by holey enums (use the position number for array indexing) while there is no such implementation for subtypes with predicates. (Actually having holey arrays is a non-starter for a language intended for embedded systems, because of the extreme memory waste involved.) Indeed, Janus/Ada takes this to the logical limit and only uses the enumeration "internal codes" when storing an enumeration value in memory. For all other uses, only position numbers are used (in particular, excluding optimized code, an "internal code" will never be stored in a machine register). That way, only loads/stores have to mess with enumeration rep clauses -- array indexing, slicing, and looping don't need to know anything about that. (The optimizer tries to remove redundant conversions back and forth, so there's little overhead in normal use.) ************************************************************* From: Steve Baird Sent: Thursday, July 12, 2018 3:25 PM > Question: Can we have overloaded Attribute-names? I would argue against introducing attribute overloading for something as unimportant as these two attributes even if there was no "compatibility with existing usage" argument to consider (and there is). I think the impact on compilers and related tools would outweigh any benefits, at least in the case of these two attributes. ************************************************************* Result of Letter Ballot, closed August 13, 2018 For approval: Baird, Brukardt, Dismukes, Duff, Moore, Rosen, Schonberg, Taft, Vardanega Abstain: Barnes, Burns, Cousins, Ploedereder Against approval: None. *************************************************************