FIR2IR: element-wise SAM conversion for vararg
This commit is contained in:
committed by
Mikhail Glukhikh
parent
5d394528f6
commit
bc6693fa6e
+31
-4
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.references.FirReference
|
|||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
||||||
import org.jetbrains.kotlin.fir.render
|
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.calls.isFunctional
|
||||||
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
|
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
|
||||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||||
@@ -529,12 +530,37 @@ class CallAndReferenceGenerator(
|
|||||||
|
|
||||||
private fun IrExpression.applySamConversionIfNeeded(
|
private fun IrExpression.applySamConversionIfNeeded(
|
||||||
argument: FirExpression,
|
argument: FirExpression,
|
||||||
parameter: FirValueParameter?
|
parameter: FirValueParameter?,
|
||||||
|
shouldUnwrapVarargType: Boolean = false
|
||||||
): IrExpression {
|
): IrExpression {
|
||||||
if (parameter == null || !needSamConversion(argument, parameter)) {
|
if (parameter == null) {
|
||||||
return this
|
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<IrVarargElement>
|
||||||
|
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.
|
// Make sure the converted IrType owner indeed has a single abstract method, since FunctionReferenceLowering relies on it.
|
||||||
if (!samType.isSamType) return this
|
if (!samType.isSamType) return this
|
||||||
return IrTypeOperatorCallImpl(this.startOffset, this.endOffset, samType, IrTypeOperator.SAM_CONVERSION, samType, 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 {
|
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 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
|
return false
|
||||||
}
|
}
|
||||||
// On the other hand, the actual type should be a functional type.
|
// On the other hand, the actual type should be a functional type.
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ private fun Candidate.prepareExpectedType(
|
|||||||
context: ResolutionContext
|
context: ResolutionContext
|
||||||
): ConeKotlinType? {
|
): ConeKotlinType? {
|
||||||
if (parameter == null) return null
|
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
|
val expectedType = getExpectedTypeWithSAMConversion(session, argument, basicExpectedType, context) ?: basicExpectedType
|
||||||
return this.substitutor.substituteOrSelf(expectedType)
|
return this.substitutor.substituteOrSelf(expectedType)
|
||||||
}
|
}
|
||||||
@@ -391,8 +391,7 @@ fun FirExpression.isFunctional(session: FirSession): Boolean =
|
|||||||
else -> typeRef.coneTypeSafe<ConeKotlinType>()?.isBuiltinFunctionalType(session) == true
|
else -> typeRef.coneTypeSafe<ConeKotlinType>()?.isBuiltinFunctionalType(session) == true
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun FirExpression.getExpectedType(
|
fun FirExpression.getExpectedTypeForSAMConversion(
|
||||||
session: FirSession,
|
|
||||||
parameter: FirValueParameter/*, languageVersionSettings: LanguageVersionSettings*/
|
parameter: FirValueParameter/*, languageVersionSettings: LanguageVersionSettings*/
|
||||||
): ConeKotlinType {
|
): ConeKotlinType {
|
||||||
val shouldUnwrapVarargType = when (this) {
|
val shouldUnwrapVarargType = when (this) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument
|
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
|
|
||||||
fun interface MyRunnable {
|
fun interface MyRunnable {
|
||||||
fun run()
|
fun run()
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
// FILE: kt31908.kt
|
// FILE: kt31908.kt
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
var result = "failed"
|
var result = "failed"
|
||||||
|
|||||||
+36
-31
@@ -24,34 +24,38 @@ FILE fqName:<root> fileName:/samConversionInVarargs.kt
|
|||||||
BLOCK_BODY
|
BLOCK_BODY
|
||||||
CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
||||||
foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo
|
foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo
|
||||||
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
|
TYPE_OP type=<root>.IFoo origin=SAM_CONVERSION typeOperand=<root>.IFoo
|
||||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
|
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
|
||||||
VALUE_PARAMETER name:it index:0 type:kotlin.Int
|
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
|
||||||
BLOCK_BODY
|
VALUE_PARAMETER name:it index:0 type:kotlin.Int
|
||||||
RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.testLambda'
|
BLOCK_BODY
|
||||||
GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit
|
RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.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
|
FUN name:testSeveralLambdas visibility:public modality:FINAL <> () returnType:kotlin.Unit
|
||||||
BLOCK_BODY
|
BLOCK_BODY
|
||||||
CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
||||||
foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo
|
foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo
|
||||||
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
|
TYPE_OP type=<root>.IFoo origin=SAM_CONVERSION typeOperand=<root>.IFoo
|
||||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
|
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
|
||||||
VALUE_PARAMETER name:it index:0 type:kotlin.Int
|
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
|
||||||
BLOCK_BODY
|
VALUE_PARAMETER name:it index:0 type:kotlin.Int
|
||||||
RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.testSeveralLambdas'
|
BLOCK_BODY
|
||||||
GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit
|
RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.testSeveralLambdas'
|
||||||
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
|
GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit
|
||||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
|
TYPE_OP type=<root>.IFoo origin=SAM_CONVERSION typeOperand=<root>.IFoo
|
||||||
VALUE_PARAMETER name:it index:0 type:kotlin.Int
|
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
|
||||||
BLOCK_BODY
|
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
|
||||||
RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.testSeveralLambdas'
|
VALUE_PARAMETER name:it index:0 type:kotlin.Int
|
||||||
GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit
|
BLOCK_BODY
|
||||||
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
|
RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.testSeveralLambdas'
|
||||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
|
GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit
|
||||||
VALUE_PARAMETER name:it index:0 type:kotlin.Int
|
TYPE_OP type=<root>.IFoo origin=SAM_CONVERSION typeOperand=<root>.IFoo
|
||||||
BLOCK_BODY
|
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
|
||||||
RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.testSeveralLambdas'
|
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
|
||||||
GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit
|
VALUE_PARAMETER name:it index:0 type:kotlin.Int
|
||||||
|
BLOCK_BODY
|
||||||
|
RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.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
|
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]
|
VALUE_PARAMETER name:xs index:0 type:kotlin.IntArray varargElementType:kotlin.Int [vararg]
|
||||||
BLOCK_BODY
|
BLOCK_BODY
|
||||||
@@ -61,10 +65,11 @@ FILE fqName:<root> fileName:/samConversionInVarargs.kt
|
|||||||
BLOCK_BODY
|
BLOCK_BODY
|
||||||
CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
||||||
foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo
|
foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo
|
||||||
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=ADAPTED_FUNCTION_REFERENCE
|
TYPE_OP type=<root>.IFoo origin=SAM_CONVERSION typeOperand=<root>.IFoo
|
||||||
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:withVarargOfInt visibility:local modality:FINAL <> (p0:kotlin.Int) returnType:kotlin.Unit
|
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=ADAPTED_FUNCTION_REFERENCE
|
||||||
VALUE_PARAMETER ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE name:p0 index:0 type:kotlin.Int
|
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:withVarargOfInt visibility:local modality:FINAL <> (p0:kotlin.Int) returnType:kotlin.Unit
|
||||||
BLOCK_BODY
|
VALUE_PARAMETER ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE name:p0 index:0 type:kotlin.Int
|
||||||
CALL 'public final fun withVarargOfInt (vararg xs: kotlin.Int): kotlin.String declared in <root>' type=kotlin.String origin=null
|
BLOCK_BODY
|
||||||
xs: VARARG type=kotlin.IntArray varargElementType=kotlin.Int
|
CALL 'public final fun withVarargOfInt (vararg xs: kotlin.Int): kotlin.String declared in <root>' type=kotlin.String origin=null
|
||||||
GET_VAR 'p0: kotlin.Int declared in <root>.testAdaptedCR.withVarargOfInt' type=kotlin.Int origin=null
|
xs: VARARG type=kotlin.IntArray varargElementType=kotlin.Int
|
||||||
|
GET_VAR 'p0: kotlin.Int declared in <root>.testAdaptedCR.withVarargOfInt' type=kotlin.Int origin=null
|
||||||
|
|||||||
+6
-5
@@ -26,11 +26,12 @@ FILE fqName:<root> fileName:/samConversionInVarargsMixed.kt
|
|||||||
GET_VAR 'a: kotlin.Any declared in <root>.test' type=kotlin.Any origin=null
|
GET_VAR 'a: kotlin.Any declared in <root>.test' type=kotlin.Any origin=null
|
||||||
then: CALL 'public final fun foo (vararg rs: <root>.MyRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
then: CALL 'public final fun foo (vararg rs: <root>.MyRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
||||||
rs: VARARG type=kotlin.Array<out <root>.MyRunnable> varargElementType=<root>.MyRunnable
|
rs: VARARG type=kotlin.Array<out <root>.MyRunnable> varargElementType=<root>.MyRunnable
|
||||||
FUN_EXPR type=kotlin.Function0<kotlin.Unit> origin=LAMBDA
|
TYPE_OP type=<root>.MyRunnable origin=SAM_CONVERSION typeOperand=<root>.MyRunnable
|
||||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.Unit
|
FUN_EXPR type=kotlin.Function0<kotlin.Unit> origin=LAMBDA
|
||||||
BLOCK_BODY
|
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.Unit
|
||||||
RETURN type=kotlin.Nothing from='local final fun <anonymous> (): kotlin.Unit declared in <root>.test'
|
BLOCK_BODY
|
||||||
GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit
|
RETURN type=kotlin.Nothing from='local final fun <anonymous> (): kotlin.Unit declared in <root>.test'
|
||||||
|
GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit
|
||||||
GET_VAR 'r: <root>.MyRunnable declared in <root>.test' type=<root>.MyRunnable origin=null
|
GET_VAR 'r: <root>.MyRunnable declared in <root>.test' type=<root>.MyRunnable origin=null
|
||||||
TYPE_OP type=<root>.MyRunnable origin=IMPLICIT_CAST typeOperand=<root>.MyRunnable
|
TYPE_OP type=<root>.MyRunnable origin=IMPLICIT_CAST typeOperand=<root>.MyRunnable
|
||||||
GET_VAR 'a: kotlin.Any declared in <root>.test' type=kotlin.Any origin=null
|
GET_VAR 'a: kotlin.Any declared in <root>.test' type=kotlin.Any origin=null
|
||||||
|
|||||||
Reference in New Issue
Block a user