Introduce new type checker.
This commit is contained in:
@@ -50,6 +50,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.TypeUtils.noExpectedType
|
||||
import org.jetbrains.kotlin.types.checker.ErrorTypesAreEqualToAnything
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import java.util.*
|
||||
|
||||
@@ -178,7 +179,7 @@ class CandidateResolver(
|
||||
// TODO: use constraint system to check if candidateKCallableType can be a subtype of expectedType
|
||||
val substituteDontCare = makeConstantSubstitutor(candidateTypeParameters, TypeUtils.DONT_CARE)
|
||||
val subTypeSubstituted = substituteDontCare.substitute(subType, Variance.INVARIANT) ?: return true
|
||||
return KotlinTypeChecker.ERROR_TYPES_ARE_EQUAL_TO_ANYTHING.isSubtypeOf(subTypeSubstituted, superType)
|
||||
return ErrorTypesAreEqualToAnything.isSubtypeOf(subTypeSubstituted, superType)
|
||||
}
|
||||
|
||||
private fun CallCandidateResolutionContext<*>.checkVisibilityWithoutReceiver() = checkAndReport {
|
||||
|
||||
@@ -132,14 +132,14 @@ public class TypeIntersector {
|
||||
return TypeUtils.makeNullableAsSpecified(resultingTypes.get(0), allNullable);
|
||||
}
|
||||
|
||||
TypeConstructor constructor = new IntersectionTypeConstructor(Annotations.Companion.getEMPTY(), resultingTypes);
|
||||
IntersectionTypeConstructor constructor = new IntersectionTypeConstructor(resultingTypes);
|
||||
|
||||
return KotlinTypeFactory.simpleType(
|
||||
Annotations.Companion.getEMPTY(),
|
||||
constructor,
|
||||
Collections.<TypeProjection>emptyList(),
|
||||
allNullable,
|
||||
TypeIntersectionScope.create("member scope for intersection type " + constructor, resultingTypes)
|
||||
constructor.createScopeForKotlinType()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.types
|
||||
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.checker.ErrorTypesAreEqualToAnything
|
||||
|
||||
/**
|
||||
* This is temporary hack for type intersector.
|
||||
@@ -33,7 +33,7 @@ internal fun hackForTypeIntersector(types: Collection<KotlinType>): KotlinType?
|
||||
|
||||
return types.firstOrNull { candidate ->
|
||||
types.all {
|
||||
KotlinTypeChecker.ERROR_TYPES_ARE_EQUAL_TO_ANYTHING.isSubtypeOf(candidate, it)
|
||||
ErrorTypesAreEqualToAnything.isSubtypeOf(candidate, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// FILE: A.java
|
||||
public interface A<T> {
|
||||
}
|
||||
|
||||
// FILE: B.java
|
||||
public class B implements A<String> {
|
||||
}
|
||||
|
||||
// FILE: 1.kt
|
||||
class C: B()
|
||||
|
||||
class D: B(), A<String>
|
||||
class E: B(), A<String?>
|
||||
|
||||
fun eatAString(a: A<String>) {}
|
||||
fun eatAStringN(a: A<String?>) {}
|
||||
|
||||
fun test(b: B, c: C, d: D, e: E) {
|
||||
eatAString(b)
|
||||
eatAString(c)
|
||||
eatAString(d)
|
||||
eatAString(<!TYPE_MISMATCH!>e<!>)
|
||||
|
||||
eatAStringN(b)
|
||||
eatAStringN(c)
|
||||
eatAStringN(<!TYPE_MISMATCH!>d<!>)
|
||||
eatAStringN(e)
|
||||
}
|
||||
|
||||
// FILE: 3.kt
|
||||
|
||||
interface X : A<String>
|
||||
interface Y: X
|
||||
interface Z: X
|
||||
|
||||
class W: B(), Z
|
||||
|
||||
fun test2(w: W) {
|
||||
eatAString(w)
|
||||
eatAStringN(<!TYPE_MISMATCH!>w<!>)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package
|
||||
|
||||
public fun eatAString(/*0*/ a: A<kotlin.String>): kotlin.Unit
|
||||
public fun eatAStringN(/*0*/ a: A<kotlin.String?>): kotlin.Unit
|
||||
public fun test(/*0*/ b: B, /*1*/ c: C, /*2*/ d: D, /*3*/ e: E): kotlin.Unit
|
||||
public fun test2(/*0*/ w: W): kotlin.Unit
|
||||
|
||||
public interface A</*0*/ T : kotlin.Any!> {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public open class B : A<kotlin.String!> {
|
||||
public constructor B()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class C : B {
|
||||
public constructor C()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class D : B, A<kotlin.String> {
|
||||
public constructor D()
|
||||
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class E : B, A<kotlin.String?> {
|
||||
public constructor E()
|
||||
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class W : B, Z {
|
||||
public constructor W()
|
||||
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public interface X : A<kotlin.String> {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public interface Y : X {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public interface Z : X {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -18921,6 +18921,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("javaAndKotlinSuperType.kt")
|
||||
public void testJavaAndKotlinSuperType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/subtyping/javaAndKotlinSuperType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt2069.kt")
|
||||
public void testKt2069() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/subtyping/kt2069.kt");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 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.
|
||||
@@ -182,13 +182,13 @@ public class KotlinTypeCheckerTest extends KotlinTestWithEnvironment {
|
||||
assertCommonSupertype("Base_T<in Int>", "Base_T<Int>", "Base_T<in Int>");
|
||||
assertCommonSupertype("Base_T<in Int>", "Derived_T<Int>", "Base_T<in Int>");
|
||||
assertCommonSupertype("Base_T<in Int>", "Derived_T<in Int>", "Base_T<Int>");
|
||||
assertCommonSupertype("Base_T<*>", "Base_T<Int>", "Base_T<*>");
|
||||
assertCommonSupertype("Base_T<out Any?>", "Base_T<Int>", "Base_T<*>");
|
||||
|
||||
assertCommonSupertype("Base_T<out Parent>", "Base_T<A>", "Base_T<B>");
|
||||
}
|
||||
|
||||
public void testCommonSupertypesForRecursive() throws Exception {
|
||||
assertCommonSupertype("Rec<out Rec<out Rec<out Rec<out Rec<out Any?>>>>>", "ARec", "BRec");
|
||||
assertCommonSupertype("Rec<out Rec<out Rec<out Rec<out Rec<*>>>>>", "ARec", "BRec");
|
||||
}
|
||||
|
||||
public void testIntersect() throws Exception {
|
||||
|
||||
+5
-2
@@ -24,11 +24,14 @@ import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.Variance.IN_VARIANCE
|
||||
import org.jetbrains.kotlin.types.Variance.OUT_VARIANCE
|
||||
import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
|
||||
class CapturedTypeConstructor(
|
||||
val typeProjection: TypeProjection
|
||||
): TypeConstructor {
|
||||
var newTypeConstructor: NewCapturedTypeConstructor? = null
|
||||
|
||||
init {
|
||||
assert(typeProjection.projectionKind != Variance.INVARIANT) {
|
||||
"Only nontrivial projections can be captured, not: $typeProjection"
|
||||
@@ -59,8 +62,8 @@ class CapturedTypeConstructor(
|
||||
}
|
||||
|
||||
class CapturedType(
|
||||
private val typeProjection: TypeProjection,
|
||||
override val constructor: TypeConstructor = CapturedTypeConstructor(typeProjection),
|
||||
val typeProjection: TypeProjection,
|
||||
override val constructor: CapturedTypeConstructor = CapturedTypeConstructor(typeProjection),
|
||||
override val isMarkedNullable: Boolean = false,
|
||||
override val annotations: Annotations = Annotations.EMPTY
|
||||
): SimpleType(), SubtypingRepresentatives {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 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.
|
||||
@@ -45,7 +45,7 @@ class TypeIntersectionScope private constructor(override val workerScope: Chaine
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun create(message: String, types: List<KotlinType>): MemberScope {
|
||||
fun create(message: String, types: Collection<KotlinType>): MemberScope {
|
||||
val chainedScope = ChainedMemberScope(message, types.map { it.memberScope })
|
||||
if (types.size <= 1) return chainedScope
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 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.
|
||||
@@ -22,15 +22,16 @@ import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotatedImpl;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
import org.jetbrains.kotlin.resolve.scopes.TypeIntersectionScope;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class IntersectionTypeConstructor extends AnnotatedImpl implements TypeConstructor {
|
||||
public class IntersectionTypeConstructor implements TypeConstructor {
|
||||
private final Set<KotlinType> intersectedTypes;
|
||||
private final int hashCode;
|
||||
|
||||
public IntersectionTypeConstructor(Annotations annotations, Collection<KotlinType> typesToIntersect) {
|
||||
super(annotations);
|
||||
public IntersectionTypeConstructor(Collection<KotlinType> typesToIntersect) {
|
||||
assert !typesToIntersect.isEmpty() : "Attempt to create an empty intersection";
|
||||
|
||||
this.intersectedTypes = new LinkedHashSet<KotlinType>(typesToIntersect);
|
||||
@@ -49,6 +50,10 @@ public class IntersectionTypeConstructor extends AnnotatedImpl implements TypeCo
|
||||
return intersectedTypes;
|
||||
}
|
||||
|
||||
public MemberScope createScopeForKotlinType() {
|
||||
return TypeIntersectionScope.create("member scope for intersection type " + this, intersectedTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinal() {
|
||||
return false;
|
||||
@@ -106,4 +111,9 @@ public class IntersectionTypeConstructor extends AnnotatedImpl implements TypeCo
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Annotations getAnnotations() {
|
||||
return Annotations.Companion.getEMPTY();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.checker.StrictEqualityTypeChecker
|
||||
|
||||
/**
|
||||
* [KotlinType] has only two direct subclasses: [WrappedType] and [UnwrappedType].
|
||||
@@ -63,7 +63,7 @@ sealed class KotlinType : Annotated {
|
||||
if (this === other) return true
|
||||
if (other !is KotlinType) return false
|
||||
|
||||
return isMarkedNullable == other.isMarkedNullable && KotlinTypeChecker.FLEXIBLE_UNEQUAL_TO_INFLEXIBLE.equalTypes(this, other)
|
||||
return isMarkedNullable == other.isMarkedNullable && StrictEqualityTypeChecker.strictEqualTypes(unwrap(), other.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.checker
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.types.*
|
||||
|
||||
fun intersectTypes(types: List<UnwrappedType>): UnwrappedType {
|
||||
when (types.size) {
|
||||
0 -> error("Expected some types")
|
||||
1 -> return types.single()
|
||||
}
|
||||
var hasFlexibleTypes = false
|
||||
var hasErrorType = false
|
||||
val lowerBounds = types.map {
|
||||
hasErrorType = hasErrorType || it.isError
|
||||
when (it) {
|
||||
is SimpleType -> it
|
||||
is FlexibleType -> {
|
||||
hasFlexibleTypes = true
|
||||
it.lowerBound
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasErrorType) {
|
||||
return ErrorUtils.createErrorType("Intersection of error types: $types")
|
||||
}
|
||||
|
||||
if (!hasFlexibleTypes) {
|
||||
return intersectTypes(lowerBounds)
|
||||
}
|
||||
|
||||
val upperBounds = types.map { it.upperIfFlexible() }
|
||||
/**
|
||||
* We should save this rules:
|
||||
* - if for each type from types type is subtype of A, then intersectionType should be subtype of A
|
||||
* - same for type B which is subtype of all types.
|
||||
*
|
||||
* Note: when we construct intersection type of dynamic(or Raw type) & other type, we can get non-dynamic type. // todo discuss
|
||||
*/
|
||||
return KotlinTypeFactory.flexibleType(intersectTypes(lowerBounds), intersectTypes(upperBounds))
|
||||
}
|
||||
|
||||
// types.size >= 2
|
||||
// It is incorrect see to nullability here, because of KT-12684
|
||||
private fun intersectTypes(types: List<SimpleType>): SimpleType {
|
||||
val constructor = IntersectionTypeConstructor(types)
|
||||
return KotlinTypeFactory.simpleType(Annotations.EMPTY, constructor, listOf(), false, constructor.createScopeForKotlinType())
|
||||
}
|
||||
|
||||
@@ -26,22 +26,7 @@ public interface KotlinTypeChecker {
|
||||
boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b);
|
||||
}
|
||||
|
||||
KotlinTypeChecker DEFAULT = new KotlinTypeCheckerImpl(new TypeCheckingProcedure(new TypeCheckerProcedureCallbacksImpl()));
|
||||
|
||||
KotlinTypeChecker ERROR_TYPES_ARE_EQUAL_TO_ANYTHING = new KotlinTypeCheckerImpl(new TypeCheckingProcedure(new TypeCheckerProcedureCallbacksImpl() {
|
||||
@Override
|
||||
public boolean assertEqualTypes(@NotNull KotlinType a, @NotNull KotlinType b, @NotNull TypeCheckingProcedure typeCheckingProcedure) {
|
||||
return a.isError() || b.isError() || super.assertEqualTypes(a, b, typeCheckingProcedure);
|
||||
}
|
||||
}));
|
||||
|
||||
KotlinTypeChecker FLEXIBLE_UNEQUAL_TO_INFLEXIBLE = new KotlinTypeCheckerImpl(new TypeCheckingProcedure(new TypeCheckerProcedureCallbacksImpl()) {
|
||||
@Override
|
||||
protected boolean heterogeneousEquivalence(KotlinType inflexibleType, KotlinType flexibleType) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
KotlinTypeChecker DEFAULT = NewKotlinTypeChecker.INSTANCE;
|
||||
|
||||
boolean isSubtypeOf(@NotNull KotlinType subtype, @NotNull KotlinType supertype);
|
||||
boolean equalTypes(@NotNull KotlinType a, @NotNull KotlinType b);
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.checker
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
import org.jetbrains.kotlin.utils.DO_NOTHING_2
|
||||
|
||||
fun captureFromArguments(
|
||||
type: SimpleType,
|
||||
status: CaptureStatus,
|
||||
acceptNewCapturedType: ((argumentIndex: Int, NewCapturedType) -> Unit) = DO_NOTHING_2
|
||||
): SimpleType {
|
||||
val arguments = type.arguments
|
||||
if (arguments.all { it.projectionKind == Variance.INVARIANT }) return type
|
||||
|
||||
val newArguments = arguments.mapIndexed {
|
||||
index, projection ->
|
||||
if (projection.projectionKind == Variance.INVARIANT) return@mapIndexed projection
|
||||
|
||||
val lowerType = if (!projection.isStarProjection && projection.projectionKind == Variance.IN_VARIANCE) {
|
||||
projection.type.unwrap()
|
||||
} else null
|
||||
|
||||
NewCapturedType(status, lowerType, projection).asTypeProjection() // todo optimization: do not create type projection
|
||||
}
|
||||
|
||||
val substitutor = TypeConstructorSubstitution.create(type.constructor, newArguments).buildSubstitutor()
|
||||
for (index in arguments.indices) {
|
||||
val oldProjection = arguments[index]
|
||||
val newProjection = newArguments[index]
|
||||
|
||||
if (oldProjection.projectionKind == Variance.INVARIANT) continue
|
||||
var upperBounds = type.constructor.parameters[index].upperBounds.map {
|
||||
NewKotlinTypeChecker.transformToNewType(substitutor.safeSubstitute(it, Variance.INVARIANT).unwrap())
|
||||
}
|
||||
if (!oldProjection.isStarProjection && oldProjection.projectionKind == Variance.OUT_VARIANCE) {
|
||||
upperBounds += NewKotlinTypeChecker.transformToNewType(oldProjection.type.unwrap())
|
||||
}
|
||||
|
||||
val capturedType = newProjection.type as NewCapturedType
|
||||
capturedType.constructor.initializeSupertypes(upperBounds)
|
||||
acceptNewCapturedType(index, capturedType)
|
||||
}
|
||||
|
||||
return KotlinTypeFactory.simpleType(type.annotations, type.constructor, newArguments, type.isMarkedNullable)
|
||||
}
|
||||
|
||||
enum class CaptureStatus {
|
||||
FOR_SUBTYPING,
|
||||
FROM_EXPRESSION
|
||||
}
|
||||
|
||||
/**
|
||||
* Now [lowerType] is not null only for in projections.
|
||||
* Example: `Inv<in String>` For `in String` we create CapturedType with [lowerType] = String.
|
||||
*
|
||||
* TODO: interface D<T, S: List<T>, D<*, List<Number>> -> D<Q, List<Number>>
|
||||
* We should set [lowerType] for Q as Number. For this we should use constraint system.
|
||||
*
|
||||
*/
|
||||
class NewCapturedType(
|
||||
val captureStatus: CaptureStatus,
|
||||
override val constructor: NewCapturedTypeConstructor,
|
||||
val lowerType: UnwrappedType?,
|
||||
override val annotations: Annotations = Annotations.EMPTY,
|
||||
override val isMarkedNullable: Boolean = false
|
||||
): SimpleType() {
|
||||
|
||||
constructor(captureStatus: CaptureStatus, lowerType: UnwrappedType?, projection: TypeProjection): this(captureStatus, NewCapturedTypeConstructor(projection), lowerType)
|
||||
|
||||
override val arguments: List<TypeProjection> get() = listOf()
|
||||
override val memberScope: MemberScope // todo what about foo().bar() where foo() return captured type?
|
||||
get() = ErrorUtils.createErrorScope("No member resolution should be done on captured type!", true)
|
||||
override val isError: Boolean get() = false
|
||||
|
||||
override fun replaceAnnotations(newAnnotations: Annotations) =
|
||||
NewCapturedType(captureStatus, constructor, lowerType, newAnnotations, isMarkedNullable)
|
||||
|
||||
override fun makeNullableAsSpecified(newNullability: Boolean) =
|
||||
NewCapturedType(captureStatus, constructor, lowerType, annotations, newNullability)
|
||||
}
|
||||
|
||||
class NewCapturedTypeConstructor(val projection: TypeProjection, private var supertypes: List<UnwrappedType>? = null) : TypeConstructor {
|
||||
fun initializeSupertypes(supertypes: List<UnwrappedType>) {
|
||||
assert(this.supertypes == null) {
|
||||
"Already initialized! oldValue = ${this.supertypes}, newValue = $supertypes"
|
||||
}
|
||||
this.supertypes = supertypes
|
||||
}
|
||||
|
||||
override fun getSupertypes() = supertypes ?: emptyList()
|
||||
override fun getParameters(): List<TypeParameterDescriptor> = emptyList()
|
||||
|
||||
override fun isFinal() = false
|
||||
override fun isDenotable() = false
|
||||
override fun getDeclarationDescriptor(): ClassifierDescriptor? = null
|
||||
override fun getBuiltIns(): KotlinBuiltIns = projection.type.builtIns
|
||||
override val annotations: Annotations get() = Annotations.EMPTY
|
||||
|
||||
override fun toString() = "CapturedType($projection)"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.checker
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.CapturedType
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.TypeCheckerContext.SupertypesPolicy
|
||||
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
|
||||
object StrictEqualityTypeChecker {
|
||||
/**
|
||||
* String! != String & A<String!> != A<String>, also A<in Nothing> != A<out Any?>
|
||||
* also A<*> != A<out Any?>
|
||||
* different error types non-equals even errorTypeEqualToAnything
|
||||
*/
|
||||
fun strictEqualTypes(a: UnwrappedType, b: UnwrappedType): Boolean {
|
||||
if (a === b) return true
|
||||
|
||||
if (a is SimpleType && b is SimpleType) return strictEqualTypes(a, b)
|
||||
if (a is FlexibleType && b is FlexibleType) {
|
||||
return strictEqualTypes(a.lowerBound, b.lowerBound) &&
|
||||
strictEqualTypes(a.upperBound, b.upperBound)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun strictEqualTypes(a: SimpleType, b: SimpleType): Boolean {
|
||||
if (a.isMarkedNullable != b.isMarkedNullable
|
||||
|| a.constructor != b.constructor
|
||||
|| a.arguments.size != b.arguments.size
|
||||
) {
|
||||
return false
|
||||
}
|
||||
for (i in a.arguments.indices) {
|
||||
val aArg = a.arguments[i]
|
||||
val bArg = b.arguments[i]
|
||||
if (aArg.isStarProjection != bArg.isStarProjection) return false
|
||||
|
||||
// both non-star
|
||||
if (!aArg.isStarProjection) {
|
||||
if (aArg.projectionKind != bArg.projectionKind) return false
|
||||
if (!strictEqualTypes(aArg.type.unwrap(), bArg.type.unwrap())) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object ErrorTypesAreEqualToAnything : KotlinTypeChecker {
|
||||
override fun isSubtypeOf(subtype: KotlinType, supertype: KotlinType): Boolean =
|
||||
NewKotlinTypeChecker.run { TypeCheckerContext(true).isSubtypeOf(subtype.unwrap(), supertype.unwrap()) }
|
||||
|
||||
override fun equalTypes(a: KotlinType, b: KotlinType): Boolean =
|
||||
NewKotlinTypeChecker.run { TypeCheckerContext(true).equalTypes(a.unwrap(), b.unwrap()) }
|
||||
}
|
||||
|
||||
object NewKotlinTypeChecker : KotlinTypeChecker {
|
||||
override fun isSubtypeOf(subtype: KotlinType, supertype: KotlinType): Boolean =
|
||||
TypeCheckerContext(true).run { isSubtypeOf(subtype.unwrap(), supertype.unwrap()) } // todo fix flag errorTypeEqualsToAnything
|
||||
override fun equalTypes(a: KotlinType, b: KotlinType): Boolean =
|
||||
TypeCheckerContext(false).run { equalTypes(a.unwrap(), b.unwrap()) }
|
||||
|
||||
fun TypeCheckerContext.equalTypes(a: UnwrappedType, b: UnwrappedType): Boolean {
|
||||
if (a === b) return true
|
||||
|
||||
return isSubtypeOf(a, b) && isSubtypeOf(b, a)
|
||||
}
|
||||
|
||||
fun TypeCheckerContext.isSubtypeOf(subType: UnwrappedType, superType: UnwrappedType) =
|
||||
isSubtypeOf(subType.lowerIfFlexible(), superType.upperIfFlexible())
|
||||
|
||||
fun TypeCheckerContext.isSubtypeOf(subType: SimpleType, superType: SimpleType): Boolean {
|
||||
return isSubtypeOfForNewTypes(transformToNewType(subType), transformToNewType(superType))
|
||||
}
|
||||
|
||||
fun transformToNewType(type: SimpleType): SimpleType {
|
||||
if (type is CapturedType) {
|
||||
val lowerType = type.typeProjection.check { it.projectionKind == Variance.IN_VARIANCE }?.type?.unwrap()
|
||||
|
||||
// it is incorrect calculate this type directly because of recursive star projections
|
||||
if (type.constructor.newTypeConstructor == null) {
|
||||
type.constructor.newTypeConstructor = NewCapturedTypeConstructor(type.typeProjection, type.constructor.supertypes.map { it.unwrap() })
|
||||
}
|
||||
val newCapturedType = NewCapturedType(CaptureStatus.FOR_SUBTYPING, type.constructor.newTypeConstructor!!,
|
||||
lowerType, type.annotations, type.isMarkedNullable)
|
||||
return newCapturedType
|
||||
}
|
||||
|
||||
if (type.constructor is IntersectionTypeConstructor && type.isMarkedNullable) {
|
||||
val newSuperTypes = type.constructor.supertypes.map { it.makeNullable() }
|
||||
val newConstructor = IntersectionTypeConstructor(newSuperTypes)
|
||||
return KotlinTypeFactory.simpleType(type.annotations, newConstructor, listOf(), false, newConstructor.createScopeForKotlinType())
|
||||
}
|
||||
|
||||
if (type.constructor is IntegerValueTypeConstructor) {
|
||||
val newConstructor = IntersectionTypeConstructor(type.constructor.supertypes.map { TypeUtils.makeNullableAsSpecified(it, type.isMarkedNullable) })
|
||||
return KotlinTypeFactory.simpleType(type.annotations, newConstructor, listOf(), false, type.memberScope)
|
||||
}
|
||||
|
||||
return type
|
||||
}
|
||||
|
||||
fun transformToNewType(type: UnwrappedType): UnwrappedType =
|
||||
when (type) {
|
||||
is SimpleType -> transformToNewType(type)
|
||||
is FlexibleType -> {
|
||||
val newLower = transformToNewType(type.lowerBound)
|
||||
val newUpper = transformToNewType(type.upperBound)
|
||||
if (newLower !== type.lowerBound || newUpper !== type.upperBound) {
|
||||
KotlinTypeFactory.flexibleType(newLower, newUpper)
|
||||
}
|
||||
else {
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TypeCheckerContext.isSubtypeOfForNewTypes(subType: SimpleType, superType: SimpleType): Boolean {
|
||||
if (isSubtypeByExternalRule(subType, superType)) return true // todo: do not call this for T? <: String?
|
||||
|
||||
if (subType.isError || superType.isError) {
|
||||
if (errorTypeEqualsToAnything) return true
|
||||
|
||||
if (subType.isMarkedNullable && !superType.isMarkedNullable) return false
|
||||
|
||||
return StrictEqualityTypeChecker.strictEqualTypes(subType.makeNullableAsSpecified(false), superType.makeNullableAsSpecified(false))
|
||||
}
|
||||
|
||||
if (superType is NewCapturedType && superType.lowerType != null && isSubtypeOf(subType, superType.lowerType)) return true
|
||||
|
||||
(superType.constructor as? IntersectionTypeConstructor)?.let {
|
||||
assert(!superType.isMarkedNullable) { "Intersection type should not be marked nullable!: $superType" }
|
||||
return it.supertypes.all { isSubtypeOf(subType, it.unwrap()) }
|
||||
}
|
||||
|
||||
return isSubtypeOfForSingleClassifierType(subType, superType)
|
||||
}
|
||||
|
||||
private fun TypeCheckerContext.hasNothingSupertype(type: SimpleType) = // todo add tests
|
||||
anySupertype(type, KotlinBuiltIns::isNothingOrNullableNothing) {
|
||||
if (it.isClassType) {
|
||||
SupertypesPolicy.None
|
||||
}
|
||||
else {
|
||||
SupertypesPolicy.LowerIfFlexible
|
||||
}
|
||||
}
|
||||
|
||||
private fun TypeCheckerContext.isSubtypeOfForSingleClassifierType(subType: SimpleType, superType: SimpleType): Boolean {
|
||||
assert(subType.isSingleClassifierType || subType.isIntersectionType) { "Not singleClassifierType and not intersection subType: $subType" }
|
||||
assert(superType.isSingleClassifierType) { "Not singleClassifierType superType: $superType" }
|
||||
|
||||
if (!NullabilityChecker.isPossibleSubtype(this, subType, superType)) return false
|
||||
|
||||
val superConstructor = superType.constructor
|
||||
val supertypesWithSameConstructor = findCorrespondingSupertypes(subType, superConstructor)
|
||||
when (supertypesWithSameConstructor.size) {
|
||||
0 -> return hasNothingSupertype(subType) // todo Nothing & Array<Number> <: Array<String>
|
||||
1 -> return isSubtypeForSameConstructor(supertypesWithSameConstructor.first().arguments, superType)
|
||||
|
||||
else -> { // at least 2 supertypes with same constructors. Such case is rare
|
||||
if (supertypesWithSameConstructor.any { isSubtypeForSameConstructor(it.arguments, superType) }) return true
|
||||
|
||||
val newArguments = superConstructor.parameters.mapIndexed { index, parameterDescriptor ->
|
||||
val allProjections = supertypesWithSameConstructor.map {
|
||||
it.arguments.getOrNull(index)?.check { it.projectionKind == Variance.INVARIANT }?.type?.unwrap()
|
||||
?: error("Incorrect type: $it, subType: $subType, superType: $superType")
|
||||
}
|
||||
|
||||
// todo discuss
|
||||
intersectTypes(allProjections).asTypeProjection()
|
||||
}
|
||||
|
||||
return isSubtypeForSameConstructor(newArguments, superType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nullability was checked earlier via nullabilityChecker
|
||||
private fun TypeCheckerContext.findCorrespondingSupertypes(
|
||||
baseType: SimpleType,
|
||||
constructor: TypeConstructor
|
||||
): List<SimpleType> {
|
||||
fun TypeCheckerContext.collectAndFilter(classType: SimpleType, constructor: TypeConstructor) =
|
||||
selectOnlyPureKotlinSupertypes(collectAllSupertypesWithGivenTypeConstructor(classType, constructor))
|
||||
|
||||
if (baseType.isClassType) {
|
||||
return collectAndFilter(baseType, constructor)
|
||||
}
|
||||
|
||||
// i.e. superType is not a classType
|
||||
if (constructor !is ClassDescriptor) {
|
||||
return collectAllSupertypesWithGivenTypeConstructor(baseType, constructor)
|
||||
}
|
||||
|
||||
// todo add tests
|
||||
val classTypeSupertypes = SmartList<SimpleType>()
|
||||
anySupertype(baseType, { false }) {
|
||||
if (it.isClassType) {
|
||||
classTypeSupertypes.add(it)
|
||||
SupertypesPolicy.None
|
||||
}
|
||||
else {
|
||||
SupertypesPolicy.LowerIfFlexible
|
||||
}
|
||||
}
|
||||
|
||||
return classTypeSupertypes.flatMap { collectAndFilter(it, constructor) }
|
||||
}
|
||||
|
||||
private fun TypeCheckerContext.collectAllSupertypesWithGivenTypeConstructor(
|
||||
baseType: SimpleType,
|
||||
constructor: TypeConstructor
|
||||
): List<SimpleType> {
|
||||
|
||||
var result: MutableList<SimpleType>? = null
|
||||
|
||||
anySupertype(baseType, { false }) {
|
||||
val current = captureFromArguments(it, CaptureStatus.FOR_SUBTYPING)
|
||||
|
||||
if (current.constructor == constructor) {
|
||||
if (result == null) {
|
||||
result = SmartList()
|
||||
}
|
||||
result!!.add(current)
|
||||
|
||||
SupertypesPolicy.None
|
||||
}
|
||||
else if (current.arguments.isEmpty()) {
|
||||
SupertypesPolicy.LowerIfFlexible
|
||||
}
|
||||
else {
|
||||
SupertypesPolicy.LowerIfFlexibleWithCustomSubstitutor(TypeConstructorSubstitution.create(current).buildSubstitutor())
|
||||
}
|
||||
}
|
||||
|
||||
return result ?: emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have several paths to some interface, we should prefer pure kotlin path.
|
||||
* Example:
|
||||
*
|
||||
* class MyList : AbstractList<String>(), MutableList<String>
|
||||
*
|
||||
* We should see `String` in `get` function and others, also MyList is not subtype of MutableList<String?>
|
||||
*
|
||||
* More tests: javaAndKotlinSuperType & purelyImplementedCollection folder
|
||||
*/
|
||||
private fun selectOnlyPureKotlinSupertypes(supertypes: List<SimpleType>): List<SimpleType> {
|
||||
if (supertypes.size < 2) return supertypes
|
||||
|
||||
val allPureSupertypes = supertypes.filter { it.arguments.all { !it.type.isFlexible() } }
|
||||
if (allPureSupertypes.isNotEmpty()) {
|
||||
return allPureSupertypes
|
||||
}
|
||||
else {
|
||||
return supertypes
|
||||
}
|
||||
}
|
||||
|
||||
private fun effectiveVariance(declared: Variance, useSite: Variance): Variance? {
|
||||
if (declared == Variance.INVARIANT) return useSite
|
||||
if (useSite == Variance.INVARIANT) return declared
|
||||
|
||||
// both not INVARIANT
|
||||
if (declared == useSite) return declared
|
||||
|
||||
// composite In with Out
|
||||
return null
|
||||
}
|
||||
|
||||
private fun TypeCheckerContext.isSubtypeForSameConstructor(
|
||||
capturedSubArguments: List<TypeProjection>,
|
||||
superType: SimpleType
|
||||
): Boolean {
|
||||
val parameters = superType.constructor.parameters
|
||||
|
||||
for (index in parameters.indices) {
|
||||
val superProjection = superType.arguments[index] // todo error index
|
||||
if (superProjection.isStarProjection) continue // A<B> <: A<*>
|
||||
|
||||
val superArgumentType = superProjection.type.unwrap()
|
||||
val subArgumentType = capturedSubArguments[index].let {
|
||||
assert(it.projectionKind == Variance.INVARIANT) { "Incorrect sub argument: $it" }
|
||||
it.type.unwrap()
|
||||
}
|
||||
|
||||
val variance = effectiveVariance(parameters[index].variance, superProjection.projectionKind)
|
||||
?: return errorTypeEqualsToAnything // todo exception?
|
||||
|
||||
val correctArgument = runWithArgumentsSettings(subArgumentType) {
|
||||
when (variance) {
|
||||
Variance.INVARIANT -> equalTypes(subArgumentType, superArgumentType)
|
||||
Variance.OUT_VARIANCE -> isSubtypeOf(subArgumentType, superArgumentType)
|
||||
Variance.IN_VARIANCE -> isSubtypeOf(superArgumentType, subArgumentType)
|
||||
}
|
||||
}
|
||||
if (!correctArgument) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
object NullabilityChecker {
|
||||
|
||||
// this method checks only nullability
|
||||
fun isPossibleSubtype(context: TypeCheckerContext, subType: SimpleType, superType: SimpleType): Boolean =
|
||||
context.runIsPossibleSubtype(subType, superType)
|
||||
|
||||
private fun TypeCheckerContext.runIsPossibleSubtype(subType: SimpleType, superType: SimpleType): Boolean {
|
||||
// it makes for case String? & Any <: String
|
||||
assert(subType.isIntersectionType || subType.isSingleClassifierType) {"Not singleClassifierType superType: $superType"}
|
||||
assert(superType.isSingleClassifierType) {"Not singleClassifierType superType: $superType"}
|
||||
|
||||
// superType is actually nullable
|
||||
if (superType.isMarkedNullable) return true
|
||||
|
||||
// i.e. subType is not-nullable
|
||||
if (hasNotNullSupertype(subType, SupertypesPolicy.LowerIfFlexible)) return true
|
||||
|
||||
// i.e subType hasn't not-null supertype, but superType has
|
||||
if (hasNotNullSupertype(superType, SupertypesPolicy.UpperIfFlexible)) return false
|
||||
|
||||
// both superType and subType hasn't not-null supertype.
|
||||
|
||||
/**
|
||||
* If we still don't know, it means, that superType is not classType, for example -- type parameter.
|
||||
*
|
||||
* For captured types with lower bound this function can give to you false result. Example:
|
||||
* class A<T>, A<in Number> => \exist Q : Number <: Q. A<Q>
|
||||
* isPossibleSubtype(Number, Q) = false.
|
||||
* Such cases should be taken in to account in [NewKotlinTypeChecker.isSubtypeOf] (same for intersection types)
|
||||
*/
|
||||
|
||||
// classType cannot has special type in supertype list
|
||||
if (subType.isClassType) return false
|
||||
|
||||
return hasPathByNotMarkedNullableNodes(subType, superType.constructor)
|
||||
}
|
||||
|
||||
private fun TypeCheckerContext.hasNotNullSupertype(type: SimpleType, supertypesPolicy: SupertypesPolicy) =
|
||||
anySupertype(type, { it.isClassType && !it.isMarkedNullable }) {
|
||||
if (it.isMarkedNullable) SupertypesPolicy.None else supertypesPolicy
|
||||
}
|
||||
|
||||
private fun TypeCheckerContext.hasPathByNotMarkedNullableNodes(start: SimpleType, end: TypeConstructor) =
|
||||
anySupertype(start, { !it.isMarkedNullable && it.constructor == end }) {
|
||||
if (it.isMarkedNullable) SupertypesPolicy.None else SupertypesPolicy.LowerIfFlexible
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* ClassType means that type constructor for this type is type for real class or interface
|
||||
*/
|
||||
private val SimpleType.isClassType: Boolean get() = constructor.declarationDescriptor is ClassDescriptor
|
||||
|
||||
/**
|
||||
* SingleClassifierType is one of the following types:
|
||||
* - classType
|
||||
* - type for type parameter
|
||||
* - captured type
|
||||
*
|
||||
* Such types can contains error types in our arguments, but type constructor isn't errorTypeConstructor
|
||||
*/
|
||||
private val SimpleType.isSingleClassifierType: Boolean
|
||||
get() = !isError &&
|
||||
constructor.declarationDescriptor !is TypeAliasDescriptor &&
|
||||
(constructor.declarationDescriptor != null || this is CapturedType || this is NewCapturedType)
|
||||
|
||||
private val SimpleType.isIntersectionType: Boolean
|
||||
get() = constructor is IntersectionTypeConstructor
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.checker
|
||||
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
import java.util.*
|
||||
|
||||
open class TypeCheckerContext(val errorTypeEqualsToAnything: Boolean) {
|
||||
protected var argumentsDepth = 0
|
||||
|
||||
private var supertypesLocked = false
|
||||
private var supertypesDeque: ArrayDeque<SimpleType>? = null
|
||||
private var supertypesSet: MutableSet<SimpleType>? = null
|
||||
|
||||
open fun isSubtypeByExternalRule(subType: SimpleType, superType: SimpleType) = false
|
||||
|
||||
inline fun <T> runWithArgumentsSettings(subArgument: UnwrappedType, f: TypeCheckerContext.() -> T): T {
|
||||
if (argumentsDepth > 100) {
|
||||
error("Arguments depth is too high. Some related argument: $subArgument")
|
||||
}
|
||||
|
||||
argumentsDepth++
|
||||
val result = f()
|
||||
argumentsDepth--
|
||||
return result
|
||||
}
|
||||
|
||||
private fun initialize() {
|
||||
assert(!supertypesLocked)
|
||||
supertypesLocked = true
|
||||
|
||||
if (supertypesDeque == null) {
|
||||
supertypesDeque = ArrayDeque()
|
||||
}
|
||||
if (supertypesSet == null) {
|
||||
supertypesSet = SmartSet.create()
|
||||
}
|
||||
}
|
||||
|
||||
private fun clear() {
|
||||
supertypesDeque!!.clear()
|
||||
supertypesSet!!.clear()
|
||||
supertypesLocked = false
|
||||
}
|
||||
|
||||
internal fun anySupertype(
|
||||
start: SimpleType,
|
||||
predicate: (SimpleType) -> Boolean,
|
||||
supertypesPolicy: (SimpleType) -> SupertypesPolicy
|
||||
): Boolean {
|
||||
initialize()
|
||||
|
||||
val deque = supertypesDeque!!
|
||||
val visitedSupertypes = supertypesSet!!
|
||||
|
||||
deque.push(start)
|
||||
while (deque.isNotEmpty()) {
|
||||
if (visitedSupertypes.size > 1000) {
|
||||
error("Too many supertypes for type: $start. Supertypes = ${visitedSupertypes.joinToString()}")
|
||||
}
|
||||
val current = deque.pop()
|
||||
|
||||
if (!visitedSupertypes.add(current)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (predicate(current)) {
|
||||
clear()
|
||||
return true
|
||||
}
|
||||
|
||||
val policy = supertypesPolicy(current).check { it != SupertypesPolicy.None } ?: continue
|
||||
for (supertype in current.constructor.supertypes) deque.add(policy.transformType(supertype))
|
||||
}
|
||||
|
||||
clear()
|
||||
return false
|
||||
}
|
||||
|
||||
internal sealed class SupertypesPolicy {
|
||||
abstract fun transformType(type: KotlinType): SimpleType
|
||||
|
||||
object None : SupertypesPolicy() {
|
||||
override fun transformType(type: KotlinType) = throw UnsupportedOperationException("Should not be called")
|
||||
}
|
||||
|
||||
object UpperIfFlexible : SupertypesPolicy() {
|
||||
override fun transformType(type: KotlinType) = type.upperIfFlexible()
|
||||
}
|
||||
|
||||
object LowerIfFlexible : SupertypesPolicy() {
|
||||
override fun transformType(type: KotlinType) = type.lowerIfFlexible()
|
||||
}
|
||||
|
||||
class LowerIfFlexibleWithCustomSubstitutor(val substitutor: TypeSubstitutor): SupertypesPolicy() {
|
||||
override fun transformType(type: KotlinType) =
|
||||
substitutor.safeSubstitute(type.lowerIfFlexible(), Variance.INVARIANT).asSimpleType()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions
|
||||
import org.jetbrains.kotlin.types.checker.ErrorTypesAreEqualToAnything
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
|
||||
@@ -49,7 +50,7 @@ fun Collection<KotlinType>.singleBestRepresentative(): KotlinType? {
|
||||
other ->
|
||||
// We consider error types equal to anything here, so that intersections like
|
||||
// {Array<String>, Array<[ERROR]>} work correctly
|
||||
candidate == other || KotlinTypeChecker.ERROR_TYPES_ARE_EQUAL_TO_ANYTHING.equalTypes(candidate, other)
|
||||
candidate == other || ErrorTypesAreEqualToAnything.equalTypes(candidate, other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 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.
|
||||
@@ -31,6 +31,7 @@ private val ALWAYS_NULL: (Any?) -> Any? = { null }
|
||||
fun <T, R: Any> alwaysNull(): (T) -> R? = ALWAYS_NULL as (T) -> R?
|
||||
|
||||
val DO_NOTHING: (Any?) -> Unit = { }
|
||||
val DO_NOTHING_2: (Any?, Any?) -> Unit = { x, y -> }
|
||||
val DO_NOTHING_3: (Any?, Any?, Any?) -> Unit = { x, y, z -> }
|
||||
|
||||
fun <T> doNothing(): (T) -> Unit = DO_NOTHING
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 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.
|
||||
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.CallHandle
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.checker.StrictEqualityTypeChecker
|
||||
import org.jetbrains.kotlin.types.typeUtil.*
|
||||
import java.util.*
|
||||
|
||||
@@ -53,7 +53,7 @@ fun FuzzyType.presentationType(): KotlinType {
|
||||
for ((argument, typeParameter) in type.arguments.zip(type.constructor.parameters)) {
|
||||
if (argument.projectionKind == Variance.INVARIANT) {
|
||||
val equalToFreeParameter = freeParameters.firstOrNull {
|
||||
KotlinTypeChecker.FLEXIBLE_UNEQUAL_TO_INFLEXIBLE.equalTypes(it.defaultType, argument.type)
|
||||
StrictEqualityTypeChecker.strictEqualTypes(it.defaultType, argument.type.unwrap())
|
||||
} ?: continue
|
||||
|
||||
map[equalToFreeParameter.typeConstructor] = createProjection(typeParameter.defaultType, Variance.INVARIANT, null)
|
||||
@@ -186,7 +186,7 @@ fun TypeSubstitution.hasConflictWith(other: TypeSubstitution, freeParameters: Co
|
||||
val type = parameter.defaultType
|
||||
val substituted1 = this[type] ?: return@any false
|
||||
val substituted2 = other[type] ?: return@any false
|
||||
!KotlinTypeChecker.FLEXIBLE_UNEQUAL_TO_INFLEXIBLE.equalTypes(substituted1.type, substituted2.type) || substituted1.projectionKind != substituted2.projectionKind
|
||||
!StrictEqualityTypeChecker.strictEqualTypes(substituted1.type.unwrap(), substituted2.type.unwrap()) || substituted1.projectionKind != substituted2.projectionKind
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializ
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.checker.StrictEqualityTypeChecker
|
||||
import org.jetbrains.kotlin.types.createDynamicType
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
|
||||
@@ -30,8 +30,8 @@ object DynamicTypeDeserializer : FlexibleTypeDeserializer {
|
||||
|
||||
override fun create(proto: ProtoBuf.Type, flexibleId: String, lowerBound: SimpleType, upperBound: SimpleType): KotlinType {
|
||||
if (flexibleId != id) return ErrorUtils.createErrorType("Unexpected id: $flexibleId. ($lowerBound..$upperBound)")
|
||||
if (KotlinTypeChecker.FLEXIBLE_UNEQUAL_TO_INFLEXIBLE.equalTypes(lowerBound, lowerBound.builtIns.nothingType) &&
|
||||
KotlinTypeChecker.FLEXIBLE_UNEQUAL_TO_INFLEXIBLE.equalTypes(upperBound, upperBound.builtIns.nullableAnyType)) {
|
||||
if (StrictEqualityTypeChecker.strictEqualTypes(lowerBound, lowerBound.builtIns.nothingType) &&
|
||||
StrictEqualityTypeChecker.strictEqualTypes(upperBound, upperBound.builtIns.nullableAnyType)) {
|
||||
return createDynamicType(lowerBound.builtIns)
|
||||
}
|
||||
else {
|
||||
|
||||
Reference in New Issue
Block a user