From 64f0688b71eb6d4a596af1c53075a67d21bb3a54 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Fri, 24 Nov 2017 15:04:10 +0300 Subject: [PATCH] [NI] Introduce `DefinitelyNotNullType` as type for `T!!` or `{T & Any}` --- .../components/NewTypeSubstitutor.kt | 12 ++-- .../TypeCheckerContextForConstraintSystem.kt | 27 ++------ .../kotlin/types/TypeApproximator.kt | 26 ++++++- .../org/jetbrains/kotlin/types/KotlinType.kt | 2 +- .../kotlin/types/KotlinTypeFactory.kt | 2 +- .../jetbrains/kotlin/types/SpecialTypes.kt | 67 ++++++++++++++++++- .../org/jetbrains/kotlin/types/TypeUtils.java | 7 +- .../kotlin/types/checker/IntersectionType.kt | 20 +++--- .../kotlin/types/checker/NewCapturedType.kt | 4 +- .../types/checker/NewKotlinTypeChecker.kt | 19 ++++-- 10 files changed, 138 insertions(+), 48 deletions(-) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt index 2aae3d3837d..be5f583d103 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt @@ -74,16 +74,17 @@ interface NewTypeSubstitutor { if (typeConstructor is NewCapturedTypeConstructor) { if (!runCapturedChecks) return null - assert(type is NewCapturedType) { // KT-16147 + assert(type is NewCapturedType || (type is DefinitelyNotNullType && type.original is NewCapturedType)) { // KT-16147 "Type is inconsistent -- somewhere we create type with typeConstructor = $typeConstructor " + "and class: ${type::class.java.canonicalName}. type.toString() = $type" } - val lower = (type as NewCapturedType).lowerType?.let { substitute(it, keepAnnotation, runCapturedChecks = false) } + val capturedType = if (type is DefinitelyNotNullType) type.original as NewCapturedType else type as NewCapturedType + val lower = capturedType.lowerType?.let { substitute(it, keepAnnotation, runCapturedChecks = false) } if (lower != null) throw IllegalStateException("Illegal type substitutor: $this, " + "because for captured type '$type' lower type approximation should be null, but it is: '$lower'," + - "original lower type: '${type.lowerType}") + "original lower type: '${capturedType.lowerType}") - type.constructor.supertypes.forEach { supertype -> + typeConstructor.supertypes.forEach { supertype -> substitute(supertype, keepAnnotation, runCapturedChecks = false)?.let { throw IllegalStateException("Illegal type substitutor: $this, " + "because for captured type '$type' supertype approximation should be null, but it is: '$supertype'," + @@ -111,6 +112,9 @@ interface NewTypeSubstitutor { if (type.isMarkedNullable) { replacement = replacement.makeNullableAsSpecified(true) } + if (type.isDefinitelyNotNullType) { + replacement = replacement.makeDefinitelyNotNullOrNotNull() + } return replacement } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerContextForConstraintSystem.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerContextForConstraintSystem.kt index 2b50f6c1533..6d92c9a61b2 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerContextForConstraintSystem.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerContextForConstraintSystem.kt @@ -139,17 +139,9 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT } // Foo <: T! -- (Foo & Any .. Foo) <: T - // T <: T! -- (T..T?) <: T - // we can't use constraint (T & Any .. T) in the latter, because non-platform type with type variables is incorrect private fun addLowerConstraintWithSimpleSubtype(typeVariable: FlexibleType, subType: SimpleType) { assertFlexibleTypeVariable(typeVariable) - - val constraintType = if (isMyTypeVariable(subType)) - KotlinTypeFactory.flexibleType(subType.makeNullableAsSpecified(false), subType.makeNullableAsSpecified(true)) - else - KotlinTypeFactory.flexibleType(subType.definitelyNotNull(), subType) - - addLowerConstraint(typeVariable.constructor, constraintType) + addLowerConstraint(typeVariable.constructor, KotlinTypeFactory.flexibleType(subType.makeSimpleTypeDefinitelyNotNullOrNotNull(), subType)) } // (Foo..Bar) <: T! -- (Foo & Any .. Bar) <: T @@ -159,28 +151,18 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT val lowerBound = subType.lowerBound val upperBound = subType.upperBound - if (isMyTypeVariable(lowerBound) || isMyTypeVariable(upperBound)) { - assertFlexibleTypeVariable(subType) - // This means that we are trying to add constraint like T! <: A! - addLowerConstraint(typeVariable.constructor, subType) - return - } - - addLowerConstraint(typeVariable.constructor, KotlinTypeFactory.flexibleType(lowerBound.definitelyNotNull(), upperBound)) + addLowerConstraint(typeVariable.constructor, KotlinTypeFactory.flexibleType(lowerBound.makeSimpleTypeDefinitelyNotNullOrNotNull(), upperBound)) } // Foo <: T or // Foo <: T? -- Foo & Any <: T private fun addLowerConstraintForSimpleType(typeVariable: SimpleType, subType: UnwrappedType) { if (typeVariable.isMarkedNullable) - addLowerConstraint(typeVariable.constructor, subType.definitelyNotNull()) + addLowerConstraint(typeVariable.constructor, subType.makeDefinitelyNotNullOrNotNull()) else addLowerConstraint(typeVariable.constructor, subType) } - private fun UnwrappedType.definitelyNotNull(): UnwrappedType = intersectTypes(listOf(this, this.builtIns.anyType)) - private fun SimpleType.definitelyNotNull(): SimpleType = intersectTypes(listOf(this, this.builtIns.anyType)) - private fun assertFlexibleTypeVariable(typeVariable: FlexibleType) { assert(typeVariable.lowerBound.constructor == typeVariable.upperBound.constructor) { "Flexible type variable ($typeVariable) should have bounds with the same type constructor, i.e. (T..T?)" @@ -196,6 +178,9 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT @Suppress("NAME_SHADOWING") val typeVariable = typeVariable.lowerIfFlexible() + @Suppress("NAME_SHADOWING") + val superType = if (typeVariable is DefinitelyNotNullType) superType.makeNullableAsSpecified(true) else superType + addUpperConstraint(typeVariable.constructor, superType) if (typeVariable.isMarkedNullable) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt index 39ac42353ca..e16c903f300 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt @@ -39,6 +39,7 @@ open class TypeApproximatorConfiguration { open val dynamic get() = false // DynamicType open val rawType get() = false // RawTypeImpl open val errorType get() = false + open val definitelyNotNullType get() = true open val intersection: IntersectionStrategy = TO_COMMON_SUPERTYPE open val typeVariable: (TypeVariableTypeConstructor) -> Boolean = { false } @@ -61,6 +62,7 @@ open class TypeApproximatorConfiguration { object PublicDeclaration : AllFlexibleSameValue() { override val allFlexible get() = true override val errorType get() = true + override val definitelyNotNullType get() = false } abstract class AbstractCapturedTypesApproximation(val approximatedCapturedStatus: CaptureStatus): TypeApproximatorConfiguration.AllFlexibleSameValue() { @@ -262,6 +264,10 @@ class TypeApproximator { return approximateParametrizedType(type, conf, toSuper, depth + 1) } + if (type is DefinitelyNotNullType) { + return approximateDefinitelyNotNullType(type, conf, toSuper, depth) + } + val typeConstructor = type.constructor if (typeConstructor is NewCapturedTypeConstructor) { @@ -283,6 +289,24 @@ class TypeApproximator { return null // simple classifier type } + private fun approximateDefinitelyNotNullType( + type: DefinitelyNotNullType, + conf: TypeApproximatorConfiguration, + toSuper: Boolean, + depth: Int + ): UnwrappedType? { + val approximatedOriginalType = approximateTo(type.original, conf, toSuper, depth) + return if (conf.definitelyNotNullType) { + approximatedOriginalType?.makeDefinitelyNotNullOrNotNull() + } + else { + if (toSuper) + (approximatedOriginalType ?: type.original).makeNullableAsSpecified(false) + else + type.defaultResult(toSuper) + } + } + private fun isApproximateDirectionToSuper(effectiveVariance: Variance, toSuper: Boolean) = when (effectiveVariance) { Variance.OUT_VARIANCE -> toSuper @@ -364,7 +388,7 @@ class TypeApproximator { * Note that for case Inv we will chose Inv, because it is more informative then Inv. * May be we should do the same for deeper types, but not now. */ - if (argumentType is NewCapturedType) { + if (argumentType.constructor is NewCapturedTypeConstructor) { val subType = approximateToSubType(argumentType, conf, depth) ?: continue@loop if (!subType.isTrivialSub()) { newArguments[index] = TypeProjectionImpl(Variance.IN_VARIANCE, subType) diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/KotlinType.kt b/core/descriptors/src/org/jetbrains/kotlin/types/KotlinType.kt index f50fc71c6b8..3dcab44929b 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/KotlinType.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/KotlinType.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 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. diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeFactory.kt b/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeFactory.kt index 687eb6acf33..94171d072f1 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeFactory.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeFactory.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 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. diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt index f668c7a261b..f101d274f44 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 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. @@ -16,9 +16,13 @@ package org.jetbrains.kotlin.types +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.types.checker.NewCapturedType +import org.jetbrains.kotlin.types.checker.NewTypeVariableConstructor +import org.jetbrains.kotlin.types.checker.NullabilityChecker abstract class DelegatingSimpleType : SimpleType() { protected abstract val delegate: SimpleType @@ -55,3 +59,64 @@ class LazyWrappedType(storageManager: StorageManager, computation: () -> KotlinT override fun isComputed(): Boolean = lazyValue.isComputed() } + +class DefinitelyNotNullType private constructor(val original: SimpleType) : DelegatingSimpleType(), CustomTypeVariable { + companion object { + internal fun makeDefinitelyNotNull(type: UnwrappedType): DefinitelyNotNullType? { + return when { + type is DefinitelyNotNullType -> type + + makesSenseToBeDefinitelyNotNull(type) -> { + if (type is FlexibleType) { + assert(type.lowerBound.constructor == type.upperBound.constructor) { + "DefinitelyNotNullType for flexible type ($type) can be created only from type variable with the same constructor for bounds" + } + } + + + DefinitelyNotNullType(type.lowerIfFlexible()) + } + + else -> null + } + } + + fun makesSenseToBeDefinitelyNotNull(type: UnwrappedType): Boolean = + canHaveUndefinedNullability(type) && !NullabilityChecker.isSubtypeOfAny(type) + + private fun canHaveUndefinedNullability(type: UnwrappedType): Boolean = + type.constructor is NewTypeVariableConstructor || + type.constructor.declarationDescriptor is TypeParameterDescriptor || + type is NewCapturedType + } + + override val delegate: SimpleType + get() = original + + override val isMarkedNullable: Boolean + get() = false + + override val isTypeVariable: Boolean + get() = delegate.constructor is NewTypeVariableConstructor || + delegate.constructor.declarationDescriptor is TypeParameterDescriptor + + override fun substitutionResult(replacement: KotlinType): KotlinType = + replacement.unwrap().makeDefinitelyNotNullOrNotNull() + + override fun replaceAnnotations(newAnnotations: Annotations): DefinitelyNotNullType = + DefinitelyNotNullType(delegate.replaceAnnotations(newAnnotations)) + + override fun makeNullableAsSpecified(newNullability: Boolean): SimpleType = + if (newNullability) delegate.makeNullableAsSpecified(newNullability) else this + + override fun toString(): String = "$delegate!!" +} + +val KotlinType.isDefinitelyNotNullType: Boolean + get() = unwrap() is DefinitelyNotNullType + +fun SimpleType.makeSimpleTypeDefinitelyNotNullOrNotNull(): SimpleType = + DefinitelyNotNullType.makeDefinitelyNotNull(this) ?: makeNullableAsSpecified(false) + +fun UnwrappedType.makeDefinitelyNotNullOrNotNull(): UnwrappedType = + DefinitelyNotNullType.makeDefinitelyNotNull(this) ?: makeNullableAsSpecified(false) diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java index 2c030e790ab..6d624f161d2 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 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. @@ -395,6 +395,11 @@ public class TypeUtils { return true; } + if (unwrappedType instanceof DefinitelyNotNullType && + contains(((DefinitelyNotNullType) unwrappedType).getOriginal(), isSpecialType)) { + return true; + } + TypeConstructor typeConstructor = type.getConstructor(); if (typeConstructor instanceof IntersectionTypeConstructor) { IntersectionTypeConstructor intersectionTypeConstructor = (IntersectionTypeConstructor) typeConstructor; diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt index 4a58ae8a760..0195eb45f01 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt @@ -86,12 +86,15 @@ object TypeIntersector { /** * 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 + * + * NOT_NULL means that there is one type which is subtype of Any => all types can be made definitely not null, + * making types definitely not null (not just not null) makes sense when we have intersection of type parameters like {T!! & S} + * * 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(LinkedHashSet()) { - if (resultNullability == ResultNullability.NOT_NULL) it.makeNullableAsSpecified(false) else it + if (resultNullability == ResultNullability.NOT_NULL) it.makeSimpleTypeDefinitelyNotNullOrNotNull() else it } return intersectTypesWithoutIntersectionType(correctNullability) @@ -154,15 +157,10 @@ object TypeIntersector { 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 - } + get() = when { + isMarkedNullable -> ACCEPT_NULL + NullabilityChecker.isSubtypeOfAny(this) -> NOT_NULL + else -> UNKNOWN } } } diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt index 0d61a3785fa..23265171942 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 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. @@ -39,7 +39,7 @@ fun prepareArgumentTypeRegardingCaptureTypes(argumentType: UnwrappedType): Unwra return intersectTypes(preparedSuperTypes).makeNullableAsSpecified(simpleType.isMarkedNullable) } if (simpleType is NewCapturedType) { - // todo may be we should respect flexible capture types also... + // todo may be we should respect flexible/definitelyNotNull captured types also... return simpleType.constructor.supertypes.takeIf { it.isNotEmpty() }?.let { intersectTypes(it).makeNullableAsSpecified(simpleType.isMarkedNullable) } ?: argumentType.builtIns.nullableAnyType diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt index 6c0053da5cc..3691d5070e5 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 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. @@ -53,6 +53,7 @@ object StrictEqualityTypeChecker { fun strictEqualTypes(a: SimpleType, b: SimpleType): Boolean { if (a.isMarkedNullable != b.isMarkedNullable + || a.isDefinitelyNotNullType != b.isDefinitelyNotNullType || a.constructor != b.constructor || a.arguments.size != b.arguments.size ) { @@ -111,7 +112,9 @@ object NewKotlinTypeChecker : KotlinTypeChecker { lowerIfFlexible().isMarkedNullable != upperIfFlexible().isMarkedNullable private fun isCommonDenotableType(type: KotlinType) = - type.constructor.isDenotable && !type.isDynamic() && type.lowerIfFlexible().constructor == type.upperIfFlexible().constructor + type.constructor.isDenotable && + !type.isDynamic() && !type.isDefinitelyNotNullType && + type.lowerIfFlexible().constructor == type.upperIfFlexible().constructor fun TypeCheckerContext.isSubtypeOf(subType: UnwrappedType, superType: UnwrappedType): Boolean { if (subType === superType) return true @@ -407,13 +410,19 @@ object NullabilityChecker { // superType is actually nullable if (superType.isMarkedNullable) return true + // i.e. subType is definitely not null + if (subType.isDefinitelyNotNullType) return true + // i.e. subType is not-nullable if (hasNotNullSupertype(subType, SupertypesPolicy.LowerIfFlexible)) return true + // i.e. subType hasn't not-null supertype and isn't definitely not-null, but superType is definitely not-null + if (superType.isDefinitelyNotNullType) return false + // 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. + // both superType and subType hasn't not-null supertype and are not definitely not null. /** * If we still don't know, it means, that superType is not classType, for example -- type parameter. @@ -431,7 +440,7 @@ object NullabilityChecker { } private fun TypeCheckerContext.hasNotNullSupertype(type: SimpleType, supertypesPolicy: SupertypesPolicy) = - anySupertype(type, { it.isClassType && !it.isMarkedNullable }) { + anySupertype(type, { (it.isClassType && !it.isMarkedNullable) || it.isDefinitelyNotNullType }) { if (it.isMarkedNullable) SupertypesPolicy.None else supertypesPolicy } @@ -464,7 +473,7 @@ val SimpleType.isClassType: Boolean get() = constructor.declarationDescriptor is val SimpleType.isSingleClassifierType: Boolean get() = !isError && constructor.declarationDescriptor !is TypeAliasDescriptor && - (constructor.declarationDescriptor != null || this is CapturedType || this is NewCapturedType) + (constructor.declarationDescriptor != null || this is CapturedType || this is NewCapturedType || this is DefinitelyNotNullType) val SimpleType.isIntersectionType: Boolean get() = constructor is IntersectionTypeConstructor