From 85f55dec9aecdeeb5596497c16d9e42485603c38 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 17 Dec 2018 17:00:58 +0300 Subject: [PATCH] Support SAM conversion in psi2ir SAM conversion takes a function value (function type or a subtype), and produces a SAM interface value. --- .../backend/common/CheckIrElementVisitor.kt | 7 +- .../backend/js/lower/TypeOperatorLowering.kt | 6 +- .../inline/DeepCopyIrTreeWithDescriptors.kt | 3 +- .../generators/ArgumentsGenerationUtils.kt | 74 ++++++++- .../kotlin/psi2ir/generators/CallGenerator.kt | 22 +++ .../transformations/InsertImplicitCasts.kt | 23 +++ .../ir/expressions/IrTypeOperatorCall.kt | 3 +- .../irText/expressions/sam/samConstructors.kt | 7 + .../expressions/sam/samConstructors.txt | 27 ++++ .../irText/expressions/sam/samConversions.kt | 31 ++++ .../irText/expressions/sam/samConversions.txt | 63 ++++++++ .../sam/samConversionsWithSmartCasts.kt | 71 +++++++++ .../sam/samConversionsWithSmartCasts.txt | 143 ++++++++++++++++++ .../testData/ir/irText/lambdas/samAdapter.txt | 5 +- .../kotlin/ir/IrTextTestCaseGenerated.java | 28 ++++ 15 files changed, 494 insertions(+), 19 deletions(-) create mode 100644 compiler/testData/ir/irText/expressions/sam/samConstructors.kt create mode 100644 compiler/testData/ir/irText/expressions/sam/samConstructors.txt create mode 100644 compiler/testData/ir/irText/expressions/sam/samConversions.kt create mode 100644 compiler/testData/ir/irText/expressions/sam/samConversions.txt create mode 100644 compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.kt create mode 100644 compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.txt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt index 42e5cbfc970..d9684c44fbe 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt @@ -19,13 +19,13 @@ package org.jetbrains.kotlin.backend.common import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.isAnnotationClass import org.jetbrains.kotlin.ir.util.render -import org.jetbrains.kotlin.ir.util.superTypes import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal import org.jetbrains.kotlin.types.KotlinType @@ -166,7 +166,8 @@ class CheckIrElementVisitor( IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.IMPLICIT_NOTNULL, IrTypeOperator.IMPLICIT_COERCION_TO_UNIT, - IrTypeOperator.IMPLICIT_INTEGER_COERCION -> typeOperand.toKotlinType() + IrTypeOperator.IMPLICIT_INTEGER_COERCION, + IrTypeOperator.SAM_CONVERSION -> typeOperand.toKotlinType() IrTypeOperator.SAFE_CAST -> typeOperand.makeNullable().toKotlinType() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt index 5ba144ead9e..376e07d41d6 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt @@ -21,10 +21,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.isFunctionOrKFunction -import org.jetbrains.kotlin.ir.util.isInterface -import org.jetbrains.kotlin.ir.util.isNullable -import org.jetbrains.kotlin.ir.util.isTypeParameter +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformer class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { @@ -80,6 +77,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { IrTypeOperator.NOT_INSTANCEOF -> lowerInstanceOf(expression, data, true) IrTypeOperator.CAST -> lowerCast(expression, data, false) IrTypeOperator.SAFE_CAST -> lowerCast(expression, data, true) + IrTypeOperator.SAM_CONVERSION -> TODO("SAM conversion: ${expression.render()}") } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/DeepCopyIrTreeWithDescriptors.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/DeepCopyIrTreeWithDescriptors.kt index 16c8c0f1db3..530a0d19da1 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/DeepCopyIrTreeWithDescriptors.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/DeepCopyIrTreeWithDescriptors.kt @@ -832,7 +832,8 @@ internal class DeepCopyIrTreeWithDescriptors(val targetDescriptor: FunctionDescr IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.IMPLICIT_NOTNULL, IrTypeOperator.IMPLICIT_COERCION_TO_UNIT, - IrTypeOperator.IMPLICIT_INTEGER_COERCION -> type + IrTypeOperator.IMPLICIT_INTEGER_COERCION, + IrTypeOperator.SAM_CONVERSION -> type IrTypeOperator.SAFE_CAST -> type.makeNullable() IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF -> context.irBuiltIns.booleanType diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ArgumentsGenerationUtils.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ArgumentsGenerationUtils.kt index e634b387b34..bf7f96a7d11 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ArgumentsGenerationUtils.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ArgumentsGenerationUtils.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.psi2ir.generators import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType +import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor @@ -24,7 +25,11 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.expressions.IrDeclarationReference import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpressionWithCopy +import org.jetbrains.kotlin.ir.expressions.IrTypeOperator import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.types.classifierOrFail +import org.jetbrains.kotlin.load.java.sam.SamAdapterDescriptor +import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.psiUtil.endOffset @@ -37,6 +42,7 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getSuperCallExpressio import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.scopes.receivers.* +import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeSubstitutor @@ -158,13 +164,15 @@ fun StatementGenerator.generateCallReceiver( ): CallReceiver { val dispatchReceiverValue: IntermediateValue? val extensionReceiverValue: IntermediateValue? + val startOffset = ktDefaultElement.startOffsetSkippingComments + val endOffset = ktDefaultElement.endOffset when (calleeDescriptor) { is ImportedFromObjectCallableDescriptor<*> -> { assert(dispatchReceiver == null) { "Call for member imported from object $calleeDescriptor has non-null dispatch receiver $dispatchReceiver" } dispatchReceiverValue = - generateReceiverForCalleeImportedFromObject(ktDefaultElement.startOffsetSkippingComments, ktDefaultElement.endOffset, calleeDescriptor) + generateReceiverForCalleeImportedFromObject(startOffset, endOffset, calleeDescriptor) extensionReceiverValue = generateReceiverOrNull(ktDefaultElement, extensionReceiver) } is TypeAliasConstructorDescriptor -> { @@ -186,7 +194,7 @@ fun StatementGenerator.generateCallReceiver( SimpleCallReceiver(dispatchReceiverValue, extensionReceiverValue) extensionReceiverValue != null || dispatchReceiverValue != null -> SafeCallReceiver( - this, ktDefaultElement.startOffsetSkippingComments, ktDefaultElement.endOffset, + this, startOffset, endOffset, extensionReceiverValue, dispatchReceiverValue, isAssignmentReceiver ) else -> @@ -250,7 +258,7 @@ fun StatementGenerator.generateVarargExpressionUsing( return irVararg } -private fun StatementGenerator.generateValueArgument( +fun StatementGenerator.generateValueArgument( valueArgument: ResolvedValueArgument, valueParameter: ValueParameterDescriptor ) = generateValueArgumentUsing(valueArgument, valueParameter) { generateExpression(it) } @@ -369,6 +377,51 @@ private fun StatementGenerator.pregenerateValueArguments(call: CallBuilder, reso pregenerateValueArgumentsUsing(call, resolvedCall) { generateExpression(it) } + + generateSamConversionForValueArgumentsIfRequired(call, resolvedCall.resultingDescriptor) +} + +private fun StatementGenerator.generateSamConversionForValueArgumentsIfRequired(call: CallBuilder, originalDescriptor: CallableDescriptor) { + val underlyingDescriptor = when (originalDescriptor) { + is SamAdapterDescriptor<*> -> originalDescriptor.baseDescriptorForSynthetic + is SamAdapterExtensionFunctionDescriptor -> originalDescriptor.baseDescriptorForSynthetic + else -> return + } + + val originalValueParameters = originalDescriptor.valueParameters + val underlyingValueParameters = underlyingDescriptor.valueParameters + + assert(originalValueParameters.size == underlyingValueParameters.size) { + "Mismatching value parameters, $originalDescriptor vs $underlyingDescriptor: " + + "${originalValueParameters.size} != ${underlyingValueParameters.size}" + } + assert(originalValueParameters.size == call.argumentsCount) { + "Mismatching value parameters, $originalDescriptor vs call: " + + "${originalValueParameters.size} != ${call.argumentsCount}" + } + + for (i in underlyingValueParameters.indices) { + val originalParameterType = originalValueParameters[i].type + val underlyingParameterType = underlyingValueParameters[i].type + + if (!SingleAbstractMethodUtils.isSamType(underlyingParameterType)) continue + if (!originalParameterType.isFunctionTypeOrSubtype) continue + + val originalArgument = call.irValueArgumentsByIndex[i] ?: continue + + val targetType = underlyingParameterType.toIrType() + val targetClassifier = targetType.classifierOrFail + + call.irValueArgumentsByIndex[i] = + IrTypeOperatorCallImpl( + originalArgument.startOffset, originalArgument.endOffset, + targetType, + IrTypeOperator.SAM_CONVERSION, + targetType, + targetClassifier, + originalArgument + ) + } } fun StatementGenerator.pregenerateValueArgumentsUsing( @@ -398,15 +451,20 @@ fun StatementGenerator.pregenerateCallReceivers(resolvedCall: ResolvedCall<*>): return call } +private fun unwrapSpecialDescriptor(originalDescriptor: CallableDescriptor): CallableDescriptor = + when (originalDescriptor) { + is ImportedFromObjectCallableDescriptor<*> -> unwrapSpecialDescriptor(originalDescriptor.callableFromObject) + is TypeAliasConstructorDescriptor -> originalDescriptor.underlyingConstructorDescriptor + is SamAdapterDescriptor<*> -> unwrapSpecialDescriptor(originalDescriptor.baseDescriptorForSynthetic) + is SamAdapterExtensionFunctionDescriptor -> unwrapSpecialDescriptor(originalDescriptor.baseDescriptorForSynthetic) + else -> originalDescriptor + } + fun unwrapCallableDescriptorAndTypeArguments(resolvedCall: ResolvedCall<*>): CallBuilder { val originalDescriptor = resolvedCall.resultingDescriptor val candidateDescriptor = resolvedCall.candidateDescriptor - val unwrappedDescriptor = when (originalDescriptor) { - is ImportedFromObjectCallableDescriptor<*> -> originalDescriptor.callableFromObject - is TypeAliasConstructorDescriptor -> originalDescriptor.underlyingConstructorDescriptor - else -> originalDescriptor - } + val unwrappedDescriptor = unwrapSpecialDescriptor(originalDescriptor) val originalTypeArguments = resolvedCall.typeArguments val unsubstitutedUnwrappedDescriptor = unwrappedDescriptor.original diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt index 0791de8a6d0..3fb4e54da67 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt @@ -21,7 +21,9 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.util.referenceFunction +import org.jetbrains.kotlin.load.java.sam.SamConstructorDescriptor import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments @@ -40,6 +42,8 @@ class CallGenerator(statementGenerator: StatementGenerator) : StatementGenerator val descriptor = call.descriptor return when (descriptor) { + is SamConstructorDescriptor -> + generateSamConstructorCall(descriptor, startOffset, endOffset, call) is PropertyDescriptor -> generatePropertyGetterCall(descriptor, startOffset, endOffset, call) is FunctionDescriptor -> @@ -51,6 +55,24 @@ class CallGenerator(statementGenerator: StatementGenerator) : StatementGenerator } } + private fun generateSamConstructorCall( + descriptor: SamConstructorDescriptor, + startOffset: Int, + endOffset: Int, + call: CallBuilder + ): IrExpression { + val targetType = descriptor.returnType!!.toIrType() + + return IrTypeOperatorCallImpl( + startOffset, endOffset, + targetType, + IrTypeOperator.SAM_CONVERSION, + targetType, + targetType.classifierOrFail, + call.irValueArgumentsByIndex[0]!! + ) + } + fun generateValueReference( startOffset: Int, endOffset: Int, diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt index 01cbda8fe72..e64db342266 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt @@ -30,7 +30,10 @@ import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.types.impl.originalKotlinType import org.jetbrains.kotlin.ir.util.TypeTranslator +import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor +import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils import org.jetbrains.kotlin.psi2ir.containsNull import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext import org.jetbrains.kotlin.types.KotlinType @@ -167,6 +170,26 @@ open class InsertImplicitCasts( finallyExpression = finallyExpression?.coerceToUnit() } + override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression = + if (expression.operator == IrTypeOperator.SAM_CONVERSION) + expression.coerceArgumentToFunctionalType() + else + super.visitTypeOperator(expression) + + private fun IrTypeOperatorCall.coerceArgumentToFunctionalType(): IrExpression { + val targetClassDescriptor = typeOperandClassifier.descriptor as? JavaClassDescriptor + ?: throw AssertionError("Target type of $operator should be a Java class: ${render()}") + + val singleAbstractMethod = SingleAbstractMethodUtils.getSingleAbstractMethodOrNull(targetClassDescriptor) + ?: throw AssertionError("$targetClassDescriptor should have a single abstract method") + + val functionalType = SingleAbstractMethodUtils.getFunctionTypeForAbstractMethod(singleAbstractMethod, false) + + argument = argument.cast(functionalType) + + return this + } + override fun visitVararg(expression: IrVararg): IrExpression = expression.transformPostfix { elements.forEachIndexed { i, element -> diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt index c233add29f7..1776d14a422 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt @@ -27,7 +27,8 @@ enum class IrTypeOperator { IMPLICIT_INTEGER_COERCION, SAFE_CAST, INSTANCEOF, - NOT_INSTANCEOF; + NOT_INSTANCEOF, + SAM_CONVERSION; } interface IrTypeOperatorCall : IrExpression { diff --git a/compiler/testData/ir/irText/expressions/sam/samConstructors.kt b/compiler/testData/ir/irText/expressions/sam/samConstructors.kt new file mode 100644 index 00000000000..656af50977d --- /dev/null +++ b/compiler/testData/ir/irText/expressions/sam/samConstructors.kt @@ -0,0 +1,7 @@ +// WITH_JDK +fun test1() = Runnable { } + +fun test2(a: () -> Unit) = Runnable(a) + +fun foo() {} +fun test3() = Runnable(::foo) \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/sam/samConstructors.txt b/compiler/testData/ir/irText/expressions/sam/samConstructors.txt new file mode 100644 index 00000000000..292c02938d8 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/sam/samConstructors.txt @@ -0,0 +1,27 @@ +FILE fqName: fileName:/samConstructors.kt + FUN name:test1 visibility:public modality:FINAL <> () returnType:java.lang.Runnable flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test1(): Runnable' + TYPE_OP type=java.lang.Runnable origin=SAM_CONVERSION typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + BLOCK type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): Unit' + GET_OBJECT 'Unit' type=kotlin.Unit + FUNCTION_REFERENCE '(): Unit' type=kotlin.Function0 origin=LAMBDA + FUN name:test2 visibility:public modality:FINAL <> (a:kotlin.Function0) returnType:java.lang.Runnable flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Function0 flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test2(() -> Unit): Runnable' + TYPE_OP type=java.lang.Runnable origin=SAM_CONVERSION typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + FUN name:foo visibility:public modality:FINAL <> () returnType:kotlin.Unit flags: + BLOCK_BODY + FUN name:test3 visibility:public modality:FINAL <> () returnType:java.lang.Runnable flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test3(): Runnable' + TYPE_OP type=java.lang.Runnable origin=SAM_CONVERSION typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + FUNCTION_REFERENCE 'foo(): Unit' type=kotlin.reflect.KFunction0 origin=null diff --git a/compiler/testData/ir/irText/expressions/sam/samConversions.kt b/compiler/testData/ir/irText/expressions/sam/samConversions.kt new file mode 100644 index 00000000000..c8420db8188 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/sam/samConversions.kt @@ -0,0 +1,31 @@ +// WITH_JDK +// FILE: samConversions.kt +fun J.test0(a: Runnable) { + J.runStatic(a) + runIt(a) +} + +fun test1() { + J.runStatic { test1() } +} + +fun J.test2() { + runIt { test1() } +} + +fun J.test3(a: () -> Unit) { + run2(a, a) +} + +fun J.test4(a: () -> Unit, b: () -> Unit, flag: Boolean) { + runIt(if (flag) a else b) +} + +// FILE: J.java +public class J { + public static void runStatic(Runnable r) {} + + public void runIt(Runnable r) {} + + public void run2(Runnable r1, Runnable r2) {} +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/sam/samConversions.txt b/compiler/testData/ir/irText/expressions/sam/samConversions.txt new file mode 100644 index 00000000000..c4b7b199300 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/sam/samConversions.txt @@ -0,0 +1,63 @@ +FILE fqName: fileName:/samConversions.kt + FUN name:test0 visibility:public modality:FINAL <> ($receiver:J, a:java.lang.Runnable) returnType:kotlin.Unit flags: + $receiver: VALUE_PARAMETER name: type:J flags: + VALUE_PARAMETER name:a index:0 type:java.lang.Runnable flags: + BLOCK_BODY + CALL 'runStatic(Runnable!): Unit' type=kotlin.Unit origin=null + r: GET_VAR 'value-parameter a: Runnable' type=java.lang.Runnable origin=null + CALL 'runIt(Runnable!): Unit' type=kotlin.Unit origin=null + $this: GET_VAR 'this@test0: J' type=J origin=null + r: GET_VAR 'value-parameter a: Runnable' type=java.lang.Runnable origin=null + FUN name:test1 visibility:public modality:FINAL <> () returnType:kotlin.Unit flags: + BLOCK_BODY + CALL 'runStatic(Runnable!): Unit' type=kotlin.Unit origin=null + r: TYPE_OP type=java.lang.Runnable? origin=SAM_CONVERSION typeOperand=java.lang.Runnable? + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + BLOCK type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): Unit' + CALL 'test1(): Unit' type=kotlin.Unit origin=null + FUNCTION_REFERENCE '(): Unit' type=kotlin.Function0 origin=LAMBDA + FUN name:test2 visibility:public modality:FINAL <> ($receiver:J) returnType:kotlin.Unit flags: + $receiver: VALUE_PARAMETER name: type:J flags: + BLOCK_BODY + CALL 'runIt(Runnable!): Unit' type=kotlin.Unit origin=null + $this: GET_VAR 'this@test2: J' type=J origin=null + r: TYPE_OP type=java.lang.Runnable? origin=SAM_CONVERSION typeOperand=java.lang.Runnable? + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + BLOCK type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): Unit' + CALL 'test1(): Unit' type=kotlin.Unit origin=null + FUNCTION_REFERENCE '(): Unit' type=kotlin.Function0 origin=LAMBDA + FUN name:test3 visibility:public modality:FINAL <> ($receiver:J, a:kotlin.Function0) returnType:kotlin.Unit flags: + $receiver: VALUE_PARAMETER name: type:J flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Function0 flags: + BLOCK_BODY + CALL 'run2(Runnable!, Runnable!): Unit' type=kotlin.Unit origin=null + $this: GET_VAR 'this@test3: J' type=J origin=null + r1: TYPE_OP type=java.lang.Runnable? origin=SAM_CONVERSION typeOperand=java.lang.Runnable? + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + r2: TYPE_OP type=java.lang.Runnable? origin=SAM_CONVERSION typeOperand=java.lang.Runnable? + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + FUN name:test4 visibility:public modality:FINAL <> ($receiver:J, a:kotlin.Function0, b:kotlin.Function0, flag:kotlin.Boolean) returnType:kotlin.Unit flags: + $receiver: VALUE_PARAMETER name: type:J flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Function0 flags: + VALUE_PARAMETER name:b index:1 type:kotlin.Function0 flags: + VALUE_PARAMETER name:flag index:2 type:kotlin.Boolean flags: + BLOCK_BODY + CALL 'runIt(Runnable!): Unit' type=kotlin.Unit origin=null + $this: GET_VAR 'this@test4: J' type=J origin=null + r: TYPE_OP type=java.lang.Runnable? origin=SAM_CONVERSION typeOperand=java.lang.Runnable? + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + WHEN type=kotlin.Function0 origin=null + BRANCH + if: GET_VAR 'value-parameter flag: Boolean' type=kotlin.Boolean origin=null + then: GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: GET_VAR 'value-parameter b: () -> Unit' type=kotlin.Function0 origin=null diff --git a/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.kt b/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.kt new file mode 100644 index 00000000000..2c5223c3023 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.kt @@ -0,0 +1,71 @@ +// !LANGUAGE: -NewInference +// NB new inference doesn't really work with old JVM back-end. + +// WITH_JDK +// FILE: samConversionsWithSmartCasts.kt + +fun test1(a: () -> Unit) { + if (a is Runnable) { + J.runStatic(a) + } +} + +fun test2(a: () -> Unit) { + if (a is Runnable) { + J().run1(a) + } +} + +fun test3(a: () -> Unit) { + if (a is Runnable) { + J().run2(a, a) + } +} + +fun test4(a: () -> Unit, b: () -> Unit) { + if (a is Runnable) { + J().run2(a, b) + } +} + +fun test5(a: Any) { + if (a is Runnable) { + J().run1(a) + } +} + +fun test5x(a: Any) { + if (a is Runnable) { + a as () -> Unit + J().run1(a) + } +} + +fun test6(a: Any) { + a as () -> Unit + J().run1(a) +} + +fun test7(a: (Int) -> Int) { + a as () -> Unit + J().run1(a) +} + +fun test8(a: () -> Unit) { + J().run1(J.id(a)) +} + +fun test9() { + J().run1(::test9) +} + +// FILE: J.java +public class J { + public static void runStatic(Runnable r) {} + + public void run1(Runnable r) {} + + public void run2(Runnable r1, Runnable r2) {} + + public static T id(T x) { return x; } +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.txt b/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.txt new file mode 100644 index 00000000000..475ebb05e51 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.txt @@ -0,0 +1,143 @@ +FILE fqName: fileName:/samConversionsWithSmartCasts.kt + FUN name:test1 visibility:public modality:FINAL <> (a:kotlin.Function0) returnType:kotlin.Unit flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Function0 flags: + BLOCK_BODY + WHEN type=kotlin.Unit origin=null + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + then: BLOCK type=kotlin.Unit origin=null + CALL 'runStatic(Runnable!): Unit' type=kotlin.Unit origin=null + r: TYPE_OP type=java.lang.Runnable origin=IMPLICIT_CAST typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + FUN name:test2 visibility:public modality:FINAL <> (a:kotlin.Function0) returnType:kotlin.Unit flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Function0 flags: + BLOCK_BODY + WHEN type=kotlin.Unit origin=null + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + then: BLOCK type=kotlin.Unit origin=null + CALL 'run1(Runnable!): Unit' type=kotlin.Unit origin=null + $this: CALL 'constructor J()' type=J origin=null + r: TYPE_OP type=java.lang.Runnable origin=IMPLICIT_CAST typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + FUN name:test3 visibility:public modality:FINAL <> (a:kotlin.Function0) returnType:kotlin.Unit flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Function0 flags: + BLOCK_BODY + WHEN type=kotlin.Unit origin=null + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + then: BLOCK type=kotlin.Unit origin=null + CALL 'run2(Runnable!, Runnable!): Unit' type=kotlin.Unit origin=null + $this: CALL 'constructor J()' type=J origin=null + r1: TYPE_OP type=java.lang.Runnable origin=IMPLICIT_CAST typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + r2: TYPE_OP type=java.lang.Runnable origin=IMPLICIT_CAST typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + FUN name:test4 visibility:public modality:FINAL <> (a:kotlin.Function0, b:kotlin.Function0) returnType:kotlin.Unit flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Function0 flags: + VALUE_PARAMETER name:b index:1 type:kotlin.Function0 flags: + BLOCK_BODY + WHEN type=kotlin.Unit origin=null + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + then: BLOCK type=kotlin.Unit origin=null + CALL 'run2(Runnable!, Runnable!): Unit' type=kotlin.Unit origin=null + $this: CALL 'constructor J()' type=J origin=null + r1: TYPE_OP type=java.lang.Runnable? origin=SAM_CONVERSION typeOperand=java.lang.Runnable? + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + r2: TYPE_OP type=java.lang.Runnable? origin=SAM_CONVERSION typeOperand=java.lang.Runnable? + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter b: () -> Unit' type=kotlin.Function0 origin=null + FUN name:test5 visibility:public modality:FINAL <> (a:kotlin.Any) returnType:kotlin.Unit flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Any flags: + BLOCK_BODY + WHEN type=kotlin.Unit origin=null + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: Any' type=kotlin.Any origin=null + then: BLOCK type=kotlin.Unit origin=null + CALL 'run1(Runnable!): Unit' type=kotlin.Unit origin=null + $this: CALL 'constructor J()' type=J origin=null + r: TYPE_OP type=java.lang.Runnable origin=IMPLICIT_CAST typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: Any' type=kotlin.Any origin=null + FUN name:test5x visibility:public modality:FINAL <> (a:kotlin.Any) returnType:kotlin.Unit flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Any flags: + BLOCK_BODY + WHEN type=kotlin.Unit origin=null + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: Any' type=kotlin.Any origin=null + then: BLOCK type=kotlin.Unit origin=null + TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public flags: superTypes:[kotlin.Any] + TYPE_OP type=kotlin.Function0 origin=CAST typeOperand=kotlin.Function0 + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Function0 modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Function] + GET_VAR 'value-parameter a: Any' type=kotlin.Any origin=null + CALL 'run1(Runnable!): Unit' type=kotlin.Unit origin=null + $this: CALL 'constructor J()' type=J origin=null + r: TYPE_OP type=java.lang.Runnable origin=IMPLICIT_CAST typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter a: Any' type=kotlin.Any origin=null + FUN name:test6 visibility:public modality:FINAL <> (a:kotlin.Any) returnType:kotlin.Unit flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Any flags: + BLOCK_BODY + TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public flags: superTypes:[kotlin.Any] + TYPE_OP type=kotlin.Function0 origin=CAST typeOperand=kotlin.Function0 + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Function0 modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Function] + GET_VAR 'value-parameter a: Any' type=kotlin.Any origin=null + CALL 'run1(Runnable!): Unit' type=kotlin.Unit origin=null + $this: CALL 'constructor J()' type=J origin=null + r: TYPE_OP type=java.lang.Runnable? origin=SAM_CONVERSION typeOperand=java.lang.Runnable? + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + TYPE_OP type=kotlin.Function0 origin=IMPLICIT_CAST typeOperand=kotlin.Function0 + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Function0 modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Function] + GET_VAR 'value-parameter a: Any' type=kotlin.Any origin=null + FUN name:test7 visibility:public modality:FINAL <> (a:kotlin.Function1) returnType:kotlin.Unit flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Function1 flags: + BLOCK_BODY + TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public flags: superTypes:[kotlin.Any] + TYPE_OP type=kotlin.Function0 origin=CAST typeOperand=kotlin.Function0 + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Function0 modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Function] + GET_VAR 'value-parameter a: (Int) -> Int' type=kotlin.Function1 origin=null + CALL 'run1(Runnable!): Unit' type=kotlin.Unit origin=null + $this: CALL 'constructor J()' type=J origin=null + r: TYPE_OP type=java.lang.Runnable? origin=SAM_CONVERSION typeOperand=java.lang.Runnable? + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + TYPE_OP type=kotlin.Function0 origin=IMPLICIT_CAST typeOperand=kotlin.Function0 + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Function0 modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Function] + GET_VAR 'value-parameter a: (Int) -> Int' type=kotlin.Function1 origin=null + FUN name:test8 visibility:public modality:FINAL <> (a:kotlin.Function0) returnType:kotlin.Unit flags: + VALUE_PARAMETER name:a index:0 type:kotlin.Function0 flags: + BLOCK_BODY + CALL 'run1(Runnable!): Unit' type=kotlin.Unit origin=null + $this: CALL 'constructor J()' type=J origin=null + r: TYPE_OP type=java.lang.Runnable? origin=SAM_CONVERSION typeOperand=java.lang.Runnable? + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + CALL 'id(T!): T!' type=kotlin.Function0? origin=null + : kotlin.Function0? + x: GET_VAR 'value-parameter a: () -> Unit' type=kotlin.Function0 origin=null + FUN name:test9 visibility:public modality:FINAL <> () returnType:kotlin.Unit flags: + BLOCK_BODY + CALL 'run1(Runnable!): Unit' type=kotlin.Unit origin=null + $this: CALL 'constructor J()' type=J origin=null + r: TYPE_OP type=java.lang.Runnable? origin=SAM_CONVERSION typeOperand=java.lang.Runnable? + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + FUNCTION_REFERENCE 'test9(): Unit' type=kotlin.reflect.KFunction0 origin=null diff --git a/compiler/testData/ir/irText/lambdas/samAdapter.txt b/compiler/testData/ir/irText/lambdas/samAdapter.txt index 4820cce3a90..32e3fb194c8 100644 --- a/compiler/testData/ir/irText/lambdas/samAdapter.txt +++ b/compiler/testData/ir/irText/lambdas/samAdapter.txt @@ -2,8 +2,9 @@ FILE fqName: fileName:/samAdapter.kt FUN name:test1 visibility:public modality:FINAL <> () returnType:kotlin.Unit flags: BLOCK_BODY VAR name:hello type:java.lang.Runnable flags:val - CALL 'Runnable(() -> Unit): Runnable' type=java.lang.Runnable origin=null - function: BLOCK type=kotlin.Function0 origin=LAMBDA + TYPE_OP type=java.lang.Runnable origin=SAM_CONVERSION typeOperand=java.lang.Runnable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Runnable modality:ABSTRACT visibility:public flags: superTypes:[kotlin.Any] + BLOCK type=kotlin.Function0 origin=LAMBDA FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit flags: BLOCK_BODY RETURN type=kotlin.Nothing from='(): Unit' diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index d3c582035e1..43690d4fa01 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -1284,6 +1284,34 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { runTest("compiler/testData/ir/irText/expressions/floatingPointComparisons/whenByFloatingPoint.kt"); } } + + @TestMetadata("compiler/testData/ir/irText/expressions/sam") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Sam extends AbstractIrTextTestCase { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInSam() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/irText/expressions/sam"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("samConstructors.kt") + public void testSamConstructors() throws Exception { + runTest("compiler/testData/ir/irText/expressions/sam/samConstructors.kt"); + } + + @TestMetadata("samConversions.kt") + public void testSamConversions() throws Exception { + runTest("compiler/testData/ir/irText/expressions/sam/samConversions.kt"); + } + + @TestMetadata("samConversionsWithSmartCasts.kt") + public void testSamConversionsWithSmartCasts() throws Exception { + runTest("compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.kt"); + } + } } @TestMetadata("compiler/testData/ir/irText/lambdas")