Skip to content

Using alternative underlying collection for varargs #4

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 1 commit 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 @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand Down Expand Up @@ -531,18 +530,18 @@ 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<String>` has real source
* (see [KtFakeSourceElementKind.ArrayTypeFromVarargParameter]).
* (see [KtFakeSourceElementKind.CollectionTypeFromVarargParameter]).
* - if FIR reference points to the type with generic parameters (like `Foo<Bar>`), its source is not [KtTypeReference], but
* [KtNameReferenceExpression].
*/
private val FirResolvedTypeRef.correspondingTypePsi: KtUserType?
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
}
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.CollectionTypeFromVarargParameter) type.spreadableCollectionElementType()
else type

private fun ClassId.toTargetPsi(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
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.spreadableCollectionElementType()!! 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 @@ -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
Expand All @@ -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()
Expand All @@ -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
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.CollectionTypeFromVarargParameter)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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() }`
//
Expand All @@ -75,7 +77,8 @@ fun Candidate.resolveArgumentExpression(
context,
isReceiver,
isDispatch,
useNullableArgumentType = true
useNullableArgumentType = true,
isSpread = isSpread
)
} else {
// Assignment
Expand All @@ -88,7 +91,8 @@ fun Candidate.resolveArgumentExpression(
isReceiver = false,
isDispatch = false,
sink = sink,
context = context
context = context,
isSpread = isSpread
)
}
}
Expand All @@ -101,7 +105,8 @@ fun Candidate.resolveArgumentExpression(
sink,
context,
isReceiver,
isDispatch
isDispatch,
isSpread = isSpread
)
else
preprocessCallableReference(argument, expectedType, context)
Expand All @@ -113,7 +118,8 @@ fun Candidate.resolveArgumentExpression(
sink,
context,
isReceiver,
isDispatch
isDispatch,
isSpread = isSpread
)
is FirBlock -> resolveBlockArgument(
csBuilder,
Expand All @@ -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)
}
}

Expand All @@ -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()) {
Expand All @@ -148,7 +156,8 @@ private fun Candidate.resolveBlockArgument(
isReceiver = false,
isDispatch = false,
sink = sink,
context = context
context = context,
isSpread = isSpread
)
return
}
Expand All @@ -160,7 +169,8 @@ private fun Candidate.resolveBlockArgument(
sink,
context,
isReceiver,
isDispatch
isDispatch,
isSpread = isSpread
)
}
}
Expand All @@ -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(
Expand All @@ -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
Expand All @@ -201,7 +213,8 @@ fun Candidate.resolveSubCallArgument(
context,
isReceiver,
isDispatch,
useNullableArgumentType
useNullableArgumentType,
isSpread = isSpread
)
}

Expand Down Expand Up @@ -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
Expand All @@ -251,7 +265,8 @@ fun Candidate.resolvePlainExpressionArgument(
context,
isReceiver,
isDispatch,
useNullableArgumentType
useNullableArgumentType,
isSpread = isSpread
)
}

Expand All @@ -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)

Expand All @@ -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
)
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -413,6 +438,7 @@ private fun checkApplicabilityForArgumentType(
}
}


if (!isReceiver) {
sink.reportDiagnostic(subtypeError(expectedType))
return
Expand All @@ -427,6 +453,7 @@ private fun checkApplicabilityForArgumentType(
sink.reportDiagnostic(InapplicableWrongReceiver(expectedType, argumentType))
}
}

}

internal fun Candidate.resolveArgument(
Expand All @@ -449,7 +476,8 @@ internal fun Candidate.resolveArgument(
sink,
context,
isReceiver,
false
false,
isSpread = argument is FirSpreadArgumentExpression && parameter?.isVararg == true
)
}

Expand Down Expand Up @@ -709,4 +737,4 @@ private fun ConeKotlinType.hasSupertypeWithGivenClassId(classId: ClassId, contex
typeConstructor is ConeClassLikeLookupTag && typeConstructor.classId == classId
}
}
}
}
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.transformTypeToUnderlyingVarargType()
property.backingField?.transformTypeToUnderlyingVarargType()
setAccessorTypesByPropertyType(property)
}

Expand Down Expand Up @@ -339,7 +339,7 @@ open class FirTypeResolveTransformer(
withDeclaration(valueParameter) {
valueParameter.transformReturnTypeRef(this, data)
valueParameter.transformAnnotations(this, data)
valueParameter.transformVarargTypeToArrayType()
valueParameter.transformVarargTypeToUnderlyingType()
valueParameter
}
}
Expand Down
Loading