From d0854d5b451a91739ed4e6ae606c6ffc73ce9884 Mon Sep 17 00:00:00 2001 From: Dmitrii Gridin Date: Thu, 13 Jul 2023 16:22:59 +0200 Subject: [PATCH] [FIR] propagate copy of original ConeKotlinType to anonymous functions to avoid concurrent modification It is required to avoid leakage of annotations instance from the original type It should be enough to just create a new instance of an annotation without a deep copy, because transformer shouldn't touch it ^KT-60387 Fixed --- ...ropagateFlexibleTypeToAnonymousFunction.kt | 21 ++++++ ...peToAnonymousFunction.out_of_src_roots.txt | 23 +++++++ ...opagateFlexibleTypeToAnonymousFunction.txt | 23 +++++++ ...opagateFlexibleTypeToAnonymousFunction2.kt | 21 ++++++ ...eToAnonymousFunction2.out_of_src_roots.txt | 23 +++++++ ...pagateFlexibleTypeToAnonymousFunction2.txt | 23 +++++++ ...nonymousFunctionWithAnnotatedParameter.txt | 4 +- ...onymousFunctionWithAnnotatedParameter2.txt | 6 +- ...hAnnotatedParameterOnImplicitTypePhase.txt | 12 ++-- ...ContentRootGetOrBuildFirTestGenerated.java | 12 ++++ .../fir/SourceGetOrBuildFirTestGenerated.java | 12 ++++ ...CompilerTestFE10TestdataTestGenerated.java | 6 ++ ...sticCompilerFE10TestDataTestGenerated.java | 6 ++ ...eeOldFrontendDiagnosticsTestGenerated.java | 6 ++ ...siOldFrontendDiagnosticsTestGenerated.java | 6 ++ .../jetbrains/kotlin/fir/types/TypeUtils.kt | 67 +++++++++++++++++++ .../kotlin/fir/resolve/ResolveUtils.kt | 7 +- .../inference/PostponedArgumentsAnalyzer.kt | 2 +- .../types/CustomAnnotationTypeAttribute.kt | 44 ++++++++++++ ...ropagateFlexibleTypeToAnonymousFunction.kt | 22 ++++++ .../test/runners/DiagnosticTestGenerated.java | 6 ++ 21 files changed, 336 insertions(+), 16 deletions(-) create mode 100644 analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.kt create mode 100644 analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.out_of_src_roots.txt create mode 100644 analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.txt create mode 100644 analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.kt create mode 100644 analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.out_of_src_roots.txt create mode 100644 analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.txt create mode 100644 compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.kt new file mode 100644 index 00000000000..61ce0cd61a4 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.kt @@ -0,0 +1,21 @@ +// FILE: usage.kt + +val propertyToResolve: String + get() = JavaClass.function()?.let { " ($it)" } ?: "" + + +// FILE: Anno.java +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE_USE) +public @interface Anno { + String value(); +} + +// FILE: JavaClass.java +public class JavaClass { + public static @Anno("outer") List<@Anno("middle") List<@Anno("inner") Integer>> function() { + return null; + } +} diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.out_of_src_roots.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.out_of_src_roots.txt new file mode 100644 index 00000000000..661eb76d01e --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.out_of_src_roots.txt @@ -0,0 +1,23 @@ +KT element: KtProperty +FIR element: FirPropertyImpl +FIR source kind: KtRealSourceElementKind + +FIR element rendered: +public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ #.#()?.{ $subj$.#( = [ResolvedTo(BODY_RESOLVE)] let@fun (): R|kotlin/String| { + ^ (String( (), #, String())) + } + ) } ?: String() + } + +FIR FILE: +FILE: [ResolvedTo(IMPORTS)] usage.kt + [ResolvedTo(BODY_RESOLVE)] annotations container + public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ #.#()?.{ $subj$.#( = [ResolvedTo(BODY_RESOLVE)] let@fun (): R|kotlin/String| { + ^ (String( (), #, String())) + } + ) } ?: String() + } \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.txt new file mode 100644 index 00000000000..f364c23cf8a --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.txt @@ -0,0 +1,23 @@ +KT element: KtProperty +FIR element: FirPropertyImpl +FIR source kind: KtRealSourceElementKind + +FIR element rendered: +public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ Q|JavaClass|.R|/JavaClass.function|()?.{ $subj$.R|kotlin/let|( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(value = String(outer)) List|): R|kotlin/String| { + ^ (String( (), R|/it|, String())) + } + ) } ?: String() + } + +FIR FILE: +FILE: [ResolvedTo(IMPORTS)] usage.kt + [ResolvedTo(BODY_RESOLVE)] annotations container + public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ Q|JavaClass|.R|/JavaClass.function|()?.{ $subj$.R|kotlin/let|( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(value = String(outer)) List|): R|kotlin/String| { + ^ (String( (), R|/it|, String())) + } + ) } ?: String() + } diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.kt new file mode 100644 index 00000000000..a14b293a995 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.kt @@ -0,0 +1,21 @@ +// FILE: usage.kt + +val propertyToResolve: String + get() = JavaClass.function().let { " ($it)" } + + +// FILE: Anno.java +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE_USE) +public @interface Anno { + String value(); +} + +// FILE: JavaClass.java +public class JavaClass { + public static @Anno("outer") List<@Anno("middle") List<@Anno("inner") Integer>> function() { + return null; + } +} diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.out_of_src_roots.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.out_of_src_roots.txt new file mode 100644 index 00000000000..bd24680a314 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.out_of_src_roots.txt @@ -0,0 +1,23 @@ +KT element: KtProperty +FIR element: FirPropertyImpl +FIR source kind: KtRealSourceElementKind + +FIR element rendered: +public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ #.#().#( = [ResolvedTo(BODY_RESOLVE)] let@fun (): R|kotlin/String| { + ^ (String( (), #, String())) + } + ) + } + +FIR FILE: +FILE: [ResolvedTo(IMPORTS)] usage.kt + [ResolvedTo(BODY_RESOLVE)] annotations container + public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ #.#().#( = [ResolvedTo(BODY_RESOLVE)] let@fun (): R|kotlin/String| { + ^ (String( (), #, String())) + } + ) + } \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.txt new file mode 100644 index 00000000000..6e8e7b0e79c --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.txt @@ -0,0 +1,23 @@ +KT element: KtProperty +FIR element: FirPropertyImpl +FIR source kind: KtRealSourceElementKind + +FIR element rendered: +public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ Q|JavaClass|.R|/JavaClass.function|().R|kotlin/let|( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(value = String(outer)) List!|): R|kotlin/String| { + ^ (String( (), R|/it|, String())) + } + ) + } + +FIR FILE: +FILE: [ResolvedTo(IMPORTS)] usage.kt + [ResolvedTo(BODY_RESOLVE)] annotations container + public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ Q|JavaClass|.R|/JavaClass.function|().R|kotlin/let|( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(value = String(outer)) List!|): R|kotlin/String| { + ^ (String( (), R|/it|, String())) + } + ) + } diff --git a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter.txt b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter.txt index 7d37fe86459..7a2b775f5f7 100644 --- a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter.txt +++ b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter.txt @@ -229,8 +229,8 @@ FILE: [ResolvedTo(IMPORTS)] anonymousFunctionWithAnnotatedParameter.kt public [ResolvedTo(STATUS)] [ContainingClassKey=Anno] get(): R|kotlin/String| } - public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?| - public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?| { + public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(String(outer)) kotlin/collections/List<@R|Anno|(String(middle)) kotlin/collections/List<@R|Anno|(String(inner)) kotlin/Int>>?| + public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(String(outer)) kotlin/collections/List<@R|Anno|(String(middle)) kotlin/collections/List<@R|Anno|(String(inner)) kotlin/Int>>?| { ^ Null(null) } public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| diff --git a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter2.txt b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter2.txt index dc186d1d84f..7032ccfe0cc 100644 --- a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter2.txt +++ b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter2.txt @@ -229,13 +229,13 @@ FILE: [ResolvedTo(IMPORTS)] anonymousFunctionWithAnnotatedParameter2.kt public [ResolvedTo(STATUS)] [ContainingClassKey=Anno] get(): R|kotlin/String| } - public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?| - public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?| { + public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(String(outer)) kotlin/collections/List<@R|Anno|(String(middle)) kotlin/collections/List<@R|Anno|(String(inner)) kotlin/Int>>?| + public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(String(outer)) kotlin/collections/List<@R|Anno|(String(middle)) kotlin/collections/List<@R|Anno|(String(inner)) kotlin/Int>>?| { ^ Null(null) } public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { - ^ R|/nullablePropertyWithAnnotatedType|.R|kotlin/let|>?|, R|kotlin/String|>( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?|): R|kotlin/String| { + ^ R|/nullablePropertyWithAnnotatedType|.R|kotlin/let|>?|, R|kotlin/String|>( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?|): R|kotlin/String| { ^ (String( (), R|/it|, String())) } ) diff --git a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameterOnImplicitTypePhase.txt b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameterOnImplicitTypePhase.txt index 93a3bde62a5..73d7c66c567 100644 --- a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameterOnImplicitTypePhase.txt +++ b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameterOnImplicitTypePhase.txt @@ -185,8 +185,8 @@ FILE: [ResolvedTo(IMPORTS)] anonymousFunctionWithAnnotatedParameterOnImplicitTyp public [ResolvedTo(STATUS)] [ContainingClassKey=Anno] get(): R|kotlin/String| } - public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(message = String(str)) kotlin/String?| - public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(message = String(str)) kotlin/String?| { + public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(String(str)) kotlin/String?| + public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(String(str)) kotlin/String?| { ^ Null(null) } public final [ResolvedTo(IMPLICIT_TYPES_BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| @@ -209,8 +209,8 @@ FILE: [ResolvedTo(IMPORTS)] anonymousFunctionWithAnnotatedParameterOnImplicitTyp public [ResolvedTo(STATUS)] [ContainingClassKey=Anno] get(): R|kotlin/String| } - public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(message = String(str)) kotlin/String?| - public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(message = String(str)) kotlin/String?| { + public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(String(str)) kotlin/String?| + public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(String(str)) kotlin/String?| { ^ Null(null) } public final [ResolvedTo(ANNOTATIONS_ARGUMENTS_MAPPING)] val propertyToResolve: R|kotlin/String| @@ -233,8 +233,8 @@ FILE: [ResolvedTo(IMPORTS)] anonymousFunctionWithAnnotatedParameterOnImplicitTyp public [ResolvedTo(STATUS)] [ContainingClassKey=Anno] get(): R|kotlin/String| } - public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(message = String(str)) kotlin/String?| - public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(message = String(str)) kotlin/String?| { + public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(String(str)) kotlin/String?| + public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(String(str)) kotlin/String?| { ^ Null(null) } public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java index 7e256fba4e4..328af533d94 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java @@ -1159,6 +1159,18 @@ public class OutOfContentRootGetOrBuildFirTestGenerated extends AbstractOutOfCon runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/primaryConstructorValueParameter.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.kt"); + } + + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction2.kt") + public void testPropagateFlexibleTypeToAnonymousFunction2() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.kt"); + } + @Test @TestMetadata("secondaryConstructorValueParameter.kt") public void testSecondaryConstructorValueParameter() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java index d341d16c269..8cf04279506 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java @@ -1159,6 +1159,18 @@ public class SourceGetOrBuildFirTestGenerated extends AbstractSourceGetOrBuildFi runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/primaryConstructorValueParameter.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.kt"); + } + + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction2.kt") + public void testPropagateFlexibleTypeToAnonymousFunction2() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.kt"); + } + @Test @TestMetadata("secondaryConstructorValueParameter.kt") public void testSecondaryConstructorValueParameter() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index e02bde46145..34bbd529a1e 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -20258,6 +20258,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/j+k/privatePropertyAndSetterMultiModule.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt"); + } + @Test @TestMetadata("protectedStaticSamePackage.kt") public void testProtectedStaticSamePackage() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 3533d26cb9c..4d02df81343 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -20258,6 +20258,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/j+k/privatePropertyAndSetterMultiModule.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt"); + } + @Test @TestMetadata("protectedStaticSamePackage.kt") public void testProtectedStaticSamePackage() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 5e3f413694a..ce6772baf3b 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -20258,6 +20258,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/j+k/privatePropertyAndSetterMultiModule.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt"); + } + @Test @TestMetadata("protectedStaticSamePackage.kt") public void testProtectedStaticSamePackage() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index 351d2c2f9dd..695e05c065d 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -20264,6 +20264,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/j+k/privatePropertyAndSetterMultiModule.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt"); + } + @Test @TestMetadata("protectedStaticSamePackage.kt") public void testProtectedStaticSamePackage() throws Exception { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt index 632cfdc7db2..567f7478bac 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt @@ -100,6 +100,73 @@ fun ConeDefinitelyNotNullType.Companion.create( fun ConeDynamicType.Companion.create(session: FirSession): ConeDynamicType = ConeDynamicType(session.builtinTypes.nothingType.type, session.builtinTypes.nullableAnyType.type) +/** + * This call is required if you want to use a type with annotations that are linked to a declaration from this declaration inside another + * to avoid concurrent modification problems (see KT-60387). + * + * Otherwise, those annotations can be transformed from different threads simultaneously that is error-prone. + * Example: + * ```kotlin + * val declaration: @Anno("outer") Int? get() = null + * val anotherDeclaration: String get() = declaration?.let { "$it" }.toString() + * ``` + * here for `declaration?.let { "$it" }` will be created an anonymous function with argument that + * will have the same type as the return type of `declaration`. + * So, now we have 2 possible scenarios: + * + * Wrong case 1 – The type was inserted to a new type ref as it is. + * This means that a type reference from `declaration` and from the anonymous lambda will have the same instance of `Anno`. + * Now we resole `anotherDeclaration` to [FirResolvePhase.BODY_RESOLVE] phase + * and as a result we will have resolved annotation arguments in the type. + * At the same time, `declaration` can be still in [FirResolvePhase.CONTRACTS] phase, because no one called a resolution yet. + * So now imagine a situation when one thread (1) wants to read annotation arguments from the fully resolved anonymous function, + * and another thread (2) wants to resolve `declaration` to [FirResolvePhase.ANNOTATIONS_ARGUMENTS_MAPPING] phase. + * As a result, we will have a moment there thread 2 will replace arguments with a lazy expression + * to be sure that we will have a safe basis for resolution (see StateKeeper concept in LL FIR). + * And thread 1 will see unexpected unresolved annotation arguments in fully resolved function, because those type references + * use the same instance of the type (`FirArgumentListImpl` cannot be cast to `FirResolvedArgumentList`). + * So we lost here. + * + * Right case 2 – The type was "copied" by [independentInstance]. + * In this case, `declaration` and `anotherDeclaration` will have different instances of one `@Anno("outer") Int?` type. + * This means that we can't come to a situation where we can modify a fully resolved annotation from another thread. + * + * @return an instance of a type that has no annotations associated with any declaration, + * so it won't be changed from LL FIR lazy transformers concurrently + * + * @see CustomAnnotationTypeAttribute.independentInstance + */ +fun ConeKotlinType.independentInstance(): ConeKotlinType = instanceWithIndependentArguments().instanceWithIndependentAnnotations() + +private fun ConeKotlinType.instanceWithIndependentArguments(): ConeKotlinType { + val typeProjections = typeArguments + if (typeProjections.isEmpty()) return this + + var argumentsChanged = false + val newArguments = type.typeArguments.map { originalArgument -> + if (originalArgument !is ConeKotlinType) + originalArgument + else + originalArgument.independentInstance().also { + if (it !== originalArgument) { + argumentsChanged = true + } + } + } + + return if (argumentsChanged) withArguments(newArguments.toTypedArray()) else this +} + +private fun ConeKotlinType.instanceWithIndependentAnnotations(): ConeKotlinType { + val custom = attributes.custom ?: return this + val newAnnotations = custom.independentInstance() + if (newAnnotations === custom) { + return this + } + + val newAttributes = attributes.remove(custom).plus(newAnnotations) + return withAttributes(newAttributes) +} fun ConeKotlinType.makeConeTypeDefinitelyNotNullOrNotNull( typeContext: ConeTypeContext, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index afe5c433e95..a23dd5e2b0b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -481,9 +481,8 @@ fun FirCheckedSafeCallSubject.propagateTypeFromOriginalReceiver( .coneTypeSafe() ?: return val expandedReceiverType = receiverType.fullyExpandedType(session) - - val resolvedTypeRef = - typeRef.resolvedTypeFromPrototype(expandedReceiverType.makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext)) + val updatedReceiverType = expandedReceiverType.makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext).independentInstance() + val resolvedTypeRef = typeRef.resolvedTypeFromPrototype(updatedReceiverType) replaceTypeRef(resolvedTypeRef) session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, source, file.source) } @@ -506,7 +505,7 @@ fun FirSafeCallExpression.propagateTypeFromQualifiedAccessAfterNullCheck( } } - val resolvedTypeRef = typeRef.resolvedTypeFromPrototype(resultingType) + val resolvedTypeRef = typeRef.resolvedTypeFromPrototype(resultingType.independentInstance()) replaceTypeRef(resolvedTypeRef) session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, source, file.source) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt index f4b23607f19..df6b3a7c173 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt @@ -118,7 +118,7 @@ class PostponedArgumentsAnalyzer( val stubsForPostponedVariables = c.bindingStubsForPostponedVariables() val currentSubstitutor = c.buildCurrentSubstitutor(stubsForPostponedVariables.mapKeys { it.key.freshTypeConstructor(c) }) - fun substitute(type: ConeKotlinType) = currentSubstitutor.safeSubstitute(c, type) as ConeKotlinType + fun substitute(type: ConeKotlinType) = (currentSubstitutor.safeSubstitute(c, type) as ConeKotlinType).independentInstance() val receiver = lambda.receiver?.let(::substitute) val contextReceivers = lambda.contextReceivers.map(::substitute) diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/CustomAnnotationTypeAttribute.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/CustomAnnotationTypeAttribute.kt index 43eb2bc9159..d81dd59db68 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/CustomAnnotationTypeAttribute.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/CustomAnnotationTypeAttribute.kt @@ -6,10 +6,33 @@ package org.jetbrains.kotlin.fir.types import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCallCopy +import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCopy import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import kotlin.reflect.KClass +/** + * @param containerSymbols a list of symbols that should be resolved to make [annotations] are fully resolved. + * Required only for "lazy" resolve mode in AA FIR to make a type annotation lazily resolved. + * See KtFirAnnotationListForType for reference. + * Example: + * ```kotlin + * fun foo(): @Anno Type + * ``` + * This `Anno` annotation will have `foo` function as [containerSymbols]. + * More than one [containerSymbols] possible in case of type aliases: + * ```kotlin + * interface BaseInterface + * typealias FirstTypeAlias = @Anno1 BaseInterface + * typealias SecondTypeAlias = @Anno2 FirstTypeAlias + * + * fun foo(): @Anno3 SecondTypeAlias = TODO() + * ``` + * here `@Anno3 SecondTypeAlias` will be expanded to ` @Anno1 @Anno2 @Anno3 BaseInterface` + * and will have all intermediate type-aliases as [containerSymbols]. + */ class CustomAnnotationTypeAttribute( val annotations: List, val containerSymbols: List> = emptyList(), @@ -34,6 +57,27 @@ class CustomAnnotationTypeAttribute( override val key: KClass get() = CustomAnnotationTypeAttribute::class + + /** + * Return an instance of the attribute that is not linked to any [containerSymbols]. + * It is required to avoid concurrent modification of those annotations from the linked + * declaration and another call site (e.g., if a type was propagated to an anonymous function). + * + * See KT-60387 as an example of a possible concurrent problem. + */ + fun independentInstance(): CustomAnnotationTypeAttribute = if (containerSymbols.isEmpty()) { + this + } else { + CustomAnnotationTypeAttribute( + annotations = annotations.map { + if (it is FirAnnotationCall) { + buildAnnotationCallCopy(it) {} + } else { + buildAnnotationCopy(it) {} + } + } + ) + } } val ConeAttributes.custom: CustomAnnotationTypeAttribute? by ConeAttributes.attributeAccessor() diff --git a/compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt b/compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt new file mode 100644 index 00000000000..0d46abf3375 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt @@ -0,0 +1,22 @@ +// FIR_IDENTICAL +// FILE: usage.kt +val propertyToResolve: String + get() = JavaClass.function()?.let { " ($it)" } ?: "" + +// FILE: Anno.java +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE_USE) +public @interface Anno { + String value(); +} + +// FILE: JavaClass.java +import java.util.List; + +public class JavaClass { + public static @Anno("outer") List<@Anno("middle") List<@Anno("inner") Integer>> function() { + return null; + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 42175e8ec51..82f83fd1262 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -20264,6 +20264,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/j+k/privatePropertyAndSetterMultiModule.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt"); + } + @Test @TestMetadata("protectedStaticSamePackage.kt") public void testProtectedStaticSamePackage() throws Exception {