public TypeBinding resolveType(BlockScope scope) { // due to syntax lhs may be only a NameReference, a FieldReference or an ArrayReference this.constant = Constant.NotAConstant; if (!(this.lhs instanceof Reference) || this.lhs.isThis()) { scope.problemReporter().expressionShouldBeAVariable(this.lhs); return null; } TypeBinding lhsType = lhs.resolveType(scope); this.expression.setExpectedType(lhsType); // needed in case of generic method invocation if (lhsType != null) { this.resolvedType = lhsType.capture(scope, this.sourceEnd); } TypeBinding rhsType = this.expression.resolveType(scope); if (lhsType == null || rhsType == null) { return null; } // check for assignment with no effect Binding left = getDirectBinding(this.lhs); if (left != null && left == getDirectBinding(this.expression)) { scope.problemReporter().assignmentHasNoEffect(this, left.shortReadableName()); } // Compile-time conversion of base-types : implicit narrowing integer into byte/short/character // may require to widen the rhs expression at runtime if (lhsType != rhsType) { // must call before computeConversion() and typeMismatchError() scope.compilationUnitScope().recordTypeConversion(lhsType, rhsType); } if ((this.expression.isConstantValueOfTypeAssignableToType(rhsType, lhsType) || (lhsType.isBaseType() && BaseTypeBinding.isWidening(lhsType.id, rhsType.id))) || rhsType.isCompatibleWith(lhsType)) { this.expression.computeConversion(scope, lhsType, rhsType); checkAssignment(scope, lhsType, rhsType); if (this.expression instanceof CastExpression && (this.expression.bits & ASTNode.UnnecessaryCast) == 0) { CastExpression.checkNeedForAssignedCast(scope, lhsType, (CastExpression) this.expression); } return this.resolvedType; } else if (scope.isBoxingCompatibleWith(rhsType, lhsType) || (rhsType.isBaseType() // narrowing then boxing ? && scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5 // autoboxing && !lhsType.isBaseType() && this.expression.isConstantValueOfTypeAssignableToType( rhsType, scope.environment().computeBoxingType(lhsType)))) { this.expression.computeConversion(scope, lhsType, rhsType); if (this.expression instanceof CastExpression && (this.expression.bits & ASTNode.UnnecessaryCast) == 0) { CastExpression.checkNeedForAssignedCast(scope, lhsType, (CastExpression) this.expression); } return this.resolvedType; } scope.problemReporter().typeMismatchError(rhsType, lhsType, this.expression, this.lhs); return lhsType; }
/** * 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 Object reduceSubType(Scope scope, TypeBinding subCandidate, TypeBinding superCandidate) { // 18.2.3 Subtyping Constraints if (subCandidate.isProperType(true) && superCandidate.isProperType(true)) { if (subCandidate.isCompatibleWith(superCandidate, scope)) return TRUE; return FALSE; } if (subCandidate.id == TypeIds.T_null) return TRUE; if (superCandidate.id == TypeIds.T_null) return FALSE; if (subCandidate instanceof InferenceVariable) return new TypeBound((InferenceVariable) subCandidate, superCandidate, SUBTYPE, this.isSoft); if (superCandidate instanceof InferenceVariable) return new TypeBound( (InferenceVariable) superCandidate, subCandidate, SUPERTYPE, this.isSoft); // normalize to have variable on LHS switch (superCandidate.kind()) { case Binding.GENERIC_TYPE: case Binding.TYPE: case Binding.RAW_TYPE: { if (subCandidate.isSubtypeOf(superCandidate)) return TRUE; return FALSE; } case Binding.PARAMETERIZED_TYPE: { List<ConstraintFormula> constraints = new ArrayList<>(); while (superCandidate != null && superCandidate.kind() == Binding.PARAMETERIZED_TYPE && subCandidate != null) { if (!addConstraintsFromTypeParameters( subCandidate, (ParameterizedTypeBinding) superCandidate, constraints)) return FALSE; // travel to enclosing types to check if they have type parameters, too: superCandidate = superCandidate.enclosingType(); subCandidate = subCandidate.enclosingType(); } switch (constraints.size()) { case 0: return TRUE; case 1: return constraints.get(0); default: return constraints.toArray(new ConstraintFormula[constraints.size()]); } } case Binding.ARRAY_TYPE: TypeBinding tPrime = ((ArrayBinding) superCandidate).elementsType(); // let S'[] be the most specific array type that is a supertype of S (or S itself) ArrayBinding sPrimeArray = null; switch (subCandidate.kind()) { case Binding.INTERSECTION_TYPE: { WildcardBinding intersection = (WildcardBinding) subCandidate; sPrimeArray = findMostSpecificSuperArray( intersection.bound, intersection.otherBounds, intersection); break; } case Binding.ARRAY_TYPE: sPrimeArray = (ArrayBinding) subCandidate; break; case Binding.TYPE_PARAMETER: { TypeVariableBinding subTVB = (TypeVariableBinding) subCandidate; sPrimeArray = findMostSpecificSuperArray(subTVB.firstBound, subTVB.otherUpperBounds(), subTVB); break; } default: return FALSE; } if (sPrimeArray == null) return FALSE; TypeBinding sPrime = sPrimeArray.elementsType(); if (!tPrime.isPrimitiveType() && !sPrime.isPrimitiveType()) { return ConstraintTypeFormula.create(sPrime, tPrime, SUBTYPE, this.isSoft); } return TypeBinding.equalsEquals(tPrime, sPrime) ? TRUE : FALSE; // same primitive type? // "type variable" has two implementations in JDT: case Binding.WILDCARD_TYPE: if (subCandidate.kind() == Binding.INTERSECTION_TYPE) { ReferenceBinding[] intersectingTypes = subCandidate.getIntersectingTypes(); if (intersectingTypes != null) for (int i = 0; i < intersectingTypes.length; i++) if (TypeBinding.equalsEquals(intersectingTypes[i], superCandidate)) return true; } WildcardBinding variable = (WildcardBinding) superCandidate; if (variable.boundKind == Wildcard.SUPER) return ConstraintTypeFormula.create(subCandidate, variable.bound, SUBTYPE, this.isSoft); return FALSE; case Binding.TYPE_PARAMETER: // similar to wildcard, but different queries for lower bound if (subCandidate.kind() == Binding.INTERSECTION_TYPE) { ReferenceBinding[] intersectingTypes = subCandidate.getIntersectingTypes(); if (intersectingTypes != null) for (int i = 0; i < intersectingTypes.length; i++) if (TypeBinding.equalsEquals(intersectingTypes[i], superCandidate)) return true; } if (superCandidate instanceof CaptureBinding) { CaptureBinding capture = (CaptureBinding) superCandidate; if (capture.lowerBound != null && (capture.firstBound == null || capture.firstBound.id == TypeIds.T_JavaLangObject)) return ConstraintTypeFormula.create( subCandidate, capture.lowerBound, SUBTYPE, this.isSoft); } return FALSE; case Binding.INTERSECTION_TYPE: superCandidate = ((WildcardBinding) superCandidate).allBounds(); // $FALL-THROUGH$ case Binding.INTERSECTION_TYPE18: TypeBinding[] intersectingTypes = ((IntersectionTypeBinding18) superCandidate).intersectingTypes; ConstraintFormula[] result = new ConstraintFormula[intersectingTypes.length]; for (int i = 0; i < intersectingTypes.length; i++) { result[i] = ConstraintTypeFormula.create( subCandidate, intersectingTypes[i], SUBTYPE, this.isSoft); } return result; case Binding.POLY_TYPE: PolyTypeBinding poly = (PolyTypeBinding) superCandidate; Invocation invocation = (Invocation) poly.expression; MethodBinding binding = invocation.binding(); if (binding == null || !binding.isValidBinding()) return FALSE; TypeBinding returnType = binding.isConstructor() ? binding.declaringClass : binding.returnType; return reduceSubType( scope, subCandidate, returnType.capture(scope, invocation.sourceStart(), invocation.sourceEnd())); } throw new IllegalStateException("Unexpected RHS " + superCandidate); // $NON-NLS-1$ }
/** * Initialize capture bounds using substituted supertypes e.g. given X<U, V extends X<U, V>>, * capture(X<E,?>) = X<E,capture>, where capture extends X<E,capture> */ public void initializeBounds(Scope scope, ParameterizedTypeBinding capturedParameterizedType) { TypeVariableBinding wildcardVariable = this.wildcard.typeVariable(); if (wildcardVariable == null) { // error resilience when capturing Zork<?> // no substitution for wildcard bound (only formal bounds from type variables are to be // substituted: 104082) TypeBinding originalWildcardBound = this.wildcard.bound; switch (this.wildcard.boundKind) { case Wildcard.EXTENDS: // still need to capture bound supertype as well so as not to expose wildcards to the // outside (111208) TypeBinding capturedWildcardBound = originalWildcardBound.capture(scope, this.position); if (originalWildcardBound.isInterface()) { this.superclass = scope.getJavaLangObject(); this.superInterfaces = new ReferenceBinding[] {(ReferenceBinding) capturedWildcardBound}; } else { // the wildcard bound should be a subtype of variable superclass // it may occur that the bound is less specific, then consider glb (202404) if (capturedWildcardBound.isArrayType() || capturedWildcardBound == this) { this.superclass = scope.getJavaLangObject(); } else { this.superclass = (ReferenceBinding) capturedWildcardBound; } this.superInterfaces = Binding.NO_SUPERINTERFACES; } this.firstBound = capturedWildcardBound; if ((capturedWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) this.tagBits &= ~TagBits.HasTypeVariable; break; case Wildcard.UNBOUND: this.superclass = scope.getJavaLangObject(); this.superInterfaces = Binding.NO_SUPERINTERFACES; this.tagBits &= ~TagBits.HasTypeVariable; break; case Wildcard.SUPER: this.superclass = scope.getJavaLangObject(); this.superInterfaces = Binding.NO_SUPERINTERFACES; this.lowerBound = this.wildcard.bound; if ((originalWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) this.tagBits &= ~TagBits.HasTypeVariable; break; } return; } ReferenceBinding originalVariableSuperclass = wildcardVariable.superclass; ReferenceBinding substitutedVariableSuperclass = (ReferenceBinding) Scope.substitute(capturedParameterizedType, originalVariableSuperclass); // prevent cyclic capture: given X<T>, capture(X<? extends T> could yield a circular type if (substitutedVariableSuperclass == this) substitutedVariableSuperclass = originalVariableSuperclass; ReferenceBinding[] originalVariableInterfaces = wildcardVariable.superInterfaces(); ReferenceBinding[] substitutedVariableInterfaces = Scope.substitute(capturedParameterizedType, originalVariableInterfaces); if (substitutedVariableInterfaces != originalVariableInterfaces) { // prevent cyclic capture: given X<T>, capture(X<? extends T> could yield a circular type for (int i = 0, length = substitutedVariableInterfaces.length; i < length; i++) { if (substitutedVariableInterfaces[i] == this) substitutedVariableInterfaces[i] = originalVariableInterfaces[i]; } } // no substitution for wildcard bound (only formal bounds from type variables are to be // substituted: 104082) TypeBinding originalWildcardBound = this.wildcard.bound; switch (this.wildcard.boundKind) { case Wildcard.EXTENDS: // still need to capture bound supertype as well so as not to expose wildcards to the // outside (111208) TypeBinding capturedWildcardBound = originalWildcardBound.capture(scope, this.position); if (originalWildcardBound.isInterface()) { this.superclass = substitutedVariableSuperclass; // merge wildcard bound into variable superinterfaces using glb if (substitutedVariableInterfaces == Binding.NO_SUPERINTERFACES) { this.superInterfaces = new ReferenceBinding[] {(ReferenceBinding) capturedWildcardBound}; } else { int length = substitutedVariableInterfaces.length; System.arraycopy( substitutedVariableInterfaces, 0, substitutedVariableInterfaces = new ReferenceBinding[length + 1], 1, length); substitutedVariableInterfaces[0] = (ReferenceBinding) capturedWildcardBound; this.superInterfaces = Scope.greaterLowerBound(substitutedVariableInterfaces); } } else { // the wildcard bound should be a subtype of variable superclass // it may occur that the bound is less specific, then consider glb (202404) if (capturedWildcardBound.isArrayType() || capturedWildcardBound == this) { this.superclass = substitutedVariableSuperclass; } else { this.superclass = (ReferenceBinding) capturedWildcardBound; if (this.superclass.isSuperclassOf(substitutedVariableSuperclass)) { this.superclass = substitutedVariableSuperclass; } } this.superInterfaces = substitutedVariableInterfaces; } this.firstBound = capturedWildcardBound; if ((capturedWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) this.tagBits &= ~TagBits.HasTypeVariable; break; case Wildcard.UNBOUND: this.superclass = substitutedVariableSuperclass; this.superInterfaces = substitutedVariableInterfaces; this.tagBits &= ~TagBits.HasTypeVariable; break; case Wildcard.SUPER: this.superclass = substitutedVariableSuperclass; if (wildcardVariable.firstBound == substitutedVariableSuperclass || originalWildcardBound == substitutedVariableSuperclass) { this.firstBound = substitutedVariableSuperclass; } this.superInterfaces = substitutedVariableInterfaces; this.lowerBound = originalWildcardBound; if ((originalWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) this.tagBits &= ~TagBits.HasTypeVariable; break; } }