Copyright (c) 2000, 2019 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation Stephan Herrmann - Contributions for bug 282152 - [1.5][compiler] Generics code rejected by Eclipse but accepted by javac bug 349326 - [1.7] new warning for missing try-with-resources bug 359362 - FUP of bug 349326: Resource leak on non-Closeable resource bug 358903 - Filter practically unimportant resource leak warnings bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation. bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files Bug 415043 - [1.8][null] Follow-up re null type annotations after bug 392099 Bug 417295 - [1.8[[null] Massage type annotated null analysis to gel well with deep encoded type bindings. Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec) Bug 426792 - [1.8][inference][impl] generify new type inference engine Bug 428019 - [1.8][compiler] Type inference failure with nested generic invocation. Bug 429384 - [1.8][null] implement conformance rules for null-annotated lower / upper type bounds Bug 431269 - [1.8][compiler][null] StackOverflow in nullAnnotatedReadableName Bug 431408 - Java 8 (1.8) generics bug Bug 435962 - [RC2] StackOverFlowError when building Bug 438458 - [1.8][null] clean up handling of null type annotations wrt type variables Bug 438250 - [1.8][null] NPE trying to report bogus null annotation conflict Bug 438179 - [1.8][null] 'Contradictory null annotations' error on type variable with explicit null-annotation. Bug 440143 - [1.8][null] one more case of contradictory null annotations regarding type variables Bug 440759 - [1.8][null] @NonNullByDefault should never affect wildcards and uses of a type variable Bug 441693 - [1.8][null] Bogus warning for type argument annotated with @NonNull Bug 456497 - [1.8][null] during inference nullness from target type is lost against weaker hint from applicability analysis Bug 456459 - Discrepancy between Eclipse compiler and javac - Enums, interfaces, and generics Bug 456487 - [1.8][null] @Nullable type variant of @NonNull-constrained type parameter causes grief Bug 462790 - [null] NPE in Expression.computeConversion() Bug 456532 - [1.8][null] ReferenceBinding.appendNullAnnotation() includes phantom annotations in error messages Jesper S Møller - Contributions for bug 381345 : [1.8] Take care of the Java 8 major version Bug 527554 - [18.3] Compiler support for JEP 286 Local-Variable Type
/******************************************************************************* * Copyright (c) 2000, 2019 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for * bug 282152 - [1.5][compiler] Generics code rejected by Eclipse but accepted by javac * bug 349326 - [1.7] new warning for missing try-with-resources * bug 359362 - FUP of bug 349326: Resource leak on non-Closeable resource * bug 358903 - Filter practically unimportant resource leak warnings * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation. * bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files * Bug 415043 - [1.8][null] Follow-up re null type annotations after bug 392099 * Bug 417295 - [1.8[[null] Massage type annotated null analysis to gel well with deep encoded type bindings. * Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec) * Bug 426792 - [1.8][inference][impl] generify new type inference engine * Bug 428019 - [1.8][compiler] Type inference failure with nested generic invocation. * Bug 429384 - [1.8][null] implement conformance rules for null-annotated lower / upper type bounds * Bug 431269 - [1.8][compiler][null] StackOverflow in nullAnnotatedReadableName * Bug 431408 - Java 8 (1.8) generics bug * Bug 435962 - [RC2] StackOverFlowError when building * Bug 438458 - [1.8][null] clean up handling of null type annotations wrt type variables * Bug 438250 - [1.8][null] NPE trying to report bogus null annotation conflict * Bug 438179 - [1.8][null] 'Contradictory null annotations' error on type variable with explicit null-annotation. * Bug 440143 - [1.8][null] one more case of contradictory null annotations regarding type variables * Bug 440759 - [1.8][null] @NonNullByDefault should never affect wildcards and uses of a type variable * Bug 441693 - [1.8][null] Bogus warning for type argument annotated with @NonNull * Bug 456497 - [1.8][null] during inference nullness from target type is lost against weaker hint from applicability analysis * Bug 456459 - Discrepancy between Eclipse compiler and javac - Enums, interfaces, and generics * Bug 456487 - [1.8][null] @Nullable type variant of @NonNull-constrained type parameter causes grief * Bug 462790 - [null] NPE in Expression.computeConversion() * Bug 456532 - [1.8][null] ReferenceBinding.appendNullAnnotation() includes phantom annotations in error messages * Jesper S Møller <jesper@selskabet.org> - Contributions for bug 381345 : [1.8] Take care of the Java 8 major version * Bug 527554 - [18.3] Compiler support for JEP 286 Local-Variable Type *******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup; import java.util.Arrays; import java.util.Set; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.NullAnnotationMatching; import org.eclipse.jdt.internal.compiler.ast.NullAnnotationMatching.CheckMode; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants.BoundCheckStatus;
Binding for a type parameter, held by source/binary type or method.
/** * Binding for a type parameter, held by source/binary type or method. */
public class TypeVariableBinding extends ReferenceBinding { public Binding declaringElement; // binding of declaring type or method public int rank; // declaration rank, can be used to match variable in parameterized type
Denote the first explicit (binding) bound amongst the supertypes (from declaration in source) If no superclass was specified, then it denotes the first superinterface, or null if none was specified.
/** * Denote the first explicit (binding) bound amongst the supertypes (from declaration in source) * If no superclass was specified, then it denotes the first superinterface, or null if none was specified. */
public TypeBinding firstBound; // MUST NOT be modified directly, use setter ! // actual resolved variable supertypes (if no superclass bound, then associated to Object) public ReferenceBinding superclass; // MUST NOT be modified directly, use setter ! public ReferenceBinding[] superInterfaces; // MUST NOT be modified directly, use setter ! public char[] genericTypeSignature; LookupEnvironment environment; public TypeVariableBinding(char[] sourceName, Binding declaringElement, int rank, LookupEnvironment environment) { this.sourceName = sourceName; this.declaringElement = declaringElement; this.rank = rank; this.modifiers = ClassFileConstants.AccPublic | ExtraCompilerModifiers.AccGenericSignature; // treat type var as public this.tagBits |= TagBits.HasTypeVariable; this.environment = environment; this.typeBits = TypeIds.BitUninitialized; computeId(environment); } // for subclass CaptureBinding protected TypeVariableBinding(char[] sourceName, LookupEnvironment environment) { this.sourceName = sourceName; this.modifiers = ClassFileConstants.AccPublic | ExtraCompilerModifiers.AccGenericSignature; // treat type var as public this.tagBits |= TagBits.HasTypeVariable; this.environment = environment; this.typeBits = TypeIds.BitUninitialized; // don't yet compute the ID! } public TypeVariableBinding(TypeVariableBinding prototype) { super(prototype); this.declaringElement = prototype.declaringElement; this.rank = prototype.rank; this.firstBound = prototype.firstBound; this.superclass = prototype.superclass; if (prototype.superInterfaces != null) { int len = prototype.superInterfaces.length; if (len > 0) System.arraycopy(prototype.superInterfaces, 0, this.superInterfaces = new ReferenceBinding[len], 0, len); else this.superInterfaces = Binding.NO_SUPERINTERFACES; } this.genericTypeSignature = prototype.genericTypeSignature; this.environment = prototype.environment; prototype.tagBits |= TagBits.HasAnnotatedVariants; this.tagBits &= ~TagBits.HasAnnotatedVariants; }
Returns true if the argument type satisfies all bounds of the type parameter
Params:
  • location – if non-null this may be used for reporting errors relating to null type annotations (if enabled)
/** * Returns true if the argument type satisfies all bounds of the type parameter * @param location if non-null this may be used for reporting errors relating to null type annotations (if enabled) */
public TypeConstants.BoundCheckStatus boundCheck(Substitution substitution, TypeBinding argumentType, Scope scope, ASTNode location) { TypeConstants.BoundCheckStatus code = internalBoundCheck(substitution, argumentType, scope, location); if (code == BoundCheckStatus.MISMATCH) { if (argumentType instanceof TypeVariableBinding && scope != null) { TypeBinding bound = ((TypeVariableBinding)argumentType).firstBound; if (bound instanceof ParameterizedTypeBinding) { BoundCheckStatus code2 = boundCheck(substitution, bound.capture(scope, -1, -1), scope, location); // no capture position needed as this capture will never escape this context return code.betterOf(code2); } } } return code; } private TypeConstants.BoundCheckStatus internalBoundCheck(Substitution substitution, TypeBinding argumentType, Scope scope, ASTNode location) { if (argumentType == TypeBinding.NULL || TypeBinding.equalsEquals(argumentType, this)) { return BoundCheckStatus.OK; } boolean hasSubstitution = substitution != null; if (!(argumentType instanceof ReferenceBinding || argumentType.isArrayType())) return BoundCheckStatus.MISMATCH; // special case for re-entrant source types (selection, code assist, etc)... // can request additional types during hierarchy walk that are found as source types that also 'need' to connect their hierarchy if (this.superclass == null) return BoundCheckStatus.OK; BoundCheckStatus nullStatus = BoundCheckStatus.OK; boolean checkNullAnnotations = scope.environment().usesNullTypeAnnotations() && (location == null || (location.bits & ASTNode.InsideJavadoc) == 0); if (argumentType.kind() == Binding.WILDCARD_TYPE) { WildcardBinding wildcard = (WildcardBinding) argumentType; switch(wildcard.boundKind) { case Wildcard.EXTENDS : boolean checkedAsOK = false; TypeBinding wildcardBound = wildcard.bound; if (TypeBinding.equalsEquals(wildcardBound, this)) checkedAsOK = true; // OK per JLS, but may require null checking below boolean isArrayBound = wildcardBound.isArrayType(); if (!wildcardBound.isInterface()) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superclass) : this.superclass; if (!checkedAsOK) { if (substitutedSuperType.id != TypeIds.T_JavaLangObject) { if (isArrayBound) { if (!wildcardBound.isCompatibleWith(substitutedSuperType, scope)) return BoundCheckStatus.MISMATCH; } else { TypeBinding match = wildcardBound.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null) { if (substitutedSuperType.isProvablyDistinct(match)) { return BoundCheckStatus.MISMATCH; } } else { match = substitutedSuperType.findSuperTypeOriginatingFrom(wildcardBound); if (match != null) { if (match.isProvablyDistinct(wildcardBound)) { return BoundCheckStatus.MISMATCH; } } else { if (denotesRelevantSuperClass(wildcardBound) && denotesRelevantSuperClass(substitutedSuperType)) { // non-object real superclass should have produced a valid 'match' above return BoundCheckStatus.MISMATCH; } // not fully spec-ed in JLS, but based on email communication (2017-09-13): // (a) bound check should apply capture // (b) capture applies glb // (c) and then the glb should be checked for well-formedness (see Scope.isMalformedPair() - this part missing in JLS). // Since we don't do (a), nor (b) for this case, we just directly proceed to (b) here. // For (a) see ParameterizedTypeBinding.boundCheck() - comment added as of this commit // for (b) see CaptureBinding.initializeBounds() - comment added as of this commit if (Scope.greaterLowerBound(new TypeBinding[] {substitutedSuperType, wildcardBound}, scope, this.environment) == null) { return BoundCheckStatus.MISMATCH; } } } } } } if (checkNullAnnotations && argumentType.hasNullTypeAnnotations()) { nullStatus = nullBoundCheck(scope, argumentType, substitutedSuperType, substitution, location, nullStatus); } } boolean mustImplement = isArrayBound || ((ReferenceBinding)wildcardBound).isFinal(); for (int i = 0, length = this.superInterfaces.length; i < length; i++) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superInterfaces[i]) : this.superInterfaces[i]; if (!checkedAsOK) { if (isArrayBound) { if (!wildcardBound.isCompatibleWith(substitutedSuperType, scope)) return BoundCheckStatus.MISMATCH; } else { TypeBinding match = wildcardBound.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null) { if (substitutedSuperType.isProvablyDistinct(match)) { return BoundCheckStatus.MISMATCH; } } else if (mustImplement) { return BoundCheckStatus.MISMATCH; // cannot be extended further to satisfy missing bounds } } } if (checkNullAnnotations && argumentType.hasNullTypeAnnotations()) { nullStatus = nullBoundCheck(scope, argumentType, substitutedSuperType, substitution, location, nullStatus); } } if (nullStatus != null) return nullStatus; break; case Wildcard.SUPER : // if the wildcard is lower-bounded by a type variable that has no relevant upper bound there's nothing to check here (bug 282152): if (wildcard.bound.isTypeVariable() && ((TypeVariableBinding)wildcard.bound).superclass.id == TypeIds.T_JavaLangObject) { return nullBoundCheck(scope, argumentType, null, substitution, location, nullStatus); } else { TypeBinding bound = wildcard.bound; if (checkNullAnnotations && this.environment.containsNullTypeAnnotation(wildcard.typeAnnotations)) bound = this.environment.createAnnotatedType(bound.withoutToplevelNullAnnotation(), wildcard.getTypeAnnotations()); BoundCheckStatus status = boundCheck(substitution, bound, scope, null); // do not report null-errors against the tweaked bound ... if (status == BoundCheckStatus.NULL_PROBLEM && location != null) scope.problemReporter().nullityMismatchTypeArgument(this, wildcard, location); // ... but against the wildcard return status; } case Wildcard.UNBOUND : if (checkNullAnnotations && argumentType.hasNullTypeAnnotations()) { return nullBoundCheck(scope, argumentType, null, substitution, location, nullStatus); } break; } return BoundCheckStatus.OK; } boolean unchecked = false; if (this.superclass.id != TypeIds.T_JavaLangObject) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superclass) : this.superclass; if (TypeBinding.notEquals(substitutedSuperType, argumentType)) { if (!argumentType.isCompatibleWith(substitutedSuperType, scope)) { return BoundCheckStatus.MISMATCH; } TypeBinding match = argumentType.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null){ // Enum#RAW is not a substitute for <E extends Enum<E>> (86838) if (match.isRawType() && substitutedSuperType.isBoundParameterizedType()) unchecked = true; } } if (checkNullAnnotations) { nullStatus = nullBoundCheck(scope, argumentType, substitutedSuperType, substitution, location, nullStatus); } } for (int i = 0, length = this.superInterfaces.length; i < length; i++) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superInterfaces[i]) : this.superInterfaces[i]; if (TypeBinding.notEquals(substitutedSuperType, argumentType)) { if (!argumentType.isCompatibleWith(substitutedSuperType, scope)) { return BoundCheckStatus.MISMATCH; } TypeBinding match = argumentType.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null){ // Enum#RAW is not a substitute for <E extends Enum<E>> (86838) if (match.isRawType() && substitutedSuperType.isBoundParameterizedType()) unchecked = true; } } if (checkNullAnnotations) { nullStatus = nullBoundCheck(scope, argumentType, substitutedSuperType, substitution, location, nullStatus); } } if (checkNullAnnotations && nullStatus != BoundCheckStatus.NULL_PROBLEM) { long nullBits = this.tagBits & TagBits.AnnotationNullMASK; if (nullBits != 0 && nullBits != (argumentType.tagBits & TagBits.AnnotationNullMASK)) { if (location != null) scope.problemReporter().nullityMismatchTypeArgument(this, argumentType, location); nullStatus = BoundCheckStatus.NULL_PROBLEM; } } return unchecked ? BoundCheckStatus.UNCHECKED : nullStatus != null ? nullStatus : BoundCheckStatus.OK; } private BoundCheckStatus nullBoundCheck(Scope scope, TypeBinding argumentType, TypeBinding substitutedSuperType, Substitution substitution, ASTNode location, BoundCheckStatus previousStatus) { if (NullAnnotationMatching.analyse(this, argumentType, substitutedSuperType, substitution, -1, null, CheckMode.BOUND_CHECK).isAnyMismatch()) { if (location != null) scope.problemReporter().nullityMismatchTypeArgument(this, argumentType, location); return BoundCheckStatus.NULL_PROBLEM; } return previousStatus; } boolean denotesRelevantSuperClass(TypeBinding type) { if (!type.isTypeVariable() && !type.isInterface() && type.id != TypeIds.T_JavaLangObject) return true; ReferenceBinding aSuperClass = type.superclass(); return aSuperClass != null && aSuperClass.id != TypeIds.T_JavaLangObject && !aSuperClass.isTypeVariable(); } public int boundsCount() { if (this.firstBound == null) return 0; if (this.firstBound.isInterface()) return this.superInterfaces.length; // only interface bounds return this.superInterfaces.length + 1; // class or array type isn't contained in superInterfaces }
See Also:
  • canBeInstantiated.canBeInstantiated()
/** * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#canBeInstantiated() */
@Override public boolean canBeInstantiated() { return false; }
Collect the substitutes into a map for certain type variables inside the receiver type e.g. Collection.collectSubstitutes(Collection>, Map), will populate Map with: T --> List Constraints: A << F corresponds to: F.collectSubstitutes(..., A, ..., CONSTRAINT_EXTENDS (1)) A = F corresponds to: F.collectSubstitutes(..., A, ..., CONSTRAINT_EQUAL (0)) A >> F corresponds to: F.collectSubstitutes(..., A, ..., CONSTRAINT_SUPER (2))
/** * Collect the substitutes into a map for certain type variables inside the receiver type * e.g. Collection<T>.collectSubstitutes(Collection<List<X>>, Map), will populate Map with: T --> List<X> * Constraints: * A << F corresponds to: F.collectSubstitutes(..., A, ..., CONSTRAINT_EXTENDS (1)) * A = F corresponds to: F.collectSubstitutes(..., A, ..., CONSTRAINT_EQUAL (0)) * A >> F corresponds to: F.collectSubstitutes(..., A, ..., CONSTRAINT_SUPER (2)) */
@Override public void collectSubstitutes(Scope scope, TypeBinding actualType, InferenceContext inferenceContext, int constraint) { // only infer for type params of the generic method if (this.declaringElement != inferenceContext.genericMethod) return; // cannot infer anything from a null type switch (actualType.kind()) { case Binding.BASE_TYPE : if (actualType == TypeBinding.NULL) return; TypeBinding boxedType = scope.environment().computeBoxingType(actualType); if (boxedType == actualType) return; //$IDENTITY-COMPARISON$ actualType = boxedType; break; case Binding.POLY_TYPE: // cannot steer inference, only learn from it. case Binding.WILDCARD_TYPE : return; // wildcards are not true type expressions (JLS 15.12.2.7, p.453 2nd discussion) } // reverse constraint, to reflect variable on rhs: A << T --> T >: A int variableConstraint; switch(constraint) { case TypeConstants.CONSTRAINT_EQUAL : variableConstraint = TypeConstants.CONSTRAINT_EQUAL; break; case TypeConstants.CONSTRAINT_EXTENDS : variableConstraint = TypeConstants.CONSTRAINT_SUPER; break; default: //case CONSTRAINT_SUPER : variableConstraint =TypeConstants.CONSTRAINT_EXTENDS; break; } inferenceContext.recordSubstitute(this, actualType, variableConstraint); } /* * declaringUniqueKey : genericTypeSignature * p.X<T> { ... } --> Lp/X;:TT; * p.X { <T> void foo() {...} } --> Lp/X;.foo()V:TT; */ @Override public char[] computeUniqueKey(boolean isLeaf) { StringBuffer buffer = new StringBuffer(); Binding declaring = this.declaringElement; if (!isLeaf && declaring.kind() == Binding.METHOD) { // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=97902 MethodBinding methodBinding = (MethodBinding) declaring; ReferenceBinding declaringClass = methodBinding.declaringClass; buffer.append(declaringClass.computeUniqueKey(false/*not a leaf*/)); buffer.append(':'); MethodBinding[] methods = declaringClass.methods(); if (methods != null) for (int i = 0, length = methods.length; i < length; i++) { MethodBinding binding = methods[i]; if (binding == methodBinding) { buffer.append(i); break; } } } else { buffer.append(declaring.computeUniqueKey(false/*not a leaf*/)); buffer.append(':'); } buffer.append(genericTypeSignature()); int length = buffer.length(); char[] uniqueKey = new char[length]; buffer.getChars(0, length, uniqueKey, 0); return uniqueKey; } @Override public char[] constantPoolName() { /* java/lang/Object */ if (this.firstBound != null) { return this.firstBound.constantPoolName(); } return this.superclass.constantPoolName(); // java/lang/Object } @Override public TypeBinding clone(TypeBinding enclosingType) { return new TypeVariableBinding(this); } @Override public String annotatedDebugName() { StringBuffer buffer = new StringBuffer(10); buffer.append(super.annotatedDebugName()); if (!this.inRecursiveFunction) { this.inRecursiveFunction = true; try { if (this.superclass != null && TypeBinding.equalsEquals(this.firstBound, this.superclass)) { buffer.append(" extends ").append(this.superclass.annotatedDebugName()); //$NON-NLS-1$ } if (this.superInterfaces != null && this.superInterfaces != Binding.NO_SUPERINTERFACES) { if (TypeBinding.notEquals(this.firstBound, this.superclass)) { buffer.append(" extends "); //$NON-NLS-1$ } for (int i = 0, length = this.superInterfaces.length; i < length; i++) { if (i > 0 || TypeBinding.equalsEquals(this.firstBound, this.superclass)) { buffer.append(" & "); //$NON-NLS-1$ } buffer.append(this.superInterfaces[i].annotatedDebugName()); } } } finally { this.inRecursiveFunction = false; } } return buffer.toString(); }
See Also:
  • debugName.debugName()
/** * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#debugName() */
@Override public String debugName() { if (this.hasTypeAnnotations()) return super.annotatedDebugName(); return new String(this.sourceName); } @Override public TypeBinding erasure() { if (this.firstBound != null) { return this.firstBound.erasure(); } return this.superclass; // java/lang/Object }
T::Ljava/util/Map;:Ljava/io/Serializable; T:LY
/** * T::Ljava/util/Map;:Ljava/io/Serializable; * T:LY<TT;> */
public char[] genericSignature() { StringBuffer sig = new StringBuffer(10); sig.append(this.sourceName).append(':'); int interfaceLength = this.superInterfaces == null ? 0 : this.superInterfaces.length; if (interfaceLength == 0 || TypeBinding.equalsEquals(this.firstBound, this.superclass)) { if (this.superclass != null) sig.append(this.superclass.genericTypeSignature()); } for (int i = 0; i < interfaceLength; i++) { sig.append(':').append(this.superInterfaces[i].genericTypeSignature()); } int sigLength = sig.length(); char[] genericSignature = new char[sigLength]; sig.getChars(0, sigLength, genericSignature, 0); return genericSignature; }
T::Ljava/util/Map;:Ljava/io/Serializable; T:LY
/** * T::Ljava/util/Map;:Ljava/io/Serializable; * T:LY<TT;> */
@Override public char[] genericTypeSignature() { if (this.genericTypeSignature != null) return this.genericTypeSignature; return this.genericTypeSignature = CharOperation.concat('T', this.sourceName, ';'); }
Compute the initial type bounds for one inference variable as per JLS8 sect 18.1.3.
/** * Compute the initial type bounds for one inference variable as per JLS8 sect 18.1.3. */
TypeBound[] getTypeBounds(InferenceVariable variable, InferenceSubstitution theta) { int n = boundsCount(); if (n == 0) return NO_TYPE_BOUNDS; TypeBound[] bounds = new TypeBound[n]; int idx = 0; if (!this.firstBound.isInterface()) bounds[idx++] = TypeBound.createBoundOrDependency(theta, this.firstBound, variable); for (int i = 0; i < this.superInterfaces.length; i++) bounds[idx++] = TypeBound.createBoundOrDependency(theta, this.superInterfaces[i], variable); return bounds; } boolean hasOnlyRawBounds() { if (this.superclass != null && TypeBinding.equalsEquals(this.firstBound, this.superclass)) if (!this.superclass.isRawType()) return false; if (this.superInterfaces != null) for (int i = 0, l = this.superInterfaces.length; i < l; i++) if (!this.superInterfaces[i].isRawType()) return false; return true; } @Override public boolean hasTypeBit(int bit) { if (this.typeBits == TypeIds.BitUninitialized) { // initialize from bounds this.typeBits = 0; if (this.superclass != null && this.superclass.hasTypeBit(~TypeIds.BitUninitialized)) this.typeBits |= (this.superclass.typeBits & TypeIds.InheritableBits); if (this.superInterfaces != null) for (int i = 0, l = this.superInterfaces.length; i < l; i++) if (this.superInterfaces[i].hasTypeBit(~TypeIds.BitUninitialized)) this.typeBits |= (this.superInterfaces[i].typeBits & TypeIds.InheritableBits); } return (this.typeBits & bit) != 0; }
Returns true if the type variable is directly bound to a given type
/** * Returns true if the type variable is directly bound to a given type */
public boolean isErasureBoundTo(TypeBinding type) { if (TypeBinding.equalsEquals(this.superclass.erasure(), type)) return true; for (int i = 0, length = this.superInterfaces.length; i < length; i++) { if (TypeBinding.equalsEquals(this.superInterfaces[i].erasure(), type)) return true; } return false; } @Override public boolean isHierarchyConnected() { return (this.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0; }
Returns true if the 2 variables are playing exact same role: they have the same bounds, providing one is substituted with the other: > is interchangeable with >.
/** * Returns true if the 2 variables are playing exact same role: they have * the same bounds, providing one is substituted with the other: <T1 extends * List<T1>> is interchangeable with <T2 extends List<T2>>. */
public boolean isInterchangeableWith(TypeVariableBinding otherVariable, Substitution substitute) { if (TypeBinding.equalsEquals(this, otherVariable)) return true; int length = this.superInterfaces.length; if (length != otherVariable.superInterfaces.length) return false; if (TypeBinding.notEquals(this.superclass, Scope.substitute(substitute, otherVariable.superclass))) return false; next : for (int i = 0; i < length; i++) { TypeBinding superType = Scope.substitute(substitute, otherVariable.superInterfaces[i]); for (int j = 0; j < length; j++) if (TypeBinding.equalsEquals(superType, this.superInterfaces[j])) continue next; return false; // not a match } return true; } @Override public boolean isSubtypeOf(TypeBinding other, boolean simulatingBugJDK8026527) { if (isSubTypeOfRTL(other)) return true; if (this.firstBound != null && this.firstBound.isSubtypeOf(other, simulatingBugJDK8026527)) return true; if (this.superclass != null && this.superclass.isSubtypeOf(other, simulatingBugJDK8026527)) return true; if (this.superInterfaces != null) for (int i = 0, l = this.superInterfaces.length; i < l; i++) if (this.superInterfaces[i].isSubtypeOf(other, false)) return true; return other.id == TypeIds.T_JavaLangObject; } // to prevent infinite recursion when inspecting recursive generics: boolean inRecursiveFunction = false; @Override public boolean enterRecursiveFunction() { if (this.inRecursiveFunction) return false; this.inRecursiveFunction = true; return true; } @Override public void exitRecursiveFunction() { this.inRecursiveFunction = false; } // to prevent infinite recursion when inspecting recursive generics: boolean inRecursiveProjectionFunction = false; public boolean enterRecursiveProjectionFunction() { if (this.inRecursiveProjectionFunction) return false; this.inRecursiveProjectionFunction = true; return true; } public void exitRecursiveProjectionFunction() { this.inRecursiveProjectionFunction = false; } @Override public boolean isProperType(boolean admitCapture18) { // handle recursive calls: if (this.inRecursiveFunction) // be optimistic, since this node is not an inference variable return true; this.inRecursiveFunction = true; try { if (this.superclass != null && !this.superclass.isProperType(admitCapture18)) { return false; } if (this.superInterfaces != null) for (int i = 0, l = this.superInterfaces.length; i < l; i++) if (!this.superInterfaces[i].isProperType(admitCapture18)) { return false; } return true; } finally { this.inRecursiveFunction = false; } } @Override TypeBinding substituteInferenceVariable(InferenceVariable var, TypeBinding substituteType) { if (this.inRecursiveFunction) return this; this.inRecursiveFunction = true; try { boolean haveSubstitution = false; ReferenceBinding currentSuperclass = this.superclass; if (currentSuperclass != null) { currentSuperclass = (ReferenceBinding) currentSuperclass.substituteInferenceVariable(var, substituteType); haveSubstitution |= TypeBinding.notEquals(currentSuperclass, this.superclass); } ReferenceBinding[] currentSuperInterfaces = null; if (this.superInterfaces != null) { int length = this.superInterfaces.length; if (haveSubstitution) System.arraycopy(this.superInterfaces, 0, currentSuperInterfaces=new ReferenceBinding[length], 0, length); for (int i = 0; i < length; i++) { ReferenceBinding currentSuperInterface = this.superInterfaces[i]; if (currentSuperInterface != null) { currentSuperInterface = (ReferenceBinding) currentSuperInterface.substituteInferenceVariable(var, substituteType); if (TypeBinding.notEquals(currentSuperInterface, this.superInterfaces[i])) { if (currentSuperInterfaces == null) System.arraycopy(this.superInterfaces, 0, currentSuperInterfaces=new ReferenceBinding[length], 0, length); currentSuperInterfaces[i] = currentSuperInterface; haveSubstitution = true; } } } } if (haveSubstitution) { TypeVariableBinding newVar = new TypeVariableBinding(this.sourceName, this.declaringElement, this.rank, this.environment); newVar.superclass = currentSuperclass; newVar.superInterfaces = currentSuperInterfaces; newVar.tagBits = this.tagBits; return newVar; } return this; } finally { this.inRecursiveFunction = false; } }
Returns true if the type was declared as a type variable
/** * Returns true if the type was declared as a type variable */
@Override public boolean isTypeVariable() { return true; } // /** // * Returns the original type variable for a given variable. // * Only different from receiver for type variables of generic methods of parameterized types // * e.g. X<U> { <V1 extends U> U foo(V1) } --> X<String> { <V2 extends String> String foo(V2) } // * and V2.original() --> V1 // */ // public TypeVariableBinding original() { // if (this.declaringElement.kind() == Binding.METHOD) { // MethodBinding originalMethod = ((MethodBinding)this.declaringElement).original(); // if (originalMethod != this.declaringElement) { // return originalMethod.typeVariables[this.rank]; // } // } else { // ReferenceBinding originalType = (ReferenceBinding)((ReferenceBinding)this.declaringElement).erasure(); // if (originalType != this.declaringElement) { // return originalType.typeVariables()[this.rank]; // } // } // return this; // } @Override public int kind() { return Binding.TYPE_PARAMETER; } @Override public boolean mentionsAny(TypeBinding[] parameters, int idx) { if (this.inRecursiveFunction) return false; // nothing seen this.inRecursiveFunction = true; try { if (super.mentionsAny(parameters, idx)) return true; if (this.superclass != null && this.superclass.mentionsAny(parameters, idx)) return true; if (this.superInterfaces != null) for (int j = 0; j < this.superInterfaces.length; j++) { if (this.superInterfaces[j].mentionsAny(parameters, idx)) return true; } return false; } finally { this.inRecursiveFunction = false; } } @Override void collectInferenceVariables(Set<InferenceVariable> variables) { if (this.inRecursiveFunction) return; // nothing seen this.inRecursiveFunction = true; try { if (this.superclass != null) this.superclass.collectInferenceVariables(variables); if (this.superInterfaces != null) for (int j = 0; j < this.superInterfaces.length; j++) { this.superInterfaces[j].collectInferenceVariables(variables); } } finally { this.inRecursiveFunction = false; } } public TypeBinding[] otherUpperBounds() { if (this.firstBound == null) return Binding.NO_TYPES; if (TypeBinding.equalsEquals(this.firstBound, this.superclass)) return this.superInterfaces; int otherLength = this.superInterfaces.length - 1; if (otherLength > 0) { TypeBinding[] otherBounds; System.arraycopy(this.superInterfaces, 1, otherBounds = new TypeBinding[otherLength], 0, otherLength); return otherBounds; } return Binding.NO_TYPES; }
See Also:
  • readableName.readableName()
/** * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#readableName() */
@Override public char[] readableName() { return this.sourceName; } ReferenceBinding resolve() { if ((this.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0) return this; long nullTagBits = this.tagBits & TagBits.AnnotationNullMASK; TypeBinding oldSuperclass = this.superclass, oldFirstInterface = null; if (this.superclass != null) { ReferenceBinding resolveType = (ReferenceBinding) BinaryTypeBinding.resolveType(this.superclass, this.environment, true /* raw conversion */); this.tagBits |= resolveType.tagBits & TagBits.ContainsNestedTypeReferences; long superNullTagBits = resolveType.tagBits & TagBits.AnnotationNullMASK; if (superNullTagBits != 0L) { if (nullTagBits == 0L) { if ((superNullTagBits & TagBits.AnnotationNonNull) != 0) { nullTagBits = superNullTagBits; } } else { // System.err.println("TODO(stephan): report proper error: conflict binary TypeVariable vs. first bound"); } } this.setSuperClass(resolveType); } ReferenceBinding[] interfaces = this.superInterfaces; int length; if ((length = interfaces.length) != 0) { oldFirstInterface = interfaces[0]; for (int i = length; --i >= 0;) { ReferenceBinding resolveType = (ReferenceBinding) BinaryTypeBinding.resolveType(interfaces[i], this.environment, true /* raw conversion */); this.tagBits |= resolveType.tagBits & TagBits.ContainsNestedTypeReferences; long superNullTagBits = resolveType.tagBits & TagBits.AnnotationNullMASK; if (superNullTagBits != 0L) { if (nullTagBits == 0L) { if ((superNullTagBits & TagBits.AnnotationNonNull) != 0) { nullTagBits = superNullTagBits; } } else { // System.err.println("TODO(stephan): report proper error: conflict binary TypeVariable vs. bound "+i); } } interfaces[i] = resolveType; } } if (nullTagBits != 0) this.tagBits |= nullTagBits | TagBits.HasNullTypeAnnotation; // refresh the firstBound in case it changed if (this.firstBound != null) { if (TypeBinding.equalsEquals(this.firstBound, oldSuperclass)) { this.setFirstBound(this.superclass); } else if (TypeBinding.equalsEquals(this.firstBound, oldFirstInterface)) { this.setFirstBound(interfaces[0]); } } this.modifiers &= ~ExtraCompilerModifiers.AccUnresolved; return this; } @Override public void setTypeAnnotations(AnnotationBinding[] annotations, boolean evalNullAnnotations) { if (getClass() == TypeVariableBinding.class) { // TVB only: if the declaration itself carries type annotations, // make sure TypeSystem will still have an unannotated variant at position 0, to answer getUnannotated() // (in this case the unannotated type is never explicit in source code, that's why we need this charade). this.environment.typeSystem.forceRegisterAsDerived(this); } else { this.environment.getUnannotatedType(this); // exposes original TVB/capture to type system for id stamping purposes. } super.setTypeAnnotations(annotations, evalNullAnnotations); }
See Also:
  • shortReadableName.shortReadableName()
/** * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#shortReadableName() */
@Override public char[] shortReadableName() { return readableName(); } @Override public ReferenceBinding superclass() { return this.superclass; } @Override public ReferenceBinding[] superInterfaces() { return this.superInterfaces; }
See Also:
  • toString.toString()
/** * @see java.lang.Object#toString() */
@Override public String toString() { if (this.hasTypeAnnotations()) return annotatedDebugName(); StringBuffer buffer = new StringBuffer(10); buffer.append('<').append(this.sourceName);//.append('[').append(this.rank).append(']'); if (this.superclass != null && TypeBinding.equalsEquals(this.firstBound, this.superclass)) { buffer.append(" extends ").append(this.superclass.debugName()); //$NON-NLS-1$ } if (this.superInterfaces != null && this.superInterfaces != Binding.NO_SUPERINTERFACES) { if (TypeBinding.notEquals(this.firstBound, this.superclass)) { buffer.append(" extends "); //$NON-NLS-1$ } for (int i = 0, length = this.superInterfaces.length; i < length; i++) { if (i > 0 || TypeBinding.equalsEquals(this.firstBound, this.superclass)) { buffer.append(" & "); //$NON-NLS-1$ } buffer.append(this.superInterfaces[i].debugName()); } } buffer.append('>'); return buffer.toString(); } @Override public char[] nullAnnotatedReadableName(CompilerOptions options, boolean shortNames) { StringBuffer nameBuffer = new StringBuffer(10); appendNullAnnotation(nameBuffer, options); nameBuffer.append(this.sourceName()); if (!this.inRecursiveFunction) { this.inRecursiveFunction = true; try { if (this.superclass != null && TypeBinding.equalsEquals(this.firstBound, this.superclass)) { nameBuffer.append(" extends ").append(this.superclass.nullAnnotatedReadableName(options, shortNames)); //$NON-NLS-1$ } if (this.superInterfaces != null && this.superInterfaces != Binding.NO_SUPERINTERFACES) { if (TypeBinding.notEquals(this.firstBound, this.superclass)) { nameBuffer.append(" extends "); //$NON-NLS-1$ } for (int i = 0, length = this.superInterfaces.length; i < length; i++) { if (i > 0 || TypeBinding.equalsEquals(this.firstBound, this.superclass)) { nameBuffer.append(" & "); //$NON-NLS-1$ } nameBuffer.append(this.superInterfaces[i].nullAnnotatedReadableName(options, shortNames)); } } } finally { this.inRecursiveFunction = false; } } int nameLength = nameBuffer.length(); char[] readableName = new char[nameLength]; nameBuffer.getChars(0, nameLength, readableName, 0); return readableName; } @Override protected void appendNullAnnotation(StringBuffer nameBuffer, CompilerOptions options) { int oldSize = nameBuffer.length(); super.appendNullAnnotation(nameBuffer, options); if (oldSize == nameBuffer.length()) { // nothing appended in super.appendNullAnnotation()? if (hasNullTypeAnnotations()) { // see if the prototype has null type annotations: TypeVariableBinding[] typeVariables = null; if (this.declaringElement instanceof ReferenceBinding) { typeVariables = ((ReferenceBinding) this.declaringElement).typeVariables(); } else if (this.declaringElement instanceof MethodBinding) { typeVariables = ((MethodBinding) this.declaringElement).typeVariables(); } if (typeVariables != null && typeVariables.length > this.rank) { TypeVariableBinding prototype = typeVariables[this.rank]; if (prototype != this)//$IDENTITY-COMPARISON$ prototype.appendNullAnnotation(nameBuffer, options); } } } } @Override public TypeBinding unannotated() { return this.hasTypeAnnotations() ? this.environment.getUnannotatedType(this) : this; } @Override public TypeBinding withoutToplevelNullAnnotation() { if (!hasNullTypeAnnotations()) return this; TypeBinding unannotated = this.environment.getUnannotatedType(this); AnnotationBinding[] newAnnotations = this.environment.filterNullTypeAnnotations(this.typeAnnotations); if (newAnnotations.length > 0) return this.environment.createAnnotatedType(unannotated, newAnnotations); return unannotated; }
Upper bound doesn't perform erasure
/** * Upper bound doesn't perform erasure */
public TypeBinding upperBound() { if (this.firstBound != null) { return this.firstBound; } return this.superclass; // java/lang/Object } public TypeBinding[] allUpperBounds() { if (this.superclass == null) return this.superInterfaces; if (this.superInterfaces == null || this.superInterfaces.length == 0) return new TypeBinding[] { this.superclass }; int nInterfaces = this.superInterfaces.length; TypeBinding[] all = Arrays.copyOf(this.superInterfaces, nInterfaces+1); all[nInterfaces] = this.superclass; return all; } public void evaluateNullAnnotations(Scope scope, TypeParameter parameter) { long nullTagBits = NullAnnotationMatching.validNullTagBits(this.tagBits); if (this.firstBound != null && this.firstBound.isValidBinding()) { long superNullTagBits = NullAnnotationMatching.validNullTagBits(this.firstBound.tagBits); if (superNullTagBits != 0L) { if (nullTagBits == 0L) { if ((superNullTagBits & TagBits.AnnotationNonNull) != 0) { nullTagBits = superNullTagBits; } } else if (superNullTagBits != nullTagBits) { if(parameter != null) this.firstBound = nullMismatchOnBound(parameter, this.firstBound, superNullTagBits, nullTagBits, scope); } } } ReferenceBinding[] interfaces = this.superInterfaces; int length; if (interfaces != null && (length = interfaces.length) != 0) { for (int i = length; --i >= 0;) { ReferenceBinding resolveType = interfaces[i]; long superNullTagBits = NullAnnotationMatching.validNullTagBits(resolveType.tagBits); if (superNullTagBits != 0L) { if (nullTagBits == 0L) { if ((superNullTagBits & TagBits.AnnotationNonNull) != 0) { nullTagBits = superNullTagBits; } } else if (superNullTagBits != nullTagBits) { if(parameter != null) interfaces[i] = (ReferenceBinding) nullMismatchOnBound(parameter, resolveType, superNullTagBits, nullTagBits, scope); } } } } if (nullTagBits != 0) this.tagBits |= nullTagBits | TagBits.HasNullTypeAnnotation; } private TypeBinding nullMismatchOnBound(TypeParameter parameter, TypeBinding boundType, long superNullTagBits, long nullTagBits, Scope scope) { // not finding bound should be considered a compiler bug TypeReference bound = findBound(boundType, parameter); Annotation ann = bound.findAnnotation(superNullTagBits); if (ann != null) { // explicit annotation: error scope.problemReporter().contradictoryNullAnnotationsOnBounds(ann, nullTagBits); this.tagBits &= ~TagBits.AnnotationNullMASK; } else { // implicit annotation: let the new one override return boundType.withoutToplevelNullAnnotation(); } return boundType; } private TypeReference findBound(TypeBinding bound, TypeParameter parameter) { if (parameter.type != null && TypeBinding.equalsEquals(parameter.type.resolvedType, bound)) return parameter.type; TypeReference[] bounds = parameter.bounds; if (bounds != null) { for (int i = 0; i < bounds.length; i++) { if (TypeBinding.equalsEquals(bounds[i].resolvedType, bound)) return bounds[i]; } } return null; } /* An annotated type variable use differs from its declaration exactly in its annotations and in nothing else. Propagate writes to all annotated variants so the clones evolve along. */ public TypeBinding setFirstBound(TypeBinding firstBound) { this.firstBound = firstBound; if ((this.tagBits & TagBits.HasAnnotatedVariants) != 0) { TypeBinding [] annotatedTypes = getDerivedTypesForDeferredInitialization(); for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { TypeVariableBinding annotatedType = (TypeVariableBinding) annotatedTypes[i]; annotatedType.firstBound = firstBound; } } if (firstBound != null && firstBound.hasNullTypeAnnotations()) this.tagBits |= TagBits.HasNullTypeAnnotation; return firstBound; } /* An annotated type variable use differs from its declaration exactly in its annotations and in nothing else. Propagate writes to all annotated variants so the clones evolve along. */ public ReferenceBinding setSuperClass(ReferenceBinding superclass) { this.superclass = superclass; if ((this.tagBits & TagBits.HasAnnotatedVariants) != 0) { TypeBinding [] annotatedTypes = getDerivedTypesForDeferredInitialization(); for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { TypeVariableBinding annotatedType = (TypeVariableBinding) annotatedTypes[i]; annotatedType.superclass = superclass; } } return superclass; } /* An annotated type variable use differs from its declaration exactly in its annotations and in nothing else. Propagate writes to all annotated variants so the clones evolve along. */ public ReferenceBinding [] setSuperInterfaces(ReferenceBinding[] superInterfaces) { this.superInterfaces = superInterfaces; if ((this.tagBits & TagBits.HasAnnotatedVariants) != 0) { TypeBinding [] annotatedTypes = getDerivedTypesForDeferredInitialization(); for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { TypeVariableBinding annotatedType = (TypeVariableBinding) annotatedTypes[i]; annotatedType.superInterfaces = superInterfaces; } } return superInterfaces; } protected TypeBinding[] getDerivedTypesForDeferredInitialization() { return this.environment.getAnnotatedTypes(this); } public TypeBinding combineTypeAnnotations(TypeBinding substitute) { if (hasTypeAnnotations()) { // may need to merge annotations from the original variable and from substitution: if (hasRelevantTypeUseNullAnnotations()) { // explicit type use null annotation overrides any annots on type parameter and concrete type arguments substitute = substitute.withoutToplevelNullAnnotation(); } if (this.typeAnnotations != Binding.NO_ANNOTATIONS) return this.environment.createAnnotatedType(substitute, this.typeAnnotations); // annots on originalVariable not relevant, and substitute has annots, keep substitute unmodified: } return substitute; } private boolean hasRelevantTypeUseNullAnnotations() { TypeVariableBinding[] parameters; if (this.declaringElement instanceof ReferenceBinding) { parameters = ((ReferenceBinding)this.declaringElement).original().typeVariables(); } else if (this.declaringElement instanceof MethodBinding) { parameters = ((MethodBinding)this.declaringElement).original().typeVariables; } else { throw new IllegalStateException("Unexpected declaring element:"+String.valueOf(this.declaringElement.readableName())); //$NON-NLS-1$ } TypeVariableBinding parameter = parameters[this.rank]; // recognize explicit annots by their effect on null tag bits, if there's no effect, then the annot is not considered relevant long currentNullBits = this.tagBits & TagBits.AnnotationNullMASK; long declarationNullBits = parameter.tagBits & TagBits.AnnotationNullMASK; return (currentNullBits & ~declarationNullBits) != 0; } @Override public boolean acceptsNonNullDefault() { return false; } @Override public long updateTagBits() { if (!this.inRecursiveFunction) { this.inRecursiveFunction = true; try { if (this.superclass != null) this.tagBits |= this.superclass.updateTagBits(); if (this.superInterfaces != null) for (TypeBinding superIfc : this.superInterfaces) this.tagBits |= superIfc.updateTagBits(); } finally { this.inRecursiveFunction = false; } } return super.updateTagBits(); } @Override public boolean isFreeTypeVariable() { return this.environment.usesNullTypeAnnotations() && this.environment.globalOptions.pessimisticNullAnalysisForFreeTypeVariablesEnabled && (this.tagBits & TagBits.AnnotationNullMASK) == 0; } @Override public ReferenceBinding upwardsProjection(Scope scope, TypeBinding[] mentionedTypeVariables) { return this; } @Override public ReferenceBinding downwardsProjection(Scope scope, TypeBinding[] mentionedTypeVariables) { return this; } }