Introduce new type checker.

This commit is contained in:
Stanislav Erokhin
2016-06-29 19:26:30 +03:00
parent d4d98c87ee
commit 6556cde329
20 changed files with 857 additions and 43 deletions
@@ -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 {
@@ -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
}
}
@@ -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 {