[FIR] Fix missing type mismatch with array literals
Clarify existing type mismatch errors on array literals ^KT-60474 Fixed Remove ResolutionMode.TransformingArrayLiterals
This commit is contained in:
committed by
Space Team
parent
e1ebbc10d9
commit
f4e4c5e724
@@ -56,8 +56,11 @@ val ConeKotlinType.isNonPrimitiveArray: Boolean
|
||||
val ConeKotlinType.isPrimitiveArray: Boolean
|
||||
get() = this is ConeClassLikeType && lookupTag.classId in StandardClassIds.primitiveArrayTypeByElementType.values
|
||||
|
||||
val ConeKotlinType.isUnsignedArray: Boolean
|
||||
get() = this is ConeClassLikeType && lookupTag.classId in StandardClassIds.unsignedArrayTypeByElementType.values
|
||||
|
||||
val ConeKotlinType.isPrimitiveOrUnsignedArray: Boolean
|
||||
get() = this is ConeClassLikeType && (lookupTag.classId in StandardClassIds.primitiveArrayTypeByElementType.values || lookupTag.classId in StandardClassIds.unsignedArrayTypeByElementType.values)
|
||||
get() = isPrimitiveArray || isUnsignedArray
|
||||
|
||||
val ConeKotlinType.isUnsignedTypeOrNullableUnsignedType: Boolean get() = isAnyOfBuiltinType(StandardClassIds.unsignedTypes)
|
||||
val ConeKotlinType.isUnsignedType: Boolean get() = isUnsignedTypeOrNullableUnsignedType && nullability == ConeNullability.NOT_NULL
|
||||
|
||||
@@ -530,24 +530,34 @@ class FirCallResolver(
|
||||
constructorSymbol?.lazyResolveToPhase(FirResolvePhase.TYPES)
|
||||
|
||||
if (constructorSymbol != null && annotation.arguments.isNotEmpty()) {
|
||||
// We want to "desugar" array literal arguments whose expected type is not a primitive or unsigned array to
|
||||
// function calls to arrayOf so that we can properly complete them eventually.
|
||||
// We want to "desugar" array literal arguments to arrayOf, intArrayOf, floatArrayOf and other *arrayOf* calls
|
||||
// so that we can properly complete them eventually.
|
||||
// In order to find out what the expected type is, we need to run argument mapping.
|
||||
// However, we don't want to resolve them with expectedType because we don't want to force completion before the whole
|
||||
// call is completed so that type variables are preserved.
|
||||
// We therefore use a special resolution mode that triggers array literal desugaring but doesn't force completion.
|
||||
// We don't want to force full completion before the whole call is completed so that type variables are preserved.
|
||||
// But we need to pass expectType to figure out the correct *arrayOf* function (because Array<T> and primitive arrays can't be matched).
|
||||
val mapping = transformer.resolutionContext.bodyResolveComponents.mapArguments(
|
||||
annotation.arguments, constructorSymbol.fir, originScope = null, callSiteIsOperatorCall = false,
|
||||
)
|
||||
val argumentsToParameters = mapping.toArgumentToParameterMapping()
|
||||
annotation.replaceArgumentList(buildArgumentList {
|
||||
source = annotation.argumentList.source
|
||||
annotation.arguments.mapTo(arguments) {
|
||||
val isPrimitiveOrUnsignedArrayType =
|
||||
argumentsToParameters[it]?.returnTypeRef?.coneType?.isPrimitiveOrUnsignedArray == true
|
||||
val resolutionMode =
|
||||
if (!isPrimitiveOrUnsignedArrayType) ResolutionMode.ContextDependent.TransformingArrayLiterals else ResolutionMode.ContextDependent.Default
|
||||
it.transformSingle(transformer, resolutionMode)
|
||||
annotation.arguments.mapTo(arguments) { arg ->
|
||||
val resolutionMode = if (arg.unwrapArgument() is FirArrayOfCall) {
|
||||
(argumentsToParameters[arg]?.returnTypeRef as? FirResolvedTypeRef)?.let {
|
||||
// Enabling expectedTypeMismatchIsReportedInChecker clarifies error messages:
|
||||
// It will be reported single ARGUMENT_TYPE_MISMATCH on the array literal in checkApplicabilityForArgumentType
|
||||
// instead of several TYPE_MISMATCH for every mismatched argument.
|
||||
ResolutionMode.WithExpectedType(
|
||||
it,
|
||||
forceFullCompletion = false,
|
||||
expectedTypeMismatchIsReportedInChecker = true
|
||||
)
|
||||
} ?: ResolutionMode.ContextDependent.Default
|
||||
} else {
|
||||
ResolutionMode.ContextDependent
|
||||
}
|
||||
|
||||
arg.transformSingle(transformer, resolutionMode)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -18,11 +18,6 @@ sealed class ResolutionMode(val forceFullCompletion: Boolean) {
|
||||
}
|
||||
|
||||
data object Delegate : ContextDependent()
|
||||
|
||||
/**
|
||||
* Forces array literals to be transformed to arrayOf calls.
|
||||
*/
|
||||
data object TransformingArrayLiterals : ContextDependent()
|
||||
}
|
||||
|
||||
data object ContextIndependent : ResolutionMode(forceFullCompletion = true)
|
||||
|
||||
+44
-21
@@ -6,11 +6,16 @@
|
||||
package org.jetbrains.kotlin.fir.resolve.transformers
|
||||
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fakeElement
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.caches.FirCache
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.FirSimpleFunctionBuilder
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildTypeParameter
|
||||
@@ -23,7 +28,6 @@ import org.jetbrains.kotlin.fir.expressions.builder.buildFunctionCall
|
||||
import org.jetbrains.kotlin.fir.moduleData
|
||||
import org.jetbrains.kotlin.fir.references.FirReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReference
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildResolvedErrorReference
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirStubReference
|
||||
import org.jetbrains.kotlin.fir.references.isError
|
||||
@@ -32,7 +36,6 @@ import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.*
|
||||
import org.jetbrains.kotlin.fir.resolve.createErrorReferenceWithExistingCandidate
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.toErrorReference
|
||||
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
|
||||
@@ -63,12 +66,7 @@ class FirSyntheticCallGenerator(
|
||||
private val idFunction: FirSimpleFunction = generateSyntheticSelectFunction(SyntheticCallableId.ID)
|
||||
private val checkNotNullFunction: FirSimpleFunction = generateSyntheticCheckNotNullFunction()
|
||||
private val elvisFunction: FirSimpleFunction = generateSyntheticElvisFunction()
|
||||
|
||||
private val arrayOfSymbol by lazy(LazyThreadSafetyMode.NONE) {
|
||||
session.symbolProvider
|
||||
.getTopLevelFunctionSymbols(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, ArrayFqNames.ARRAY_OF_FUNCTION)
|
||||
.firstOrNull()
|
||||
}
|
||||
private val arrayOfSymbolCache: FirCache<Name, FirNamedFunctionSymbol, Nothing?> = session.firCachesFactory.createCache(::getArrayOfSymbol)
|
||||
|
||||
private fun assertSyntheticResolvableReferenceIsNotResolved(resolvable: FirResolvable) {
|
||||
// All synthetic calls (FirWhenExpression, FirTryExpression, FirElvisExpression, FirCheckNotNullCall)
|
||||
@@ -166,26 +164,51 @@ class FirSyntheticCallGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
fun generateSyntheticArrayOfCall(arrayOfCall: FirArrayOfCall, context: ResolutionContext): FirFunctionCall {
|
||||
fun generateSyntheticArrayOfCall(
|
||||
arrayOfCall: FirArrayOfCall,
|
||||
expectedTypeRef: FirTypeRef,
|
||||
context: ResolutionContext
|
||||
): FirFunctionCall {
|
||||
val argumentList = arrayOfCall.argumentList
|
||||
return buildFunctionCall {
|
||||
this.argumentList = argumentList
|
||||
calleeReference = arrayOfSymbol?.let {
|
||||
generateCalleeReferenceWithCandidate(
|
||||
arrayOfCall,
|
||||
it.fir,
|
||||
argumentList,
|
||||
ArrayFqNames.ARRAY_OF_FUNCTION,
|
||||
callKind = CallKind.Function,
|
||||
context = context,
|
||||
)
|
||||
} ?: buildErrorNamedReference {
|
||||
diagnostic = ConeUnresolvedNameError(ArrayFqNames.ARRAY_OF_FUNCTION)
|
||||
}
|
||||
calleeReference = generateCalleeReferenceWithCandidate(
|
||||
arrayOfCall,
|
||||
calculateArrayOfSymbol(expectedTypeRef).fir,
|
||||
argumentList,
|
||||
ArrayFqNames.ARRAY_OF_FUNCTION,
|
||||
callKind = CallKind.Function,
|
||||
context = context,
|
||||
)
|
||||
source = arrayOfCall.source
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateArrayOfSymbol(expectedTypeRef: FirTypeRef): FirNamedFunctionSymbol {
|
||||
val coneType = expectedTypeRef.coneType
|
||||
val arrayCallName = when {
|
||||
coneType.isPrimitiveArray -> {
|
||||
val arrayElementClassId = coneType.arrayElementType()!!.classId
|
||||
val primitiveType = PrimitiveType.getByShortName(arrayElementClassId!!.shortClassName.asString())
|
||||
ArrayFqNames.PRIMITIVE_TYPE_TO_ARRAY[primitiveType]!!
|
||||
}
|
||||
coneType.isUnsignedArray -> {
|
||||
val arrayElementClassId = coneType.arrayElementType()!!.classId
|
||||
ArrayFqNames.UNSIGNED_TYPE_TO_ARRAY[arrayElementClassId!!.asSingleFqName()]!!
|
||||
}
|
||||
else -> {
|
||||
ArrayFqNames.ARRAY_OF_FUNCTION
|
||||
}
|
||||
}
|
||||
return arrayOfSymbolCache.getValue(arrayCallName)
|
||||
}
|
||||
|
||||
private fun getArrayOfSymbol(arrayOfName: Name): FirNamedFunctionSymbol {
|
||||
return session.symbolProvider
|
||||
.getTopLevelFunctionSymbols(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, arrayOfName)
|
||||
.single()
|
||||
}
|
||||
|
||||
fun resolveCallableReferenceWithSyntheticOuterCall(
|
||||
callableReferenceAccess: FirCallableReferenceAccess,
|
||||
expectedTypeRef: FirTypeRef?,
|
||||
|
||||
+22
-23
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
|
||||
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.KtSourceElement
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fakeElement
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
@@ -31,7 +30,6 @@ import org.jetbrains.kotlin.fir.resolve.*
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.*
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.FirStubInferenceSession
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.replaceLambdaArgumentInvocationKinds
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperator
|
||||
@@ -47,7 +45,6 @@ import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.resolve.ArrayFqNames
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.ConstantValueKind
|
||||
@@ -1634,27 +1631,29 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
|
||||
)
|
||||
}
|
||||
|
||||
override fun transformArrayOfCall(arrayOfCall: FirArrayOfCall, data: ResolutionMode): FirStatement =
|
||||
override fun transformArrayLiteral(arrayOfCall: FirArrayOfCall, data: ResolutionMode): FirStatement =
|
||||
whileAnalysing(session, arrayOfCall) {
|
||||
if (data is ResolutionMode.ContextDependent.Default) {
|
||||
// Argument for primitive array parameter in annotation call or argument in non-annotation call (unsupported).
|
||||
arrayOfCall.transformChildren(transformer, data)
|
||||
arrayOfCall
|
||||
} else if (
|
||||
data is ResolutionMode.WithExpectedType && !data.expectedTypeRef.coneType.isPrimitiveOrUnsignedArray ||
|
||||
data is ResolutionMode.ContextDependent.TransformingArrayLiterals
|
||||
) {
|
||||
// Default value of Array<T> parameter or argument for Array<T> parameter in annotation call.
|
||||
arrayOfCall.transformChildren(transformer, ResolutionMode.ContextDependent)
|
||||
val call = components.syntheticCallGenerator.generateSyntheticArrayOfCall(arrayOfCall, resolutionContext)
|
||||
callCompleter.completeCall(call, data)
|
||||
arrayOfCallTransformer.transformFunctionCall(call, session)
|
||||
} else {
|
||||
// Default value of primitive array parameter or other unsupported usage.
|
||||
val syntheticIdCall = components.syntheticCallGenerator.generateSyntheticIdCall(arrayOfCall, resolutionContext)
|
||||
arrayOfCall.transformChildren(transformer, ResolutionMode.ContextDependent)
|
||||
callCompleter.completeCall(syntheticIdCall, data)
|
||||
arrayOfCall
|
||||
when (data) {
|
||||
is ResolutionMode.ContextDependent.Default -> {
|
||||
// Argument in non-annotation call (unsupported) or if type of array parameter is unresolved.
|
||||
arrayOfCall.transformChildren(transformer, data)
|
||||
arrayOfCall
|
||||
}
|
||||
is ResolutionMode.WithExpectedType -> {
|
||||
// Default value of array parameter (Array<T> or primitive array such as IntArray, FloatArray, ...)
|
||||
// or argument for array parameter in annotation call.
|
||||
arrayOfCall.transformChildren(transformer, ResolutionMode.ContextDependent)
|
||||
val call = components.syntheticCallGenerator.generateSyntheticArrayOfCall(arrayOfCall, data.expectedTypeRef, resolutionContext)
|
||||
callCompleter.completeCall(call, data)
|
||||
arrayOfCallTransformer.transformFunctionCall(call, session)
|
||||
}
|
||||
else -> {
|
||||
// Other unsupported usage.
|
||||
val syntheticIdCall = components.syntheticCallGenerator.generateSyntheticIdCall(arrayOfCall, resolutionContext)
|
||||
arrayOfCall.transformChildren(transformer, ResolutionMode.ContextDependent)
|
||||
callCompleter.completeCall(syntheticIdCall, data)
|
||||
arrayOfCall
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ fun test1() {}
|
||||
@Foo([], [], [])
|
||||
fun test2() {}
|
||||
|
||||
@Foo([1f], <!ARGUMENT_TYPE_MISMATCH!>[' ']<!>, [1])
|
||||
@Foo([<!ARGUMENT_TYPE_MISMATCH!>1f<!>], <!ARGUMENT_TYPE_MISMATCH!>[' ']<!>, [<!ARGUMENT_TYPE_MISMATCH!>1<!>])
|
||||
fun test3() {}
|
||||
|
||||
@Foo(c = [1f], b = [""], a = [1])
|
||||
|
||||
Vendored
+3
-3
@@ -12,7 +12,7 @@ fun basicTypes() {
|
||||
}
|
||||
|
||||
fun basicTypesWithErrors() {
|
||||
val a: IntArray = <!INITIALIZER_TYPE_MISMATCH!>[1.0]<!>
|
||||
val b: ShortArray = <!INITIALIZER_TYPE_MISMATCH!>[1.0]<!>
|
||||
val c: CharArray = <!INITIALIZER_TYPE_MISMATCH!>["a"]<!>
|
||||
val a: IntArray = [<!ARGUMENT_TYPE_MISMATCH!>1.0<!>]
|
||||
val b: ShortArray = [<!ARGUMENT_TYPE_MISMATCH!>1.0<!>]
|
||||
val c: CharArray = [<!ARGUMENT_TYPE_MISMATCH!>"a"<!>]
|
||||
}
|
||||
|
||||
Vendored
+4
-1
@@ -8,7 +8,7 @@ annotation class Ann4(vararg val a: String = ["/"])
|
||||
annotation class Ann5(vararg val a: Ann4 = [])
|
||||
annotation class Ann6(vararg val a: Ann4 = [Ann4(*["a", "b"])])
|
||||
|
||||
annotation class Ann7(vararg val a: Long = <!INITIALIZER_TYPE_MISMATCH!>[1L, null, ""]<!>)
|
||||
annotation class Ann7(vararg val a: Long = [1L, <!NULL_FOR_NONNULL_TYPE!>null<!>, <!ARGUMENT_TYPE_MISMATCH!>""<!>])
|
||||
|
||||
@Ann1(*[])
|
||||
fun test1_0() {}
|
||||
@@ -31,6 +31,9 @@ fun test5() {}
|
||||
@Ann6(*[])
|
||||
fun test6() {}
|
||||
|
||||
@Ann7(1, 2)
|
||||
fun test7() {}
|
||||
|
||||
annotation class AnnArray(val a: Array<String>)
|
||||
|
||||
@AnnArray(<!NON_VARARG_SPREAD!>*<!>["/"])
|
||||
|
||||
+3
@@ -31,6 +31,9 @@ fun test5() {}
|
||||
@Ann6(*[])
|
||||
fun test6() {}
|
||||
|
||||
@Ann7(1, 2)
|
||||
fun test7() {}
|
||||
|
||||
annotation class AnnArray(val a: Array<String>)
|
||||
|
||||
@AnnArray(<!NON_VARARG_SPREAD_ERROR!>*<!>["/"])
|
||||
|
||||
+1
@@ -7,6 +7,7 @@ package
|
||||
@Ann3(a = {0.0.toFloat(), Infinity.toFloat()}) public fun test3(): kotlin.Unit
|
||||
@Ann5(a = {Ann4(a = {"/"})}) public fun test5(): kotlin.Unit
|
||||
@Ann6(a = {}) public fun test6(): kotlin.Unit
|
||||
@Ann7(a = {1.toLong(), 2.toLong()}) public fun test7(): kotlin.Unit
|
||||
@AnnArray(a = {"/"}) public fun testArray(): kotlin.Unit
|
||||
@Ann1(a = {{""}}) public fun testVararg(): kotlin.Unit
|
||||
|
||||
|
||||
+1
-1
@@ -21,6 +21,6 @@ annotation class Base(
|
||||
)
|
||||
|
||||
annotation class Err(
|
||||
val a: IntArray = <!INITIALIZER_TYPE_MISMATCH!>[1L]<!>,
|
||||
val a: IntArray = [<!ARGUMENT_TYPE_MISMATCH!>1L<!>],
|
||||
val b: Array<String> = <!TYPE_MISMATCH, TYPE_MISMATCH!>[1]<!>
|
||||
)
|
||||
|
||||
compiler/testData/diagnostics/tests/collectionLiterals/defaultValuesWithConstantsInAnnotation.fir.kt
Vendored
+3
-3
@@ -17,7 +17,7 @@ annotation class Bar(
|
||||
)
|
||||
|
||||
annotation class Baz(
|
||||
val a: IntArray = <!INITIALIZER_TYPE_MISMATCH!>[null]<!>,
|
||||
val b: IntArray = <!INITIALIZER_TYPE_MISMATCH!>[1, null, 2]<!>,
|
||||
val c: IntArray = <!INITIALIZER_TYPE_MISMATCH!>[<!NO_THIS!>this<!>]<!>
|
||||
val a: IntArray = [<!NULL_FOR_NONNULL_TYPE!>null<!>],
|
||||
val b: IntArray = [1, <!NULL_FOR_NONNULL_TYPE!>null<!>, 2],
|
||||
val c: IntArray = [<!NO_THIS!>this<!>]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user