From d5a6991b2dc01f0e2adea1e6b06657d6eee393d8 Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Thu, 10 Dec 2020 00:32:39 -0800 Subject: [PATCH] FIR: extend SAM conversion to subtype of functional type --- .../generators/CallAndReferenceGenerator.kt | 10 ++++- .../kotlin/fir/resolve/LookupTagUtils.kt | 4 ++ .../kotlin/fir/resolve/SamResolution.kt | 23 +++++----- .../kotlin/fir/resolve/calls/Arguments.kt | 45 ++++++++++++++----- ...ntersectionTypeToFunInterfaceConversion.kt | 1 - ...fFunctionalTypeToFunInterfaceConversion.kt | 1 - .../caoWithAdaptationForSam.fir.kt.txt | 4 +- .../caoWithAdaptationForSam.fir.txt | 12 +++-- .../samConversionsWithSmartCasts.fir.kt.txt | 12 ++--- .../samConversionsWithSmartCasts.fir.txt | 33 ++++++++------ 10 files changed, 96 insertions(+), 49 deletions(-) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index 35353eda734..df570b58712 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -22,6 +22,8 @@ import org.jetbrains.kotlin.fir.resolve.calls.isFunctional import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType import org.jetbrains.kotlin.fir.resolve.inference.isKMutableProperty +import org.jetbrains.kotlin.fir.resolve.resolveFunctionTypeIfSamInterface +import org.jetbrains.kotlin.fir.resolve.toFirRegularClass import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.symbols.impl.* @@ -618,8 +620,12 @@ class CallAndReferenceGenerator( if (expectedType is ConeTypeParameterType || expectedType.isBuiltinFunctionalType(session)) { return false } - // On the other hand, the actual type should be a functional type. - return argument.isFunctional(session) + // On the other hand, the actual type should be either a functional type or a subtype of a class that has a contributed `invoke`. + val lookupTag = parameter.returnTypeRef.coneTypeSafe()?.lookupTag + val firRegularClass = lookupTag?.toFirRegularClass(session) + // TODO: cache SAM interface to resolved function type, as [SamResolution] does? + val expectedFunctionType = firRegularClass?.resolveFunctionTypeIfSamInterface(session, scopeSession) + return argument.isFunctional(session, scopeSession, expectedFunctionType) } private fun IrExpression.applyAssigningArrayElementsToVarargInNamedForm( diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/LookupTagUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/LookupTagUtils.kt index 3ed79836a58..14f6da469f7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/LookupTagUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/LookupTagUtils.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.resolve import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag @@ -32,6 +33,9 @@ fun ConeClassLikeLookupTag.toSymbol(useSiteSession: FirSession): FirClassLikeSym } } +fun ConeClassLikeLookupTag.toFirRegularClass(session: FirSession): FirRegularClass? = + session.firSymbolProvider.getSymbolByLookupTag(this)?.fir as? FirRegularClass + @OptIn(LookupTagInternals::class) fun ConeClassLikeLookupTagImpl.bindSymbolToLookupTag(session: FirSession, symbol: FirClassLikeSymbol<*>?) { boundSymbol = OneElementWeakMap(session, symbol) 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 0b8947f2440..86ec79b1a28 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 @@ -70,11 +70,7 @@ class FirSamResolverImpl( } private fun getFunctionTypeForPossibleSamType(type: ConeClassLikeType): ConeLookupTagBasedType? { - val firRegularClass = - firSession.firSymbolProvider - .getSymbolByLookupTag(type.lookupTag) - ?.fir as? FirRegularClass - ?: return null + val firRegularClass = type.lookupTag.toFirRegularClass(firSession) ?: return null val unsubstitutedFunctionType = resolveFunctionTypeIfSamInterface(firRegularClass) ?: return null @@ -216,11 +212,7 @@ class FirSamResolverImpl( private fun resolveFunctionTypeIfSamInterface(firRegularClass: FirRegularClass): ConeLookupTagBasedType? { return resolvedFunctionType.getOrPut(firRegularClass) { - if (!firRegularClass.status.isFun) return@getOrPut NULL_STUB - val abstractMethod = firRegularClass.getSingleAbstractMethodOrNull(firSession, scopeSession) ?: return@getOrPut NULL_STUB - // TODO: val shouldConvertFirstParameterToDescriptor = samWithReceiverResolvers.any { it.shouldConvertFirstSamParameterToReceiver(abstractMethod) } - - abstractMethod.getFunctionTypeForAbstractMethod() + firRegularClass.resolveFunctionTypeIfSamInterface(firSession, scopeSession) ?: NULL_STUB } as? ConeLookupTagBasedType } @@ -230,6 +222,17 @@ class FirSamResolverImpl( } } +fun FirRegularClass.resolveFunctionTypeIfSamInterface( + session: FirSession, + scopeSession: ScopeSession +): ConeLookupTagBasedType? { + if (!this.status.isFun) return null + val abstractMethod = getSingleAbstractMethodOrNull(session, scopeSession) ?: return null + // TODO: val shouldConvertFirstParameterToDescriptor = samWithReceiverResolvers.any { it.shouldConvertFirstSamParameterToReceiver(abstractMethod) } + + return abstractMethod.getFunctionTypeForAbstractMethod() +} + private fun FirRegularClass.getSingleAbstractMethodOrNull( session: FirSession, scopeSession: ScopeSession, 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 25593e0f709..9c62316d0e6 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 @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition import org.jetbrains.kotlin.types.model.CaptureStatus +import org.jetbrains.kotlin.utils.addToStdlib.runIf fun Candidate.resolveArgumentExpression( csBuilder: ConstraintSystemBuilder, @@ -336,10 +337,9 @@ internal fun Candidate.resolveArgument( sink: CheckerSink, context: ResolutionContext ) { - argument.resultType.ensureResolvedTypeDeclaration(context.session) - val expectedType = prepareExpectedType(context.session, argument, parameter, context) + val expectedType = prepareExpectedType(context.session, context.bodyResolveComponents.scopeSession, argument, parameter, context) resolveArgumentExpression( this.system.getBuilder(), argument, @@ -354,18 +354,20 @@ internal fun Candidate.resolveArgument( private fun Candidate.prepareExpectedType( session: FirSession, + scopeSession: ScopeSession, argument: FirExpression, parameter: FirValueParameter?, context: ResolutionContext ): ConeKotlinType? { if (parameter == null) return null val basicExpectedType = argument.getExpectedTypeForSAMConversion(parameter/*, LanguageVersionSettings*/) - val expectedType = getExpectedTypeWithSAMConversion(session, argument, basicExpectedType, context) ?: basicExpectedType + val expectedType = getExpectedTypeWithSAMConversion(session, scopeSession, argument, basicExpectedType, context) ?: basicExpectedType return this.substitutor.substituteOrSelf(expectedType) } private fun Candidate.getExpectedTypeWithSAMConversion( session: FirSession, + scopeSession: ScopeSession, argument: FirExpression, candidateExpectedType: ConeKotlinType, context: ResolutionContext @@ -375,20 +377,43 @@ private fun Candidate.getExpectedTypeWithSAMConversion( val firFunction = symbol.fir as? FirFunction<*> ?: return null if (!context.bodyResolveComponents.samResolver.shouldRunSamConversionForFunction(firFunction)) return null - if (!argument.isFunctional(session)) return null - // TODO: resolvedCall.registerArgumentWithSamConversion(argument, SamConversionDescription(convertedTypeByOriginal, convertedTypeByCandidate!!)) - return context.bodyResolveComponents.samResolver.getFunctionTypeForPossibleSamType(candidateExpectedType).apply { - usesSAM = true + val expectedFunctionType = context.bodyResolveComponents.samResolver.getFunctionTypeForPossibleSamType(candidateExpectedType) + return runIf(argument.isFunctional(session, scopeSession, expectedFunctionType)) { + expectedFunctionType.apply { + // Even though the `expectedFunctionalType` could be `null`, we should mark the flag to indicate that the argument is a + // functional type. That will help avoid ambiguous `invoke` resolutions. See KT-39824 + usesSAM = true + } } } -fun FirExpression.isFunctional(session: FirSession): Boolean = +fun FirExpression.isFunctional( + session: FirSession, + scopeSession: ScopeSession, + expectedFunctionType: ConeKotlinType?, +): Boolean { when ((this as? FirWrappedArgumentExpression)?.expression ?: this) { - is FirAnonymousFunction, is FirCallableReferenceAccess -> true - else -> typeRef.coneTypeSafe()?.isBuiltinFunctionalType(session) == true + is FirAnonymousFunction, is FirCallableReferenceAccess -> return true + else -> { + // Either a functional type or a subtype of a class that has a contributed `invoke`. + val coneType = typeRef.coneTypeSafe() ?: return false + if (coneType.isBuiltinFunctionalType(session)) { + return true + } + val classLikeExpectedFunctionType = expectedFunctionType as? ConeClassLikeType + if (classLikeExpectedFunctionType == null || coneType is ConeIntegerLiteralType) { + return false + } + val invokeSymbol = + coneType.findContributedInvokeSymbol( + session, scopeSession, classLikeExpectedFunctionType, shouldCalculateReturnTypesOfFakeOverrides = false + ) + return invokeSymbol != null + } } +} fun FirExpression.getExpectedTypeForSAMConversion( parameter: FirValueParameter/*, languageVersionSettings: LanguageVersionSettings*/ diff --git a/compiler/testData/codegen/box/funInterface/intersectionTypeToFunInterfaceConversion.kt b/compiler/testData/codegen/box/funInterface/intersectionTypeToFunInterfaceConversion.kt index 4d517beeff1..559237c5ce5 100644 --- a/compiler/testData/codegen/box/funInterface/intersectionTypeToFunInterfaceConversion.kt +++ b/compiler/testData/codegen/box/funInterface/intersectionTypeToFunInterfaceConversion.kt @@ -1,6 +1,5 @@ // DONT_TARGET_EXACT_BACKEND: WASM // WASM_MUTE_REASON: SAM_CONVERSIONS -// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND: JS, JS_IR // IGNORE_BACKEND: JS_IR_ES6 diff --git a/compiler/testData/codegen/box/funInterface/subtypeOfFunctionalTypeToFunInterfaceConversion.kt b/compiler/testData/codegen/box/funInterface/subtypeOfFunctionalTypeToFunInterfaceConversion.kt index ab655dd96a2..514727ebe19 100644 --- a/compiler/testData/codegen/box/funInterface/subtypeOfFunctionalTypeToFunInterfaceConversion.kt +++ b/compiler/testData/codegen/box/funInterface/subtypeOfFunctionalTypeToFunInterfaceConversion.kt @@ -1,6 +1,5 @@ // DONT_TARGET_EXACT_BACKEND: WASM // WASM_MUTE_REASON: SAM_CONVERSIONS -// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND: JS, JS_IR // IGNORE_BACKEND: JS_IR_ES6 diff --git a/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.kt.txt b/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.kt.txt index 16b3427c296..2598c69ea47 100644 --- a/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.kt.txt +++ b/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.kt.txt @@ -64,7 +64,7 @@ fun test4(fn: Function1) { fn is IFoo -> { // BLOCK val <>: A = A val <>: IFoo = fn /*as IFoo */ - <>.set(i = <>, newValue = <>.get(i = <>).plus(other = 1)) + <>.set(i = <> /*-> IFoo */, newValue = <>.get(i = <> /*-> IFoo */).plus(other = 1)) } } } @@ -84,7 +84,7 @@ fun test6(a: Any) { { // BLOCK val <>: A = A val <>: Function1 = a /*as Function1 */ - <>.set(i = <>, newValue = <>.get(i = <>).plus(other = 1)) + <>.set(i = <> /*-> IFoo */, newValue = <>.get(i = <> /*-> IFoo */).plus(other = 1)) } } diff --git a/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.txt b/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.txt index d1943517037..b74efa16760 100644 --- a/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.txt +++ b/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.txt @@ -141,11 +141,13 @@ FILE fqName: fileName:/caoWithAdaptationForSam.kt GET_VAR 'fn: kotlin.Function1 declared in .test4' type=kotlin.Function1 origin=null CALL 'public final fun set (i: .IFoo, newValue: kotlin.Int): kotlin.Unit [operator] declared in ' type=kotlin.Unit origin=null $receiver: GET_VAR 'val tmp_2: .A [val] declared in .test4' type=.A origin=null - i: GET_VAR 'val tmp_3: .IFoo [val] declared in .test4' type=.IFoo origin=null + i: TYPE_OP type=.IFoo origin=SAM_CONVERSION typeOperand=.IFoo + GET_VAR 'val tmp_3: .IFoo [val] declared in .test4' type=.IFoo origin=null newValue: CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null $this: CALL 'public final fun get (i: .IFoo): kotlin.Int [operator] declared in ' type=kotlin.Int origin=null $receiver: GET_VAR 'val tmp_2: .A [val] declared in .test4' type=.A origin=null - i: GET_VAR 'val tmp_3: .IFoo [val] declared in .test4' type=.IFoo origin=null + i: TYPE_OP type=.IFoo origin=SAM_CONVERSION typeOperand=.IFoo + GET_VAR 'val tmp_3: .IFoo [val] declared in .test4' type=.IFoo origin=null other: CONST Int type=kotlin.Int value=1 FUN name:test5 visibility:public modality:FINAL <> (a:kotlin.Any) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Any @@ -187,9 +189,11 @@ FILE fqName: fileName:/caoWithAdaptationForSam.kt GET_VAR 'a: kotlin.Any declared in .test6' type=kotlin.Any origin=null CALL 'public final fun set (i: .IFoo, newValue: kotlin.Int): kotlin.Unit [operator] declared in ' type=kotlin.Unit origin=null $receiver: GET_VAR 'val tmp_6: .A [val] declared in .test6' type=.A origin=null - i: GET_VAR 'val tmp_7: kotlin.Function1 [val] declared in .test6' type=kotlin.Function1 origin=null + i: TYPE_OP type=.IFoo origin=SAM_CONVERSION typeOperand=.IFoo + GET_VAR 'val tmp_7: kotlin.Function1 [val] declared in .test6' type=kotlin.Function1 origin=null newValue: CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null $this: CALL 'public final fun get (i: .IFoo): kotlin.Int [operator] declared in ' type=kotlin.Int origin=null $receiver: GET_VAR 'val tmp_6: .A [val] declared in .test6' type=.A origin=null - i: GET_VAR 'val tmp_7: kotlin.Function1 [val] declared in .test6' type=kotlin.Function1 origin=null + i: TYPE_OP type=.IFoo origin=SAM_CONVERSION typeOperand=.IFoo + GET_VAR 'val tmp_7: kotlin.Function1 [val] declared in .test6' type=kotlin.Function1 origin=null other: CONST Int type=kotlin.Int value=1 diff --git a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.kt.txt b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.kt.txt index b769f5e4a61..91c55e89d1e 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.kt.txt +++ b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.kt.txt @@ -14,29 +14,29 @@ fun run2(r1: KRunnable, r2: KRunnable) { } fun test0(a: T) where T : KRunnable, T : Function0 { - run1(r = a) + run1(r = a /*-> KRunnable */) } fun test1(a: Function0) { when { - a is KRunnable -> run1(r = a /*as KRunnable */) + a is KRunnable -> run1(r = a /*as KRunnable */ /*-> KRunnable */) } } fun test2(a: KRunnable) { a as Function0 /*~> Unit */ - run1(r = a /*as Function0 */) + run1(r = a /*as Function0 */ /*-> KRunnable */) } fun test3(a: Function0) { when { - a is KRunnable -> run2(r1 = a /*as KRunnable */, r2 = a /*as KRunnable */) + a is KRunnable -> run2(r1 = a /*as KRunnable */ /*-> KRunnable */, r2 = a /*as KRunnable */ /*-> KRunnable */) } } fun test4(a: Function0, b: Function0) { when { - a is KRunnable -> run2(r1 = a /*as KRunnable */, r2 = b /*-> KRunnable */) + a is KRunnable -> run2(r1 = a /*as KRunnable */ /*-> KRunnable */, r2 = b /*-> KRunnable */) } } @@ -50,7 +50,7 @@ fun test5x(a: Any) { when { a is KRunnable -> { // BLOCK a /*as KRunnable */ as Function0 /*~> Unit */ - run1(r = a /*as KRunnable */) + run1(r = a /*as KRunnable */ /*-> KRunnable */) } } } diff --git a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.txt b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.txt index 86e5d5b4cdb..d89270747af 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.txt +++ b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.txt @@ -34,7 +34,8 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt VALUE_PARAMETER name:a index:0 type:T of .test0 BLOCK_BODY CALL 'public final fun run1 (r: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null - r: GET_VAR 'a: T of .test0 declared in .test0' type=T of .test0 origin=null + r: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable + GET_VAR 'a: T of .test0 declared in .test0' type=T of .test0 origin=null FUN name:test1 visibility:public modality:FINAL <> (a:kotlin.Function0) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Function0 BLOCK_BODY @@ -43,8 +44,9 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=.KRunnable GET_VAR 'a: kotlin.Function0 declared in .test1' type=kotlin.Function0 origin=null then: CALL 'public final fun run1 (r: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null - r: TYPE_OP type=.KRunnable origin=IMPLICIT_CAST typeOperand=.KRunnable - GET_VAR 'a: kotlin.Function0 declared in .test1' type=kotlin.Function0 origin=null + r: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable + TYPE_OP type=.KRunnable origin=IMPLICIT_CAST typeOperand=.KRunnable + GET_VAR 'a: kotlin.Function0 declared in .test1' type=kotlin.Function0 origin=null FUN name:test2 visibility:public modality:FINAL <> (a:.KRunnable) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:.KRunnable BLOCK_BODY @@ -52,8 +54,9 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt TYPE_OP type=kotlin.Function0 origin=CAST typeOperand=kotlin.Function0 GET_VAR 'a: .KRunnable declared in .test2' type=.KRunnable origin=null CALL 'public final fun run1 (r: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null - r: TYPE_OP type=kotlin.Function0 origin=IMPLICIT_CAST typeOperand=kotlin.Function0 - GET_VAR 'a: .KRunnable declared in .test2' type=.KRunnable origin=null + r: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable + TYPE_OP type=kotlin.Function0 origin=IMPLICIT_CAST typeOperand=kotlin.Function0 + GET_VAR 'a: .KRunnable declared in .test2' type=.KRunnable origin=null FUN name:test3 visibility:public modality:FINAL <> (a:kotlin.Function0) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Function0 BLOCK_BODY @@ -62,10 +65,12 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=.KRunnable GET_VAR 'a: kotlin.Function0 declared in .test3' type=kotlin.Function0 origin=null then: CALL 'public final fun run2 (r1: .KRunnable, r2: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null - r1: TYPE_OP type=.KRunnable origin=IMPLICIT_CAST typeOperand=.KRunnable - GET_VAR 'a: kotlin.Function0 declared in .test3' type=kotlin.Function0 origin=null - r2: TYPE_OP type=.KRunnable origin=IMPLICIT_CAST typeOperand=.KRunnable - GET_VAR 'a: kotlin.Function0 declared in .test3' type=kotlin.Function0 origin=null + r1: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable + TYPE_OP type=.KRunnable origin=IMPLICIT_CAST typeOperand=.KRunnable + GET_VAR 'a: kotlin.Function0 declared in .test3' type=kotlin.Function0 origin=null + r2: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable + TYPE_OP type=.KRunnable origin=IMPLICIT_CAST typeOperand=.KRunnable + GET_VAR 'a: kotlin.Function0 declared in .test3' type=kotlin.Function0 origin=null FUN name:test4 visibility:public modality:FINAL <> (a:kotlin.Function0, b:kotlin.Function0) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Function0 VALUE_PARAMETER name:b index:1 type:kotlin.Function0 @@ -75,8 +80,9 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=.KRunnable GET_VAR 'a: kotlin.Function0 declared in .test4' type=kotlin.Function0 origin=null then: CALL 'public final fun run2 (r1: .KRunnable, r2: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null - r1: TYPE_OP type=.KRunnable origin=IMPLICIT_CAST typeOperand=.KRunnable - GET_VAR 'a: kotlin.Function0 declared in .test4' type=kotlin.Function0 origin=null + r1: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable + TYPE_OP type=.KRunnable origin=IMPLICIT_CAST typeOperand=.KRunnable + GET_VAR 'a: kotlin.Function0 declared in .test4' type=kotlin.Function0 origin=null r2: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable 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 @@ -102,8 +108,9 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt TYPE_OP type=.KRunnable origin=IMPLICIT_CAST typeOperand=.KRunnable GET_VAR 'a: kotlin.Any declared in .test5x' type=kotlin.Any origin=null CALL 'public final fun run1 (r: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null - r: TYPE_OP type=.KRunnable origin=IMPLICIT_CAST typeOperand=.KRunnable - GET_VAR 'a: kotlin.Any declared in .test5x' type=kotlin.Any origin=null + r: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable + TYPE_OP type=.KRunnable origin=IMPLICIT_CAST typeOperand=.KRunnable + GET_VAR 'a: kotlin.Any declared in .test5x' type=kotlin.Any origin=null FUN name:test6 visibility:public modality:FINAL <> (a:kotlin.Any) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Any BLOCK_BODY