‘final’ variables: an alternative definition and its usefulness.
Robert Jacques
sandford at jhu.edu
Sat Mar 21 19:54:23 PDT 2009
I propose to enhance the concept of ‘final’ to variables in order fill a
much needed place in the current type system. Final variables would be
transitive (a.k.a. deep), a super-type of non-final, assignable only at
declaration and apply only to references. Essentially, it would be a
references only version of ‘const’. Despite this similarity, it is
orthogonal to both the immutable-mutable-const and the shared-local-scope
(a.k.a. shared-heap-stack) type trees. The primary reasons for proposing
yet another type qualifier is bug 2095 and the lack of an equivalent of
‘const’ for the shared-local-scope storage type system.
Regarding Bug 2095
Bug 2095 occurs because a reference to a reference type is implicitly
convertible to a reference to a reference super-type. i.e.
class A {}
class B : A {}
B[] b = new B[10];
A[] a = b; // Practically, this is really important
a[0] = new A(); // But allowing this causes bugs, since b has also
changed
Now, the implicit conversion of B[] to A[] is really important for subtype
and all functions operating on arrays of the super-type in order to avoid
casting left, right and center. However, if the implicitly converted
super-type array is assigned to, and then the original array is accessed,
random code execution can occur. Similarly, immutability can be
circumvented.
Final solves this problem by providing an alternative implicit conversion
of B[] to final A[]. E.g.
final A[] fa = b; // Still valid, and mutability/storage isn’t changed.
a[0] = new A(); // But now this is a compile time error.
Regarding shared-local-scope
The storage type system, consisting of shared, local and scope provides
many correctness and performance advantages. However, akin to mutable and
immutable there is no safe way to cast between them. This is an undesired
situation as it would require three separate variants of most functions.
The currently allowed implicit conversion between local and scope has
resulted in functions being prevented from returning explicit scope types.
However, a scope object can still escape, resulting in bugs.
Final references can not escape a scope as they can only be assigned at
declaration. Thus, final scope and final local may be implicitly casted
between themselves and either may be implicitly casted to final shared.
(The reason final shared may not be implicitly casted to final local or
final scope is that member variables will be protected by memory fences
and thus are accessed differently at the machine level)
Regarding Transitivity
Though bug 2095 would require arrays of array, etc to be implicitly
converted to final in a transitive manner, complete transitivity is not
required and represents a limitation on the actual objects. It is the need
to guarantee that final variables can not result in an object escaping its
scope that motivates full transitivity. In a situation where the storage
type of member variables and member returns is dictated by an object’s own
storage type, the implicit casting of final-storage-types results in the
member variable and return storage types to become unknown, necessitating
the implicit casting of them to final and thus transitivity.
Additional ‘final’ restrictions
Functions whose return type is final may result an object’s escape. This
can occur only when the returned object is final or scope. While returning
scope types is already illegal, but returning a final object may be needed
and logically valid. However, as a potentially dangerous operation
explicit casting should be required for final types. Another possible
escape could occur when final member variables are assigned during
construction and explicit casts should again be required.
More information about the Digitalmars-d
mailing list