[NI] New inference -- initial commit.
This commit is contained in:
@@ -230,14 +230,14 @@ public class ErrorUtils {
|
||||
@Nullable
|
||||
@Override
|
||||
public ClassifierDescriptor getContributedClassifier(@NotNull Name name, @NotNull LookupLocation location) {
|
||||
throw new IllegalStateException();
|
||||
throw new IllegalStateException(debugMessage+", required name: " + name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"}) // KT-9898 Impossible implement kotlin interface from java
|
||||
public Collection getContributedVariables(@NotNull Name name, @NotNull LookupLocation location) {
|
||||
throw new IllegalStateException();
|
||||
throw new IllegalStateException(debugMessage+", required name: " + name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -246,7 +246,7 @@ public class ErrorUtils {
|
||||
// method is covariantly overridden in Kotlin, but collections in Java are invariant
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public Collection getContributedFunctions(@NotNull Name name, @NotNull LookupLocation location) {
|
||||
throw new IllegalStateException();
|
||||
throw new IllegalStateException(debugMessage+", required name: " + name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -254,7 +254,7 @@ public class ErrorUtils {
|
||||
public Collection<DeclarationDescriptor> getContributedDescriptors(
|
||||
@NotNull DescriptorKindFilter kindFilter, @NotNull Function1<? super Name, Boolean> nameFilter
|
||||
) {
|
||||
throw new IllegalStateException();
|
||||
throw new IllegalStateException(debugMessage);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -18,6 +18,10 @@ package org.jetbrains.kotlin.types.checker
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import java.util.*
|
||||
import kotlin.collections.HashSet
|
||||
|
||||
fun intersectWrappedTypes(types: Collection<KotlinType>) = intersectTypes(types.map { it.unwrap() })
|
||||
|
||||
fun intersectTypes(types: List<UnwrappedType>): UnwrappedType {
|
||||
when (types.size) {
|
||||
@@ -58,7 +62,92 @@ fun intersectTypes(types: List<UnwrappedType>): UnwrappedType {
|
||||
// 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())
|
||||
return TypeIntersector.intersectTypes(types)
|
||||
}
|
||||
|
||||
object TypeIntersector {
|
||||
|
||||
internal fun intersectTypes(types: List<SimpleType>): SimpleType {
|
||||
assert(types.size > 1) {
|
||||
"Size should be at least 2, but it is ${types.size}"
|
||||
}
|
||||
val inputTypes = ArrayList<SimpleType>()
|
||||
for (type in types) {
|
||||
if (type.constructor is IntersectionTypeConstructor) {
|
||||
inputTypes.addAll(type.constructor.supertypes.map {
|
||||
it.upperIfFlexible().let { if (type.isMarkedNullable) it.makeNullableAsSpecified(true) else it }
|
||||
})
|
||||
}
|
||||
else {
|
||||
inputTypes.add(type)
|
||||
}
|
||||
}
|
||||
val resultNullability = inputTypes.fold(ResultNullability.START, ResultNullability::combine)
|
||||
/**
|
||||
* resultNullability. Value description:
|
||||
* ACCEPT_NULL means that all types marked nullable
|
||||
* NOT_NULL means that there is one type which is subtype of Any => all types can be marked not nullable
|
||||
* UNKNOWN means, that we do not know, i.e. more precisely, all singleClassifier types marked nullable if any,
|
||||
* and other types is captured types or type parameters without not-null upper bound. Example: `String? & T` such types we should leave as is.
|
||||
*/
|
||||
val correctNullability = inputTypes.mapTo(HashSet()) {
|
||||
if (resultNullability == ResultNullability.NOT_NULL) it.makeNullableAsSpecified(false) else it
|
||||
}
|
||||
|
||||
return intersectTypesWithoutIntersectionType(correctNullability)
|
||||
}
|
||||
|
||||
// nullability here is correct
|
||||
private fun intersectTypesWithoutIntersectionType(inputTypes: Set<SimpleType>): SimpleType {
|
||||
// Any and Nothing should leave
|
||||
// Note that duplicates should be dropped because we have Set here.
|
||||
val filteredSupertypes = inputTypes.filterNot { upper ->
|
||||
inputTypes.any { upper != it && NewKotlinTypeChecker.isSubtypeOf(it, upper) }
|
||||
}
|
||||
|
||||
assert(filteredSupertypes.isNotEmpty()) {
|
||||
"This collections cannot be empty! input types: ${inputTypes.joinToString()}"
|
||||
}
|
||||
|
||||
if (filteredSupertypes.size < 2) return filteredSupertypes.single()
|
||||
|
||||
val constructor = IntersectionTypeConstructor(inputTypes)
|
||||
return KotlinTypeFactory.simpleType(Annotations.EMPTY, constructor, listOf(), false, constructor.createScopeForKotlinType())
|
||||
}
|
||||
|
||||
/**
|
||||
* Let T is type parameter with upper bound Any?. resultNullability(String? & T) = UNKNOWN => String? & T
|
||||
*/
|
||||
private enum class ResultNullability {
|
||||
START {
|
||||
override fun combine(nextType: UnwrappedType) = nextType.resultNullability
|
||||
},
|
||||
ACCEPT_NULL {
|
||||
override fun combine(nextType: UnwrappedType) = nextType.resultNullability
|
||||
},
|
||||
// example: type parameter without not-null supertype
|
||||
UNKNOWN {
|
||||
override fun combine(nextType: UnwrappedType) =
|
||||
nextType.resultNullability.let {
|
||||
if (it == ACCEPT_NULL) this else it
|
||||
}
|
||||
},
|
||||
NOT_NULL {
|
||||
override fun combine(nextType: UnwrappedType) = this
|
||||
};
|
||||
|
||||
abstract fun combine(nextType: UnwrappedType): ResultNullability
|
||||
|
||||
protected val UnwrappedType.resultNullability: ResultNullability
|
||||
get() {
|
||||
if (isMarkedNullable) return ACCEPT_NULL
|
||||
|
||||
if (NullabilityChecker.isSubtypeOfAny(this)) {
|
||||
return NOT_NULL
|
||||
}
|
||||
else {
|
||||
return UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,13 +26,52 @@ import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
import org.jetbrains.kotlin.utils.DO_NOTHING_2
|
||||
|
||||
// if input type is capturedType, then we approximate it to UpperBound
|
||||
// null means that type should be leaved as is
|
||||
fun prepareArgumentTypeRegardingCaptureTypes(argumentType: UnwrappedType): UnwrappedType? {
|
||||
val simpleType = NewKotlinTypeChecker.transformToNewType(argumentType.lowerIfFlexible())
|
||||
if (simpleType.constructor is IntersectionTypeConstructor){
|
||||
var changed = false
|
||||
val preparedSuperTypes = simpleType.constructor.supertypes.map {
|
||||
prepareArgumentTypeRegardingCaptureTypes(it.unwrap())?.apply { changed = true } ?: it.unwrap()
|
||||
}
|
||||
if (!changed) return null
|
||||
return intersectTypes(preparedSuperTypes).makeNullableAsSpecified(simpleType.isMarkedNullable)
|
||||
}
|
||||
if (simpleType is NewCapturedType) {
|
||||
// todo may be we should respect flexible capture types also...
|
||||
return simpleType.constructor.supertypes.takeIf { it.isNotEmpty() }?.let(::intersectTypes) ?: argumentType.builtIns.nullableAnyType
|
||||
}
|
||||
return captureFromExpression(simpleType)
|
||||
}
|
||||
|
||||
fun captureFromExpression(type: UnwrappedType): UnwrappedType? = when (type) {
|
||||
is SimpleType -> captureFromExpression(type)
|
||||
// i.e. if there is nothing to capture -- no changes, if there is something -- use lowerBound as base type
|
||||
is FlexibleType -> captureFromExpression(type.lowerBound)
|
||||
}
|
||||
|
||||
fun captureFromExpression(type: SimpleType): UnwrappedType? {
|
||||
val typeConstructor = type.constructor
|
||||
if (typeConstructor is IntersectionTypeConstructor) {
|
||||
var changed = false
|
||||
val capturedSupertypes = typeConstructor.supertypes.map {
|
||||
captureFromExpression(it.unwrap())?.apply { changed = true } ?: it.unwrap()
|
||||
}
|
||||
if (!changed) return null
|
||||
return intersectTypes(capturedSupertypes).makeNullableAsSpecified(type.isMarkedNullable)
|
||||
}
|
||||
return captureFromArguments(type, CaptureStatus.FROM_EXPRESSION)
|
||||
}
|
||||
|
||||
// this function suppose that input type is simple classifier type
|
||||
fun captureFromArguments(
|
||||
type: SimpleType,
|
||||
status: CaptureStatus,
|
||||
acceptNewCapturedType: ((argumentIndex: Int, NewCapturedType) -> Unit) = DO_NOTHING_2
|
||||
): SimpleType {
|
||||
): SimpleType? {
|
||||
val arguments = type.arguments
|
||||
if (arguments.all { it.projectionKind == Variance.INVARIANT }) return type
|
||||
if (arguments.all { it.projectionKind == Variance.INVARIANT }) return null
|
||||
|
||||
val newArguments = arguments.map {
|
||||
projection ->
|
||||
@@ -68,6 +107,7 @@ fun captureFromArguments(
|
||||
|
||||
enum class CaptureStatus {
|
||||
FOR_SUBTYPING,
|
||||
FOR_INCORPORATION,
|
||||
FROM_EXPRESSION
|
||||
}
|
||||
|
||||
@@ -86,7 +126,7 @@ class NewCapturedType(
|
||||
override val annotations: Annotations = Annotations.EMPTY,
|
||||
override val isMarkedNullable: Boolean = false
|
||||
): SimpleType() {
|
||||
constructor(captureStatus: CaptureStatus, lowerType: UnwrappedType?, projection: TypeProjection) :
|
||||
internal constructor(captureStatus: CaptureStatus, lowerType: UnwrappedType?, projection: TypeProjection) :
|
||||
this(captureStatus, NewCapturedTypeConstructor(projection), lowerType)
|
||||
|
||||
override val arguments: List<TypeProjection> get() = listOf()
|
||||
|
||||
@@ -250,7 +250,7 @@ object NewKotlinTypeChecker : KotlinTypeChecker {
|
||||
var result: MutableList<SimpleType>? = null
|
||||
|
||||
anySupertype(baseType, { false }) {
|
||||
val current = captureFromArguments(it, CaptureStatus.FOR_SUBTYPING)
|
||||
val current = captureFromArguments(it, CaptureStatus.FOR_SUBTYPING) ?: it
|
||||
|
||||
when {
|
||||
areEqualTypeConstructors(current.constructor, constructor) -> {
|
||||
@@ -290,7 +290,7 @@ object NewKotlinTypeChecker : KotlinTypeChecker {
|
||||
return if (allPureSupertypes.isNotEmpty()) allPureSupertypes else supertypes
|
||||
}
|
||||
|
||||
private fun effectiveVariance(declared: Variance, useSite: Variance): Variance? {
|
||||
fun effectiveVariance(declared: Variance, useSite: Variance): Variance? {
|
||||
if (declared == Variance.INVARIANT) return useSite
|
||||
if (useSite == Variance.INVARIANT) return declared
|
||||
|
||||
@@ -341,12 +341,15 @@ object NullabilityChecker {
|
||||
fun isPossibleSubtype(context: TypeCheckerContext, subType: SimpleType, superType: SimpleType): Boolean =
|
||||
context.runIsPossibleSubtype(subType, superType)
|
||||
|
||||
fun isSubtypeOfAny(type: UnwrappedType): Boolean =
|
||||
TypeCheckerContext(false).hasNotNullSupertype(type.lowerIfFlexible(), SupertypesPolicy.LowerIfFlexible)
|
||||
|
||||
private fun TypeCheckerContext.runIsPossibleSubtype(subType: SimpleType, superType: SimpleType): Boolean {
|
||||
// it makes for case String? & Any <: String
|
||||
assert(subType.isIntersectionType || subType.isSingleClassifierType || subType.isAllowedTypeVariable) {
|
||||
"Not singleClassifierType superType: $superType"
|
||||
}
|
||||
assert(superType.isSingleClassifierType || subType.isAllowedTypeVariable) {
|
||||
assert(superType.isSingleClassifierType || superType.isAllowedTypeVariable) {
|
||||
"Not singleClassifierType superType: $superType"
|
||||
}
|
||||
|
||||
@@ -391,7 +394,7 @@ object NullabilityChecker {
|
||||
/**
|
||||
* 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
|
||||
val SimpleType.isClassType: Boolean get() = constructor.declarationDescriptor is ClassDescriptor
|
||||
|
||||
/**
|
||||
* SingleClassifierType is one of the following types:
|
||||
@@ -401,10 +404,10 @@ private val SimpleType.isClassType: Boolean get() = constructor.declarationDescr
|
||||
*
|
||||
* Such types can contains error types in our arguments, but type constructor isn't errorTypeConstructor
|
||||
*/
|
||||
private val SimpleType.isSingleClassifierType: Boolean
|
||||
val SimpleType.isSingleClassifierType: Boolean
|
||||
get() = !isError &&
|
||||
constructor.declarationDescriptor !is TypeAliasDescriptor &&
|
||||
(constructor.declarationDescriptor != null || this is CapturedType || this is NewCapturedType)
|
||||
|
||||
private val SimpleType.isIntersectionType: Boolean
|
||||
val SimpleType.isIntersectionType: Boolean
|
||||
get() = constructor is IntersectionTypeConstructor
|
||||
@@ -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.
|
||||
@@ -91,7 +91,7 @@ public class DFS {
|
||||
return topologicalOrder(nodes, neighbors, new VisitedWithSet<N>());
|
||||
}
|
||||
|
||||
private static <N> void doDfs(@NotNull N current, @NotNull Neighbors<N> neighbors, @NotNull Visited<N> visited, @NotNull NodeHandler<N, ?> handler) {
|
||||
public static <N> void doDfs(@NotNull N current, @NotNull Neighbors<N> neighbors, @NotNull Visited<N> visited, @NotNull NodeHandler<N, ?> handler) {
|
||||
if (!visited.checkAndMarkVisited(current)) return;
|
||||
if (!handler.beforeChildren(current)) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user