[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 val ConeKotlinType.isPrimitiveArray: Boolean
get() = this is ConeClassLikeType && lookupTag.classId in StandardClassIds.primitiveArrayTypeByElementType.values 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 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.isUnsignedTypeOrNullableUnsignedType: Boolean get() = isAnyOfBuiltinType(StandardClassIds.unsignedTypes)
val ConeKotlinType.isUnsignedType: Boolean get() = isUnsignedTypeOrNullableUnsignedType && nullability == ConeNullability.NOT_NULL val ConeKotlinType.isUnsignedType: Boolean get() = isUnsignedTypeOrNullableUnsignedType && nullability == ConeNullability.NOT_NULL
@@ -530,24 +530,34 @@ class FirCallResolver(
constructorSymbol?.lazyResolveToPhase(FirResolvePhase.TYPES) constructorSymbol?.lazyResolveToPhase(FirResolvePhase.TYPES)
if (constructorSymbol != null && annotation.arguments.isNotEmpty()) { if (constructorSymbol != null && annotation.arguments.isNotEmpty()) {
// We want to "desugar" array literal arguments whose expected type is not a primitive or unsigned array to // We want to "desugar" array literal arguments to arrayOf, intArrayOf, floatArrayOf and other *arrayOf* calls
// function calls to arrayOf so that we can properly complete them eventually. // so that we can properly complete them eventually.
// In order to find out what the expected type is, we need to run argument mapping. // 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 // We don't want to force full completion before the whole call is completed so that type variables are preserved.
// 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).
// We therefore use a special resolution mode that triggers array literal desugaring but doesn't force completion.
val mapping = transformer.resolutionContext.bodyResolveComponents.mapArguments( val mapping = transformer.resolutionContext.bodyResolveComponents.mapArguments(
annotation.arguments, constructorSymbol.fir, originScope = null, callSiteIsOperatorCall = false, annotation.arguments, constructorSymbol.fir, originScope = null, callSiteIsOperatorCall = false,
) )
val argumentsToParameters = mapping.toArgumentToParameterMapping() val argumentsToParameters = mapping.toArgumentToParameterMapping()
annotation.replaceArgumentList(buildArgumentList { annotation.replaceArgumentList(buildArgumentList {
source = annotation.argumentList.source source = annotation.argumentList.source
annotation.arguments.mapTo(arguments) { annotation.arguments.mapTo(arguments) { arg ->
val isPrimitiveOrUnsignedArrayType = val resolutionMode = if (arg.unwrapArgument() is FirArrayOfCall) {
argumentsToParameters[it]?.returnTypeRef?.coneType?.isPrimitiveOrUnsignedArray == true (argumentsToParameters[arg]?.returnTypeRef as? FirResolvedTypeRef)?.let {
val resolutionMode = // Enabling expectedTypeMismatchIsReportedInChecker clarifies error messages:
if (!isPrimitiveOrUnsignedArrayType) ResolutionMode.ContextDependent.TransformingArrayLiterals else ResolutionMode.ContextDependent.Default // It will be reported single ARGUMENT_TYPE_MISMATCH on the array literal in checkApplicabilityForArgumentType
it.transformSingle(transformer, resolutionMode) // 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 { } else {
@@ -18,11 +18,6 @@ sealed class ResolutionMode(val forceFullCompletion: Boolean) {
} }
data object Delegate : ContextDependent() data object Delegate : ContextDependent()
/**
* Forces array literals to be transformed to arrayOf calls.
*/
data object TransformingArrayLiterals : ContextDependent()
} }
data object ContextIndependent : ResolutionMode(forceFullCompletion = true) data object ContextIndependent : ResolutionMode(forceFullCompletion = true)
@@ -6,11 +6,16 @@
package org.jetbrains.kotlin.fir.resolve.transformers package org.jetbrains.kotlin.fir.resolve.transformers
import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.FirElement 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.*
import org.jetbrains.kotlin.fir.declarations.builder.FirSimpleFunctionBuilder import org.jetbrains.kotlin.fir.declarations.builder.FirSimpleFunctionBuilder
import org.jetbrains.kotlin.fir.declarations.builder.buildTypeParameter 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.moduleData
import org.jetbrains.kotlin.fir.references.FirReference import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReference 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.builder.buildResolvedErrorReference
import org.jetbrains.kotlin.fir.references.impl.FirStubReference import org.jetbrains.kotlin.fir.references.impl.FirStubReference
import org.jetbrains.kotlin.fir.references.isError 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.calls.*
import org.jetbrains.kotlin.fir.resolve.createErrorReferenceWithExistingCandidate import org.jetbrains.kotlin.fir.resolve.createErrorReferenceWithExistingCandidate
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError 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.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.toErrorReference import org.jetbrains.kotlin.fir.resolve.toErrorReference
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
@@ -63,12 +66,7 @@ class FirSyntheticCallGenerator(
private val idFunction: FirSimpleFunction = generateSyntheticSelectFunction(SyntheticCallableId.ID) private val idFunction: FirSimpleFunction = generateSyntheticSelectFunction(SyntheticCallableId.ID)
private val checkNotNullFunction: FirSimpleFunction = generateSyntheticCheckNotNullFunction() private val checkNotNullFunction: FirSimpleFunction = generateSyntheticCheckNotNullFunction()
private val elvisFunction: FirSimpleFunction = generateSyntheticElvisFunction() private val elvisFunction: FirSimpleFunction = generateSyntheticElvisFunction()
private val arrayOfSymbolCache: FirCache<Name, FirNamedFunctionSymbol, Nothing?> = session.firCachesFactory.createCache(::getArrayOfSymbol)
private val arrayOfSymbol by lazy(LazyThreadSafetyMode.NONE) {
session.symbolProvider
.getTopLevelFunctionSymbols(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, ArrayFqNames.ARRAY_OF_FUNCTION)
.firstOrNull()
}
private fun assertSyntheticResolvableReferenceIsNotResolved(resolvable: FirResolvable) { private fun assertSyntheticResolvableReferenceIsNotResolved(resolvable: FirResolvable) {
// All synthetic calls (FirWhenExpression, FirTryExpression, FirElvisExpression, FirCheckNotNullCall) // 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 val argumentList = arrayOfCall.argumentList
return buildFunctionCall { return buildFunctionCall {
this.argumentList = argumentList this.argumentList = argumentList
calleeReference = arrayOfSymbol?.let { calleeReference = generateCalleeReferenceWithCandidate(
generateCalleeReferenceWithCandidate( arrayOfCall,
arrayOfCall, calculateArrayOfSymbol(expectedTypeRef).fir,
it.fir, argumentList,
argumentList, ArrayFqNames.ARRAY_OF_FUNCTION,
ArrayFqNames.ARRAY_OF_FUNCTION, callKind = CallKind.Function,
callKind = CallKind.Function, context = context,
context = context, )
)
} ?: buildErrorNamedReference {
diagnostic = ConeUnresolvedNameError(ArrayFqNames.ARRAY_OF_FUNCTION)
}
source = arrayOfCall.source 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( fun resolveCallableReferenceWithSyntheticOuterCall(
callableReferenceAccess: FirCallableReferenceAccess, callableReferenceAccess: FirCallableReferenceAccess,
expectedTypeRef: FirTypeRef?, expectedTypeRef: FirTypeRef?,
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.* 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.calls.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.* import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.inference.FirStubInferenceSession 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.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.replaceLambdaArgumentInvocationKinds import org.jetbrains.kotlin.fir.resolve.transformers.replaceLambdaArgumentInvocationKinds
import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperator 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.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.ArrayFqNames
import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.ConstantValueKind 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) { whileAnalysing(session, arrayOfCall) {
if (data is ResolutionMode.ContextDependent.Default) { when (data) {
// Argument for primitive array parameter in annotation call or argument in non-annotation call (unsupported). is ResolutionMode.ContextDependent.Default -> {
arrayOfCall.transformChildren(transformer, data) // Argument in non-annotation call (unsupported) or if type of array parameter is unresolved.
arrayOfCall arrayOfCall.transformChildren(transformer, data)
} else if ( arrayOfCall
data is ResolutionMode.WithExpectedType && !data.expectedTypeRef.coneType.isPrimitiveOrUnsignedArray || }
data is ResolutionMode.ContextDependent.TransformingArrayLiterals is ResolutionMode.WithExpectedType -> {
) { // Default value of array parameter (Array<T> or primitive array such as IntArray, FloatArray, ...)
// Default value of Array<T> parameter or argument for Array<T> parameter in annotation call. // or argument for array parameter in annotation call.
arrayOfCall.transformChildren(transformer, ResolutionMode.ContextDependent) arrayOfCall.transformChildren(transformer, ResolutionMode.ContextDependent)
val call = components.syntheticCallGenerator.generateSyntheticArrayOfCall(arrayOfCall, resolutionContext) val call = components.syntheticCallGenerator.generateSyntheticArrayOfCall(arrayOfCall, data.expectedTypeRef, resolutionContext)
callCompleter.completeCall(call, data) callCompleter.completeCall(call, data)
arrayOfCallTransformer.transformFunctionCall(call, session) arrayOfCallTransformer.transformFunctionCall(call, session)
} else { }
// Default value of primitive array parameter or other unsupported usage. else -> {
val syntheticIdCall = components.syntheticCallGenerator.generateSyntheticIdCall(arrayOfCall, resolutionContext) // Other unsupported usage.
arrayOfCall.transformChildren(transformer, ResolutionMode.ContextDependent) val syntheticIdCall = components.syntheticCallGenerator.generateSyntheticIdCall(arrayOfCall, resolutionContext)
callCompleter.completeCall(syntheticIdCall, data) arrayOfCall.transformChildren(transformer, ResolutionMode.ContextDependent)
arrayOfCall callCompleter.completeCall(syntheticIdCall, data)
arrayOfCall
}
} }
} }
@@ -6,7 +6,7 @@ fun test1() {}
@Foo([], [], []) @Foo([], [], [])
fun test2() {} fun test2() {}
@Foo([1f], <!ARGUMENT_TYPE_MISMATCH!>[' ']<!>, [1]) @Foo([<!ARGUMENT_TYPE_MISMATCH!>1f<!>], <!ARGUMENT_TYPE_MISMATCH!>[' ']<!>, [<!ARGUMENT_TYPE_MISMATCH!>1<!>])
fun test3() {} fun test3() {}
@Foo(c = [1f], b = [""], a = [1]) @Foo(c = [1f], b = [""], a = [1])
@@ -12,7 +12,7 @@ fun basicTypes() {
} }
fun basicTypesWithErrors() { fun basicTypesWithErrors() {
val a: IntArray = <!INITIALIZER_TYPE_MISMATCH!>[1.0]<!> val a: IntArray = [<!ARGUMENT_TYPE_MISMATCH!>1.0<!>]
val b: ShortArray = <!INITIALIZER_TYPE_MISMATCH!>[1.0]<!> val b: ShortArray = [<!ARGUMENT_TYPE_MISMATCH!>1.0<!>]
val c: CharArray = <!INITIALIZER_TYPE_MISMATCH!>["a"]<!> 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 Ann5(vararg val a: Ann4 = [])
annotation class Ann6(vararg val a: Ann4 = [Ann4(*["a", "b"])]) 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(*[]) @Ann1(*[])
fun test1_0() {} fun test1_0() {}
@@ -31,6 +31,9 @@ fun test5() {}
@Ann6(*[]) @Ann6(*[])
fun test6() {} fun test6() {}
@Ann7(1, 2)
fun test7() {}
annotation class AnnArray(val a: Array<String>) annotation class AnnArray(val a: Array<String>)
@AnnArray(<!NON_VARARG_SPREAD!>*<!>["/"]) @AnnArray(<!NON_VARARG_SPREAD!>*<!>["/"])
@@ -31,6 +31,9 @@ fun test5() {}
@Ann6(*[]) @Ann6(*[])
fun test6() {} fun test6() {}
@Ann7(1, 2)
fun test7() {}
annotation class AnnArray(val a: Array<String>) annotation class AnnArray(val a: Array<String>)
@AnnArray(<!NON_VARARG_SPREAD_ERROR!>*<!>["/"]) @AnnArray(<!NON_VARARG_SPREAD_ERROR!>*<!>["/"])
@@ -7,6 +7,7 @@ package
@Ann3(a = {0.0.toFloat(), Infinity.toFloat()}) public fun test3(): kotlin.Unit @Ann3(a = {0.0.toFloat(), Infinity.toFloat()}) public fun test3(): kotlin.Unit
@Ann5(a = {Ann4(a = {"/"})}) public fun test5(): kotlin.Unit @Ann5(a = {Ann4(a = {"/"})}) public fun test5(): kotlin.Unit
@Ann6(a = {}) public fun test6(): 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 @AnnArray(a = {"/"}) public fun testArray(): kotlin.Unit
@Ann1(a = {{""}}) public fun testVararg(): kotlin.Unit @Ann1(a = {{""}}) public fun testVararg(): kotlin.Unit
@@ -21,6 +21,6 @@ annotation class Base(
) )
annotation class Err( 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]<!> val b: Array<String> = <!TYPE_MISMATCH, TYPE_MISMATCH!>[1]<!>
) )
@@ -17,7 +17,7 @@ annotation class Bar(
) )
annotation class Baz( annotation class Baz(
val a: IntArray = <!INITIALIZER_TYPE_MISMATCH!>[null]<!>, val a: IntArray = [<!NULL_FOR_NONNULL_TYPE!>null<!>],
val b: IntArray = <!INITIALIZER_TYPE_MISMATCH!>[1, null, 2]<!>, val b: IntArray = [1, <!NULL_FOR_NONNULL_TYPE!>null<!>, 2],
val c: IntArray = <!INITIALIZER_TYPE_MISMATCH!>[<!NO_THIS!>this<!>]<!> val c: IntArray = [<!NO_THIS!>this<!>]
) )