diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirImportOptimizer.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirImportOptimizer.kt index ebb71cea3276e..d973ead2f1427 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirImportOptimizer.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirImportOptimizer.kt @@ -41,7 +41,6 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression import org.jetbrains.kotlin.psi.psiUtil.getPossiblyQualifiedCallExpression import org.jetbrains.kotlin.psi.psiUtil.unwrapNullability -import org.jetbrains.kotlin.resolve.ImportPath import org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment @@ -477,14 +476,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.CollectionTypeFromVarargParameter; // 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.CollectionTypeFromVarargParameter -> true else -> false } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt index 95722af82cbf7..4c1ae09ed64ec 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.analysis.api.fir.components import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile import com.intellij.psi.SmartPsiElementPointer import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.analysis.api.components.* @@ -531,7 +530,7 @@ private class ElementsToShortenCollector( * * This code handles some quirks of FIR sources and PSI: * - in `vararg args: String` declaration, `String` type reference has fake source, but `Array` has real source - * (see [KtFakeSourceElementKind.ArrayTypeFromVarargParameter]). + * (see [KtFakeSourceElementKind.CollectionTypeFromVarargParameter]). * - if FIR reference points to the type with generic parameters (like `Foo`), its source is not [KtTypeReference], but * [KtNameReferenceExpression]. */ @@ -539,10 +538,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.CollectionTypeFromVarargParameter -> 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.CollectionTypeFromVarargParameter -> psi else -> realPsi } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/FirReferenceResolveHelper.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/FirReferenceResolveHelper.kt index a8e3c7c309dcc..da04717e12298 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/FirReferenceResolveHelper.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/FirReferenceResolveHelper.kt @@ -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.CollectionTypeFromVarargParameter) type.spreadableCollectionElementType() else type private fun ClassId.toTargetPsi( diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt index 097abec1ee139..b146d25a3abf5 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt @@ -171,6 +171,7 @@ class IrBuiltInsOverFir( override val setClass: IrClassSymbol by lazy { loadClass(StandardClassIds.Set) } override val listClass: IrClassSymbol by lazy { loadClass(StandardClassIds.List) } override val mapClass: IrClassSymbol by lazy { loadClass(StandardClassIds.Map) } + override val arrayListClass: IrClassSymbol by lazy { loadClass(StandardClassIds.ArrayList) } override val mapEntryClass: IrClassSymbol by lazy { loadClass(StandardClassIds.MapEntry) } override val iterableClass: IrClassSymbol by lazy { loadClass(StandardClassIds.Iterable) } 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 ab4da19d91d4e..ace105e589347 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 @@ -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.spreadableCollectionElementType()!! else parameterType val samFunctionType = getFunctionTypeForPossibleSamType(unwrappedParameterType) irArgument = irArgument.applySuspendConversionIfNeeded(argument, samFunctionType ?: unwrappedParameterType) irArgument = irArgument.applySamConversionIfNeeded(argument, parameter) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt index 78e4e09ee77ea..38d06c8c76c91 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.types import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.name.canBeSpreaded import org.jetbrains.kotlin.utils.addToStdlib.runIf val ConeKotlinType.isArrayOrPrimitiveArray: Boolean @@ -19,6 +20,10 @@ fun ConeKotlinType.createOutArrayType(nullable: Boolean = false, createPrimitive return ConeKotlinTypeProjectionOut(this).createArrayType(nullable, createPrimitiveArrayType) } +fun ConeTypeProjection.createVarargUnderlyingType(): ConeClassLikeType { + return StandardClassIds.ArrayList.constructClassLikeType(arrayOf(this)) +} + fun ConeTypeProjection.createArrayType(nullable: Boolean = false, createPrimitiveArrayTypeIfPossible: Boolean = true): ConeClassLikeType { if (this is ConeKotlinTypeProjection && createPrimitiveArrayTypeIfPossible) { val type = type.lowerBoundIfFlexible() @@ -35,6 +40,24 @@ fun ConeTypeProjection.createArrayType(nullable: Boolean = false, createPrimitiv return StandardClassIds.Array.constructClassLikeType(arrayOf(this), nullable) } +fun ConeKotlinType.spreadableCollectionElementType(checkUnsignedArrays: Boolean = true): ConeKotlinType? { + return when (val argument = spreadableCollectionElementTypeArgument(checkUnsignedArrays)) { + is ConeKotlinTypeProjection -> argument.type + else -> null + } +} + +private fun ConeKotlinType.spreadableCollectionElementTypeArgument(checkUnsignedArrays: Boolean = true): ConeTypeProjection? { + val type = this.lowerBoundIfFlexible() + if (type !is ConeClassLikeType) return null + val classId = type.lookupTag.classId + if (classId.canBeSpreaded()) { + return type.typeArguments.first() + } + return arrayElementType(checkUnsignedArrays) +} + + fun ConeKotlinType.arrayElementType(checkUnsignedArrays: Boolean = true): ConeKotlinType? { return when (val argument = arrayElementTypeArgument(checkUnsignedArrays)) { is ConeKotlinTypeProjection -> argument.type diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt index 5c3e1aa741354..550a3da25f614 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt @@ -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.CollectionTypeFromVarargParameter) } } 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 ad717bcfddf9a..ad751c3e6e7c9 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 @@ -46,7 +46,8 @@ fun Candidate.resolveArgumentExpression( sink: CheckerSink, context: ResolutionContext, isReceiver: Boolean, - isDispatch: Boolean + isDispatch: Boolean, + isSpread: Boolean = false, ) { when (argument) { is FirFunctionCall, is FirWhenExpression, is FirTryExpression, is FirCheckNotNullCall, is FirElvisExpression -> resolveSubCallArgument( @@ -56,7 +57,8 @@ fun Candidate.resolveArgumentExpression( sink, context, isReceiver, - isDispatch + isDispatch, + isSpread = isSpread ) // x?.bar() is desugared to `x SAFE-CALL-OPERATOR { $not-null-receiver$.bar() }` // @@ -75,7 +77,8 @@ fun Candidate.resolveArgumentExpression( context, isReceiver, isDispatch, - useNullableArgumentType = true + useNullableArgumentType = true, + isSpread = isSpread ) } else { // Assignment @@ -88,7 +91,8 @@ fun Candidate.resolveArgumentExpression( isReceiver = false, isDispatch = false, sink = sink, - context = context + context = context, + isSpread = isSpread ) } } @@ -101,7 +105,8 @@ fun Candidate.resolveArgumentExpression( sink, context, isReceiver, - isDispatch + isDispatch, + isSpread = isSpread ) else preprocessCallableReference(argument, expectedType, context) @@ -113,7 +118,8 @@ fun Candidate.resolveArgumentExpression( sink, context, isReceiver, - isDispatch + isDispatch, + isSpread = isSpread ) is FirBlock -> resolveBlockArgument( csBuilder, @@ -122,9 +128,10 @@ fun Candidate.resolveArgumentExpression( sink, context, isReceiver, - isDispatch + isDispatch, + isSpread =isSpread ) - else -> resolvePlainExpressionArgument(csBuilder, argument, expectedType, sink, context, isReceiver, isDispatch) + else -> resolvePlainExpressionArgument(csBuilder, argument, expectedType, sink, context, isReceiver, isDispatch, isSpread = isSpread) } } @@ -135,7 +142,8 @@ private fun Candidate.resolveBlockArgument( sink: CheckerSink, context: ResolutionContext, isReceiver: Boolean, - isDispatch: Boolean + isDispatch: Boolean, + isSpread: Boolean = false ) { val returnArguments = block.returnExpressions() if (returnArguments.isEmpty()) { @@ -148,7 +156,8 @@ private fun Candidate.resolveBlockArgument( isReceiver = false, isDispatch = false, sink = sink, - context = context + context = context, + isSpread = isSpread ) return } @@ -160,7 +169,8 @@ private fun Candidate.resolveBlockArgument( sink, context, isReceiver, - isDispatch + isDispatch, + isSpread = isSpread ) } } @@ -173,7 +183,8 @@ fun Candidate.resolveSubCallArgument( context: ResolutionContext, isReceiver: Boolean, isDispatch: Boolean, - useNullableArgumentType: Boolean = false + useNullableArgumentType: Boolean = false, + isSpread: Boolean = false ) { require(argument is FirExpression) val candidate = argument.candidate() ?: return resolvePlainExpressionArgument( @@ -184,7 +195,8 @@ fun Candidate.resolveSubCallArgument( context, isReceiver, isDispatch, - useNullableArgumentType + useNullableArgumentType, + isSpread = isSpread ) /* * It's important to extract type from argument neither from symbol, because of symbol contains @@ -201,7 +213,8 @@ fun Candidate.resolveSubCallArgument( context, isReceiver, isDispatch, - useNullableArgumentType + useNullableArgumentType, + isSpread = isSpread ) } @@ -233,7 +246,8 @@ fun Candidate.resolvePlainExpressionArgument( context: ResolutionContext, isReceiver: Boolean, isDispatch: Boolean, - useNullableArgumentType: Boolean = false + useNullableArgumentType: Boolean = false, + isSpread: Boolean = false ) { if (expectedType == null) return @@ -251,7 +265,8 @@ fun Candidate.resolvePlainExpressionArgument( context, isReceiver, isDispatch, - useNullableArgumentType + useNullableArgumentType, + isSpread = isSpread ) } @@ -264,7 +279,8 @@ fun Candidate.resolvePlainArgumentType( context: ResolutionContext, isReceiver: Boolean, isDispatch: Boolean, - useNullableArgumentType: Boolean = false + useNullableArgumentType: Boolean = false, + isSpread: Boolean = false ) { val position = if (isReceiver) ConeReceiverConstraintPosition(argument) else ConeArgumentConstraintPosition(argument) @@ -289,7 +305,7 @@ fun Candidate.resolvePlainArgumentType( } checkApplicabilityForArgumentType( - csBuilder, argument, argumentTypeForApplicabilityCheck, expectedType, position, isReceiver, isDispatch, sink, context + csBuilder, argument, argumentTypeForApplicabilityCheck, expectedType, position, isReceiver, isDispatch, sink, context, isSpread = isSpread ) } @@ -332,16 +348,25 @@ private fun checkApplicabilityForArgumentType( csBuilder: ConstraintSystemBuilder, argument: FirExpression, argumentTypeBeforeCapturing: ConeKotlinType, - expectedType: ConeKotlinType?, + initialExpectedType: ConeKotlinType?, position: ConstraintPosition, isReceiver: Boolean, isDispatch: Boolean, sink: CheckerSink, - context: ResolutionContext + context: ResolutionContext, + isSpread: Boolean = false ) { - if (expectedType == null) return + var expectedType = initialExpectedType ?: return + var argumentType = captureFromTypeParameterUpperBoundIfNeeded(argumentTypeBeforeCapturing, expectedType, context.session) + + if (isSpread && !argumentType.isNullable) { + argumentType = argumentType.spreadableCollectionElementType()?.also { + expectedType = + expectedType.spreadableCollectionElementType() + ?: error("Could not retrieve expected element type for vararg parameter. Parameter type is ${expectedType.renderReadable()}") + } ?: argumentType + } - val argumentType = captureFromTypeParameterUpperBoundIfNeeded(argumentTypeBeforeCapturing, expectedType, context.session) fun subtypeError(actualExpectedType: ConeKotlinType): ResolutionDiagnostic { if (argument.isNullLiteral && actualExpectedType.nullability == ConeNullability.NOT_NULL) { @@ -413,6 +438,7 @@ private fun checkApplicabilityForArgumentType( } } + if (!isReceiver) { sink.reportDiagnostic(subtypeError(expectedType)) return @@ -427,6 +453,7 @@ private fun checkApplicabilityForArgumentType( sink.reportDiagnostic(InapplicableWrongReceiver(expectedType, argumentType)) } } + } internal fun Candidate.resolveArgument( @@ -449,7 +476,8 @@ internal fun Candidate.resolveArgument( sink, context, isReceiver, - false + false, + isSpread = argument is FirSpreadArgumentExpression && parameter?.isVararg == true ) } @@ -709,4 +737,4 @@ private fun ConeKotlinType.hasSupertypeWithGivenClassId(classId: ClassId, contex typeConstructor is ConeClassLikeLookupTag && typeConstructor.classId == classId } } -} +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt index 81795e3e6fafe..491fe8444e253 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt @@ -208,8 +208,8 @@ open class FirTypeResolveTransformer( .transformAnnotations(this, data) if (property.isFromVararg == true) { - property.transformTypeToArrayType() - property.backingField?.transformTypeToArrayType() + property.transformTypeToUnderlyingVarargType() + property.backingField?.transformTypeToUnderlyingVarargType() setAccessorTypesByPropertyType(property) } @@ -339,7 +339,7 @@ open class FirTypeResolveTransformer( withDeclaration(valueParameter) { valueParameter.transformReturnTypeRef(this, data) valueParameter.transformAnnotations(this, data) - valueParameter.transformVarargTypeToArrayType() + valueParameter.transformVarargTypeToUnderlyingType() valueParameter } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/TransformUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/TransformUtils.kt index cb40dfd79a89f..f749e5bc4cd52 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/TransformUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/TransformUtils.kt @@ -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() { +internal fun FirValueParameter.transformVarargTypeToUnderlyingType() { if (isVararg) { - this.transformTypeToArrayType() + this.transformTypeToUnderlyingVarargType() } } -internal fun FirCallableDeclaration.transformTypeToArrayType() { +internal fun FirCallableDeclaration.transformTypeToUnderlyingVarargType() { 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.CollectionTypeFromVarargParameter ) return val returnType = returnTypeRef.coneType replaceReturnTypeRef( buildResolvedTypeRef { source = returnTypeRef.source - type = ConeKotlinTypeProjectionOut(returnType).createArrayType() + type = ConeKotlinTypeProjectionOut(returnType).createVarargUnderlyingType() annotations += returnTypeRef.annotations // ? do we really need replacing source of nested delegatedTypeRef ? - delegatedTypeRef = returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.ArrayTypeFromVarargParameter) + delegatedTypeRef = returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.CollectionTypeFromVarargParameter) } ) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveUtils.kt index 48d6ee375aa00..abf903254463b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveUtils.kt @@ -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.spreadableCollectionElementType()?.approximateIntegerLiteralType() val argumentList = argumentMapping.keys.toList() var indexAfterVarargs = argumentList.size val newArgumentMapping = linkedMapOf() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index 5f263893578e4..a03f01125e773 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -40,7 +40,7 @@ import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolver import org.jetbrains.kotlin.fir.resolve.transformers.contracts.runContractResolveForFunction -import org.jetbrains.kotlin.fir.resolve.transformers.transformVarargTypeToArrayType +import org.jetbrains.kotlin.fir.resolve.transformers.transformVarargTypeToUnderlyingType import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef @@ -93,7 +93,7 @@ open class FirDeclarationsResolveTransformer( if (callableMember is FirFunction) { callableMember.valueParameters.forEach { it.transformReturnTypeRef(transformer, ResolutionMode.ContextIndependent) - it.transformVarargTypeToArrayType() + it.transformVarargTypeToUnderlyingType() } } } diff --git a/compiler/frontend.common/src/org/jetbrains/kotlin/KtSourceElement.kt b/compiler/frontend.common/src/org/jetbrains/kotlin/KtSourceElement.kt index 0a437e643e5f5..949bbb0956fa0 100644 --- a/compiler/frontend.common/src/org/jetbrains/kotlin/KtSourceElement.kt +++ b/compiler/frontend.common/src/org/jetbrains/kotlin/KtSourceElement.kt @@ -166,7 +166,7 @@ sealed class KtFakeSourceElementKind(final override val shouldSkipErrorTypeRepor object DataClassGeneratedMembers : KtFakeSourceElementKind(shouldSkipErrorTypeReporting = true) // (vararg x: Int) --> (x: Array) where array type ref has a fake source kind - object ArrayTypeFromVarargParameter : KtFakeSourceElementKind() + object CollectionTypeFromVarargParameter : 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 diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt index a0f0e1ae29a6d..97fbccc3b3800 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt @@ -153,6 +153,8 @@ open class BuiltinSymbolsBase(val irBuiltIns: IrBuiltIns, private val symbolTabl val mutableIterable get() = irBuiltIns.mutableIterableClass val mutableIterator get() = irBuiltIns.mutableIteratorClass val mutableListIterator get() = irBuiltIns.mutableListIteratorClass + + val arrayList get() = irBuiltIns.arrayListClass val comparable get() = irBuiltIns.comparableClass private val binaryOperatorCache = mutableMapOf, IrSimpleFunctionSymbol>() diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/VarargLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/VarargLowering.kt index 25acc9665a838..dba317be178b9 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/VarargLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/VarargLowering.kt @@ -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 @@ -57,7 +54,7 @@ internal class VarargLowering(val context: JvmBackendContext) : FileLoweringPass if (parameter.varargElementType != null && !parameter.hasDefaultValue()) { // Compute the correct type for the array argument. val arrayType = parameter.type.substitute(expression.typeSubstitutionMap).makeNotNull() - expression.putValueArgument(i, createBuilder().irArrayOf(arrayType)) + expression.putValueArgument(i, createBuilder().irVarargOf(arrayType)) } } @@ -65,9 +62,9 @@ internal class VarargLowering(val context: JvmBackendContext) : FileLoweringPass } override fun visitVararg(expression: IrVararg): IrExpression = - createBuilder(expression.startOffset, expression.endOffset).irArray(expression.type) { addVararg(expression) } + createBuilder(expression.startOffset, expression.endOffset).irVararg(expression.type) { addVararg(expression) } - private fun IrArrayBuilder.addVararg(expression: IrVararg) { + private fun IrVarargBuilder.addVararg(expression: IrVararg) { loop@ for (element in expression.elements) { when (element) { is IrExpression -> +element.transform(this@VarargLowering, null) @@ -94,7 +91,7 @@ internal class VarargLowering(val context: JvmBackendContext) : FileLoweringPass } internal val PRIMITIVE_ARRAY_OF_NAMES: Set = - (PrimitiveType.values().map { type -> type.name } + UnsignedType.values().map { type -> type.typeName.asString() }) + (PrimitiveType.entries.map { type -> type.name } + UnsignedType.entries.map { type -> type.typeName.asString() }) .map { name -> name.toLowerCaseAsciiOnly() + "ArrayOf" }.toSet() internal const val ARRAY_OF_NAME = "arrayOf" diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt index aa019163647ce..ab540ae286585 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt @@ -851,6 +851,26 @@ class JvmSymbols( val kClassJavaPropertyGetter: IrSimpleFunction = kClassJava.owner.getter!! + val alternativeSpreadBuilder: IrClassSymbol = createClass(FqName("kotlin.jvm.internal.AlternativeSpreadBuilder")) { klass -> + klass.addConstructor().apply { + addValueParameter("size", irBuiltIns.intType) + } + + klass.addFunction("addSpread", irBuiltIns.unitType).apply { + addValueParameter("container", irBuiltIns.anyNType) + } + + klass.addFunction("add", irBuiltIns.unitType).apply { + addValueParameter("element", irBuiltIns.anyNType) + } + + klass.addFunction("size", irBuiltIns.intType) + + klass.addFunction("toArray", irBuiltIns.arrayClass.typeWith(irBuiltIns.anyNType)).apply { + addValueParameter("a", irBuiltIns.arrayClass.typeWith(irBuiltIns.anyNType)) + } + } + val spreadBuilder: IrClassSymbol = createClass(FqName("kotlin.jvm.internal.SpreadBuilder")) { klass -> klass.addConstructor().apply { addValueParameter("size", irBuiltIns.intType) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrVarargBuilder.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrVarargBuilder.kt new file mode 100644 index 0000000000000..e493b91ee229a --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrVarargBuilder.kt @@ -0,0 +1,164 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.backend.jvm.ir + +import org.jetbrains.kotlin.backend.jvm.unboxInlineClass +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.functions +import org.jetbrains.kotlin.ir.util.getPropertyGetter + +inline fun JvmIrBuilder.irVararg(arrayType: IrType, block: IrVarargBuilder.() -> Unit): IrExpression = + IrVarargBuilder(this, arrayType).apply { block() }.build() + +fun JvmIrBuilder.irVarargOf(arrayType: IrType, elements: List = listOf()): IrExpression = + irVararg(arrayType) { elements.forEach { +it } } + +private class IrVarargElement(val expression: IrExpression, val isSpread: Boolean) + +class IrVarargBuilder(val builder: JvmIrBuilder, val arrayType: IrType) { + val elementType = arrayType.getArrayElementType(builder.context.irBuiltIns) + + private val elements: MutableList = mutableListOf() + private val spreadBuilder = builder.irSymbols.alternativeSpreadBuilder + + private val hasSpread + get() = elements.any { it.isSpread } + + operator fun IrExpression.unaryPlus() = add(this) + fun add(expression: IrExpression) = elements.add(IrVarargElement(expression, false)) + + fun addSpread(expression: IrExpression) = elements.add(IrVarargElement(expression, true)) + + fun build(): IrExpression { + val array = when { + elements.isEmpty() -> newCollection(0) + !hasSpread -> buildSimpleArray() + elements.size == 1 -> copyArray(elements.single().expression) + else -> buildComplexArray() + } + return array + } + + // Construct a new array of the specified size + private fun newCollection(size: Int) = newCollection(builder.irInt(size)) + + private fun newCollection(size: IrExpression): IrExpression { + val arrayConstructor = builder.irSymbols.arrayList.constructors.single { it.owner.valueParameters.size == 1 } + + return builder.irCall(arrayConstructor, arrayType).apply { + if (typeArgumentsCount != 0) + putTypeArgument(0, elementType) + putValueArgument(0, size) + } + } + + // Build an array without spreads + private fun buildSimpleArray(): IrExpression = + builder.irBlock { + val result = irTemporary(newCollection(elements.size)) + + val set = builder.irSymbols.arrayList.functions.single { + it.owner.name.asString() == "set" + } + + for ((index, element) in elements.withIndex()) { + +irCall(set).apply { + dispatchReceiver = irGet(result) + putValueArgument(0, irInt(index)) + putValueArgument(1, element.expression) + } + } + + +irGet(result) + } + + // Copy a single spread expression, unless it refers to a newly constructed array. + private fun copyArray(spread: IrExpression): IrExpression { +// if (spread is IrConstructorCall || +// (spread is IrFunctionAccessExpression && spread.symbol == builder.irSymbols.arrayOfNulls) +// ) +// return spread + +// return builder.irBlock { +// val spreadVar = if (spread is IrGetValue) spread.symbol.owner else irTemporary(spread) +// val size = builder.irSymbols.arrayList.getPropertyGetter("size")!! +// val arrayCopyOf = builder.irSymbols.getArraysCopyOfFunction(unwrappedArrayType as IrSimpleType) +// // TODO consider using System.arraycopy if the requested array type is non-generic. +// +irCall(arrayCopyOf).apply { +// putValueArgument(0, coerce(irGet(spreadVar), unwrappedArrayType)) +// putValueArgument(1, irCall(size).apply { dispatchReceiver = irGet(spreadVar) }) +// } +// } + +// builder.irBlock { +// val result = irTemporary(newCollection(elements.size)) +// +// val set = builder.irSymbols.arrayList.functions.single { +// it.owner.name.asString() == "set" +// } +// +// for ((index, element) in spread.()) { +// +irCall(set).apply { +// dispatchReceiver = irGet(result) +// putValueArgument(0, irInt(index)) +// putValueArgument(1, coerce(element.expression, elementType)) +// } +// } +// +// +irGet(result) +// } + + return newCollection(builder.irInt(0)) + } + + // Build an array containing spread expressions. + private fun buildComplexArray(): IrExpression { + return newCollection(builder.irInt(0)) +// val spreadBuilder = if (unwrappedArrayType.isBoxedArray) +// builder.irSymbols.spreadBuilder +// else +// builder.irSymbols.primitiveSpreadBuilders.getValue(elementType) +// +// val addElement = spreadBuilder.functions.single { it.owner.name.asString() == "add" } +// val addSpread = spreadBuilder.functions.single { it.owner.name.asString() == "addSpread" } +// val toArray = spreadBuilder.functions.single { it.owner.name.asString() == "toArray" } +// +// return builder.irBlock { +// val spreadBuilderVar = irTemporary(irCallConstructor(spreadBuilder.constructors.single(), listOf()).apply { +// putValueArgument(0, irInt(elements.size)) +// }) +// +// for (element in elements) { +// +irCall(if (element.isSpread) addSpread else addElement).apply { +// dispatchReceiver = irGet(spreadBuilderVar) +// putValueArgument(0, coerce(element.expression, if (element.isSpread) unwrappedArrayType else elementType)) +// } +// } +// +// val toArrayCall = irCall(toArray).apply { +// dispatchReceiver = irGet(spreadBuilderVar) +// if (unwrappedArrayType.isBoxedArray) { +// val size = spreadBuilder.functions.single { it.owner.name.asString() == "size" } +// putValueArgument(0, irCall(builder.irSymbols.arrayOfNulls, arrayType).apply { +// putTypeArgument(0, elementType) +// putValueArgument(0, irCall(size).apply { +// dispatchReceiver = irGet(spreadBuilderVar) +// }) +// }) +// } +// } +// +// if (unwrappedArrayType.isBoxedArray) +// +builder.irImplicitCast(toArrayCall, unwrappedArrayType) +// else +// +toArrayCall +// } + } + +} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/descriptors/IrBuiltInsOverDescriptors.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/descriptors/IrBuiltInsOverDescriptors.kt index 22f3292765eb5..8c8019d7c640c 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/descriptors/IrBuiltInsOverDescriptors.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/descriptors/IrBuiltInsOverDescriptors.kt @@ -367,6 +367,7 @@ class IrBuiltInsOverDescriptors( override val comparableClass = builtIns.comparable.toIrSymbol() override val arrayClass = builtIns.array.toIrSymbol() + override val arrayListClass = builtIns.arrayList.toIrSymbol() override val throwableType = builtIns.throwable.defaultType.toIrType() override val throwableClass = builtIns.throwable.toIrSymbol() diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt index 4e314e485e4b7..bac07ad02acac 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt @@ -72,6 +72,7 @@ abstract class IrBuiltIns { abstract val setClass: IrClassSymbol abstract val listClass: IrClassSymbol abstract val mapClass: IrClassSymbol + abstract val arrayListClass: IrClassSymbol abstract val mapEntryClass: IrClassSymbol abstract val iterableClass: IrClassSymbol abstract val iteratorClass: IrClassSymbol diff --git a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt index eac2407f463a6..9097b43f562cf 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt @@ -122,6 +122,7 @@ object StandardClassIds { val ListIterator = "ListIterator".collectionsId() val Set = "Set".collectionsId() val Map = "Map".collectionsId() + val ArrayList = "ArrayList".collectionsId() val MutableIterator = "MutableIterator".collectionsId() val CharIterator = "CharIterator".collectionsId() @@ -261,6 +262,16 @@ private fun String.coroutinesId() = ClassId(StandardClassIds.BASE_COROUTINES_PAC private fun String.enumsId() = ClassId(StandardClassIds.BASE_ENUMS_PACKAGE, Name.identifier(this)) private fun String.concurrentId() = ClassId(StandardClassIds.BASE_CONCURRENT_PACKAGE, Name.identifier(this)) +fun ClassId.canBeSpreaded() = listOf( + StandardClassIds.Array, + StandardClassIds.Collection, + StandardClassIds.MutableCollection, + StandardClassIds.List, + StandardClassIds.MutableList, + StandardClassIds.Set, + StandardClassIds.MutableSet, + StandardClassIds.ArrayList +).contains(this) private fun String.testId() = ClassId(StandardClassIds.BASE_TEST_PACKAGE, Name.identifier(this)) private fun String.callableId(packageName: FqName) = CallableId(packageName, Name.identifier(this)) diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 14bdca083a7a6..7bec197f1ed38 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -283,6 +283,11 @@ public ClassDescriptor getArray() { return getBuiltInClassByName("Array"); } + @NotNull + public ClassDescriptor getArrayList() { + return getBuiltInClassByName("ArrayList"); + } + @NotNull public ClassDescriptor getPrimitiveArrayClassDescriptor(@NotNull PrimitiveType type) { return getBuiltInClassByName(type.getArrayTypeName().asString()); diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/AlternativeSpreadBuilder.kt b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/AlternativeSpreadBuilder.kt new file mode 100644 index 0000000000000..8c40ca1bd8625 --- /dev/null +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/AlternativeSpreadBuilder.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.jvm.internal + + +public abstract class AlternativeSpreadBuilder() { + + private val values: MutableList = mutableListOf() + + + public fun addSpread(spreadArgument: Array) { + values.addAll(spreadArgument) + } + + public fun addSpread(spreadArgument: List) { + values.addAll(spreadArgument) + } + + public fun addSpread(spreadArgument: Set) { + values.addAll(spreadArgument) + } + + public fun add(value: T) { + values.add(value) + } + + protected fun size(): Int { + return values.size + } + + protected fun toArrayList(): ArrayList { + return ArrayList(values) + } + + protected fun empty(): ArrayList { + return ArrayList() + } +}