[FIR] Add implementation of intersection types to Fir type system

This commit is contained in:
Dmitriy Novozhilov
2019-07-29 09:59:33 +03:00
parent 1708a34eb8
commit 637fb55a7b
17 changed files with 350 additions and 26 deletions
@@ -173,4 +173,53 @@ class ConeDefinitelyNotNullType(val original: ConeKotlinType): ConeKotlinType(),
get() = original.typeArguments
override val nullability: ConeNullability
get() = ConeNullability.NOT_NULL
}
/*
* Contract of the intersection type: it is flat. It means that
* intersection type can not contains another intersection types
* inside it. To keep this contract construct new intersection types
* only via ConeTypeIntersector
*/
class ConeIntersectionType(
val constructor: ConeIntersectionTypeConstructor
) : ConeKotlinType(), SimpleTypeMarker {
override val typeArguments: Array<out ConeKotlinTypeProjection>
get() = emptyArray()
override val nullability: ConeNullability
get() = ConeNullability.NOT_NULL
val intersectedTypes: Collection<ConeKotlinType> get() = constructor.intersectedTypes
val statusMap: Map<ConeKotlinType, ConeIntersectionTypeConstructor.IntersectionStatus> get() = constructor.statusMap
}
class ConeIntersectionTypeConstructor(
val intersectedTypes: Collection<ConeKotlinType>,
val statusMap: Map<ConeKotlinType, IntersectionStatus>
) : TypeConstructorMarker {
val supertypes: Collection<ConeKotlinType> get() = intersectedTypes
/*
* IMPORTANT: use this method only for types from intersectedTypes
*/
fun getStatus(type: ConeKotlinType): IntersectionStatus {
return statusMap[type] ?: error("")
}
enum class IntersectionStatus {
FROM_INFERENCE,
FROM_SMARTCAST
}
}
fun ConeIntersectionTypeConstructor.mapTypes(func: (ConeKotlinType) -> ConeKotlinType): ConeIntersectionTypeConstructor {
val newStatusMap = mutableMapOf<ConeKotlinType, ConeIntersectionTypeConstructor.IntersectionStatus>()
val newTypes = intersectedTypes.map { type ->
val newType = func(type)
// TODO: what if some types squash? What status should we choose?
newStatusMap[newType] = getStatus(type)
newType
}
return ConeIntersectionTypeConstructor(newTypes, newStatusMap)
}
@@ -39,5 +39,12 @@ fun ConeKotlinType.render(): String {
append(">")
}
}
is ConeIntersectionType -> {
intersectedTypes.joinToString(
separator = " & ",
prefix = "it(",
postfix = ")"
)
}
} + nullabilitySuffix
}
@@ -599,6 +599,12 @@ class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver
}
}
private fun FlowContent.generate(intersectionType: ConeIntersectionType) {
+"("
generateList(intersectionType.intersectedTypes.toList(), " & ") { generate(it) }
+")"
}
private fun FlowContent.generate(type: ConeClassType) {
resolved {
symbolRef(type.lookupTag.toSymbol(session)) {
@@ -704,6 +710,7 @@ class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver
is ConeFlexibleType -> resolved { generate(type) }
is ConeCapturedType -> inlineUnsupported(type)
is ConeDefinitelyNotNullType -> inlineUnsupported(type)
is ConeIntersectionType -> resolved { generate(type) }
}
if (type.typeArguments.isNotEmpty()) {
+"<"
@@ -58,6 +58,10 @@ fun ConeKotlinType.toIrType(session: FirSession, declarationStorage: Fir2IrDecla
}
is ConeCapturedType -> TODO()
is ConeDefinitelyNotNullType -> TODO()
is ConeIntersectionType -> {
// TODO: add intersectionTypeApproximation
intersectedTypes.first().toIrType(session, declarationStorage)
}
}
}
@@ -164,6 +164,13 @@ fun <T : ConeKotlinType> T.withNullability(nullability: ConeNullability): T {
is ConeFlexibleType -> ConeFlexibleType(lowerBound.withNullability(nullability), upperBound.withNullability(nullability)) as T
is ConeTypeVariableType -> ConeTypeVariableType(nullability, lookupTag) as T
is ConeCapturedType -> ConeCapturedType(captureStatus, lowerType, nullability, constructor) as T
is ConeIntersectionType -> when (nullability) {
ConeNullability.NULLABLE -> ConeIntersectionType(constructor.mapTypes {
it.withNullability(nullability)
})
ConeNullability.UNKNOWN -> this // TODO: is that correct?
ConeNullability.NOT_NULL -> this
} as T
else -> error("sealed: ${this::class}")
}
}
@@ -38,6 +38,7 @@ fun ConeKotlinType.scope(useSiteSession: FirSession, scopeSession: ScopeSession)
)
}
is ConeFlexibleType -> lowerBound.scope(useSiteSession, scopeSession)
is ConeIntersectionType -> FirCompositeScope(intersectedTypes.mapNotNullTo(mutableListOf()) { it.scope(useSiteSession, scopeSession) })
else -> error("Failed type ${this}")
}
}
@@ -75,9 +75,23 @@ abstract class AbstractConeSubstitutor : ConeSubstitutor() {
is ConeFlexibleType -> this.substituteBounds()
is ConeCapturedType -> return null
is ConeDefinitelyNotNullType -> this.substituteOriginal()
is ConeIntersectionType -> this.substituteIntersectedTypes()
}
}
private fun ConeIntersectionType.substituteIntersectedTypes(): ConeIntersectionType? {
val substitutedTypes = ArrayList<ConeKotlinType>(intersectedTypes.size)
var somethingIsSubstituted = false
for (type in intersectedTypes) {
val substitutedType = substituteOrNull(type)?.also {
somethingIsSubstituted = true
} ?: type
substitutedTypes += substitutedType
}
if (!somethingIsSubstituted) return null
return ConeIntersectionType(ConeIntersectionTypeConstructor(substitutedTypes, statusMap))
}
private fun ConeDefinitelyNotNullType.substituteOriginal(): ConeDefinitelyNotNullType? {
TODO()
}
@@ -60,6 +60,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
is ConeCapturedType -> this
is ConeLookupTagBasedType -> this
is ConeDefinitelyNotNullType -> this
is ConeIntersectionType -> this
is ConeFlexibleType -> null
else -> error("Unknown simpleType: $this")
}
@@ -128,6 +129,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
typeArguments,
nullable
)
is ConeIntersectionType -> this.withNullability(ConeNullability.create(nullable))
else -> error("!")
}
}
@@ -139,6 +141,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
is ConeAbbreviatedType -> this.directExpansionType(session)?.typeConstructor()
?: ErrorTypeConstructor("Failed to expand alias: ${this}")
is ConeLookupTagBasedType -> this.lookupTag.toSymbol(session) ?: ErrorTypeConstructor("Unresolved: ${this.lookupTag}")
is ConeIntersectionType -> constructor
else -> error("?: ${this}")
}
@@ -199,7 +202,11 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
override fun TypeConstructorMarker.parametersCount(): Int {
//require(this is ConeSymbol)
return when (this) {
is ConeTypeParameterSymbol, is ConeCapturedTypeConstructor, is ErrorTypeConstructor, is ConeTypeVariableTypeConstructor -> 0
is ConeTypeParameterSymbol,
is ConeCapturedTypeConstructor,
is ErrorTypeConstructor,
is ConeTypeVariableTypeConstructor,
is ConeIntersectionTypeConstructor -> 0
is FirClassSymbol -> fir.typeParameters.size
is FirTypeAliasSymbol -> fir.typeParameters.size
else -> error("?!:10")
@@ -225,12 +232,13 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
is FirClassSymbol -> fir.superConeTypes
is FirTypeAliasSymbol -> listOfNotNull(fir.expandedConeType)
is ConeCapturedTypeConstructor -> supertypes!!
is ConeIntersectionTypeConstructor -> intersectedTypes
else -> error("?!:13")
}
}
override fun TypeConstructorMarker.isIntersection(): Boolean {
return false // TODO
return this is ConeIntersectionTypeConstructor
}
override fun TypeConstructorMarker.isClassTypeConstructor(): Boolean {
@@ -269,8 +277,9 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
override fun TypeConstructorMarker.isDenotable(): Boolean {
//TODO
return when (this) {
is ConeCapturedTypeConstructor -> false
is ConeTypeVariableTypeConstructor -> false
is ConeCapturedTypeConstructor,
is ConeTypeVariableTypeConstructor,
is ConeIntersectionTypeConstructor -> false
is ConeSymbol -> true
else -> true
}
@@ -351,6 +360,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
if (isError()) return false
if (this is ConeCapturedType) return true
if (this is ConeTypeVariableType) return false
if (this is ConeIntersectionType) return false
require(this is ConeLookupTagBasedType)
val typeConstructor = this.typeConstructor()
return typeConstructor is FirClassSymbol ||
@@ -370,11 +380,13 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
}
override fun intersectTypes(types: List<SimpleTypeMarker>): SimpleTypeMarker {
return types.first() // TODO: proper implementation
@Suppress("UNCHECKED_CAST")
return ConeTypeIntersector.intersectTypes(this, types as List<ConeKotlinType>) as SimpleTypeMarker
}
override fun intersectTypes(types: List<KotlinTypeMarker>): KotlinTypeMarker {
return types.first() // TODO: proper implementation
@Suppress("UNCHECKED_CAST")
return ConeTypeIntersector.intersectTypes(this, types as List<ConeKotlinType>)
}
private fun prepareClassLikeType(
@@ -0,0 +1,185 @@
/*
* 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.types
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.types.ConeIntersectionTypeConstructor.IntersectionStatus
import org.jetbrains.kotlin.types.AbstractTypeChecker
import java.util.*
import kotlin.collections.LinkedHashSet
object ConeTypeIntersector {
fun intersectTypes(context: ConeTypeContext, types: List<ConeKotlinType>): ConeKotlinType {
require(context is ConeInferenceContext)
return intersectTypes(context, types, types.map { IntersectionStatus.FROM_INFERENCE })
}
private fun intersectTypes(
context: ConeInferenceContext,
types: List<ConeKotlinType>,
intersectionStatus: List<IntersectionStatus>
): ConeKotlinType {
assert(types.size == intersectionStatus.size)
when (types.size) {
0 -> error("Expected some types")
1 -> return types.single()
}
val inputTypes = mutableListOf<ConeKotlinType>()
val statusMap = mutableMapOf<ConeKotlinType, IntersectionStatus>()
flatIntersectionTypes(types, intersectionStatus, inputTypes, statusMap)
/**
* resultNullability. Value description:
* ACCEPT_NULL means that all types marked nullable
*
* NOT_NULL means that there is one type which is subtype of Any => all types can be made definitely not null,
* making types definitely not null (not just not null) makes sense when we have intersection of type parameters like {T!! & S}
*
* UNKNOWN means, that we do not know, i.e. more precisely, all singleClassifier types marked nullable if any,
* and other types is captured types or type parameters without not-null upper bound. Example: `String? & T` such types we should leave as is.
*/
val resultNullability = inputTypes.fold(ResultNullability.START) { nullability, nextType ->
nullability.combine(nextType.type, context)
}
val inputTypesWithCorrectNullability = inputTypes.mapTo(LinkedHashSet()) {
if (resultNullability == ResultNullability.NOT_NULL) with(context) {
val resultType = it.makeDefinitelyNotNullOrNotNull() as ConeKotlinType
statusMap[resultType] = statusMap.remove(it)!!
resultType
} else it
}
return intersectTypesWithoutIntersectionType(context, inputTypesWithCorrectNullability, statusMap)
}
private fun intersectTypesWithoutIntersectionType(
context: ConeTypeContext,
inputTypes: Set<ConeKotlinType>,
statusMap: MutableMap<ConeKotlinType, IntersectionStatus>
): ConeKotlinType {
if (inputTypes.size == 1) return inputTypes.single().type
// Any and Nothing should leave
// Note that duplicates should be dropped because we have Set here.
val errorMessage = { "This collections cannot be empty! input types: ${inputTypes.joinToString()}" }
val filteredEqualTypes = filterTypes(inputTypes) { lower, upper ->
/*
* Here we drop types from intersection set for cases like that:
*
* interface A
* interface B : A
*
* type = (A & B & ...)
*
* We want to drop A from that set, because it's useless for type checking. But in case if
* A came from inference and B came from smartcast we want to safe both types in intersection
*/
isStrictSupertype(context, lower, upper) && statusMap[lower]!! <= statusMap[upper]!!
}
assert(filteredEqualTypes.isNotEmpty(), errorMessage)
// TODO
// IntegerLiteralTypeConstructor.findIntersectionType(filteredEqualTypes)?.let { return it }
val filteredSuperAndEqualTypes = filterTypes(filteredEqualTypes) { a, b ->
AbstractTypeChecker.equalTypes(context, a, b)
}
assert(filteredSuperAndEqualTypes.isNotEmpty(), errorMessage)
if (filteredSuperAndEqualTypes.size < 2) return filteredSuperAndEqualTypes.single()
val constructor = ConeIntersectionTypeConstructor(
filteredSuperAndEqualTypes,
filteredSuperAndEqualTypes.associateWithTo(mutableMapOf()) { statusMap[it]!! }
)
return ConeIntersectionType(constructor)
}
private fun filterTypes(
inputTypes: Collection<ConeKotlinType>,
predicate: (lower: ConeKotlinType, upper: ConeKotlinType) -> Boolean
): List<ConeKotlinType> {
val filteredTypes = ArrayList(inputTypes)
val iterator = filteredTypes.iterator()
while (iterator.hasNext()) {
val upper = iterator.next()
val shouldFilter = filteredTypes.any { lower -> lower !== upper && predicate(lower, upper) }
if (shouldFilter) iterator.remove()
}
return filteredTypes
}
private fun isStrictSupertype(context: ConeTypeContext, subtype: ConeKotlinType, supertype: ConeKotlinType): Boolean {
return with(AbstractTypeChecker) {
isSubtypeOf(context, subtype, supertype) && !isSubtypeOf(context, supertype, subtype)
}
}
@Suppress("NOTHING_TO_INLINE")
private inline fun MutableMap<ConeKotlinType, IntersectionStatus>.updateStatus(type: ConeKotlinType, status: IntersectionStatus) {
// We should keep status FROM_SMARTCAST for correct diagnostic reporting
val existingStatus = get(type)
if (existingStatus == null) {
put(type, status)
} else {
put(type, minOf(status, existingStatus))
}
}
private fun flatIntersectionTypes(
inputTypes: List<ConeKotlinType>,
intersectionStatus: List<IntersectionStatus>,
typeCollector: MutableList<ConeKotlinType>,
statusMap: MutableMap<ConeKotlinType, IntersectionStatus>
) {
for ((inputType, status) in inputTypes.zip(intersectionStatus)) {
if (inputType is ConeIntersectionType) {
for (type in inputType.intersectedTypes) {
typeCollector += type
statusMap.updateStatus(type, status)
}
} else {
typeCollector += inputType
statusMap.updateStatus(inputType, status)
}
}
}
private enum class ResultNullability {
START {
override fun combine(nextType: ConeKotlinType, context: ConeTypeContext): ResultNullability =
nextType.resultNullability(context)
},
ACCEPT_NULL {
override fun combine(nextType: ConeKotlinType, context: ConeTypeContext): ResultNullability =
nextType.resultNullability(context)
},
// example: type parameter without not-null supertype
UNKNOWN {
override fun combine(nextType: ConeKotlinType, context: ConeTypeContext): ResultNullability =
nextType.resultNullability(context).let {
if (it == ACCEPT_NULL) this else it
}
},
NOT_NULL {
override fun combine(nextType: ConeKotlinType, context: ConeTypeContext): ResultNullability = this
};
abstract fun combine(nextType: ConeKotlinType, context: ConeTypeContext): ResultNullability
protected fun ConeKotlinType.resultNullability(context: ConeTypeContext): ResultNullability =
when {
isMarkedNullable -> ACCEPT_NULL
ConeNullabilityChecker.isSubtypeOfAny(context, this) -> NOT_NULL
else -> UNKNOWN
}
}
}
@@ -0,0 +1,19 @@
/*
* 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.types
import org.jetbrains.kotlin.types.AbstractNullabilityChecker
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
object ConeNullabilityChecker {
fun isSubtypeOfAny(context: ConeTypeContext, type: ConeKotlinType): Boolean {
val actualType = with(context) { type.lowerBoundIfFlexible() }
return with(AbstractNullabilityChecker) {
context.newBaseTypeCheckerContext(false)
.hasNotNullSupertype(actualType, AbstractTypeCheckerContext.SupertypesPolicy.LowerIfFlexible)
}
}
}
@@ -0,0 +1,14 @@
interface A
interface B
class Clazz1 : A, B
class Clazz2 : A, B
fun <K> select(x: K, y: K): K
fun test() = select(Clazz1(), Clazz2())
fun <T> makeNull(x: T): T? = null
fun testNull() = makeNull(select(Clazz1(), Clazz2()))
@@ -19,9 +19,9 @@ FILE: intersectionTypes.kt
public final fun test(): R|it(A & B)| {
^test R|/select|<R|it(A & B)|>(R|/Clazz1.Clazz1|(), R|/Clazz2.Clazz2|())
}
public final fun <T> makeNull(x: R|T|): R|T|? {
public final fun <T> makeNull(x: R|T|): R|T?| {
^makeNull Null(null)
}
public final fun testNull(): R|it(A & B)| {
public final fun testNull(): R|it(A? & B?)| {
^testNull R|/makeNull|<R|it(A & B)|>(R|/select|<R|it(A & B)|>(R|/Clazz1.Clazz1|(), R|/Clazz2.Clazz2|()))
}
@@ -94,6 +94,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase {
runTest("compiler/fir/resolve/testData/resolve/genericFunctions.kt");
}
@TestMetadata("intersectionTypes.kt")
public void testIntersectionTypes() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/intersectionTypes.kt");
}
@TestMetadata("nestedClass.kt")
public void testNestedClass() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/nestedClass.kt");
@@ -34,7 +34,7 @@ FILE fqName:<root> fileName:/intersectionType1_NI.kt
RETURN type=kotlin.Nothing from='public final fun foo <T> (a: kotlin.Array<<root>.In<T of <root>.foo>>, b: kotlin.Array<<root>.In<kotlin.String>>): kotlin.Boolean declared in <root>'
CALL 'public final fun ofType <K> (y: kotlin.Any?): kotlin.Boolean [inline] declared in <root>' type=kotlin.Boolean origin=null
$receiver: CALL 'public final fun get (index: kotlin.Int): <root>.In<T of <root>.foo> declared in kotlin.Array' type=<root>.In<T of <root>.foo> origin=null
$this: CALL 'public final fun select <S> (x: S of <root>.select, y: S of <root>.select): S of <root>.select declared in <root>' type=kotlin.Array<<root>.In<T of <root>.foo>> origin=null
$this: CALL 'public final fun select <S> (x: S of <root>.select, y: S of <root>.select): S of <root>.select declared in <root>' type=kotlin.Array<out <root>.In<T of <root>.foo>> origin=null
<S>: <none>
x: GET_VAR 'a: kotlin.Array<<root>.In<T of <root>.foo>> declared in <root>.foo' type=kotlin.Array<<root>.In<T of <root>.foo>> origin=null
y: GET_VAR 'b: kotlin.Array<<root>.In<kotlin.String>> declared in <root>.foo' type=kotlin.Array<<root>.In<kotlin.String>> origin=null
@@ -34,7 +34,7 @@ FILE fqName:<root> fileName:/intersectionType1_OI.kt
RETURN type=kotlin.Nothing from='public final fun foo <T> (a: kotlin.Array<<root>.In<T of <root>.foo>>, b: kotlin.Array<<root>.In<kotlin.String>>): kotlin.Boolean declared in <root>'
CALL 'public final fun ofType <K> (y: kotlin.Any?): kotlin.Boolean [inline] declared in <root>' type=kotlin.Boolean origin=null
$receiver: CALL 'public final fun get (index: kotlin.Int): <root>.In<T of <root>.foo> declared in kotlin.Array' type=<root>.In<T of <root>.foo> origin=null
$this: CALL 'public final fun select <S> (x: S of <root>.select, y: S of <root>.select): S of <root>.select declared in <root>' type=kotlin.Array<<root>.In<T of <root>.foo>> origin=null
$this: CALL 'public final fun select <S> (x: S of <root>.select, y: S of <root>.select): S of <root>.select declared in <root>' type=kotlin.Array<out <root>.In<T of <root>.foo>> origin=null
<S>: <none>
x: GET_VAR 'a: kotlin.Array<<root>.In<T of <root>.foo>> declared in <root>.foo' type=kotlin.Array<<root>.In<T of <root>.foo>> origin=null
y: GET_VAR 'b: kotlin.Array<<root>.In<kotlin.String>> declared in <root>.foo' type=kotlin.Array<<root>.In<kotlin.String>> origin=null
@@ -75,24 +75,24 @@ FILE fqName:<root> fileName:/intersectionType2_NI.kt
RETURN type=kotlin.Nothing from='public final fun run <T> (fn: kotlin.Function0<T of <root>.run>): T of <root>.run declared in <root>'
CALL 'public abstract fun invoke (): T of <root>.run declared in kotlin.Function0' type=T of <root>.run origin=null
$this: GET_VAR 'fn: kotlin.Function0<T of <root>.run> declared in <root>.run' type=kotlin.Function0<T of <root>.run> origin=null
FUN name:foo visibility:public modality:FINAL <> () returnType:<root>.B
FUN name:foo visibility:public modality:FINAL <> () returnType:<root>.Foo
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun foo (): <root>.B declared in <root>'
CALL 'public final fun run <T> (fn: kotlin.Function0<T of <root>.run>): T of <root>.run declared in <root>' type=<root>.B origin=null
RETURN type=kotlin.Nothing from='public final fun foo (): <root>.Foo declared in <root>'
CALL 'public final fun run <T> (fn: kotlin.Function0<T of <root>.run>): T of <root>.run declared in <root>' type=<root>.Foo origin=null
<T>: <none>
fn: FUN_EXPR type=kotlin.Function0<<root>.B> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:<root>.B
fn: FUN_EXPR type=kotlin.Function0<<root>.Foo> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:<root>.Foo
BLOCK_BODY
VAR name:mm type:<root>.B [val]
CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.B' type=<root>.B origin=null
VAR name:nn type:<root>.C [val]
CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.C' type=<root>.C origin=null
VAR name:c type:<root>.B [val]
WHEN type=<root>.B origin=IF
VAR name:c type:<root>.Foo [val]
WHEN type=<root>.Foo origin=IF
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: GET_VAR 'val mm: <root>.B [val] declared in <root>.foo.<anonymous>' type=<root>.B origin=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: GET_VAR 'val nn: <root>.C [val] declared in <root>.foo.<anonymous>' type=<root>.C origin=null
GET_VAR 'val c: <root>.B [val] declared in <root>.foo.<anonymous>' type=<root>.B origin=null
GET_VAR 'val c: <root>.Foo [val] declared in <root>.foo.<anonymous>' type=<root>.Foo origin=null
@@ -75,24 +75,24 @@ FILE fqName:<root> fileName:/intersectionType2_OI.kt
RETURN type=kotlin.Nothing from='public final fun run <T> (fn: kotlin.Function0<T of <root>.run>): T of <root>.run declared in <root>'
CALL 'public abstract fun invoke (): T of <root>.run declared in kotlin.Function0' type=T of <root>.run origin=null
$this: GET_VAR 'fn: kotlin.Function0<T of <root>.run> declared in <root>.run' type=kotlin.Function0<T of <root>.run> origin=null
FUN name:foo visibility:public modality:FINAL <> () returnType:<root>.B
FUN name:foo visibility:public modality:FINAL <> () returnType:<root>.Foo
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun foo (): <root>.B declared in <root>'
CALL 'public final fun run <T> (fn: kotlin.Function0<T of <root>.run>): T of <root>.run declared in <root>' type=<root>.B origin=null
RETURN type=kotlin.Nothing from='public final fun foo (): <root>.Foo declared in <root>'
CALL 'public final fun run <T> (fn: kotlin.Function0<T of <root>.run>): T of <root>.run declared in <root>' type=<root>.Foo origin=null
<T>: <none>
fn: FUN_EXPR type=kotlin.Function0<<root>.B> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:<root>.B
fn: FUN_EXPR type=kotlin.Function0<<root>.Foo> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:<root>.Foo
BLOCK_BODY
VAR name:mm type:<root>.B [val]
CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.B' type=<root>.B origin=null
VAR name:nn type:<root>.C [val]
CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.C' type=<root>.C origin=null
VAR name:c type:<root>.B [val]
WHEN type=<root>.B origin=IF
VAR name:c type:<root>.Foo [val]
WHEN type=<root>.Foo origin=IF
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: GET_VAR 'val mm: <root>.B [val] declared in <root>.foo.<anonymous>' type=<root>.B origin=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: GET_VAR 'val nn: <root>.C [val] declared in <root>.foo.<anonymous>' type=<root>.C origin=null
GET_VAR 'val c: <root>.B [val] declared in <root>.foo.<anonymous>' type=<root>.B origin=null
GET_VAR 'val c: <root>.Foo [val] declared in <root>.foo.<anonymous>' type=<root>.Foo origin=null