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:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user