From b07729339628f3b54f7479ec119cb1a9d7743501 Mon Sep 17 00:00:00 2001 From: Alejandro Serrano Mena Date: Wed, 13 Dec 2023 12:22:26 +0100 Subject: [PATCH] [FIR] Allow, but also warn, KMutableProperty with captured types ^KT-63589 Fixed --- .../diagnostics/KtFirDataClassConverters.kt | 6 +++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 4 ++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 5 +++ ...cCompilerTestFirTestdataTestGenerated.java | 12 ++++++ ...osticCompilerFirTestDataTestGenerated.java | 12 ++++++ ...ePropertyGenericButNotCapturedType.fir.txt | 34 +++++++++++++++++ ...utablePropertyGenericButNotCapturedType.kt | 24 ++++++++++++ .../mutablePropertyWithCapturedType.fir.txt | 21 ++++++++++ .../mutablePropertyWithCapturedType.kt | 9 +++++ .../FirLightTreeDiagnosticsTestGenerated.java | 12 ++++++ .../FirPsiDiagnosticTestGenerated.java | 12 ++++++ .../diagnostics/FirDiagnosticsList.kt | 1 + .../fir/analysis/diagnostics/FirErrors.kt | 1 + .../expression/FirCallableReferenceChecker.kt | 38 ++++++++++++++----- .../diagnostics/FirErrorsDefaultMessages.kt | 2 + .../kotlin/fir/types/ConeTypeUtils.kt | 2 + .../calls/CallableReferenceResolution.kt | 29 +++++++------- 17 files changed, 200 insertions(+), 24 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 02b28f85fb5..3a973296ccd 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -2662,6 +2662,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.MUTABLE_PROPERTY_WITH_CAPTURED_TYPE) { firDiagnostic -> + MutablePropertyWithCapturedTypeImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.NOTHING_TO_OVERRIDE) { firDiagnostic -> NothingToOverrideImpl( firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a), diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 57e91d7c1b4..0be47a7c7e2 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -1885,6 +1885,10 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = UnsupportedClassLiteralsWithEmptyLhs::class } + interface MutablePropertyWithCapturedType : KtFirDiagnostic { + override val diagnosticClass get() = MutablePropertyWithCapturedType::class + } + interface NothingToOverride : KtFirDiagnostic { override val diagnosticClass get() = NothingToOverride::class val declaration: KtCallableSymbol diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 8d6403bfcd3..1da6b1d3381 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -2266,6 +2266,11 @@ internal class UnsupportedClassLiteralsWithEmptyLhsImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.UnsupportedClassLiteralsWithEmptyLhs +internal class MutablePropertyWithCapturedTypeImpl( + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.MutablePropertyWithCapturedType + internal class NothingToOverrideImpl( override val declaration: KtCallableSymbol, firDiagnostic: KtPsiDiagnostic, diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java index 70844ec04eb..cc2799a7711 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java @@ -4177,6 +4177,18 @@ public class DiagnosticCompilerTestFirTestdataTestGenerated extends AbstractDiag runTest("compiler/fir/analysis-tests/testData/resolve/references/leakedImplicitType.kt"); } + @Test + @TestMetadata("mutablePropertyGenericButNotCapturedType.kt") + public void testMutablePropertyGenericButNotCapturedType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.kt"); + } + + @Test + @TestMetadata("mutablePropertyWithCapturedType.kt") + public void testMutablePropertyWithCapturedType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.kt"); + } + @Test @TestMetadata("referenceToExtension.kt") public void testReferenceToExtension() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java index a916519201e..94aecb7e227 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java @@ -4177,6 +4177,18 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated runTest("compiler/fir/analysis-tests/testData/resolve/references/leakedImplicitType.kt"); } + @Test + @TestMetadata("mutablePropertyGenericButNotCapturedType.kt") + public void testMutablePropertyGenericButNotCapturedType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.kt"); + } + + @Test + @TestMetadata("mutablePropertyWithCapturedType.kt") + public void testMutablePropertyWithCapturedType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.kt"); + } + @Test @TestMetadata("referenceToExtension.kt") public void testReferenceToExtension() throws Exception { diff --git a/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.fir.txt b/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.fir.txt new file mode 100644 index 00000000000..17c9d6e2b61 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.fir.txt @@ -0,0 +1,34 @@ +FILE: mutablePropertyGenericButNotCapturedType.kt + public final class Generic : R|kotlin/Any| { + public constructor(): R|Generic| { + super() + } + + } + public final class Klass : R|kotlin/Any| { + public constructor(): R|Klass| { + super() + } + + public final var mutableProperty: R|Generic| = R|/Generic.Generic|() + public get(): R|Generic| + public set(value: R|Generic|): R|kotlin/Unit| + + public final fun testWithinClass(): R|kotlin/Unit| { + lval mutableProperty: R|kotlin/reflect/KMutableProperty1, Generic>| = Q|Klass|::R|/Klass.mutableProperty| + R|/mutableProperty|.R|SubstitutionOverride|(this@R|/Klass|, R|/Generic.Generic|()) + } + + } + public final fun testConcreteType(): R|kotlin/Unit| { + lval mutableProperty: R|kotlin/reflect/KMutableProperty1, Generic>| = Q|Klass|::R|SubstitutionOverride|>| + R|/mutableProperty|.R|SubstitutionOverride|(R|/Klass.Klass|(), R|/Generic.Generic|()) + } + public final fun testGenericType(): R|kotlin/Unit| { + lval mutableProperty: R|kotlin/reflect/KMutableProperty1, Generic>| = Q|Klass|::R|SubstitutionOverride|>| + R|/mutableProperty|.R|SubstitutionOverride|(R|/Klass.Klass|(), R|/Generic.Generic|()) + } + public final fun testGenericTypeWithBounds(): R|kotlin/Unit| { + lval mutableProperty: R|kotlin/reflect/KMutableProperty1, Generic>| = Q|Klass|::R|SubstitutionOverride|>| + R|/mutableProperty|.R|SubstitutionOverride|(R|/Klass.Klass|(), R|/Generic.Generic|()) + } diff --git a/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.kt b/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.kt new file mode 100644 index 00000000000..64493d99770 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.kt @@ -0,0 +1,24 @@ +class Generic +class Klass { + var mutableProperty: Generic = Generic() + + fun testWithinClass() { + val mutableProperty = Klass::mutableProperty + mutableProperty.set(this, Generic()) + } +} + +fun testConcreteType() { + val mutableProperty = Klass::mutableProperty + mutableProperty.set(Klass(), Generic()) +} + +fun testGenericType() { + val mutableProperty = Klass::mutableProperty + mutableProperty.set(Klass(), Generic()) +} + +fun testGenericTypeWithBounds() { + val mutableProperty = Klass::mutableProperty + mutableProperty.set(Klass(), Generic()) +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.fir.txt b/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.fir.txt new file mode 100644 index 00000000000..c98e48c54a1 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.fir.txt @@ -0,0 +1,21 @@ +FILE: mutablePropertyWithCapturedType.kt + public final class Generic : R|kotlin/Any| { + public constructor(): R|Generic| { + super() + } + + } + public final class Klass : R|kotlin/Any| { + public constructor(): R|Klass| { + super() + } + + public final var mutableProperty: R|Generic| = R|/Generic.Generic|() + public get(): R|Generic| + public set(value: R|Generic|): R|kotlin/Unit| + + } + public final fun test(): R|kotlin/Unit| { + lval mutableProperty: R|kotlin/reflect/KMutableProperty1, Generic>| = Q|Klass|::R|SubstitutionOverride|>| + R|/mutableProperty|.R|SubstitutionOverride|(R|/Klass.Klass|(), R|/Generic.Generic|()) + } diff --git a/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.kt b/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.kt new file mode 100644 index 00000000000..619770df802 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.kt @@ -0,0 +1,9 @@ +class Generic +class Klass { + var mutableProperty: Generic = Generic() +} + +fun test() { + val mutableProperty = Klass<*>::mutableProperty + mutableProperty.set(Klass(), Generic()) +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java index a5316569d76..468abb6492e 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java @@ -4177,6 +4177,18 @@ public class FirLightTreeDiagnosticsTestGenerated extends AbstractFirLightTreeDi runTest("compiler/fir/analysis-tests/testData/resolve/references/leakedImplicitType.kt"); } + @Test + @TestMetadata("mutablePropertyGenericButNotCapturedType.kt") + public void testMutablePropertyGenericButNotCapturedType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.kt"); + } + + @Test + @TestMetadata("mutablePropertyWithCapturedType.kt") + public void testMutablePropertyWithCapturedType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.kt"); + } + @Test @TestMetadata("referenceToExtension.kt") public void testReferenceToExtension() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java index 827dbc7b549..e253cc12fe8 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java @@ -4177,6 +4177,18 @@ public class FirPsiDiagnosticTestGenerated extends AbstractFirPsiDiagnosticTest runTest("compiler/fir/analysis-tests/testData/resolve/references/leakedImplicitType.kt"); } + @Test + @TestMetadata("mutablePropertyGenericButNotCapturedType.kt") + public void testMutablePropertyGenericButNotCapturedType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyGenericButNotCapturedType.kt"); + } + + @Test + @TestMetadata("mutablePropertyWithCapturedType.kt") + public void testMutablePropertyWithCapturedType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/references/mutablePropertyWithCapturedType.kt"); + } + @Test @TestMetadata("referenceToExtension.kt") public void testReferenceToExtension() throws Exception { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index f556b3f07af..c66bcb99825 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -892,6 +892,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("lhsType") } val UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS by error() + val MUTABLE_PROPERTY_WITH_CAPTURED_TYPE by warning() } val OVERRIDES by object : DiagnosticGroup("overrides") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index cdeaf2df33d..08888bfbdcd 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -507,6 +507,7 @@ object FirErrors { val NULLABLE_TYPE_IN_CLASS_LITERAL_LHS: KtDiagnosticFactory0 by error0() val EXPRESSION_OF_NULLABLE_TYPE_IN_CLASS_LITERAL_LHS: KtDiagnosticFactory1 by error1() val UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS: KtDiagnosticFactory0 by error0() + val MUTABLE_PROPERTY_WITH_CAPTURED_TYPE: KtDiagnosticFactory0 by warning0() // overrides val NOTHING_TO_OVERRIDE: KtDiagnosticFactory1> by error1>(SourceElementPositioningStrategies.OVERRIDE_MODIFIER) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt index 655d44eaa2c..4d83a0fcbbb 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.fir.analysis.checkers.classKind import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext @@ -18,29 +19,32 @@ import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.references.resolved +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol +import org.jetbrains.kotlin.fir.types.* object FirCallableReferenceChecker : FirQualifiedAccessExpressionChecker() { override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { if (expression !is FirCallableReferenceAccess) return - checkReferenceIsToAllowedMember(expression, context, reporter) + // UNRESOLVED_REFERENCE will be reported separately. + val reference = expression.calleeReference.resolved ?: return + val referredSymbol = reference.resolvedSymbol + val source = reference.source ?: return + if (source.kind is KtFakeSourceElementKind) return + + checkReferenceIsToAllowedMember(referredSymbol, source, context, reporter) + checkCapturedTypeInMutableReference(expression, referredSymbol, source, context, reporter) } // See FE 1.0 [DoubleColonExpressionResolver#checkReferenceIsToAllowedMember] private fun checkReferenceIsToAllowedMember( - callableReferenceAccess: FirCallableReferenceAccess, + referredSymbol: FirBasedSymbol<*>, + source: KtSourceElement, context: CheckerContext, reporter: DiagnosticReporter ) { - // UNRESOLVED_REFERENCE will be reported separately. - val reference = callableReferenceAccess.calleeReference.resolved ?: return - - val source = reference.source ?: return - if (source.kind is KtFakeSourceElementKind) return - - val referredSymbol = reference.resolvedSymbol if (referredSymbol is FirConstructorSymbol && referredSymbol.getContainingClassSymbol(context.session)?.classKind == ClassKind.ANNOTATION_CLASS) { reporter.reportOn(source, FirErrors.CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR, context) } @@ -50,4 +54,20 @@ object FirCallableReferenceChecker : FirQualifiedAccessExpressionChecker() { reporter.reportOn(source, FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED, referredSymbol, context) } } + + private fun checkCapturedTypeInMutableReference( + callableReferenceAccess: FirCallableReferenceAccess, + referredSymbol: FirBasedSymbol<*>, + source: KtSourceElement, + context: CheckerContext, + reporter: DiagnosticReporter + ) { + if (!callableReferenceAccess.resolvedType.isKMutableProperty(context.session)) return + if (referredSymbol !is FirCallableSymbol<*>) return + + val returnType = context.returnTypeCalculator.tryCalculateReturnType(referredSymbol) + if (returnType.type.hasCapture()) { + reporter.reportOn(source, FirErrors.MUTABLE_PROPERTY_WITH_CAPTURED_TYPE, context) + } + } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index d840b0e8a1d..52386892d8d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -403,6 +403,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MUST_BE_INITIALIZ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MUST_BE_INITIALIZED_OR_FINAL_OR_ABSTRACT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MUST_BE_INITIALIZED_OR_FINAL_OR_ABSTRACT_WARNING import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MUST_BE_INITIALIZED_WARNING +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MUTABLE_PROPERTY_WITH_CAPTURED_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NAMED_ARGUMENTS_NOT_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NAMED_PARAMETER_NOT_FOUND import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NAME_FOR_AMBIGUOUS_PARAMETER @@ -1515,6 +1516,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { RENDER_TYPE ) map.put(UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS, "Class literals with empty left hand side are unsupported.") + map.put(MUTABLE_PROPERTY_WITH_CAPTURED_TYPE, "Captured type in mutable property reference. Usages of 'set' may lead to cast exceptions.") // Value classes map.put(VALUE_CLASS_NOT_TOP_LEVEL, "Value class cannot be local or inner.") diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt index 2fd3a13897b..9aaa590fd32 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt @@ -137,3 +137,5 @@ fun ConeKotlinType.renderReadableWithFqNames(): String { } fun ConeKotlinType.hasError(): Boolean = contains { it is ConeErrorType } + +fun ConeKotlinType.hasCapture(): Boolean = contains { it is ConeCapturedType } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallableReferenceResolution.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallableReferenceResolution.kt index b47aded151a..7bcfbaa843b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallableReferenceResolution.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallableReferenceResolution.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation import org.jetbrains.kotlin.resolve.calls.inference.runTransaction import org.jetbrains.kotlin.resolve.calls.tower.isSuccess import org.jetbrains.kotlin.types.AbstractTypeChecker +import org.jetbrains.kotlin.types.TypeApproximatorConfiguration import org.jetbrains.kotlin.types.expressions.CoercionStrategy import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment @@ -175,7 +176,19 @@ private fun buildResultingTypeAndAdaptation( contextReceivers = fir.contextReceivers.map { it.typeRef.coneType } ) to callableReferenceAdaptation } - is FirVariable -> createKPropertyType(fir, receiverType, returnTypeRef, candidate) to null + is FirVariable -> { + val returnType = returnTypeRef.type + val isMutable = fir.canBeMutableReference(candidate) + val propertyType = when { + isMutable && returnType.hasCapture() -> + // capturing types in mutable property references is unsound in general + context.inferenceComponents.resultTypeResolver.typeApproximator + .approximateToSuperType(returnType, TypeApproximatorConfiguration.InternalTypesApproximation) as? ConeKotlinType + ?: returnType + else -> returnType + } + createKPropertyType(receiverType, propertyType, isMutable) to null + } else -> ConeErrorType(ConeUnsupportedCallableReferenceTarget(candidate)) to null } } @@ -482,20 +495,6 @@ fun ConeKotlinType.isKCallableType(): Boolean { return this.classId == StandardClassIds.KCallable } -private fun createKPropertyType( - propertyOrField: FirVariable, - receiverType: ConeKotlinType?, - returnTypeRef: FirResolvedTypeRef, - candidate: Candidate, -): ConeKotlinType { - val propertyType = returnTypeRef.type - return org.jetbrains.kotlin.fir.resolve.createKPropertyType( - receiverType, - propertyType, - isMutable = propertyOrField.canBeMutableReference(candidate) - ) -} - private fun FirVariable.canBeMutableReference(candidate: Candidate): Boolean { if (!isVar) return false if (this is FirField) return true