March 22, 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.
Top | Discussion index | About this forum | D home