diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index 7f90d76b7d8..2b30e69dc2c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -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) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/CastDiagnosticsUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/CastDiagnosticsUtil.kt index c2926616969..82f9e80f8f5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/CastDiagnosticsUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/CastDiagnosticsUtil.kt @@ -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 diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 3cd124d362f..9b87163c90c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -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; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java index 0f642b0fdcd..d3a360563d7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java @@ -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; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt index e6d91246fec..49fc1b0d367 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt @@ -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)) } } diff --git a/compiler/testData/diagnostics/nativeTests/forwardDeclarations.kt b/compiler/testData/diagnostics/nativeTests/forwardDeclarations.kt index 8af4d3ab03f..7a31f662360 100644 --- a/compiler/testData/diagnostics/nativeTests/forwardDeclarations.kt +++ b/compiler/testData/diagnostics/nativeTests/forwardDeclarations.kt @@ -92,15 +92,15 @@ fun testInline4() { val c : (objcnames.protocols.FwdProtocol) -> Unit = ::inlineF } -fun testCheckedAs1(x : lib.FwdStruct) = x as cnames.structs.FwdStruct -fun testCheckedAs2(x : lib.FwdObjcClass) = x 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 as? cnames.structs.FwdStruct -fun testCheckedSafeAs5(x : lib.FwdObjcClass) = x 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) = x as cnames.structs.FwdStruct -fun testUnCheckedAs2(x : lib2.FwdObjcClass) = x as objcnames.classes.FwdObjcClass +fun testUnCheckedAs1(x : lib2.FwdStruct) = x as cnames.structs.FwdStruct +fun testUnCheckedAs2(x : lib2.FwdObjcClass) = x as objcnames.classes.FwdObjcClass fun testUnCheckedAs3(x : lib2.FwdProtocol) = x as objcnames.protocols.FwdProtocol -fun testUnCheckedAs4(x : lib.FwdStruct) = x as objcnames.classes.FwdObjcClass +fun testUnCheckedAs4(x : lib.FwdStruct) = x as objcnames.classes.FwdObjcClass diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/PlatformSpecificCastChecker.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/PlatformSpecificCastChecker.kt new file mode 100644 index 00000000000..cb96ecebdcb --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/PlatformSpecificCastChecker.kt @@ -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 + } + } +} diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativePlatformSpecificCastChecker.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativePlatformSpecificCastChecker.kt new file mode 100644 index 00000000000..2804a2a10e2 --- /dev/null +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativePlatformSpecificCastChecker.kt @@ -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 + } + +} \ No newline at end of file diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt index 2774ccc7ea5..f070c6f903a 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt @@ -30,7 +30,8 @@ object NativePlatformConfigurator : PlatformConfiguratorBase( NativeObjCNameChecker, NativeObjCNameOverridesChecker, NativeObjCRefinementChecker, NativeObjCRefinementAnnotationChecker, NativeObjCRefinementOverridesChecker, NativeHiddenFromObjCInheritanceChecker, - ) + ), + platformSpecificCastChecker = NativePlatformSpecificCastChecker ) { override fun configureModuleComponents(container: StorageComponentContainer) { container.useInstance(NativeInliningRule)