Fix KT-47708 in FIR by transferring SAM annotations to synthetic constr.

This commit is contained in:
Mikhail Glukhikh
2022-04-20 12:41:14 +03:00
committed by Space
parent 9b6430d455
commit 6f17a8713c
14 changed files with 160 additions and 32 deletions
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
import org.jetbrains.kotlin.analysis.api.types.KtType import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.fir.resolve.FirSamResolverImpl 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.canBeNull
import org.jetbrains.kotlin.fir.types.functionClassKind import org.jetbrains.kotlin.fir.types.functionClassKind
import org.jetbrains.kotlin.fir.types.typeApproximator import org.jetbrains.kotlin.fir.types.typeApproximator
@@ -30,7 +29,7 @@ internal class KtFirTypeInfoProvider(
firSession, firSession,
analysisSession.getScopeSessionFor(firSession), analysisSession.getScopeSessionFor(firSession),
) )
return samResolver.getFunctionTypeForPossibleSamType(coneType) != null return samResolver.getSamInfoForPossibleSamType(coneType) != null
} }
override fun getFunctionClassKind(type: KtType): FunctionClassKind? { override fun getFunctionClassKind(type: KtType): FunctionClassKind? {
@@ -37192,6 +37192,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt"); 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 @Test
@TestMetadata("noRetentionAfter.kt") @TestMetadata("noRetentionAfter.kt")
public void testNoRetentionAfter() throws Exception { public void testNoRetentionAfter() throws Exception {
@@ -37192,6 +37192,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt"); 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 @Test
@TestMetadata("noRetentionAfter.kt") @TestMetadata("noRetentionAfter.kt")
public void testNoRetentionAfter() throws Exception { public void testNoRetentionAfter() throws Exception {
@@ -37192,6 +37192,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt"); 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 @Test
@TestMetadata("noRetentionAfter.kt") @TestMetadata("noRetentionAfter.kt")
public void testNoRetentionAfter() throws Exception { public void testNoRetentionAfter() throws Exception {
@@ -485,7 +485,7 @@ internal class AdapterGenerator(
} }
internal fun getFunctionTypeForPossibleSamType(parameterType: ConeKotlinType): ConeKotlinType? { internal fun getFunctionTypeForPossibleSamType(parameterType: ConeKotlinType): ConeKotlinType? {
return samResolver.getFunctionTypeForPossibleSamType(parameterType) return samResolver.getSamInfoForPossibleSamType(parameterType)?.type
} }
/** /**
@@ -39,28 +39,37 @@ import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
abstract class FirSamResolver { abstract class FirSamResolver {
abstract fun getFunctionTypeForPossibleSamType(type: ConeKotlinType): ConeKotlinType? abstract fun getSamInfoForPossibleSamType(type: ConeKotlinType): SAMInfo<ConeKotlinType>?
abstract fun shouldRunSamConversionForFunction(firFunction: FirFunction): Boolean abstract fun shouldRunSamConversionForFunction(firFunction: FirFunction): Boolean
abstract fun getSamConstructor(firRegularClass: FirRegularClass): FirSimpleFunction? abstract fun getSamConstructor(firRegularClass: FirRegularClass): FirSimpleFunction?
fun getFunctionTypeForPossibleSamType(type: ConeKotlinType): ConeKotlinType? =
getSamInfoForPossibleSamType(type)?.type
} }
private val SAM_PARAMETER_NAME = Name.identifier("function") private val SAM_PARAMETER_NAME = Name.identifier("function")
data class SAMInfo<out C : ConeKotlinType>(internal val symbol: FirNamedFunctionSymbol, val type: C)
class FirSamResolverImpl( class FirSamResolverImpl(
private val session: FirSession, private val session: FirSession,
private val scopeSession: ScopeSession, private val scopeSession: ScopeSession,
private val outerClassManager: FirOuterClassManager? = null, private val outerClassManager: FirOuterClassManager? = null,
) : FirSamResolver() { ) : FirSamResolver() {
private val resolvedFunctionType: NullableMap<FirRegularClass, ConeLookupTagBasedType?> = NullableMap() private val resolvedFunctionType: NullableMap<FirRegularClass, SAMInfo<ConeLookupTagBasedType>?> = NullableMap()
private val samConstructorsCache = session.samConstructorStorage.samConstructors private val samConstructorsCache = session.samConstructorStorage.samConstructors
override fun getFunctionTypeForPossibleSamType(type: ConeKotlinType): ConeKotlinType? { override fun getSamInfoForPossibleSamType(type: ConeKotlinType): SAMInfo<ConeKotlinType>? {
return when (type) { return when (type) {
is ConeClassLikeType -> getFunctionTypeForPossibleSamType(type.fullyExpandedType(session)) is ConeClassLikeType -> getFunctionTypeForPossibleSamType(type.fullyExpandedType(session))
is ConeFlexibleType -> ConeFlexibleType( is ConeFlexibleType -> {
getFunctionTypeForPossibleSamType(type.lowerBound)?.lowerBoundIfFlexible() ?: return null, val (lowerSymbol, lowerType) = getSamInfoForPossibleSamType(type.lowerBound) ?: return null
getFunctionTypeForPossibleSamType(type.upperBound)?.upperBoundIfFlexible() ?: return null, val (_, upperType) = getSamInfoForPossibleSamType(type.upperBound) ?: return null
) SAMInfo(
lowerSymbol,
ConeFlexibleType(lowerType.lowerBoundIfFlexible(), upperType.upperBoundIfFlexible())
)
}
is ConeErrorType, is ConeStubType -> null is ConeErrorType, is ConeStubType -> null
// TODO: support those types as well // TODO: support those types as well
is ConeTypeParameterType, is ConeTypeVariableType, is ConeTypeParameterType, is ConeTypeVariableType,
@@ -72,14 +81,17 @@ class FirSamResolverImpl(
} }
} }
private fun getFunctionTypeForPossibleSamType(type: ConeClassLikeType): ConeLookupTagBasedType? { private fun getFunctionTypeForPossibleSamType(type: ConeClassLikeType): SAMInfo<ConeLookupTagBasedType>? {
@OptIn(LookupTagInternals::class) @OptIn(LookupTagInternals::class)
val firRegularClass = type.lookupTag.toFirRegularClass(session) ?: return null 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()) { 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 = val substitutor =
@@ -110,7 +122,7 @@ class FirSamResolverImpl(
"Function type should always be ConeLookupTagBasedType, but ${result::class} was found" "Function type should always be ConeLookupTagBasedType, but ${result::class} was found"
} }
return result return SAMInfo(functionSymbol, result)
} }
override fun getSamConstructor(firRegularClass: FirRegularClass): FirSimpleFunction? { override fun getSamConstructor(firRegularClass: FirRegularClass): FirSimpleFunction? {
@@ -119,7 +131,7 @@ class FirSamResolverImpl(
fun buildSamConstructor(classSymbol: FirRegularClassSymbol): FirNamedFunctionSymbol? { fun buildSamConstructor(classSymbol: FirRegularClassSymbol): FirNamedFunctionSymbol? {
val firRegularClass = classSymbol.fir val firRegularClass = classSymbol.fir
val functionType = resolveFunctionTypeIfSamInterface(firRegularClass) ?: return null val (functionSymbol, functionType) = resolveFunctionTypeIfSamInterface(firRegularClass) ?: return null
val classId = firRegularClass.classId val classId = firRegularClass.classId
val symbol = FirSyntheticFunctionSymbol( val symbol = FirSyntheticFunctionSymbol(
@@ -217,19 +229,21 @@ class FirSamResolverImpl(
resolvePhase = FirResolvePhase.BODY_RESOLVE resolvePhase = FirResolvePhase.BODY_RESOLVE
} }
annotations += functionSymbol.annotations
resolvePhase = FirResolvePhase.BODY_RESOLVE resolvePhase = FirResolvePhase.BODY_RESOLVE
}.apply { }.apply {
containingClassForStaticMemberAttr = outerClassManager?.outerClass(firRegularClass.symbol)?.toLookupTag() containingClassForStaticMemberAttr = outerClassManager?.outerClass(firRegularClass.symbol)?.toLookupTag()
}.symbol }.symbol
} }
private fun resolveFunctionTypeIfSamInterface(firRegularClass: FirRegularClass): ConeLookupTagBasedType? { private fun resolveFunctionTypeIfSamInterface(firRegularClass: FirRegularClass): SAMInfo<ConeLookupTagBasedType>? {
return resolvedFunctionType.getOrPut(firRegularClass) { return resolvedFunctionType.getOrPut(firRegularClass) {
if (!firRegularClass.status.isFun) return@getOrPut null if (!firRegularClass.status.isFun) return@getOrPut null
val abstractMethod = firRegularClass.getSingleAbstractMethodOrNull(session, scopeSession) ?: return@getOrPut null val abstractMethod = firRegularClass.getSingleAbstractMethodOrNull(session, scopeSession) ?: return@getOrPut null
// TODO: val shouldConvertFirstParameterToDescriptor = samWithReceiverResolvers.any { it.shouldConvertFirstSamParameterToReceiver(abstractMethod) } // TODO: val shouldConvertFirstParameterToDescriptor = samWithReceiverResolvers.any { it.shouldConvertFirstSamParameterToReceiver(abstractMethod) }
abstractMethod.getFunctionTypeForAbstractMethod() SAMInfo(abstractMethod.symbol, abstractMethod.getFunctionTypeForAbstractMethod())
} }
} }
@@ -188,7 +188,7 @@ abstract class AbstractConeCallConflictResolver(
if (!call.usesSAM) { if (!call.usesSAM) {
TypeWithConversion(argumentType) TypeWithConversion(argumentType)
} else { } else {
val functionType = samResolver.getFunctionTypeForPossibleSamType(argumentType)?.second val functionType = samResolver.getSamInfoForPossibleSamType(argumentType)?.type
if (functionType == null) TypeWithConversion(argumentType) if (functionType == null) TypeWithConversion(argumentType)
else TypeWithConversion(functionType, argumentType) else TypeWithConversion(functionType, argumentType)
} }
@@ -506,7 +506,7 @@ private fun Candidate.getExpectedTypeWithSAMConversion(
// TODO: resolvedCall.registerArgumentWithSamConversion(argument, SamConversionDescription(convertedTypeByOriginal, convertedTypeByCandidate!!)) // TODO: resolvedCall.registerArgumentWithSamConversion(argument, SamConversionDescription(convertedTypeByOriginal, convertedTypeByCandidate!!))
val expectedFunctionType = context.bodyResolveComponents.samResolver.getFunctionTypeForPossibleSamType(candidateExpectedType) val (_, expectedFunctionType) = context.bodyResolveComponents.samResolver.getSamInfoForPossibleSamType(candidateExpectedType)
?: return null ?: return null
return runIf(argument.isFunctional(session, scopeSession, expectedFunctionType)) { return runIf(argument.isFunctional(session, scopeSession, expectedFunctionType)) {
usesSAM = true usesSAM = true
@@ -589,19 +589,16 @@ class FirCallCompletionResultsWriterTransformer(
val firRegularClass = val firRegularClass =
session.symbolProvider.getClassLikeSymbolByClassId(expectedArgumentType.lookupTag.classId)?.fir as? FirRegularClass session.symbolProvider.getClassLikeSymbolByClassId(expectedArgumentType.lookupTag.classId)?.fir as? FirRegularClass
firRegularClass?.let { firRegularClass?.let answer@{
val functionType = samResolver.getFunctionTypeForPossibleSamType(firRegularClass.defaultType()) val (_, functionType) = samResolver.getSamInfoForPossibleSamType(firRegularClass.defaultType())
if (functionType != null) { ?: return@answer null
createFunctionalType( createFunctionalType(
functionType.typeArguments.dropLast(1).map { it as ConeKotlinType }, functionType.typeArguments.dropLast(1).map { it as ConeKotlinType },
null, null,
functionType.typeArguments.last() as ConeKotlinType, functionType.typeArguments.last() as ConeKotlinType,
functionType.classId?.relativeClassName?.asString() functionType.classId?.relativeClassName?.asString()
?.startsWith(FunctionClassKind.SuspendFunction.classNamePrefix) == true ?.startsWith(FunctionClassKind.SuspendFunction.classNamePrefix) == true
) )
} else {
null
}
} }
} }
else -> null else -> null
@@ -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 <!OPT_IN_OVERRIDE_ERROR!>experimentalMethod<!>() {} // correctly fails check
}
val lambda = <!OPT_IN_USAGE_ERROR!>StableInterface<!> {} // this does not get flagged
}
@ExperimentalKotlinAnnotation
fun suppressed() {
val lambda = StableInterface {}
}
@@ -0,0 +1,33 @@
FILE: insideSAM.fir.kt
@R|kotlin/RequiresOptIn|() public final annotation class ExperimentalKotlinAnnotation : R|kotlin/Annotation| {
public constructor(): R|ExperimentalKotlinAnnotation| {
super<R|kotlin/Any|>()
}
}
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|<anonymous>| {
super<R|kotlin/Any|>()
}
public final override fun experimentalMethod(): R|kotlin/Unit| {
}
}
lval lambda: R|StableInterface| = R|/StableInterface|(<L> = StableInterface@fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
^@StableInterface Unit
}
)
}
@R|ExperimentalKotlinAnnotation|() public final fun suppressed(): R|kotlin/Unit| {
lval lambda: R|StableInterface| = R|/StableInterface|(<L> = StableInterface@fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
^@StableInterface Unit
}
)
}
@@ -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 <!OPT_IN_OVERRIDE_ERROR!>experimentalMethod<!>() {} // correctly fails check
}
val lambda = StableInterface {} // this does not get flagged
}
@ExperimentalKotlinAnnotation
fun suppressed() {
val lambda = StableInterface {}
}
@@ -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
}
@@ -37282,6 +37282,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt"); 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 @Test
@TestMetadata("noRetentionAfter.kt") @TestMetadata("noRetentionAfter.kt")
public void testNoRetentionAfter() throws Exception { public void testNoRetentionAfter() throws Exception {