Extract TypeIntersector from TypeUtils
This commit is contained in:
+2
-5
@@ -41,12 +41,9 @@ import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.INCOMPLETE_TY
|
|||||||
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.OTHER_ERROR
|
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.OTHER_ERROR
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||||
import org.jetbrains.kotlin.types.JetType
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
|
||||||
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
|
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
|
||||||
import org.jetbrains.kotlin.types.TypeUtils.makeConstantSubstitutor
|
import org.jetbrains.kotlin.types.TypeUtils.makeConstantSubstitutor
|
||||||
import org.jetbrains.kotlin.types.Variance
|
|
||||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||||
|
|
||||||
@@ -191,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 TypeUtils.intersect(JetTypeChecker.DEFAULT, possibleTypes)
|
return TypeIntersector.intersect(JetTypeChecker.DEFAULT, possibleTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <D : CallableDescriptor> completeTypeInferenceDependentOnFunctionArgumentsForCall(
|
public fun <D : CallableDescriptor> completeTypeInferenceDependentOnFunctionArgumentsForCall(
|
||||||
|
|||||||
+2
-1
@@ -33,6 +33,7 @@ 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.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.TypeUtils;
|
import org.jetbrains.kotlin.types.TypeUtils;
|
||||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
|
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
|
||||||
|
|
||||||
@@ -151,7 +152,7 @@ public class SmartCastUtils {
|
|||||||
}
|
}
|
||||||
if (subTypes.isEmpty()) return null;
|
if (subTypes.isEmpty()) return null;
|
||||||
|
|
||||||
JetType intersection = TypeUtils.intersect(JetTypeChecker.DEFAULT, subTypes);
|
JetType intersection = TypeIntersector.intersect(JetTypeChecker.DEFAULT, subTypes);
|
||||||
if (intersection == null || !intersection.getConstructor().isDenotable()) {
|
if (intersection == null || !intersection.getConstructor().isDenotable()) {
|
||||||
return receiverParameterType;
|
return receiverParameterType;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1251,7 +1251,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 (TypeUtils.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(
|
||||||
|
|||||||
+1
-1
@@ -44,8 +44,8 @@ import java.util.Set;
|
|||||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean;
|
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean;
|
||||||
import static org.jetbrains.kotlin.diagnostics.Errors.*;
|
import static org.jetbrains.kotlin.diagnostics.Errors.*;
|
||||||
import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
|
import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
|
||||||
|
import static org.jetbrains.kotlin.types.TypeIntersector.isIntersectionEmpty;
|
||||||
import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
|
import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
|
||||||
import static org.jetbrains.kotlin.types.TypeUtils.isIntersectionEmpty;
|
|
||||||
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.newWritableScopeImpl;
|
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.newWritableScopeImpl;
|
||||||
|
|
||||||
public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||||
|
|||||||
@@ -523,7 +523,7 @@ public class JetTypeCheckerTest extends JetLiteFixture {
|
|||||||
for (String type : types) {
|
for (String type : types) {
|
||||||
typesToIntersect.add(makeType(type));
|
typesToIntersect.add(makeType(type));
|
||||||
}
|
}
|
||||||
JetType result = TypeUtils.intersect(JetTypeChecker.DEFAULT, typesToIntersect);
|
JetType result = TypeIntersector.intersect(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);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -178,7 +178,7 @@ 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 TypeUtils.intersect(JetTypeChecker.DEFAULT, supertypesJet)
|
return TypeIntersector.intersect(JetTypeChecker.DEFAULT, supertypesJet)
|
||||||
?: ErrorUtils.createErrorType("Can't intersect upper bounds of " + javaType.getPresentableText())
|
?: ErrorUtils.createErrorType("Can't intersect upper bounds of " + javaType.getPresentableText())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -135,7 +135,7 @@ 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();
|
||||||
JetType upperBoundsAsType = TypeUtils.intersect(JetTypeChecker.DEFAULT, upperBounds);
|
JetType upperBoundsAsType = TypeIntersector.intersect(JetTypeChecker.DEFAULT, upperBounds);
|
||||||
return upperBoundsAsType != null ? upperBoundsAsType : getBuiltIns(this).getNothingType();
|
return upperBoundsAsType != null ? upperBoundsAsType : getBuiltIns(this).getNothingType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ public class TypeBoundsImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val upperBounds = filterBounds(bounds, TypeBounds.BoundKind.UPPER_BOUND, values)
|
val upperBounds = filterBounds(bounds, TypeBounds.BoundKind.UPPER_BOUND, values)
|
||||||
val intersectionOfUpperBounds = TypeUtils.intersect(JetTypeChecker.DEFAULT, upperBounds)
|
val intersectionOfUpperBounds = TypeIntersector.intersect(JetTypeChecker.DEFAULT, upperBounds)
|
||||||
if (!upperBounds.isEmpty() && intersectionOfUpperBounds != null) {
|
if (!upperBounds.isEmpty() && intersectionOfUpperBounds != null) {
|
||||||
if (tryPossibleAnswer(bounds, intersectionOfUpperBounds)) {
|
if (tryPossibleAnswer(bounds, intersectionOfUpperBounds)) {
|
||||||
return setOf(intersectionOfUpperBounds)
|
return setOf(intersectionOfUpperBounds)
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ public class CommonSupertypes {
|
|||||||
return new TypeProjectionImpl(projectionKind, findCommonSupertype(outs, recursionDepth + 1, maxDepth));
|
return new TypeProjectionImpl(projectionKind, findCommonSupertype(outs, recursionDepth + 1, maxDepth));
|
||||||
}
|
}
|
||||||
if (ins != null) {
|
if (ins != null) {
|
||||||
JetType intersection = TypeUtils.intersect(JetTypeChecker.DEFAULT, ins);
|
JetType intersection = TypeIntersector.intersect(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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.types;
|
||||||
|
|
||||||
|
import kotlin.Unit;
|
||||||
|
import kotlin.jvm.functions.Function1;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||||
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl;
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.ChainedScope;
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.JetScope;
|
||||||
|
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.jetbrains.kotlin.resolve.calls.inference.InferencePackage.registerTypeVariables;
|
||||||
|
import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL;
|
||||||
|
|
||||||
|
public class TypeIntersector {
|
||||||
|
public static boolean isIntersectionEmpty(@NotNull JetType typeA, @NotNull JetType typeB) {
|
||||||
|
return intersect(JetTypeChecker.DEFAULT, new LinkedHashSet<JetType>(Arrays.asList(typeA, typeB))) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set<JetType> types) {
|
||||||
|
if (types.isEmpty()) {
|
||||||
|
return KotlinBuiltIns.getInstance().getNullableAnyType();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types.size() == 1) {
|
||||||
|
return types.iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersection of T1..Tn is an intersection of their non-null versions,
|
||||||
|
// made nullable is they all were nullable
|
||||||
|
boolean allNullable = true;
|
||||||
|
boolean nothingTypePresent = false;
|
||||||
|
List<JetType> nullabilityStripped = new ArrayList<JetType>(types.size());
|
||||||
|
for (JetType type : types) {
|
||||||
|
if (type.isError()) continue;
|
||||||
|
|
||||||
|
nothingTypePresent |= KotlinBuiltIns.isNothingOrNullableNothing(type);
|
||||||
|
allNullable &= type.isMarkedNullable();
|
||||||
|
nullabilityStripped.add(TypeUtils.makeNotNullable(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nothingTypePresent) {
|
||||||
|
return allNullable ? KotlinBuiltIns.getInstance().getNullableNothingType() : KotlinBuiltIns.getInstance().getNothingType();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nullabilityStripped.isEmpty()) {
|
||||||
|
// All types were errors
|
||||||
|
return ErrorUtils.createErrorType("Intersection of errors types: " + types);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we remove types that have subtypes in the list
|
||||||
|
List<JetType> resultingTypes = new ArrayList<JetType>();
|
||||||
|
outer:
|
||||||
|
for (JetType type : nullabilityStripped) {
|
||||||
|
if (!TypeUtils.canHaveSubtypes(typeChecker, type)) {
|
||||||
|
for (JetType other : nullabilityStripped) {
|
||||||
|
// It makes sense to check for subtyping (other <: type), despite that
|
||||||
|
// type is not supposed to be open, for there're enums
|
||||||
|
if (!TypeUnifier.mayBeEqual(type, other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TypeUtils.makeNullableAsSpecified(type, allNullable);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (JetType other : nullabilityStripped) {
|
||||||
|
if (!type.equals(other) && typeChecker.isSubtypeOf(other, type)) {
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't add type if it is already present, to avoid trivial type intersections in result
|
||||||
|
for (JetType other : resultingTypes) {
|
||||||
|
if (typeChecker.equalTypes(other, type)) {
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultingTypes.add(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultingTypes.isEmpty()) {
|
||||||
|
// If we ended up here, it means that all types from `nullabilityStripped` were excluded by the code above
|
||||||
|
// most likely, this is because they are all semantically interchangeable (e.g. List<Foo>! and List<Foo>),
|
||||||
|
// in that case, we can safely select the best representative out of that set and return it
|
||||||
|
// TODO: maybe return the most specific among the types that are subtypes to all others in the `nullabilityStripped`?
|
||||||
|
// TODO: e.g. among {Int, Int?, Int!}, return `Int` (now it returns `Int!`).
|
||||||
|
JetType bestRepresentative = TypesPackage.singleBestRepresentative(nullabilityStripped);
|
||||||
|
if (bestRepresentative == null) {
|
||||||
|
throw new AssertionError("Empty intersection for types " + types);
|
||||||
|
}
|
||||||
|
return TypeUtils.makeNullableAsSpecified(bestRepresentative, allNullable);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultingTypes.size() == 1) {
|
||||||
|
return TypeUtils.makeNullableAsSpecified(resultingTypes.get(0), allNullable);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeConstructor constructor = new IntersectionTypeConstructor(Annotations.EMPTY, resultingTypes);
|
||||||
|
|
||||||
|
JetScope[] scopes = new JetScope[resultingTypes.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (JetType type : resultingTypes) {
|
||||||
|
scopes[i] = type.getMemberScope();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JetTypeImpl.create(
|
||||||
|
Annotations.EMPTY,
|
||||||
|
constructor,
|
||||||
|
allNullable,
|
||||||
|
Collections.<TypeProjection>emptyList(),
|
||||||
|
new IntersectionScope(constructor, scopes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO : check intersectibility, don't use a chanied scope
|
||||||
|
private static class IntersectionScope extends ChainedScope {
|
||||||
|
public IntersectionScope(@NotNull TypeConstructor constructor, @NotNull JetScope[] scopes) {
|
||||||
|
super(null, "member scope for intersection type " + constructor, scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public DeclarationDescriptor getContainingDeclaration() {
|
||||||
|
throw new UnsupportedOperationException("Should not call getContainingDeclaration on intersection scope " + this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TypeUnifier {
|
||||||
|
private static class TypeParameterUsage {
|
||||||
|
private final TypeParameterDescriptor typeParameterDescriptor;
|
||||||
|
private final Variance howTheTypeParameterIsUsed;
|
||||||
|
|
||||||
|
public TypeParameterUsage(TypeParameterDescriptor typeParameterDescriptor, Variance howTheTypeParameterIsUsed) {
|
||||||
|
this.typeParameterDescriptor = typeParameterDescriptor;
|
||||||
|
this.howTheTypeParameterIsUsed = howTheTypeParameterIsUsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean mayBeEqual(@NotNull JetType type, @NotNull JetType other) {
|
||||||
|
return unify(type, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean unify(JetType withParameters, JetType expected) {
|
||||||
|
// T -> how T is used
|
||||||
|
final Map<TypeParameterDescriptor, Variance> parameters = new HashMap<TypeParameterDescriptor, Variance>();
|
||||||
|
Function1<TypeParameterUsage, Unit> processor = new Function1<TypeParameterUsage, Unit>() {
|
||||||
|
@Override
|
||||||
|
public Unit invoke(TypeParameterUsage parameterUsage) {
|
||||||
|
Variance howTheTypeIsUsedBefore = parameters.get(parameterUsage.typeParameterDescriptor);
|
||||||
|
if (howTheTypeIsUsedBefore == null) {
|
||||||
|
howTheTypeIsUsedBefore = Variance.INVARIANT;
|
||||||
|
}
|
||||||
|
parameters.put(parameterUsage.typeParameterDescriptor,
|
||||||
|
parameterUsage.howTheTypeParameterIsUsed.superpose(howTheTypeIsUsedBefore));
|
||||||
|
return Unit.INSTANCE$;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
|
||||||
|
processAllTypeParameters(expected, Variance.INVARIANT, processor);
|
||||||
|
ConstraintSystemImpl constraintSystem = new ConstraintSystemImpl();
|
||||||
|
registerTypeVariables(constraintSystem, parameters);
|
||||||
|
constraintSystem.addSubtypeConstraint(withParameters, expected, SPECIAL.position());
|
||||||
|
|
||||||
|
return constraintSystem.getStatus().isSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processAllTypeParameters(JetType type, Variance howThisTypeIsUsed, Function1<TypeParameterUsage, Unit> result) {
|
||||||
|
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
|
||||||
|
if (descriptor instanceof TypeParameterDescriptor) {
|
||||||
|
result.invoke(new TypeParameterUsage((TypeParameterDescriptor) descriptor, howThisTypeIsUsed));
|
||||||
|
}
|
||||||
|
for (TypeProjection projection : type.getArguments()) {
|
||||||
|
if (projection.isStarProjection()) continue;
|
||||||
|
processAllTypeParameters(projection.getType(), projection.getProjectionKind(), result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,19 +17,15 @@
|
|||||||
package org.jetbrains.kotlin.types;
|
package org.jetbrains.kotlin.types;
|
||||||
|
|
||||||
import kotlin.KotlinPackage;
|
import kotlin.KotlinPackage;
|
||||||
import kotlin.Unit;
|
|
||||||
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.builtins.KotlinBuiltIns;
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
|
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl;
|
|
||||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor;
|
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.ChainedScope;
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.JetScope;
|
import org.jetbrains.kotlin.resolve.scopes.JetScope;
|
||||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
|
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
|
||||||
import org.jetbrains.kotlin.utils.DFS;
|
import org.jetbrains.kotlin.utils.DFS;
|
||||||
@@ -37,9 +33,6 @@ import org.jetbrains.kotlin.utils.UtilsPackage;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.resolve.calls.inference.InferencePackage.registerTypeVariables;
|
|
||||||
import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL;
|
|
||||||
|
|
||||||
public class TypeUtils {
|
public class TypeUtils {
|
||||||
public static final JetType DONT_CARE = ErrorUtils.createErrorTypeWithCustomDebugName("DONT_CARE");
|
public static final JetType DONT_CARE = ErrorUtils.createErrorTypeWithCustomDebugName("DONT_CARE");
|
||||||
public static final JetType PLACEHOLDER_FUNCTION_TYPE = ErrorUtils.createErrorTypeWithCustomDebugName("PLACEHOLDER_FUNCTION_TYPE");
|
public static final JetType PLACEHOLDER_FUNCTION_TYPE = ErrorUtils.createErrorTypeWithCustomDebugName("PLACEHOLDER_FUNCTION_TYPE");
|
||||||
@@ -165,173 +158,6 @@ public class TypeUtils {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isIntersectionEmpty(@NotNull JetType typeA, @NotNull JetType typeB) {
|
|
||||||
return intersect(JetTypeChecker.DEFAULT, new LinkedHashSet<JetType>(Arrays.asList(typeA, typeB))) == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set<JetType> types) {
|
|
||||||
if (types.isEmpty()) {
|
|
||||||
return KotlinBuiltIns.getInstance().getNullableAnyType();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (types.size() == 1) {
|
|
||||||
return types.iterator().next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intersection of T1..Tn is an intersection of their non-null versions,
|
|
||||||
// made nullable is they all were nullable
|
|
||||||
boolean allNullable = true;
|
|
||||||
boolean nothingTypePresent = false;
|
|
||||||
List<JetType> nullabilityStripped = new ArrayList<JetType>(types.size());
|
|
||||||
for (JetType type : types) {
|
|
||||||
if (type.isError()) continue;
|
|
||||||
|
|
||||||
nothingTypePresent |= KotlinBuiltIns.isNothingOrNullableNothing(type);
|
|
||||||
allNullable &= type.isMarkedNullable();
|
|
||||||
nullabilityStripped.add(makeNotNullable(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nothingTypePresent) {
|
|
||||||
return allNullable ? KotlinBuiltIns.getInstance().getNullableNothingType() : KotlinBuiltIns.getInstance().getNothingType();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nullabilityStripped.isEmpty()) {
|
|
||||||
// All types were errors
|
|
||||||
return ErrorUtils.createErrorType("Intersection of errors types: " + types);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we remove types that have subtypes in the list
|
|
||||||
List<JetType> resultingTypes = new ArrayList<JetType>();
|
|
||||||
outer:
|
|
||||||
for (JetType type : nullabilityStripped) {
|
|
||||||
if (!canHaveSubtypes(typeChecker, type)) {
|
|
||||||
for (JetType other : nullabilityStripped) {
|
|
||||||
// It makes sense to check for subtyping (other <: type), despite that
|
|
||||||
// type is not supposed to be open, for there're enums
|
|
||||||
if (!TypeUnifier.mayBeEqual(type, other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return makeNullableAsSpecified(type, allNullable);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (JetType other : nullabilityStripped) {
|
|
||||||
if (!type.equals(other) && typeChecker.isSubtypeOf(other, type)) {
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't add type if it is already present, to avoid trivial type intersections in result
|
|
||||||
for (JetType other : resultingTypes) {
|
|
||||||
if (typeChecker.equalTypes(other, type)) {
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resultingTypes.add(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resultingTypes.isEmpty()) {
|
|
||||||
// If we ended up here, it means that all types from `nullabilityStripped` were excluded by the code above
|
|
||||||
// most likely, this is because they are all semantically interchangeable (e.g. List<Foo>! and List<Foo>),
|
|
||||||
// in that case, we can safely select the best representative out of that set and return it
|
|
||||||
// TODO: maybe return the most specific among the types that are subtypes to all others in the `nullabilityStripped`?
|
|
||||||
// TODO: e.g. among {Int, Int?, Int!}, return `Int` (now it returns `Int!`).
|
|
||||||
JetType bestRepresentative = TypesPackage.singleBestRepresentative(nullabilityStripped);
|
|
||||||
if (bestRepresentative == null) {
|
|
||||||
throw new AssertionError("Empty intersection for types " + types);
|
|
||||||
}
|
|
||||||
return makeNullableAsSpecified(bestRepresentative, allNullable);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resultingTypes.size() == 1) {
|
|
||||||
return makeNullableAsSpecified(resultingTypes.get(0), allNullable);
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeConstructor constructor = new IntersectionTypeConstructor(Annotations.EMPTY, resultingTypes);
|
|
||||||
|
|
||||||
JetScope[] scopes = new JetScope[resultingTypes.size()];
|
|
||||||
int i = 0;
|
|
||||||
for (JetType type : resultingTypes) {
|
|
||||||
scopes[i] = type.getMemberScope();
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return JetTypeImpl.create(
|
|
||||||
Annotations.EMPTY,
|
|
||||||
constructor,
|
|
||||||
allNullable,
|
|
||||||
Collections.<TypeProjection>emptyList(),
|
|
||||||
new IntersectionScope(constructor, scopes)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO : check intersectibility, don't use a chanied scope
|
|
||||||
public static class IntersectionScope extends ChainedScope {
|
|
||||||
public IntersectionScope(@NotNull TypeConstructor constructor, @NotNull JetScope[] scopes) {
|
|
||||||
super(null, "member scope for intersection type " + constructor, scopes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor getContainingDeclaration() {
|
|
||||||
throw new UnsupportedOperationException("Should not call getContainingDeclaration on intersection scope " + this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TypeUnifier {
|
|
||||||
private static class TypeParameterUsage {
|
|
||||||
private final TypeParameterDescriptor typeParameterDescriptor;
|
|
||||||
private final Variance howTheTypeParameterIsUsed;
|
|
||||||
|
|
||||||
public TypeParameterUsage(TypeParameterDescriptor typeParameterDescriptor, Variance howTheTypeParameterIsUsed) {
|
|
||||||
this.typeParameterDescriptor = typeParameterDescriptor;
|
|
||||||
this.howTheTypeParameterIsUsed = howTheTypeParameterIsUsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean mayBeEqual(@NotNull JetType type, @NotNull JetType other) {
|
|
||||||
return unify(type, other);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean unify(JetType withParameters, JetType expected) {
|
|
||||||
// T -> how T is used
|
|
||||||
final Map<TypeParameterDescriptor, Variance> parameters = new HashMap<TypeParameterDescriptor, Variance>();
|
|
||||||
Function1<TypeParameterUsage, Unit> processor = new Function1<TypeParameterUsage, Unit>() {
|
|
||||||
@Override
|
|
||||||
public Unit invoke(TypeParameterUsage parameterUsage) {
|
|
||||||
Variance howTheTypeIsUsedBefore = parameters.get(parameterUsage.typeParameterDescriptor);
|
|
||||||
if (howTheTypeIsUsedBefore == null) {
|
|
||||||
howTheTypeIsUsedBefore = Variance.INVARIANT;
|
|
||||||
}
|
|
||||||
parameters.put(parameterUsage.typeParameterDescriptor,
|
|
||||||
parameterUsage.howTheTypeParameterIsUsed.superpose(howTheTypeIsUsedBefore));
|
|
||||||
return Unit.INSTANCE$;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
|
|
||||||
processAllTypeParameters(expected, Variance.INVARIANT, processor);
|
|
||||||
ConstraintSystemImpl constraintSystem = new ConstraintSystemImpl();
|
|
||||||
registerTypeVariables(constraintSystem, parameters);
|
|
||||||
constraintSystem.addSubtypeConstraint(withParameters, expected, SPECIAL.position());
|
|
||||||
|
|
||||||
return constraintSystem.getStatus().isSuccessful();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void processAllTypeParameters(JetType type, Variance howThisTypeIsUsed, Function1<TypeParameterUsage, Unit> result) {
|
|
||||||
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
|
|
||||||
if (descriptor instanceof TypeParameterDescriptor) {
|
|
||||||
result.invoke(new TypeParameterUsage((TypeParameterDescriptor) descriptor, howThisTypeIsUsed));
|
|
||||||
}
|
|
||||||
for (TypeProjection projection : type.getArguments()) {
|
|
||||||
if (projection.isStarProjection()) continue;
|
|
||||||
processAllTypeParameters(projection.getType(), projection.getProjectionKind(), result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean canHaveSubtypes(JetTypeChecker typeChecker, @NotNull JetType type) {
|
public static boolean canHaveSubtypes(JetTypeChecker typeChecker, @NotNull JetType type) {
|
||||||
if (type.isMarkedNullable()) {
|
if (type.isMarkedNullable()) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+1
-1
@@ -196,7 +196,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 = TypeUtils.intersect(JetTypeChecker.DEFAULT, expectedTypes)
|
val theType = TypeIntersector.intersect(JetTypeChecker.DEFAULT, expectedTypes)
|
||||||
if (theType != null) {
|
if (theType != null) {
|
||||||
return arrayOf(theType)
|
return arrayOf(theType)
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-28
@@ -16,36 +16,32 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass
|
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass
|
||||||
|
|
||||||
import org.jetbrains.kotlin.psi.JetFile
|
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
|
||||||
import com.intellij.psi.JavaPsiFacade
|
|
||||||
import org.jetbrains.kotlin.psi.JetExpression
|
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
|
||||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
|
||||||
import org.jetbrains.kotlin.types.Variance
|
|
||||||
import org.jetbrains.kotlin.psi.Call
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.Qualifier
|
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.noSubstitutions
|
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
|
||||||
import com.intellij.codeInsight.intention.IntentionAction
|
|
||||||
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
|
|
||||||
import com.intellij.codeInsight.daemon.quickFix.CreateClassOrPackageFix
|
import com.intellij.codeInsight.daemon.quickFix.CreateClassOrPackageFix
|
||||||
import org.jetbrains.kotlin.idea.quickfix.DelegatingIntentionAction
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
import org.jetbrains.kotlin.idea.JetBundle
|
import com.intellij.psi.JavaPsiFacade
|
||||||
import com.intellij.psi.PsiPackage
|
|
||||||
import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
|
|
||||||
import com.intellij.psi.PsiClass
|
import com.intellij.psi.PsiClass
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiPackage
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.idea.JetBundle
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
|
import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.DelegatingIntentionAction
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.noSubstitutions
|
||||||
|
import org.jetbrains.kotlin.psi.Call
|
||||||
|
import org.jetbrains.kotlin.psi.JetExpression
|
||||||
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
|
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.Qualifier
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
import org.jetbrains.kotlin.types.Variance
|
||||||
|
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind as ClassDescriptorKind
|
import org.jetbrains.kotlin.descriptors.ClassKind as ClassDescriptorKind
|
||||||
|
|
||||||
private fun String.checkClassName(): Boolean = isNotEmpty() && Character.isUpperCase(first())
|
private fun String.checkClassName(): Boolean = isNotEmpty() && Character.isUpperCase(first())
|
||||||
|
|||||||
+1
-1
@@ -519,7 +519,7 @@ private class MutableParameter(
|
|||||||
|
|
||||||
private val defaultType: JetType by lazy {
|
private val defaultType: JetType by lazy {
|
||||||
writable = false
|
writable = false
|
||||||
TypeUtils.intersect(JetTypeChecker.DEFAULT, defaultTypes)!!
|
TypeIntersector.intersect(JetTypeChecker.DEFAULT, defaultTypes)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private val parameterTypeCandidates: List<JetType> by lazy {
|
private val parameterTypeCandidates: List<JetType> by lazy {
|
||||||
|
|||||||
Reference in New Issue
Block a user