From bc6693fa6ed73fa5bf7ecf8ea2f6fc9dc033024b Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Tue, 6 Oct 2020 07:58:02 -0700 Subject: [PATCH] FIR2IR: element-wise SAM conversion for vararg --- .../generators/CallAndReferenceGenerator.kt | 35 ++++++++-- .../kotlin/fir/resolve/calls/Arguments.kt | 5 +- .../box/funInterface/funConversionInVararg.kt | 1 - compiler/testData/codegen/box/sam/kt31908.kt | 1 - .../samConversionInVarargs.fir.txt | 67 ++++++++++--------- .../samConversionInVarargsMixed.fir.txt | 11 +-- 6 files changed, 75 insertions(+), 45 deletions(-) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index 1b929141fd4..ce8c1cb8758 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.references.FirReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.render +import org.jetbrains.kotlin.fir.resolve.calls.getExpectedTypeForSAMConversion import org.jetbrains.kotlin.fir.resolve.calls.isFunctional import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType import org.jetbrains.kotlin.fir.resolve.toSymbol @@ -529,12 +530,37 @@ class CallAndReferenceGenerator( private fun IrExpression.applySamConversionIfNeeded( argument: FirExpression, - parameter: FirValueParameter? + parameter: FirValueParameter?, + shouldUnwrapVarargType: Boolean = false ): IrExpression { - if (parameter == null || !needSamConversion(argument, parameter)) { + if (parameter == null) { return this } - val samType = parameter.returnTypeRef.toIrType() + if (this is IrVararg) { + // element-wise SAM conversion if and only if we can build 1-to-1 mapping for elements. + if (argument !is FirVarargArgumentsExpression || argument.arguments.size != elements.size) { + return this + } + val argumentMapping = this.elements.zip(argument.arguments).toMap() + // [IrElementTransformer] is not preferred, since it's hard to visit vararg elements only. + val irVarargElements = elements as MutableList + irVarargElements.replaceAll { irVarargElement -> + if (irVarargElement is IrExpression) { + val firVarargArgument = + argumentMapping[irVarargElement] ?: error("Can't find the original FirExpression for ${irVarargElement.render()}") + irVarargElement.applySamConversionIfNeeded(firVarargArgument, parameter, shouldUnwrapVarargType = true) + } else + irVarargElement + } + return this + } + if (!needSamConversion(argument, parameter)) { + return this + } + var samType = parameter.returnTypeRef.toIrType() + if (shouldUnwrapVarargType) { + samType = samType.getArrayElementType(irBuiltIns) + } // Make sure the converted IrType owner indeed has a single abstract method, since FunctionReferenceLowering relies on it. if (!samType.isSamType) return this return IrTypeOperatorCallImpl(this.startOffset, this.endOffset, samType, IrTypeOperator.SAM_CONVERSION, samType, this) @@ -542,7 +568,8 @@ class CallAndReferenceGenerator( private fun needSamConversion(argument: FirExpression, parameter: FirValueParameter): Boolean { // If the expected type is a built-in functional type, we don't need SAM conversion. - if (parameter.returnTypeRef.coneType.isBuiltinFunctionalType(session)) { + val expectedType = argument.getExpectedTypeForSAMConversion(parameter) + if (expectedType is ConeTypeParameterType || expectedType.isBuiltinFunctionalType(session)) { return false } // On the other hand, the actual type should be a functional type. diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index 219609b3f83..c1b7f5a3fd3 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -360,7 +360,7 @@ private fun Candidate.prepareExpectedType( context: ResolutionContext ): ConeKotlinType? { if (parameter == null) return null - val basicExpectedType = argument.getExpectedType(session, parameter/*, LanguageVersionSettings*/) + val basicExpectedType = argument.getExpectedTypeForSAMConversion(parameter/*, LanguageVersionSettings*/) val expectedType = getExpectedTypeWithSAMConversion(session, argument, basicExpectedType, context) ?: basicExpectedType return this.substitutor.substituteOrSelf(expectedType) } @@ -391,8 +391,7 @@ fun FirExpression.isFunctional(session: FirSession): Boolean = else -> typeRef.coneTypeSafe()?.isBuiltinFunctionalType(session) == true } -internal fun FirExpression.getExpectedType( - session: FirSession, +fun FirExpression.getExpectedTypeForSAMConversion( parameter: FirValueParameter/*, languageVersionSettings: LanguageVersionSettings*/ ): ConeKotlinType { val shouldUnwrapVarargType = when (this) { diff --git a/compiler/testData/codegen/box/funInterface/funConversionInVararg.kt b/compiler/testData/codegen/box/funInterface/funConversionInVararg.kt index 5741ce3e58b..5dc007dbd2b 100644 --- a/compiler/testData/codegen/box/funInterface/funConversionInVararg.kt +++ b/compiler/testData/codegen/box/funInterface/funConversionInVararg.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument -// IGNORE_BACKEND_FIR: JVM_IR fun interface MyRunnable { fun run() diff --git a/compiler/testData/codegen/box/sam/kt31908.kt b/compiler/testData/codegen/box/sam/kt31908.kt index ae6700d1e89..3d7df05a9b4 100644 --- a/compiler/testData/codegen/box/sam/kt31908.kt +++ b/compiler/testData/codegen/box/sam/kt31908.kt @@ -1,5 +1,4 @@ // TARGET_BACKEND: JVM -// IGNORE_BACKEND_FIR: JVM_IR // FILE: kt31908.kt fun box(): String { var result = "failed" diff --git a/compiler/testData/ir/irText/expressions/funInterface/samConversionInVarargs.fir.txt b/compiler/testData/ir/irText/expressions/funInterface/samConversionInVarargs.fir.txt index f254a234fbd..632dab3c1e6 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/samConversionInVarargs.fir.txt +++ b/compiler/testData/ir/irText/expressions/funInterface/samConversionInVarargs.fir.txt @@ -24,34 +24,38 @@ FILE fqName: fileName:/samConversionInVarargs.kt BLOCK_BODY CALL 'public final fun useVararg (vararg foos: .IFoo): kotlin.Unit declared in ' type=kotlin.Unit origin=null foos: VARARG type=kotlin.Array.IFoo> varargElementType=.IFoo - FUN_EXPR type=kotlin.Function1 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit - VALUE_PARAMETER name:it index:0 type:kotlin.Int - BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (it: kotlin.Int): kotlin.Unit declared in .testLambda' - GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit + TYPE_OP type=.IFoo origin=SAM_CONVERSION typeOperand=.IFoo + FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit + VALUE_PARAMETER name:it index:0 type:kotlin.Int + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (it: kotlin.Int): kotlin.Unit declared in .testLambda' + GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit FUN name:testSeveralLambdas visibility:public modality:FINAL <> () returnType:kotlin.Unit BLOCK_BODY CALL 'public final fun useVararg (vararg foos: .IFoo): kotlin.Unit declared in ' type=kotlin.Unit origin=null foos: VARARG type=kotlin.Array.IFoo> varargElementType=.IFoo - FUN_EXPR type=kotlin.Function1 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit - VALUE_PARAMETER name:it index:0 type:kotlin.Int - BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (it: kotlin.Int): kotlin.Unit declared in .testSeveralLambdas' - GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit - FUN_EXPR type=kotlin.Function1 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit - VALUE_PARAMETER name:it index:0 type:kotlin.Int - BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (it: kotlin.Int): kotlin.Unit declared in .testSeveralLambdas' - GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit - FUN_EXPR type=kotlin.Function1 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit - VALUE_PARAMETER name:it index:0 type:kotlin.Int - BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (it: kotlin.Int): kotlin.Unit declared in .testSeveralLambdas' - GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit + TYPE_OP type=.IFoo origin=SAM_CONVERSION typeOperand=.IFoo + FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit + VALUE_PARAMETER name:it index:0 type:kotlin.Int + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (it: kotlin.Int): kotlin.Unit declared in .testSeveralLambdas' + GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit + TYPE_OP type=.IFoo origin=SAM_CONVERSION typeOperand=.IFoo + FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit + VALUE_PARAMETER name:it index:0 type:kotlin.Int + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (it: kotlin.Int): kotlin.Unit declared in .testSeveralLambdas' + GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit + TYPE_OP type=.IFoo origin=SAM_CONVERSION typeOperand=.IFoo + FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit + VALUE_PARAMETER name:it index:0 type:kotlin.Int + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (it: kotlin.Int): kotlin.Unit declared in .testSeveralLambdas' + GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit FUN name:withVarargOfInt visibility:public modality:FINAL <> (xs:kotlin.IntArray) returnType:kotlin.String VALUE_PARAMETER name:xs index:0 type:kotlin.IntArray varargElementType:kotlin.Int [vararg] BLOCK_BODY @@ -61,10 +65,11 @@ FILE fqName: fileName:/samConversionInVarargs.kt BLOCK_BODY CALL 'public final fun useVararg (vararg foos: .IFoo): kotlin.Unit declared in ' type=kotlin.Unit origin=null foos: VARARG type=kotlin.Array.IFoo> varargElementType=.IFoo - FUN_EXPR type=kotlin.Function1 origin=ADAPTED_FUNCTION_REFERENCE - FUN ADAPTER_FOR_CALLABLE_REFERENCE name:withVarargOfInt visibility:local modality:FINAL <> (p0:kotlin.Int) returnType:kotlin.Unit - VALUE_PARAMETER ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE name:p0 index:0 type:kotlin.Int - BLOCK_BODY - CALL 'public final fun withVarargOfInt (vararg xs: kotlin.Int): kotlin.String declared in ' type=kotlin.String origin=null - xs: VARARG type=kotlin.IntArray varargElementType=kotlin.Int - GET_VAR 'p0: kotlin.Int declared in .testAdaptedCR.withVarargOfInt' type=kotlin.Int origin=null + TYPE_OP type=.IFoo origin=SAM_CONVERSION typeOperand=.IFoo + FUN_EXPR type=kotlin.Function1 origin=ADAPTED_FUNCTION_REFERENCE + FUN ADAPTER_FOR_CALLABLE_REFERENCE name:withVarargOfInt visibility:local modality:FINAL <> (p0:kotlin.Int) returnType:kotlin.Unit + VALUE_PARAMETER ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE name:p0 index:0 type:kotlin.Int + BLOCK_BODY + CALL 'public final fun withVarargOfInt (vararg xs: kotlin.Int): kotlin.String declared in ' type=kotlin.String origin=null + xs: VARARG type=kotlin.IntArray varargElementType=kotlin.Int + GET_VAR 'p0: kotlin.Int declared in .testAdaptedCR.withVarargOfInt' type=kotlin.Int origin=null diff --git a/compiler/testData/ir/irText/expressions/funInterface/samConversionInVarargsMixed.fir.txt b/compiler/testData/ir/irText/expressions/funInterface/samConversionInVarargsMixed.fir.txt index 4aa43a25cad..8c9e4438ad6 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/samConversionInVarargsMixed.fir.txt +++ b/compiler/testData/ir/irText/expressions/funInterface/samConversionInVarargsMixed.fir.txt @@ -26,11 +26,12 @@ FILE fqName: fileName:/samConversionInVarargsMixed.kt GET_VAR 'a: kotlin.Any declared in .test' type=kotlin.Any origin=null then: CALL 'public final fun foo (vararg rs: .MyRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null rs: VARARG type=kotlin.Array.MyRunnable> varargElementType=.MyRunnable - FUN_EXPR type=kotlin.Function0 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit - BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (): kotlin.Unit declared in .test' - GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit + TYPE_OP type=.MyRunnable origin=SAM_CONVERSION typeOperand=.MyRunnable + FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (): kotlin.Unit declared in .test' + GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit GET_VAR 'r: .MyRunnable declared in .test' type=.MyRunnable origin=null TYPE_OP type=.MyRunnable origin=IMPLICIT_CAST typeOperand=.MyRunnable GET_VAR 'a: kotlin.Any declared in .test' type=kotlin.Any origin=null