From df6635085bd2412ce6524a2f4b7bcb6795739e9e Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Tue, 8 Dec 2020 16:52:39 +0300 Subject: [PATCH] [JS IR] Replace calls with invalid type arguments for type parameters with call to `errorCode` function from runtime. --- .../backend/js/lower/calls/CallsLowering.kt | 3 +- ...InvalidTypeArgumentForReifiedParameters.kt | 53 +++++++++++ .../semantic/reifiedWithWrongArguments.kt | 91 +++++++++++++++++++ .../IrJsCodegenBoxErrorTestGenerated.java | 5 + .../stdlib/js-ir/src/kotlin/jsClass_js-ir.kt | 2 +- 5 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/ReplaceCallsWithInvalidTypeArgumentForReifiedParameters.kt create mode 100644 compiler/testData/codegen/boxError/semantic/reifiedWithWrongArguments.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLowering.kt index a77455da59d..20d3ff41a5c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLowering.kt @@ -16,8 +16,6 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.ir.visitors.IrElementTransformer -import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid class CallsLowering(val context: JsIrBackendContext) : BodyLoweringPass { private val transformers = listOf( @@ -32,6 +30,7 @@ class CallsLowering(val context: JsIrBackendContext) : BodyLoweringPass { BuiltInConstructorCalls(context), JsonIntrinsics(context), NativeGetterSetterTransformer(context), + ReplaceCallsWithInvalidTypeArgumentForReifiedParameters(context), ) override fun lower(irBody: IrBody, container: IrDeclaration) { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/ReplaceCallsWithInvalidTypeArgumentForReifiedParameters.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/ReplaceCallsWithInvalidTypeArgumentForReifiedParameters.kt new file mode 100644 index 00000000000..35637c6ad82 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/ReplaceCallsWithInvalidTypeArgumentForReifiedParameters.kt @@ -0,0 +1,53 @@ +/* + * 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. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower.calls + +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.util.getArgumentsWithIr +import org.jetbrains.kotlin.ir.util.render + + +class ReplaceCallsWithInvalidTypeArgumentForReifiedParameters(val context: JsIrBackendContext) : CallsTransformer { + override fun transformFunctionAccess(call: IrFunctionAccessExpression, doNotIntrinsify: Boolean): IrExpression { + if (!context.errorPolicy.allowErrors) return call + + val function = call.symbol.owner + + for (typeParameter in function.typeParameters) { + if (!typeParameter.isReified) continue + val typeArgument = call.getTypeArgument(typeParameter.index) + + if (typeArgument?.classOrNull == null) { + val args = call.getArgumentsWithIr().map { it.second } + + val callErrorCode = JsIrBuilder.buildCall(context.errorCodeSymbol!!).apply { + putValueArgument( + 0, + IrConstImpl.string( + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + context.irBuiltIns.stringType, + "Invalid type argument (${typeArgument?.render()}) for reified type parameter (${typeParameter.render()})" + ) + ) + } + + if (args.isEmpty()) return callErrorCode + + return IrCompositeImpl(-1, -1, call.type, call.origin, args + listOf(callErrorCode)) + } + } + + return call + } +} diff --git a/compiler/testData/codegen/boxError/semantic/reifiedWithWrongArguments.kt b/compiler/testData/codegen/boxError/semantic/reifiedWithWrongArguments.kt new file mode 100644 index 00000000000..abd2ad617d6 --- /dev/null +++ b/compiler/testData/codegen/boxError/semantic/reifiedWithWrongArguments.kt @@ -0,0 +1,91 @@ +// ERROR_POLICY: SEMANTIC + +// MODULE: lib +// FILE: t.kt +import kotlin.reflect.KClass + +var l = "" +fun log(s: String, r: Any? = null): Any? { + l += s + ";" + return r +} + +inline fun assertFails(block: () -> Unit) { + try { + block() + } catch (e: Throwable) { + return + } + + fail("Expected an exception to be thrown, but was completed successfully.") +} + +fun getKClassFromRaifed(b: Boolean): KClass? { + log("getKClassFromRaifed($b)") + if (b) return R::class + log("getKClassFromRaifed: null") + return null +} + +fun getKClassFromT(b: Boolean): KClass? { + log("getKClassFromT($b)") + if (b) return T::class + log("getKClassFromT: null") + return null +} + +fun getKClassFromErrorType(b: Boolean): KClass<*>? { + log("getKClassFromErrorType($b)") + if (b) return ErrT::class + log("getKClassFromErrorType: null") + return null +} + +inline fun test1() { + assertFails { getKClassFromRaifed(true) } + assertFails { getKClassFromRaifed(true) } + assertFails { getKClassFromRaifed(true) } + assertFails { getKClassFromT(true) } + assertFails { getKClassFromT(true) } + assertFails { getKClassFromT(true) } + assertFails { getKClassFromErrorType(true) } + getKClassFromRaifed(false) + getKClassFromRaifed(false) + getKClassFromRaifed(false) + getKClassFromT(false) + getKClassFromT(false) + getKClassFromT(false) + getKClassFromErrorType(false) +} + +inline fun foo(a: Any, b: Any, c: Any): KClass<*>? { + log("foo") + return R::class +} + +fun testSideEffects() { + assertFails { foo(log("1", 1), log("2", 2), log("3", 3)) } + foo(log("a", 1), log("b", 2), log("c", 3)) +} + +// MODULE: main(lib) +// FILE: b.kt + +fun box(): String { + test1() + testSideEffects() + + + val expected = "getKClassFromRaifed(true);getKClassFromRaifed(true);getKClassFromRaifed(true);" + + "getKClassFromT(true);getKClassFromT(true);getKClassFromT(true);" + + "getKClassFromErrorType(true);" + + "getKClassFromRaifed(false);getKClassFromRaifed: null;getKClassFromRaifed(false);getKClassFromRaifed: null;getKClassFromRaifed(false);getKClassFromRaifed: null;" + + "getKClassFromT(false);getKClassFromT: null;getKClassFromT(false);getKClassFromT: null;getKClassFromT(false);getKClassFromT: null;" + + "getKClassFromErrorType(false);getKClassFromErrorType: null;" + + "1;2;3;foo;a;b;c;foo;" + + if (l != expected) + return "l = $l" + + return "OK" +} diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxErrorTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxErrorTestGenerated.java index 8ddba725597..f87eb67f83e 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxErrorTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxErrorTestGenerated.java @@ -72,6 +72,11 @@ public class IrJsCodegenBoxErrorTestGenerated extends AbstractIrJsCodegenBoxErro runTest("compiler/testData/codegen/boxError/semantic/reifiedNonInline.kt"); } + @TestMetadata("reifiedWithWrongArguments.kt") + public void testReifiedWithWrongArguments() throws Exception { + runTest("compiler/testData/codegen/boxError/semantic/reifiedWithWrongArguments.kt"); + } + @TestMetadata("typeMismatch.kt") public void testTypeMismatch() throws Exception { runTest("compiler/testData/codegen/boxError/semantic/typeMismatch.kt"); diff --git a/libraries/stdlib/js-ir/src/kotlin/jsClass_js-ir.kt b/libraries/stdlib/js-ir/src/kotlin/jsClass_js-ir.kt index 1524617027c..a538c7268d8 100644 --- a/libraries/stdlib/js-ir/src/kotlin/jsClass_js-ir.kt +++ b/libraries/stdlib/js-ir/src/kotlin/jsClass_js-ir.kt @@ -5,4 +5,4 @@ package kotlin.js -internal external fun jsClass(): JsClass +internal inline fun jsClass(): JsClass = throw NotImplementedError("Implemented as intrinsic")