Don't emit CAST_NEVER_SUCCEEDS when casting to a forward declaration type

^KT-58929
This commit is contained in:
Timofey Solonin
2023-09-11 11:13:55 +02:00
committed by Space Team
parent 28f90b3ea8
commit 53584cdd49
9 changed files with 73 additions and 12 deletions
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.builtins.PlatformSpecificCastChecker
import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMapper
import org.jetbrains.kotlin.container.*
import org.jetbrains.kotlin.resolve.calls.checkers.*
@@ -128,6 +129,7 @@ abstract class PlatformConfiguratorBase(
private val identifierChecker: IdentifierChecker? = null,
private val overloadFilter: OverloadFilter? = null,
private val platformToKotlinClassMapper: PlatformToKotlinClassMapper? = null,
private val platformSpecificCastChecker: PlatformSpecificCastChecker? = null,
private val delegationFilter: DelegationFilter? = null,
private val overridesBackwardCompatibilityHelper: OverridesBackwardCompatibilityHelper? = null,
private val declarationReturnTypeSanitizer: DeclarationReturnTypeSanitizer? = null
@@ -154,6 +156,7 @@ abstract class PlatformConfiguratorBase(
useInstanceIfNotNull(identifierChecker)
useInstanceIfNotNull(overloadFilter)
useInstanceIfNotNull(platformToKotlinClassMapper)
useInstanceIfNotNull(platformSpecificCastChecker)
useInstanceIfNotNull(delegationFilter)
useInstanceIfNotNull(overridesBackwardCompatibilityHelper)
useInstanceIfNotNull(declarationReturnTypeSanitizer)
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.types
import com.google.common.collect.Maps
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PlatformSpecificCastChecker
import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMapper
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
@@ -41,11 +42,12 @@ object CastDiagnosticsUtil {
fun isCastPossible(
lhsType: KotlinType,
rhsType: KotlinType,
platformToKotlinClassMapper: PlatformToKotlinClassMapper
platformToKotlinClassMapper: PlatformToKotlinClassMapper,
platformSpecificCastChecker: PlatformSpecificCastChecker
): Boolean {
val typeConstructor = lhsType.constructor
if (typeConstructor is IntersectionTypeConstructor) {
return typeConstructor.supertypes.any { isCastPossible(it, rhsType, platformToKotlinClassMapper) }
return typeConstructor.supertypes.any { isCastPossible(it, rhsType, platformToKotlinClassMapper, platformSpecificCastChecker) }
}
val rhsNullable = TypeUtils.isNullableType(rhsType)
val lhsNullable = TypeUtils.isNullableType(lhsType)
@@ -59,6 +61,7 @@ object CastDiagnosticsUtil {
// This is an oversimplification (which does not render the method incomplete):
// we consider any type parameter capable of taking any value, which may be made more precise if we considered bounds
if (TypeUtils.isTypeParameter(lhsType) || TypeUtils.isTypeParameter(rhsType)) return true
if (platformSpecificCastChecker.isCastPossible(lhsType, rhsType)) return true
if (isFinal(lhsType) || isFinal(rhsType)) return false
if (isTrait(lhsType) || isTrait(rhsType)) return true
@@ -376,7 +376,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return;
}
if (!TypeUtilsKt.isStubType(actualType) && !CastDiagnosticsUtil.isCastPossible(actualType, targetType, components.platformToKotlinClassMapper)) {
if (!TypeUtilsKt.isStubType(actualType) && !CastDiagnosticsUtil.isCastPossible(actualType, targetType, components.platformToKotlinClassMapper, components.platformSpecificCastChecker)) {
context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference()));
return;
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.types.expressions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.PlatformSpecificCastChecker;
import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMapper;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.context.GlobalContext;
@@ -36,6 +37,7 @@ public class ExpressionTypingComponents {
public ExpressionTypingServices expressionTypingServices;
public CallResolver callResolver;
public PlatformToKotlinClassMapper platformToKotlinClassMapper;
public PlatformSpecificCastChecker platformSpecificCastChecker;
public ControlStructureTypingUtils controlStructureTypingUtils;
public ForLoopConventionsChecker forLoopConventionsChecker;
public FakeCallResolver fakeCallResolver;
@@ -100,6 +102,11 @@ public class ExpressionTypingComponents {
this.platformToKotlinClassMapper = platformToKotlinClassMapper;
}
@Inject
public void setPlatformToKotlinClassMap(@NotNull PlatformSpecificCastChecker platformSpecificCastChecker) {
this.platformSpecificCastChecker = platformSpecificCastChecker;
}
@Inject
public void setControlStructureTypingUtils(@NotNull ControlStructureTypingUtils controlStructureTypingUtils) {
this.controlStructureTypingUtils = controlStructureTypingUtils;
@@ -678,7 +678,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
.takeIf { it.isNotEmpty() }
?: possibleTypes
if (nonTrivialTypes.none { CastDiagnosticsUtil.isCastPossible(it, targetType, components.platformToKotlinClassMapper) }) {
if (nonTrivialTypes.none { CastDiagnosticsUtil.isCastPossible(it, targetType, components.platformToKotlinClassMapper, components.platformSpecificCastChecker) }) {
context.trace.report(USELESS_IS_CHECK.on(isCheck, negated))
}
}
@@ -92,15 +92,15 @@ fun testInline4() {
val c : (objcnames.protocols.FwdProtocol) -> Unit = ::<!FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT!>inlineF<!>
}
fun testCheckedAs1(x : lib.FwdStruct) = x <!CAST_NEVER_SUCCEEDS!>as<!> cnames.structs.FwdStruct
fun testCheckedAs2(x : lib.FwdObjcClass) = x <!CAST_NEVER_SUCCEEDS!>as<!> objcnames.classes.FwdObjcClass
fun testCheckedAs1(x : lib.FwdStruct) = x as cnames.structs.FwdStruct
fun testCheckedAs2(x : lib.FwdObjcClass) = x as objcnames.classes.FwdObjcClass
fun testCheckedAs3(x : lib.FwdProtocol) = x as objcnames.protocols.FwdProtocol
fun testCheckedSafeAs4(x : lib.FwdStruct) = x <!CAST_NEVER_SUCCEEDS!>as?<!> cnames.structs.FwdStruct
fun testCheckedSafeAs5(x : lib.FwdObjcClass) = x <!CAST_NEVER_SUCCEEDS!>as?<!> objcnames.classes.FwdObjcClass
fun testCheckedSafeAs4(x : lib.FwdStruct) = x as? cnames.structs.FwdStruct
fun testCheckedSafeAs5(x : lib.FwdObjcClass) = x as? objcnames.classes.FwdObjcClass
fun testCheckedSafeAs6(x : lib.FwdProtocol) = x as? objcnames.protocols.FwdProtocol
fun testUnCheckedAs1(x : lib2.FwdStruct) = <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x <!CAST_NEVER_SUCCEEDS!>as<!> cnames.structs.FwdStruct<!>
fun testUnCheckedAs2(x : lib2.FwdObjcClass) = <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x <!CAST_NEVER_SUCCEEDS!>as<!> objcnames.classes.FwdObjcClass<!>
fun testUnCheckedAs1(x : lib2.FwdStruct) = <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as cnames.structs.FwdStruct<!>
fun testUnCheckedAs2(x : lib2.FwdObjcClass) = <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as objcnames.classes.FwdObjcClass<!>
fun testUnCheckedAs3(x : lib2.FwdProtocol) = <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as objcnames.protocols.FwdProtocol<!>
fun testUnCheckedAs4(x : lib.FwdStruct) = <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x <!CAST_NEVER_SUCCEEDS!>as<!> objcnames.classes.FwdObjcClass<!>
fun testUnCheckedAs4(x : lib.FwdStruct) = <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as objcnames.classes.FwdObjcClass<!>
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2018 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.builtins
import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.types.KotlinType
@DefaultImplementation(impl = PlatformSpecificCastChecker.Default::class)
interface PlatformSpecificCastChecker {
fun isCastPossible(fromType: KotlinType, toType: KotlinType): Boolean
class Default : PlatformSpecificCastChecker {
override fun isCastPossible(fromType: KotlinType, toType: KotlinType): Boolean {
return false
}
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2023 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.resolve.konan.diagnostics
import org.jetbrains.kotlin.builtins.PlatformSpecificCastChecker
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.konan.getForwardDeclarationKindOrNull
import org.jetbrains.kotlin.types.KotlinType
object NativePlatformSpecificCastChecker : PlatformSpecificCastChecker {
override fun isCastPossible(fromType: KotlinType, toType: KotlinType): Boolean {
return isCastToAForwardDeclaration(toType)
}
/**
* Here, we only check that we are casting to a forward declaration to suppress a CAST_NEVER_SUCCEEDS warning. The cast is further
* checked in NativeForwardDeclarationRttiChecker.
*/
private fun isCastToAForwardDeclaration(forwardDeclarationType: KotlinType): Boolean {
val forwardDeclarationClassDescriptor = forwardDeclarationType.constructor.declarationDescriptor
if (forwardDeclarationClassDescriptor !is ClassDescriptor) return false
return forwardDeclarationClassDescriptor.getForwardDeclarationKindOrNull() != null
}
}
@@ -30,7 +30,8 @@ object NativePlatformConfigurator : PlatformConfiguratorBase(
NativeObjCNameChecker, NativeObjCNameOverridesChecker,
NativeObjCRefinementChecker, NativeObjCRefinementAnnotationChecker,
NativeObjCRefinementOverridesChecker, NativeHiddenFromObjCInheritanceChecker,
)
),
platformSpecificCastChecker = NativePlatformSpecificCastChecker
) {
override fun configureModuleComponents(container: StorageComponentContainer) {
container.useInstance(NativeInliningRule)