Remove redundant locals

Do not transform already transformed suspend lambdas
Ignore duplicates of continuation in local variable table during redundant locals elimination.
This commit is contained in:
Ilmir Usmanov
2018-02-27 19:08:37 +03:00
parent 81f3e39f29
commit 826d667398
28 changed files with 785 additions and 113 deletions
@@ -1103,8 +1103,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> 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);
}
@@ -70,6 +70,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
private final Stack<ClassDescriptor> classStack = new Stack<>();
private final Stack<String> nameStack = new Stack<>();
private final Stack<FunctionDescriptor> functionsStack = new Stack<>();
private final Set<ClassDescriptor> 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) {
@@ -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))
@@ -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<AbstractInsnNode, AbstractInsnNode> {
val insns = methodNode.instructions.asSequence().filter { predicate(it) }.toList()
val cfg = ControlFlowGraph.build(methodNode)
val res = hashMapOf<AbstractInsnNode, AbstractInsnNode>()
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<AbstractInsnNode, AbstractInsnNode> {
val insns = methodNode.instructions.asSequence().filter { predicate(it) }.toList()
val cfg = ControlFlowGraph.build(methodNode)
val res = hashMapOf<AbstractInsnNode, AbstractInsnNode>()
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<AbstractInsnNode> {
val visited = hashSetOf<AbstractInsnNode>()
fun dfs(current: AbstractInsnNode): Collection<AbstractInsnNode> {
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`
}
}
@@ -106,19 +106,6 @@ object ReturnUnitMethodTransformer : MethodTransformer() {
private fun isSuspendingCallReturningUnit(node: AbstractInsnNode): Boolean =
node.safeAs<MethodInsnNode>()?.next?.next?.let(::isReturnsUnitMarker) == true
private fun findSourceInstructions(
internalClassName: String,
methodNode: MethodNode,
pops: Collection<AbstractInsnNode>
): Map<AbstractInsnNode, Collection<AbstractInsnNode>> {
val frames = analyze(internalClassName, methodNode, IgnoringCopyOperationSourceInterpreter())
return pops.keysToMap {
val index = methodNode.instructions.indexOf(it)
if (isUnreachable(index, frames)) return@keysToMap emptySet<AbstractInsnNode>()
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<AbstractInsnNode> =
@@ -132,3 +119,15 @@ object ReturnUnitMethodTransformer : MethodTransformer() {
}
}
internal fun findSourceInstructions(
internalClassName: String,
methodNode: MethodNode,
insns: Collection<AbstractInsnNode>
): Map<AbstractInsnNode, Collection<AbstractInsnNode>> {
val frames = MethodTransformer.analyze(internalClassName, methodNode, IgnoringCopyOperationSourceInterpreter())
return insns.keysToMap {
val index = methodNode.instructions.indexOf(it)
if (isUnreachable(index, frames)) return@keysToMap emptySet<AbstractInsnNode>()
frames[index].getStack(0).insns
}
}
@@ -177,19 +177,14 @@ private fun NewResolvedCallImpl<VariableDescriptor>.asDummyOldResolvedCall(bindi
}
fun ResolvedCall<*>.isSuspendNoInlineCall(codegen: ExpressionCodegen): Boolean {
var isCrossinline = false
var isInlineLambda = false
if (this is VariableAsFunctionResolvedCall) {
variableCall.resultingDescriptor.safeAs<ValueParameterDescriptor>()?.let {
isCrossinline = it.isCrossinline
isInlineLambda = !isCrossinline && !it.isNoinline && codegen.context.functionDescriptor.isInline
}
}
return resultingDescriptor.safeAs<FunctionDescriptor>()
?.let {
val inline = it.isInline || isCrossinline || isInlineLambda
it.isSuspend && (!inline || it.isBuiltInSuspendCoroutineOrReturnInJvm() || it.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm())
} == true
val isInlineLambda = this.safeAs<VariableAsFunctionResolvedCall>()
?.variableCall?.resultingDescriptor?.safeAs<ValueParameterDescriptor>()
?.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 {
@@ -87,7 +87,8 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
protected val expressionMap = linkedMapOf<Int, LambdaInfo>()
var activeLambda: LambdaInfo? = null; protected set
var activeLambda: LambdaInfo? = null
protected set
private val defaultSourceMapper = sourceCompiler.lazySourceMapper
@@ -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
@@ -25,7 +25,7 @@ class RegeneratedLambdaFieldRemapper(
originalLambdaInternalName: String,
override val newLambdaInternalName: String,
parameters: Parameters,
private val recapturedLambdas: Map<String, LambdaInfo>,
val recapturedLambdas: Map<String, LambdaInfo>,
remapper: FieldRemapper,
private val isConstructor: Boolean
) : FieldRemapper(originalLambdaInternalName, remapper, parameters) {
@@ -201,7 +201,7 @@ public interface Errors {
DiagnosticFactory2<PsiElement, KtModifierKeywordToken, String> DEPRECATED_MODIFIER_CONTAINING_DECLARATION = DiagnosticFactory2.create(WARNING);
DiagnosticFactory1<PsiElement, KtModifierKeywordToken> ILLEGAL_INLINE_PARAMETER_MODIFIER = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<KtParameter> INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE = DiagnosticFactory0.create(WARNING);
DiagnosticFactory1<KtAnnotationEntry, String> WRONG_ANNOTATION_TARGET = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<KtAnnotationEntry, String, String> WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET = DiagnosticFactory2.create(ERROR);
DiagnosticFactory1<KtAnnotationEntry, String> WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE = DiagnosticFactory1.create(WARNING);
@@ -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,
@@ -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)
}
@@ -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))
}
@@ -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) {
@@ -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<out E> {
suspend fun consume(sink: Sink<E>)
companion object Factory
}
interface Sink<in E> {
suspend fun send(item: E)
fun close(cause: Throwable?)
}
inline fun <E> source(crossinline action: suspend Sink<E>.() -> Unit): SourceCrossinline<E> = object : SourceCrossinline<E> {
override suspend fun consume(sink: Sink<E>) {
var cause: Throwable? = null
try {
action(sink)
} catch (e: Throwable) {
cause = e
}
sink.close(cause)
}
}
fun SourceCrossinline.Factory.range(start: Int, count: Int): SourceCrossinline<Int> = source<Int> {
for (i in start until (start + count)) {
send(i)
}
}
suspend inline fun <E> SourceCrossinline<E>.consumeEach(crossinline action: suspend (E) -> Unit) {
consume(object : Sink<E> {
override suspend fun send(item: E) = action(item)
override fun close(cause: Throwable?) { cause?.let { throw it } }
})
}
suspend inline fun <E, R> SourceCrossinline<E>.fold(initial: R, crossinline operation: suspend (acc: R, E) -> R): R {
var acc = initial
consumeEach {
acc = operation(acc, it)
}
return acc
}
inline fun <E> SourceCrossinline<E>.filter(crossinline predicate: (E) -> Boolean) = source<E> {
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"
}
@@ -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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <init>(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 <clinit>(): void
private method <init>(): void
}
@kotlin.Metadata
public interface SourceCrossinline {
public final static field Factory: SourceCrossinline$Factory
inner class SourceCrossinline$Factory
static method <clinit>(): 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
}
@@ -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
@@ -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<Unit> {
@@ -30,7 +31,10 @@ fun builder(c: suspend () -> Unit) {
})
}
suspend fun calculate() = "OK"
suspend fun calculate() = suspendCoroutineOrReturn<String> {
it.resume("OK")
COROUTINE_SUSPENDED
}
fun box() : String {
var res = "FAIL 1"
@@ -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
@@ -1,2 +0,0 @@
// SKIP_TXT
inline fun foo(crossinline <!WRONG_MODIFIER_TARGET!>suspend<!> <!UNUSED_PARAMETER!>c<!>: () -> Unit) {}
@@ -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(<!REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE!>c: suspend () -> Unit<!>) {
suspend inline fun test(c: <!REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE!>suspend<!> () -> Unit) {
c()
val o = object: SuspendRunnable {
override suspend fun run() {
@@ -5,7 +5,7 @@ inline fun foo2(crossinline x: suspend () -> Unit) {}
<!NOTHING_TO_INLINE!>inline<!> fun foo3(noinline x: suspend () -> Unit) {}
<!NOTHING_TO_INLINE!>inline<!> fun foo4(<!INCOMPATIBLE_MODIFIERS!>noinline<!> <!INCOMPATIBLE_MODIFIERS!>crossinline<!> x: suspend () -> Unit) {}
suspend inline fun bar1(<!REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE!>x: suspend () -> Unit<!>) {}
suspend inline fun bar1(x: <!REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE!>suspend<!> () -> Unit) {}
suspend inline fun bar2(crossinline x: suspend () -> Unit) {}
suspend inline fun bar3(noinline x: suspend () -> Unit) {}
suspend inline fun bar4(<!INCOMPATIBLE_MODIFIERS!>noinline<!> <!INCOMPATIBLE_MODIFIERS!>crossinline<!> x: suspend () -> Unit) {}
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");