[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:
Ivan Kochurkin
2023-07-21 19:50:27 +02:00
committed by Space Team
parent e1ebbc10d9
commit f4e4c5e724
12 changed files with 107 additions and 70 deletions
@@ -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)
@@ -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?,
@@ -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
}
}
}
@@ -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])
@@ -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"<!>]
}
@@ -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!>*<!>["/"])
@@ -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!>*<!>["/"])
@@ -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
@@ -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]<!>
)
@@ -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<!>]
)