[IR] Implement TypeChecker context and full isSubtypeOf/commonSuperType for IrType

This commit is contained in:
Roman Artemev
2019-04-09 16:02:42 +03:00
committed by Dmitry Petrov
parent b7d098af56
commit 3fc1419d01
5 changed files with 250 additions and 27 deletions
@@ -6,31 +6,53 @@
package org.jetbrains.kotlin.ir.types
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.types.impl.makeTypeIntersection
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
class IrTypeCheckerContext(override val irBuiltIns: IrBuiltIns) : IrTypeSystemContext, AbstractTypeCheckerContext() {
override fun substitutionSupertypePolicy(type: SimpleTypeMarker): SupertypesPolicy.DoCustomTransform {
require(type is IrSimpleType)
val parameters = extractTypeParameters((type.classifier as IrClassSymbol).owner).map { it.symbol }
val substitution = parameters.zip(type.arguments).toMap()
return object : SupertypesPolicy.DoCustomTransform() {
override fun transformType(context: AbstractTypeCheckerContext, type: KotlinTypeMarker): SimpleTypeMarker {
return makeTypeProjection(type as IrType, Variance.INVARIANT) as SimpleTypeMarker
require(type is IrSimpleType)
return substituteArguments(type)
}
private fun substituteArguments(type: IrSimpleType): IrSimpleType {
val realArguments = type.arguments.map {
substitute(it)
}.toList()
return IrSimpleTypeImpl(type.classifier, type.hasQuestionMark, realArguments, type.annotations)
}
private fun substitute(type: IrTypeArgument): IrTypeArgument {
if (type is IrStarProjection) return type
val actualType = (type as IrTypeProjection).type as IrSimpleType
substitution[actualType.classifier]?.let { return it }
return makeTypeProjection(substituteArguments(actualType), type.variance)
}
}
}
override fun areEqualTypeConstructors(a: TypeConstructorMarker, b: TypeConstructorMarker) = super.isEqualTypeConstructors(a, b)
@Suppress("UNCHECKED_CAST")
override fun intersectTypes(types: List<KotlinTypeMarker>) = makeTypeIntersection(types as List<IrType>)
override val isErrorTypeEqualsToAnything = false
override val KotlinTypeMarker.isAllowedTypeVariable: Boolean
get() = false
override fun newBaseTypeCheckerContext(errorTypesEqualToAnything: Boolean): AbstractTypeCheckerContext {
return IrTypeCheckerContext(irBuiltIns)
}
}
@@ -7,19 +7,25 @@ package org.jetbrains.kotlin.ir.types
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.FqNameEqualityChecker
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeIntersection
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.types.impl.*
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.checker.convertVariance
import org.jetbrains.kotlin.types.model.*
interface IrTypeSystemContext : TypeSystemContext {
private val NO_INFER_ANNOTATION_FQ_NAME = FqName("kotlin.internal.NoInfer")
private val EXACT_ANNOTATION_FQ_NAME = FqName("kotlin.internal.Exact")
interface IrTypeSystemContext : TypeSystemInferenceExtensionContext {
val irBuiltIns: IrBuiltIns
@@ -29,6 +35,8 @@ interface IrTypeSystemContext : TypeSystemContext {
override fun KotlinTypeMarker.isError() = this is IrErrorType
override fun SimpleTypeMarker.isStubType() = false
override fun FlexibleTypeMarker.asDynamicType() = this as? IrDynamicType
override fun FlexibleTypeMarker.asRawType(): RawTypeMarker? = null
@@ -47,7 +55,7 @@ interface IrTypeSystemContext : TypeSystemContext {
override fun SimpleTypeMarker.asDefinitelyNotNullType(): DefinitelyNotNullTypeMarker? = null
override fun SimpleTypeMarker.isMarkedNullable(): Boolean = (this as IrSimpleType).hasQuestionMark
override fun SimpleTypeMarker.isMarkedNullable(): Boolean = (this as IrSimpleType).containsNull()
override fun SimpleTypeMarker.withNullability(nullable: Boolean): SimpleTypeMarker {
val simpleType = this as IrSimpleType
@@ -75,13 +83,11 @@ interface IrTypeSystemContext : TypeSystemContext {
override fun TypeArgumentMarker.getType() = (this as IrTypeProjection).type
private fun getTypeParameters(typeConstructor: TypeConstructorMarker): List<IrTypeParameter> {
val ownTypeParameterContainer = when(typeConstructor) {
is IrTypeParameterSymbol -> (typeConstructor.owner.parent as IrTypeParametersContainer)
is IrClassSymbol -> typeConstructor.owner
return when (typeConstructor) {
is IrTypeParameterSymbol -> emptyList()
is IrClassSymbol -> typeConstructor.owner.typeParameters
else -> error("unsupported type constructor")
}
return ownTypeParameterContainer.typeParameters
}
override fun TypeConstructorMarker.parametersCount() = getTypeParameters(this).size
@@ -110,9 +116,11 @@ interface IrTypeSystemContext : TypeSystemContext {
override fun TypeParameterMarker.getTypeConstructor() = this as IrTypeParameterSymbol
override fun isEqualTypeConstructors(c1: TypeConstructorMarker, c2: TypeConstructorMarker) = (c1 === c2)
override fun isEqualTypeConstructors(c1: TypeConstructorMarker, c2: TypeConstructorMarker) = FqNameEqualityChecker.areEqual(
c1 as IrClassifierSymbol, c2 as IrClassifierSymbol
)
override fun TypeConstructorMarker.isDenotable() = false
override fun TypeConstructorMarker.isDenotable() = true
override fun TypeConstructorMarker.isCommonFinalClassConstructor(): Boolean {
val classSymbol = this as? IrClassSymbol ?: return false
@@ -129,16 +137,17 @@ interface IrTypeSystemContext : TypeSystemContext {
*
* See https://jetbrains.github.io/kotlin-spec/#type-capturing
*/
override fun captureFromArguments(type: SimpleTypeMarker, status: CaptureStatus): SimpleTypeMarker? {
// TODO: is that correct?
require(type is IrSimpleType)
val classifier = type.classifier as IrClassSymbol
val classifier = type.classifier as? IrClassSymbol ?: return null
val typeArguments = type.arguments
if (!classifier.isBound) return null
val typeParameters = classifier.owner.typeParameters
val typeParameters = extractTypeParameters(classifier.owner)
require(typeArguments.size == typeParameters.size)
@@ -157,7 +166,10 @@ interface IrTypeSystemContext : TypeSystemContext {
val additionalBounds =
if (argument is IrTypeProjection && argument.variance == Variance.OUT_VARIANCE) listOf(argument.type) else emptyList()
newArguments += makeTypeProjection(makeTypeIntersection(parameter.superTypes + additionalBounds), Variance.INVARIANT)
newArguments += makeTypeProjection(
makeTypeIntersection(parameter.superTypes + additionalBounds),
Variance.INVARIANT
)
}
return IrSimpleTypeImpl(type.classifier, type.hasQuestionMark, newArguments, type.annotations)
@@ -171,8 +183,171 @@ interface IrTypeSystemContext : TypeSystemContext {
override fun KotlinTypeMarker.isNotNullNothing(): Boolean {
val simpleType = this as? IrSimpleType ?: return false
return simpleType.classifier === irBuiltIns.nothingClass && !simpleType.hasQuestionMark
return simpleType == irBuiltIns.nothingType && !simpleType.hasQuestionMark
}
override fun SimpleTypeMarker.isSingleClassifierType() = true
override fun SimpleTypeMarker.possibleIntegerTypes() = irBuiltIns.run {
setOf(byteType, shortType, intType, longType)
}
override fun TypeConstructorMarker.isIntegerLiteralTypeConstructor() = false
override fun nullableNothingType() = irBuiltIns.nothingNType as IrSimpleType
override fun nullableAnyType() = irBuiltIns.anyNType as IrSimpleType
override fun nothingType() = irBuiltIns.nothingType as IrSimpleType
override fun createFlexibleType(lowerBound: SimpleTypeMarker, upperBound: SimpleTypeMarker): KotlinTypeMarker {
require(lowerBound == nothingType())
require(upperBound == nullableAnyType())
return IrDynamicTypeImpl(null, emptyList(), Variance.INVARIANT)
}
override fun createSimpleType(
constructor: TypeConstructorMarker,
arguments: List<TypeArgumentMarker>,
nullable: Boolean
): SimpleTypeMarker = IrSimpleTypeImpl(constructor as IrClassifierSymbol, nullable, arguments.map { it as IrTypeArgument }, emptyList())
private fun TypeVariance.convertVariance(): Variance {
return when (this) {
TypeVariance.INV -> Variance.INVARIANT
TypeVariance.IN -> Variance.IN_VARIANCE
TypeVariance.OUT -> Variance.OUT_VARIANCE
}
}
override fun createTypeArgument(type: KotlinTypeMarker, variance: TypeVariance): TypeArgumentMarker =
makeTypeProjection(type as IrType, variance.convertVariance())
override fun createStarProjection(typeParameter: TypeParameterMarker) = IrStarProjectionImpl
override fun KotlinTypeMarker.canHaveUndefinedNullability() = this is IrSimpleType && classifier is IrTypeParameterSymbol
override fun SimpleTypeMarker.typeDepth() = 2
override fun KotlinTypeMarker.typeDepth() = if (this is IrStarProjection) 1 else 0
override fun findCommonIntegerLiteralTypesSuperType(explicitSupertypes: List<SimpleTypeMarker>): SimpleTypeMarker? =
irBuiltIns.intType as IrSimpleType
override fun KotlinTypeMarker.contains(predicate: (KotlinTypeMarker) -> Boolean) = predicate(this)
override fun TypeConstructorMarker.isUnitTypeConstructor() = isEqualTypeConstructors(this, irBuiltIns.unitClass)
override fun TypeConstructorMarker.getApproximatedIntegerLiteralType(): KotlinTypeMarker = irBuiltIns.intType
override fun Collection<KotlinTypeMarker>.singleBestRepresentative(): KotlinTypeMarker? {
if (this.size == 1) return this.first()
return this.firstOrNull { candidate ->
this.all { other ->
// We consider error types equal to anything here, so that intersections like
// {Array<String>, Array<[ERROR]>} work correctly
candidate == other || other is IrErrorType
}
}
}
override fun KotlinTypeMarker.isUnit() = (this as IrType) == irBuiltIns.unitType
override fun KotlinTypeMarker.withNullability(nullable: Boolean) = if (this is IrSimpleType)
IrSimpleTypeImpl(classifier, true, arguments, annotations)
else this
override fun KotlinTypeMarker.makeDefinitelyNotNullOrNotNull(): KotlinTypeMarker {
error("Captured Type is not supported for IrType")
}
override fun SimpleTypeMarker.makeSimpleTypeDefinitelyNotNullOrNotNull(): SimpleTypeMarker {
error("Captured Type is not supported for IrType")
}
override fun createCapturedType(
constructorProjection: TypeArgumentMarker,
constructorSupertypes: List<KotlinTypeMarker>,
lowerType: KotlinTypeMarker?,
captureStatus: CaptureStatus
): CapturedTypeMarker {
error("Captured Type is not supported for IrType")
}
override fun createStubType(typeVariable: TypeVariableMarker) = error("Stub type is not supported for IrTypes")
override fun KotlinTypeMarker.removeAnnotations() = with(this as IrType) {
if (annotations.isEmpty()) this
else {
when {
this is IrDynamicType -> IrDynamicTypeImpl(null, emptyList(), Variance.INVARIANT)
this is IrSimpleType -> IrSimpleTypeImpl(classifier, hasQuestionMark, arguments, emptyList())
else -> this
}
}
}
override fun SimpleTypeMarker.replaceArguments(newArguments: List<TypeArgumentMarker>): SimpleTypeMarker = with(this as IrSimpleType) {
IrSimpleTypeImpl(classifier, hasQuestionMark, newArguments.map { it as IrTypeArgument }, annotations)
}
override fun KotlinTypeMarker.hasExactAnnotation(): Boolean = with(this as IrType) {
annotations.hasAnnotation(EXACT_ANNOTATION_FQ_NAME)
}
override fun KotlinTypeMarker.hasNoInferAnnotation(): Boolean = with(this as IrType) {
annotations.hasAnnotation(NO_INFER_ANNOTATION_FQ_NAME)
}
override fun TypeVariableMarker.freshTypeConstructor(): TypeConstructorMarker =
error("freshTypeConstructor is not supported for IrType")
override fun CapturedTypeMarker.typeConstructorProjection() =
error("typeConstructorProjection is not supported for IrType")
override fun KotlinTypeMarker.isNullableType() = this is IrDynamicType || this is IrSimpleType && hasQuestionMark
override fun DefinitelyNotNullTypeMarker.original(): SimpleTypeMarker = error("DefinitelyNotNullTypeMarker is not supported for IrType")
override fun typeSubstitutorByTypeConstructor(map: Map<TypeConstructorMarker, KotlinTypeMarker>): TypeSubstitutorMarker {
TODO("not implemented")
}
override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker {
TODO("not implemented")
}
override fun TypeVariableMarker.defaultType(): SimpleTypeMarker = error("defaultType is not supported for IrType")
@Suppress("UNCHECKED_CAST")
override fun intersectTypes(types: List<SimpleTypeMarker>): SimpleTypeMarker =
makeTypeIntersection(types as List<IrType>) as SimpleTypeMarker
@Suppress("UNCHECKED_CAST")
override fun intersectTypes(types: List<KotlinTypeMarker>): KotlinTypeMarker =
makeTypeIntersection(types as List<IrType>)
}
fun extractTypeParameters(klass: IrTypeParametersContainer): List<IrTypeParameter> {
val result = mutableListOf<IrTypeParameter>()
var current: IrTypeParametersContainer? = klass
while (current != null) {
result += current.typeParameters
current =
when (current) {
is IrClass -> when {
current.isInner -> current.parent as IrClass
current.visibility == Visibilities.LOCAL -> current.parent as? IrTypeParametersContainer
else -> null
}
is IrConstructor -> current.parent as IrClass
is IrFunction -> if (current.visibility == Visibilities.LOCAL || current.dispatchReceiverParameter != null) {
current.parent as? IrTypeParametersContainer
} else null
else -> null
}
}
return result
}
@@ -5,11 +5,15 @@
package org.jetbrains.kotlin.ir.types
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.FqNameEqualityChecker
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
import org.jetbrains.kotlin.utils.DFS
fun IrClassifierSymbol.superTypes() = when (this) {
@@ -59,3 +63,13 @@ fun Collection<IrClassifierSymbol>.commonSuperclass(): IrClassifierSymbol {
) { it.owner.render() }}"
)
}
fun IrType.isSubtypeOf(superType: IrType, irBuiltIns: IrBuiltIns): Boolean {
return AbstractTypeChecker.isSubtypeOf(IrTypeCheckerContext(irBuiltIns) as AbstractTypeCheckerContext, this, superType)
}
fun Collection<IrType>.commonSupertype(irBuiltIns: IrBuiltIns): IrType {
return NewCommonSuperTypeCalculator.run {
IrTypeCheckerContext(irBuiltIns).commonSuperType(map { it }) as IrType
}
}
@@ -99,6 +99,8 @@ fun makeTypeProjection(type: IrType, variance: Variance): IrTypeProjection =
}
fun makeTypeIntersection(types: List<IrType>) =
if (types.size == 1) types[1]
else TODO("Not implemented")
fun makeTypeIntersection(types: List<IrType>): IrType =
with(types.map { makeTypeProjection(it, Variance.INVARIANT).type }.distinct()) {
if (size == 1) return single()
else first { !(it.isAny() || it.isNullableAny()) }
}
@@ -47,6 +47,12 @@ fun IrType.isPrimitiveType(): Boolean = KotlinBuiltIns.FQ_NAMES.fqNameToPrimitiv
fun IrType.isNullablePrimitiveType(): Boolean = KotlinBuiltIns.FQ_NAMES.fqNameToPrimitiveType.keys.any { isNullableClassType(it) }
fun IrType.isMarkedNullable() = (this as? IrSimpleType)?.hasQuestionMark ?: false
fun IrType.containsNull(): Boolean = when {
this is IrDynamicType -> true
this is IrStarProjection -> true
this is IrSimpleType -> hasQuestionMark || classifier.superTypes().any { it.containsNull() }
else -> false
}
fun IrType.isUnit() = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.unit)
fun IrType.isNullableUnit() = isNullableClassType(KotlinBuiltIns.FQ_NAMES.unit)
@@ -58,6 +64,10 @@ fun IrType.isByte(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._byte)
fun IrType.isShort(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._short)
fun IrType.isInt(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._int)
fun IrType.isLong(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._long)
fun IrType.isUByte(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.uByteFqName.toUnsafe())
fun IrType.isUShort(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.uShortFqName.toUnsafe())
fun IrType.isUInt(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.uIntFqName.toUnsafe())
fun IrType.isULong(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.uLongFqName.toUnsafe())
fun IrType.isFloat(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._float)
fun IrType.isDouble(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._double)
fun IrType.isNumber(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.number)