From 6f17a8713c9fb37643c8dc6e5639823aa938de56 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 20 Apr 2022 12:41:14 +0300 Subject: [PATCH] Fix KT-47708 in FIR by transferring SAM annotations to synthetic constr. --- .../fir/components/KtFirTypeInfoProvider.kt | 3 +- ...CompilerTestFE10TestdataTestGenerated.java | 6 +++ ...irOldFrontendDiagnosticsTestGenerated.java | 6 +++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 +++ .../backend/generators/AdapterGenerator.kt | 2 +- .../kotlin/fir/resolve/SamResolution.kt | 42 ++++++++++++------- .../calls/AbstractConeCallConflictResolver.kt | 2 +- .../kotlin/fir/resolve/calls/Arguments.kt | 2 +- ...rCallCompletionResultsWriterTransformer.kt | 23 +++++----- .../experimental/insideSAM.fir.kt | 21 ++++++++++ .../experimental/insideSAM.fir.txt | 33 +++++++++++++++ .../testsWithStdLib/experimental/insideSAM.kt | 21 ++++++++++ .../experimental/insideSAM.txt | 19 +++++++++ .../test/runners/DiagnosticTestGenerated.java | 6 +++ 14 files changed, 160 insertions(+), 32 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.fir.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.txt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirTypeInfoProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirTypeInfoProvider.kt index d20cb8f5636..3a18bbf16d5 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirTypeInfoProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirTypeInfoProvider.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken import org.jetbrains.kotlin.analysis.api.types.KtType import org.jetbrains.kotlin.builtins.functions.FunctionClassKind import org.jetbrains.kotlin.fir.resolve.FirSamResolverImpl -import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.types.canBeNull import org.jetbrains.kotlin.fir.types.functionClassKind import org.jetbrains.kotlin.fir.types.typeApproximator @@ -30,7 +29,7 @@ internal class KtFirTypeInfoProvider( firSession, analysisSession.getScopeSessionFor(firSession), ) - return samResolver.getFunctionTypeForPossibleSamType(coneType) != null + return samResolver.getSamInfoForPossibleSamType(coneType) != null } override fun getFunctionClassKind(type: KtType): FunctionClassKind? { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 02d19bbc90e..4bc40261f4c 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -37192,6 +37192,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt"); } + @Test + @TestMetadata("insideSAM.kt") + public void testInsideSAM() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.kt"); + } + @Test @TestMetadata("noRetentionAfter.kt") public void testNoRetentionAfter() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 7d63d4aa2f5..fbcfb219f8e 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -37192,6 +37192,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt"); } + @Test + @TestMetadata("insideSAM.kt") + public void testInsideSAM() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.kt"); + } + @Test @TestMetadata("noRetentionAfter.kt") public void testNoRetentionAfter() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 782a343f65c..dc6b86a684a 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -37192,6 +37192,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt"); } + @Test + @TestMetadata("insideSAM.kt") + public void testInsideSAM() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.kt"); + } + @Test @TestMetadata("noRetentionAfter.kt") public void testNoRetentionAfter() throws Exception { diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt index 4f27ededd89..7c19df91eb1 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt @@ -485,7 +485,7 @@ internal class AdapterGenerator( } internal fun getFunctionTypeForPossibleSamType(parameterType: ConeKotlinType): ConeKotlinType? { - return samResolver.getFunctionTypeForPossibleSamType(parameterType) + return samResolver.getSamInfoForPossibleSamType(parameterType)?.type } /** diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SamResolution.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SamResolution.kt index 715d6b4d004..2ffead03632 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SamResolution.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SamResolution.kt @@ -39,28 +39,37 @@ import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.types.Variance abstract class FirSamResolver { - abstract fun getFunctionTypeForPossibleSamType(type: ConeKotlinType): ConeKotlinType? + abstract fun getSamInfoForPossibleSamType(type: ConeKotlinType): SAMInfo? abstract fun shouldRunSamConversionForFunction(firFunction: FirFunction): Boolean abstract fun getSamConstructor(firRegularClass: FirRegularClass): FirSimpleFunction? + + fun getFunctionTypeForPossibleSamType(type: ConeKotlinType): ConeKotlinType? = + getSamInfoForPossibleSamType(type)?.type } private val SAM_PARAMETER_NAME = Name.identifier("function") +data class SAMInfo(internal val symbol: FirNamedFunctionSymbol, val type: C) + class FirSamResolverImpl( private val session: FirSession, private val scopeSession: ScopeSession, private val outerClassManager: FirOuterClassManager? = null, ) : FirSamResolver() { - private val resolvedFunctionType: NullableMap = NullableMap() + private val resolvedFunctionType: NullableMap?> = NullableMap() private val samConstructorsCache = session.samConstructorStorage.samConstructors - override fun getFunctionTypeForPossibleSamType(type: ConeKotlinType): ConeKotlinType? { + override fun getSamInfoForPossibleSamType(type: ConeKotlinType): SAMInfo? { return when (type) { is ConeClassLikeType -> getFunctionTypeForPossibleSamType(type.fullyExpandedType(session)) - is ConeFlexibleType -> ConeFlexibleType( - getFunctionTypeForPossibleSamType(type.lowerBound)?.lowerBoundIfFlexible() ?: return null, - getFunctionTypeForPossibleSamType(type.upperBound)?.upperBoundIfFlexible() ?: return null, - ) + is ConeFlexibleType -> { + val (lowerSymbol, lowerType) = getSamInfoForPossibleSamType(type.lowerBound) ?: return null + val (_, upperType) = getSamInfoForPossibleSamType(type.upperBound) ?: return null + SAMInfo( + lowerSymbol, + ConeFlexibleType(lowerType.lowerBoundIfFlexible(), upperType.upperBoundIfFlexible()) + ) + } is ConeErrorType, is ConeStubType -> null // TODO: support those types as well is ConeTypeParameterType, is ConeTypeVariableType, @@ -72,14 +81,17 @@ class FirSamResolverImpl( } } - private fun getFunctionTypeForPossibleSamType(type: ConeClassLikeType): ConeLookupTagBasedType? { + private fun getFunctionTypeForPossibleSamType(type: ConeClassLikeType): SAMInfo? { @OptIn(LookupTagInternals::class) val firRegularClass = type.lookupTag.toFirRegularClass(session) ?: return null - val unsubstitutedFunctionType = resolveFunctionTypeIfSamInterface(firRegularClass) ?: return null + val (functionSymbol, unsubstitutedFunctionType) = resolveFunctionTypeIfSamInterface(firRegularClass) ?: return null if (firRegularClass.typeParameters.isEmpty()) { - return unsubstitutedFunctionType.withNullability(ConeNullability.create(type.isMarkedNullable), session.typeContext) + return SAMInfo( + functionSymbol, + unsubstitutedFunctionType.withNullability(ConeNullability.create(type.isMarkedNullable), session.typeContext) + ) } val substitutor = @@ -110,7 +122,7 @@ class FirSamResolverImpl( "Function type should always be ConeLookupTagBasedType, but ${result::class} was found" } - return result + return SAMInfo(functionSymbol, result) } override fun getSamConstructor(firRegularClass: FirRegularClass): FirSimpleFunction? { @@ -119,7 +131,7 @@ class FirSamResolverImpl( fun buildSamConstructor(classSymbol: FirRegularClassSymbol): FirNamedFunctionSymbol? { val firRegularClass = classSymbol.fir - val functionType = resolveFunctionTypeIfSamInterface(firRegularClass) ?: return null + val (functionSymbol, functionType) = resolveFunctionTypeIfSamInterface(firRegularClass) ?: return null val classId = firRegularClass.classId val symbol = FirSyntheticFunctionSymbol( @@ -217,19 +229,21 @@ class FirSamResolverImpl( resolvePhase = FirResolvePhase.BODY_RESOLVE } + annotations += functionSymbol.annotations + resolvePhase = FirResolvePhase.BODY_RESOLVE }.apply { containingClassForStaticMemberAttr = outerClassManager?.outerClass(firRegularClass.symbol)?.toLookupTag() }.symbol } - private fun resolveFunctionTypeIfSamInterface(firRegularClass: FirRegularClass): ConeLookupTagBasedType? { + private fun resolveFunctionTypeIfSamInterface(firRegularClass: FirRegularClass): SAMInfo? { return resolvedFunctionType.getOrPut(firRegularClass) { if (!firRegularClass.status.isFun) return@getOrPut null val abstractMethod = firRegularClass.getSingleAbstractMethodOrNull(session, scopeSession) ?: return@getOrPut null // TODO: val shouldConvertFirstParameterToDescriptor = samWithReceiverResolvers.any { it.shouldConvertFirstSamParameterToReceiver(abstractMethod) } - abstractMethod.getFunctionTypeForAbstractMethod() + SAMInfo(abstractMethod.symbol, abstractMethod.getFunctionTypeForAbstractMethod()) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/AbstractConeCallConflictResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/AbstractConeCallConflictResolver.kt index cad175c4da3..cee709b79c5 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/AbstractConeCallConflictResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/AbstractConeCallConflictResolver.kt @@ -188,7 +188,7 @@ abstract class AbstractConeCallConflictResolver( if (!call.usesSAM) { TypeWithConversion(argumentType) } else { - val functionType = samResolver.getFunctionTypeForPossibleSamType(argumentType)?.second + val functionType = samResolver.getSamInfoForPossibleSamType(argumentType)?.type if (functionType == null) TypeWithConversion(argumentType) else TypeWithConversion(functionType, argumentType) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index ed990b7f62e..d99f991c39c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -506,7 +506,7 @@ private fun Candidate.getExpectedTypeWithSAMConversion( // TODO: resolvedCall.registerArgumentWithSamConversion(argument, SamConversionDescription(convertedTypeByOriginal, convertedTypeByCandidate!!)) - val expectedFunctionType = context.bodyResolveComponents.samResolver.getFunctionTypeForPossibleSamType(candidateExpectedType) + val (_, expectedFunctionType) = context.bodyResolveComponents.samResolver.getSamInfoForPossibleSamType(candidateExpectedType) ?: return null return runIf(argument.isFunctional(session, scopeSession, expectedFunctionType)) { usesSAM = true diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt index 54fabddf207..c6f67176d44 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt @@ -589,19 +589,16 @@ class FirCallCompletionResultsWriterTransformer( val firRegularClass = session.symbolProvider.getClassLikeSymbolByClassId(expectedArgumentType.lookupTag.classId)?.fir as? FirRegularClass - firRegularClass?.let { - val functionType = samResolver.getFunctionTypeForPossibleSamType(firRegularClass.defaultType()) - if (functionType != null) { - createFunctionalType( - functionType.typeArguments.dropLast(1).map { it as ConeKotlinType }, - null, - functionType.typeArguments.last() as ConeKotlinType, - functionType.classId?.relativeClassName?.asString() - ?.startsWith(FunctionClassKind.SuspendFunction.classNamePrefix) == true - ) - } else { - null - } + firRegularClass?.let answer@{ + val (_, functionType) = samResolver.getSamInfoForPossibleSamType(firRegularClass.defaultType()) + ?: return@answer null + createFunctionalType( + functionType.typeArguments.dropLast(1).map { it as ConeKotlinType }, + null, + functionType.typeArguments.last() as ConeKotlinType, + functionType.classId?.relativeClassName?.asString() + ?.startsWith(FunctionClassKind.SuspendFunction.classNamePrefix) == true + ) } } else -> null diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.fir.kt new file mode 100644 index 00000000000..8657f69d522 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.fir.kt @@ -0,0 +1,21 @@ +// FIR_DUMP +@RequiresOptIn +annotation class ExperimentalKotlinAnnotation + +internal fun interface StableInterface { + @ExperimentalKotlinAnnotation // @ExperimentalStdlibApi + fun experimentalMethod() +} + +fun regressionTestOverrides() { + val anonymous: StableInterface = object : StableInterface { + override fun experimentalMethod() {} // correctly fails check + } + val lambda = StableInterface {} // this does not get flagged +} + +@ExperimentalKotlinAnnotation +fun suppressed() { + val lambda = StableInterface {} +} + diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.fir.txt b/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.fir.txt new file mode 100644 index 00000000000..18c128ef190 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.fir.txt @@ -0,0 +1,33 @@ +FILE: insideSAM.fir.kt + @R|kotlin/RequiresOptIn|() public final annotation class ExperimentalKotlinAnnotation : R|kotlin/Annotation| { + public constructor(): R|ExperimentalKotlinAnnotation| { + super() + } + + } + internal abstract fun interface StableInterface : R|kotlin/Any| { + @R|ExperimentalKotlinAnnotation|() public abstract fun experimentalMethod(): R|kotlin/Unit| + + } + public final fun regressionTestOverrides(): R|kotlin/Unit| { + lval anonymous: R|StableInterface| = object : R|StableInterface| { + private constructor(): R|| { + super() + } + + public final override fun experimentalMethod(): R|kotlin/Unit| { + } + + } + + lval lambda: R|StableInterface| = R|/StableInterface|( = StableInterface@fun (): R|kotlin/Unit| { + ^@StableInterface Unit + } + ) + } + @R|ExperimentalKotlinAnnotation|() public final fun suppressed(): R|kotlin/Unit| { + lval lambda: R|StableInterface| = R|/StableInterface|( = StableInterface@fun (): R|kotlin/Unit| { + ^@StableInterface Unit + } + ) + } diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.kt new file mode 100644 index 00000000000..1e35fc476ce --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.kt @@ -0,0 +1,21 @@ +// FIR_DUMP +@RequiresOptIn +annotation class ExperimentalKotlinAnnotation + +internal fun interface StableInterface { + @ExperimentalKotlinAnnotation // @ExperimentalStdlibApi + fun experimentalMethod() +} + +fun regressionTestOverrides() { + val anonymous: StableInterface = object : StableInterface { + override fun experimentalMethod() {} // correctly fails check + } + val lambda = StableInterface {} // this does not get flagged +} + +@ExperimentalKotlinAnnotation +fun suppressed() { + val lambda = StableInterface {} +} + diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.txt b/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.txt new file mode 100644 index 00000000000..36633083239 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.txt @@ -0,0 +1,19 @@ +package + +public fun regressionTestOverrides(): kotlin.Unit +@ExperimentalKotlinAnnotation public fun suppressed(): kotlin.Unit + +@kotlin.RequiresOptIn public final annotation class ExperimentalKotlinAnnotation : kotlin.Annotation { + public constructor ExperimentalKotlinAnnotation() + 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 +} + +internal fun interface StableInterface { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @ExperimentalKotlinAnnotation public abstract fun experimentalMethod(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + 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 2c5dc653e71..03cbfd01f41 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 @@ -37282,6 +37282,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt"); } + @Test + @TestMetadata("insideSAM.kt") + public void testInsideSAM() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/insideSAM.kt"); + } + @Test @TestMetadata("noRetentionAfter.kt") public void testNoRetentionAfter() throws Exception {