Skip to content

Allowing Iterable collections to be unpacked using spread operator #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -477,14 +477,14 @@ private sealed interface TypeQualifier {
private val FirResolvedTypeRef.isPresentInSource: Boolean
get() = when (source?.kind) {
is KtRealSourceElementKind -> {
val isArrayFromVararg = delegatedTypeRef?.source?.kind is KtFakeSourceElementKind.ArrayTypeFromVarargParameter;
val isArrayFromVararg = delegatedTypeRef?.source?.kind is KtFakeSourceElementKind.UnderlyingTypeFromVarargParameter;

// type ref with delegated type ref with such source kind is NOT directly present in the source, so we ignore it
!isArrayFromVararg
}

// type ref with such source kind is explicitly present in the source, so we want to see it
is KtFakeSourceElementKind.ArrayTypeFromVarargParameter -> true
is KtFakeSourceElementKind.UnderlyingTypeFromVarargParameter -> true

else -> false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -539,10 +539,10 @@ private class ElementsToShortenCollector(
get() {
val sourcePsi = when {
// array type for vararg parameters is not present in the code, so no need to handle it
delegatedTypeRef?.source?.kind == KtFakeSourceElementKind.ArrayTypeFromVarargParameter -> null
delegatedTypeRef?.source?.kind == KtFakeSourceElementKind.UnderlyingTypeFromVarargParameter -> null

// but the array's underlying type is present with a fake source, and needs to be handled
source?.kind == KtFakeSourceElementKind.ArrayTypeFromVarargParameter -> psi
source?.kind == KtFakeSourceElementKind.UnderlyingTypeFromVarargParameter -> psi

else -> realPsi
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ internal object FirReferenceResolveHelper {
}

private fun FirResolvedTypeRef.getDeclaredType() =
if (this.delegatedTypeRef?.source?.kind == KtFakeSourceElementKind.ArrayTypeFromVarargParameter) type.arrayElementType()
if (this.delegatedTypeRef?.source?.kind == KtFakeSourceElementKind.UnderlyingTypeFromVarargParameter) type.arrayElementType()
else type

private fun ClassId.toTargetPsi(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,17 @@ class IrBuiltInsOverFir(
findFunctions(kotlinPackage, Name.identifier("arrayOf")).distinct().single()
}

override val listOf: IrSimpleFunctionSymbol by lazy {
findFunctions(StandardClassIds.BASE_COLLECTIONS_PACKAGE, Name.identifier("listOf")).distinct()
.single { it.descriptor.valueParameters.getOrNull(0)?.varargElementType != null }
}

override val mutableListOf: IrSimpleFunctionSymbol by lazy {
findFunctions(StandardClassIds.BASE_COLLECTIONS_PACKAGE, Name.identifier("mutableListOf")).distinct()
.single { it.descriptor.valueParameters.size == 0 }
}


override fun getNonBuiltInFunctionsByExtensionReceiver(
name: Name,
vararg packageNameSegments: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1089,7 +1089,7 @@ class CallAndReferenceGenerator(
if (parameter?.returnTypeRef is FirResolvedTypeRef) {
// Java type case (from annotations)
val parameterType = parameter.returnTypeRef.coneType
val unwrappedParameterType = if (parameter.isVararg) parameterType.arrayElementType()!! else parameterType
val unwrappedParameterType = if (parameter.isVararg) parameterType.typeArguments.first().type!! else parameterType
val samFunctionType = getFunctionTypeForPossibleSamType(unwrappedParameterType)
irArgument = irArgument.applySuspendConversionIfNeeded(argument, samFunctionType ?: unwrappedParameterType)
irArgument = irArgument.applySamConversionIfNeeded(argument, parameter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ fun ConeTypeProjection.createArrayType(nullable: Boolean = false, createPrimitiv
return StandardClassIds.Array.constructClassLikeType(arrayOf(this), nullable)
}

fun ConeTypeProjection.createIterableType(nullable: Boolean = false): ConeClassLikeType {
return StandardClassIds.MutableList.constructClassLikeType(arrayOf(this), nullable)
}

fun ConeKotlinType.arrayElementType(checkUnsignedArrays: Boolean = true): ConeKotlinType? {
return when (val argument = arrayElementTypeArgument(checkUnsignedArrays)) {
is ConeKotlinTypeProjection -> argument.type
Expand All @@ -45,18 +49,16 @@ fun ConeKotlinType.arrayElementType(checkUnsignedArrays: Boolean = true): ConeKo
private fun ConeKotlinType.arrayElementTypeArgument(checkUnsignedArrays: Boolean = true): ConeTypeProjection? {
val type = this.lowerBoundIfFlexible()
if (type !is ConeClassLikeType) return null
val classId = type.lookupTag.classId
if (classId == StandardClassIds.Array) {
return type.typeArguments.first()
}

val elementType = StandardClassIds.elementTypeByPrimitiveArrayType[classId] ?: runIf(checkUnsignedArrays) {
StandardClassIds.elementTypeByUnsignedArrayType[classId]
}
if (elementType != null) {
return elementType.constructClassLikeType(emptyArray(), isNullable = false)
}

return null
val classId = type.lookupTag.classId
return type.typeArguments.firstOrNull()
}

fun ConeKotlinType.varargElementType(): ConeKotlinType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,6 @@ fun FirErrorTypeRef.wrapIntoArray(): FirResolvedTypeRef {
return buildResolvedTypeRef {
source = typeRef.source
type = StandardClassIds.Array.constructClassLikeType(arrayOf(ConeKotlinTypeProjectionOut(typeRef.coneType)))
delegatedTypeRef = typeRef.copyWithNewSourceKind(KtFakeSourceElementKind.ArrayTypeFromVarargParameter)
delegatedTypeRef = typeRef.copyWithNewSourceKind(KtFakeSourceElementKind.UnderlyingTypeFromVarargParameter)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ open class FirTypeResolveTransformer(
.transformAnnotations(this, data)

if (property.isFromVararg == true) {
property.transformTypeToArrayType()
property.backingField?.transformTypeToArrayType()
property.transformTypeToUnderlyingType()
property.backingField?.transformTypeToUnderlyingType()
setAccessorTypesByPropertyType(property)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,32 @@ import org.jetbrains.kotlin.fir.copyWithNewSourceKind
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.types.ConeKotlinTypeProjectionOut
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.createArrayType

internal fun FirValueParameter.transformVarargTypeToArrayType() {
if (isVararg) {
this.transformTypeToArrayType()
this.transformTypeToUnderlyingType()
}
}

internal fun FirCallableDeclaration.transformTypeToArrayType() {
internal fun FirCallableDeclaration.transformTypeToUnderlyingType() {
val returnTypeRef = this.returnTypeRef
require(returnTypeRef is FirResolvedTypeRef)
// If the delegated type is already resolved, it means we have already created a resolved array type for this vararg type declaration.
// This is because in the buildResolvedTypeRef call below, we set the delegated type ref to the previous (non-vararg) resolved type ref.
if (returnTypeRef.delegatedTypeRef is FirResolvedTypeRef &&
returnTypeRef.delegatedTypeRef?.source?.kind == KtFakeSourceElementKind.ArrayTypeFromVarargParameter
returnTypeRef.delegatedTypeRef?.source?.kind == KtFakeSourceElementKind.UnderlyingTypeFromVarargParameter
) return
val returnType = returnTypeRef.coneType

replaceReturnTypeRef(
buildResolvedTypeRef {
source = returnTypeRef.source
type = ConeKotlinTypeProjectionOut(returnType).createArrayType()
type = ConeKotlinTypeProjectionOut(returnType).createIterableType()
annotations += returnTypeRef.annotations
// ? do we really need replacing source of nested delegatedTypeRef ?
delegatedTypeRef = returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.ArrayTypeFromVarargParameter)
delegatedTypeRef = returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.UnderlyingTypeFromVarargParameter)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ internal fun remapArgumentsWithVararg(
// Create a FirVarargArgumentExpression for the vararg arguments.
// The order of arguments in the mapping must be preserved for FIR2IR, hence we have to find where the vararg arguments end.
// FIR2IR uses the mapping order to determine if arguments need to be reordered.
val varargElementType = varargArrayType.arrayElementType()?.approximateIntegerLiteralType()
val varargElementType = varargArrayType.typeArguments.first().type?.approximateIntegerLiteralType()
val argumentList = argumentMapping.keys.toList()
var indexAfterVarargs = argumentList.size
val newArgumentMapping = linkedMapOf<FirExpression, FirValueParameter>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ sealed class KtFakeSourceElementKind(final override val shouldSkipErrorTypeRepor
object DataClassGeneratedMembers : KtFakeSourceElementKind(shouldSkipErrorTypeReporting = true)

// (vararg x: Int) --> (x: Array<out Int>) where array type ref has a fake source kind
object ArrayTypeFromVarargParameter : KtFakeSourceElementKind()
object UnderlyingTypeFromVarargParameter : KtFakeSourceElementKind()

// val (a,b) = x --> val a = x.component1(); val b = x.component2()
// where componentN calls will have the fake source elements refer to the corresponding KtDestructuringDeclarationEntry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ open class BuiltinSymbolsBase(val irBuiltIns: IrBuiltIns, private val symbolTabl
val arrayOf: IrSimpleFunctionSymbol get() = irBuiltIns.arrayOf
val arrayOfNulls: IrSimpleFunctionSymbol get() = irBuiltIns.arrayOfNulls

val listOf: IrSimpleFunctionSymbol get() = irBuiltIns.listOf
val mutableListOf: IrSimpleFunctionSymbol get() = irBuiltIns.mutableListOf

val array get() = irBuiltIns.arrayClass

val byteArray get() = irBuiltIns.byteArray
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.ir.IrArrayBuilder
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.irArray
import org.jetbrains.kotlin.backend.jvm.ir.irArrayOf
import org.jetbrains.kotlin.backend.jvm.ir.*
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.UnsignedType
Expand Down Expand Up @@ -64,10 +61,11 @@ internal class VarargLowering(val context: JvmBackendContext) : FileLoweringPass
return expression
}

override fun visitVararg(expression: IrVararg): IrExpression =
createBuilder(expression.startOffset, expression.endOffset).irArray(expression.type) { addVararg(expression) }
override fun visitVararg(expression: IrVararg): IrExpression {
return createBuilder(expression.startOffset, expression.endOffset).irSpreadArray(expression.type) { addVararg(expression) }
}

private fun IrArrayBuilder.addVararg(expression: IrVararg) {
private fun IrSpreadBuilder.addVararg(expression: IrVararg) {
loop@ for (element in expression.elements) {
when (element) {
is IrExpression -> +element.transform(this@VarargLowering, null)
Expand Down
Loading