[JS BEs] use star projection when type parameter used recursively

#KT-37128 Fixed
This commit is contained in:
Zalim Bashorov
2020-03-05 13:10:26 +03:00
parent 8c7562d338
commit 7cf8697e30
10 changed files with 127 additions and 39 deletions
@@ -25201,6 +25201,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt"); runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt");
} }
@TestMetadata("reifiedTypeArgumentWithRecursion.kt")
public void testReifiedTypeArgumentWithRecursion() throws Exception {
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithRecursion.kt");
}
@TestMetadata("safecast.kt") @TestMetadata("safecast.kt")
public void testSafecast() throws Exception { public void testSafecast() throws Exception {
runTest("compiler/testData/codegen/box/reified/safecast.kt"); runTest("compiler/testData/codegen/box/reified/safecast.kt");
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -136,9 +136,10 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass
} }
} }
fun createKType(type: IrType): IrExpression { private fun createKType(type: IrType, visitedTypeParams: MutableSet<IrTypeParameter>): IrExpression {
if (type is IrSimpleType) if (type is IrSimpleType)
return createSimpleKType(type) return createSimpleKType(type, visitedTypeParams)
if (type is IrDynamicType) if (type is IrDynamicType)
return createDynamicType() return createDynamicType()
error("Unexpected type $type") error("Unexpected type $type")
@@ -148,7 +149,7 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass
return buildCall(context.intrinsics.createDynamicKType!!) return buildCall(context.intrinsics.createDynamicKType!!)
} }
private fun createSimpleKType(type: IrSimpleType): IrExpression { private fun createSimpleKType(type: IrSimpleType, visitedTypeParams: MutableSet<IrTypeParameter>): IrExpression {
val classifier: IrClassifierSymbol = type.classifier val classifier: IrClassifierSymbol = type.classifier
// TODO: Check why do we have un-substituted reified parameters // TODO: Check why do we have un-substituted reified parameters
@@ -156,9 +157,9 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass
// error("Fail") // error("Fail")
// } // }
val kClassifier = createKClassifier(classifier) val kClassifier = createKClassifier(classifier, visitedTypeParams)
// TODO: Use static array types // TODO: Use static array types
val arguments = type.arguments.map { createKTypeProjection(it) }.toJsArrayLiteral( val arguments = type.arguments.map { createKTypeProjection(it, visitedTypeParams) }.toJsArrayLiteral(
context, context,
context.dynamicType, context.dynamicType,
context.dynamicType context.dynamicType
@@ -172,7 +173,7 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass
) )
} }
private fun createKTypeProjection(tp: IrTypeArgument): IrExpression { private fun createKTypeProjection(tp: IrTypeArgument, visitedTypeParams: MutableSet<IrTypeParameter>): IrExpression {
if (tp !is IrTypeProjection) { if (tp !is IrTypeProjection) {
return buildCall(context.intrinsics.getStarKTypeProjection!!) return buildCall(context.intrinsics.getStarKTypeProjection!!)
} }
@@ -183,20 +184,24 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass
Variance.OUT_VARIANCE -> context.intrinsics.createCovariantKTypeProjection!! Variance.OUT_VARIANCE -> context.intrinsics.createCovariantKTypeProjection!!
} }
val kType = createKType(tp.type) val kType = createKType(tp.type, visitedTypeParams)
return buildCall(factoryName, kType) return buildCall(factoryName, kType)
} }
private fun createKClassifier(classifier: IrClassifierSymbol): IrExpression = private fun createKClassifier(classifier: IrClassifierSymbol, visitedTypeParams: MutableSet<IrTypeParameter>): IrExpression =
when (classifier) { when (classifier) {
is IrTypeParameterSymbol -> createKTypeParameter(classifier.owner) is IrTypeParameterSymbol -> createKTypeParameter(classifier.owner, visitedTypeParams)
else -> callGetKClass(typeArgument = classifier.defaultType) else -> callGetKClass(typeArgument = classifier.defaultType)
} }
private fun createKTypeParameter(typeParameter: IrTypeParameter): IrExpression { private fun createKTypeParameter(typeParameter: IrTypeParameter, visitedTypeParams: MutableSet<IrTypeParameter>): IrExpression {
if (typeParameter in visitedTypeParams) return buildCall(context.intrinsics.getStarKTypeProjection!!)
visitedTypeParams.add(typeParameter)
val name = JsIrBuilder.buildString(context.irBuiltIns.stringType, typeParameter.name.asString()) val name = JsIrBuilder.buildString(context.irBuiltIns.stringType, typeParameter.name.asString())
val upperBounds = typeParameter.superTypes.map { createKType(it) }.toJsArrayLiteral( val upperBounds = typeParameter.superTypes.map { createKType(it, visitedTypeParams) }.toJsArrayLiteral(
context, context,
context.dynamicType, context.dynamicType,
context.dynamicType context.dynamicType
@@ -218,7 +223,9 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass
name, name,
upperBounds, upperBounds,
variance variance
) ).also {
visitedTypeParams.remove(typeParameter)
}
} }
override fun lower(irBody: IrBody, container: IrDeclaration) { override fun lower(irBody: IrBody, container: IrDeclaration) {
@@ -238,7 +245,7 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass
override fun visitCall(expression: IrCall): IrExpression = override fun visitCall(expression: IrCall): IrExpression =
if (Symbols.isTypeOfIntrinsic(expression.symbol)) { if (Symbols.isTypeOfIntrinsic(expression.symbol)) {
createKType(expression.getTypeArgument(0)!!) createKType(expression.getTypeArgument(0)!!, hashSetOf())
} else { } else {
super.visitCall(expression) super.visitCall(expression)
} }
@@ -0,0 +1,33 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// KJS_WITH_FULL_RUNTIME
// See KT-37128
import kotlin.reflect.typeOf
// TODO check real effects to fix the behavior when we reach consensus
// and to be sure that something is not dropped by optimizations.
@OptIn(kotlin.ExperimentalStdlibApi::class)
inline fun <reified T> T.causeBug() {
val x = this
x is T
x as T
T::class
typeOf<T>()
Array<T>(1) { x }
}
interface SomeToImplement<SELF_TVAR>
class Y : SomeToImplement<Y>
class Something<T> where T: SomeToImplement<T> {
fun op() = causeBug()
}
fun box(): String {
Something<Y>().op()
return "OK"
}
@@ -26717,6 +26717,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt"); runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt");
} }
@TestMetadata("reifiedTypeArgumentWithRecursion.kt")
public void testReifiedTypeArgumentWithRecursion() throws Exception {
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithRecursion.kt");
}
@TestMetadata("safecast.kt") @TestMetadata("safecast.kt")
public void testSafecast() throws Exception { public void testSafecast() throws Exception {
runTest("compiler/testData/codegen/box/reified/safecast.kt"); runTest("compiler/testData/codegen/box/reified/safecast.kt");
@@ -25534,6 +25534,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt"); runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt");
} }
@TestMetadata("reifiedTypeArgumentWithRecursion.kt")
public void testReifiedTypeArgumentWithRecursion() throws Exception {
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithRecursion.kt");
}
@TestMetadata("safecast.kt") @TestMetadata("safecast.kt")
public void testSafecast() throws Exception { public void testSafecast() throws Exception {
runTest("compiler/testData/codegen/box/reified/safecast.kt"); runTest("compiler/testData/codegen/box/reified/safecast.kt");
@@ -25201,6 +25201,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt"); runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt");
} }
@TestMetadata("reifiedTypeArgumentWithRecursion.kt")
public void testReifiedTypeArgumentWithRecursion() throws Exception {
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithRecursion.kt");
}
@TestMetadata("safecast.kt") @TestMetadata("safecast.kt")
public void testSafecast() throws Exception { public void testSafecast() throws Exception {
runTest("compiler/testData/codegen/box/reified/safecast.kt"); runTest("compiler/testData/codegen/box/reified/safecast.kt");
@@ -20592,6 +20592,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt"); runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt");
} }
@TestMetadata("reifiedTypeArgumentWithRecursion.kt")
public void testReifiedTypeArgumentWithRecursion() throws Exception {
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithRecursion.kt");
}
@TestMetadata("safecast.kt") @TestMetadata("safecast.kt")
public void testSafecast() throws Exception { public void testSafecast() throws Exception {
runTest("compiler/testData/codegen/box/reified/safecast.kt"); runTest("compiler/testData/codegen/box/reified/safecast.kt");
@@ -20652,6 +20652,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt"); runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithIntersectionTypeAsTypeArgument.kt");
} }
@TestMetadata("reifiedTypeArgumentWithRecursion.kt")
public void testReifiedTypeArgumentWithRecursion() throws Exception {
runTest("compiler/testData/codegen/box/reified/reifiedTypeArgumentWithRecursion.kt");
}
@TestMetadata("safecast.kt") @TestMetadata("safecast.kt")
public void testSafecast() throws Exception { public void testSafecast() throws Exception {
runTest("compiler/testData/codegen/box/reified/safecast.kt"); runTest("compiler/testData/codegen/box/reified/safecast.kt");
@@ -27,12 +27,16 @@ object TypeOfFIF : FunctionIntrinsicFactory {
object Intrinsic : FunctionIntrinsic() { object Intrinsic : FunctionIntrinsic() {
override fun apply(callInfo: CallInfo, arguments: List<JsExpression>, context: TranslationContext): JsExpression { override fun apply(callInfo: CallInfo, arguments: List<JsExpression>, context: TranslationContext): JsExpression {
val type = callInfo.resolvedCall.typeArguments.values.single() val type = callInfo.resolvedCall.typeArguments.values.single()
return KTypeConstructor(context).createKType(type) return context.createKType(type)
} }
} }
} }
class KTypeConstructor(val context: TranslationContext) { fun TranslationContext.createKType(type: KotlinType): JsExpression = KTypeConstructor(this).createKType(type)
private class KTypeConstructor(private val context: TranslationContext) {
private val visitedTypeParams = hashSetOf<TypeParameterDescriptor>()
fun callHelperFunction(name: String, vararg arguments: JsExpression) = fun callHelperFunction(name: String, vararg arguments: JsExpression) =
JsInvocation(context.getReferenceToIntrinsic(name), *arguments) JsInvocation(context.getReferenceToIntrinsic(name), *arguments)
@@ -92,7 +96,7 @@ class KTypeConstructor(val context: TranslationContext) {
) )
} }
fun createKTypeProjection(tp: TypeProjection): JsExpression { private fun createKTypeProjection(tp: TypeProjection): JsExpression {
if (tp.isStarProjection) { if (tp.isStarProjection) {
return callHelperFunction(Namer.GET_START_KTYPE_PROJECTION) return callHelperFunction(Namer.GET_START_KTYPE_PROJECTION)
} }
@@ -108,32 +112,46 @@ class KTypeConstructor(val context: TranslationContext) {
} }
fun createKClassifier(classifier: ClassifierDescriptor): JsExpression = private fun createKClassifier(classifier: ClassifierDescriptor): JsExpression =
when (classifier) { when (classifier) {
is TypeParameterDescriptor -> createKTypeParameter(classifier) is TypeParameterDescriptor -> createKTypeParameter(classifier, visitedTypeParams)
else -> ExpressionVisitor.getObjectKClass(context, classifier) else -> ExpressionVisitor.getObjectKClass(context, classifier)
} }
fun createKTypeParameter(typeParameter: TypeParameterDescriptor): JsExpression { private fun createKTypeParameter(
val name = JsStringLiteral(typeParameter.name.asString()) typeParameter: TypeParameterDescriptor,
val upperBounds = JsArrayLiteral(typeParameter.upperBounds.map { createKType(it) }) visitedTypeParams: MutableSet<TypeParameterDescriptor>
val variance = when (typeParameter.variance) { ): JsExpression {
Variance.INVARIANT -> JsStringLiteral("invariant") fun createKTypeParameterImpl(): JsExpression {
Variance.IN_VARIANCE -> JsStringLiteral("in") val name = JsStringLiteral(typeParameter.name.asString())
Variance.OUT_VARIANCE -> JsStringLiteral("out") val upperBounds = JsArrayLiteral(typeParameter.upperBounds.map { createKType(it) })
} val variance = when (typeParameter.variance) {
if (typeParameter.isReified) { //// TODO use consts, enums, numbers???
val kClassName = context.getNameForIntrinsic(SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE.suggestedName) Variance.INVARIANT -> JsStringLiteral("invariant")
kClassName.specialFunction = SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE Variance.IN_VARIANCE -> JsStringLiteral("in")
Variance.OUT_VARIANCE -> JsStringLiteral("out")
}
if (typeParameter.isReified) {
val kClassName = context.getNameForIntrinsic(SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE.suggestedName)
kClassName.specialFunction = SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE
return JsInvocation(kClassName.makeRef(), getReferenceToJsClass(typeParameter, context)) return JsInvocation(kClassName.makeRef(), getReferenceToJsClass(typeParameter, context))
}
return callHelperFunction(
Namer.CREATE_KTYPE_PARAMETER,
name,
upperBounds,
variance
)
} }
return callHelperFunction( if (typeParameter in visitedTypeParams) return callHelperFunction(Namer.GET_START_KTYPE_PROJECTION)
Namer.CREATE_KTYPE_PARAMETER,
name, visitedTypeParams.add(typeParameter)
upperBounds,
variance return createKTypeParameterImpl().also {
) visitedTypeParams.remove(typeParameter)
}
} }
} }
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.KTypeConstructor import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.createKType
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -128,7 +128,7 @@ fun <T, S> List<T>.splitToRanges(classifier: (T) -> S): List<Pair<List<T>, S>> {
fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsExpression = fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsExpression =
getReferenceToJsClassOrArray(type, context).also { getReferenceToJsClassOrArray(type, context).also {
it.kType = KTypeConstructor(context).createKType(type) it.kType = context.createKType(type)
} }
fun getReferenceToJsClassOrArray(type: KotlinType, context: TranslationContext): JsExpression { fun getReferenceToJsClassOrArray(type: KotlinType, context: TranslationContext): JsExpression {