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 package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.builtins.PlatformSpecificCastChecker
import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMapper import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMapper
import org.jetbrains.kotlin.container.* import org.jetbrains.kotlin.container.*
import org.jetbrains.kotlin.resolve.calls.checkers.* import org.jetbrains.kotlin.resolve.calls.checkers.*
@@ -128,6 +129,7 @@ abstract class PlatformConfiguratorBase(
private val identifierChecker: IdentifierChecker? = null, private val identifierChecker: IdentifierChecker? = null,
private val overloadFilter: OverloadFilter? = null, private val overloadFilter: OverloadFilter? = null,
private val platformToKotlinClassMapper: PlatformToKotlinClassMapper? = null, private val platformToKotlinClassMapper: PlatformToKotlinClassMapper? = null,
private val platformSpecificCastChecker: PlatformSpecificCastChecker? = null,
private val delegationFilter: DelegationFilter? = null, private val delegationFilter: DelegationFilter? = null,
private val overridesBackwardCompatibilityHelper: OverridesBackwardCompatibilityHelper? = null, private val overridesBackwardCompatibilityHelper: OverridesBackwardCompatibilityHelper? = null,
private val declarationReturnTypeSanitizer: DeclarationReturnTypeSanitizer? = null private val declarationReturnTypeSanitizer: DeclarationReturnTypeSanitizer? = null
@@ -154,6 +156,7 @@ abstract class PlatformConfiguratorBase(
useInstanceIfNotNull(identifierChecker) useInstanceIfNotNull(identifierChecker)
useInstanceIfNotNull(overloadFilter) useInstanceIfNotNull(overloadFilter)
useInstanceIfNotNull(platformToKotlinClassMapper) useInstanceIfNotNull(platformToKotlinClassMapper)
useInstanceIfNotNull(platformSpecificCastChecker)
useInstanceIfNotNull(delegationFilter) useInstanceIfNotNull(delegationFilter)
useInstanceIfNotNull(overridesBackwardCompatibilityHelper) useInstanceIfNotNull(overridesBackwardCompatibilityHelper)
useInstanceIfNotNull(declarationReturnTypeSanitizer) useInstanceIfNotNull(declarationReturnTypeSanitizer)
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.types
import com.google.common.collect.Maps import com.google.common.collect.Maps
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PlatformSpecificCastChecker
import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMapper import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMapper
import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.builtins.isFunctionType
@@ -41,11 +42,12 @@ object CastDiagnosticsUtil {
fun isCastPossible( fun isCastPossible(
lhsType: KotlinType, lhsType: KotlinType,
rhsType: KotlinType, rhsType: KotlinType,
platformToKotlinClassMapper: PlatformToKotlinClassMapper platformToKotlinClassMapper: PlatformToKotlinClassMapper,
platformSpecificCastChecker: PlatformSpecificCastChecker
): Boolean { ): Boolean {
val typeConstructor = lhsType.constructor val typeConstructor = lhsType.constructor
if (typeConstructor is IntersectionTypeConstructor) { 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 rhsNullable = TypeUtils.isNullableType(rhsType)
val lhsNullable = TypeUtils.isNullableType(lhsType) val lhsNullable = TypeUtils.isNullableType(lhsType)
@@ -59,6 +61,7 @@ object CastDiagnosticsUtil {
// This is an oversimplification (which does not render the method incomplete): // 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 // 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 (TypeUtils.isTypeParameter(lhsType) || TypeUtils.isTypeParameter(rhsType)) return true
if (platformSpecificCastChecker.isCastPossible(lhsType, rhsType)) return true
if (isFinal(lhsType) || isFinal(rhsType)) return false if (isFinal(lhsType) || isFinal(rhsType)) return false
if (isTrait(lhsType) || isTrait(rhsType)) return true if (isTrait(lhsType) || isTrait(rhsType)) return true
@@ -376,7 +376,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return; 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())); context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference()));
return; return;
} }
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.types.expressions;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.PlatformSpecificCastChecker;
import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMapper; import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMapper;
import org.jetbrains.kotlin.config.LanguageVersionSettings; import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.context.GlobalContext; import org.jetbrains.kotlin.context.GlobalContext;
@@ -36,6 +37,7 @@ public class ExpressionTypingComponents {
public ExpressionTypingServices expressionTypingServices; public ExpressionTypingServices expressionTypingServices;
public CallResolver callResolver; public CallResolver callResolver;
public PlatformToKotlinClassMapper platformToKotlinClassMapper; public PlatformToKotlinClassMapper platformToKotlinClassMapper;
public PlatformSpecificCastChecker platformSpecificCastChecker;
public ControlStructureTypingUtils controlStructureTypingUtils; public ControlStructureTypingUtils controlStructureTypingUtils;
public ForLoopConventionsChecker forLoopConventionsChecker; public ForLoopConventionsChecker forLoopConventionsChecker;
public FakeCallResolver fakeCallResolver; public FakeCallResolver fakeCallResolver;
@@ -100,6 +102,11 @@ public class ExpressionTypingComponents {
this.platformToKotlinClassMapper = platformToKotlinClassMapper; this.platformToKotlinClassMapper = platformToKotlinClassMapper;
} }
@Inject
public void setPlatformToKotlinClassMap(@NotNull PlatformSpecificCastChecker platformSpecificCastChecker) {
this.platformSpecificCastChecker = platformSpecificCastChecker;
}
@Inject @Inject
public void setControlStructureTypingUtils(@NotNull ControlStructureTypingUtils controlStructureTypingUtils) { public void setControlStructureTypingUtils(@NotNull ControlStructureTypingUtils controlStructureTypingUtils) {
this.controlStructureTypingUtils = controlStructureTypingUtils; this.controlStructureTypingUtils = controlStructureTypingUtils;
@@ -678,7 +678,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
.takeIf { it.isNotEmpty() } .takeIf { it.isNotEmpty() }
?: possibleTypes ?: 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)) 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<!> 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 testCheckedAs1(x : lib.FwdStruct) = x as cnames.structs.FwdStruct
fun testCheckedAs2(x : lib.FwdObjcClass) = x <!CAST_NEVER_SUCCEEDS!>as<!> objcnames.classes.FwdObjcClass fun testCheckedAs2(x : lib.FwdObjcClass) = x as objcnames.classes.FwdObjcClass
fun testCheckedAs3(x : lib.FwdProtocol) = x as objcnames.protocols.FwdProtocol fun testCheckedAs3(x : lib.FwdProtocol) = x as objcnames.protocols.FwdProtocol
fun testCheckedSafeAs4(x : lib.FwdStruct) = x <!CAST_NEVER_SUCCEEDS!>as?<!> cnames.structs.FwdStruct fun testCheckedSafeAs4(x : lib.FwdStruct) = x as? cnames.structs.FwdStruct
fun testCheckedSafeAs5(x : lib.FwdObjcClass) = x <!CAST_NEVER_SUCCEEDS!>as?<!> objcnames.classes.FwdObjcClass fun testCheckedSafeAs5(x : lib.FwdObjcClass) = x as? objcnames.classes.FwdObjcClass
fun testCheckedSafeAs6(x : lib.FwdProtocol) = x as? objcnames.protocols.FwdProtocol 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 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 <!CAST_NEVER_SUCCEEDS!>as<!> objcnames.classes.FwdObjcClass<!> 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 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, NativeObjCNameChecker, NativeObjCNameOverridesChecker,
NativeObjCRefinementChecker, NativeObjCRefinementAnnotationChecker, NativeObjCRefinementChecker, NativeObjCRefinementAnnotationChecker,
NativeObjCRefinementOverridesChecker, NativeHiddenFromObjCInheritanceChecker, NativeObjCRefinementOverridesChecker, NativeHiddenFromObjCInheritanceChecker,
) ),
platformSpecificCastChecker = NativePlatformSpecificCastChecker
) { ) {
override fun configureModuleComponents(container: StorageComponentContainer) { override fun configureModuleComponents(container: StorageComponentContainer) {
container.useInstance(NativeInliningRule) container.useInstance(NativeInliningRule)