[FIR] Prefer the non-smartcasted receiver in resolution if it's visible

Previously, we would ignore the candidate with the non-smartcasted
receiver if they have the same symbol.
Now we prefer them when they're visible or when the smart cast changes
the nullability.
This commit is contained in:
Kirill Rakhman
2023-12-18 17:32:28 +01:00
committed by Space Team
parent 5186ba80e2
commit ad2689ecbf
15 changed files with 105 additions and 251 deletions
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.resolve.calls.CandidateFactory
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
internal class CandidateFactoriesAndCollectors(
// Common calls
@@ -85,8 +86,8 @@ private class TowerScopeLevelProcessor(
scope: FirScope,
objectsByName: Boolean,
isFromOriginalTypeInPresenceOfSmartCast: Boolean,
) {
resultCollector.consumeCandidate(
): CandidateApplicability {
return resultCollector.consumeCandidate(
group, candidateFactory.createCandidate(
callInfo,
symbol,
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef
import org.jetbrains.kotlin.fir.utils.exceptions.withConeTypeEntry
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.HidesMembers
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
@@ -59,7 +60,7 @@ abstract class TowerScopeLevel {
scope: FirScope,
objectsByName: Boolean = false,
isFromOriginalTypeInPresenceOfSmartCast: Boolean = false,
)
): CandidateApplicability
}
}
@@ -86,14 +87,13 @@ class MemberScopeTowerLevel(
val scope = dispatchReceiverValue.scope(session, scopeSession) ?: return ProcessResult.SCOPE_EMPTY
var (empty, candidates) = scope.collectCandidates(processScopeMembers)
val scopeWithoutSmartcast = getOriginalReceiverExpressionIfStableSmartCast()
?.resolvedType
?.scope(
session,
scopeSession,
bodyResolveComponents.returnTypeCalculator.callableCopyTypeCalculator,
requiredMembersPhase = FirResolvePhase.STATUS,
)
val receiverTypeWithoutSmartCast = getOriginalReceiverExpressionIfStableSmartCast()?.resolvedType
val scopeWithoutSmartcast = receiverTypeWithoutSmartCast?.scope(
session,
scopeSession,
bodyResolveComponents.returnTypeCalculator.callableCopyTypeCalculator,
requiredMembersPhase = FirResolvePhase.STATUS,
)
if (scopeWithoutSmartcast == null) {
consumeCandidates(
@@ -107,12 +107,22 @@ class MemberScopeTowerLevel(
scopeWithoutSmartcast.collectCandidates(processScopeMembers).let { (isEmpty, originalCandidates) ->
empty = empty && isEmpty
for (originalCandidate in originalCandidates) {
map[originalCandidate.member] = MemberFromSmartcastScope(originalCandidate, cameFromSmartcast = false)
map[originalCandidate.member] = MemberFromSmartcastScope(originalCandidate, DispatchReceiverToUse.UnwrapSmartcast)
}
}
for (candidateFromSmartCast in candidates) {
map[candidateFromSmartCast.member] = MemberFromSmartcastScope(candidateFromSmartCast, cameFromSmartcast = true)
val existing = map[candidateFromSmartCast.member]
// If both scopes return the same symbol, we want to prefer the candidate from the original scope without smartcast
// with two exceptions:
// - When the smart-casted type is always null, we want to return it and report UNSAFE_CALL.
// - When the original type can be null, in this case the smart-case either makes it not-null or the call is red anyway.
if (existing == null || dispatchReceiverValue.type.isNullableNothing || receiverTypeWithoutSmartCast.canBeNull) {
map[candidateFromSmartCast.member] = MemberFromSmartcastScope(candidateFromSmartCast, DispatchReceiverToUse.SmartcastWithoutUnwrapping)
} else {
existing.dispatchReceiverToUse = DispatchReceiverToUse.SmartcastIfUnwrappedInvisible
}
}
consumeCandidates(
@@ -170,9 +180,15 @@ class MemberScopeTowerLevel(
return if (empty) ProcessResult.SCOPE_EMPTY else ProcessResult.FOUND
}
private data class MemberFromSmartcastScope<T : FirCallableSymbol<*>>(
private enum class DispatchReceiverToUse(val unwrapSmartcast: Boolean) {
UnwrapSmartcast(true),
SmartcastWithoutUnwrapping(false),
SmartcastIfUnwrappedInvisible(true),
}
private class MemberFromSmartcastScope<T : FirCallableSymbol<*>>(
val memberWithBaseScope: MemberWithBaseScope<T>,
val cameFromSmartcast: Boolean
var dispatchReceiverToUse: DispatchReceiverToUse,
)
private fun <T : FirCallableSymbol<*>> FirTypeScope.collectCandidates(
@@ -208,13 +224,11 @@ class MemberScopeTowerLevel(
?: candidatesWithSmartcast?.values?.map { it.memberWithBaseScope }
?: error("candidatesWithoutSmartcast or candidatesWithSmartcast should be not null")
for (candidateWithScope in candidates) {
val (candidate, scope) = candidateWithScope
for ((candidate, scope) in candidates) {
if (candidate.hasConsistentExtensionReceiver(givenExtensionReceiverOptions)) {
val isFromOriginalTypeInPresenceOfSmartCast = candidatesWithSmartcast != null &&
!candidatesWithSmartcast.getValue(candidateWithScope.member).cameFromSmartcast
val dispatchReceiverToUse = when {
val dispatchReceiverToUse = candidatesWithSmartcast?.getValue(candidate)?.dispatchReceiverToUse
val isFromOriginalTypeInPresenceOfSmartCast = dispatchReceiverToUse?.unwrapSmartcast == true
val dispatchReceiver = when {
isFromOriginalTypeInPresenceOfSmartCast ->
getOriginalReceiverExpressionIfStableSmartCast()
// For a chain inference stub in dispatch receiver, we have to provide an explicit cast to Any
@@ -229,13 +243,23 @@ class MemberScopeTowerLevel(
else -> dispatchReceiverValue.receiverExpression
}
output.consumeCandidate(
val applicability = output.consumeCandidate(
candidate,
dispatchReceiverToUse,
dispatchReceiver,
givenExtensionReceiverOptions,
scope,
isFromOriginalTypeInPresenceOfSmartCast = isFromOriginalTypeInPresenceOfSmartCast
)
if (applicability == CandidateApplicability.K2_VISIBILITY_ERROR && dispatchReceiverToUse == DispatchReceiverToUse.SmartcastIfUnwrappedInvisible) {
output.consumeCandidate(
candidate,
dispatchReceiverValue.receiverExpression,
givenExtensionReceiverOptions,
scope,
isFromOriginalTypeInPresenceOfSmartCast = false
)
}
}
}
}
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.resolve.calls.inference.model.InferredEmptyIntersection
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.types.Variance
@@ -140,6 +141,18 @@ class FirCallCompletionResultsWriterTransformer(
replaceCalleeReference(calleeReference.toResolvedReference())
replaceDispatchReceiver(dispatchReceiver)
replaceExtensionReceiver(extensionReceiver)
// If the explicit receiver is a smartcast expression, we can choose to use the unwrapped expression as dispatch receiver.
// To maintain the invariant
// explicitReceiver != null => explicitReceiver == dispatchReceiver || explicitReceiver == extensionReceiver
// we update the explicit receiver here.
// We only do this if the candidate is successful, otherwise, we can lose the explicit receiver node in red code like
// fun f(s: String, action: (String.() -> Unit)?) {
// s.action?.let { it() }
//}
if (subCandidate.explicitReceiverKind == ExplicitReceiverKind.DISPATCH_RECEIVER && subCandidate.applicability.isSuccess) {
replaceExplicitReceiver(dispatchReceiver)
}
}
qualifiedAccessExpression.replaceContextReceiverArguments(subCandidate.contextReceiverArguments())
@@ -29,12 +29,10 @@ FILE fqName:<root> fileName:/propertyInitializationAfterSmartCast.kt
TYPE_OP type=<root>.Derived_1 origin=CAST typeOperand=<root>.Derived_1
GET_VAR '<this>: <root>.Base_1 declared in <root>.Base_1' type=<root>.Base_1 origin=null
SET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:x type:kotlin.Any? visibility:private [final]' type=kotlin.Unit origin=null
receiver: TYPE_OP type=<root>.Derived_1 origin=IMPLICIT_CAST typeOperand=<root>.Derived_1
GET_VAR '<this>: <root>.Base_1 declared in <root>.Base_1' type=<root>.Base_1 origin=null
receiver: GET_VAR '<this>: <root>.Base_1 declared in <root>.Base_1' type=<root>.Base_1 origin=null
value: CONST String type=kotlin.String value="O"
SET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:y type:kotlin.Any? visibility:private [final]' type=kotlin.Unit origin=null
receiver: TYPE_OP type=<root>.Derived_1 origin=IMPLICIT_CAST typeOperand=<root>.Derived_1
GET_VAR '<this>: <root>.Base_1 declared in <root>.Base_1' type=<root>.Base_1 origin=null
receiver: GET_VAR '<this>: <root>.Base_1 declared in <root>.Base_1' type=<root>.Base_1 origin=null
value: CONST String type=kotlin.String value="O"
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
@@ -114,12 +112,10 @@ FILE fqName:<root> fileName:/propertyInitializationAfterSmartCast.kt
TYPE_OP type=<root>.Impl_2 origin=CAST typeOperand=<root>.Impl_2
GET_VAR '<this>: <root>.Base_2 declared in <root>.Base_2' type=<root>.Base_2 origin=null
SET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:x type:kotlin.Any? visibility:private [final]' type=kotlin.Unit origin=null
receiver: TYPE_OP type=<root>.Impl_2 origin=IMPLICIT_CAST typeOperand=<root>.Impl_2
GET_VAR '<this>: <root>.Base_2 declared in <root>.Base_2' type=<root>.Base_2 origin=null
receiver: GET_VAR '<this>: <root>.Base_2 declared in <root>.Base_2' type=<root>.Base_2 origin=null
value: CONST String type=kotlin.String value="K"
SET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:y type:kotlin.Any? visibility:private [final]' type=kotlin.Unit origin=null
receiver: TYPE_OP type=<root>.Impl_2 origin=IMPLICIT_CAST typeOperand=<root>.Impl_2
GET_VAR '<this>: <root>.Base_2 declared in <root>.Base_2' type=<root>.Base_2 origin=null
receiver: GET_VAR '<this>: <root>.Base_2 declared in <root>.Base_2' type=<root>.Base_2 origin=null
value: CONST String type=kotlin.String value="K"
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
@@ -1,5 +1,3 @@
// IGNORE_BACKEND_K2: JVM_IR
open class SuperFoo {
public fun bar() {
if (this is Foo) {
@@ -27,4 +27,21 @@ class Derived : Base() {
x.y = x.y + 1
}
}
protected fun baz2(x: Base?) {
x.<!INVISIBLE_REFERENCE!>foo<!>()
x.<!INVISIBLE_REFERENCE!>bar<!>()
x.<!INVISIBLE_REFERENCE!>x<!> = x.<!INVISIBLE_REFERENCE!>x<!> + 1
x.<!INVISIBLE_SETTER!>y<!> = x<!UNSAFE_CALL!>.<!>y + 1
if (x is Derived) {
x.foo()
x.bar()
x.baz(x)
x.x = x.x + 1
x.y = x.y + 1
}
}
}
@@ -27,4 +27,21 @@ class Derived : Base() {
<!INVISIBLE_SETTER!>x.y<!> = x.y + 1
}
}
protected fun baz2(x: Base?) {
x<!UNSAFE_CALL!>.<!><!INVISIBLE_MEMBER!>foo<!>()
x<!UNSAFE_CALL!>.<!><!INVISIBLE_MEMBER!>bar<!>()
x<!UNSAFE_CALL!>.<!><!INVISIBLE_MEMBER!>x<!> = x<!UNSAFE_CALL!>.<!><!INVISIBLE_MEMBER!>x<!> + 1
<!INVISIBLE_SETTER!>x<!UNSAFE_CALL!>.<!>y<!> = x<!UNSAFE_CALL!>.<!>y + 1
if (x is Derived) {
<!DEBUG_INFO_SMARTCAST!>x<!>.foo()
<!DEBUG_INFO_SMARTCAST!>x<!>.bar()
<!DEBUG_INFO_SMARTCAST!>x<!>.baz(<!DEBUG_INFO_SMARTCAST!>x<!>)
<!DEBUG_INFO_SMARTCAST!>x<!>.x = <!DEBUG_INFO_SMARTCAST!>x<!>.x + 1
<!DEBUG_INFO_SMARTCAST!>x<!>.y = <!DEBUG_INFO_SMARTCAST!>x<!>.y + 1
}
}
}
@@ -1,24 +0,0 @@
package
public open class Base {
public constructor Base()
protected open var x: kotlin.Int
public open var y: kotlin.Int
protected open fun bar(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
protected open fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Derived : Base {
public constructor Derived()
protected open override /*1*/ /*fake_override*/ var x: kotlin.Int
public open override /*1*/ /*fake_override*/ var y: kotlin.Int
protected open override /*1*/ fun bar(): kotlin.Unit
protected final fun baz(/*0*/ x: Base): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
protected open override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -41,10 +41,10 @@ fun testWithSubject_ok(x: Boolean, y: Boolean?, any: Any, z: Boolean) {
x !in x -> {}
x is String -> {}
x !is String -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!><<!> x -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!>><!> x -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!><=<!> x -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!>>=<!> x -> {}
x < x -> {}
x > x -> {}
x <= x -> {}
x >= x -> {}
x == x -> {}
x != x -> {}
<!DEPRECATED_IDENTITY_EQUALS!>x === x<!> -> {}
@@ -1,65 +0,0 @@
FILE fqName:<root> fileName:/smartCastInValInitialization.kt
CLASS CLASS name:RootBus modality:FINAL visibility:public superTypes:[<root>.MessageBusImpl]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.RootBus
CONSTRUCTOR visibility:public <> () returnType:<root>.RootBus [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.MessageBusImpl'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:RootBus modality:FINAL visibility:public superTypes:[<root>.MessageBusImpl]'
PROPERTY FAKE_OVERRIDE name:parentBus visibility:public modality:FINAL [fake_override,val]
overridden:
public final parentBus: kotlin.Any?
FUN FAKE_OVERRIDE name:<get-parentBus> visibility:public modality:FINAL <> ($this:<root>.MessageBusImpl) returnType:kotlin.Any? [fake_override]
correspondingProperty: PROPERTY FAKE_OVERRIDE name:parentBus visibility:public modality:FINAL [fake_override,val]
overridden:
public final fun <get-parentBus> (): kotlin.Any? declared in <root>.MessageBusImpl
$this: VALUE_PARAMETER name:<this> type:<root>.MessageBusImpl
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.MessageBusImpl
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in <root>.MessageBusImpl
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in <root>.MessageBusImpl
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
CLASS CLASS name:MessageBusImpl modality:OPEN visibility:public superTypes:[kotlin.Any]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.MessageBusImpl
CONSTRUCTOR visibility:public <> () returnType:<root>.MessageBusImpl [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in kotlin.Any'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:MessageBusImpl modality:OPEN visibility:public superTypes:[kotlin.Any]'
PROPERTY name:parentBus visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:parentBus type:kotlin.Any? visibility:private [final]
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-parentBus> visibility:public modality:FINAL <> ($this:<root>.MessageBusImpl) returnType:kotlin.Any?
correspondingProperty: PROPERTY name:parentBus visibility:public modality:FINAL [val]
$this: VALUE_PARAMETER name:<this> type:<root>.MessageBusImpl
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-parentBus> (): kotlin.Any? declared in <root>.MessageBusImpl'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:parentBus type:kotlin.Any? visibility:private [final]' type=kotlin.Any? origin=null
receiver: GET_VAR '<this>: <root>.MessageBusImpl declared in <root>.MessageBusImpl.<get-parentBus>' type=<root>.MessageBusImpl origin=null
ANONYMOUS_INITIALIZER isStatic=false
BLOCK_BODY
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
TYPE_OP type=<root>.RootBus origin=CAST typeOperand=<root>.RootBus
GET_VAR '<this>: <root>.MessageBusImpl declared in <root>.MessageBusImpl' type=<root>.MessageBusImpl origin=null
SET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:parentBus type:kotlin.Any? visibility:private [final]' type=kotlin.Unit origin=null
receiver: TYPE_OP type=<root>.RootBus origin=IMPLICIT_CAST typeOperand=<root>.RootBus
GET_VAR '<this>: <root>.MessageBusImpl declared in <root>.MessageBusImpl' type=<root>.MessageBusImpl origin=null
value: CONST Null type=kotlin.Nothing? value=null
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
@@ -1,25 +0,0 @@
class RootBus : MessageBusImpl {
constructor() /* primary */ {
super/*MessageBusImpl*/()
/* <init>() */
}
}
open class MessageBusImpl {
constructor() /* primary */ {
super/*Any*/()
/* <init>() */
}
val parentBus: Any?
get
init {
<this> as RootBus /*~> Unit */
<this> /*as RootBus */.#parentBus = null
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
class RootBus: MessageBusImpl()
open class MessageBusImpl {
@@ -1,76 +0,0 @@
FILE fqName:<root> fileName:/smartCastAside.kt
CLASS INTERFACE name:A modality:ABSTRACT visibility:public superTypes:[kotlin.Any]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.A
FUN name:foo visibility:public modality:ABSTRACT <> ($this:<root>.A) returnType:kotlin.Unit
$this: VALUE_PARAMETER name:<this> type:<root>.A
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
CLASS INTERFACE name:B modality:ABSTRACT visibility:public superTypes:[<root>.A]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.B
FUN name:bar visibility:public modality:ABSTRACT <> ($this:<root>.B) returnType:kotlin.Unit
$this: VALUE_PARAMETER name:<this> type:<root>.B
FUN FAKE_OVERRIDE name:foo visibility:public modality:ABSTRACT <> ($this:<root>.A) returnType:kotlin.Unit [fake_override]
overridden:
public abstract fun foo (): kotlin.Unit declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:<root>.A
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
CLASS INTERFACE name:C modality:ABSTRACT visibility:public superTypes:[<root>.A]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.C
FUN name:baz visibility:public modality:ABSTRACT <> ($this:<root>.C) returnType:kotlin.Unit
$this: VALUE_PARAMETER name:<this> type:<root>.C
FUN FAKE_OVERRIDE name:foo visibility:public modality:ABSTRACT <> ($this:<root>.A) returnType:kotlin.Unit [fake_override]
overridden:
public abstract fun foo (): kotlin.Unit declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:<root>.A
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN name:test visibility:public modality:FINAL <> (param:<root>.B) returnType:kotlin.Unit
VALUE_PARAMETER name:param index:0 type:<root>.B
BLOCK_BODY
WHEN type=kotlin.Unit origin=IF
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=<root>.C
GET_VAR 'param: <root>.B declared in <root>.test' type=<root>.B origin=null
then: BLOCK type=kotlin.Unit origin=null
CALL 'public abstract fun foo (): kotlin.Unit declared in <root>.A' type=kotlin.Unit origin=null
$this: TYPE_OP type=<root>.C origin=IMPLICIT_CAST typeOperand=<root>.C
GET_VAR 'param: <root>.B declared in <root>.test' type=<root>.B origin=null
CALL 'public abstract fun bar (): kotlin.Unit declared in <root>.B' type=kotlin.Unit origin=null
$this: GET_VAR 'param: <root>.B declared in <root>.test' type=<root>.B origin=null
CALL 'public abstract fun baz (): kotlin.Unit declared in <root>.C' type=kotlin.Unit origin=null
$this: TYPE_OP type=<root>.C origin=IMPLICIT_CAST typeOperand=<root>.C
GET_VAR 'param: <root>.B declared in <root>.test' type=<root>.B origin=null
@@ -1,24 +0,0 @@
interface A {
abstract fun foo()
}
interface B : A {
abstract fun bar()
}
interface C : A {
abstract fun baz()
}
fun test(param: B) {
when {
param is C -> { // BLOCK
param /*as C */.foo()
param.bar()
param /*as C */.baz()
}
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
interface A {
fun foo()
}