From 97024d9ccb94bed4d90668465d53be7000e33312 Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Tue, 18 Jul 2023 12:01:05 +0200 Subject: [PATCH] [FIR] Resolve array literal argument for non-primitive-array parameter as arrayOf call in annotation calls This allows us to properly complete array literals arguments of annotation calls fixing several false-negative type mismatch errors as well as enabling the inference of generic type arguments. #KT-59581 Fixed #KT-58883 Fixed --- ...n_multipleAnnotations_collectionLiteral.kt | 2 +- .../jetbrains/kotlin/fir/FirCallResolver.kt | 81 ++++++++++++++----- .../kotlin/fir/resolve/ResolutionMode.kt | 5 ++ .../FirExpressionsResolveTransformer.kt | 8 +- .../argumentsOfAnnotation.fir.kt | 14 +++- .../argumentsOfAnnotation.kt | 12 +++ .../argumentsOfAnnotation.txt | 39 +++++++++ .../argumentsOfAnnotationWithKClass.fir.kt | 4 +- .../collectionLiteralsWithVarargs.fir.kt | 4 +- ...assignArrayToVararagInNamedForm_1_3.fir.kt | 2 +- ...assignArrayToVararagInNamedForm_1_4.fir.kt | 2 +- .../genericAnnotationClasses.fir.ir.txt | 2 +- .../genericAnnotationClasses.fir.kt.txt | 2 +- .../annotations/genericAnnotationClasses.kt | 2 +- 14 files changed, 145 insertions(+), 34 deletions(-) diff --git a/analysis/analysis-api/testData/components/callResolver/resolveCall/annotationInAnnotation_multipleAnnotations_collectionLiteral.kt b/analysis/analysis-api/testData/components/callResolver/resolveCall/annotationInAnnotation_multipleAnnotations_collectionLiteral.kt index 6613d1be0f7..a24e24da024 100644 --- a/analysis/analysis-api/testData/components/callResolver/resolveCall/annotationInAnnotation_multipleAnnotations_collectionLiteral.kt +++ b/analysis/analysis-api/testData/components/callResolver/resolveCall/annotationInAnnotation_multipleAnnotations_collectionLiteral.kt @@ -2,5 +2,5 @@ annotation class Annotation(vararg val strings: String) annotation class AnnotationArray(vararg val annos: Annotation) -@AnnotationArray([Annotation("v1", "v2"), Annotation(["v3", "v4"])]) +@AnnotationArray(annos = [Annotation("v1", "v2"), Annotation(["v3", "v4"])]) class C diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt index 200a02125d1..54d2f92b248 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.ConeStubDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList import org.jetbrains.kotlin.fir.expressions.builder.buildResolvedReifiedParameterReference import org.jetbrains.kotlin.fir.references.* import org.jetbrains.kotlin.fir.references.builder.buildBackingFieldReference @@ -37,11 +38,14 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressions import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.builder.buildStarProjection import org.jetbrains.kotlin.fir.types.builder.buildTypeProjectionWithVariance +import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder @@ -520,24 +524,40 @@ class FirCallResolver( fun resolveAnnotationCall(annotation: FirAnnotationCall): FirAnnotationCall? { val reference = annotation.calleeReference as? FirSimpleNamedReference ?: return null - annotation.replaceArgumentList(annotation.argumentList.transform(transformer, ResolutionMode.ContextDependent)) - - val callInfo = CallInfo( - annotation, - CallKind.Function, - name = reference.name, - explicitReceiver = null, - annotation.argumentList, - isImplicitInvoke = false, - typeArguments = annotation.typeArguments, - session, - components.file, - components.containingDeclarations - ) - val annotationClassSymbol = annotation.getCorrespondingClassSymbolOrNull(session) val resolvedReference = if (annotationClassSymbol != null && annotationClassSymbol.fir.classKind == ClassKind.ANNOTATION_CLASS) { - val resolutionResult = createCandidateForAnnotationCall(annotationClassSymbol, callInfo) + val constructorSymbol = getConstructorSymbol(annotationClassSymbol) + 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. + // 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. + 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) + } + }) + } else { + annotation.replaceArgumentList(annotation.argumentList.transform(transformer, ResolutionMode.ContextDependent.Default)) + } + + val callInfo = toCallInfo(annotation, reference) + + val resolutionResult = constructorSymbol + ?.let { runResolutionForGivenSymbol(callInfo, it) } ?: ResolutionResult(callInfo, CandidateApplicability.HIDDEN, emptyList()) createResolvedNamedReference( reference, @@ -548,6 +568,10 @@ class FirCallResolver( explicitReceiver = null ) } else { + annotation.replaceArgumentList(annotation.argumentList.transform(transformer, ResolutionMode.ContextDependent.Default)) + + val callInfo = toCallInfo(annotation, reference) + buildReferenceWithErrorCandidate( callInfo, if (annotationClassSymbol != null) ConeIllegalAnnotationError(reference.name) @@ -562,10 +586,20 @@ class FirCallResolver( } } - private fun createCandidateForAnnotationCall( - annotationClassSymbol: FirRegularClassSymbol, - callInfo: CallInfo - ): ResolutionResult? { + private fun toCallInfo(annotation: FirAnnotationCall, reference: FirSimpleNamedReference): CallInfo = CallInfo( + annotation, + CallKind.Function, + name = reference.name, + explicitReceiver = null, + annotation.argumentList, + isImplicitInvoke = false, + typeArguments = annotation.typeArguments, + session, + components.file, + components.containingDeclarations + ) + + private fun getConstructorSymbol(annotationClassSymbol: FirRegularClassSymbol): FirConstructorSymbol? { var constructorSymbol: FirConstructorSymbol? = null annotationClassSymbol.fir.unsubstitutedScope( session, @@ -577,11 +611,14 @@ class FirCallResolver( constructorSymbol = it } } - if (constructorSymbol == null) return null + return constructorSymbol + } + + private fun runResolutionForGivenSymbol(callInfo: CallInfo, symbol: FirBasedSymbol<*>): ResolutionResult { val candidateFactory = CandidateFactory(transformer.resolutionContext, callInfo) val candidate = candidateFactory.createCandidate( callInfo, - constructorSymbol!!, + symbol, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, scope = null ) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt index ecdb7c11f80..7d1d7cd7edf 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt @@ -18,6 +18,11 @@ 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) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index 6f68c66ac1e..950c5ac6a6b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -1637,14 +1637,20 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT override fun transformArrayOfCall(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) { + } else if ( + data is ResolutionMode.WithExpectedType && !data.expectedTypeRef.coneType.isPrimitiveOrUnsignedArray || + data is ResolutionMode.ContextDependent.TransformingArrayLiterals + ) { + // Default value of Array parameter or argument for Array 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) diff --git a/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.fir.kt b/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.fir.kt index 7f72e048a44..64fc9dcfd51 100644 --- a/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.fir.kt +++ b/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.fir.kt @@ -6,7 +6,7 @@ fun test1() {} @Foo([], [], []) fun test2() {} -@Foo([1f], [' '], [1]) +@Foo([1f], [' '], [1]) fun test3() {} @Foo(c = [1f], b = [""], a = [1]) @@ -26,3 +26,15 @@ fun test7() {} @Foo([two], [], []) fun test8() {} + +interface I +class C : I + +annotation class Test1(val x: Int) +annotation class Test2>(val x: Test1>) +@Repeatable annotation class Test3(val x: Array>>) + +@Test3([Test2>(Test1(40))]) +@Test3([Test2>(Test1(40))]) +@Test3([Test2(Test1(40))]) +fun test9() {} diff --git a/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.kt b/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.kt index d4736a8b08c..f70aaa788bf 100644 --- a/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.kt +++ b/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.kt @@ -26,3 +26,15 @@ fun test7() {} @Foo([two], [], []) fun test8() {} + +interface I +class C : I + +annotation class Test1(val x: Int) +annotation class Test2>(val x: Test1>) +@Repeatable annotation class Test3(val x: Array>>) + +@Test3([Test2>(Test1(40))]) +@Test3([Test2>(Test1(40))]) +@Test3([Test2(Test1(40))]) +fun test9() {} diff --git a/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.txt b/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.txt index 1088834eb80..836845c95cd 100644 --- a/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.txt +++ b/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotation.txt @@ -10,6 +10,14 @@ public val two: kotlin.Int = 2 @Foo(a = {1}, b = {}, c = {}) public fun test6(): kotlin.Unit @Foo(a = {3}, b = {}, c = {}) public fun test7(): kotlin.Unit @Foo(a = {2}, b = {}, c = {}) public fun test8(): kotlin.Unit +@Test3(x = {Test2(x = Test1(x = 40))}) @Test3(x = {Test2(x = Test1(x = 40))}) @Test3(x = {Test2(x = Test1(x = 40))}) public fun test9(): kotlin.Unit + +public final class C : I { + public constructor C() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} public final annotation class Foo : kotlin.Annotation { public constructor Foo(/*0*/ a: kotlin.IntArray, /*1*/ b: kotlin.Array, /*2*/ c: kotlin.FloatArray) @@ -20,3 +28,34 @@ public final annotation class Foo : kotlin.Annotation { public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + +public interface I { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final annotation class Test1 : kotlin.Annotation { + public constructor Test1(/*0*/ x: kotlin.Int) + public final val x: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final annotation class Test2> : kotlin.Annotation { + public constructor Test2>(/*0*/ x: Test1>) + public final val x: Test1> + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.annotation.Repeatable public final annotation class Test3 : kotlin.Annotation { + public constructor Test3(/*0*/ x: kotlin.Array>>) + public final val x: kotlin.Array>> + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + diff --git a/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotationWithKClass.fir.kt b/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotationWithKClass.fir.kt index 01392b2f838..67e38045c11 100644 --- a/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotationWithKClass.fir.kt +++ b/compiler/testData/diagnostics/tests/collectionLiterals/argumentsOfAnnotationWithKClass.fir.kt @@ -20,10 +20,10 @@ fun test3() {} @Foo([Gen::class]) fun test4() {} -@Foo([""]) +@Foo([""]) fun test5() {} -@Foo([Int::class, 1]) +@Foo([Int::class, 1]) fun test6() {} @Bar diff --git a/compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsWithVarargs.fir.kt b/compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsWithVarargs.fir.kt index 7a695277178..8624cf006c7 100644 --- a/compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsWithVarargs.fir.kt +++ b/compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsWithVarargs.fir.kt @@ -16,7 +16,7 @@ fun test1_0() {} @Ann1(*["a", "b"]) fun test1_1() {} -@Ann1(*["a", 1, null]) +@Ann1(*["a", 1, null]) fun test1_2() {} @Ann2(*[]) @@ -36,5 +36,5 @@ annotation class AnnArray(val a: Array) @AnnArray(*["/"]) fun testArray() {} -@Ann1([""]) +@Ann1([""]) fun testVararg() {} diff --git a/compiler/testData/diagnostics/tests/varargs/assignArrayToVararagInNamedForm_1_3.fir.kt b/compiler/testData/diagnostics/tests/varargs/assignArrayToVararagInNamedForm_1_3.fir.kt index b8657c339d0..08bca4fbfbe 100644 --- a/compiler/testData/diagnostics/tests/varargs/assignArrayToVararagInNamedForm_1_3.fir.kt +++ b/compiler/testData/diagnostics/tests/varargs/assignArrayToVararagInNamedForm_1_3.fir.kt @@ -19,7 +19,7 @@ fun test_fun(s: String, arr: Array) { } fun test_ann(s: String, arr: Array) { - @Ann([""], x = 1) + @Ann([""], x = 1) foo() @Ann(*[""], x = 1) foo() diff --git a/compiler/testData/diagnostics/tests/varargs/assignArrayToVararagInNamedForm_1_4.fir.kt b/compiler/testData/diagnostics/tests/varargs/assignArrayToVararagInNamedForm_1_4.fir.kt index 248e7dc5432..e56adf14c76 100644 --- a/compiler/testData/diagnostics/tests/varargs/assignArrayToVararagInNamedForm_1_4.fir.kt +++ b/compiler/testData/diagnostics/tests/varargs/assignArrayToVararagInNamedForm_1_4.fir.kt @@ -19,7 +19,7 @@ fun test_fun(s: String, arr: Array) { } fun test_ann(s: String, arr: Array) { - @Ann([""], x = 1) + @Ann([""], x = 1) foo() @Ann(*[""], x = 1) foo() diff --git a/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.fir.ir.txt b/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.fir.ir.txt index c1ed03dd534..332fab1437c 100644 --- a/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.fir.ir.txt +++ b/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.fir.ir.txt @@ -222,7 +222,7 @@ FILE fqName:ann fileName:/genericAnnotationClasses.kt Test1(x = '42') Test2(x = '38') Test3>(x = Test1>>(x = '39')) - Test4(x = [Test3(x = Test1(x = '40')), Test3(x = Test1(x = '50')), Test3(x = Test1(x = '60'))]) + Test4(x = [Test3>(x = Test1>>(x = '40')), Test3>(x = Test1>>(x = '50')), Test3>(x = Test1>>(x = '60'))]) Test5(xs = [Test3>(x = Test1>>(x = '70')), Test3>(x = Test1>>(x = '80'))]) $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:ann.CC CONSTRUCTOR visibility:public <> () returnType:ann.CC [primary] diff --git a/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.fir.kt.txt b/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.fir.kt.txt index 4c8fbd072ff..2bfcc0dc06c 100644 --- a/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.fir.kt.txt +++ b/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.fir.kt.txt @@ -90,7 +90,7 @@ open annotation class Test5 : Annotation { @Test1(x = 42) @Test2(x = 38) @Test3>(x = Test1>>(x = 39)) -@Test4(x = [Test3(x = Test1(x = 40)), Test3(x = Test1(x = 50)), Test3(x = Test1(x = 60))]) +@Test4(x = [Test3>(x = Test1>>(x = 40)), Test3>(x = Test1>>(x = 50)), Test3>(x = Test1>>(x = 60))]) @Test5(xs = [Test3>(x = Test1>>(x = 70)), Test3>(x = Test1>>(x = 80))]) class CC { constructor() /* primary */ { diff --git a/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.kt b/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.kt index 8b348ec95be..0a77de3fb97 100644 --- a/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.kt +++ b/compiler/testData/ir/irText/declarations/annotations/genericAnnotationClasses.kt @@ -1,5 +1,5 @@ // MUTE_SIGNATURE_COMPARISON_K2: ANY -// ^ KT-54517 +// ^ KT-60136 package ann