Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// FIR_IDENTICAL
// RUN_PIPELINE_TILL: BACKEND
// ISSUE: KT-77979
// WITH_STDLIB

sealed interface Adt<A, B>
data class A<A, B>(val x: B) : Adt<A, B>
sealed interface BAdt<B, A> : Adt<A, B>
data class B<A, B>(val x: B) : BAdt<A, B>
data class C<A, B>(val x: A) : BAdt<A, B>

fun example(a: Adt<Nothing, String>) {
when (a) {
is A -> {}
is C -> {}
// no need in case B
}
}

/* GENERATED_FIR_TAGS: classDeclaration, data, functionDeclaration, interfaceDeclaration, isExpression, nullableType,
primaryConstructor, propertyDeclaration, sealed, smartcast, typeParameter, whenExpression, whenWithSubject */
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// FIR_IDENTICAL
// RUN_PIPELINE_TILL: BACKEND
// ISSUE: KT-77979
// WITH_STDLIB

sealed interface Adt<out A>
data class A<A: Number>(val x: A) : Adt<A>
data class B(val x: Unit) : Adt<Nothing>

fun example(a: Adt<String>) {
when (a) {
is B -> {}
// no need in case A
}
}

/* GENERATED_FIR_TAGS: classDeclaration, data, functionDeclaration, interfaceDeclaration, isExpression, nullableType,
out, primaryConstructor, propertyDeclaration, sealed, typeConstraint, typeParameter, whenExpression, whenWithSubject */
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,40 @@ fun checkUpperBoundViolated(
}
}

fun checkUpperBoundViolatedNoReport(
typeParameters: List<FirTypeParameterSymbol>,
typeArguments: List<ConeTypeProjection>,
session: FirSession,
): Boolean {
val substitution = typeParameters.zip(typeArguments).toMap()
val substitutor = FE10LikeConeSubstitutor(substitution, session)

val count = minOf(typeParameters.size, typeArguments.size)
val typeSystemContext = session.typeContext

for (index in 0 until count) {
val argument = typeArguments[index]
val argumentType = argument.type
if (argumentType != null) {
if (argumentType.typeArguments.isEmpty() && argumentType !is ConeTypeParameterType) {
val intersection =
typeSystemContext.intersectTypes(typeParameters[index].resolvedBounds.map { it.coneType })
val upperBound = substitutor.substituteOrSelf(intersection)
if (!AbstractTypeChecker.isSubtypeOf(
typeSystemContext,
argumentType,
upperBound,
stubTypesEqualToAnything = true
)
) {
return true
}
}
}
}
return false
}

/**
* @returns true if the diagnostic was reported
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ fun FirSession.doUnify(
targetTypeParameters: Set<FirTypeParameterSymbol>,
result: MutableMap<FirTypeParameterSymbol, ConeTypeProjection>,
): Boolean {
val originalType = originalTypeProjection.type?.lowerBoundIfFlexible()?.fullyExpandedType(this)
val typeWithParameters = typeWithParametersProjection.type?.lowerBoundIfFlexible()?.fullyExpandedType(this)
val originalType = originalTypeProjection.type?.lowerBoundIfFlexible()?.fullyExpandedType()
val typeWithParameters = typeWithParametersProjection.type?.lowerBoundIfFlexible()?.fullyExpandedType()

if (typeWithParameters is ConeErrorType) {
return true // Return true to avoid loosing `result` substitution
Expand Down
1 change: 1 addition & 0 deletions compiler/fir/resolve/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {
dependencies {
api(project(":compiler:fir:providers"))
api(project(":compiler:fir:semantics"))
api(project(":compiler:fir:checkers"))
implementation(project(":core:util.runtime"))

compileOnly(libs.guava)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.checkers.checkUpperBoundViolatedNoReport
import org.jetbrains.kotlin.fir.declarations.FirEnumEntry
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.declaredProperties
import org.jetbrains.kotlin.fir.declarations.getSealedClassInheritors
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
Expand All @@ -21,7 +23,9 @@ import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.resolve.transformers.WhenOnSealedClassExhaustivenessChecker.ConditionChecker.processBranch
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.computeRepresentativeTypeForBareType
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
Expand Down Expand Up @@ -447,12 +451,40 @@ private object WhenOnSealedClassExhaustivenessChecker : WhenExhaustivenessChecke
}

for (notCheckedRegularClasses in notCheckedRegularClasses) {
destination += WhenMissingCase.IsTypeCheckIsMissing(
notCheckedRegularClasses.classId,
notCheckedRegularClasses.fir.classKind.isSingleton,
notCheckedRegularClasses.ownTypeParameterSymbols.size
)
if (!isUninhabited(notCheckedRegularClasses, subjectType, session)) {
destination += WhenMissingCase.IsTypeCheckIsMissing(
notCheckedRegularClasses.classId,
notCheckedRegularClasses.fir.classKind.isSingleton,
notCheckedRegularClasses.ownTypeParameterSymbols.size
)
}
}
}

private fun isUninhabited(
classSymbol: FirClassSymbol<*>,
subjectType: ConeKotlinType,
session: FirSession,
): Boolean {
val classType =
session.computeRepresentativeTypeForBareType(classSymbol.defaultType(), subjectType) ?: return false
val boundsViolated =
checkUpperBoundViolatedNoReport(classSymbol.typeParameterSymbols, classType.typeArguments.toList(), session)
val containsNothing: Boolean by lazy {
val typeMapping =
classSymbol.typeParameterSymbols.zip(classType.typeArguments).mapNotNull { (parameter, arg) ->
when (arg) {
is ConeKotlinType -> parameter to arg
is ConeKotlinTypeProjectionOut -> parameter to arg.type
else -> null
}
}.toMap()
val substitutor = substitutorByMap(typeMapping, session)
val typesOfProperties = classSymbol.declaredProperties(session)
.map { substitutor.substituteOrSelf(it.resolvedReturnType) }
typesOfProperties.any { it.isNothing }
}
return boundsViolated || containsNothing
}

private fun inferVariantsFromSubjectSmartCast(subject: FirExpression, data: Info) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve

import org.jetbrains.kotlin.fir.SessionHolder
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
Expand All @@ -16,7 +17,7 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration

fun BodyResolveComponents.computeRepresentativeTypeForBareType(type: ConeClassLikeType, originalType: ConeKotlinType): ConeKotlinType? {
fun SessionHolder.computeRepresentativeTypeForBareType(type: ConeClassLikeType, originalType: ConeKotlinType): ConeKotlinType? {
originalType.lowerBoundIfFlexible().fullyExpandedType().let {
if (it !== originalType) return computeRepresentativeTypeForBareType(type, it)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import kotlin.reflect.KClass

abstract class FirSession @PrivateSessionConstructor constructor(
val kind: Kind
) : ComponentArrayOwner<FirSessionComponent, FirSessionComponent>() {
) : ComponentArrayOwner<FirSessionComponent, FirSessionComponent>(), SessionHolder {
companion object : ConeTypeRegistry<FirSessionComponent, FirSessionComponent>() {
inline fun <reified T : FirSessionComponent> sessionComponentAccessor(): ArrayMapAccessor<FirSessionComponent, FirSessionComponent, T> {
return generateAccessor(T::class)
Expand All @@ -39,6 +39,9 @@ abstract class FirSession @PrivateSessionConstructor constructor(

open val builtinTypes: BuiltinTypes = BuiltinTypes()

override val session: FirSession
get() = this

final override val typeRegistry: TypeRegistry<FirSessionComponent, FirSessionComponent> = Companion

@SessionConfiguration
Expand Down