psi2ir: ersatz type approximation for intersection types

Emulate old JVM back-end behavior for intersection type mapping.

IrType renderer should render the IR type, not the original Kotlin type.
This commit is contained in:
Dmitry Petrov
2018-12-06 16:54:35 +03:00
parent 0e4908087c
commit f8582c1929
11 changed files with 599 additions and 71 deletions
@@ -22,12 +22,15 @@ import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addIfNotNull
fun IrElement.render() = accept(RenderIrElementVisitor(), null)
@@ -351,38 +354,84 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
override fun visitErrorCallExpression(expression: IrErrorCallExpression, data: Nothing?): String =
"ERROR_CALL '${expression.description}' type=${expression.type.render()}"
}
companion object {
val DECLARATION_RENDERER = DescriptorRenderer.withOptions {
withDefinedIn = false
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OPEN_OVERRIDE
includePropertyConstant = true
classifierNamePolicy = ClassifierNamePolicy.FULLY_QUALIFIED
verbose = false
modifiers = DescriptorRendererModifier.ALL
private val DECLARATION_RENDERER = DescriptorRenderer.withOptions {
withDefinedIn = false
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OPEN_OVERRIDE
includePropertyConstant = true
classifierNamePolicy = ClassifierNamePolicy.FULLY_QUALIFIED
verbose = false
modifiers = DescriptorRendererModifier.ALL
}
private val REFERENCE_RENDERER = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES
internal fun IrDeclaration.name(): String =
descriptor.name.toString()
internal fun DescriptorRenderer.renderDescriptor(descriptor: DeclarationDescriptor): String =
if (descriptor is ReceiverParameterDescriptor)
"this@${descriptor.containingDeclaration.name}: ${descriptor.type}"
else
render(descriptor)
internal fun IrDeclaration.renderDeclared(): String =
DECLARATION_RENDERER.renderDescriptor(this.descriptor)
internal fun DeclarationDescriptor.ref(): String =
REFERENCE_RENDERER.renderDescriptor(this.original)
internal fun KotlinType.render(): String =
DECLARATION_RENDERER.renderType(this)
internal fun IrDeclaration.renderOriginIfNonTrivial(): String =
if (origin != IrDeclarationOrigin.DEFINED) origin.toString() + " " else ""
internal fun IrType.renderTypeInner(): String =
when (this) {
is IrDynamicType -> "dynamic"
is IrErrorType -> "ERROR"
is IrSimpleType -> buildString {
append(DECLARATION_RENDERER.renderClassifierName(classifier.descriptor)) // TODO get rid of descriptors
if (arguments.isNotEmpty()) {
append(
arguments.joinToString(prefix = "<", postfix = ">", separator = ", ") {
it.renderTypeArgument()
}
)
}
if (hasQuestionMark) {
append('?')
}
}
val REFERENCE_RENDERER = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES
internal fun IrDeclaration.name(): String =
descriptor.name.toString()
internal fun DescriptorRenderer.renderDescriptor(descriptor: DeclarationDescriptor): String =
if (descriptor is ReceiverParameterDescriptor)
"this@${descriptor.containingDeclaration.name}: ${descriptor.type}"
else
render(descriptor)
internal fun IrDeclaration.renderDeclared(): String =
DECLARATION_RENDERER.renderDescriptor(this.descriptor)
internal fun DeclarationDescriptor.ref(): String =
REFERENCE_RENDERER.renderDescriptor(this.original)
internal fun KotlinType.render(): String =
DECLARATION_RENDERER.renderType(this)
internal fun IrDeclaration.renderOriginIfNonTrivial(): String =
if (origin != IrDeclarationOrigin.DEFINED) origin.toString() + " " else ""
else ->
originalKotlinType?.let {
DECLARATION_RENDERER.renderType(it)
} ?: "IrType without originalKotlinType: $this"
}
}
private fun IrTypeArgument.renderTypeArgument(): String =
when (this) {
is IrStarProjection -> "*"
is IrTypeProjection -> buildString {
append(variance.label)
if (variance != Variance.INVARIANT) append(' ')
append(type.render())
}
else -> "IrTypeArgument[$this]"
}
internal fun renderTypeAnnotations(annotations: List<IrCall>) =
if (annotations.isEmpty())
""
else
annotations.joinToString(prefix = "", postfix = " ", separator = " ") { "@[${it.render()}]" }
fun IrType.render() = "${renderTypeAnnotations(annotations)}${renderTypeInner()}"
@@ -1,14 +0,0 @@
/*
* Copyright 2010-2018 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.ir.util
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
fun IrType.render() =
originalKotlinType?.let {
RenderIrElementVisitor.DECLARATION_RENDERER.renderType(it)
} ?: "IrType without originalKotlinType: $this"
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.ir.types.impl.IrErrorTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes
class TypeTranslator(
@@ -33,14 +34,14 @@ class TypeTranslator(
fun enterScope(irElement: IrTypeParametersContainer) {
typeParametersResolver.enterTypeParameterScope(irElement)
if(enterTableScope) {
if (enterTableScope) {
symbolTable.enterScope(irElement.descriptor)
}
}
fun leaveScope(irElement: IrTypeParametersContainer) {
typeParametersResolver.leaveTypeParameterScope()
if(enterTableScope) {
if (enterTableScope) {
symbolTable.leaveScope(irElement.descriptor)
}
}
@@ -54,43 +55,45 @@ class TypeTranslator(
private fun resolveTypeParameter(typeParameterDescriptor: TypeParameterDescriptor) =
typeParametersResolver.resolveScopedTypeParameter(typeParameterDescriptor)
?: symbolTable.referenceTypeParameter(typeParameterDescriptor)
?: symbolTable.referenceTypeParameter(typeParameterDescriptor)
fun translateType(ktType: KotlinType): IrType =
translateType(ktType, Variance.INVARIANT).type
private fun translateType(ktType0: KotlinType, variance: Variance): IrTypeProjection {
// TODO "old" JVM BE does this for reified type arguments. Is it ok for arbitrary subexpressions?
val ktTypeUpper = ktType0.approximate(languageVersionSettings)
private fun translateType(ktType: KotlinType, variance: Variance): IrTypeProjection {
val approximatedType = LegacyTypeApproximation().approximate(ktType)
when {
ktTypeUpper.isError -> return IrErrorTypeImpl(ktTypeUpper, translateTypeAnnotations(ktTypeUpper.annotations), variance)
ktTypeUpper.isDynamic() -> return IrDynamicTypeImpl(ktTypeUpper, translateTypeAnnotations(ktTypeUpper.annotations), variance)
ktTypeUpper.isFlexible() -> return translateType(ktTypeUpper.upperIfFlexible(), variance)
approximatedType.isError ->
return IrErrorTypeImpl(approximatedType, translateTypeAnnotations(approximatedType.annotations), variance)
approximatedType.isDynamic() ->
return IrDynamicTypeImpl(approximatedType, translateTypeAnnotations(approximatedType.annotations), variance)
approximatedType.isFlexible() ->
return translateType(approximatedType.upperIfFlexible(), variance)
}
val ktTypeConstructor = ktTypeUpper.constructor
val ktTypeDescriptor = ktTypeConstructor.declarationDescriptor ?: throw AssertionError("No descriptor for type $ktTypeUpper")
val ktTypeConstructor = approximatedType.constructor
val ktTypeDescriptor = ktTypeConstructor.declarationDescriptor
?: throw AssertionError("No descriptor for type $approximatedType")
return when (ktTypeDescriptor) {
is TypeParameterDescriptor ->
IrSimpleTypeImpl(
ktTypeUpper,
approximatedType,
resolveTypeParameter(ktTypeDescriptor),
ktTypeUpper.isMarkedNullable,
approximatedType.isMarkedNullable,
emptyList(),
translateTypeAnnotations(ktTypeUpper.annotations),
translateTypeAnnotations(approximatedType.annotations),
variance
)
is ClassDescriptor ->
IrSimpleTypeImpl(
ktTypeUpper,
approximatedType,
symbolTable.referenceClass(ktTypeDescriptor),
ktTypeUpper.isMarkedNullable,
translateTypeArguments(ktTypeUpper.arguments),
translateTypeAnnotations(ktTypeUpper.annotations),
approximatedType.isMarkedNullable,
translateTypeArguments(approximatedType.arguments),
translateTypeAnnotations(approximatedType.annotations),
variance
)
@@ -99,15 +102,36 @@ class TypeTranslator(
}
}
private fun KotlinType.approximate(languageVersionSettings: LanguageVersionSettings): KotlinType {
if (this.constructor.isDenotable) return this
private inner class LegacyTypeApproximation {
fun approximate(ktType: KotlinType): KotlinType {
val properlyApproximatedType = approximateByKotlinRules(ktType)
// If there's an intersection type, take the most common supertype of its intermediate supertypes.
// That's what old back-end effectively does.
val typeConstructor = properlyApproximatedType.constructor
if (typeConstructor is IntersectionTypeConstructor) {
val commonSupertype = CommonSupertypes.commonSupertype(typeConstructor.supertypes)
return approximate(commonSupertype.replaceArgumentsWithStarProjections())
}
// Other types should be approximated properly. Right? Riiight?
return properlyApproximatedType
}
private fun approximateByKotlinRules(ktType: KotlinType): KotlinType {
if (ktType.constructor.isDenotable) return ktType
return if (languageVersionSettings.supportsFeature(LanguageFeature.NewInference))
typeApproximatorForNI.approximateDeclarationType(ktType, local = false, languageVersionSettings = languageVersionSettings)
else
approximateCapturedTypes(ktType).upper
}
return if (languageVersionSettings.supportsFeature(LanguageFeature.NewInference))
typeApproximatorForNI.approximateDeclarationType(this, local = false, languageVersionSettings = languageVersionSettings)
else
approximateCapturedTypes(this).upper
}
private fun translateTypeAnnotations(annotations: Annotations): List<IrCall> =
annotations.map(constantValueGenerator::generateAnnotationConstructorCall)