From 05cb0e8994158c5a96364c65721b37029b3b6d14 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 20 Apr 2020 14:45:18 +0300 Subject: [PATCH] KNVE: support in JVM_IR and JS_IR --- .../jetbrains/kotlin/backend/common/ir/Ir.kt | 3 + .../KotlinNothingValueExceptionLowering.kt | 71 +++++++++++++++++++ .../ir/backend/js/JsIrBackendContext.kt | 6 +- .../kotlin/ir/backend/js/JsLoweringPhases.kt | 7 ++ .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 13 +++- .../kotlin/backend/jvm/JvmSymbols.kt | 21 ++++-- .../backend/jvm/codegen/ExpressionCodegen.kt | 2 - .../jvm/intrinsics/IrIntrinsicMethods.kt | 3 +- .../ThrowKotlinNothingValueException.kt | 29 ++++++++ .../kotlin/backend/wasm/WasmSymbols.kt | 2 + .../box/nothingValue/nothingValueException.kt | 3 +- libraries/stdlib/js-ir/runtime/hacks.kt | 4 ++ 12 files changed, 150 insertions(+), 14 deletions(-) create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/KotlinNothingValueExceptionLowering.kt create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ThrowKotlinNothingValueException.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt index 5ab10f2f09b..b4b181900ea 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt @@ -232,6 +232,9 @@ abstract class Symbols(val context: T, symbolTable abstract val ThrowUninitializedPropertyAccessException: IrSimpleFunctionSymbol + open val ThrowKotlinNothingValueException: IrSimpleFunctionSymbol + get() = TODO("Support KotlinNothingValueException in Kotlin/Native and make this val abstract") + abstract val stringBuilder: IrClassSymbol abstract val defaultConstructorMarker: IrClassSymbol diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/KotlinNothingValueExceptionLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/KotlinNothingValueExceptionLowering.kt new file mode 100644 index 00000000000..b80b3ab48d9 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/KotlinNothingValueExceptionLowering.kt @@ -0,0 +1,71 @@ +/* + * 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.backend.common.lower + +import org.jetbrains.kotlin.backend.common.BodyLoweringPass +import org.jetbrains.kotlin.backend.common.CommonBackendContext +import org.jetbrains.kotlin.ir.builders.irCall +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrSymbolDeclaration +import org.jetbrains.kotlin.ir.expressions.IrBody +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.types.isNothing +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid + +class KotlinNothingValueExceptionLowering(val backendContext: CommonBackendContext) : BodyLoweringPass { + override fun lower(irBody: IrBody, container: IrDeclaration) { + irBody.transformChildrenVoid( + Transformer((container as IrSymbolDeclaration<*>).symbol) + ) + } + + private inner class Transformer(val parent: IrSymbol) : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression = + if (expression.type.isNothing()) { + // Replace call 'foo' of type 'kotlin.Nothing' with a block: + // + // { + // [ call 'foo' with type: 'kotlin.Unit' ] + // call ThrowKotlinNothingValueException(): Nothing + // }: Nothing + // + // Changing type of 'foo' to 'kotlin.Unit' is requires so that the 'ThrowKotlinNothingValueException(): Nothing' + // is not considered dead code and is not removed. + // Note that type 'kotlin.Nothing' might be inferred in some cases of projected types. + // See KT-30330 for an example of such code where call of type 'kotlin.Nothing' terminates and produces some value + // (although doing so by subverting the type system). + backendContext.createIrBuilder(parent, expression.startOffset, expression.endOffset).run { + irBlock(expression, null, context.irBuiltIns.nothingType) { + +changeTypeToUnit(expression) + +irCall(backendContext.ir.symbols.ThrowKotlinNothingValueException) + } + } + } else { + expression + } + + private fun changeTypeToUnit(call: IrCall): IrCall = + IrCallImpl( + call.startOffset, call.endOffset, + backendContext.irBuiltIns.unitType, + call.symbol, + call.typeArgumentsCount, call.valueArgumentsCount, call.origin, call.superQualifierSymbol + ).also { newCall -> + for (i in 0 until call.typeArgumentsCount) { + newCall.putTypeArgument(i, call.getTypeArgument(i)) + } + newCall.dispatchReceiver = call.dispatchReceiver + newCall.extensionReceiver = call.extensionReceiver + for (i in 0 until call.valueArgumentsCount) { + newCall.putValueArgument(i, call.getValueArgument(i)) + } + } + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index d089cbf1b86..4a637272b85 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -194,6 +194,9 @@ class JsIrBackendContext( override val ThrowUninitializedPropertyAccessException = symbolTable.referenceSimpleFunction(getFunctions(FqName("kotlin.throwUninitializedPropertyAccessException")).single()) + override val ThrowKotlinNothingValueException: IrSimpleFunctionSymbol = + symbolTable.referenceSimpleFunction(getFunctions(FqName("kotlin.throwKotlinNothingValueException")).single()) + override val defaultConstructorMarker = symbolTable.referenceClass(context.getJsInternalClass("DefaultConstructorMarker")) @@ -215,7 +218,8 @@ class JsIrBackendContext( override val coroutineContextGetter = symbolTable.referenceSimpleFunction(context.coroutineContextProperty.getter!!) - override val suspendCoroutineUninterceptedOrReturn = symbolTable.referenceSimpleFunction(getJsInternalFunction(COROUTINE_SUSPEND_OR_RETURN_JS_NAME)) + override val suspendCoroutineUninterceptedOrReturn = + symbolTable.referenceSimpleFunction(getJsInternalFunction(COROUTINE_SUSPEND_OR_RETURN_JS_NAME)) override val coroutineGetContext = symbolTable.referenceSimpleFunction(getJsInternalFunction(GET_COROUTINE_CONTEXT_NAME)) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index d3bd73ea95e..c5812997620 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -159,6 +159,12 @@ private val lateinitUsageLoweringPhase = makeBodyLoweringPhase( description = "Insert checks for lateinit field references" ) +private val kotlinNothingValueExceptionPhase = makeBodyLoweringPhase( + ::KotlinNothingValueExceptionLowering, + name = "KotlinNothingValueException", + description = "Throw proper exception for calls returning value of type 'kotlin.Nothing'" +) + private val stripTypeAliasDeclarationsPhase = makeDeclarationTransformerPhase( { StripTypeAliasDeclarationsLowering() }, name = "StripTypeAliasDeclarations", @@ -638,6 +644,7 @@ val loweringList = listOf( annotationConstructorLowering, initializersLoweringPhase, initializersCleanupLoweringPhase, + kotlinNothingValueExceptionPhase, // Common prefix ends enumEntryInstancesLoweringPhase, enumEntryInstancesBodyLoweringPhase, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 1b7cea2f382..9c124c20b98 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -118,7 +118,8 @@ internal val localDeclarationsPhase = makeIrFilePhase( override fun forClass(declaration: IrClass, inInlineFunctionScope: Boolean): Visibility = if (declaration.origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL || declaration.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL || - declaration.origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE) { + declaration.origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE + ) { scopedVisibility(inInlineFunctionScope) } else { declaration.visibility @@ -255,12 +256,17 @@ private val syntheticAccessorPhase = makeIrFilePhase( prerequisite = setOf(objectClassPhase, staticDefaultFunctionPhase, interfacePhase) ) -private val tailrecPhase = makeIrFilePhase( +private val tailrecPhase = makeIrFilePhase( ::JvmTailrecLowering, name = "Tailrec", description = "Handle tailrec calls" ) +private val kotlinNothingValueExceptionPhase = makeIrFilePhase( + ::KotlinNothingValueExceptionLowering, + name = "KotlinNothingValueException", + description = "Throw proper exception for calls returning value of type 'kotlin.Nothing'" +) @Suppress("Reformat") private val jvmFilePhases = @@ -353,10 +359,11 @@ private val jvmFilePhases = jvmArgumentNullabilityAssertions then toArrayPhase then jvmOptimizationLoweringPhase then - ifNullExpressionsFusionPhase then + ifNullExpressionsFusionPhase then additionalClassAnnotationPhase then typeOperatorLowering then replaceKFunctionInvokeWithFunctionInvokePhase then + kotlinNothingValueExceptionPhase then checkLocalNamesWithOldBackendPhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt index 6d791f4dd81..6799e4cd62b 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt @@ -3,6 +3,8 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ +@file:Suppress("PropertyName") + package org.jetbrains.kotlin.backend.jvm import org.jetbrains.kotlin.backend.common.ir.Symbols @@ -21,7 +23,6 @@ import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin -import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrPackageFragment import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.symbols.* @@ -76,11 +77,12 @@ class JvmSymbols( override val ThrowTypeCastException: IrFunctionSymbol = typeCastExceptionClass.constructors.single() - private val unsupportedOperationExceptionClass: IrClassSymbol = createClass(FqName("java.lang.UnsupportedOperationException")) { klass -> - klass.addConstructor().apply { - addValueParameter("message", irBuiltIns.stringType.makeNullable()) + private val unsupportedOperationExceptionClass: IrClassSymbol = + createClass(FqName("java.lang.UnsupportedOperationException")) { klass -> + klass.addConstructor().apply { + addValueParameter("message", irBuiltIns.stringType.makeNullable()) + } } - } val ThrowUnsupportOperationExceptionClass: IrFunctionSymbol = unsupportedOperationExceptionClass.constructors.single() @@ -679,6 +681,15 @@ class JvmSymbols( val runSuspendFunction: IrSimpleFunctionSymbol = kotlinCoroutinesJvmInternalRunSuspendKt.functionByName("runSuspend") + + override val ThrowKotlinNothingValueException: IrSimpleFunctionSymbol = + buildFun { + name = Name.identifier("ThrowKotlinNothingValueException") + origin = IrDeclarationOrigin.IR_BUILTINS_STUB + returnType = irBuiltIns.nothingType + }.apply { + parent = kotlinJvmInternalPackage + }.symbol } private fun IrClassSymbol.functionByName(name: String): IrSimpleFunctionSymbol = diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 4d1a9a54411..7b728f2c440 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -451,8 +451,6 @@ class ExpressionCodegen( return when { expression.type.isNothing() -> { - mv.aconst(null) - mv.athrow() unitValue } expression is IrConstructorCall -> diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt index 0909b3b85f5..2961402d4c4 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt @@ -112,7 +112,8 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) { irBuiltIns.dataClassArrayMemberToStringSymbol.toKey()!! to IrDataClassArrayMemberToString, symbols.unsafeCoerceIntrinsic.toKey()!! to UnsafeCoerce, symbols.signatureStringIntrinsic.toKey()!! to SignatureString, - symbols.reassignParameterIntrinsic.toKey()!! to ReassignParameter + symbols.reassignParameterIntrinsic.toKey()!! to ReassignParameter, + symbols.ThrowKotlinNothingValueException.toKey()!! to ThrowKotlinNothingValueException ) + numberConversionMethods() + unaryFunForPrimitives("plus", UnaryPlus) + diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ThrowKotlinNothingValueException.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ThrowKotlinNothingValueException.kt new file mode 100644 index 00000000000..3d30cadbbf8 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ThrowKotlinNothingValueException.kt @@ -0,0 +1,29 @@ +/* + * 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.backend.jvm.intrinsics + +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.org.objectweb.asm.Type + +object ThrowKotlinNothingValueException : IntrinsicMethod() { + override fun toCallable( + expression: IrFunctionAccessExpression, + signature: JvmMethodSignature, + context: JvmBackendContext + ): IrIntrinsicFunction = + IrIntrinsicFunction.create(expression, signature, context) { mv -> + if (context.state.useKotlinNothingValueException) { + mv.anew(Type.getObjectType("kotlin/KotlinNothingValueException")) + mv.dup() + mv.invokespecial("kotlin/KotlinNothingValueException", "", "()V", false) + } else { + mv.aconst(null) + } + mv.athrow() + } +} \ No newline at end of file diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt index 244ddfe9978..8cb9ab7b34c 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt @@ -34,6 +34,8 @@ class WasmSymbols( get() = TODO() override val ThrowUninitializedPropertyAccessException get() = TODO() + override val ThrowKotlinNothingValueException: IrSimpleFunctionSymbol + get() = TODO() override val defaultConstructorMarker get() = TODO() override val stringBuilder diff --git a/compiler/testData/codegen/box/nothingValue/nothingValueException.kt b/compiler/testData/codegen/box/nothingValue/nothingValueException.kt index 1fd3671ed40..9db59b442df 100644 --- a/compiler/testData/codegen/box/nothingValue/nothingValueException.kt +++ b/compiler/testData/codegen/box/nothingValue/nothingValueException.kt @@ -1,5 +1,4 @@ -// IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS, JS_IR, JVM_IR, NATIVE +// IGNORE_BACKEND: JS, NATIVE fun something(): T = Any() as T diff --git a/libraries/stdlib/js-ir/runtime/hacks.kt b/libraries/stdlib/js-ir/runtime/hacks.kt index 5ad0bfc94c4..0ef97818825 100644 --- a/libraries/stdlib/js-ir/runtime/hacks.kt +++ b/libraries/stdlib/js-ir/runtime/hacks.kt @@ -9,6 +9,10 @@ package kotlin internal fun throwUninitializedPropertyAccessException(name: String): Nothing = throw UninitializedPropertyAccessException("lateinit property $name has not been initialized") +@PublishedApi +internal fun throwKotlinNothingValueException(): Nothing = + throw KotlinNothingValueException() + internal fun noWhenBranchMatchedException(): Nothing = throw NoWhenBranchMatchedException()