Make type-system KotlinType-independent

- Port NewKotlinTypeChecker.equalTypes
- Decouple new-type transform from isSubtypeOf
- Port isSubtypeForSameConstructor
- Port checkSubtypeForSpecialCases
- Port isSubTypeOf without internals
- Port anySupertype
- Port isSubtypeForSameConstructor, findCorrespondingSupertypes
- Port isSubtypeOfForSingleClassifierType
- Port NullabilityChecker
- Reorder checks for performance
This commit is contained in:
Simon Ogorodnik
2019-01-22 20:54:01 +03:00
parent d210e8bad0
commit 4882627712
25 changed files with 1049 additions and 582 deletions
@@ -820,14 +820,18 @@ public abstract class KotlinBuiltIns {
}
private static boolean isConstructedFromGivenClass(@NotNull KotlinType type, @NotNull FqNameUnsafe fqName) {
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
return descriptor instanceof ClassDescriptor && classFqNameEquals(descriptor, fqName);
return isTypeConstructorForGivenClass(type.getConstructor(), fqName);
}
public static boolean isConstructedFromGivenClass(@NotNull KotlinType type, @NotNull FqName fqName) {
return isConstructedFromGivenClass(type, fqName.toUnsafe());
}
public static boolean isTypeConstructorForGivenClass(@NotNull TypeConstructor typeConstructor, @NotNull FqNameUnsafe fqName) {
ClassifierDescriptor descriptor = typeConstructor.getDeclarationDescriptor();
return descriptor instanceof ClassDescriptor && classFqNameEquals(descriptor, fqName);
}
private static boolean classFqNameEquals(@NotNull ClassifierDescriptor descriptor, @NotNull FqNameUnsafe fqName) {
// Quick check to avoid creation of full FqName instance
return descriptor.getName().equals(fqName.shortName()) &&
@@ -20,11 +20,11 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeConstructor;
import org.jetbrains.kotlin.types.Variance;
import org.jetbrains.kotlin.types.model.TypeParameterIM;
import org.jetbrains.kotlin.types.model.TypeParameterMarker;
import java.util.List;
public interface TypeParameterDescriptor extends ClassifierDescriptor, TypeParameterIM {
public interface TypeParameterDescriptor extends ClassifierDescriptor, TypeParameterMarker {
boolean isReified();
@NotNull
@@ -25,7 +25,7 @@ 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.model.CapturedTypeIM
import org.jetbrains.kotlin.types.model.CapturedTypeMarker
import org.jetbrains.kotlin.types.typeUtil.builtIns
interface CapturedTypeConstructor : TypeConstructor {
@@ -69,7 +69,7 @@ class CapturedType(
override val constructor: CapturedTypeConstructor = CapturedTypeConstructorImpl(typeProjection),
override val isMarkedNullable: Boolean = false,
override val annotations: Annotations = Annotations.EMPTY
) : SimpleType(), SubtypingRepresentatives, CapturedTypeIM {
) : SimpleType(), SubtypingRepresentatives, CapturedTypeMarker {
override val arguments: List<TypeProjection>
get() = listOf()
@@ -22,9 +22,10 @@ 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.StrictEqualityTypeChecker
import org.jetbrains.kotlin.types.model.FlexibleTypeIM
import org.jetbrains.kotlin.types.model.KotlinTypeIM
import org.jetbrains.kotlin.types.model.SimpleTypeIM
import org.jetbrains.kotlin.types.model.FlexibleTypeMarker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.model.TypeArgumentListMarker
/**
* [KotlinType] has only two direct subclasses: [WrappedType] and [UnwrappedType].
@@ -43,7 +44,7 @@ import org.jetbrains.kotlin.types.model.SimpleTypeIM
*
* For type creation see [KotlinTypeFactory].
*/
sealed class KotlinType : Annotated, KotlinTypeIM {
sealed class KotlinType : Annotated, KotlinTypeMarker {
abstract val constructor: TypeConstructor
abstract val arguments: List<TypeProjection>
@@ -122,7 +123,7 @@ sealed class UnwrappedType: KotlinType() {
* then all your types are simple.
* Or more precisely, all instances are subclasses of [SimpleType] or [WrappedType] (which contains [SimpleType] inside).
*/
abstract class SimpleType : UnwrappedType(), SimpleTypeIM {
abstract class SimpleType : UnwrappedType(), SimpleTypeMarker, TypeArgumentListMarker {
abstract override fun replaceAnnotations(newAnnotations: Annotations): SimpleType
abstract override fun makeNullableAsSpecified(newNullability: Boolean): SimpleType
@@ -141,7 +142,7 @@ abstract class SimpleType : UnwrappedType(), SimpleTypeIM {
// lowerBound is a subtype of upperBound
abstract class FlexibleType(val lowerBound: SimpleType, val upperBound: SimpleType) :
UnwrappedType(), SubtypingRepresentatives, FlexibleTypeIM {
UnwrappedType(), SubtypingRepresentatives, FlexibleTypeMarker {
abstract val delegate: SimpleType
@@ -16,6 +16,6 @@
package org.jetbrains.kotlin.types
import org.jetbrains.kotlin.types.model.RawTypeIM
import org.jetbrains.kotlin.types.model.RawTypeMarker
interface RawType : RawTypeIM
interface RawType : RawTypeMarker
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.checker.NewTypeVariableConstructor
import org.jetbrains.kotlin.types.checker.NullabilityChecker
import org.jetbrains.kotlin.types.model.DefinitelyNotNullTypeIM
import org.jetbrains.kotlin.types.model.DefinitelyNotNullTypeMarker
import org.jetbrains.kotlin.types.typeUtil.canHaveUndefinedNullability
abstract class DelegatingSimpleType : SimpleType() {
@@ -62,7 +62,7 @@ class LazyWrappedType(storageManager: StorageManager, computation: () -> KotlinT
}
class DefinitelyNotNullType private constructor(val original: SimpleType) : DelegatingSimpleType(), CustomTypeVariable,
DefinitelyNotNullTypeIM {
DefinitelyNotNullTypeMarker {
companion object {
internal fun makeDefinitelyNotNull(type: UnwrappedType): DefinitelyNotNullType? {
@@ -22,12 +22,12 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.types.model.TypeConstructorIM;
import org.jetbrains.kotlin.types.model.TypeConstructorMarker;
import java.util.Collection;
import java.util.List;
public interface TypeConstructor extends TypeConstructorIM {
public interface TypeConstructor extends TypeConstructorMarker {
/**
* It may differ from ClassDescriptor.declaredParameters if the class is inner, in such case
* it also contains additional parameters from outer declarations.
@@ -17,9 +17,9 @@
package org.jetbrains.kotlin.types;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.types.model.TypeArgumentIM;
import org.jetbrains.kotlin.types.model.TypeArgumentMarker;
public interface TypeProjection extends TypeArgumentIM {
public interface TypeProjection extends TypeArgumentMarker {
@NotNull
Variance getProjectionKind();
@@ -0,0 +1,69 @@
/*
* 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.types.checker.NewKotlinTypeChecker.transformToNewType
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
open class ClassicTypeCheckerContext(val errorTypeEqualsToAnything: Boolean, val allowedTypeVariable: Boolean = true) : ClassicTypeSystemContext, AbstractTypeCheckerContext() {
override fun intersectTypes(types: List<KotlinTypeMarker>): KotlinTypeMarker {
@Suppress("UNCHECKED_CAST")
return org.jetbrains.kotlin.types.checker.intersectTypes(types as List<UnwrappedType>)
}
override fun prepareType(type: KotlinTypeMarker): KotlinTypeMarker {
return super.prepareType(transformToNewType((type as KotlinType).unwrap()))
}
override val isErrorTypeEqualsToAnything: Boolean
get() = errorTypeEqualsToAnything
override fun areEqualTypeConstructors(a: TypeConstructorMarker, b: TypeConstructorMarker): Boolean {
require(a is TypeConstructor, a::errorMessage)
require(b is TypeConstructor, b::errorMessage)
return areEqualTypeConstructors(a, b)
}
open fun areEqualTypeConstructors(a: TypeConstructor, b: TypeConstructor): Boolean {
return a == b
}
override fun substitutionSupertypePolicy(type: SimpleTypeMarker): SupertypesPolicy.DoCustomTransform {
require(type is SimpleType, type::errorMessage)
val substitutor = TypeConstructorSubstitution.create(type).buildSubstitutor()
return object : SupertypesPolicy.DoCustomTransform() {
override fun transformType(context: AbstractTypeCheckerContext, type: KotlinTypeMarker): SimpleTypeMarker {
return substitutor.safeSubstitute(
type.lowerBoundIfFlexible() as KotlinType,
Variance.INVARIANT
).asSimpleType()!!
}
}
}
override val KotlinTypeMarker.isAllowedTypeVariable: Boolean get() = this is UnwrappedType && allowedTypeVariable && constructor is NewTypeVariableConstructor
}
private fun Any.errorMessage(): String {
return "ClassicTypeCheckerContext couldn't handle ${this::class} $this"
}
@@ -0,0 +1,260 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.types.checker
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.calls.inference.CapturedType
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.types.model.CaptureStatus
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
interface ClassicTypeSystemContext : TypeSystemContext {
override fun TypeConstructorMarker.isDenotable(): Boolean {
require(this is TypeConstructor, this::errorMessage)
return this.isDenotable
}
override fun SimpleTypeMarker.withNullability(nullable: Boolean): SimpleTypeMarker {
require(this is SimpleType, this::errorMessage)
return this.makeNullableAsSpecified(nullable)
}
override fun KotlinTypeMarker.isError(): Boolean {
require(this is KotlinType, this::errorMessage)
return this.isError
}
override fun SimpleTypeMarker.isStubType(): Boolean {
require(this is SimpleType, this::errorMessage)
return this is StubType
}
override fun CapturedTypeMarker.lowerType(): KotlinTypeMarker? {
require(this is NewCapturedType, this::errorMessage)
return this.lowerType
}
override fun TypeConstructorMarker.isIntersection(): Boolean {
require(this is TypeConstructor, this::errorMessage)
return this is IntersectionTypeConstructor
}
override fun identicalArguments(a: SimpleTypeMarker, b: SimpleTypeMarker): Boolean {
require(a is SimpleType, a::errorMessage)
require(b is SimpleType, b::errorMessage)
return a.arguments === b.arguments
}
override fun KotlinTypeMarker.asSimpleType(): SimpleTypeMarker? {
require(this is KotlinType, this::errorMessage)
return this.unwrap() as? SimpleType
}
override fun KotlinTypeMarker.asFlexibleType(): FlexibleTypeMarker? {
require(this is KotlinType, this::errorMessage)
return this.unwrap() as? FlexibleType
}
override fun FlexibleTypeMarker.asDynamicType(): DynamicTypeMarker? {
require(this is FlexibleType, this::errorMessage)
return this as? DynamicType
}
override fun FlexibleTypeMarker.asRawType(): RawTypeMarker? {
require(this is FlexibleType, this::errorMessage)
return this as? RawType
}
override fun FlexibleTypeMarker.upperBound(): SimpleTypeMarker {
require(this is FlexibleType, this::errorMessage)
return this.upperBound
}
override fun FlexibleTypeMarker.lowerBound(): SimpleTypeMarker {
require(this is FlexibleType, this::errorMessage)
return this.lowerBound
}
override fun SimpleTypeMarker.asCapturedType(): CapturedTypeMarker? {
require(this is SimpleType, this::errorMessage)
return this as? NewCapturedType
}
override fun SimpleTypeMarker.asDefinitelyNotNullType(): DefinitelyNotNullTypeMarker? {
require(this is SimpleType, this::errorMessage)
return this as? DefinitelyNotNullType
}
override fun SimpleTypeMarker.isMarkedNullable(): Boolean {
require(this is SimpleType, this::errorMessage)
return this.isMarkedNullable
}
override fun SimpleTypeMarker.typeConstructor(): TypeConstructorMarker {
require(this is SimpleType, this::errorMessage)
return this.constructor
}
override fun SimpleTypeMarker.argumentsCount(): Int {
require(this is SimpleType, this::errorMessage)
return this.arguments.size
}
override fun SimpleTypeMarker.getArgument(index: Int): TypeArgumentMarker {
require(this is SimpleType, this::errorMessage)
return this.arguments[index]
}
override fun TypeArgumentMarker.isStarProjection(): Boolean {
require(this is TypeProjection, this::errorMessage)
return this.isStarProjection
}
override fun TypeArgumentMarker.getVariance(): TypeVariance {
require(this is TypeProjection, this::errorMessage)
return this.projectionKind.convertVariance()
}
private fun Variance.convertVariance(): TypeVariance {
return when (this) {
Variance.INVARIANT -> TypeVariance.INV
Variance.IN_VARIANCE -> TypeVariance.IN
Variance.OUT_VARIANCE -> TypeVariance.OUT
}
}
override fun TypeArgumentMarker.getType(): KotlinTypeMarker {
require(this is TypeProjection, this::errorMessage)
return this.type.unwrap()
}
override fun TypeConstructorMarker.parametersCount(): Int {
require(this is TypeConstructor, this::errorMessage)
return this.parameters.size
}
override fun TypeConstructorMarker.getParameter(index: Int): TypeParameterMarker {
require(this is TypeConstructor, this::errorMessage)
return this.parameters[index]
}
override fun TypeConstructorMarker.supertypes(): Collection<KotlinTypeMarker> {
require(this is TypeConstructor, this::errorMessage)
return this.supertypes
}
override fun TypeParameterMarker.getVariance(): TypeVariance {
require(this is TypeParameterDescriptor, this::errorMessage)
return this.variance.convertVariance()
}
override fun TypeParameterMarker.upperBoundCount(): Int {
require(this is TypeParameterDescriptor, this::errorMessage)
return this.upperBounds.size
}
override fun TypeParameterMarker.getUpperBound(index: Int): KotlinTypeMarker {
require(this is TypeParameterDescriptor, this::errorMessage)
return this.upperBounds[index]
}
override fun TypeParameterMarker.getTypeConstructor(): TypeConstructorMarker {
require(this is TypeParameterDescriptor, this::errorMessage)
return this.typeConstructor
}
override fun isEqualTypeConstructors(c1: TypeConstructorMarker, c2: TypeConstructorMarker): Boolean {
require(c1 is TypeConstructor, c1::errorMessage)
require(c2 is TypeConstructor, c2::errorMessage)
return c1 == c2
}
override fun TypeConstructorMarker.isClassTypeConstructor(): Boolean {
require(this is TypeConstructor, this::errorMessage)
return declarationDescriptor is ClassDescriptor
}
override fun TypeConstructorMarker.isCommonFinalClassConstructor(): Boolean {
require(this is TypeConstructor, this::errorMessage)
val classDescriptor = declarationDescriptor as? ClassDescriptor ?: return false
return classDescriptor.isFinalClass &&
classDescriptor.kind != ClassKind.ENUM_ENTRY &&
classDescriptor.kind != ClassKind.ANNOTATION_CLASS
}
override fun TypeArgumentListMarker.get(index: Int): TypeArgumentMarker {
return when (this) {
is SimpleTypeMarker -> getArgument(index)
is ArgumentList -> get(index)
else -> error("unknown type argument list type: $this, ${this::class}")
}
}
override fun TypeArgumentListMarker.size(): Int {
return when (this) {
is SimpleTypeMarker -> argumentsCount()
is ArgumentList -> size
else -> error("unknown type argument list type: $this, ${this::class}")
}
}
override fun SimpleTypeMarker.asArgumentList(): TypeArgumentListMarker {
require(this is SimpleType, this::errorMessage)
return this
}
override fun captureFromArguments(type: SimpleTypeMarker, status: CaptureStatus): SimpleTypeMarker? {
require(type is SimpleType, type::errorMessage)
return org.jetbrains.kotlin.types.checker.captureFromArguments(type, status)
}
override fun TypeConstructorMarker.isAnyConstructor(): Boolean {
require(this is TypeConstructor, this::errorMessage)
return KotlinBuiltIns.isTypeConstructorForGivenClass(this, FQ_NAMES.any)
}
override fun TypeConstructorMarker.isNothingConstructor(): Boolean {
require(this is TypeConstructor, this::errorMessage)
return KotlinBuiltIns.isTypeConstructorForGivenClass(this, FQ_NAMES.nothing)
}
override fun KotlinTypeMarker.asTypeArgument(): TypeArgumentMarker {
require(this is KotlinType, this::errorMessage)
return this.asTypeProjection()
}
/**
*
* 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
*/
override fun SimpleTypeMarker.isSingleClassifierType(): Boolean {
require(this is SimpleType, this::errorMessage)
return !isError &&
constructor.declarationDescriptor !is TypeAliasDescriptor &&
(constructor.declarationDescriptor != null || this is CapturedType || this is NewCapturedType || this is DefinitelyNotNullType)
}
override fun KotlinTypeMarker.isNotNullNothing(): Boolean {
require(this is KotlinType, this::errorMessage)
return typeConstructor().isNothingConstructor() && !TypeUtils.isNullableType(this)
}
}
@Suppress("NOTHING_TO_INLINE")
private inline fun Any.errorMessage(): String {
return "ClassicTypeSystemContext couldn't handle: $this, ${this::class}"
}
@@ -23,7 +23,8 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.resolve.calls.inference.CapturedTypeConstructor
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.model.CapturedTypeIM
import org.jetbrains.kotlin.types.model.CaptureStatus
import org.jetbrains.kotlin.types.model.CapturedTypeMarker
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.utils.DO_NOTHING_2
@@ -110,12 +111,6 @@ fun captureFromArguments(
return KotlinTypeFactory.simpleType(type.annotations, type.constructor, newArguments, type.isMarkedNullable)
}
enum class CaptureStatus {
FOR_SUBTYPING,
FOR_INCORPORATION,
FROM_EXPRESSION
}
/**
* Now [lowerType] is not null only for in projections.
* Example: `Inv<in String>` For `in String` we create CapturedType with [lowerType] = String.
@@ -130,7 +125,7 @@ class NewCapturedType(
val lowerType: UnwrappedType?, // todo check lower type for nullable captured types
override val annotations: Annotations = Annotations.EMPTY,
override val isMarkedNullable: Boolean = false
) : SimpleType(), CapturedTypeIM {
) : SimpleType(), CapturedTypeMarker {
internal constructor(captureStatus: CaptureStatus, lowerType: UnwrappedType?, projection: TypeProjection) :
this(captureStatus, NewCapturedTypeConstructor(projection), lowerType)
@@ -16,121 +16,57 @@
package org.jetbrains.kotlin.types.checker
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.descriptors.isFinalClass
import org.jetbrains.kotlin.resolve.calls.inference.CapturedType
import org.jetbrains.kotlin.resolve.calls.inference.CapturedTypeConstructorImpl
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.TypeCheckerContext.LowerCapturedTypePolicy.*
import org.jetbrains.kotlin.types.checker.TypeCheckerContext.SeveralSupertypesWithSameConstructorPolicy.*
import org.jetbrains.kotlin.types.checker.TypeCheckerContext.SupertypesPolicy
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.AbstractNullabilityChecker.hasNotNullSupertype
import org.jetbrains.kotlin.types.AbstractNullabilityChecker.hasPathByNotMarkedNullableNodes
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext.SupertypesPolicy
import org.jetbrains.kotlin.types.model.CaptureStatus
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object StrictEqualityTypeChecker {
private val context = object : ClassicTypeSystemContext {}
/**
* 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
return AbstractStrictEqualityTypeChecker.strictEqualTypes(context, a, b)
}
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
) {
return false
}
if (a.arguments === b.arguments) return true
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
return AbstractStrictEqualityTypeChecker.strictEqualTypes(context, a, b)
}
}
object ErrorTypesAreEqualToAnything : KotlinTypeChecker {
override fun isSubtypeOf(subtype: KotlinType, supertype: KotlinType): Boolean =
NewKotlinTypeChecker.run { TypeCheckerContext(true).isSubtypeOf(subtype.unwrap(), supertype.unwrap()) }
NewKotlinTypeChecker.run { ClassicTypeCheckerContext(true).isSubtypeOf(subtype.unwrap(), supertype.unwrap()) }
override fun equalTypes(a: KotlinType, b: KotlinType): Boolean =
NewKotlinTypeChecker.run { TypeCheckerContext(true).equalTypes(a.unwrap(), b.unwrap()) }
NewKotlinTypeChecker.run { ClassicTypeCheckerContext(true).equalTypes(a.unwrap(), b.unwrap()) }
}
object NewKotlinTypeChecker : KotlinTypeChecker {
override fun isSubtypeOf(subtype: KotlinType, supertype: KotlinType): Boolean =
TypeCheckerContext(true).isSubtypeOf(subtype.unwrap(), supertype.unwrap()) // todo fix flag errorTypeEqualsToAnything
ClassicTypeCheckerContext(true).isSubtypeOf(subtype.unwrap(), supertype.unwrap()) // todo fix flag errorTypeEqualsToAnything
override fun equalTypes(a: KotlinType, b: KotlinType): Boolean =
TypeCheckerContext(false).equalTypes(a.unwrap(), b.unwrap())
ClassicTypeCheckerContext(false).equalTypes(a.unwrap(), b.unwrap())
fun TypeCheckerContext.equalTypes(a: UnwrappedType, b: UnwrappedType): Boolean {
if (a === b) return true
if (isCommonDenotableType(a) && isCommonDenotableType(b)) {
if (!areEqualTypeConstructors(a.constructor, b.constructor)) return false
if (a.arguments.isEmpty()) {
if (a.hasFlexibleNullability() || b.hasFlexibleNullability()) return true
return a.isMarkedNullable == b.isMarkedNullable
}
}
return isSubtypeOf(a, b) && isSubtypeOf(b, a)
fun ClassicTypeCheckerContext.equalTypes(a: UnwrappedType, b: UnwrappedType): Boolean {
return AbstractTypeChecker.equalTypes(this, a, b)
}
private fun KotlinType.hasFlexibleNullability() =
lowerIfFlexible().isMarkedNullable != upperIfFlexible().isMarkedNullable
private fun isCommonDenotableType(type: KotlinType) =
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
val newSubType = transformToNewType(subType)
val newSuperType = transformToNewType(superType)
checkSubtypeForSpecialCases(newSubType.lowerIfFlexible(), newSuperType.upperIfFlexible())?.let {
addSubtypeConstraint(newSubType, newSuperType)
return it
}
// we should add constraints with flexible types, otherwise we never get flexible type as answer in constraint system
addSubtypeConstraint(newSubType, newSuperType)?.let { return it }
return isSubtypeOfForSingleClassifierType(newSubType.lowerIfFlexible(), newSuperType.upperIfFlexible())
fun ClassicTypeCheckerContext.isSubtypeOf(subType: UnwrappedType, superType: UnwrappedType): Boolean {
return AbstractTypeChecker.isSubtypeOf(this, subType, superType)
}
fun transformToNewType(type: SimpleType): SimpleType {
@@ -142,20 +78,36 @@ object NewKotlinTypeChecker : KotlinTypeChecker {
// it is incorrect calculate this type directly because of recursive star projections
if (constructor.newTypeConstructor == null) {
constructor.newTypeConstructor = NewCapturedTypeConstructor(constructor.projection, constructor.supertypes.map { it.unwrap() })
constructor.newTypeConstructor =
NewCapturedTypeConstructor(constructor.projection, constructor.supertypes.map { it.unwrap() })
}
return NewCapturedType(CaptureStatus.FOR_SUBTYPING, constructor.newTypeConstructor!!,
lowerType, type.annotations, type.isMarkedNullable)
return NewCapturedType(
CaptureStatus.FOR_SUBTYPING, constructor.newTypeConstructor!!,
lowerType, type.annotations, type.isMarkedNullable
)
}
is IntegerValueTypeConstructor -> {
val newConstructor = IntersectionTypeConstructor(constructor.supertypes.map { TypeUtils.makeNullableAsSpecified(it, type.isMarkedNullable) })
return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(type.annotations, newConstructor, listOf(), false, type.memberScope)
val newConstructor =
IntersectionTypeConstructor(constructor.supertypes.map { TypeUtils.makeNullableAsSpecified(it, type.isMarkedNullable) })
return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(
type.annotations,
newConstructor,
listOf(),
false,
type.memberScope
)
}
is IntersectionTypeConstructor -> if (type.isMarkedNullable) {
val newConstructor = constructor.transformComponents(transform = { it.makeNullable() }) ?: constructor
return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(type.annotations, newConstructor, listOf(), false, newConstructor.createScopeForKotlinType())
return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(
type.annotations,
newConstructor,
listOf(),
false,
newConstructor.createScopeForKotlinType()
)
}
}
@@ -163,188 +115,27 @@ object NewKotlinTypeChecker : KotlinTypeChecker {
}
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
}
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
}
}.inheritEnhancement(type)
private fun TypeCheckerContext.checkSubtypeForSpecialCases(subType: SimpleType, superType: SimpleType): Boolean? {
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 (subType is StubType || superType is StubType) return true
if (superType is NewCapturedType && superType.lowerType != null) {
when (getLowerCapturedTypePolicy(subType, superType)) {
CHECK_ONLY_LOWER -> return isSubtypeOf(subType, superType.lowerType)
CHECK_SUBTYPE_AND_LOWER -> if(isSubtypeOf(subType, superType.lowerType)) return true
SKIP_LOWER -> { /*do nothing*/ }
}
}
}.inheritEnhancement(type)
(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 null
}
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 || subType.isAllowedTypeVariable) {
"Not singleClassifierType and not intersection subType: $subType"
}
assert(superType.isSingleClassifierType || superType.isAllowedTypeVariable) {
"Not singleClassifierType superType: $superType"
}
if (!NullabilityChecker.isPossibleSubtype(this, subType, superType)) return false
val superConstructor = superType.constructor
if (subType.constructor == superConstructor && superConstructor.parameters.isEmpty()) return true
if (superType.isAnyOrNullableAny()) return true
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
when (sameConstructorPolicy) {
FORCE_NOT_SUBTYPE -> return false
TAKE_FIRST_FOR_SUBTYPING -> return isSubtypeForSameConstructor(supertypesWithSameConstructor.first().arguments, superType)
CHECK_ANY_OF_THEM,
INTERSECT_ARGUMENTS_AND_CHECK_AGAIN ->
if (supertypesWithSameConstructor.any { isSubtypeForSameConstructor(it.arguments, superType) }) return true
}
if (sameConstructorPolicy != INTERSECT_ARGUMENTS_AND_CHECK_AGAIN) return false
val newArguments = superConstructor.parameters.mapIndexed { index, _ ->
val allProjections = supertypesWithSameConstructor.map {
it.arguments.getOrNull(index)?.takeIf { it.projectionKind == Variance.INVARIANT }?.type?.unwrap()
?: error("Incorrect type: $it, subType: $subType, superType: $superType")
}
// todo discuss
intersectTypes(allProjections).asTypeProjection()
}
return isSubtypeForSameConstructor(newArguments, superType)
}
}
}
private fun TypeCheckerContext.collectAndFilter(classType: SimpleType, constructor: TypeConstructor) =
selectOnlyPureKotlinSupertypes(collectAllSupertypesWithGivenTypeConstructor(classType, constructor))
// nullability was checked earlier via nullabilityChecker
// should be used only if you really sure that it is correct
fun TypeCheckerContext.findCorrespondingSupertypes(
baseType: SimpleType,
constructor: TypeConstructor
fun ClassicTypeCheckerContext.findCorrespondingSupertypes(
baseType: SimpleType,
constructor: TypeConstructor
): List<SimpleType> {
if (baseType.isClassType) {
return collectAndFilter(baseType, constructor)
return AbstractTypeChecker.run {
findCorrespondingSupertypes(baseType, constructor) as List<SimpleType>
}
// i.e. superType is not a classType
if (constructor.declarationDescriptor !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> {
if (constructor.declarationDescriptor.safeAs<ClassDescriptor>()?.isCommonFinalClass == true) {
return if (areEqualTypeConstructors(baseType.constructor, constructor))
listOf(captureFromArguments(baseType, CaptureStatus.FOR_SUBTYPING) ?: baseType)
else
emptyList()
}
val result: MutableList<SimpleType> = SmartList()
anySupertype(baseType, { false }) {
val current = captureFromArguments(it, CaptureStatus.FOR_SUBTYPING) ?: it
when {
areEqualTypeConstructors(current.constructor, constructor) -> {
result.add(current)
SupertypesPolicy.None
}
current.arguments.isEmpty() -> {
SupertypesPolicy.LowerIfFlexible
}
else -> {
SupertypesPolicy.LowerIfFlexibleWithCustomSubstitutor(TypeConstructorSubstitution.create(current).buildSubstitutor())
}
}
}
return result
}
private val ClassDescriptor.isCommonFinalClass: Boolean
get() = isFinalClass && kind != ClassKind.ENUM_ENTRY && kind != ClassKind.ANNOTATION_CLASS
/**
* 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() } }
return if (allPureSupertypes.isNotEmpty()) allPureSupertypes else supertypes
}
fun effectiveVariance(declared: Variance, useSite: Variance): Variance? {
@@ -358,112 +149,28 @@ object NewKotlinTypeChecker : KotlinTypeChecker {
return null
}
private fun TypeCheckerContext.isSubtypeForSameConstructor(
capturedSubArguments: List<TypeProjection>,
superType: SimpleType
): Boolean {
if (capturedSubArguments === superType.arguments) return true
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)
fun isSubtypeOfAny(type: UnwrappedType): Boolean =
TypeCheckerContext(false).hasNotNullSupertype(type.lowerIfFlexible(), SupertypesPolicy.LowerIfFlexible)
ClassicTypeCheckerContext(false).hasNotNullSupertype(type.lowerIfFlexible(), SupertypesPolicy.LowerIfFlexible)
fun hasPathByNotMarkedNullableNodes(start: SimpleType, end: TypeConstructor) =
TypeCheckerContext(false).hasPathByNotMarkedNullableNodes(start, end)
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 || superType.isAllowedTypeVariable) {
"Not singleClassifierType superType: $superType"
}
// 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 and are not definitely not null.
/**
* 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) || it.isDefinitelyNotNullType }) {
if (it.isMarkedNullable) SupertypesPolicy.None else supertypesPolicy
}
private fun TypeCheckerContext.hasPathByNotMarkedNullableNodes(start: SimpleType, end: TypeConstructor) =
anySupertype(start, { it.isNothing() || (!it.isMarkedNullable && it.constructor == end) }) {
if (it.isMarkedNullable) SupertypesPolicy.None else SupertypesPolicy.LowerIfFlexible
}
ClassicTypeCheckerContext(false).hasPathByNotMarkedNullableNodes(start, end)
}
fun UnwrappedType.hasSupertypeWithGivenTypeConstructor(typeConstructor: TypeConstructor) =
TypeCheckerContext(false).anySupertype(lowerIfFlexible(), { it.constructor == typeConstructor }, { SupertypesPolicy.LowerIfFlexible })
ClassicTypeCheckerContext(false).anySupertype(lowerIfFlexible(), {
require(it is SimpleType)
it.constructor == typeConstructor
}, { SupertypesPolicy.LowerIfFlexible })
fun UnwrappedType.anySuperTypeConstructor(predicate: (TypeConstructor) -> Boolean) =
TypeCheckerContext(false).anySupertype(lowerIfFlexible(), { predicate(it.constructor) }, { SupertypesPolicy.LowerIfFlexible })
ClassicTypeCheckerContext(false).anySupertype(lowerIfFlexible(), {
require(it is SimpleType)
predicate(it.constructor)
}, { SupertypesPolicy.LowerIfFlexible })
/**
* ClassType means that type constructor for this type is type for real class or interface
@@ -1,138 +0,0 @@
/*
* 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 java.util.*
open class TypeCheckerContext(val errorTypeEqualsToAnything: Boolean, val allowedTypeVariable: Boolean = true) {
protected var argumentsDepth = 0
private var supertypesLocked = false
private var supertypesDeque: ArrayDeque<SimpleType>? = null
private var supertypesSet: MutableSet<SimpleType>? = null
open fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType): Boolean? = null
open fun areEqualTypeConstructors(a: TypeConstructor, b: TypeConstructor): Boolean {
return a == b
}
open fun getLowerCapturedTypePolicy(subType: SimpleType, superType: NewCapturedType) = LowerCapturedTypePolicy.CHECK_SUBTYPE_AND_LOWER
open val sameConstructorPolicy get() = SeveralSupertypesWithSameConstructorPolicy.INTERSECT_ARGUMENTS_AND_CHECK_AGAIN
internal 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(4)
}
if (supertypesSet == null) {
supertypesSet = SmartSet.create()
}
}
private fun clear() {
supertypesDeque!!.clear()
supertypesSet!!.clear()
supertypesLocked = false
}
internal inline fun anySupertype(
start: SimpleType,
predicate: (SimpleType) -> Boolean,
supertypesPolicy: (SimpleType) -> SupertypesPolicy
): Boolean {
if (predicate(start)) return true
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
val policy = supertypesPolicy(current).takeIf { it != SupertypesPolicy.None } ?: continue
for (supertype in current.constructor.supertypes) {
val newType = policy.transformType(supertype)
if (predicate(newType)) {
clear()
return true
}
deque.add(newType)
}
}
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()
}
}
enum class SeveralSupertypesWithSameConstructorPolicy {
TAKE_FIRST_FOR_SUBTYPING,
FORCE_NOT_SUBTYPE,
CHECK_ANY_OF_THEM,
INTERSECT_ARGUMENTS_AND_CHECK_AGAIN
}
enum class LowerCapturedTypePolicy {
CHECK_ONLY_LOWER,
CHECK_SUBTYPE_AND_LOWER,
SKIP_LOWER
}
val UnwrappedType.isAllowedTypeVariable: Boolean get() = allowedTypeVariable && constructor is NewTypeVariableConstructor
}
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions
import org.jetbrains.kotlin.types.model.DynamicTypeIM
import org.jetbrains.kotlin.types.model.DynamicTypeMarker
import org.jetbrains.kotlin.types.typeUtil.builtIns
open class DynamicTypesSettings {
@@ -40,7 +40,7 @@ fun createDynamicType(builtIns: KotlinBuiltIns) = DynamicType(builtIns, Annotati
class DynamicType(
builtIns: KotlinBuiltIns,
override val annotations: Annotations
) : FlexibleType(builtIns.nothingType, builtIns.nullableAnyType), DynamicTypeIM {
) : FlexibleType(builtIns.nothingType, builtIns.nullableAnyType), DynamicTypeMarker {
override val delegate: SimpleType get() = upperBound
// Nullability has no effect on dynamics