TypeIntersector: refactor it to being a static utility

Deal with intersecting empty set of types on the call site
This commit is contained in:
Pavel V. Talanov
2015-09-21 18:40:45 +03:00
parent b85fe40275
commit b9da08d11c
13 changed files with 29 additions and 53 deletions
@@ -49,8 +49,7 @@ import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
class GenericCandidateResolver( class GenericCandidateResolver(
private val argumentTypeResolver: ArgumentTypeResolver, private val argumentTypeResolver: ArgumentTypeResolver
private val typeIntersector: TypeIntersector
) { ) {
fun <D : CallableDescriptor> inferTypeArguments(context: CallCandidateResolutionContext<D>): ResolutionStatus { fun <D : CallableDescriptor> inferTypeArguments(context: CallCandidateResolutionContext<D>): ResolutionStatus {
@@ -189,7 +188,7 @@ class GenericCandidateResolver(
val possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue) val possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue)
if (possibleTypes.isEmpty()) return type if (possibleTypes.isEmpty()) return type
return typeIntersector.intersect(JetTypeChecker.DEFAULT, possibleTypes) return TypeIntersector.intersectTypes(JetTypeChecker.DEFAULT, possibleTypes)
} }
public fun <D : CallableDescriptor> completeTypeInferenceDependentOnFunctionArgumentsForCall( public fun <D : CallableDescriptor> completeTypeInferenceDependentOnFunctionArgumentsForCall(
@@ -22,17 +22,13 @@ import kotlin.KotlinPackage;
import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.kotlin.psi.JetExpression; import org.jetbrains.kotlin.psi.JetExpression;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver; import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver;
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver;
import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.TypeIntersector; import org.jetbrains.kotlin.types.TypeIntersector;
import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.TypeUtils;
@@ -47,9 +43,6 @@ import static org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST;
public class SmartCastManager { public class SmartCastManager {
public SmartCastManager() {
}
@NotNull @NotNull
public List<JetType> getSmartCastVariants( public List<JetType> getSmartCastVariants(
@NotNull ReceiverValue receiverToCast, @NotNull ReceiverValue receiverToCast,
@@ -146,7 +139,7 @@ public class SmartCastManager {
} }
if (subTypes.isEmpty()) return null; if (subTypes.isEmpty()) return null;
JetType intersection = TypeIntersector.intersectTypes(KotlinBuiltIns.getInstance(), JetTypeChecker.DEFAULT, subTypes); JetType intersection = TypeIntersector.intersectTypes(JetTypeChecker.DEFAULT, subTypes);
if (intersection == null || !intersection.getConstructor().isDenotable()) { if (intersection == null || !intersection.getConstructor().isDenotable()) {
return receiverParameterType; return receiverParameterType;
} }
@@ -1278,7 +1278,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
JetType rightType = facade.getTypeInfo(right, context).getType(); JetType rightType = facade.getTypeInfo(right, context).getType();
if (rightType != null) { if (rightType != null) {
if (components.typeIntersector.isIntersectionEmpty(leftType, rightType)) { if (TypeIntersector.isIntersectionEmpty(leftType, rightType)) {
context.trace.report(EQUALITY_NOT_APPLICABLE.on(expression, expression.getOperationReference(), leftType, rightType)); context.trace.report(EQUALITY_NOT_APPLICABLE.on(expression, expression.getOperationReference(), leftType, rightType));
} }
SenselessComparisonChecker.checkSenselessComparisonWithNull( SenselessComparisonChecker.checkSenselessComparisonWithNull(
@@ -55,7 +55,6 @@ public class ExpressionTypingComponents {
/*package*/ ModifiersChecker modifiersChecker; /*package*/ ModifiersChecker modifiersChecker;
/*package*/ DataFlowAnalyzer dataFlowAnalyzer; /*package*/ DataFlowAnalyzer dataFlowAnalyzer;
/*package*/ Iterable<CallChecker> callCheckers; /*package*/ Iterable<CallChecker> callCheckers;
/*package*/ TypeIntersector typeIntersector;
@Inject @Inject
public void setGlobalContext(@NotNull GlobalContext globalContext) { public void setGlobalContext(@NotNull GlobalContext globalContext) {
@@ -171,9 +170,4 @@ public class ExpressionTypingComponents {
public void setCallCheckers(@NotNull Iterable<CallChecker> callCheckers) { public void setCallCheckers(@NotNull Iterable<CallChecker> callCheckers) {
this.callCheckers = callCheckers; this.callCheckers = callCheckers;
} }
@Inject
public void setTypeIntersector(@NotNull TypeIntersector typeIntersector) {
this.typeIntersector = typeIntersector;
}
} }
@@ -347,7 +347,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
if (type == null) { if (type == null) {
return; return;
} }
if (components.typeIntersector.isIntersectionEmpty(type, subjectType)) { if (TypeIntersector.isIntersectionEmpty(type, subjectType)) {
context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType)); context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType));
return; return;
} }
@@ -524,7 +524,7 @@ public class JetTypeCheckerTest extends JetLiteFixture {
for (String type : types) { for (String type : types) {
typesToIntersect.add(makeType(type)); typesToIntersect.add(makeType(type));
} }
JetType result = TypeIntersector.intersectTypes(KotlinBuiltIns.getInstance(), JetTypeChecker.DEFAULT, typesToIntersect); JetType result = TypeIntersector.intersectTypes(JetTypeChecker.DEFAULT, typesToIntersect);
// assertNotNull("Intersection is null for " + typesToIntersect, result); // assertNotNull("Intersection is null for " + typesToIntersect, result);
assertEquals(makeType(expected), result); assertEquals(makeType(expected), result);
} }
@@ -176,7 +176,10 @@ class LazyJavaTypeResolver(
for (supertype in (classifier() as JavaTypeParameter).getUpperBounds()) { for (supertype in (classifier() as JavaTypeParameter).getUpperBounds()) {
supertypesJet.add(transformJavaType(supertype, UPPER_BOUND.toAttributes())) supertypesJet.add(transformJavaType(supertype, UPPER_BOUND.toAttributes()))
} }
return TypeIntersector.intersectTypes(KotlinBuiltIns.getInstance(), JetTypeChecker.DEFAULT, supertypesJet) if (supertypesJet.isEmpty()) {
return c.module.builtIns.nullableAnyType
}
return TypeIntersector.intersectTypes(JetTypeChecker.DEFAULT, supertypesJet)
?: ErrorUtils.createErrorType("Can't intersect upper bounds of " + javaType.getPresentableText()) ?: ErrorUtils.createErrorType("Can't intersect upper bounds of " + javaType.getPresentableText())
} }
@@ -136,9 +136,8 @@ public abstract class AbstractTypeParameterDescriptor extends DeclarationDescrip
private JetType computeUpperBoundsAsType() { private JetType computeUpperBoundsAsType() {
Set<JetType> upperBounds = getUpperBounds(); Set<JetType> upperBounds = getUpperBounds();
assert !upperBounds.isEmpty() : "Upper bound list is empty in " + getName(); assert !upperBounds.isEmpty() : "Upper bound list is empty in " + getName();
KotlinBuiltIns builtIns = getBuiltIns(this); JetType upperBoundsAsType = TypeIntersector.intersectTypes(JetTypeChecker.DEFAULT, upperBounds);
JetType upperBoundsAsType = TypeIntersector.intersectTypes(builtIns, JetTypeChecker.DEFAULT, upperBounds); return upperBoundsAsType != null ? upperBoundsAsType : getBuiltIns(this).getNothingType();
return upperBoundsAsType != null ? upperBoundsAsType : builtIns.getNothingType();
} }
@NotNull @NotNull
@@ -135,9 +135,9 @@ public class TypeBoundsImpl(
} }
val upperBounds = filterBounds(bounds, TypeBounds.BoundKind.UPPER_BOUND, values) val upperBounds = filterBounds(bounds, TypeBounds.BoundKind.UPPER_BOUND, values)
val intersectionOfUpperBounds = TypeIntersector(typeVariable.builtIns).intersect(JetTypeChecker.DEFAULT, upperBounds) if (upperBounds.isNotEmpty()) {
if (!upperBounds.isEmpty() && intersectionOfUpperBounds != null) { val intersectionOfUpperBounds = TypeIntersector.intersectTypes(JetTypeChecker.DEFAULT, upperBounds)
if (tryPossibleAnswer(bounds, intersectionOfUpperBounds)) { if (intersectionOfUpperBounds != null && tryPossibleAnswer(bounds, intersectionOfUpperBounds)) {
return setOf(intersectionOfUpperBounds) return setOf(intersectionOfUpperBounds)
} }
} }
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.types.typeUtil.TypeUtilPackage;
import java.util.*; import java.util.*;
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getBuiltIns;
import static org.jetbrains.kotlin.types.TypeUtils.topologicallySortSuperclassesAndRecordAllInstances; import static org.jetbrains.kotlin.types.TypeUtils.topologicallySortSuperclassesAndRecordAllInstances;
import static org.jetbrains.kotlin.types.Variance.IN_VARIANCE; import static org.jetbrains.kotlin.types.Variance.IN_VARIANCE;
import static org.jetbrains.kotlin.types.Variance.OUT_VARIANCE; import static org.jetbrains.kotlin.types.Variance.OUT_VARIANCE;
@@ -299,6 +298,7 @@ public class CommonSupertypes {
} }
if (outs != null) { if (outs != null) {
assert !outs.isEmpty() : "Out projections is empty for parameter " + parameterDescriptor + ", type projections " + typeProjections;
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE; Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
JetType superType = findCommonSupertype(outs, recursionDepth + 1, maxDepth); JetType superType = findCommonSupertype(outs, recursionDepth + 1, maxDepth);
for (JetType upperBound: parameterDescriptor.getUpperBounds()) { for (JetType upperBound: parameterDescriptor.getUpperBounds()) {
@@ -309,7 +309,8 @@ public class CommonSupertypes {
return new TypeProjectionImpl(projectionKind, superType); return new TypeProjectionImpl(projectionKind, superType);
} }
if (ins != null) { if (ins != null) {
JetType intersection = TypeIntersector.intersectTypes(getBuiltIns(parameterDescriptor), JetTypeChecker.DEFAULT, ins); assert !ins.isEmpty() : "In projections is empty for parameter " + parameterDescriptor + ", type projections " + typeProjections;
JetType intersection = TypeIntersector.intersectTypes(JetTypeChecker.DEFAULT, ins);
if (intersection == null) { if (intersection == null) {
return new TypeProjectionImpl(OUT_VARIANCE, findCommonSupertype(parameterDescriptor.getUpperBounds(), recursionDepth + 1, maxDepth)); return new TypeProjectionImpl(OUT_VARIANCE, findCommonSupertype(parameterDescriptor.getUpperBounds(), recursionDepth + 1, maxDepth));
} }
@@ -37,31 +37,16 @@ import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.Co
public class TypeIntersector { public class TypeIntersector {
private final KotlinBuiltIns builtIns; public static boolean isIntersectionEmpty(@NotNull JetType typeA, @NotNull JetType typeB) {
return intersectTypes(JetTypeChecker.DEFAULT, new LinkedHashSet<JetType>(Arrays.asList(typeA, typeB))) == null;
public TypeIntersector(@NotNull KotlinBuiltIns builtIns) {
this.builtIns = builtIns;
} }
public boolean isIntersectionEmpty(@NotNull JetType typeA, @NotNull JetType typeB) {
return intersect(JetTypeChecker.DEFAULT, new LinkedHashSet<JetType>(Arrays.asList(typeA, typeB))) == null;
}
//TODO: usages of this method should be removed
@Nullable @Nullable
public static JetType intersectTypes( public static JetType intersectTypes(
@NotNull KotlinBuiltIns builtIns,
@NotNull JetTypeChecker typeChecker, @NotNull JetTypeChecker typeChecker,
@NotNull Set<JetType> types @NotNull Set<JetType> types
) { ) {
return new TypeIntersector(builtIns).intersect(typeChecker, types); assert (!types.isEmpty()) : "Attempting to intersect empty set of types, this case should be dealt with on the call site.";
}
@Nullable
public JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set<JetType> types) {
if (types.isEmpty()) {
return builtIns.getNullableAnyType();
}
if (types.size() == 1) { if (types.size() == 1) {
return types.iterator().next(); return types.iterator().next();
@@ -69,19 +54,21 @@ public class TypeIntersector {
// Intersection of T1..Tn is an intersection of their non-null versions, // Intersection of T1..Tn is an intersection of their non-null versions,
// made nullable is they all were nullable // made nullable is they all were nullable
JetType nothingOrNullableNothing = null;
boolean allNullable = true; boolean allNullable = true;
boolean nothingTypePresent = false;
List<JetType> nullabilityStripped = new ArrayList<JetType>(types.size()); List<JetType> nullabilityStripped = new ArrayList<JetType>(types.size());
for (JetType type : types) { for (JetType type : types) {
if (type.isError()) continue; if (type.isError()) continue;
nothingTypePresent |= KotlinBuiltIns.isNothingOrNullableNothing(type); if (KotlinBuiltIns.isNothingOrNullableNothing(type)) {
nothingOrNullableNothing = type;
}
allNullable &= type.isMarkedNullable(); allNullable &= type.isMarkedNullable();
nullabilityStripped.add(TypeUtils.makeNotNullable(type)); nullabilityStripped.add(TypeUtils.makeNotNullable(type));
} }
if (nothingTypePresent) { if (nothingOrNullableNothing != null) {
return allNullable ? builtIns.getNullableNothingType() : builtIns.getNothingType(); return TypeUtils.makeNullableAsSpecified(nothingOrNullableNothing, allNullable);
} }
if (nullabilityStripped.isEmpty()) { if (nullabilityStripped.isEmpty()) {
@@ -217,7 +217,7 @@ private fun JetNamedDeclaration.guessType(context: BindingContext): Array<JetTyp
if (expectedTypes.isEmpty() || expectedTypes.any { expectedType -> ErrorUtils.containsErrorType(expectedType) }) { if (expectedTypes.isEmpty() || expectedTypes.any { expectedType -> ErrorUtils.containsErrorType(expectedType) }) {
return arrayOf() return arrayOf()
} }
val theType = TypeIntersector.intersectTypes(KotlinBuiltIns.getInstance(), JetTypeChecker.DEFAULT, expectedTypes) val theType = TypeIntersector.intersectTypes(JetTypeChecker.DEFAULT, expectedTypes)
if (theType != null) { if (theType != null) {
return arrayOf(theType) return arrayOf(theType)
} }
@@ -531,7 +531,7 @@ private class MutableParameter(
private val defaultType: JetType by lazy { private val defaultType: JetType by lazy {
writable = false writable = false
if (defaultTypes.isNotEmpty()) { if (defaultTypes.isNotEmpty()) {
TypeIntersector.intersectTypes(originalDescriptor.builtIns, JetTypeChecker.DEFAULT, defaultTypes)!! TypeIntersector.intersectTypes(JetTypeChecker.DEFAULT, defaultTypes)!!
} }
else originalType else originalType
} }