[FIR] Don't smartcast variables to invisible types
#KT-44802 Fixed
This commit is contained in:
+5
@@ -2885,6 +2885,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
|||||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastToNothing.kt");
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastToNothing.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("smartcastToTypeParameter.kt")
|
||||||
|
public void testSmartcastToTypeParameter() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastToTypeParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans")
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
FILE: smartcastToTypeParameter.kt
|
||||||
|
public abstract interface FirTypeScope : R|kotlin/Any| {
|
||||||
|
}
|
||||||
|
public abstract interface AbstractFirBasedSymbol<E : R|FirSymbolOwner<E>|, R|FirDeclaration|> : R|kotlin/Any| {
|
||||||
|
public abstract val fir: R|E|
|
||||||
|
public get(): R|E|
|
||||||
|
|
||||||
|
}
|
||||||
|
public abstract interface FirCallableSymbol<D : R|FirCallableDeclaration<D>|> : R|AbstractFirBasedSymbol<D>| {
|
||||||
|
}
|
||||||
|
public abstract interface FirElement : R|kotlin/Any| {
|
||||||
|
}
|
||||||
|
public abstract interface FirSymbolOwner<E : R|FirSymbolOwner<E>|, R|FirDeclaration|> : R|FirElement| {
|
||||||
|
public abstract val symbol: R|AbstractFirBasedSymbol<E>|
|
||||||
|
public get(): R|AbstractFirBasedSymbol<E>|
|
||||||
|
|
||||||
|
}
|
||||||
|
public abstract interface FirDeclaration : R|FirElement| {
|
||||||
|
}
|
||||||
|
public abstract interface FirCallableDeclaration<F : R|FirCallableDeclaration<F>|> : R|FirDeclaration|, R|FirSymbolOwner<F>| {
|
||||||
|
}
|
||||||
|
public abstract interface FirCallableMemberDeclaration<F : R|FirCallableMemberDeclaration<F>|> : R|FirCallableDeclaration<F>| {
|
||||||
|
}
|
||||||
|
private final inline fun <reified S : R|FirCallableSymbol<*>|> computeBaseSymbols(symbol: R|S|, basedSymbol: R|S|, directOverridden: R|FirTypeScope.(S) -> kotlin/collections/List<S>|): R|kotlin/Unit| {
|
||||||
|
}
|
||||||
|
public final fun R|FirCallableSymbol<*>|.dispatchReceiverClassOrNull(): R|kotlin/Boolean?| {
|
||||||
|
^dispatchReceiverClassOrNull Boolean(true)
|
||||||
|
}
|
||||||
|
private final inline fun <reified D : R|FirCallableMemberDeclaration<D>|, reified S : R|FirCallableSymbol<D>|> createFakeOverriddenIfNeeded(originalSymbol: R|FirCallableSymbol<*>|, basedSymbol: R|S|, computeDirectOverridden: R|FirTypeScope.(S) -> kotlin/collections/List<S>|, someCondition: R|kotlin/Boolean|): R|kotlin/Unit| {
|
||||||
|
when () {
|
||||||
|
(R|<local>/originalSymbol| !is R|S|) -> {
|
||||||
|
^createFakeOverriddenIfNeeded Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when () {
|
||||||
|
==(R|<local>/originalSymbol|.R|/dispatchReceiverClassOrNull|(), Boolean(true)) && R|<local>/someCondition| -> {
|
||||||
|
^createFakeOverriddenIfNeeded Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
R|/computeBaseSymbols|<R|S|>(R|<local>/originalSymbol|, R|<local>/basedSymbol|, R|<local>/computeDirectOverridden|)
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
interface FirTypeScope
|
||||||
|
|
||||||
|
interface AbstractFirBasedSymbol<E> where E : FirSymbolOwner<E>, E : FirDeclaration {
|
||||||
|
val fir: E
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FirCallableSymbol<D : FirCallableDeclaration<D>> : AbstractFirBasedSymbol<D>
|
||||||
|
|
||||||
|
interface FirElement
|
||||||
|
interface FirSymbolOwner<E> : FirElement where E : FirSymbolOwner<E>, E : FirDeclaration {
|
||||||
|
val symbol: AbstractFirBasedSymbol<E>
|
||||||
|
}
|
||||||
|
interface FirDeclaration : FirElement
|
||||||
|
interface FirCallableDeclaration<F : FirCallableDeclaration<F>> : FirDeclaration, FirSymbolOwner<F>
|
||||||
|
interface FirCallableMemberDeclaration<F : FirCallableMemberDeclaration<F>> : FirCallableDeclaration<F>
|
||||||
|
|
||||||
|
private inline fun <reified S : FirCallableSymbol<*>> computeBaseSymbols(
|
||||||
|
symbol: S,
|
||||||
|
basedSymbol: S,
|
||||||
|
directOverridden: FirTypeScope.(S) -> List<S>
|
||||||
|
) {}
|
||||||
|
|
||||||
|
fun FirCallableSymbol<*>.dispatchReceiverClassOrNull(): Boolean? = true
|
||||||
|
|
||||||
|
private inline fun <reified D : FirCallableMemberDeclaration<D>, reified S : FirCallableSymbol<D>> createFakeOverriddenIfNeeded(
|
||||||
|
originalSymbol: FirCallableSymbol<*>,
|
||||||
|
basedSymbol: S,
|
||||||
|
computeDirectOverridden: FirTypeScope.(S) -> List<S>,
|
||||||
|
someCondition: Boolean
|
||||||
|
) {
|
||||||
|
if (originalSymbol !is S) return
|
||||||
|
if (originalSymbol.dispatchReceiverClassOrNull() == true && someCondition) return
|
||||||
|
computeBaseSymbols(originalSymbol, basedSymbol, computeDirectOverridden)
|
||||||
|
}
|
||||||
+6
@@ -3263,6 +3263,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
|
|||||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastToNothing.kt");
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastToNothing.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("smartcastToTypeParameter.kt")
|
||||||
|
public void testSmartcastToTypeParameter() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastToTypeParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans")
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
|||||||
+6
@@ -3300,6 +3300,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
|||||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastToNothing.kt");
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastToNothing.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("smartcastToTypeParameter.kt")
|
||||||
|
public void testSmartcastToTypeParameter() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastToTypeParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans")
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
|||||||
+12
@@ -25651,6 +25651,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/smartcastOnSameFieldOfDifferentInstances.kt");
|
runTest("compiler/testData/diagnostics/tests/smartCasts/smartcastOnSameFieldOfDifferentInstances.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("smartcastToInvisibleType_java.kt")
|
||||||
|
public void testSmartcastToInvisibleType_java() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/smartCasts/smartcastToInvisibleType_java.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("smartcastToInvisibleType_kotlin.kt")
|
||||||
|
public void testSmartcastToInvisibleType_kotlin() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/smartCasts/smartcastToInvisibleType_kotlin.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("smartcastToNothingAfterCheckingForNull.kt")
|
@TestMetadata("smartcastToNothingAfterCheckingForNull.kt")
|
||||||
public void testSmartcastToNothingAfterCheckingForNull() throws Exception {
|
public void testSmartcastToNothingAfterCheckingForNull() throws Exception {
|
||||||
|
|||||||
+4
@@ -53,6 +53,10 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
|
|||||||
|
|
||||||
override fun updateAllReceivers(flow: PersistentFlow) =
|
override fun updateAllReceivers(flow: PersistentFlow) =
|
||||||
throw IllegalStateException("Update of all receivers is not possible for this logic system")
|
throw IllegalStateException("Update of all receivers is not possible for this logic system")
|
||||||
|
|
||||||
|
override fun ConeKotlinType.isAcceptableForSmartcast(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
effects.forEach { effect ->
|
effects.forEach { effect ->
|
||||||
|
|||||||
+6
@@ -37668,6 +37668,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt44802.kt")
|
||||||
|
public void testKt44802() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/smartCasts/kt44802.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt44804.kt")
|
@TestMetadata("kt44804.kt")
|
||||||
public void testKt44804() throws Exception {
|
public void testKt44804() throws Exception {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.fir.NoMutableState
|
|||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
import org.jetbrains.kotlin.fir.getOwnerId
|
import org.jetbrains.kotlin.fir.getOwnerId
|
||||||
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
|
import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue
|
||||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||||
|
|
||||||
@NoMutableState
|
@NoMutableState
|
||||||
@@ -23,7 +23,7 @@ object FirJavaVisibilityChecker : FirVisibilityChecker() {
|
|||||||
symbol: AbstractFirBasedSymbol<*>,
|
symbol: AbstractFirBasedSymbol<*>,
|
||||||
useSiteFile: FirFile,
|
useSiteFile: FirFile,
|
||||||
containingDeclarations: List<FirDeclaration>,
|
containingDeclarations: List<FirDeclaration>,
|
||||||
candidate: Candidate,
|
dispatchReceiver: ReceiverValue?,
|
||||||
session: FirSession
|
session: FirSession
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return when (declarationVisibility) {
|
return when (declarationVisibility) {
|
||||||
@@ -32,7 +32,7 @@ object FirJavaVisibilityChecker : FirVisibilityChecker() {
|
|||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
val ownerId = symbol.getOwnerId()
|
val ownerId = symbol.getOwnerId()
|
||||||
ownerId != null && canSeeProtectedMemberOf(containingDeclarations, candidate.dispatchReceiverValue, ownerId, session)
|
ownerId != null && canSeeProtectedMemberOf(containingDeclarations, dispatchReceiver, ownerId, session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
symbol: AbstractFirBasedSymbol<*>,
|
symbol: AbstractFirBasedSymbol<*>,
|
||||||
useSiteFile: FirFile,
|
useSiteFile: FirFile,
|
||||||
containingDeclarations: List<FirDeclaration>,
|
containingDeclarations: List<FirDeclaration>,
|
||||||
candidate: Candidate,
|
dispatchReceiver: ReceiverValue?,
|
||||||
session: FirSession
|
session: FirSession
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return true
|
return true
|
||||||
@@ -45,8 +45,6 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
declaration: T,
|
declaration: T,
|
||||||
candidate: Candidate
|
candidate: Candidate
|
||||||
): Boolean where T : FirMemberDeclaration, T : FirSymbolOwner<*> {
|
): Boolean where T : FirMemberDeclaration, T : FirSymbolOwner<*> {
|
||||||
val symbol = declaration.symbol
|
|
||||||
|
|
||||||
if (declaration is FirCallableDeclaration<*> && (declaration.isIntersectionOverride || declaration.isSubstitutionOverride)) {
|
if (declaration is FirCallableDeclaration<*> && (declaration.isIntersectionOverride || declaration.isSubstitutionOverride)) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return isVisible(declaration.originalIfFakeOverride() as T, candidate)
|
return isVisible(declaration.originalIfFakeOverride() as T, candidate)
|
||||||
@@ -56,8 +54,19 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
val useSiteFile = callInfo.containingFile
|
val useSiteFile = callInfo.containingFile
|
||||||
val containingDeclarations = callInfo.containingDeclarations
|
val containingDeclarations = callInfo.containingDeclarations
|
||||||
val session = callInfo.session
|
val session = callInfo.session
|
||||||
val provider = session.firProvider
|
|
||||||
|
|
||||||
|
return isVisible(declaration, session, useSiteFile, containingDeclarations, candidate.dispatchReceiverValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> isVisible(
|
||||||
|
declaration: T,
|
||||||
|
session: FirSession,
|
||||||
|
useSiteFile: FirFile,
|
||||||
|
containingDeclarations: List<FirDeclaration>,
|
||||||
|
dispatchReceiver: ReceiverValue?
|
||||||
|
): Boolean where T : FirMemberDeclaration, T : FirSymbolOwner<*> {
|
||||||
|
val provider = session.firProvider
|
||||||
|
val symbol = declaration.symbol
|
||||||
return when (declaration.visibility) {
|
return when (declaration.visibility) {
|
||||||
Visibilities.Internal -> {
|
Visibilities.Internal -> {
|
||||||
declaration.session == session || session.moduleVisibilityChecker?.isInFriendModule(declaration) == true
|
declaration.session == session || session.moduleVisibilityChecker?.isInFriendModule(declaration) == true
|
||||||
@@ -91,7 +100,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
|
|
||||||
Visibilities.Protected -> {
|
Visibilities.Protected -> {
|
||||||
val ownerId = symbol.getOwnerId()
|
val ownerId = symbol.getOwnerId()
|
||||||
ownerId != null && canSeeProtectedMemberOf(containingDeclarations, candidate.dispatchReceiverValue, ownerId, session)
|
ownerId != null && canSeeProtectedMemberOf(containingDeclarations, dispatchReceiver, ownerId, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> platformVisibilityCheck(
|
else -> platformVisibilityCheck(
|
||||||
@@ -99,7 +108,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
symbol,
|
symbol,
|
||||||
useSiteFile,
|
useSiteFile,
|
||||||
containingDeclarations,
|
containingDeclarations,
|
||||||
candidate,
|
dispatchReceiver,
|
||||||
session
|
session
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -110,7 +119,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
symbol: AbstractFirBasedSymbol<*>,
|
symbol: AbstractFirBasedSymbol<*>,
|
||||||
useSiteFile: FirFile,
|
useSiteFile: FirFile,
|
||||||
containingDeclarations: List<FirDeclaration>,
|
containingDeclarations: List<FirDeclaration>,
|
||||||
candidate: Candidate,
|
dispatchReceiver: ReceiverValue?,
|
||||||
session: FirSession
|
session: FirSession
|
||||||
): Boolean
|
): Boolean
|
||||||
|
|
||||||
|
|||||||
+26
-2
@@ -18,8 +18,7 @@ import org.jetbrains.kotlin.fir.expressions.*
|
|||||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||||
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
|
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.PersistentImplicitReceiverStack
|
import org.jetbrains.kotlin.fir.resolve.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.buildContractFir
|
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.buildContractFir
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.createArgumentsMapping
|
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.createArgumentsMapping
|
||||||
@@ -31,6 +30,7 @@ import org.jetbrains.kotlin.fir.symbols.CallableId
|
|||||||
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
|
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
|
import org.jetbrains.kotlin.fir.visibilityChecker
|
||||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
@@ -84,6 +84,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
private val receiverStack: PersistentImplicitReceiverStack
|
private val receiverStack: PersistentImplicitReceiverStack
|
||||||
get() = components.implicitReceiverStack as PersistentImplicitReceiverStack
|
get() = components.implicitReceiverStack as PersistentImplicitReceiverStack
|
||||||
|
|
||||||
|
private val symbolProvider = components.session.symbolProvider
|
||||||
|
private val visibilityChecker = components.session.visibilityChecker
|
||||||
|
|
||||||
override val logicSystem: PersistentLogicSystem =
|
override val logicSystem: PersistentLogicSystem =
|
||||||
object : PersistentLogicSystem(components.session.inferenceComponents.ctx) {
|
object : PersistentLogicSystem(components.session.inferenceComponents.ctx) {
|
||||||
override fun processUpdatedReceiverVariable(flow: PersistentFlow, variable: RealVariable) {
|
override fun processUpdatedReceiverVariable(flow: PersistentFlow, variable: RealVariable) {
|
||||||
@@ -109,6 +112,27 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun ConeKotlinType.isAcceptableForSmartcast(): Boolean {
|
||||||
|
return when (this) {
|
||||||
|
is ConeClassLikeType -> {
|
||||||
|
val symbol = fullyExpandedType(components.session).lookupTag.toSymbol(components.session) ?: return false
|
||||||
|
val declaration = symbol.fir as? FirRegularClass ?: return true
|
||||||
|
visibilityChecker.isVisible(
|
||||||
|
declaration,
|
||||||
|
components.session,
|
||||||
|
components.context.file,
|
||||||
|
components.context.containers,
|
||||||
|
dispatchReceiver = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is ConeTypeParameterType -> true
|
||||||
|
is ConeFlexibleType -> lowerBound.isAcceptableForSmartcast() && upperBound.isAcceptableForSmartcast()
|
||||||
|
is ConeIntersectionType -> intersectedTypes.all { it.isAcceptableForSmartcast() }
|
||||||
|
is ConeDefinitelyNotNullType -> original.isAcceptableForSmartcast()
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.resolve.dfa
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.fir.types.ConeInferenceContext
|
import org.jetbrains.kotlin.fir.types.ConeInferenceContext
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
|
import org.jetbrains.kotlin.fir.types.canBeNull
|
||||||
import org.jetbrains.kotlin.fir.types.commonSuperTypeOrNull
|
import org.jetbrains.kotlin.fir.types.commonSuperTypeOrNull
|
||||||
|
|
||||||
abstract class Flow {
|
abstract class Flow {
|
||||||
@@ -59,6 +60,8 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
|
|||||||
|
|
||||||
protected abstract fun getImplicationsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<Implication>
|
protected abstract fun getImplicationsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<Implication>
|
||||||
|
|
||||||
|
protected abstract fun ConeKotlinType.isAcceptableForSmartcast(): Boolean
|
||||||
|
|
||||||
// ------------------------------- Callbacks for updating implicit receiver stack -------------------------------
|
// ------------------------------- Callbacks for updating implicit receiver stack -------------------------------
|
||||||
|
|
||||||
abstract fun processUpdatedReceiverVariable(flow: FLOW, variable: RealVariable)
|
abstract fun processUpdatedReceiverVariable(flow: FLOW, variable: RealVariable)
|
||||||
@@ -150,7 +153,14 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val result = mutableSetOf<ConeKotlinType>()
|
val result = mutableSetOf<ConeKotlinType>()
|
||||||
context.commonSuperTypeOrNull(intersectedTypes)?.let { result.add(it) }
|
context.commonSuperTypeOrNull(intersectedTypes)?.let {
|
||||||
|
if (it.isAcceptableForSmartcast()) {
|
||||||
|
result.add(it)
|
||||||
|
} else if (!it.canBeNull) {
|
||||||
|
result.add(context.anyType())
|
||||||
|
}
|
||||||
|
Unit
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,19 +32,19 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
|
|||||||
|
|
||||||
val symbolProvider: FirSymbolProvider get() = session.symbolProvider
|
val symbolProvider: FirSymbolProvider get() = session.symbolProvider
|
||||||
|
|
||||||
override fun nullableNothingType(): SimpleTypeMarker {
|
override fun nullableNothingType(): ConeClassLikeType {
|
||||||
return session.builtinTypes.nullableNothingType.type
|
return session.builtinTypes.nullableNothingType.type
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun nullableAnyType(): SimpleTypeMarker {
|
override fun nullableAnyType(): ConeClassLikeType {
|
||||||
return session.builtinTypes.nullableAnyType.type
|
return session.builtinTypes.nullableAnyType.type
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun nothingType(): SimpleTypeMarker {
|
override fun nothingType(): ConeClassLikeType {
|
||||||
return session.builtinTypes.nothingType.type
|
return session.builtinTypes.nothingType.type
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun anyType(): SimpleTypeMarker {
|
override fun anyType(): ConeClassLikeType {
|
||||||
return session.builtinTypes.anyType.type
|
return session.builtinTypes.anyType.type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// ISSUE: KT-44802
|
||||||
|
|
||||||
|
// FILE: foo/Base.java
|
||||||
|
package foo;
|
||||||
|
|
||||||
|
public interface Base {
|
||||||
|
String foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: foo/PackagePrivateInterface.java
|
||||||
|
package foo;
|
||||||
|
|
||||||
|
interface PackagePrivateInterface extends Base {}
|
||||||
|
|
||||||
|
// FILE: foo/A.java
|
||||||
|
package foo;
|
||||||
|
|
||||||
|
public class A implements PackagePrivateInterface {
|
||||||
|
public String foo() { return "OK"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: foo/B.java
|
||||||
|
package foo;
|
||||||
|
|
||||||
|
public class B implements PackagePrivateInterface {
|
||||||
|
public String foo() { return "B"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: foo/C.java
|
||||||
|
package foo;
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
package bar
|
||||||
|
|
||||||
|
import foo.Base
|
||||||
|
import foo.A
|
||||||
|
import foo.B
|
||||||
|
|
||||||
|
fun testSmartcast(x: Base): String {
|
||||||
|
if (x !is A && x !is B) return "fail"
|
||||||
|
return x.foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box() = testSmartcast(A())
|
||||||
|
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
// ISSUE: KT-44802
|
||||||
|
// INFERENCE_HELPERS
|
||||||
|
|
||||||
|
// FILE: foo/PackagePrivateInterface.java
|
||||||
|
package foo;
|
||||||
|
|
||||||
|
interface PackagePrivateInterface {
|
||||||
|
default void foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: foo/A.java
|
||||||
|
package foo;
|
||||||
|
|
||||||
|
public class A implements PackagePrivateInterface {}
|
||||||
|
|
||||||
|
// FILE: foo/B.java
|
||||||
|
package foo;
|
||||||
|
|
||||||
|
public class B implements PackagePrivateInterface {}
|
||||||
|
|
||||||
|
// FILE: differentPackage.kt
|
||||||
|
package bar
|
||||||
|
|
||||||
|
import foo.A
|
||||||
|
import foo.B
|
||||||
|
import select
|
||||||
|
|
||||||
|
fun testSmartcast(x: Any) {
|
||||||
|
if (x is A || x is B) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any")!>x<!>.<!UNRESOLVED_REFERENCE!>foo<!>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInference(a: A, b: B) {
|
||||||
|
val x = <!DEBUG_INFO_EXPRESSION_TYPE("foo.PackagePrivateInterface")!>select(a, b)<!>
|
||||||
|
x.foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: samePackage.kt
|
||||||
|
package foo
|
||||||
|
|
||||||
|
import select
|
||||||
|
|
||||||
|
fun testSmartcast(x: Any) {
|
||||||
|
if (x is A || x is B) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & foo.PackagePrivateInterface")!>x<!>.foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInference(a: A, b: B) {
|
||||||
|
val x = <!DEBUG_INFO_EXPRESSION_TYPE("foo.PackagePrivateInterface")!>select(a, b)<!>
|
||||||
|
x.foo()
|
||||||
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
// ISSUE: KT-44802
|
||||||
|
// INFERENCE_HELPERS
|
||||||
|
|
||||||
|
// FILE: foo/PackagePrivateInterface.java
|
||||||
|
package foo;
|
||||||
|
|
||||||
|
interface PackagePrivateInterface {
|
||||||
|
default void foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: foo/A.java
|
||||||
|
package foo;
|
||||||
|
|
||||||
|
public class A implements PackagePrivateInterface {}
|
||||||
|
|
||||||
|
// FILE: foo/B.java
|
||||||
|
package foo;
|
||||||
|
|
||||||
|
public class B implements PackagePrivateInterface {}
|
||||||
|
|
||||||
|
// FILE: differentPackage.kt
|
||||||
|
package bar
|
||||||
|
|
||||||
|
import foo.A
|
||||||
|
import foo.B
|
||||||
|
import select
|
||||||
|
|
||||||
|
fun testSmartcast(x: Any) {
|
||||||
|
if (x is A || x is B) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any")!>x<!>.<!UNRESOLVED_REFERENCE!>foo<!>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInference(a: A, b: B) {
|
||||||
|
val x = <!DEBUG_INFO_EXPRESSION_TYPE("foo.PackagePrivateInterface"), INACCESSIBLE_TYPE!>select(a, b)<!>
|
||||||
|
<!INACCESSIBLE_TYPE!>x<!>.<!INVISIBLE_MEMBER!>foo<!>()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: samePackage.kt
|
||||||
|
package foo
|
||||||
|
|
||||||
|
import select
|
||||||
|
|
||||||
|
fun testSmartcast(x: Any) {
|
||||||
|
if (x is A || x is B) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any")!>x<!>.<!UNRESOLVED_REFERENCE!>foo<!>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInference(a: A, b: B) {
|
||||||
|
val x = <!DEBUG_INFO_EXPRESSION_TYPE("foo.PackagePrivateInterface")!>select(a, b)<!>
|
||||||
|
x.foo()
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun </*0*/ K> id(/*0*/ x: K): K
|
||||||
|
public fun </*0*/ K> materialize(): K
|
||||||
|
public fun </*0*/ K> select(/*0*/ vararg values: K /*kotlin.Array<out K>*/): K
|
||||||
|
|
||||||
|
package bar {
|
||||||
|
public fun testInference(/*0*/ a: foo.A, /*1*/ b: foo.B): kotlin.Unit
|
||||||
|
public fun testSmartcast(/*0*/ x: kotlin.Any): kotlin.Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
package foo {
|
||||||
|
public fun testInference(/*0*/ a: foo.A, /*1*/ b: foo.B): kotlin.Unit
|
||||||
|
public fun testSmartcast(/*0*/ x: kotlin.Any): kotlin.Unit
|
||||||
|
|
||||||
|
public open class A : foo.PackagePrivateInterface {
|
||||||
|
public constructor A()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public 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
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class B : foo.PackagePrivateInterface {
|
||||||
|
public constructor B()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public 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
|
||||||
|
}
|
||||||
|
|
||||||
|
public/*package*/ interface PackagePrivateInterface {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
// INFERENCE_HELPERS
|
||||||
|
// ISSUE: KT-44802
|
||||||
|
// FILE: a.kt
|
||||||
|
|
||||||
|
package foo
|
||||||
|
import select
|
||||||
|
|
||||||
|
private interface PrivateInterface {
|
||||||
|
fun foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class A : PrivateInterface
|
||||||
|
class B : PrivateInterface
|
||||||
|
|
||||||
|
fun testSmartcast(x: Any) {
|
||||||
|
if (x is A || x is B) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & foo.PrivateInterface")!>x<!>.foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInference(a: A, b: B) {
|
||||||
|
val x = <!DEBUG_INFO_EXPRESSION_TYPE("foo.PrivateInterface")!>select(a, b)<!>
|
||||||
|
x.foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
package bar
|
||||||
|
|
||||||
|
import foo.A
|
||||||
|
import foo.B
|
||||||
|
import select
|
||||||
|
|
||||||
|
fun testSmartcast(x: Any) {
|
||||||
|
if (x is A || x is B) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any")!>x<!>.<!UNRESOLVED_REFERENCE!>foo<!>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInference(a: A, b: B) {
|
||||||
|
val x = <!DEBUG_INFO_EXPRESSION_TYPE("foo.PrivateInterface")!>select(a, b)<!>
|
||||||
|
x.foo()
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
// INFERENCE_HELPERS
|
||||||
|
// ISSUE: KT-44802
|
||||||
|
// FILE: a.kt
|
||||||
|
|
||||||
|
package foo
|
||||||
|
import select
|
||||||
|
|
||||||
|
private interface PrivateInterface {
|
||||||
|
fun foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class A : PrivateInterface
|
||||||
|
class B : PrivateInterface
|
||||||
|
|
||||||
|
fun testSmartcast(x: Any) {
|
||||||
|
if (x is A || x is B) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any")!>x<!>.<!UNRESOLVED_REFERENCE!>foo<!>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInference(a: A, b: B) {
|
||||||
|
val x = <!DEBUG_INFO_EXPRESSION_TYPE("foo.PrivateInterface")!>select(a, b)<!>
|
||||||
|
x.foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
package bar
|
||||||
|
|
||||||
|
import foo.A
|
||||||
|
import foo.B
|
||||||
|
import select
|
||||||
|
|
||||||
|
fun testSmartcast(x: Any) {
|
||||||
|
if (x is A || x is B) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any")!>x<!>.<!UNRESOLVED_REFERENCE!>foo<!>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInference(a: A, b: B) {
|
||||||
|
val x = <!DEBUG_INFO_EXPRESSION_TYPE("foo.PrivateInterface")!>select(a, b)<!>
|
||||||
|
x.<!INVISIBLE_MEMBER!>foo<!>()
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun </*0*/ K> id(/*0*/ x: K): K
|
||||||
|
public fun </*0*/ K> materialize(): K
|
||||||
|
public fun </*0*/ K> select(/*0*/ vararg values: K /*kotlin.Array<out K>*/): K
|
||||||
|
|
||||||
|
package bar {
|
||||||
|
public fun testInference(/*0*/ a: foo.A, /*1*/ b: foo.B): kotlin.Unit
|
||||||
|
public fun testSmartcast(/*0*/ x: kotlin.Any): kotlin.Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
package foo {
|
||||||
|
public fun testInference(/*0*/ a: foo.A, /*1*/ b: foo.B): kotlin.Unit
|
||||||
|
public fun testSmartcast(/*0*/ x: kotlin.Any): kotlin.Unit
|
||||||
|
|
||||||
|
public final class A : foo.PrivateInterface {
|
||||||
|
public constructor A()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public 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
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class B : foo.PrivateInterface {
|
||||||
|
public constructor B()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public 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
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface PrivateInterface {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Generated
+12
@@ -25741,6 +25741,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/smartcastOnSameFieldOfDifferentInstances.kt");
|
runTest("compiler/testData/diagnostics/tests/smartCasts/smartcastOnSameFieldOfDifferentInstances.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("smartcastToInvisibleType_java.kt")
|
||||||
|
public void testSmartcastToInvisibleType_java() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/smartCasts/smartcastToInvisibleType_java.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("smartcastToInvisibleType_kotlin.kt")
|
||||||
|
public void testSmartcastToInvisibleType_kotlin() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/smartCasts/smartcastToInvisibleType_kotlin.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("smartcastToNothingAfterCheckingForNull.kt")
|
@TestMetadata("smartcastToNothingAfterCheckingForNull.kt")
|
||||||
public void testSmartcastToNothingAfterCheckingForNull() throws Exception {
|
public void testSmartcastToNothingAfterCheckingForNull() throws Exception {
|
||||||
|
|||||||
+6
@@ -37668,6 +37668,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt44802.kt")
|
||||||
|
public void testKt44802() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/smartCasts/kt44802.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt44804.kt")
|
@TestMetadata("kt44804.kt")
|
||||||
public void testKt44804() throws Exception {
|
public void testKt44804() throws Exception {
|
||||||
|
|||||||
+6
@@ -37668,6 +37668,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt44802.kt")
|
||||||
|
public void testKt44802() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/smartCasts/kt44802.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt44804.kt")
|
@TestMetadata("kt44804.kt")
|
||||||
public void testKt44804() throws Exception {
|
public void testKt44804() throws Exception {
|
||||||
|
|||||||
+5
@@ -30120,6 +30120,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt44802.kt")
|
||||||
|
public void testKt44802() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/smartCasts/kt44802.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kt44804.kt")
|
@TestMetadata("kt44804.kt")
|
||||||
public void testKt44804() throws Exception {
|
public void testKt44804() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/smartCasts/kt44804.kt");
|
runTest("compiler/testData/codegen/box/smartCasts/kt44804.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user