LSN018.GenlAcc ------------------------ !topic LSN on General Access Types !reference MS-3.10(4);3.1 !reference MS-3.10.2(1);3.1 !from Tucker Taft 91-10-29 !updated Tucker Taft 91-12-10 (look for "UPDATE" below) !discussion The most recent DR meeting indicated some concern with the use of 'ACCESS to cross access collection/storage pool boundaries. The most serious concern seems to be in segmented architectures, where different access collections/storage pools might be in different segments, and where the access values only include the offest within the segment (or even an index into an array if the objects in the collection/storage-pool are all the same size). One example of this situation is on a 1750A, where access values might be only 16 bits while the overall address space is 1 mega-word. A similar situation occurs in the 80x86 family. Our original proposals presumed that such "restricted" access types would be specially marked in some way, either with a STORAGE_SIZE or a SIZE, allowing us to disallow 'ACCESS for those types. However, the concern now seems more widespread. Essentially, we are being asked to reconsider the concept of "general" access types (aka "static" access types) as distinct from "normal" access types. The basic idea is that a general access type could designate objects in any storage pool which outlives the access type, whereas a normal access type could only designate objects in the storage pool associated with the access type. On conversion from a general access type to a normal access type, a run-time "constraint" check would be required to check that the designated object is in the correct pool. On conversion between normal access types, this check is static (since the associated pools are known statically). *** UPDATE: We have dropped the ability to convert *to* a pool-specific access type (except from one of its derivatives, as in Ada 83). On 'ACCESS, a similar check would be performed based on the aliased object. In general, it would be statically an error if the aliased object were on the stack and the result access type was a normal access type. P.all'ACCESS would perform the same check associated with a conversion from P to the result access type. *** UPDATE: We have dropped the ability of 'ACCESS to generate a pool-specific access type. 'ACCESS may only be used to produce a general access type. Given the need to distinguish general access types from normal access types, there are various different alternatives to be considered: 1) Have exactly one "general" access type associated with every type, e.g. T'ACCESS (T'ACCESS refuses to die!). 2) Have special syntax for declaring a general access type, e.g.: type GA is access all T; 3) Specify that all access-to-T'CLASS are general access types 4) Specify that all access-to-tagged are general access types 5) Presume all access types are general, unless some pragma or rep-clause implies otherwise, according to implementation-defined rules. (5) is the simplest, though it may force implementations for segmented architectures to change their "default" behavior. In the absence of pragmas/rep-clauses, (5) is very portable. However, in the presence of pragmas/rep-clauses, (5) introduces an unknown number of implementation (1), (3), and (4) have the undesirable property of requiring miscellaneous restructurings of an application simply to allow the use of 'ACCESS. (2) provides good portability and preserves existing application structure, but like (1), (3), and (4), requires recompilation when one decides it is important to be able to use 'ACCESS with a preexisting access type. Given the above, it seems reasonable to follow through with the proposal for specially marking general access types. GENERAL ACCESS TYPES If we alter the syntax proposed for "in" access types to be: type CA is access constant T; and presume that such access types are always "general" access types, then we can define the BNF for general access type definition as: access_type_definition ::= ACCESS [general_access_modifier] subtype_indication general_access_modifier ::= ALL | CONSTANT Alternatively, instead of "all" and "constant" we could use "in out" and "in". Unfortunately, if both "in out" and "in" are allowed in some context, then one would normally assume the default is "in" if the modifier is omitted. This would suggest that "normal" access type definitions are defining "access in" types, clearly upward incompatible! So... Let's stick with "access [all|constant]" T for now. The use of "all" is a double entendre, in that such access types can point to "all" aliased variables of the designated type, whereever they may be, and "all" meaning both read and write access is given. [Note: Another alternative to "access all T" would be "access aliased T".] To avoid having to restructure applications just to use 'ACCESS, we believe it is important to allow general access types to have allocators, STORAGE_POOLs, and STORAGE_SIZE specifications. However, the storage-pool for a general access type is only relevant at the time of evaluating an allocator. It says nothing about where values of the general access type might point, because of the use of 'ACCESS and conversion. When UNCHECKED_DEALLOCATION is applied to an object of a general access type, PROGRAM_ERROR should be raised if it designates an object not created by an allocator. If the designated object was created by an allocator, UNCHECKED_DEALLOCATION will invoke the DEALLOCATE operation of the storage pool in which the object was created. Through use of "for L'STORAGE_POOL use G'STORAGE_POOL;" it should be possible for even local access types to "join" the storage pool of some global general access type. Note that we already imply that all access types with the same scope and same designated subtype, and without STORAGE_SIZE or STORAGE_POOL specified, share a single storage pool. Furthermore, all access types with the same scope, designating tagged types in the same class, by default should share the same storage pool (to enable access-to-T'CLASS to be a "normal" access type). Implementation-defined rep-clauses/pragmas could also override this default storage-pool sharing. Instead of the "pool" check on conversion to a normal access type, only a scope check is required on conversion to a general access type. The scope check must ensure that the aliased object or its storage pool outlives the general access type. IMPLEMENTATION CONSIDERATIONS The run-time "constraint"/pool check on conversion from general access types to normal access types requires that general access values, or all aliased objects, carry sufficient information to identify the pool where the designated object originates. *** UPDATE: Having dropped the ability to convert to pool-specific access types, there are no more "pool checks." Hence, the rest of these implementation considerations are now irrelevant. In some segmented architectures, the pool may be implied by the address, meaning that the pool check is straightforward. For an access type with a STORAGE_SIZE specification, the pool check could be a simple range check. In others, a map indexed by a memory page number might be sufficient to identify the pool. In others, the general access value would have to be larger to include a pool identifier. Finally, in others, all aliased objects could have a prefix to identify their pool. Objects not created by an allocator would not match the pool of any access type, meaning that CONSTRAINT_ERROR would be raised on any attempt to designate them from a "normal" access type. The simplest implementation approach is probably to make general access values larger to contain a pool id. This allows all existing heap management mechanisms to be preserved, and only imposes overhead on a user of a general access type. As usual, this pool check should be SUPPRESSible. By SUPPRESSing it for a specific general access type, presumably the extra space for the pool-id can be omitted. The scope check on conversion to a general access type requires that the pool-id somehow indicate the scope level of the pool, and that aliased objects not in a pool have a pseudo pool-id which similarly indicates the scope level. A SIMPLE MODEL A simple model, though perhaps too expensive in some environments, is to imagine that a general access value consists of two pointers, one to the storage pool "object," and one to the object within the pool. A SCOPE_LEVEL function should be defined on all storage pools, to return the scope nesting level. For objects not created by an allocator, there should be a "stack" storage pool object which supports the SCOPE_LEVEL function, but need not provide anything else. The DEALLOCATE operation for a "stack" storage pool should raise PROGRAM_ERROR if called. The pool check on conversion to a normal access type is simply an equality check between the storage pool pointers. The scope check on conversion to a general access type involves a comparison between the value returned by the SCOPE_LEVEL function and the compile-time assigned level associated with the target general access type. The scope check can be eliminated if the target general access type is declared at the same or an inner scope relative to the source general access type. UNCHECKED_DEALLOCATION is a dispatching call to the DEALLOCATE operation of the storage pool. Parameters of mode "access" (see recent LSN on Access Types and OOP) would be represented the same way as general access values. The most recent proposal (see the LSN) is that there would be no scope check when passing by-access, but there would be a scope or pool check when applying 'ACCESS to a by-access formal. Comments welcome, the sooner the better... -Tuck ========================