Before JSR 308, the contract of type bindings was simply: the 'same' type is always represented by the same instance of TypeBinding, allowing for == to be used for checking type equality.
With type annotations in the picture, and specifically with the compiler's interest to fully evaluate those for null analysis, identity of type bindings has become a more complex topic.
The new methods TypeBinding.equalsEquals() and TypeBinding.notEquals() consider those types as equal that have the same id. The intention is that types that differ only in type annotations should share the same id.
Internally, methods like TypeSystem.getUnannotatedType() are based on the invariant that each array TypeSystem.types[id] has the "unannotated" type at position 0.
Caveat: while all types with the same id appear in the same TypeSystem.types[id], that array may also contain other derived types with different id, e.g., array, parameterized, raw types of the generic type.
That invariant may be broken, when a type variable has an annotation (semantically a declaration annotation on the type parameter, but technically a type annotation). In this case the type at position 0 is "the original type", which may get annotations set, possibly after the type has been registered in the AnnotatableTypeSystem. For that situation TypeSystem.forceRegisterAsDerived() implements an artificial swap, inserting a clone without annotations into position 0 and moving "the original type" to position 1. This caused problems when the original type was already used in a PTBKey - the problem locally fixed in #2173 .
Additionally, we have the concept of TypeBinding.prototype(). Normally this.prototype() == this, but when an annotated variant is created by ATS, then a clone is created, which remembers the original as its "prototype".
Hence we have an interesting (non-trivial) relationship between TypeSystem.getUnannotatedType() and TypeBinding.prototype(), which deserves a fresh look.
In this issue I plan to investigate the relationships between "unannotated type", "originally declared type" and "prototype". The initial question will be: Is it suitable to use the "unannotated type" as the primordial concept or should a type variable with an annotation (technically type annotation, semantically declaration annotation, see above) be considered the primordial thing, and the unannotated variant a derived thing in this case?
If these things can be clearly separated, then all magic in TypeSystem.forceRegisterAsDerived() plus the fix in #2173 could become obsolete, hopefully providing a more consistent overall story.
Follow-up from #2173:
Before JSR 308, the contract of type bindings was simply: the 'same' type is always represented by the same instance of
TypeBinding
, allowing for==
to be used for checking type equality.With type annotations in the picture, and specifically with the compiler's interest to fully evaluate those for null analysis, identity of type bindings has become a more complex topic.
The new methods
TypeBinding.equalsEquals()
andTypeBinding.notEquals()
consider those types as equal that have the sameid
. The intention is that types that differ only in type annotations should share the same id.Internally, methods like
TypeSystem.getUnannotatedType()
are based on the invariant that each arrayTypeSystem.types[id]
has the "unannotated" type at position 0.Caveat: while all types with the same id appear in the same
TypeSystem.types[id]
, that array may also contain other derived types with different id, e.g., array, parameterized, raw types of the generic type.That invariant may be broken, when a type variable has an annotation (semantically a declaration annotation on the type parameter, but technically a type annotation). In this case the type at position 0 is "the original type", which may get annotations set, possibly after the type has been registered in the
AnnotatableTypeSystem
. For that situationTypeSystem.forceRegisterAsDerived()
implements an artificial swap, inserting a clone without annotations into position 0 and moving "the original type" to position 1. This caused problems when the original type was already used in aPTBKey
- the problem locally fixed in #2173 .Additionally, we have the concept of
TypeBinding.prototype()
. Normallythis.prototype() == this
, but when an annotated variant is created byATS
, then a clone is created, which remembers the original as its "prototype".Hence we have an interesting (non-trivial) relationship between
TypeSystem.getUnannotatedType()
andTypeBinding.prototype()
, which deserves a fresh look.In this issue I plan to investigate the relationships between "unannotated type", "originally declared type" and "prototype". The initial question will be: Is it suitable to use the "unannotated type" as the primordial concept or should a type variable with an annotation (technically type annotation, semantically declaration annotation, see above) be considered the primordial thing, and the unannotated variant a derived thing in this case?
If these things can be clearly separated, then all magic in
TypeSystem.forceRegisterAsDerived()
plus the fix in #2173 could become obsolete, hopefully providing a more consistent overall story.