From f393ce598dfc6e6106027ddf5e931ef5f3cca695 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Mon, 27 Jul 2015 19:48:25 +0300 Subject: [PATCH] Extract TypeIntersector from TypeUtils --- .../resolve/calls/GenericCandidateResolver.kt | 7 +- .../calls/smartcasts/SmartCastUtils.java | 3 +- .../BasicExpressionTypingVisitor.java | 2 +- .../PatternMatchingTypingVisitor.java | 2 +- .../kotlin/types/JetTypeCheckerTest.java | 2 +- .../java/lazy/types/LazyJavaTypeResolver.kt | 2 +- .../impl/AbstractTypeParameterDescriptor.java | 2 +- .../resolve/calls/inference/TypeBoundsImpl.kt | 2 +- .../kotlin/types/CommonSupertypes.java | 2 +- .../kotlin/types/TypeIntersector.java | 205 ++++++++++++++++++ .../org/jetbrains/kotlin/types/TypeUtils.java | 174 --------------- .../callableBuilder/typeUtils.kt | 2 +- .../createClass/createClassUtils.kt | 52 ++--- .../extractableAnalysisUtil.kt | 2 +- 14 files changed, 242 insertions(+), 217 deletions(-) create mode 100644 core/descriptors/src/org/jetbrains/kotlin/types/TypeIntersector.java diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt index 4b7d1b959b6..a653c72cd17 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt @@ -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.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.types.JetType -import org.jetbrains.kotlin.types.TypeSubstitutor -import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE 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.expressions.ExpressionTypingUtils @@ -191,7 +188,7 @@ class GenericCandidateResolver( val possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue) if (possibleTypes.isEmpty()) return type - return TypeUtils.intersect(JetTypeChecker.DEFAULT, possibleTypes) + return TypeIntersector.intersect(JetTypeChecker.DEFAULT, possibleTypes) } public fun completeTypeInferenceDependentOnFunctionArgumentsForCall( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java index 0bcd488fa5e..d8e7d48fc04 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java @@ -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.ThisReceiver; import org.jetbrains.kotlin.types.JetType; +import org.jetbrains.kotlin.types.TypeIntersector; import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.checker.JetTypeChecker; @@ -151,7 +152,7 @@ public class SmartCastUtils { } 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()) { return receiverParameterType; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index b027b1c8f68..a6d6fd64a0c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -1251,7 +1251,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { JetType rightType = facade.getTypeInfo(right, context).getType(); 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)); } SenselessComparisonChecker.checkSenselessComparisonWithNull( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java index 2f8951c6f35..25b25b3bb86 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java @@ -44,8 +44,8 @@ import java.util.Set; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean; import static org.jetbrains.kotlin.diagnostics.Errors.*; 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.isIntersectionEmpty; import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.newWritableScopeImpl; public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { diff --git a/compiler/tests/org/jetbrains/kotlin/types/JetTypeCheckerTest.java b/compiler/tests/org/jetbrains/kotlin/types/JetTypeCheckerTest.java index 00f8be5e9ed..24795fc0851 100644 --- a/compiler/tests/org/jetbrains/kotlin/types/JetTypeCheckerTest.java +++ b/compiler/tests/org/jetbrains/kotlin/types/JetTypeCheckerTest.java @@ -523,7 +523,7 @@ public class JetTypeCheckerTest extends JetLiteFixture { for (String type : types) { 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); assertEquals(makeType(expected), result); } diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/LazyJavaTypeResolver.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/LazyJavaTypeResolver.kt index 85487170526..4620242d6fb 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/LazyJavaTypeResolver.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/LazyJavaTypeResolver.kt @@ -178,7 +178,7 @@ class LazyJavaTypeResolver( for (supertype in (classifier() as JavaTypeParameter).getUpperBounds()) { 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()) } diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/AbstractTypeParameterDescriptor.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/AbstractTypeParameterDescriptor.java index c37da05615c..9aeadf80d93 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/AbstractTypeParameterDescriptor.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/AbstractTypeParameterDescriptor.java @@ -135,7 +135,7 @@ public abstract class AbstractTypeParameterDescriptor extends DeclarationDescrip private JetType computeUpperBoundsAsType() { Set upperBounds = getUpperBounds(); 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(); } diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt index 2be71975086..b69d0e3673d 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt @@ -134,7 +134,7 @@ public class TypeBoundsImpl( } 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 (tryPossibleAnswer(bounds, intersectionOfUpperBounds)) { return setOf(intersectionOfUpperBounds) diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/CommonSupertypes.java b/core/descriptors/src/org/jetbrains/kotlin/types/CommonSupertypes.java index 3ee9d1a3548..f16ee694333 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/CommonSupertypes.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/CommonSupertypes.java @@ -299,7 +299,7 @@ public class CommonSupertypes { return new TypeProjectionImpl(projectionKind, findCommonSupertype(outs, recursionDepth + 1, maxDepth)); } if (ins != null) { - JetType intersection = TypeUtils.intersect(JetTypeChecker.DEFAULT, ins); + JetType intersection = TypeIntersector.intersect(JetTypeChecker.DEFAULT, ins); if (intersection == null) { return new TypeProjectionImpl(OUT_VARIANCE, findCommonSupertype(parameterDescriptor.getUpperBounds(), recursionDepth + 1, maxDepth)); } diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeIntersector.java b/core/descriptors/src/org/jetbrains/kotlin/types/TypeIntersector.java new file mode 100644 index 00000000000..4867aa1b520 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeIntersector.java @@ -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(Arrays.asList(typeA, typeB))) == null; + } + + @Nullable + public static JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set 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 nullabilityStripped = new ArrayList(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 resultingTypes = new ArrayList(); + 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! and List), + // 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.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 parameters = new HashMap(); + Function1 processor = new Function1() { + @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 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); + } + } + } +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java index 46d8a7c5a31..2b521ba265b 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java @@ -17,19 +17,15 @@ package org.jetbrains.kotlin.types; import kotlin.KotlinPackage; -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.ClassDescriptor; -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.constants.IntegerValueTypeConstructor; -import org.jetbrains.kotlin.resolve.scopes.ChainedScope; import org.jetbrains.kotlin.resolve.scopes.JetScope; import org.jetbrains.kotlin.types.checker.JetTypeChecker; import org.jetbrains.kotlin.utils.DFS; @@ -37,9 +33,6 @@ import org.jetbrains.kotlin.utils.UtilsPackage; 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 static final JetType DONT_CARE = ErrorUtils.createErrorTypeWithCustomDebugName("DONT_CARE"); public static final JetType PLACEHOLDER_FUNCTION_TYPE = ErrorUtils.createErrorTypeWithCustomDebugName("PLACEHOLDER_FUNCTION_TYPE"); @@ -165,173 +158,6 @@ public class TypeUtils { return type; } - public static boolean isIntersectionEmpty(@NotNull JetType typeA, @NotNull JetType typeB) { - return intersect(JetTypeChecker.DEFAULT, new LinkedHashSet(Arrays.asList(typeA, typeB))) == null; - } - - @Nullable - public static JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set 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 nullabilityStripped = new ArrayList(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 resultingTypes = new ArrayList(); - 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! and List), - // 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.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 parameters = new HashMap(); - Function1 processor = new Function1() { - @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 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) { if (type.isMarkedNullable()) { return true; diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt index eaf64fcf3e4..15fc202b339 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt @@ -196,7 +196,7 @@ private fun JetNamedDeclaration.guessType(context: BindingContext): Array ErrorUtils.containsErrorType(expectedType) }) { return arrayOf() } - val theType = TypeUtils.intersect(JetTypeChecker.DEFAULT, expectedTypes) + val theType = TypeIntersector.intersect(JetTypeChecker.DEFAULT, expectedTypes) if (theType != null) { return arrayOf(theType) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt index 4a791e551a2..c5daf3a671b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt @@ -16,36 +16,32 @@ 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 org.jetbrains.kotlin.idea.quickfix.DelegatingIntentionAction -import org.jetbrains.kotlin.idea.JetBundle -import com.intellij.psi.PsiPackage -import org.jetbrains.kotlin.idea.core.refactoring.canRefactor +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.psi.JavaPsiFacade 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 private fun String.checkClassName(): Boolean = isNotEmpty() && Character.isUpperCase(first()) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt index d107957660b..d1dd9aebb02 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt @@ -519,7 +519,7 @@ private class MutableParameter( private val defaultType: JetType by lazy { writable = false - TypeUtils.intersect(JetTypeChecker.DEFAULT, defaultTypes)!! + TypeIntersector.intersect(JetTypeChecker.DEFAULT, defaultTypes)!! } private val parameterTypeCandidates: List by lazy {