From 5567620563eb1b85175ad456fa3b4d6ddca891fc Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 11 Sep 2019 14:04:34 +0300 Subject: [PATCH] FIR: Support SAM conversion --- .../kotlin/fir/resolve/SamResolution.kt | 199 ++++++++++++++++++ .../kotlin/fir/resolve/calls/Arguments.kt | 25 ++- .../kotlin/fir/resolve/calls/Candidate.kt | 5 +- .../kotlin/fir/resolve/calls/InferenceUtil.kt | 4 +- .../transformers/FirBodyResolveTransformer.kt | 2 +- .../diagnostics/samConversions/genericSam.kt | 41 ++++ .../diagnostics/samConversions/genericSam.txt | 23 ++ .../diagnostics/samConversions/kotlinSam.kt | 42 ++++ .../diagnostics/samConversions/kotlinSam.txt | 56 +++++ .../notSamBecauseOfSupertype.kt | 31 +++ .../notSamBecauseOfSupertype.txt | 18 ++ .../diagnostics/samConversions/runnable.kt | 16 ++ .../diagnostics/samConversions/runnable.txt | 11 + .../samConversions/samSupertype.kt | 32 +++ .../samConversions/samSupertype.txt | 18 ++ .../samSupertypeWithOverride.kt | 34 +++ .../samSupertypeWithOverride.txt | 18 ++ .../diagnostics/samConversions/simple.kt | 26 +++ .../diagnostics/samConversions/simple.txt | 18 ++ .../fir/FirDiagnosticsTestGenerated.java | 48 +++++ .../sam/samByProjectedType.fir.txt | 4 +- .../sam/samConversionToGeneric.fir.txt | 16 +- .../expressions/sam/samConversions.fir.txt | 12 +- .../sam/samConversionsWithSmartCasts.fir.txt | 14 +- 24 files changed, 688 insertions(+), 25 deletions(-) create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SamResolution.kt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/genericSam.kt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/genericSam.txt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/kotlinSam.kt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/kotlinSam.txt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/notSamBecauseOfSupertype.kt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/notSamBecauseOfSupertype.txt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/runnable.kt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/runnable.txt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/samSupertype.kt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/samSupertype.txt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/samSupertypeWithOverride.kt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/samSupertypeWithOverride.txt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/simple.kt create mode 100644 compiler/fir/resolve/testData/diagnostics/samConversions/simple.txt 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 new file mode 100644 index 00000000000..abb472c22ac --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SamResolution.kt @@ -0,0 +1,199 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.resolve + +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.FirSessionComponent +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap +import org.jetbrains.kotlin.fir.scopes.ProcessorAction +import org.jetbrains.kotlin.fir.symbols.StandardClassIds +import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl +import org.jetbrains.kotlin.name.Name + +interface FirSamResolver : FirSessionComponent { + fun getFunctionTypeForPossibleSamType(type: ConeKotlinType): ConeKotlinType? + fun shouldRunSamConversionForFunction(firNamedFunction: FirNamedFunction): Boolean +} + +private val NULL_STUB = Any() + +class FirSamResolverImpl( + private val firSession: FirSession, + private val scopeSession: ScopeSession +) : FirSamResolver { + + private val cache: MutableMap = mutableMapOf() + + override fun getFunctionTypeForPossibleSamType(type: ConeKotlinType): ConeKotlinType? { + return when (type) { + is ConeClassType -> getFunctionTypeForPossibleSamType(type) + is ConeFlexibleType -> ConeFlexibleType( + getFunctionTypeForPossibleSamType(type.lowerBound) ?: return null, + getFunctionTypeForPossibleSamType(type.upperBound) ?: return null + ) + is ConeClassErrorType -> null + // TODO: support those types as well + is ConeAbbreviatedType, is ConeTypeParameterType, is ConeTypeVariableType, + is ConeCapturedType, is ConeDefinitelyNotNullType, is ConeIntersectionType -> null + // TODO: Thing of getting rid of this branch since ConeLookupTagBasedType should be a sealed class + is ConeLookupTagBasedType -> null + } + } + + private fun getFunctionTypeForPossibleSamType(type: ConeClassType): ConeLookupTagBasedType? { + val firRegularClass = + firSession.firSymbolProvider + .getSymbolByLookupTag(type.lookupTag) + ?.fir as? FirRegularClass + ?: return null + + val unsubstitutedFunctionType = resolveFunctionTypeIfSamInterface(firRegularClass) ?: return null + val substitutor = + substitutorByMap( + firRegularClass.typeParameters + .map { it.symbol } + .zip(type.typeArguments.map { + (it as? ConeTypedProjection)?.type + ?: ConeClassTypeImpl(ConeClassLikeLookupTagImpl(StandardClassIds.Any), emptyArray(), isNullable = true) + }) + .toMap() + ) + + val result = + substitutor + .substituteOrSelf(unsubstitutedFunctionType) + .withNullability(ConeNullability.create(type.isMarkedNullable)) + + require(result is ConeLookupTagBasedType) { + "Function type should always be ConeLookupTagBasedType, but ${result::class} was found" + } + + return result + } + + private fun resolveFunctionTypeIfSamInterface(firRegularClass: FirRegularClass): ConeKotlinType? { + return cache.getOrPut(firRegularClass) { + val abstractMethod = firRegularClass.getSingleAbstractMethodOrNull(firSession, scopeSession) ?: return@getOrPut NULL_STUB + // TODO: val shouldConvertFirstParameterToDescriptor = samWithReceiverResolvers.any { it.shouldConvertFirstSamParameterToReceiver(abstractMethod) } + + abstractMethod.getFunctionTypeForAbstractMethod(firSession) + } as? ConeKotlinType + } + + override fun shouldRunSamConversionForFunction(firNamedFunction: FirNamedFunction): Boolean { + // TODO: properly support, see org.jetbrains.kotlin.load.java.sam.JvmSamConversionTransformer.shouldRunSamConversionForFunction + return true + } +} + +private fun FirRegularClass.getSingleAbstractMethodOrNull( + session: FirSession, + scopeSession: ScopeSession +): FirNamedFunction? { + // TODO: restrict to Java interfaces + if (classKind != ClassKind.INTERFACE || hasMoreThenOneAbstractFunctionOrHasAbstractProperty()) return null + + val samCandidateNames = computeSamCandidateNames(session) + + return findSingleAbstractMethodByNames(session, scopeSession, samCandidateNames) +} + +private fun FirRegularClass.computeSamCandidateNames(session: FirSession): Set { + val classes = + lookupSuperTypes(this, lookupInterfaces = true, deep = true, useSiteSession = session) + .mapNotNullTo(mutableListOf(this)) { + (session.firSymbolProvider.getSymbolByLookupTag(it.lookupTag) as? FirClassSymbol)?.fir + } + + val samCandidateNames = mutableSetOf() + for (clazz in classes) { + for (declaration in clazz.declarations) { + if (declaration !is FirMemberDeclaration || declaration.modality != Modality.ABSTRACT) continue + samCandidateNames.add(declaration.name) + } + } + + return samCandidateNames +} + +private fun FirRegularClass.findSingleAbstractMethodByNames( + session: FirSession, + scopeSession: ScopeSession, + samCandidateNames: Set +): FirNamedFunction? { + var resultMethod: FirNamedFunction? = null + var metIncorrectMember = false + + val classUseSiteMemberScope = session.firSymbolProvider.getClassUseSiteMemberScope(classId, session, scopeSession) + + for (candidateName in samCandidateNames) { + if (classUseSiteMemberScope == null) break + if (metIncorrectMember) break + + classUseSiteMemberScope.processPropertiesByName(candidateName) { + if ((it as? FirProperty)?.modality == Modality.ABSTRACT) { + metIncorrectMember = true + ProcessorAction.STOP + } else { + ProcessorAction.NEXT + } + } + + if (metIncorrectMember) break + + classUseSiteMemberScope.processFunctionsByName(candidateName) { functionSymbol -> + val firFunction = functionSymbol.fir + require(firFunction is FirNamedFunction) { + "${functionSymbol.callableId.callableName} is expected to be FirNamedFunction, but ${functionSymbol::class} was found" + } + + if (firFunction.modality != Modality.ABSTRACT) return@processFunctionsByName ProcessorAction.NEXT + + if (resultMethod != null) { + metIncorrectMember = true + ProcessorAction.STOP + } else { + resultMethod = firFunction + ProcessorAction.NEXT + } + } + } + + if (metIncorrectMember || resultMethod == null || resultMethod!!.typeParameters.isNotEmpty()) return null + + return resultMethod +} + +private fun FirRegularClass.hasMoreThenOneAbstractFunctionOrHasAbstractProperty(): Boolean { + var wasAbstractFunction = false + for (declaration in declarations) { + if (declaration is FirProperty && declaration.modality == Modality.ABSTRACT) return true + if (declaration is FirNamedFunction && declaration.modality == Modality.ABSTRACT) { + if (wasAbstractFunction) return true + wasAbstractFunction = true + } + } + + return false +} + +private fun FirNamedFunction.getFunctionTypeForAbstractMethod(session: FirSession): ConeLookupTagBasedType { + val parameterTypes = valueParameters.map { + it.returnTypeRef.coneTypeSafe() ?: ConeKotlinErrorType("No type for parameter $it") + } + + return createFunctionalType( + session, parameterTypes, + receiverType = null, + rawReturnType = returnTypeRef.coneTypeSafe() ?: ConeKotlinErrorType("No type for return type of $this") + ) +} 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 d3c4a9e379e..7e99306ebea 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 @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.resolve.calls import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction +import org.jetbrains.kotlin.fir.declarations.FirNamedFunction import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe @@ -234,10 +235,32 @@ internal fun Candidate.resolveArgument( } private fun Candidate.prepareExpectedType(session: FirSession, argument: FirExpression, parameter: FirValueParameter): ConeKotlinType { - val expectedType = argument.getExpectedType(session, parameter/*, LanguageVersionSettings*/) + val basicExpectedType = argument.getExpectedType(session, parameter/*, LanguageVersionSettings*/) + val expectedType = getExpectedTypeWithSAMConversion(session, argument, basicExpectedType) ?: basicExpectedType return this.substitutor.substituteOrSelf(expectedType) } +private fun Candidate.getExpectedTypeWithSAMConversion( + session: FirSession, + argument: FirExpression, + candidateExpectedType: ConeKotlinType +): ConeKotlinType? { + if (candidateExpectedType.isBuiltinFunctionalType) return null + // TODO: if (!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.SamConversionPerArgument)) return null + val firNamedFunction = symbol.fir as? FirNamedFunction ?: return null + if (!samResolver.shouldRunSamConversionForFunction(firNamedFunction)) return null + + val argumentIsFunctional = when ((argument as? FirWrappedArgumentExpression)?.expression ?: argument) { + is FirAnonymousFunction, is FirCallableReferenceAccess -> true + else -> argument.typeRef.coneTypeSafe()?.isBuiltinFunctionalType == true + } + if (!argumentIsFunctional) return null + + // TODO: resolvedCall.registerArgumentWithSamConversion(argument, SamConversionDescription(convertedTypeByOriginal, convertedTypeByCandidate!!)) + + return samResolver.getFunctionTypeForPossibleSamType(candidateExpectedType) ?: return null +} + internal fun FirExpression.getExpectedType( session: FirSession, parameter: FirValueParameter/*, languageVersionSettings: LanguageVersionSettings*/ diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt index fbc7939f46f..4bd065ee33f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt @@ -58,6 +58,9 @@ class Candidate( system.addOtherSystem(baseSystem) system } + + val samResolver get() = inferenceComponents.samResolver + lateinit var substitutor: ConeSubstitutor var argumentMapping: Map? = null @@ -72,4 +75,4 @@ class Candidate( ExplicitReceiverKind.EXTENSION_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> callInfo.explicitReceiver!! else -> implicitExtensionReceiverValue?.receiverExpression ?: FirNoReceiverExpression } -} \ No newline at end of file +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt index 737e4a15aac..de45d0f58df 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.resolve.calls import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.resolve.FirSamResolver import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator @@ -57,7 +58,8 @@ class InferenceComponents( val ctx: TypeSystemInferenceExtensionContextDelegate, val session: FirSession, val returnTypeCalculator: ReturnTypeCalculator, - val scopeSession: ScopeSession + val scopeSession: ScopeSession, + val samResolver: FirSamResolver ) { private val approximator = object : AbstractTypeApproximator(ctx) { override fun createErrorType(message: String): SimpleTypeMarker { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt index 85fa1ecab9e..81c71fa5d28 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt @@ -978,7 +978,7 @@ private fun inferenceComponents(session: FirSession, returnTypeCalculator: Retur require(this is ErrorTypeConstructor) return ConeClassErrorType(reason) } - }, session, returnTypeCalculator, scopeSession) + }, session, returnTypeCalculator, scopeSession, FirSamResolverImpl(session, scopeSession)) class FirDesignatedBodyResolveTransformer( diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/genericSam.kt b/compiler/fir/resolve/testData/diagnostics/samConversions/genericSam.kt new file mode 100644 index 00000000000..4dae57a69fc --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/genericSam.kt @@ -0,0 +1,41 @@ +// FILE: MyFunction.java +public interface MyFunction { + R foo(T x); +} + +// FILE: JavaUsage.java + +public class JavaUsage { + public static void foo1(MyFunction x) {} + public static void foo2(MyFunction x) {} + public static Y foo3(MyFunction f, X x) {} +} +// FILE: main.kt + +fun main() { + JavaUsage.foo1 { x -> + x.toInt().toString() + } + + JavaUsage.foo2 { x -> + x.toInt().toString() + } + + JavaUsage.foo2 { x: Int -> + x.toString() + } + + JavaUsage.foo3( + { x -> + (x + 1).toString() + }, + 1 + ) + + JavaUsage.foo3( + { x: Number -> + x.toInt().toString() + }, + 2 + ) +} diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/genericSam.txt b/compiler/fir/resolve/testData/diagnostics/samConversions/genericSam.txt new file mode 100644 index 00000000000..ae7a7846557 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/genericSam.txt @@ -0,0 +1,23 @@ +FILE: main.kt + public final fun main(): R|kotlin/Unit| { + Q|JavaUsage|.R|/JavaUsage.foo1|( = foo1@fun (x: R|ft!|): R|ft!| { + R|/x|.R|kotlin/Int.toInt|().R|kotlin/Any.toString|() + } + ) + Q|JavaUsage|.R|/JavaUsage.foo2|( = foo2@fun (x: R|ft!|): R|ft!| { + R|/x|.R|kotlin/Number.toInt|().R|kotlin/Any.toString|() + } + ) + Q|JavaUsage|.R|/JavaUsage.foo2|( = foo2@fun (x: R|kotlin/Int|): R|ft!| { + R|/x|.R|kotlin/Any.toString|() + } + ) + Q|JavaUsage|.R|/JavaUsage.foo3||, R|ft|>(foo3@fun (x: R|ft|): R|kotlin/Unit| { + R|/x|.R|kotlin/Int.plus|(Int(1)).R|kotlin/Any.toString|() + } + , Int(1)) + Q|JavaUsage|.R|/JavaUsage.foo3||, R|ft|>(foo3@fun (x: R|kotlin/Number|): R|kotlin/Unit| { + R|/x|.R|kotlin/Number.toInt|().R|kotlin/Any.toString|() + } + , Int(2)) + } diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/kotlinSam.kt b/compiler/fir/resolve/testData/diagnostics/samConversions/kotlinSam.kt new file mode 100644 index 00000000000..c1531c5de27 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/kotlinSam.kt @@ -0,0 +1,42 @@ +// TODO: These interfaces must be marked as "fun" ones that modifier is supported +interface MyRunnable { + fun foo(x: Int): Boolean +} + +interface WithProperty { + val x: Int +} + +interface TwoAbstract : MyRunnable { + fun bar() +} + +interface Super { + fun foo(x: Int): Any +} + +interface Derived : Super { + override fun foo(x: Int): Boolean +} + +fun foo1(m: MyRunnable) {} +fun foo2(m: WithProperty) {} +fun foo3(m: TwoAbstract) {} +fun foo3(m: Derived) {} + +fun main() { + val f = { t: Int -> t > 1} + + foo1 { x -> x > 1 } + foo1(f) + + foo2 { x -> x > 1 } + foo2(f) + + foo3 { x -> x > 1 } + foo3(f) + + foo4 { x -> x > 1 } + foo4(f) + +} diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/kotlinSam.txt b/compiler/fir/resolve/testData/diagnostics/samConversions/kotlinSam.txt new file mode 100644 index 00000000000..d1da06ff491 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/kotlinSam.txt @@ -0,0 +1,56 @@ +FILE: kotlinSam.kt + public abstract interface MyRunnable : R|kotlin/Any| { + public abstract fun foo(x: R|kotlin/Int|): R|kotlin/Boolean| + + } + public abstract interface WithProperty : R|kotlin/Any| { + public abstract val x: R|kotlin/Int| + public get(): R|kotlin/Int| + + } + public abstract interface TwoAbstract : R|MyRunnable| { + public abstract fun bar(): R|kotlin/Unit| + + } + public abstract interface Super : R|kotlin/Any| { + public abstract fun foo(x: R|kotlin/Int|): R|kotlin/Any| + + } + public abstract interface Derived : R|Super| { + public abstract override fun foo(x: R|kotlin/Int|): R|kotlin/Boolean| + + } + public final fun foo1(m: R|MyRunnable|): R|kotlin/Unit| { + } + public final fun foo2(m: R|WithProperty|): R|kotlin/Unit| { + } + public final fun foo3(m: R|TwoAbstract|): R|kotlin/Unit| { + } + public final fun foo3(m: R|Derived|): R|kotlin/Unit| { + } + public final fun main(): R|kotlin/Unit| { + lval f: R|kotlin/Function1| = fun (t: R|kotlin/Int|): kotlin/Boolean { + >(R|/t|, Int(1)) + } + + R|/foo1|( = foo1@fun (x: R|kotlin/Int|): R|kotlin/Boolean| { + >(R|/x|, Int(1)) + } + ) + R|/foo1|(R|/f|) + R|/foo2|( = foo2@fun (x: R|kotlin/Nothing|): kotlin/Boolean { + >(R|/x|, Int(1)) + } + ) + #(R|/f|) + #( = foo3@fun .(x: ): { + >(x#, Int(1)) + } + ) + R|/foo3|(R|/f|) + #( = foo4@fun .(x: ): { + >(x#, Int(1)) + } + ) + #(R|/f|) + } diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/notSamBecauseOfSupertype.kt b/compiler/fir/resolve/testData/diagnostics/samConversions/notSamBecauseOfSupertype.kt new file mode 100644 index 00000000000..59864e4bae7 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/notSamBecauseOfSupertype.kt @@ -0,0 +1,31 @@ +// FILE: MyRunnable.java +public interface MyRunnable { + void bar(); +} + +// FILE: DerivedRunnable.java +public interface DerivedRunnable extends MyRunnable { + boolean foo(int x); +} + +// FILE: JavaUsage.java + +public class JavaUsage { + public static void foo(DerivedRunnable x) {} +} +// FILE: main.kt + +fun foo(m: MyRunnable) {} + +fun main() { + JavaUsage.foo { + x -> + x > 1 + } + + JavaUsage.foo({ it > 1 }) + + val x = { x: Int -> x > 1 } + + JavaUsage.foo(x) +} diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/notSamBecauseOfSupertype.txt b/compiler/fir/resolve/testData/diagnostics/samConversions/notSamBecauseOfSupertype.txt new file mode 100644 index 00000000000..5102818ff0b --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/notSamBecauseOfSupertype.txt @@ -0,0 +1,18 @@ +FILE: main.kt + public final fun foo(m: R|MyRunnable|): R|kotlin/Unit| { + } + public final fun main(): R|kotlin/Unit| { + Q|JavaUsage|.R|/JavaUsage.foo|( = foo@fun (x: R|kotlin/Nothing|): kotlin/Boolean { + >(R|/x|, Int(1)) + } + ) + Q|JavaUsage|.R|/JavaUsage.foo|(foo@fun (): kotlin/Boolean { + >(#, Int(1)) + } + ) + lval x: R|kotlin/Function1| = fun (x: R|kotlin/Int|): kotlin/Boolean { + >(R|/x|, Int(1)) + } + + Q|JavaUsage|.#(R|/x|) + } diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/runnable.kt b/compiler/fir/resolve/testData/diagnostics/samConversions/runnable.kt new file mode 100644 index 00000000000..5c6815c2d5c --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/runnable.kt @@ -0,0 +1,16 @@ +// FILE: JavaClass.java +public class JavaClass { + public static void foo(Runnable x) {} + public void bar(Runnable x) {} +} + +// FILE: main.kt +fun main() { + JavaClass.foo { + "" + } + + JavaClass().bar { + "" + } +} diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/runnable.txt b/compiler/fir/resolve/testData/diagnostics/samConversions/runnable.txt new file mode 100644 index 00000000000..1a02c812105 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/runnable.txt @@ -0,0 +1,11 @@ +FILE: main.kt + public final fun main(): R|kotlin/Unit| { + Q|JavaClass|.R|/JavaClass.foo|( = foo@fun (): R|kotlin/Unit| { + String() + } + ) + R|/JavaClass.JavaClass|().R|/JavaClass.bar|( = bar@fun (): R|kotlin/Unit| { + String() + } + ) + } diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertype.kt b/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertype.kt new file mode 100644 index 00000000000..151c247c8fd --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertype.kt @@ -0,0 +1,32 @@ +// FILE: MyRunnable.java +public interface MyRunnable { + boolean foo(int x); + default void bar() {} +} + +// FILE: DerivedRunnable.java +public interface DerivedRunnable extends MyRunnable { + default void baz() {} +} + +// FILE: JavaUsage.java + +public class JavaUsage { + public static void foo(DerivedRunnable x) {} +} +// FILE: main.kt + +fun foo(m: MyRunnable) {} + +fun main() { + JavaUsage.foo { + x -> + x > 1 + } + + JavaUsage.foo({ it > 1 }) + + val x = { x: Int -> x > 1 } + + JavaUsage.foo(x) +} diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertype.txt b/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertype.txt new file mode 100644 index 00000000000..5d1edc0baaa --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertype.txt @@ -0,0 +1,18 @@ +FILE: main.kt + public final fun foo(m: R|MyRunnable|): R|kotlin/Unit| { + } + public final fun main(): R|kotlin/Unit| { + Q|JavaUsage|.R|/JavaUsage.foo|( = foo@fun (x: R|kotlin/Int|): R|kotlin/Boolean| { + >(R|/x|, Int(1)) + } + ) + Q|JavaUsage|.R|/JavaUsage.foo|(foo@fun (it: R|kotlin/Int|): R|kotlin/Boolean| { + >(R|/it|, Int(1)) + } + ) + lval x: R|kotlin/Function1| = fun (x: R|kotlin/Int|): kotlin/Boolean { + >(R|/x|, Int(1)) + } + + Q|JavaUsage|.R|/JavaUsage.foo|(R|/x|) + } diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertypeWithOverride.kt b/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertypeWithOverride.kt new file mode 100644 index 00000000000..6da19e59dac --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertypeWithOverride.kt @@ -0,0 +1,34 @@ +// FILE: MyRunnable.java +public interface MyRunnable { + Object foo(int x); + default void bar() {} +} + +// FILE: DerivedRunnable.java +public interface DerivedRunnable extends MyRunnable { + @Override + Boolean foo(int x); + default void baz() {} +} + +// FILE: JavaUsage.java + +public class JavaUsage { + public static void foo(DerivedRunnable x) {} +} +// FILE: main.kt + +fun foo(m: MyRunnable) {} + +fun main() { + JavaUsage.foo { + x -> + x > 1 + } + + JavaUsage.foo({ it > 1 }) + + val x = { x: Int -> x > 1 } + + JavaUsage.foo(x) +} diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertypeWithOverride.txt b/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertypeWithOverride.txt new file mode 100644 index 00000000000..90e1b08d47f --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/samSupertypeWithOverride.txt @@ -0,0 +1,18 @@ +FILE: main.kt + public final fun foo(m: R|MyRunnable|): R|kotlin/Unit| { + } + public final fun main(): R|kotlin/Unit| { + Q|JavaUsage|.R|/JavaUsage.foo|( = foo@fun (x: R|kotlin/Int|): R|ft!| { + >(R|/x|, Int(1)) + } + ) + Q|JavaUsage|.R|/JavaUsage.foo|(foo@fun (it: R|kotlin/Int|): R|ft!| { + >(R|/it|, Int(1)) + } + ) + lval x: R|kotlin/Function1| = fun (x: R|kotlin/Int|): kotlin/Boolean { + >(R|/x|, Int(1)) + } + + Q|JavaUsage|.R|/JavaUsage.foo|(R|/x|) + } diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/simple.kt b/compiler/fir/resolve/testData/diagnostics/samConversions/simple.kt new file mode 100644 index 00000000000..1eaae859a57 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/simple.kt @@ -0,0 +1,26 @@ +// FILE: MyRunnable.java +public interface MyRunnable { + boolean foo(int x); +} + +// FILE: JavaUsage.java + +public class JavaUsage { + public static void foo(MyRunnable x) {} +} +// FILE: main.kt + +fun foo(m: MyRunnable) {} + +fun main() { + JavaUsage.foo { + x -> + x > 1 + } + + JavaUsage.foo({ it > 1 }) + + val x = { x: Int -> x > 1 } + + JavaUsage.foo(x) +} diff --git a/compiler/fir/resolve/testData/diagnostics/samConversions/simple.txt b/compiler/fir/resolve/testData/diagnostics/samConversions/simple.txt new file mode 100644 index 00000000000..5d1edc0baaa --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/samConversions/simple.txt @@ -0,0 +1,18 @@ +FILE: main.kt + public final fun foo(m: R|MyRunnable|): R|kotlin/Unit| { + } + public final fun main(): R|kotlin/Unit| { + Q|JavaUsage|.R|/JavaUsage.foo|( = foo@fun (x: R|kotlin/Int|): R|kotlin/Boolean| { + >(R|/x|, Int(1)) + } + ) + Q|JavaUsage|.R|/JavaUsage.foo|(foo@fun (it: R|kotlin/Int|): R|kotlin/Boolean| { + >(R|/it|, Int(1)) + } + ) + lval x: R|kotlin/Function1| = fun (x: R|kotlin/Int|): kotlin/Boolean { + >(R|/x|, Int(1)) + } + + Q|JavaUsage|.R|/JavaUsage.foo|(R|/x|) + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index d36bd233663..5ed71efe9be 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -91,4 +91,52 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/resolve/testData/diagnostics/j+k/outerInnerClasses.kt"); } } + + @TestMetadata("compiler/fir/resolve/testData/diagnostics/samConversions") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SamConversions extends AbstractFirDiagnosticsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInSamConversions() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/diagnostics/samConversions"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("genericSam.kt") + public void testGenericSam() throws Exception { + runTest("compiler/fir/resolve/testData/diagnostics/samConversions/genericSam.kt"); + } + + @TestMetadata("kotlinSam.kt") + public void testKotlinSam() throws Exception { + runTest("compiler/fir/resolve/testData/diagnostics/samConversions/kotlinSam.kt"); + } + + @TestMetadata("notSamBecauseOfSupertype.kt") + public void testNotSamBecauseOfSupertype() throws Exception { + runTest("compiler/fir/resolve/testData/diagnostics/samConversions/notSamBecauseOfSupertype.kt"); + } + + @TestMetadata("runnable.kt") + public void testRunnable() throws Exception { + runTest("compiler/fir/resolve/testData/diagnostics/samConversions/runnable.kt"); + } + + @TestMetadata("samSupertype.kt") + public void testSamSupertype() throws Exception { + runTest("compiler/fir/resolve/testData/diagnostics/samConversions/samSupertype.kt"); + } + + @TestMetadata("samSupertypeWithOverride.kt") + public void testSamSupertypeWithOverride() throws Exception { + runTest("compiler/fir/resolve/testData/diagnostics/samConversions/samSupertypeWithOverride.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/fir/resolve/testData/diagnostics/samConversions/simple.kt"); + } + } } diff --git a/compiler/testData/ir/irText/expressions/sam/samByProjectedType.fir.txt b/compiler/testData/ir/irText/expressions/sam/samByProjectedType.fir.txt index 36b58dca825..b3775fd5d95 100644 --- a/compiler/testData/ir/irText/expressions/sam/samByProjectedType.fir.txt +++ b/compiler/testData/ir/irText/expressions/sam/samByProjectedType.fir.txt @@ -2,8 +2,8 @@ FILE fqName: fileName:/samByProjectedType.kt FUN name:test1 visibility:public modality:FINAL <> () returnType:kotlin.Unit BLOCK_BODY CALL 'public open fun bar (j: .J<*>?): kotlin.Unit declared in .H' type=kotlin.Unit origin=null - j: FUN_EXPR type=kotlin.Function1 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (x:kotlin.Any) returnType:kotlin.Any + j: FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (x:kotlin.Any) returnType:kotlin.Any? VALUE_PARAMETER name:x index:0 type:kotlin.Any BLOCK_BODY GET_VAR 'x: kotlin.Any declared in .test1.' type=kotlin.Any origin=null diff --git a/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.txt b/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.txt index 64038d2dc64..44d18cb598f 100644 --- a/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.txt +++ b/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.txt @@ -38,22 +38,22 @@ FILE fqName: fileName:/samConversionToGeneric.kt BLOCK_BODY TYPE_OP type=kotlin.Function1 origin=CAST typeOperand=kotlin.Function1 GET_VAR 'a: kotlin.Any declared in .test5' type=kotlin.Any origin=null - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - GET_VAR 'a: kotlin.Any declared in .test5' type=kotlin.Function1 origin=null + CALL 'public open fun bar (j: .J?>?): kotlin.Unit declared in .H' type=kotlin.Unit origin=null + j: GET_VAR 'a: kotlin.Any declared in .test5' type=kotlin.Function1 origin=null FUN name:test6 visibility:public modality:FINAL (a:kotlin.Function1.test6, T of .test6>) returnType:kotlin.Unit TYPE_PARAMETER name:T index:0 variance: superTypes:[] VALUE_PARAMETER name:a index:0 type:kotlin.Function1.test6, T of .test6> BLOCK_BODY - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - GET_VAR 'a: kotlin.Function1.test6, T of .test6> declared in .test6' type=kotlin.Function1.test6, T of .test6> origin=null + CALL 'public open fun bar (j: .J?>?): kotlin.Unit declared in .H' type=kotlin.Unit origin=null + j: GET_VAR 'a: kotlin.Function1.test6, T of .test6> declared in .test6' type=kotlin.Function1.test6, T of .test6> origin=null FUN name:test7 visibility:public modality:FINAL (a:kotlin.Any) returnType:kotlin.Unit TYPE_PARAMETER name:T index:0 variance: superTypes:[] VALUE_PARAMETER name:a index:0 type:kotlin.Any BLOCK_BODY TYPE_OP type=kotlin.Function1.test7, T of .test7> origin=CAST typeOperand=kotlin.Function1.test7, T of .test7> GET_VAR 'a: kotlin.Any declared in .test7' type=kotlin.Any origin=null - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - GET_VAR 'a: kotlin.Any declared in .test7' type=kotlin.Function1.test7, T of .test7> origin=null + CALL 'public open fun bar (j: .J?>?): kotlin.Unit declared in .H' type=kotlin.Unit origin=null + j: GET_VAR 'a: kotlin.Any declared in .test7' type=kotlin.Function1.test7, T of .test7> origin=null FUN name:test8 visibility:public modality:FINAL <> (efn:kotlin.Function1) returnType:IrErrorType VALUE_PARAMETER name:efn index:0 type:kotlin.Function1 BLOCK_BODY @@ -63,8 +63,8 @@ FILE fqName: fileName:/samConversionToGeneric.kt FUN name:test9 visibility:public modality:FINAL <> (efn:kotlin.Function1) returnType:kotlin.Unit VALUE_PARAMETER name:efn index:0 type:kotlin.Function1 BLOCK_BODY - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - GET_VAR 'efn: kotlin.Function1 declared in .test9' type=kotlin.Function1 origin=null + CALL 'public open fun bar (j: .J?>?): kotlin.Unit declared in .H' type=kotlin.Unit origin=null + j: GET_VAR 'efn: kotlin.Function1 declared in .test9' type=kotlin.Function1 origin=null FUN name:test10 visibility:public modality:FINAL <> (fn:kotlin.Function1) returnType:kotlin.Unit VALUE_PARAMETER name:fn index:0 type:kotlin.Function1 BLOCK_BODY diff --git a/compiler/testData/ir/irText/expressions/sam/samConversions.fir.txt b/compiler/testData/ir/irText/expressions/sam/samConversions.fir.txt index 6ca47189ed3..2f77f9d6195 100644 --- a/compiler/testData/ir/irText/expressions/sam/samConversions.fir.txt +++ b/compiler/testData/ir/irText/expressions/sam/samConversions.fir.txt @@ -28,17 +28,19 @@ FILE fqName: fileName:/samConversions.kt $receiver: VALUE_PARAMETER name: type:.J VALUE_PARAMETER name:a index:0 type:kotlin.Function0 BLOCK_BODY - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - GET_VAR 'a: kotlin.Function0 declared in .test3' type=kotlin.Function0 origin=null - GET_VAR 'a: kotlin.Function0 declared in .test3' type=kotlin.Function0 origin=null + CALL 'public open fun run2 (r1: java.lang.Runnable?, r2: java.lang.Runnable?): kotlin.Unit declared in .J' type=kotlin.Unit origin=null + $this: GET_VAR ': .J declared in .J' type=.J origin=null + r1: GET_VAR 'a: kotlin.Function0 declared in .test3' type=kotlin.Function0 origin=null + r2: GET_VAR 'a: kotlin.Function0 declared in .test3' type=kotlin.Function0 origin=null FUN name:test4 visibility:public modality:FINAL <> ($receiver:.J, a:kotlin.Function0, b:kotlin.Function0, flag:kotlin.Boolean) returnType:kotlin.Unit $receiver: VALUE_PARAMETER name: type:.J VALUE_PARAMETER name:a index:0 type:kotlin.Function0 VALUE_PARAMETER name:b index:1 type:kotlin.Function0 VALUE_PARAMETER name:flag index:2 type:kotlin.Boolean BLOCK_BODY - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - WHEN type=kotlin.Function0 origin=IF + CALL 'public open fun runIt (r: java.lang.Runnable?): kotlin.Unit declared in .J' type=kotlin.Unit origin=null + $this: GET_VAR ': .J declared in .J' type=.J origin=null + r: WHEN type=kotlin.Function0 origin=IF BRANCH if: GET_VAR 'flag: kotlin.Boolean declared in .test4' type=kotlin.Boolean origin=null then: GET_VAR 'a: kotlin.Function0 declared in .test4' type=kotlin.Function0 origin=null diff --git a/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.txt b/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.txt index 0546a4fb9b0..be9cc6cb1ec 100644 --- a/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.txt +++ b/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.txt @@ -33,13 +33,14 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt VALUE_PARAMETER name:a index:0 type:kotlin.Function0 VALUE_PARAMETER name:b index:1 type:kotlin.Function0 BLOCK_BODY - WHEN type=IrErrorType origin=IF + WHEN type=kotlin.Unit origin=IF BRANCH if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=java.lang.Runnable GET_VAR 'a: kotlin.Function0 declared in .test4' type=kotlin.Function0 origin=null - then: ERROR_CALL 'Unresolved reference: #' type=IrErrorType - GET_VAR 'a: kotlin.Function0 declared in .test4' type=java.lang.Runnable origin=null - GET_VAR 'b: kotlin.Function0 declared in .test4' type=kotlin.Function0 origin=null + then: CALL 'public open fun run2 (r1: java.lang.Runnable?, r2: java.lang.Runnable?): kotlin.Unit declared in .J' type=kotlin.Unit origin=null + $this: CONSTRUCTOR_CALL 'public constructor () [primary] declared in .J' type=.J origin=null + r1: GET_VAR 'a: kotlin.Function0 declared in .test4' type=java.lang.Runnable origin=null + r2: GET_VAR 'b: kotlin.Function0 declared in .test4' type=kotlin.Function0 origin=null FUN name:test5 visibility:public modality:FINAL <> (a:kotlin.Any) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Any BLOCK_BODY @@ -68,8 +69,9 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt BLOCK_BODY TYPE_OP type=kotlin.Function0 origin=CAST typeOperand=kotlin.Function0 GET_VAR 'a: kotlin.Any declared in .test6' type=kotlin.Any origin=null - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - GET_VAR 'a: kotlin.Any declared in .test6' type=kotlin.Function0 origin=null + CALL 'public open fun run1 (r: java.lang.Runnable?): kotlin.Unit declared in .J' type=kotlin.Unit origin=null + $this: CONSTRUCTOR_CALL 'public constructor () [primary] declared in .J' type=.J origin=null + r: GET_VAR 'a: kotlin.Any declared in .test6' type=kotlin.Function0 origin=null FUN name:test7 visibility:public modality:FINAL <> (a:kotlin.Function1) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Function1 BLOCK_BODY