diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 15aebb1c844..fb85908e14c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1103,8 +1103,11 @@ public class ExpressionCodegen extends KtVisitor impleme if (closure.isSuspend()) { // resultContinuation if (closure.isSuspendLambda()) { + // When inlining crossinline lambda, the ACONST_NULL is never popped. + // Thus, do not generate it. Otherwise, it leads to VerifyError on run-time. boolean isCrossinlineLambda = (callGenerator instanceof InlineCodegen) && - Objects.requireNonNull(((InlineCodegen) callGenerator).getActiveLambda()).isCrossInline; + Objects.requireNonNull(((InlineCodegen) callGenerator).getActiveLambda(), + "no active lambda found").isCrossInline; if (!isCrossinlineLambda) { v.aconst(null); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java index b3d57cf01b8..2f34bf2d878 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java @@ -70,6 +70,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { private final Stack classStack = new Stack<>(); private final Stack nameStack = new Stack<>(); + private final Stack functionsStack = new Stack<>(); private final Set uninitializedClasses = new HashSet<>(); private final BindingTrace bindingTrace; @@ -317,49 +318,16 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { ); closure.setSuspend(true); closure.setSuspendLambda(); - if (capturesCrossinlineSuspendLambda(functionLiteral)) { - bindingTrace.record( - CodegenBinding.CAPTURES_CROSSINLINE_SUSPEND_LAMBDA, - functionDescriptor, - true - ); - } } + functionsStack.push(functionDescriptor); super.visitLambdaExpression(lambdaExpression); + + functionsStack.pop(); nameStack.pop(); classStack.pop(); } - // If inner lambda captures crossinline suspend lambda, it is unsafe to generate state machine for it. - private boolean capturesCrossinlineSuspendLambda(@NotNull PsiElement psiNode) { - PsiElement[] children = psiNode.getChildren(); - boolean res = false; - for (PsiElement child : children) { - if (child instanceof KtCallElement) { - KtExpression callee = ((KtCallElement) child).getCalleeExpression(); - Call call = bindingContext.get(CALL, callee); - ResolvedCall resolvedCall = bindingContext.get(RESOLVED_CALL, call); - if (resolvedCall instanceof VariableAsFunctionResolvedCall) { - VariableAsFunctionResolvedCall variableAsFunction = (VariableAsFunctionResolvedCall) resolvedCall; - VariableDescriptor variableDescriptor = variableAsFunction.getVariableCall().getResultingDescriptor(); - CallableDescriptor callableDescriptor = ((ResolvedCall) variableAsFunction).getResultingDescriptor(); - if (variableDescriptor instanceof ValueParameterDescriptor && - ((ValueParameterDescriptor) variableDescriptor).isCrossinline() && - callableDescriptor instanceof FunctionDescriptor && - ((FunctionDescriptor) callableDescriptor).isSuspend()) { - FunctionDescriptor enclosingDescriptor = - bindingContext.get(ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.getCall()); - assert(enclosingDescriptor != null); - return true; - } - } - } - res = res || capturesCrossinlineSuspendLambda(child); - } - return res; - } - @Override public void visitCallableReferenceExpression(@NotNull KtCallableReferenceExpression expression) { ResolvedCall referencedFunction = CallUtilKt.getResolvedCall(expression.getCallableReference(), bindingContext); @@ -573,17 +541,11 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { recordClassForFunction(function, functionDescriptor, name, functionDescriptor); MutableClosure closure = recordClosure(classDescriptor, name); closure.setSuspend(true); - - if (capturesCrossinlineSuspendLambda(function)) { - bindingTrace.record( - CodegenBinding.CAPTURES_CROSSINLINE_SUSPEND_LAMBDA, - functionDescriptor, - true - ); - } + functionsStack.push(functionDescriptor); super.visitNamedFunction(function); + functionsStack.pop(); if (nameForClassOrPackageMember != null) { nameStack.pop(); } @@ -593,7 +555,9 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { if (nameForClassOrPackageMember != null) { nameStack.push(nameForClassOrPackageMember); + functionsStack.push(functionDescriptor); super.visitNamedFunction(function); + functionsStack.pop(); nameStack.pop(); } else { @@ -602,8 +566,10 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { recordClosure(classDescriptor, name); classStack.push(classDescriptor); + functionsStack.push(functionDescriptor); nameStack.push(name); super.visitNamedFunction(function); + functionsStack.pop(); nameStack.pop(); classStack.pop(); } @@ -631,6 +597,47 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { public void visitCallExpression(@NotNull KtCallExpression expression) { super.visitCallExpression(expression); checkSamCall(expression); + checkCrossinlineSuspendCall(expression); + } + + private void checkCrossinlineSuspendCall(@NotNull KtCallExpression expression) { + KtExpression callee = expression.getCalleeExpression(); + Call call = bindingContext.get(CALL, callee); + ResolvedCall resolvedCall = bindingContext.get(RESOLVED_CALL, call); + + if (resolvedCall instanceof VariableAsFunctionResolvedCall) { + VariableAsFunctionResolvedCall variableAsFunction = (VariableAsFunctionResolvedCall) resolvedCall; + VariableDescriptor variableDescriptor = variableAsFunction.getVariableCall().getResultingDescriptor(); + CallableDescriptor callableDescriptor = ((ResolvedCall) variableAsFunction).getResultingDescriptor(); + + if (variableDescriptor instanceof ValueParameterDescriptor && + ((ValueParameterDescriptor) variableDescriptor).isCrossinline() && + callableDescriptor instanceof FunctionDescriptor && + ((FunctionDescriptor) callableDescriptor).isSuspend()) { + FunctionDescriptor enclosingDescriptor = + bindingContext.get(ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.getCall()); + if (enclosingDescriptor == null) return; + + DeclarationDescriptor functionWithCrossinlineParameter = variableDescriptor.getContainingDeclaration(); + for (int i = functionsStack.size() - 1; i >= 0; i--) { + if (functionsStack.get(i).isSuspend()) { + Boolean alreadyPutValue = bindingTrace.getBindingContext() + .get(CodegenBinding.CAPTURES_CROSSINLINE_SUSPEND_LAMBDA, functionsStack.get(i)); + if (alreadyPutValue != null && alreadyPutValue) { + return; + } + bindingTrace.record( + CodegenBinding.CAPTURES_CROSSINLINE_SUSPEND_LAMBDA, + functionsStack.get(i), + true + ); + } + if (functionsStack.get(i) == functionWithCrossinlineParameter) { + return; + } + } + } + } } private void checkSamCall(@NotNull KtCallElement expression) { 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 26884b1f438..3b47f19b376 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -67,20 +67,24 @@ class CoroutineTransformerMethodVisitor( override fun performTransformations(methodNode: MethodNode) { removeFakeContinuationConstructorCall(methodNode) + replaceFakeContinuationsWithRealOnes( + methodNode, + if (isForNamedFunction) getLastParameterIndex(methodNode.desc, methodNode.access) else 0 + ) + + FixStackMethodTransformer().transform(containingClassInternalName, methodNode) + RedundantLocalsEliminationMethodTransformer().transform(containingClassInternalName, methodNode) + updateMaxStack(methodNode) + val suspensionPoints = collectSuspensionPoints(methodNode) // First instruction in the method node may change in case of named function val actualCoroutineStart = methodNode.instructions.first - FixStackMethodTransformer().transform(containingClassInternalName, methodNode) - if (isForNamedFunction) { ReturnUnitMethodTransformer.transform(containingClassInternalName, methodNode) if (allSuspensionPointsAreTailCalls(containingClassInternalName, methodNode, suspensionPoints)) { - continuationIndex = getLastParameterIndex(methodNode.desc, methodNode.access) - replaceFakeContinuationsWithRealOnes(methodNode, continuationIndex) - dropSuspensionMarkers(methodNode, suspensionPoints) return } @@ -101,8 +105,6 @@ class CoroutineTransformerMethodVisitor( // Actual max stack might be increased during the previous phases updateMaxStack(methodNode) - replaceFakeContinuationsWithRealOnes(methodNode, continuationIndex) - // Remove unreachable suspension points // If we don't do this, then relevant frames will not be analyzed, that is unexpected from point of view of next steps (e.g. variable spilling) removeUnreachableSuspensionPointsAndExitPoints(methodNode, suspensionPoints) @@ -820,7 +822,7 @@ private fun AbstractInsnNode?.isInvisibleInDebugVarInsn(methodNode: MethodNode): private val SAFE_OPCODES = ((Opcodes.DUP..Opcodes.DUP2_X2) + Opcodes.NOP + Opcodes.POP + Opcodes.POP2 + (Opcodes.IFEQ..Opcodes.GOTO)).toSet() -internal fun replaceFakeContinuationsWithRealOnes(methodNode: MethodNode, continuationIndex: Int) { +private fun replaceFakeContinuationsWithRealOnes(methodNode: MethodNode, continuationIndex: Int) { val fakeContinuations = methodNode.instructions.asSequence().filter(::isFakeContinuationMarker).toList() for (fakeContinuation in fakeContinuations) { methodNode.instructions.removeAll(listOf(fakeContinuation.previous.previous, fakeContinuation.previous)) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/RedundantLocalsEliminationMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/RedundantLocalsEliminationMethodTransformer.kt new file mode 100644 index 00000000000..dcd4a75772a --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/RedundantLocalsEliminationMethodTransformer.kt @@ -0,0 +1,218 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. 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.codegen.coroutines + +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.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.tree.* + +// Inliner emits a lot of locals during inlining. +// Remove all of them since these locals are +// 1) going to be spilled into continuation object +// 2) breaking tail-call elimination +class RedundantLocalsEliminationMethodTransformer : MethodTransformer() { + lateinit var internalClassName: String + override fun transform(internalClassName: String, methodNode: MethodNode) { + this.internalClassName = internalClassName + do { + var changed = false + changed = simpleRemove(methodNode) || changed + changed = removeWithReplacement(methodNode) || changed + changed = removeAloadCheckcastContinuationAstore(methodNode) || changed + } while (changed) + } + + // Replace + // GETSTATIC kotlin/Unit.INSTANCE + // ASTORE N + // ... + // ALOAD N + // with + // ... + // GETSTATIC kotlin/Unit.INSTANCE + // or + // ACONST_NULL + // ASTORE N + // ... + // ALOAD N + // with + // ... + // ACONST_NULL + // or + // ALOAD K + // ASTORE N + // ... + // ALOAD N + // with + // ... + // ALOAD K + // + // But do not remove several at a time, since the same local (for example, ALOAD 0) might be loaded and stored multiple times in + // sequence, like + // ALOAD 0 + // ASTORE 1 + // ALOAD 1 + // ASTORE 2 + // ALOAD 3 + // Here, it is unsafe to replace ALOAD 3 with ALOAD 1, and then already removed ALOAD 1 with ALOAD 0. + private fun removeWithReplacement( + methodNode: MethodNode + ): Boolean { + val insns = findSafeAstorePredecessors(methodNode, ignoreLocalVariableTable = false) { + it.isUnitInstance() || it.opcode == Opcodes.ACONST_NULL || it.opcode == Opcodes.ALOAD + } + insns.asIterable().firstOrNull { (pred, astore) -> + val index = astore.localIndex() + + methodNode.instructions.removeAll(listOf(pred, astore)) + + methodNode.instructions.asSequence() + .filter { it.opcode == Opcodes.ALOAD && it.localIndex() == index } + .toList() + .forEach { methodNode.instructions.set(it, pred.clone()) } + return true + } + return false + } + + private fun AbstractInsnNode.clone() = when (this) { + is FieldInsnNode -> FieldInsnNode(opcode, owner, name, desc) + is VarInsnNode -> VarInsnNode(opcode, `var`) + is InsnNode -> InsnNode(opcode) + is TypeInsnNode -> TypeInsnNode(opcode, desc) + else -> error("clone of $this is not implemented yet") + } + + // Remove + // ALOAD N + // POP + // or + // ACONST_NULL + // POP + // or + // GETSTATIC kotlin/Unit.INSTANCE + // POP + private fun simpleRemove(methodNode: MethodNode): Boolean { + val insns = + findPopPredecessors(methodNode) { it.isUnitInstance() || it.opcode == Opcodes.ACONST_NULL || it.opcode == Opcodes.ALOAD } + for ((pred, pop) in insns) { + methodNode.instructions.removeAll(listOf(pred, pop)) + } + return insns.isNotEmpty() + } + + private fun findPopPredecessors( + methodNode: MethodNode, + predicate: (AbstractInsnNode) -> Boolean + ): Map { + val insns = methodNode.instructions.asSequence().filter { predicate(it) }.toList() + + val cfg = ControlFlowGraph.build(methodNode) + + val res = hashMapOf() + for (insn in insns) { + val succ = findImmediateSuccessors(insn, cfg, methodNode).singleOrNull() ?: continue + if (succ.opcode != Opcodes.POP) continue + if (insn.opcode == Opcodes.ALOAD && methodNode.localVariables.firstOrNull { it.index == insn.localIndex() } != null) continue + val sources = findSourceInstructions(internalClassName, methodNode, listOf(succ)).values.flatten() + if (sources.size != 1) continue + res[insn] = succ + } + return res + } + + // Replace + // ALOAD K + // CHECKCAST Continuation + // ASTORE N + // ... + // ALOAD N + // with + // ... + // ALOAD K + // CHECKCAST Continuation + private fun removeAloadCheckcastContinuationAstore(methodNode: MethodNode): Boolean { + // Here we ignore the duplicates of continuation in local variable table, + // Since it increases performance greatly. + val insns = findSafeAstorePredecessors(methodNode, ignoreLocalVariableTable = true) { + it.opcode == Opcodes.CHECKCAST && + (it as TypeInsnNode).desc == CONTINUATION_ASM_TYPE.internalName && + it.previous?.opcode == Opcodes.ALOAD + } + + for ((checkcast, astore) in insns) { + val aload = checkcast.previous + val index = astore.localIndex() + + methodNode.instructions.removeAll(listOf(aload, checkcast, astore)) + + methodNode.instructions.asSequence() + .filter { it.opcode == Opcodes.ALOAD && it.localIndex() == index } + .toList() + .forEach { + methodNode.instructions.insertBefore(it, aload.clone()) + methodNode.instructions.set(it, checkcast.clone()) + } + } + return insns.isNotEmpty() + } + + private fun findSafeAstorePredecessors( + methodNode: MethodNode, + ignoreLocalVariableTable: Boolean, + predicate: (AbstractInsnNode) -> Boolean + ): Map { + val insns = methodNode.instructions.asSequence().filter { predicate(it) }.toList() + + val cfg = ControlFlowGraph.build(methodNode) + val res = hashMapOf() + + for (insn in insns) { + val succ = findImmediateSuccessors(insn, cfg, methodNode).singleOrNull() ?: continue + if (succ.opcode != Opcodes.ASTORE) continue + if (methodNode.instructions.asSequence().count { + it.opcode == Opcodes.ASTORE && it.localIndex() == succ.localIndex() + } != 1) continue + if (!ignoreLocalVariableTable && methodNode.localVariables.firstOrNull { it.index == succ.localIndex() } != null) continue + val sources = findSourceInstructions(internalClassName, methodNode, listOf(succ)).values.flatten() + if (sources.size > 1) continue + res[insn] = succ + } + + return res + } + + // Find all meaningful successors of insn + private fun findImmediateSuccessors( + insn: AbstractInsnNode, + cfg: ControlFlowGraph, + methodNode: MethodNode + ): Collection { + val visited = hashSetOf() + + fun dfs(current: AbstractInsnNode): Collection { + if (!visited.add(current)) return emptySet() + + return cfg.getSuccessorsIndices(current).flatMap { + val succ = methodNode.instructions[it] + if (!succ.isMeaningful || succ is JumpInsnNode || succ.opcode == Opcodes.NOP) dfs(succ) + else setOf(succ) + } + } + + return dfs(insn) + } + + private fun AbstractInsnNode.localIndex(): Int { + assert(this is VarInsnNode) + return (this as VarInsnNode).`var` + } +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/ReturnUnitMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/ReturnUnitMethodTransformer.kt index 76a223edf15..853f488bac7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/ReturnUnitMethodTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/ReturnUnitMethodTransformer.kt @@ -106,19 +106,6 @@ object ReturnUnitMethodTransformer : MethodTransformer() { 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 { - val index = methodNode.instructions.indexOf(it) - if (isUnreachable(index, frames)) return@keysToMap emptySet() - frames[index].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 = @@ -132,3 +119,15 @@ object ReturnUnitMethodTransformer : MethodTransformer() { } } +internal fun findSourceInstructions( + internalClassName: String, + methodNode: MethodNode, + insns: Collection +): Map> { + val frames = MethodTransformer.analyze(internalClassName, methodNode, IgnoringCopyOperationSourceInterpreter()) + return insns.keysToMap { + val index = methodNode.instructions.indexOf(it) + if (isUnreachable(index, frames)) return@keysToMap emptySet() + frames[index].getStack(0).insns + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt index 0e1fc33bc18..9a431c3c84a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt @@ -177,19 +177,14 @@ private fun NewResolvedCallImpl.asDummyOldResolvedCall(bindi } fun ResolvedCall<*>.isSuspendNoInlineCall(codegen: ExpressionCodegen): Boolean { - var isCrossinline = false - var isInlineLambda = false - if (this is VariableAsFunctionResolvedCall) { - variableCall.resultingDescriptor.safeAs()?.let { - isCrossinline = it.isCrossinline - isInlineLambda = !isCrossinline && !it.isNoinline && codegen.context.functionDescriptor.isInline - } - } - return resultingDescriptor.safeAs() - ?.let { - val inline = it.isInline || isCrossinline || isInlineLambda - it.isSuspend && (!inline || it.isBuiltInSuspendCoroutineOrReturnInJvm() || it.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm()) - } == true + val isInlineLambda = this.safeAs() + ?.variableCall?.resultingDescriptor?.safeAs() + ?.let { it.isCrossinline || (!it.isNoinline && codegen.context.functionDescriptor.isInline) } == true + + val functionDescriptor = resultingDescriptor as? FunctionDescriptor ?: return false + if (!functionDescriptor.isSuspend) return false + if (functionDescriptor.isBuiltInSuspendCoroutineOrReturnInJvm() || functionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm()) return true + return !(functionDescriptor.isInline || isInlineLambda) } fun CallableDescriptor.isSuspendFunctionNotSuspensionView(): Boolean { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt index 213acdb091c..2c9b3db5e5a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt @@ -87,7 +87,8 @@ abstract class InlineCodegen( protected val expressionMap = linkedMapOf() - var activeLambda: LambdaInfo? = null; protected set + var activeLambda: LambdaInfo? = null + protected set private val defaultSourceMapper = sourceCompiler.lazySourceMapper diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt index 0ec909fc85a..718568ecd75 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt @@ -10,7 +10,6 @@ import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.ClosureCodegen import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_ASM_TYPE -import org.jetbrains.kotlin.codegen.coroutines.replaceFakeContinuationsWithRealOnes import org.jetbrains.kotlin.codegen.inline.FieldRemapper.Companion.foldName import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/RegeneratedLambdaFieldRemapper.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/RegeneratedLambdaFieldRemapper.kt index 14c30767942..aed1a488530 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/RegeneratedLambdaFieldRemapper.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/RegeneratedLambdaFieldRemapper.kt @@ -25,7 +25,7 @@ class RegeneratedLambdaFieldRemapper( originalLambdaInternalName: String, override val newLambdaInternalName: String, parameters: Parameters, - private val recapturedLambdas: Map, + val recapturedLambdas: Map, remapper: FieldRemapper, private val isConstructor: Boolean ) : FieldRemapper(originalLambdaInternalName, remapper, parameters) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 9f8fabb52ca..5a1838a552d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -201,7 +201,7 @@ public interface Errors { DiagnosticFactory2 DEPRECATED_MODIFIER_CONTAINING_DECLARATION = DiagnosticFactory2.create(WARNING); DiagnosticFactory1 ILLEGAL_INLINE_PARAMETER_MODIFIER = DiagnosticFactory1.create(ERROR); DiagnosticFactory0 INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED = DiagnosticFactory0.create(ERROR); - DiagnosticFactory0 REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE = DiagnosticFactory0.create(WARNING); DiagnosticFactory1 WRONG_ANNOTATION_TARGET = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET = DiagnosticFactory2.create(ERROR); DiagnosticFactory1 WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE = DiagnosticFactory1.create(WARNING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index d84a1f54d4a..d33e1273912 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -122,8 +122,8 @@ public class DefaultErrorMessages { MAP.put(WRONG_MODIFIER_CONTAINING_DECLARATION, "Modifier ''{0}'' is not applicable inside ''{1}''", TO_STRING, TO_STRING); MAP.put(DEPRECATED_MODIFIER_CONTAINING_DECLARATION, "Modifier ''{0}'' is deprecated inside ''{1}''", TO_STRING, TO_STRING); MAP.put(ILLEGAL_INLINE_PARAMETER_MODIFIER, "Modifier ''{0}'' is allowed only for function parameters of an inline function", TO_STRING); - MAP.put(INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED, "Inline lambda parameters of suspend function type are not supported. Add 'noinline' or 'crossinline' modifier."); - MAP.put(REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE, "Redundant suspend modifier of inline lambda parameters of suspend function type."); + MAP.put(INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED, "Suspend inline lambda parameters of non-suspend function type are not supported. Add 'noinline' or 'crossinline' modifier."); + MAP.put(REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE, "Redundant 'suspend' modifier: lambda parameters of suspend function type uses existing continuation."); MAP.put(WRONG_ANNOTATION_TARGET, "This annotation is not applicable to target ''{0}''", TO_STRING); MAP.put(WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET, "This annotation is not applicable to target ''{0}'' and use site target ''@{1}''", TO_STRING, TO_STRING); MAP.put(WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt index f4c16b0a3dd..7b7f14e930d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt @@ -17,13 +17,12 @@ package org.jetbrains.kotlin.resolve.calls.checkers import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.builtins.isFunctionType -import org.jetbrains.kotlin.builtins.isSuspendFunctionType +import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.contracts.parsing.isFromContractDsl import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors.* -import org.jetbrains.kotlin.contracts.parsing.isFromContractDsl import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType @@ -239,8 +238,7 @@ internal class InlineChecker(private val descriptor: FunctionDescriptor) : CallC val containingDeclaration = descriptor.getContainingDeclaration() val isInvoke = descriptor.getName() == OperatorNameConventions.INVOKE && - containingDeclaration is ClassDescriptor && - (containingDeclaration.defaultType.isFunctionType || containingDeclaration.defaultType.isSuspendFunctionType) + containingDeclaration is ClassDescriptor && containingDeclaration.defaultType.isBuiltinFunctionalType return isInvoke || InlineUtil.isInline(descriptor) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt index 440f17da175..a0606a72f3d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.KtModifierKeywordToken +import org.jetbrains.kotlin.lexer.KtToken import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtFunction @@ -32,7 +33,6 @@ object InlineParameterChecker : DeclarationChecker { override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { if (declaration is KtFunction) { val inline = declaration.hasModifier(KtTokens.INLINE_KEYWORD) - val suspend = declaration.hasModifier(KtTokens.SUSPEND_KEYWORD) for (parameter in declaration.valueParameters) { val parameterDescriptor = context.trace.get(BindingContext.VALUE_PARAMETER, parameter) if (!inline || (parameterDescriptor != null && !parameterDescriptor.type.isBuiltinFunctionalType)) { @@ -43,8 +43,11 @@ object InlineParameterChecker : DeclarationChecker { !parameter.hasModifier(KtTokens.CROSSINLINE_KEYWORD) && parameterDescriptor?.type?.isSuspendFunctionType == true ) { - if (suspend) { - context.trace.report(Errors.REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE.on(parameter)) + if (declaration.hasModifier(KtTokens.SUSPEND_KEYWORD)) { + val typeReference = parameter.typeReference!! + val modifierList = typeReference.modifierList!! + val modifier = modifierList.getModifier(KtTokens.SUSPEND_KEYWORD)!! + context.trace.report(Errors.REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE.on(modifier)) } else { context.trace.report(Errors.INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED.on(parameter)) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java index 7f568439adf..67d0de13818 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java @@ -36,8 +36,7 @@ public class InlineUtil { public static boolean isInlineParameterExceptNullability(@NotNull ParameterDescriptor valueParameterOrReceiver) { return !(valueParameterOrReceiver instanceof ValueParameterDescriptor && ((ValueParameterDescriptor) valueParameterOrReceiver).isNoinline()) && - (FunctionTypesKt.isFunctionType(valueParameterOrReceiver.getOriginal().getType()) || - FunctionTypesKt.isSuspendFunctionType(valueParameterOrReceiver.getOriginal().getType())); + FunctionTypesKt.isBuiltinFunctionalType(valueParameterOrReceiver.getOriginal().getType()); } public static boolean isInlineParameter(@NotNull ParameterDescriptor valueParameterOrReceiver) { diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.kt new file mode 100644 index 00000000000..d15975249ad --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.kt @@ -0,0 +1,74 @@ +// IGNORE_BACKEND: JS +// WITH_RUNTIME +// WITH_COROUTINES +// CHECK_BYTECODE_LISTING +import helpers.* +import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* + +interface SourceCrossinline { + suspend fun consume(sink: Sink) + companion object Factory +} + +interface Sink { + suspend fun send(item: E) + fun close(cause: Throwable?) +} + +inline fun source(crossinline action: suspend Sink.() -> Unit): SourceCrossinline = object : SourceCrossinline { + override suspend fun consume(sink: Sink) { + var cause: Throwable? = null + try { + action(sink) + } catch (e: Throwable) { + cause = e + } + sink.close(cause) + } +} + +fun SourceCrossinline.Factory.range(start: Int, count: Int): SourceCrossinline = source { + for (i in start until (start + count)) { + send(i) + } +} + +suspend inline fun SourceCrossinline.consumeEach(crossinline action: suspend (E) -> Unit) { + consume(object : Sink { + override suspend fun send(item: E) = action(item) + override fun close(cause: Throwable?) { cause?.let { throw it } } + }) +} + +suspend inline fun SourceCrossinline.fold(initial: R, crossinline operation: suspend (acc: R, E) -> R): R { + var acc = initial + consumeEach { + acc = operation(acc, it) + } + return acc +} + +inline fun SourceCrossinline.filter(crossinline predicate: (E) -> Boolean) = source { + consumeEach { + if (predicate(it)) send(it) + } +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun Int.isGood() = this % 4 == 0 + +fun box(): String { + var res = 0 + builder { + res = SourceCrossinline + .range(1, 11) + .filter { it.isGood() } + .fold(0, { a, b -> a + b }) + } + if (res != 12) return "FAIL" + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.txt new file mode 100644 index 00000000000..e27d95ea936 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.txt @@ -0,0 +1,352 @@ +@kotlin.Metadata +public final class CrossinlineKt$box$1$doResume$$inlined$filter$1$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field L$3: java.lang.Object + field L$4: java.lang.Object + field L$5: java.lang.Object + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: CrossinlineKt$box$1$doResume$$inlined$filter$1 + inner class CrossinlineKt$box$1$doResume$$inlined$filter$1 + inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$1 + public method (p0: CrossinlineKt$box$1$doResume$$inlined$filter$1, p1: 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 CrossinlineKt$box$1$doResume$$inlined$filter$1$2$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: CrossinlineKt$box$1$doResume$$inlined$filter$1$2 + inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2 + inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2$1 + public method (p0: CrossinlineKt$box$1$doResume$$inlined$filter$1$2, p1: 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 CrossinlineKt$box$1$doResume$$inlined$filter$1$2 { + synthetic final field receiver$0$inlined: Sink + synthetic final field this$0: CrossinlineKt$box$1$doResume$$inlined$filter$1 + inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2 + inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2$1 + public method (p0: Sink, p1: CrossinlineKt$box$1$doResume$$inlined$filter$1): void + public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void + public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt$box$1$doResume$$inlined$filter$1 { + synthetic final field receiver$0$inlined: SourceCrossinline + inner class CrossinlineKt$box$1$doResume$$inlined$filter$1 + inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$1 + public method (p0: SourceCrossinline): void + public @org.jetbrains.annotations.Nullable method consume(@org.jetbrains.annotations.NotNull p0: Sink, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt$box$1$doResume$$inlined$fold$1 { + synthetic final field $acc$inlined: kotlin.jvm.internal.Ref$ObjectRef + inner class CrossinlineKt$box$1$doResume$$inlined$fold$1 + inner class CrossinlineKt$box$1$fold$$inlined$consumeEach$1$1 + public method (p0: kotlin.jvm.internal.Ref$ObjectRef): void + public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void + public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt$box$1$filter$$inlined$source$1$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field L$3: java.lang.Object + field L$4: java.lang.Object + field L$5: java.lang.Object + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1 + inner class CrossinlineKt$box$1$filter$$inlined$source$1 + inner class CrossinlineKt$box$1$filter$$inlined$source$1$1 + public method (p0: CrossinlineKt$box$1$filter$$inlined$source$1, p1: 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 CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1 + inner class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1 + inner class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1$1 + public method (p0: CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1, p1: 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 CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1 { + synthetic final field receiver$0$inlined: Sink + synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1 + inner class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1 + inner class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1$1 + public method (p0: Sink, p1: CrossinlineKt$box$1$filter$$inlined$source$1): void + public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void + public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt$box$1$filter$$inlined$source$1 { + synthetic final field $predicate$inlined: kotlin.jvm.functions.Function1 + synthetic final field receiver$0$inlined: SourceCrossinline + inner class CrossinlineKt$box$1$filter$$inlined$source$1 + inner class CrossinlineKt$box$1$filter$$inlined$source$1$1 + public method (p0: SourceCrossinline, p1: kotlin.jvm.functions.Function1): void + public @org.jetbrains.annotations.Nullable method consume(@org.jetbrains.annotations.NotNull p0: Sink, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt$box$1$fold$$inlined$consumeEach$1$1 { + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: CrossinlineKt$box$1$fold$$inlined$consumeEach$1 + inner class CrossinlineKt$box$1$fold$$inlined$consumeEach$1 + inner class CrossinlineKt$box$1$fold$$inlined$consumeEach$1$1 + public method (p0: CrossinlineKt$box$1$fold$$inlined$consumeEach$1, p1: 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 CrossinlineKt$box$1$fold$$inlined$consumeEach$1 { + synthetic final field $acc$inlined: kotlin.jvm.internal.Ref$ObjectRef + synthetic final field $operation$inlined: kotlin.jvm.functions.Function3 + inner class CrossinlineKt$box$1$fold$$inlined$consumeEach$1 + inner class CrossinlineKt$box$1$fold$$inlined$consumeEach$1$1 + public method (p0: kotlin.jvm.internal.Ref$ObjectRef, p1: kotlin.jvm.functions.Function3): void + public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void + public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +final class CrossinlineKt$box$1 { + synthetic final field $res: kotlin.jvm.internal.Ref$IntRef + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field L$3: java.lang.Object + field L$4: java.lang.Object + inner class CrossinlineKt$box$1 + method (p0: kotlin.jvm.internal.Ref$IntRef, p1: kotlin.coroutines.experimental.Continuation): void + public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): kotlin.coroutines.experimental.Continuation + 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.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object + public synthetic method invoke(p0: java.lang.Object): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt$consumeEach$2$send$1 { + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: CrossinlineKt$consumeEach$2 + inner class CrossinlineKt$consumeEach$2 + inner class CrossinlineKt$consumeEach$2$send$1 + public method (p0: CrossinlineKt$consumeEach$2, p1: 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 CrossinlineKt$consumeEach$2 { + synthetic final field $action: kotlin.jvm.functions.Function2 + inner class CrossinlineKt$consumeEach$2 + inner class CrossinlineKt$consumeEach$2$send$1 + public method (p0: kotlin.jvm.functions.Function2): void + public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void + public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt$filter$$inlined$source$1$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field L$3: java.lang.Object + field L$4: java.lang.Object + field L$5: java.lang.Object + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1 + inner class CrossinlineKt$filter$$inlined$source$1 + inner class CrossinlineKt$filter$$inlined$source$1$1 + public method (p0: CrossinlineKt$filter$$inlined$source$1, p1: 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 CrossinlineKt$filter$$inlined$source$1$lambda$1$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1$lambda$1 + inner class CrossinlineKt$filter$$inlined$source$1$lambda$1 + inner class CrossinlineKt$filter$$inlined$source$1$lambda$1$1 + public method (p0: CrossinlineKt$filter$$inlined$source$1$lambda$1, p1: 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 CrossinlineKt$filter$$inlined$source$1$lambda$1 { + synthetic final field receiver$0$inlined: Sink + synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1 + inner class CrossinlineKt$filter$$inlined$source$1$lambda$1 + inner class CrossinlineKt$filter$$inlined$source$1$lambda$1$1 + public method (p0: Sink, p1: CrossinlineKt$filter$$inlined$source$1): void + public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void + public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt$filter$$inlined$source$1 { + synthetic final field $predicate$inlined: kotlin.jvm.functions.Function1 + synthetic final field receiver$0$inlined: SourceCrossinline + inner class CrossinlineKt$filter$$inlined$source$1 + inner class CrossinlineKt$filter$$inlined$source$1$1 + public method (p0: SourceCrossinline, p1: kotlin.jvm.functions.Function1): void + public @org.jetbrains.annotations.Nullable method consume(@org.jetbrains.annotations.NotNull p0: Sink, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt$fold$$inlined$consumeEach$1$1 { + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: CrossinlineKt$fold$$inlined$consumeEach$1 + inner class CrossinlineKt$fold$$inlined$consumeEach$1 + inner class CrossinlineKt$fold$$inlined$consumeEach$1$1 + public method (p0: CrossinlineKt$fold$$inlined$consumeEach$1, p1: 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 CrossinlineKt$fold$$inlined$consumeEach$1 { + synthetic final field $acc$inlined: kotlin.jvm.internal.Ref$ObjectRef + synthetic final field $operation$inlined: kotlin.jvm.functions.Function3 + inner class CrossinlineKt$fold$$inlined$consumeEach$1 + inner class CrossinlineKt$fold$$inlined$consumeEach$1$1 + public method (p0: kotlin.jvm.internal.Ref$ObjectRef, p1: kotlin.jvm.functions.Function3): void + public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void + public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt$range$$inlined$source$1$1 { + field I$0: int + field I$1: int + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field L$3: java.lang.Object + field L$4: java.lang.Object + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: CrossinlineKt$range$$inlined$source$1 + inner class CrossinlineKt$range$$inlined$source$1 + inner class CrossinlineKt$range$$inlined$source$1$1 + public method (p0: CrossinlineKt$range$$inlined$source$1, p1: 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 CrossinlineKt$range$$inlined$source$1 { + synthetic final field $count$inlined: int + synthetic final field $start$inlined: int + inner class CrossinlineKt$range$$inlined$source$1 + inner class CrossinlineKt$range$$inlined$source$1$1 + public method (p0: int, p1: int): void + public @org.jetbrains.annotations.Nullable method consume(@org.jetbrains.annotations.NotNull p0: Sink, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt$source$1$consume$1 { + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: CrossinlineKt$source$1 + inner class CrossinlineKt$source$1 + inner class CrossinlineKt$source$1$consume$1 + public method (p0: CrossinlineKt$source$1, p1: 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 CrossinlineKt$source$1 { + synthetic final field $action: kotlin.jvm.functions.Function2 + inner class CrossinlineKt$source$1 + inner class CrossinlineKt$source$1$consume$1 + public method (p0: kotlin.jvm.functions.Function2): void + public @org.jetbrains.annotations.Nullable method consume(@org.jetbrains.annotations.NotNull p0: Sink, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CrossinlineKt { + inner class CrossinlineKt$box$1 + inner class CrossinlineKt$consumeEach$2 + inner class CrossinlineKt$source$1 + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void + private final static method consumeEach(@org.jetbrains.annotations.NotNull p0: SourceCrossinline, p1: kotlin.jvm.functions.Function2, p2: kotlin.coroutines.experimental.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.NotNull method filter(@org.jetbrains.annotations.NotNull p0: SourceCrossinline, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1): SourceCrossinline + private final static method fold(@org.jetbrains.annotations.NotNull p0: SourceCrossinline, p1: java.lang.Object, p2: kotlin.jvm.functions.Function3, p3: kotlin.coroutines.experimental.Continuation): java.lang.Object + public final static method isGood(p0: int): boolean + public final static @org.jetbrains.annotations.NotNull method range(@org.jetbrains.annotations.NotNull p0: SourceCrossinline$Factory, p1: int, p2: int): SourceCrossinline + public final static @org.jetbrains.annotations.NotNull method source(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2): SourceCrossinline +} + +@kotlin.Metadata +public interface Sink { + public abstract method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void + public abstract @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class SourceCrossinline$Factory { + synthetic final static field $$INSTANCE: SourceCrossinline$Factory + inner class SourceCrossinline$Factory + static method (): void + private method (): void +} + +@kotlin.Metadata +public interface SourceCrossinline { + public final static field Factory: SourceCrossinline$Factory + inner class SourceCrossinline$Factory + static method (): void + public abstract @org.jetbrains.annotations.Nullable method consume(@org.jetbrains.annotations.NotNull p0: Sink, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt index 6c2735eec89..392690e6a40 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt @@ -14,7 +14,6 @@ final class InlineWithoutStateMachineKt$box$1 { @kotlin.Metadata final class InlineWithoutStateMachineKt$complexSuspend$1 { field L$0: java.lang.Object - field L$1: java.lang.Object synthetic field data: java.lang.Object synthetic field exception: java.lang.Throwable inner class InlineWithoutStateMachineKt$complexSuspend$1 diff --git a/compiler/testData/codegen/boxInline/suspend/capturedVariables.kt b/compiler/testData/codegen/boxInline/suspend/capturedVariables.kt index ecc66cc5578..b05baf1a4ec 100644 --- a/compiler/testData/codegen/boxInline/suspend/capturedVariables.kt +++ b/compiler/testData/codegen/boxInline/suspend/capturedVariables.kt @@ -15,6 +15,7 @@ suspend inline fun test2(crossinline c: suspend () -> Unit) { // FILE: box.kt import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* fun builder(c: suspend () -> Unit) { c.startCoroutine(object: Continuation { @@ -30,7 +31,10 @@ fun builder(c: suspend () -> Unit) { }) } -suspend fun calculate() = "OK" +suspend fun calculate() = suspendCoroutineOrReturn { + it.resume("OK") + COROUTINE_SUSPENDED +} fun box() : String { var res = "FAIL 1" diff --git a/compiler/testData/codegen/bytecodeText/coroutines/returnUnitInLambda.kt b/compiler/testData/codegen/bytecodeText/coroutines/returnUnitInLambda.kt index 22deff56f86..4489c2cc4e4 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/returnUnitInLambda.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/returnUnitInLambda.kt @@ -25,5 +25,8 @@ fun box() : String { return res } +// When we generate code of suspend function returning unit call +// we surround it with return unit markers. +// While denerating state machine, we remove them. // 0 ICONST_2 // 0 ICONST_3 \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/crossinlineSuspendValueParameter.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/crossinlineSuspendValueParameter.kt deleted file mode 100644 index 0d33a9afa32..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/crossinlineSuspendValueParameter.kt +++ /dev/null @@ -1,2 +0,0 @@ -// SKIP_TXT -inline fun foo(crossinline suspend c: () -> Unit) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineSuspendOfSuspend.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineSuspendOfSuspend.kt index 550e66f35f6..9dc2ebb8a68 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineSuspendOfSuspend.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineSuspendOfSuspend.kt @@ -20,7 +20,7 @@ interface SuspendRunnable { // Block is NOT allowed to be called from nested classes/lambdas (as common crossinlines) // It is NOT possible to call startCoroutine on the parameter // suspend calls possible inside lambda matching to the parameter -suspend inline fun test(c: suspend () -> Unit) { +suspend inline fun test(c: suspend () -> Unit) { c() val o = object: SuspendRunnable { override suspend fun run() { diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/inline.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/inline.kt index c7a3c73cb3a..d85fd39c177 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/inline.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/inline.kt @@ -5,7 +5,7 @@ inline fun foo2(crossinline x: suspend () -> Unit) {} inline fun foo3(noinline x: suspend () -> Unit) {} inline fun foo4(noinline crossinline x: suspend () -> Unit) {} -suspend inline fun bar1(x: suspend () -> Unit) {} +suspend inline fun bar1(x: suspend () -> Unit) {} suspend inline fun bar2(crossinline x: suspend () -> Unit) {} suspend inline fun bar3(noinline x: suspend () -> Unit) {} suspend inline fun bar4(noinline crossinline x: suspend () -> Unit) {} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 770fbd2cfb2..2e028172819 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -6683,6 +6683,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("crossinline.kt") + public void testCrossinline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.kt"); + doTest(fileName); + } + @TestMetadata("inlineWithStateMachine.kt") public void testInlineWithStateMachine() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 4019ce48b9c..d11eb68b06f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -1559,12 +1559,6 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } - @TestMetadata("crossinlineSuspendValueParameter.kt") - public void testCrossinlineSuspendValueParameter() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/crossinlineSuspendValueParameter.kt"); - doTest(fileName); - } - @TestMetadata("inlineOrdinaryOfCrossinlineOrdinary.kt") public void testInlineOrdinaryOfCrossinlineOrdinary() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfCrossinlineOrdinary.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index afb49db1291..a9e18a3c0c6 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -1559,12 +1559,6 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } - @TestMetadata("crossinlineSuspendValueParameter.kt") - public void testCrossinlineSuspendValueParameter() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/crossinlineSuspendValueParameter.kt"); - doTest(fileName); - } - @TestMetadata("inlineOrdinaryOfCrossinlineOrdinary.kt") public void testInlineOrdinaryOfCrossinlineOrdinary() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfCrossinlineOrdinary.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 6f15326f3f7..ddef1d1084d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -6683,6 +6683,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("crossinline.kt") + public void testCrossinline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.kt"); + doTest(fileName); + } + @TestMetadata("inlineWithStateMachine.kt") public void testInlineWithStateMachine() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 1c19dfc7e7e..110dfdfd397 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -6683,6 +6683,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("crossinline.kt") + public void testCrossinline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.kt"); + doTest(fileName); + } + @TestMetadata("inlineWithStateMachine.kt") public void testInlineWithStateMachine() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.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 8d31b021adc..4509bd43838 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 @@ -7253,6 +7253,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("crossinline.kt") + public void testCrossinline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + @TestMetadata("inlineWithStateMachine.kt") public void testInlineWithStateMachine() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.kt");