blob: 6a308121c6a0672519c645f97d861087292d529e [file] [log] [blame]
/*******************************************************************************
* 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 - Contribution for
* Bug 423504 - [1.8] Implement "18.5.3 Functional Interface Parameterization Inference"
* Bug 426676 - [1.8][compiler] Wrong generic method type inferred from lambda expression
* Bug 426542 - [1.8] Most specific method not picked when one method has intersection type as type parameter
* Bug 428019 - [1.8][compiler] Type inference failure with nested generic invocation.
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
* Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas
* 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
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
import java.util.Set;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
/**
* Abstraction used for intersection casts in Java 8 + and inferred types:
* <ul>
* <li>type inference at 1.8+</li>
* <li>lub at 1.8+</li>
* <li>projections for 'var' at 10+</li>
* </ul>
*/
public class IntersectionTypeBinding18 extends ReferenceBinding {
public ReferenceBinding [] intersectingTypes;
private ReferenceBinding javaLangObject;
int length;
public IntersectionTypeBinding18(ReferenceBinding[] intersectingTypes, LookupEnvironment environment) {
this.intersectingTypes = intersectingTypes;
this.length = intersectingTypes.length;
if (!intersectingTypes[0].isClass()) {
this.javaLangObject = environment.getResolvedJavaBaseType(TypeConstants.JAVA_LANG_OBJECT, null);
this.modifiers |= ClassFileConstants.AccInterface;
}
}
private IntersectionTypeBinding18(IntersectionTypeBinding18 prototype) {
this.intersectingTypes = prototype.intersectingTypes;
this.length = prototype.length;
if (!this.intersectingTypes[0].isClass()) {
this.javaLangObject = prototype.javaLangObject;
this.modifiers |= ClassFileConstants.AccInterface;
}
}
@Override
public TypeBinding clone(TypeBinding enclosingType) {
return new IntersectionTypeBinding18(this);
}
@Override
protected MethodBinding[] getInterfaceAbstractContracts(Scope scope, boolean replaceWildcards, boolean filterDefaultMethods) throws InvalidInputException {
int typesLength = this.intersectingTypes.length;
MethodBinding[][] methods = new MethodBinding[typesLength][];
int contractsLength = 0;
for (int i = 0; i < typesLength; i++) {
methods[i] = this.intersectingTypes[i].getInterfaceAbstractContracts(scope, replaceWildcards, true);
contractsLength += methods[i].length;
}
MethodBinding[] contracts = new MethodBinding[contractsLength];
int idx = 0;
for (int i = 0; i < typesLength; i++) {
int len = methods[i].length;
System.arraycopy(methods[i], 0, contracts, idx, len);
idx += len;
}
return contracts;
}
@Override
public boolean hasTypeBit(int bit) { // Stephan ??
for (int i = 0; i < this.length; i++) {
if (this.intersectingTypes[i].hasTypeBit(bit))
return true;
}
return false;
}
@Override
public boolean canBeInstantiated() {
return false;
}
@Override
public boolean canBeSeenBy(PackageBinding invocationPackage) {
for (int i = 0; i < this.length; i++) {
if (!this.intersectingTypes[i].canBeSeenBy(invocationPackage))
return false;
}
return true;
}
@Override
public boolean canBeSeenBy(Scope scope) {
for (int i = 0; i < this.length; i++) {
if (!this.intersectingTypes[i].canBeSeenBy(scope))
return false;
}
return true;
}
@Override
public boolean canBeSeenBy(ReferenceBinding receiverType, ReferenceBinding invocationType) {
for (int i = 0; i < this.length; i++) {
if (!this.intersectingTypes[i].canBeSeenBy(receiverType, invocationType))
return false;
}
return true;
}
@Override
public char[] constantPoolName() {
TypeBinding erasure = erasure();
if (erasure != this) //$IDENTITY-COMPARISON$
return erasure.constantPoolName();
if (this.intersectingTypes[0].id == TypeIds.T_JavaLangObject && this.intersectingTypes.length > 1)
return this.intersectingTypes[1].constantPoolName(); // improve stack map
return this.intersectingTypes[0].constantPoolName();
}
@Override
public PackageBinding getPackage() {
throw new UnsupportedOperationException(); // cannot be referred to
}
@Override
public ReferenceBinding[] getIntersectingTypes() {
return this.intersectingTypes;
}
@Override
public ReferenceBinding superclass() {
return this.intersectingTypes[0].isClass() ? this.intersectingTypes[0] : this.javaLangObject;
}
@Override
public ReferenceBinding [] superInterfaces() {
if (this.intersectingTypes[0].isClass()) {
ReferenceBinding [] superInterfaces = new ReferenceBinding[this.length - 1];
System.arraycopy(this.intersectingTypes, 1, superInterfaces, 0, this.length - 1);
return superInterfaces;
}
return this.intersectingTypes;
}
@Override
public boolean isBoxedPrimitiveType() {
return this.intersectingTypes[0].isBoxedPrimitiveType();
}
/* Answer true if the receiver type can be assigned to the argument type (right)
*/
@Override
public boolean isCompatibleWith(TypeBinding right, Scope scope) {
// easy way out?
if (TypeBinding.equalsEquals(this, right))
return true;
// need to compare two intersection types?
int rightKind = right.kind();
TypeBinding[] rightIntersectingTypes = null;
if (rightKind == INTERSECTION_TYPE && right.boundKind() == Wildcard.EXTENDS) {
TypeBinding allRightBounds = ((WildcardBinding) right).allBounds();
if (allRightBounds instanceof IntersectionTypeBinding18)
rightIntersectingTypes = ((IntersectionTypeBinding18) allRightBounds).intersectingTypes;
} else if (rightKind == INTERSECTION_TYPE18) {
rightIntersectingTypes = ((IntersectionTypeBinding18) right).intersectingTypes;
}
if (rightIntersectingTypes != null) {
nextRequired:
for (TypeBinding required : rightIntersectingTypes) {
for (TypeBinding provided : this.intersectingTypes) {
if (provided.isCompatibleWith(required, scope))
continue nextRequired;
}
return false;
}
return true;
}
// normal case:
for (int i = 0; i < this.length; i++) {
if (this.intersectingTypes[i].isCompatibleWith(right, scope))
return true;
}
return false;
}
@Override
public boolean isSubtypeOf(TypeBinding other, boolean simulatingBugJDK8026527) {
if (TypeBinding.equalsEquals(this, other))
return true;
if (other instanceof ReferenceBinding) {
TypeBinding[] rightIntersectingTypes = ((ReferenceBinding) other).getIntersectingTypes();
if (rightIntersectingTypes != null && rightIntersectingTypes.length > 1) {
int numRequired = rightIntersectingTypes.length;
TypeBinding[] required = new TypeBinding[numRequired];
System.arraycopy(rightIntersectingTypes, 0, required, 0, numRequired);
for (int i = 0; i < this.length; i++) {
TypeBinding provided = this.intersectingTypes[i];
for (int j = 0; j < required.length; j++) {
if (required[j] == null) continue;
if (provided.isSubtypeOf(required[j], simulatingBugJDK8026527)) {
required[j] = null;
if (--numRequired == 0)
return true;
break;
}
}
}
return false;
}
}
for (int i = 0; i < this.intersectingTypes.length; i++) {
if (this.intersectingTypes[i].isSubtypeOf(other, false))
return true;
}
return false;
}
@Override
public TypeBinding erasure() {
int classIdx = -1;
for (int i = 0; i < this.intersectingTypes.length; i++) {
if (this.intersectingTypes[i].isClass() && this.intersectingTypes[i].id != TypeIds.T_JavaLangObject) { // ignore j.l.Object to improve stack map
if (classIdx == -1) {
classIdx = i;
} else {
classIdx = Integer.MAX_VALUE;
break;
}
}
}
if (classIdx > -1 && classIdx < Integer.MAX_VALUE)
return this.intersectingTypes[classIdx].erasure();
return this;
}
@Override
public char[] qualifiedSourceName() {
StringBuffer qualifiedSourceName = new StringBuffer(16);
for (int i = 0; i < this.length; i++) {
qualifiedSourceName.append(this.intersectingTypes[i].qualifiedSourceName());
if (i != this.length - 1)
qualifiedSourceName.append(" & "); //$NON-NLS-1$
}
return qualifiedSourceName.toString().toCharArray();
}
@Override
public char[] sourceName() {
StringBuffer srcName = new StringBuffer(16);
for (int i = 0; i < this.length; i++) {
srcName.append(this.intersectingTypes[i].sourceName());
if (i != this.length - 1)
srcName.append(" & "); //$NON-NLS-1$
}
return srcName.toString().toCharArray();
}
@Override
public char[] readableName() {
StringBuffer readableName = new StringBuffer(16);
for (int i = 0; i < this.length; i++) {
readableName.append(this.intersectingTypes[i].readableName());
if (i != this.length - 1)
readableName.append(" & "); //$NON-NLS-1$
}
return readableName.toString().toCharArray();
}
@Override
public char[] shortReadableName() {
StringBuffer shortReadableName = new StringBuffer(16);
for (int i = 0; i < this.length; i++) {
shortReadableName.append(this.intersectingTypes[i].shortReadableName());
if (i != this.length - 1)
shortReadableName.append(" & "); //$NON-NLS-1$
}
return shortReadableName.toString().toCharArray();
}
@Override
public boolean isIntersectionType18() {
return true;
}
@Override
public int kind() {
return Binding.INTERSECTION_TYPE18;
}
@Override
public String debugName() {
StringBuffer debugName = new StringBuffer(16);
for (int i = 0; i < this.length; i++) {
debugName.append(this.intersectingTypes[i].debugName());
if (i != this.length - 1)
debugName.append(" & "); //$NON-NLS-1$
}
return debugName.toString();
}
@Override
public String toString() {
return debugName();
}
public TypeBinding getSAMType(Scope scope) {
for (int i = 0, max = this.intersectingTypes.length; i < max; i++) {
TypeBinding typeBinding = this.intersectingTypes[i];
MethodBinding methodBinding = typeBinding.getSingleAbstractMethod(scope, true);
// Why doesn't getSingleAbstractMethod do as the javadoc says, and return null
// when it is not a SAM type
if (methodBinding != null && methodBinding.problemId() != ProblemReasons.NoSuchSingleAbstractMethod) {
return typeBinding; // answer the first SAM we find
}
}
return null;
}
//{ObjectTeams: enable crossing package boundaries:
protected
// SH}
@Override
void collectInferenceVariables(Set<InferenceVariable> variables) {
for (int i = 0; i < this.intersectingTypes.length; i++)
this.intersectingTypes[i].collectInferenceVariables(variables);
}
@Override
public ReferenceBinding upwardsProjection(Scope scope, TypeBinding[] mentionedTypeVariables) {
ReferenceBinding[] projectedTypes = new ReferenceBinding[this.intersectingTypes.length];
for (int i = 0; i < this.intersectingTypes.length; ++i) {
projectedTypes[i] = this.intersectingTypes[i].upwardsProjection(scope, mentionedTypeVariables);
}
return (ReferenceBinding) scope.environment().createIntersectionType18(projectedTypes);
}
@Override
public ReferenceBinding downwardsProjection(Scope scope, TypeBinding[] mentionedTypeVariables) {
ReferenceBinding[] projectedTypes = new ReferenceBinding[this.intersectingTypes.length];
for (int i = 0; i < this.intersectingTypes.length; ++i) {
projectedTypes[i] = this.intersectingTypes[i].downwardsProjection(scope, mentionedTypeVariables);
}
return (ReferenceBinding) scope.environment().createIntersectionType18(projectedTypes);
}
@Override
public boolean mentionsAny(TypeBinding[] parameters, int idx) {
if (super.mentionsAny(parameters, idx))
return true;
for (int i = 0; i < this.intersectingTypes.length; i++) {
if (this.intersectingTypes[i].mentionsAny(parameters, -1))
return true;
}
return false;
}
@Override
public long updateTagBits() {
for (TypeBinding intersectingType : this.intersectingTypes)
this.tagBits |= intersectingType.updateTagBits();
return super.updateTagBits();
}
}