From 7cf8697e3094dfbd3e8dfcc1324940b41882e51b Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Thu, 5 Mar 2020 13:10:26 +0300 Subject: [PATCH] [JS BEs] use star projection when type parameter used recursively #KT-37128 Fixed --- .../ir/FirBlackBoxCodegenTestGenerated.java | 5 ++ .../js/lower/ClassReferenceLowering.kt | 35 ++++++---- .../reifiedTypeArgumentWithRecursion.kt | 33 ++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 ++ .../LightAnalysisModeTestGenerated.java | 5 ++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 ++ .../IrJsCodegenBoxTestGenerated.java | 5 ++ .../semantics/JsCodegenBoxTestGenerated.java | 5 ++ .../functions/factories/TypeOfFIF.kt | 64 ++++++++++++------- .../kotlin/js/translate/utils/utils.kt | 4 +- 10 files changed, 127 insertions(+), 39 deletions(-) create mode 100644 compiler/testData/codegen/box/reified/reifiedTypeArgumentWithRecursion.kt diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 1bf95f3d97d..5893875ee3e 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -25201,6 +25201,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT 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") public void testSafecast() throws Exception { runTest("compiler/testData/codegen/box/reified/safecast.kt"); diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ClassReferenceLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ClassReferenceLowering.kt index b80160fbba8..8385567a2e5 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ClassReferenceLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ClassReferenceLowering.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. */ @@ -136,9 +136,10 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass } } - fun createKType(type: IrType): IrExpression { + private fun createKType(type: IrType, visitedTypeParams: MutableSet): IrExpression { + if (type is IrSimpleType) - return createSimpleKType(type) + return createSimpleKType(type, visitedTypeParams) if (type is IrDynamicType) return createDynamicType() error("Unexpected type $type") @@ -148,7 +149,7 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass return buildCall(context.intrinsics.createDynamicKType!!) } - private fun createSimpleKType(type: IrSimpleType): IrExpression { + private fun createSimpleKType(type: IrSimpleType, visitedTypeParams: MutableSet): IrExpression { val classifier: IrClassifierSymbol = type.classifier // TODO: Check why do we have un-substituted reified parameters @@ -156,9 +157,9 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass // error("Fail") // } - val kClassifier = createKClassifier(classifier) + val kClassifier = createKClassifier(classifier, visitedTypeParams) // TODO: Use static array types - val arguments = type.arguments.map { createKTypeProjection(it) }.toJsArrayLiteral( + val arguments = type.arguments.map { createKTypeProjection(it, visitedTypeParams) }.toJsArrayLiteral( context, 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): IrExpression { if (tp !is IrTypeProjection) { return buildCall(context.intrinsics.getStarKTypeProjection!!) } @@ -183,20 +184,24 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass Variance.OUT_VARIANCE -> context.intrinsics.createCovariantKTypeProjection!! } - val kType = createKType(tp.type) + val kType = createKType(tp.type, visitedTypeParams) return buildCall(factoryName, kType) } - private fun createKClassifier(classifier: IrClassifierSymbol): IrExpression = + private fun createKClassifier(classifier: IrClassifierSymbol, visitedTypeParams: MutableSet): IrExpression = when (classifier) { - is IrTypeParameterSymbol -> createKTypeParameter(classifier.owner) + is IrTypeParameterSymbol -> createKTypeParameter(classifier.owner, visitedTypeParams) else -> callGetKClass(typeArgument = classifier.defaultType) } - private fun createKTypeParameter(typeParameter: IrTypeParameter): IrExpression { + private fun createKTypeParameter(typeParameter: IrTypeParameter, visitedTypeParams: MutableSet): IrExpression { + if (typeParameter in visitedTypeParams) return buildCall(context.intrinsics.getStarKTypeProjection!!) + + visitedTypeParams.add(typeParameter) + 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.dynamicType, context.dynamicType @@ -218,7 +223,9 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass name, upperBounds, variance - ) + ).also { + visitedTypeParams.remove(typeParameter) + } } override fun lower(irBody: IrBody, container: IrDeclaration) { @@ -238,7 +245,7 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass override fun visitCall(expression: IrCall): IrExpression = if (Symbols.isTypeOfIntrinsic(expression.symbol)) { - createKType(expression.getTypeArgument(0)!!) + createKType(expression.getTypeArgument(0)!!, hashSetOf()) } else { super.visitCall(expression) } diff --git a/compiler/testData/codegen/box/reified/reifiedTypeArgumentWithRecursion.kt b/compiler/testData/codegen/box/reified/reifiedTypeArgumentWithRecursion.kt new file mode 100644 index 00000000000..f16882b55fb --- /dev/null +++ b/compiler/testData/codegen/box/reified/reifiedTypeArgumentWithRecursion.kt @@ -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 T.causeBug() { + val x = this + x is T + x as T + T::class + typeOf() + Array(1) { x } +} + +interface SomeToImplement + +class Y : SomeToImplement + +class Something where T: SomeToImplement { + fun op() = causeBug() +} + +fun box(): String { + Something().op() + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 44292af1444..11f6f95bdcd 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -26717,6 +26717,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { 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") public void testSafecast() throws Exception { runTest("compiler/testData/codegen/box/reified/safecast.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 22c44214312..2e2cb19d0ab 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -25534,6 +25534,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes 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") public void testSafecast() throws Exception { runTest("compiler/testData/codegen/box/reified/safecast.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index e02997f1fb4..eef771d1479 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -25201,6 +25201,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes 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") public void testSafecast() throws Exception { runTest("compiler/testData/codegen/box/reified/safecast.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 8b0aeed0f8b..6bec1f7be8c 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -20592,6 +20592,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { 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") public void testSafecast() throws Exception { runTest("compiler/testData/codegen/box/reified/safecast.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 87734f59bfc..ad1e392ae59 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -20652,6 +20652,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { 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") public void testSafecast() throws Exception { runTest("compiler/testData/codegen/box/reified/safecast.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TypeOfFIF.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TypeOfFIF.kt index 22b6f4c5059..578ab341033 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TypeOfFIF.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TypeOfFIF.kt @@ -27,12 +27,16 @@ object TypeOfFIF : FunctionIntrinsicFactory { object Intrinsic : FunctionIntrinsic() { override fun apply(callInfo: CallInfo, arguments: List, context: TranslationContext): JsExpression { 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() + fun callHelperFunction(name: String, vararg arguments: JsExpression) = 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) { 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) { - is TypeParameterDescriptor -> createKTypeParameter(classifier) + is TypeParameterDescriptor -> createKTypeParameter(classifier, visitedTypeParams) else -> ExpressionVisitor.getObjectKClass(context, classifier) } - fun createKTypeParameter(typeParameter: TypeParameterDescriptor): JsExpression { - val name = JsStringLiteral(typeParameter.name.asString()) - val upperBounds = JsArrayLiteral(typeParameter.upperBounds.map { createKType(it) }) - val variance = when (typeParameter.variance) { - Variance.INVARIANT -> JsStringLiteral("invariant") - 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 + private fun createKTypeParameter( + typeParameter: TypeParameterDescriptor, + visitedTypeParams: MutableSet + ): JsExpression { + fun createKTypeParameterImpl(): JsExpression { + val name = JsStringLiteral(typeParameter.name.asString()) + val upperBounds = JsArrayLiteral(typeParameter.upperBounds.map { createKType(it) }) + val variance = when (typeParameter.variance) { + //// TODO use consts, enums, numbers??? + Variance.INVARIANT -> JsStringLiteral("invariant") + 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( - Namer.CREATE_KTYPE_PARAMETER, - name, - upperBounds, - variance - ) + if (typeParameter in visitedTypeParams) return callHelperFunction(Namer.GET_START_KTYPE_PROJECTION) + + visitedTypeParams.add(typeParameter) + + return createKTypeParameterImpl().also { + visitedTypeParams.remove(typeParameter) + } } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt index 81879f828e1..f8833f7c8bf 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt @@ -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.expression.ExpressionVisitor 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.utils.TranslationUtils.simpleReturnFunction import org.jetbrains.kotlin.psi.* @@ -128,7 +128,7 @@ fun List.splitToRanges(classifier: (T) -> S): List, S>> { fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsExpression = getReferenceToJsClassOrArray(type, context).also { - it.kType = KTypeConstructor(context).createKType(type) + it.kType = context.createKType(type) } fun getReferenceToJsClassOrArray(type: KotlinType, context: TranslationContext): JsExpression {