diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index fdcf73fd988..d418bb60447 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -2324,6 +2324,8 @@ public class ExpressionCodegen extends KtVisitor impleme callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this); if (isSuspendCall) { + addReturnsUnitMarkerIfNecessary(v, resolvedCall); + addSuspendMarker(v, false); addInlineMarker(v, false); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt index b3f469ec073..0eb01394e52 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -77,6 +77,8 @@ class CoroutineTransformerMethodVisitor( FixStackMethodTransformer().transform(containingClassInternalName, methodNode) if (isForNamedFunction) { + ReturnUnitMethodTransformer.transform(containingClassInternalName, methodNode) + if (allSuspensionPointsAreTailCalls(containingClassInternalName, methodNode, suspensionPoints)) { dropSuspensionMarkers(methodNode, suspensionPoints) return @@ -701,7 +703,7 @@ private fun allSuspensionPointsAreTailCalls( } } -private class IgnoringCopyOperationSourceInterpreter : SourceInterpreter() { +internal class IgnoringCopyOperationSourceInterpreter : SourceInterpreter() { override fun copyOperation(insn: AbstractInsnNode?, value: SourceValue?) = value } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/ReturnUnitMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/ReturnUnitMethodTransformer.kt new file mode 100644 index 00000000000..693ec523be3 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/ReturnUnitMethodTransformer.kt @@ -0,0 +1,131 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.coroutines + +import org.jetbrains.kotlin.backend.common.pop +import org.jetbrains.kotlin.codegen.inline.isReturnsUnitMarker +import org.jetbrains.kotlin.codegen.optimization.boxing.isUnitInstance +import org.jetbrains.kotlin.codegen.optimization.common.ControlFlowGraph +import org.jetbrains.kotlin.codegen.optimization.common.asSequence +import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful +import org.jetbrains.kotlin.codegen.optimization.common.removeAll +import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.jetbrains.kotlin.utils.keysToMap +import org.jetbrains.kotlin.utils.sure +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.tree.* + +/* + * Replace POP with ARETURN iff + * 1) It is immediately followed by { GETSTATIC Unit.INSTANCE, ARETURN } sequences + * 2) It is poping Unit + */ +object ReturnUnitMethodTransformer : MethodTransformer() { + override fun transform(internalClassName: String, methodNode: MethodNode) { + val unitMarks = findReturnsUnitMarks(methodNode) + if (unitMarks.isEmpty()) return + + val units = findReturnUnitSequences(methodNode) + if (units.isEmpty()) { + cleanUpReturnsUnitMarkers(methodNode, unitMarks) + return + } + + val pops = methodNode.instructions.asSequence().filter { it.opcode == Opcodes.POP }.toList() + val popSuccessors = findSuccessors(methodNode, pops) + val sourceInsns = findSourceInstructions(internalClassName, methodNode, pops) + val safePops = filterOutUnsafes(popSuccessors, units, sourceInsns) + + // Replace POP with ARETURN for tail call optimization + safePops.forEach { methodNode.instructions.set(it, InsnNode(Opcodes.ARETURN)) } + cleanUpReturnsUnitMarkers(methodNode, unitMarks) + } + + // Return list of POPs, which can be safely replaced by ARETURNs + private fun filterOutUnsafes( + popSuccessors: Map>, + units: Collection, + sourceInsns: Map> + ): Collection { + return popSuccessors.filter { (pop, successors) -> + successors.all { it in units } && + sourceInsns[pop].sure { "Sources of $pop cannot be null" }.all(::isSuspendingCallReturningUnit) + }.keys + } + + // Find instructions which do something on stack, ignoring markers + // Return map {insn => list of found instructions} + private fun findSuccessors( + methodNode: MethodNode, + insns: List + ): Map> { + val cfg = ControlFlowGraph.build(methodNode) + return insns.keysToMap { findSuccessors(cfg, it, methodNode) } + } + + // Find all meaningful successors of [insn] + private fun findSuccessors(cfg: ControlFlowGraph, insn: AbstractInsnNode, methodNode: MethodNode): Collection { + val stack = cfg.getSuccessorsIndices(insn).mapTo(ArrayList()) { methodNode.instructions[it] } + val successors = arrayListOf() + while (stack.isNotEmpty()) { + val current = stack.pop() + if (current in successors || isReturnsUnitMarker(current)) continue + if (!current.isMeaningful || current is JumpInsnNode || current.opcode == Opcodes.NOP) { + cfg.getSuccessorsIndices(current).mapTo(stack) { methodNode.instructions[it] } + continue + } + // There can be multiple chains of { UnitInstance, POP } after inlining. Ignore them + if (current.isUnitInstance()) { + val newSuccessors = findSuccessors(cfg, current, methodNode) + if (newSuccessors.all { it.opcode == Opcodes.POP }) { + newSuccessors.flatMapTo(stack) { findSuccessors(cfg, it, methodNode) } + continue + } + } + successors.add(current) + } + return successors + } + + private fun isSuspendingCallReturningUnit(node: AbstractInsnNode): Boolean = + node.safeAs()?.next?.next?.let(::isReturnsUnitMarker) == true + + private fun findSourceInstructions( + internalClassName: String, + methodNode: MethodNode, + pops: Collection + ): Map> { + val frames = analyze(internalClassName, methodNode, IgnoringCopyOperationSourceInterpreter()) + return pops.keysToMap { + frames[methodNode.instructions.indexOf(it)].getStack(0).insns + } + } + + // Find { GETSTATIC kotlin/Unit.INSTANCE, ARETURN } sequences + // Result is list of GETSTATIC kotlin/Unit.INSTANCE instructions + private fun findReturnUnitSequences(methodNode: MethodNode): Collection = + methodNode.instructions.asSequence().filter { it.isUnitInstance() && it.next?.opcode == Opcodes.ARETURN }.toList() + + private fun findReturnsUnitMarks(methodNode: MethodNode): Collection = + methodNode.instructions.asSequence().filter(::isReturnsUnitMarker).toList() + + private fun cleanUpReturnsUnitMarkers(methodNode: MethodNode, unitMarks: Collection) { + unitMarks.forEach { methodNode.instructions.removeAll(listOf(it.previous, it)) } + } +} + diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt index 120548f44bc..5b393945934 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.codegen.inline import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.backend.common.descriptors.substitute import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.BaseExpressionCodegen @@ -28,6 +29,7 @@ import org.jetbrains.kotlin.codegen.context.CodegenContext import org.jetbrains.kotlin.codegen.context.CodegenContextUtil import org.jetbrains.kotlin.codegen.context.InlineLambdaContext import org.jetbrains.kotlin.codegen.context.MethodContext +import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction import org.jetbrains.kotlin.codegen.intrinsics.classId import org.jetbrains.kotlin.codegen.optimization.common.intConstant import org.jetbrains.kotlin.codegen.state.GenerationState @@ -43,14 +45,16 @@ import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes.ENUM_TYPE import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_CLASS_TYPE import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.resolve.source.PsiSourceElement import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor -import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.org.objectweb.asm.* import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.tree.* @@ -87,6 +91,7 @@ private const val INLINE_MARKER_FINALLY_START = "finallyStart" private const val INLINE_MARKER_FINALLY_END = "finallyEnd" private const val INLINE_MARKER_BEFORE_SUSPEND_ID = 0 private const val INLINE_MARKER_AFTER_SUSPEND_ID = 1 +private const val INLINE_MARKET_RETURNS_UNIT = 2 private val INTRINSIC_ARRAY_CONSTRUCTOR_TYPE = AsmUtil.asmTypeByClassId(classId) internal fun getMethodNode( @@ -386,6 +391,26 @@ internal fun addInlineMarker(v: InstructionAdapter, isStartNotEnd: Boolean) { ) } +internal fun addReturnsUnitMarkerIfNecessary(v: InstructionAdapter, resolvedCall: ResolvedCall<*>) { + val wrapperDescriptor = resolvedCall.candidateDescriptor.safeAs() ?: return + val unsubstitutedDescriptor = wrapperDescriptor.unwrapInitialDescriptorForSuspendFunction() + + val typeSubstitutor = TypeSubstitutor.create( + unsubstitutedDescriptor.typeParameters + .withIndex() + .associateBy({ it.value.typeConstructor }) { + TypeProjectionImpl(resolvedCall.typeArguments[wrapperDescriptor.typeParameters[it.index]] ?: return) + } + ) + + val substitutedDescriptor = unsubstitutedDescriptor.substitute(typeSubstitutor) ?: return + val returnType = substitutedDescriptor.returnType ?: return + + if (KotlinBuiltIns.isUnit(returnType)) { + addReturnsUnitMarker(v) + } +} + internal fun addSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean) { v.iconst(if (isStartNotEnd) INLINE_MARKER_BEFORE_SUSPEND_ID else INLINE_MARKER_AFTER_SUSPEND_ID) v.visitMethodInsn( @@ -395,8 +420,18 @@ internal fun addSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean) { ) } +private fun addReturnsUnitMarker(v: InstructionAdapter) { + v.iconst(INLINE_MARKET_RETURNS_UNIT) + v.visitMethodInsn( + Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME, + "mark", + "(I)V", false + ) +} + internal fun isBeforeSuspendMarker(insn: AbstractInsnNode) = isSuspendMarker(insn, INLINE_MARKER_BEFORE_SUSPEND_ID) internal fun isAfterSuspendMarker(insn: AbstractInsnNode) = isSuspendMarker(insn, INLINE_MARKER_AFTER_SUSPEND_ID) +internal fun isReturnsUnitMarker(insn: AbstractInsnNode) = isSuspendMarker(insn, INLINE_MARKET_RETURNS_UNIT) private fun isSuspendMarker(insn: AbstractInsnNode, id: Int) = isInlineMarker(insn, "mark") && insn.previous.intConstant == id diff --git a/compiler/testData/codegen/bytecodeListing/tailSuspendUnitFun.kt b/compiler/testData/codegen/bytecodeListing/tailSuspendUnitFun.kt new file mode 100644 index 00000000000..8dadb2eafc3 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/tailSuspendUnitFun.kt @@ -0,0 +1,61 @@ +suspend fun empty() {} +suspend fun withoutReturn() { empty() } +suspend fun withReturn() { return empty() } +suspend fun notTailCall() { empty(); empty() } +suspend fun lambdaAsParameter(c: suspend ()->Unit) { c() } +suspend fun lambdaAsParameterNotTailCall(c: suspend ()->Unit) { c(); c() } +suspend fun lambdaAsParameterReturn(c: suspend ()->Unit) { return c() } +suspend fun returnsInt() = 42 +// This should not be tail-call, since the caller should push Unit.INSTANCE on stack +suspend fun callsIntNotTailCall() { returnsInt() } +suspend fun multipleExitPoints(b: Boolean) { if (b) empty() else withoutReturn() } +suspend fun multipleExitPointsNotTailCall(b: Boolean) { if (b) empty() else returnsInt() } + +fun ordinary() = 1 +inline fun ordinaryInline() { ordinary() } +suspend fun multipleExitPointsWithOrdinaryInline(b: Boolean) { if (b) empty() else ordinaryInline() } + +suspend fun multipleExitPointsWhen(i: Int) { + when(i) { + 1 -> empty() + 2 -> withReturn() + 3 -> withoutReturn() + else -> lambdaAsParameter {} + } +} + +suspend fun generic(): T = TODO() +suspend fun useGenericReturningUnit() { + generic() +} + +class Generic { + suspend fun foo(): T = TODO() +} +suspend fun useGenericClass(g: Generic) { + g.foo() +} + +suspend fun genericInferType(c: () -> T): T = TODO() +suspend fun useGenericIngerType() { + genericInferType {} +} + +suspend fun nullableUnit(): Unit? = null +suspend fun useNullableUnit() { + nullableUnit() +} + +suspend fun useRunRunRunRunRun() { + run { + run { + run { + run { + run { + empty() + } + } + } + } + } +} diff --git a/compiler/testData/codegen/bytecodeListing/tailSuspendUnitFun.txt b/compiler/testData/codegen/bytecodeListing/tailSuspendUnitFun.txt new file mode 100644 index 00000000000..0b8e42d15f5 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/tailSuspendUnitFun.txt @@ -0,0 +1,115 @@ +@kotlin.Metadata +public final class Generic { + public method (): void + public final @org.jetbrains.annotations.Nullable method foo(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object +} + +@kotlin.Metadata +final class TailSuspendUnitFunKt$callsIntNotTailCall$1 { + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + inner class TailSuspendUnitFunKt$callsIntNotTailCall$1 + method (p0: kotlin.coroutines.experimental.Continuation): void + public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object + synthetic final method getLabel(): int + synthetic final method setLabel(p0: int): void +} + +@kotlin.Metadata +final class TailSuspendUnitFunKt$lambdaAsParameterNotTailCall$1 { + field L$0: java.lang.Object + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + inner class TailSuspendUnitFunKt$lambdaAsParameterNotTailCall$1 + method (p0: kotlin.coroutines.experimental.Continuation): void + public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object + synthetic final method getLabel(): int + synthetic final method setLabel(p0: int): void +} + +@kotlin.Metadata +final class TailSuspendUnitFunKt$multipleExitPointsNotTailCall$1 { + field Z$0: boolean + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + inner class TailSuspendUnitFunKt$multipleExitPointsNotTailCall$1 + method (p0: kotlin.coroutines.experimental.Continuation): void + public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object + synthetic final method getLabel(): int + synthetic final method setLabel(p0: int): void +} + +@kotlin.Metadata +final class TailSuspendUnitFunKt$multipleExitPointsWhen$2 { + inner class TailSuspendUnitFunKt$multipleExitPointsWhen$2 + method (p0: kotlin.coroutines.experimental.Continuation): void + public final @org.jetbrains.annotations.Nullable method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object + public final @org.jetbrains.annotations.Nullable method invoke(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object +} + +@kotlin.Metadata +final class TailSuspendUnitFunKt$notTailCall$1 { + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + inner class TailSuspendUnitFunKt$notTailCall$1 + method (p0: kotlin.coroutines.experimental.Continuation): void + public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object + synthetic final method getLabel(): int + synthetic final method setLabel(p0: int): void +} + +@kotlin.Metadata +final class TailSuspendUnitFunKt$useGenericIngerType$2 { + public final static field INSTANCE: TailSuspendUnitFunKt$useGenericIngerType$2 + inner class TailSuspendUnitFunKt$useGenericIngerType$2 + static method (): void + method (): void + public synthetic method invoke(): java.lang.Object + public final method invoke(): void +} + +@kotlin.Metadata +final class TailSuspendUnitFunKt$useNullableUnit$1 { + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + inner class TailSuspendUnitFunKt$useNullableUnit$1 + method (p0: kotlin.coroutines.experimental.Continuation): void + public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object + synthetic final method getLabel(): int + synthetic final method setLabel(p0: int): void +} + +@kotlin.Metadata +public final class TailSuspendUnitFunKt { + inner class TailSuspendUnitFunKt$callsIntNotTailCall$1 + inner class TailSuspendUnitFunKt$lambdaAsParameterNotTailCall$1 + inner class TailSuspendUnitFunKt$multipleExitPointsNotTailCall$1 + inner class TailSuspendUnitFunKt$multipleExitPointsWhen$2 + inner class TailSuspendUnitFunKt$notTailCall$1 + inner class TailSuspendUnitFunKt$useGenericIngerType$2 + inner class TailSuspendUnitFunKt$useNullableUnit$1 + public final static @org.jetbrains.annotations.Nullable method callsIntNotTailCall(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method empty(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method generic(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method genericInferType(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function0, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method lambdaAsParameter(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method lambdaAsParameterNotTailCall(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method lambdaAsParameterReturn(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method multipleExitPoints(p0: boolean, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method multipleExitPointsNotTailCall(p0: boolean, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method multipleExitPointsWhen(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method multipleExitPointsWithOrdinaryInline(p0: boolean, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method notTailCall(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method nullableUnit(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final static method ordinary(): int + public final static method ordinaryInline(): void + public final static @org.jetbrains.annotations.Nullable method returnsInt(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method useGenericClass(@org.jetbrains.annotations.NotNull p0: Generic, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method useGenericIngerType(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method useGenericReturningUnit(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method useNullableUnit(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method useRunRunRunRunRun(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method withReturn(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method withoutReturn(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index 4982bb4beba..a8e2c837f3f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -162,6 +162,12 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { doTest(fileName); } + @TestMetadata("tailSuspendUnitFun.kt") + public void testTailSuspendUnitFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/tailSuspendUnitFun.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/codegen/bytecodeListing/annotations") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)