[FIR] Utilize equality compatibility logic for cast checks
This makes it more consistent and fixes some overlooked corner cases. Also it was decided on the last equality applicability DM (KT-62646) that we'd like `is`/`!is`/`as`/`as?` to work similarly to `===`/`!==`. Also note that it now gives a clearer explaination of why some corner cases work the way they do. For example, `FirPsiDiagnosticTestGenerated.testLambdaInLhsOfTypeOperatorCall` yields `UNCHECKED_CAST` instead of `CAST_NEVER_SUCCEEDS`, because `toTypeInfo()` replaces all type arguments with star projections, even when the argument is not a type parameter. This is because it has been desided to work this way in KT-57779. In `FirPsiOldFrontendDiagnosticsTestGenerated..NeverSucceeds#testNoGenericsRelated` the diagnostic is introduced, because `t2 as FC1` and `FC1` is a final class with no `T5` supertype. `UNCHECKED_CAST` in `FirPsiOldFrontendDiagnosticsTestGenerated.testSmartCast` disappeared, because previously we didn't take smartcasts into account. Note that `FirPsiOldFrontendDiagnosticsTestGenerated.testMappedSubtypes` is a false positive. It appears because `isSubtypeOf()` doesn't take into account platform types in supertypes of the given types (doesn't map them).
This commit is contained in:
committed by
Space Team
parent
a5e43c9e3f
commit
fab6cec93a
+1
-1
@@ -9,7 +9,7 @@ fun test_1(b: B<String, Number>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun test_2(s: String) {
|
fun test_2(s: String) {
|
||||||
val func = { s.length } <!UNCHECKED_CAST!>as B<Int, Int><!>
|
val func = { s.length } <!CAST_NEVER_SUCCEEDS!>as<!> B<Int, Int>
|
||||||
}
|
}
|
||||||
|
|
||||||
class B<out K, V>(val k: K, val v: V)
|
class B<out K, V>(val k: K, val v: V)
|
||||||
|
|||||||
+1
-1
@@ -79,6 +79,6 @@ fun Any.test_5(): Int = when {
|
|||||||
fun Any.test_6() {
|
fun Any.test_6() {
|
||||||
this as List<*>
|
this as List<*>
|
||||||
size
|
size
|
||||||
this as String
|
this <!CAST_NEVER_SUCCEEDS!>as<!> String
|
||||||
length
|
length
|
||||||
}
|
}
|
||||||
|
|||||||
-121
@@ -5,139 +5,18 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.analysis.checkers
|
package org.jetbrains.kotlin.fir.analysis.checkers
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||||
import org.jetbrains.kotlin.fir.resolve.getClassAndItsOuterClassesWhenLocal
|
import org.jetbrains.kotlin.fir.resolve.getClassAndItsOuterClassesWhenLocal
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
|
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
|
||||||
import org.jetbrains.kotlin.fir.scopes.platformClassMapper
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||||
import org.jetbrains.kotlin.types.AbstractTypeChecker.findCorrespondingSupertypes
|
import org.jetbrains.kotlin.types.AbstractTypeChecker.findCorrespondingSupertypes
|
||||||
import org.jetbrains.kotlin.types.TypeCheckerState
|
|
||||||
import org.jetbrains.kotlin.types.model.typeConstructor
|
import org.jetbrains.kotlin.types.model.typeConstructor
|
||||||
|
|
||||||
enum class CastingType {
|
|
||||||
Possible,
|
|
||||||
Impossible,
|
|
||||||
Always
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkCasting(
|
|
||||||
lhsType: ConeKotlinType,
|
|
||||||
rhsType: ConeKotlinType,
|
|
||||||
isSafeCase: Boolean,
|
|
||||||
context: CheckerContext
|
|
||||||
): CastingType {
|
|
||||||
val lhsLowerType = lhsType.lowerBoundIfFlexible().originalIfDefinitelyNotNullable()
|
|
||||||
val rhsLowerType = rhsType.lowerBoundIfFlexible().originalIfDefinitelyNotNullable()
|
|
||||||
|
|
||||||
if (lhsLowerType is ConeErrorType || rhsLowerType is ConeErrorType) return CastingType.Possible
|
|
||||||
|
|
||||||
val session = context.session
|
|
||||||
|
|
||||||
if (lhsLowerType is ConeIntersectionType) {
|
|
||||||
var result = false
|
|
||||||
for (intersectedType in lhsLowerType.intersectedTypes) {
|
|
||||||
val isIntersectedCastPossible = checkCasting(intersectedType, rhsLowerType, isSafeCase, context)
|
|
||||||
val intersectedTypeSymbol = intersectedType.toRegularClassSymbol(session)
|
|
||||||
if (intersectedTypeSymbol?.isInterface == false && isIntersectedCastPossible == CastingType.Impossible) {
|
|
||||||
return CastingType.Impossible // Any class type in intersection type should be subtype of RHS
|
|
||||||
}
|
|
||||||
result = result or (isIntersectedCastPossible != CastingType.Impossible)
|
|
||||||
}
|
|
||||||
|
|
||||||
return if (result) CastingType.Possible else CastingType.Impossible
|
|
||||||
}
|
|
||||||
|
|
||||||
val lhsNullable = lhsLowerType.canBeNull(session)
|
|
||||||
val rhsNullable = rhsLowerType.canBeNull(session)
|
|
||||||
if (lhsLowerType.isNothing) return CastingType.Possible
|
|
||||||
if (lhsLowerType.isNullableNothing && !rhsNullable) {
|
|
||||||
return if (isSafeCase) CastingType.Always else CastingType.Impossible
|
|
||||||
}
|
|
||||||
if (rhsLowerType.isNothing) return CastingType.Impossible
|
|
||||||
if (rhsLowerType.isNullableNothing) {
|
|
||||||
return if (lhsNullable) CastingType.Possible else CastingType.Impossible
|
|
||||||
}
|
|
||||||
if (lhsNullable && rhsNullable) return CastingType.Possible
|
|
||||||
|
|
||||||
// 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 (lhsLowerType is ConeTypeParameterType || rhsLowerType is ConeTypeParameterType) return CastingType.Possible
|
|
||||||
|
|
||||||
val lhsClassSymbol = lhsLowerType.toRegularClassSymbol(session)
|
|
||||||
val rhsClassSymbol = rhsLowerType.toRegularClassSymbol(session)
|
|
||||||
val lhsNormalizedType = getCorrespondingKotlinClass(lhsClassSymbol?.defaultType() ?: lhsLowerType, session)
|
|
||||||
val rhsNormalizedType = getCorrespondingKotlinClass(rhsClassSymbol?.defaultType() ?: rhsLowerType, session)
|
|
||||||
|
|
||||||
val state = session.typeContext.newTypeCheckerState(errorTypesEqualToAnything = false, stubTypesEqualToAnything = false)
|
|
||||||
|
|
||||||
// It's an optimization, the code below with `isRoughSubtypeOf` also checks subtyping, but it's slower
|
|
||||||
if (AbstractTypeChecker.isSubtypeOf(state, lhsNormalizedType, rhsNormalizedType) ||
|
|
||||||
AbstractTypeChecker.isSubtypeOf(state, rhsNormalizedType, lhsNormalizedType)
|
|
||||||
) {
|
|
||||||
return CastingType.Possible
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRoughSubtypeOf(lhsNormalizedType, rhsNormalizedType, state, session) ||
|
|
||||||
isRoughSubtypeOf(rhsNormalizedType, lhsNormalizedType, state, session)
|
|
||||||
) {
|
|
||||||
return CastingType.Possible
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFinal(lhsNormalizedType, session) || isFinal(rhsNormalizedType, session)) return CastingType.Impossible
|
|
||||||
|
|
||||||
val lhsNormalizedTypeSymbol = lhsNormalizedType.toSymbol(session) as? FirClassSymbol<*>
|
|
||||||
val rhsNormalizedTypeSymbol = rhsNormalizedType.toSymbol(session) as? FirClassSymbol<*>
|
|
||||||
if (lhsNormalizedTypeSymbol?.isInterface == true || rhsNormalizedTypeSymbol?.isInterface == true) return CastingType.Possible
|
|
||||||
|
|
||||||
return CastingType.Impossible
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* One type is roughly subtype of another superType when one of type's supertype constructor equals another superType constructor.
|
|
||||||
*
|
|
||||||
* Note that some types have platform-specific counterparts, i.e. kotlin.String is mapped to java.lang.String,
|
|
||||||
* such types (and all their sub- and supertypes) are related too.
|
|
||||||
*
|
|
||||||
* Due to limitations in PlatformToKotlinClassMap, we only consider mapping of platform classes to Kotlin classed
|
|
||||||
* (i.e. java.lang.String -> kotlin.String) and ignore mappings that go the other way.
|
|
||||||
*/
|
|
||||||
private fun isRoughSubtypeOf(
|
|
||||||
type: ConeSimpleKotlinType,
|
|
||||||
superType: ConeSimpleKotlinType,
|
|
||||||
state: TypeCheckerState,
|
|
||||||
session: FirSession
|
|
||||||
): Boolean {
|
|
||||||
var result = false
|
|
||||||
val superTypeConstructor = superType.typeConstructor(state.typeSystemContext)
|
|
||||||
state.anySupertype(type, { typeMarker ->
|
|
||||||
val correspondingKotlinClass = getCorrespondingKotlinClass(typeMarker as ConeSimpleKotlinType, session)
|
|
||||||
if (correspondingKotlinClass.typeConstructor(state.typeSystemContext) == superTypeConstructor) {
|
|
||||||
result = true
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}, { TypeCheckerState.SupertypesPolicy.LowerIfFlexible })
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getCorrespondingKotlinClass(type: ConeSimpleKotlinType, session: FirSession): ConeSimpleKotlinType {
|
|
||||||
return session.platformClassMapper.getCorrespondingKotlinClass(type.classId)?.defaultType(emptyList()) ?: type
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isFinal(type: ConeSimpleKotlinType, session: FirSession): Boolean {
|
|
||||||
return !type.canHaveSubtypesAccordingToK1(session)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isCastErased(supertype: ConeKotlinType, subtype: ConeKotlinType, context: CheckerContext): Boolean {
|
fun isCastErased(supertype: ConeKotlinType, subtype: ConeKotlinType, context: CheckerContext): Boolean {
|
||||||
val typeContext = context.session.typeContext
|
val typeContext = context.session.typeContext
|
||||||
|
|
||||||
|
|||||||
+10
-6
@@ -32,11 +32,13 @@ internal fun FirExpression.unwrapToMoreUsefulExpression() = when (this) {
|
|||||||
internal class TypeInfo(
|
internal class TypeInfo(
|
||||||
val type: ConeKotlinType,
|
val type: ConeKotlinType,
|
||||||
val notNullType: ConeKotlinType,
|
val notNullType: ConeKotlinType,
|
||||||
|
val directType: ConeKotlinType,
|
||||||
val isEnumClass: Boolean,
|
val isEnumClass: Boolean,
|
||||||
val isPrimitive: Boolean,
|
val isPrimitive: Boolean,
|
||||||
val isBuiltin: Boolean,
|
val isBuiltin: Boolean,
|
||||||
val isValueClass: Boolean,
|
val isValueClass: Boolean,
|
||||||
val isFinal: Boolean,
|
val isFinal: Boolean,
|
||||||
|
val isClass: Boolean,
|
||||||
val canHaveSubtypesAccordingToK1: Boolean,
|
val canHaveSubtypesAccordingToK1: Boolean,
|
||||||
) {
|
) {
|
||||||
override fun toString() = "$type"
|
override fun toString() = "$type"
|
||||||
@@ -64,20 +66,22 @@ internal fun ConeKotlinType.toTypeInfo(session: FirSession): TypeInfo {
|
|||||||
val type = bounds.ifNotEmpty { ConeTypeIntersector.intersectTypes(session.typeContext, this) }
|
val type = bounds.ifNotEmpty { ConeTypeIntersector.intersectTypes(session.typeContext, this) }
|
||||||
?: session.builtinTypes.nullableAnyType.type
|
?: session.builtinTypes.nullableAnyType.type
|
||||||
val notNullType = type.withNullability(ConeNullability.NOT_NULL, session.typeContext)
|
val notNullType = type.withNullability(ConeNullability.NOT_NULL, session.typeContext)
|
||||||
|
val boundsSymbols = bounds.mapNotNull { it.toClassSymbol(session) }
|
||||||
|
|
||||||
return TypeInfo(
|
return TypeInfo(
|
||||||
type, notNullType,
|
type, notNullType, directType = this,
|
||||||
isEnumClass = bounds.any { it.isEnum(session) },
|
isEnumClass = boundsSymbols.any { it.isEnumClass },
|
||||||
isPrimitive = bounds.any { it.isPrimitiveOrNullablePrimitive },
|
isPrimitive = bounds.any { it.isPrimitiveOrNullablePrimitive },
|
||||||
isBuiltin = bounds.any { it.toClassSymbol(session)?.isBuiltin == true },
|
isBuiltin = boundsSymbols.any { it.isBuiltin },
|
||||||
isValueClass = bounds.any { it.toClassSymbol(session)?.isInline == true },
|
isValueClass = boundsSymbols.any { it.isInline },
|
||||||
isFinal = bounds.any { it.toClassSymbol(session)?.isFinalClass == true },
|
isFinal = boundsSymbols.any { it.isFinalClass },
|
||||||
|
isClass = boundsSymbols.any { it.isClass },
|
||||||
// In K1's intersector, `canHaveSubtypes()` is called for `nullabilityStripped`.
|
// In K1's intersector, `canHaveSubtypes()` is called for `nullabilityStripped`.
|
||||||
withNullability(ConeNullability.NOT_NULL, session.typeContext).canHaveSubtypesAccordingToK1(session),
|
withNullability(ConeNullability.NOT_NULL, session.typeContext).canHaveSubtypesAccordingToK1(session),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ConeClassLikeType.toKotlinTypeIfPlatform(session: FirSession): ConeClassLikeType {
|
internal fun ConeClassLikeType.toKotlinTypeIfPlatform(session: FirSession): ConeClassLikeType {
|
||||||
val kotlinClassId = session.platformClassMapper.getCorrespondingKotlinClass(lookupTag.classId)
|
val kotlinClassId = session.platformClassMapper.getCorrespondingKotlinClass(lookupTag.classId)
|
||||||
return kotlinClassId?.constructClassLikeType(typeArguments, isNullable, attributes) ?: this
|
return kotlinClassId?.constructClassLikeType(typeArguments, isNullable, attributes) ?: this
|
||||||
}
|
}
|
||||||
|
|||||||
+117
-38
@@ -8,54 +8,133 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
|||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.CastingType
|
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.checkCasting
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.finalApproximationOrSelf
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.isCastErased
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirOperation
|
import org.jetbrains.kotlin.fir.expressions.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall
|
|
||||||
import org.jetbrains.kotlin.fir.expressions.unwrapSmartcastExpression
|
|
||||||
import org.jetbrains.kotlin.fir.firPlatformSpecificCastChecker
|
import org.jetbrains.kotlin.fir.firPlatformSpecificCastChecker
|
||||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||||
import org.jetbrains.kotlin.fir.types.ConeDynamicType
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.types.coneType
|
|
||||||
import org.jetbrains.kotlin.fir.types.resolvedType
|
|
||||||
|
|
||||||
object FirCastOperatorsChecker : FirTypeOperatorCallChecker(MppCheckerKind.Common) {
|
object FirCastOperatorsChecker : FirTypeOperatorCallChecker(MppCheckerKind.Common) {
|
||||||
override fun check(expression: FirTypeOperatorCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
override fun check(expression: FirTypeOperatorCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
val session = context.session
|
val arguments = expression.argumentList.arguments
|
||||||
val firstArgument = expression.argumentList.arguments[0]
|
require(arguments.size == 1) { "Type operator call with non-1 arguments" }
|
||||||
val actualType = firstArgument.unwrapSmartcastExpression().resolvedType.fullyExpandedType(session).finalApproximationOrSelf(context)
|
|
||||||
val conversionTypeRef = expression.conversionTypeRef
|
|
||||||
val targetType = conversionTypeRef.coneType.fullyExpandedType(session).finalApproximationOrSelf(context)
|
|
||||||
|
|
||||||
if (expression.operation in FirOperation.TYPES && targetType is ConeDynamicType) {
|
val l = arguments[0].toArgumentInfo(context)
|
||||||
reporter.reportOn(conversionTypeRef.source, FirErrors.DYNAMIC_NOT_ALLOWED, context)
|
val r = expression.conversionTypeRef.coneType
|
||||||
|
.fullyExpandedType(context.session)
|
||||||
|
.finalApproximationOrSelf(context)
|
||||||
|
.toTypeInfo(context.session)
|
||||||
|
|
||||||
|
if (expression.operation in FirOperation.TYPES && r.directType is ConeDynamicType) {
|
||||||
|
reporter.reportOn(expression.conversionTypeRef.source, FirErrors.DYNAMIC_NOT_ALLOWED, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isSafeAs = expression.operation == FirOperation.SAFE_AS
|
val checkApplicability = when (expression.operation) {
|
||||||
if (expression.operation == FirOperation.AS || isSafeAs) {
|
FirOperation.IS, FirOperation.NOT_IS -> ::checkIsApplicability
|
||||||
val castType = checkCasting(actualType, targetType, isSafeAs, context)
|
FirOperation.AS, FirOperation.SAFE_AS -> ::checkAsApplicability
|
||||||
if (castType == CastingType.Impossible) {
|
else -> error("Invalid operator of FirTypeOperatorCall")
|
||||||
if (context.languageVersionSettings.supportsFeature(LanguageFeature.EnableDfaWarningsInK2)) {
|
}
|
||||||
if (!session.firPlatformSpecificCastChecker.shouldSuppressImpossibleCast(session, actualType, targetType)) {
|
|
||||||
reporter.reportOn(expression.source, FirErrors.CAST_NEVER_SUCCEEDS, context)
|
val rUserType = expression.conversionTypeRef.coneType.finalApproximationOrSelf(context)
|
||||||
}
|
|
||||||
}
|
// No need to check original types separately from smartcast types, because we only report warnings
|
||||||
} else if (castType == CastingType.Always) {
|
checkApplicability(l.smartCastTypeInfo, r, expression, context).ifInapplicable {
|
||||||
if (context.languageVersionSettings.supportsFeature(LanguageFeature.EnableDfaWarningsInK2)) {
|
return reporter.reportInapplicabilityDiagnostic(expression, it, l.originalTypeInfo, r.type, l.userType, rUserType, context)
|
||||||
reporter.reportOn(expression.source, FirErrors.USELESS_CAST, context)
|
|
||||||
}
|
|
||||||
} else if (isCastErased(actualType, targetType, context)) {
|
|
||||||
reporter.reportOn(expression.source, FirErrors.UNCHECKED_CAST, actualType, targetType, context)
|
|
||||||
}
|
|
||||||
} else if (expression.operation == FirOperation.IS) {
|
|
||||||
if (isCastErased(actualType, targetType, context)) {
|
|
||||||
reporter.reportOn(conversionTypeRef.source, FirErrors.CANNOT_CHECK_FOR_ERASED, targetType, context)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDE doesn't care this function is referenced as :: and then used polymorphicly
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun checkIsApplicability(l: TypeInfo, r: TypeInfo, expression: FirTypeOperatorCall, context: CheckerContext): Applicability {
|
||||||
|
return when {
|
||||||
|
isCastErased(l.directType, r.directType, context) -> Applicability.CAST_ERASED
|
||||||
|
else -> Applicability.APPLICABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkAsApplicability(l: TypeInfo, r: TypeInfo, expression: FirTypeOperatorCall, context: CheckerContext): Applicability {
|
||||||
|
val oneIsFinal = l.isFinal || r.isFinal
|
||||||
|
val oneIsNotNull = !l.type.isNullable || !r.type.isNullable
|
||||||
|
val isNullableNothingWithNotNull = !l.type.isNullable && r.type.isNullableNothing
|
||||||
|
|| l.type.isNullableNothing && !r.type.isNullable
|
||||||
|
|
||||||
|
return when {
|
||||||
|
l.type.isNothing && r.type.isNothingOrNullableNothing -> Applicability.APPLICABLE
|
||||||
|
r.type.isNothing -> Applicability.IMPOSSIBLE
|
||||||
|
isNullableNothingWithNotNull -> when (expression.operation) {
|
||||||
|
// (null as? WhatEver) == null
|
||||||
|
FirOperation.SAFE_AS -> Applicability.USELESS
|
||||||
|
else -> Applicability.IMPOSSIBLE
|
||||||
|
}
|
||||||
|
oneIsNotNull && oneIsFinal && areUnrelated(l, r, context) -> Applicability.IMPOSSIBLE
|
||||||
|
isCastErased(l.directType, r.directType, context) -> Applicability.CAST_ERASED
|
||||||
|
else -> Applicability.APPLICABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun areUnrelated(a: TypeInfo, b: TypeInfo, context: CheckerContext) =
|
||||||
|
!a.isSubtypeOf(b, context) && !b.isSubtypeOf(a, context)
|
||||||
|
|
||||||
|
private fun TypeInfo.isSubtypeOf(other: TypeInfo, context: CheckerContext) =
|
||||||
|
notNullType.isSubtypeOf(other.notNullType, context.session)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* K1 reports different diagnostics for different
|
||||||
|
* cases, and this enum helps to replicate the K1's
|
||||||
|
* choice of diagnostics.
|
||||||
|
*
|
||||||
|
* Should the K2's diagnostic severity differ,
|
||||||
|
* the proper version will be picked later
|
||||||
|
* when reporting the diagnostic.
|
||||||
|
*/
|
||||||
|
private enum class Applicability {
|
||||||
|
APPLICABLE,
|
||||||
|
IMPOSSIBLE,
|
||||||
|
USELESS,
|
||||||
|
CAST_ERASED,
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun Applicability.ifInapplicable(block: (Applicability) -> Unit) = when (this) {
|
||||||
|
Applicability.APPLICABLE -> {}
|
||||||
|
else -> block(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DiagnosticReporter.reportInapplicabilityDiagnostic(
|
||||||
|
expression: FirTypeOperatorCall,
|
||||||
|
applicability: Applicability,
|
||||||
|
l: TypeInfo,
|
||||||
|
r: ConeKotlinType,
|
||||||
|
lUserType: ConeKotlinType,
|
||||||
|
rUserType: ConeKotlinType,
|
||||||
|
context: CheckerContext,
|
||||||
|
) {
|
||||||
|
when (applicability) {
|
||||||
|
Applicability.IMPOSSIBLE -> getImpossibilityDiagnostic(l, r, context)?.let {
|
||||||
|
reportOn(expression.source, it, context)
|
||||||
|
}
|
||||||
|
Applicability.USELESS -> getUselessnessDiagnostic(context)?.let {
|
||||||
|
reportOn(expression.source, it, context)
|
||||||
|
}
|
||||||
|
Applicability.CAST_ERASED -> when {
|
||||||
|
expression.operation == FirOperation.AS || expression.operation == FirOperation.SAFE_AS -> {
|
||||||
|
reportOn(expression.source, FirErrors.UNCHECKED_CAST, lUserType, rUserType, context)
|
||||||
|
}
|
||||||
|
else -> reportOn(expression.conversionTypeRef.source, FirErrors.CANNOT_CHECK_FOR_ERASED, rUserType, context)
|
||||||
|
}
|
||||||
|
else -> error("Shouldn't be here")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getImpossibilityDiagnostic(l: TypeInfo, rType: ConeKotlinType, context: CheckerContext) = when {
|
||||||
|
!context.languageVersionSettings.supportsFeature(LanguageFeature.EnableDfaWarningsInK2) -> null
|
||||||
|
context.session.firPlatformSpecificCastChecker.shouldSuppressImpossibleCast(context.session, l.type, rType) -> null
|
||||||
|
else -> FirErrors.CAST_NEVER_SUCCEEDS
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUselessnessDiagnostic(context: CheckerContext) = when {
|
||||||
|
!context.languageVersionSettings.supportsFeature(LanguageFeature.EnableDfaWarningsInK2) -> null
|
||||||
|
else -> FirErrors.USELESS_CAST
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ inline fun <reified T> test(x: T?, a: Any) {
|
|||||||
a is <!CANNOT_CHECK_FOR_ERASED!>Box<T><!>
|
a is <!CANNOT_CHECK_FOR_ERASED!>Box<T><!>
|
||||||
a is <!CANNOT_CHECK_FOR_ERASED!>Array<T><!>
|
a is <!CANNOT_CHECK_FOR_ERASED!>Array<T><!>
|
||||||
a <!UNCHECKED_CAST!>as Box<T><!>
|
a <!UNCHECKED_CAST!>as Box<T><!>
|
||||||
a <!UNCHECKED_CAST!>as Array<T><!>
|
a <!CAST_NEVER_SUCCEEDS!>as<!> Array<T>
|
||||||
|
|
||||||
a is <!CANNOT_CHECK_FOR_ERASED!>Box<List<T>><!>
|
a is <!CANNOT_CHECK_FOR_ERASED!>Box<List<T>><!>
|
||||||
a is <!CANNOT_CHECK_FOR_ERASED!>Array<List<T>><!>
|
a is <!CANNOT_CHECK_FOR_ERASED!>Array<List<T>><!>
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// !DIAGNOSTICS: -PLATFORM_CLASS_MAPPED_TO_KOTLIN -UNUSED_PARAMETER -ABSTRACT_MEMBER_NOT_IMPLEMENTED -USELESS_CAST
|
||||||
|
import java.lang.CharSequence as JCS
|
||||||
|
|
||||||
|
class JSub: JCS
|
||||||
|
class Sub: CharSequence
|
||||||
|
|
||||||
|
fun test(
|
||||||
|
s: Sub,
|
||||||
|
js: JSub,
|
||||||
|
cs: CharSequence,
|
||||||
|
jcs: JCS
|
||||||
|
) {
|
||||||
|
// js as CharSequence // - this case is not supported due to limitation in PlatformToKotlinClassMap
|
||||||
|
js <!CAST_NEVER_SUCCEEDS!>as<!> JCS
|
||||||
|
|
||||||
|
s as CharSequence
|
||||||
|
s as JCS
|
||||||
|
|
||||||
|
js <!CAST_NEVER_SUCCEEDS!>as<!> Sub
|
||||||
|
s <!CAST_NEVER_SUCCEEDS!>as<!> JSub
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// FIR_IDENTICAL
|
|
||||||
// !DIAGNOSTICS: -PLATFORM_CLASS_MAPPED_TO_KOTLIN -UNUSED_PARAMETER -ABSTRACT_MEMBER_NOT_IMPLEMENTED -USELESS_CAST
|
// !DIAGNOSTICS: -PLATFORM_CLASS_MAPPED_TO_KOTLIN -UNUSED_PARAMETER -ABSTRACT_MEMBER_NOT_IMPLEMENTED -USELESS_CAST
|
||||||
import java.lang.CharSequence as JCS
|
import java.lang.CharSequence as JCS
|
||||||
|
|
||||||
|
|||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
// !DIAGNOSTICS: -USELESS_CAST -UNCHECKED_CAST
|
||||||
|
interface T1
|
||||||
|
interface T2
|
||||||
|
interface T3
|
||||||
|
open class OC1: T1
|
||||||
|
open class OC2: OC1(), T2
|
||||||
|
class FC1: OC2(), T3
|
||||||
|
interface T4: <!INTERFACE_WITH_SUPERCLASS!>OC1<!>
|
||||||
|
interface T5: T2
|
||||||
|
|
||||||
|
fun <TP1: OC1, TP2: T2, TP3: OC2> test(
|
||||||
|
t2: T2,
|
||||||
|
t4: T4,
|
||||||
|
fc1: FC1,
|
||||||
|
oc1: OC1,
|
||||||
|
oc2: OC2,
|
||||||
|
tp1: TP1,
|
||||||
|
tp2: TP2
|
||||||
|
) {
|
||||||
|
fc1 as FC1
|
||||||
|
fc1 as OC1
|
||||||
|
fc1 as T1
|
||||||
|
fc1 as TP1
|
||||||
|
|
||||||
|
oc1 as FC1
|
||||||
|
oc1 as OC2
|
||||||
|
oc2 as OC1
|
||||||
|
oc1 as T2
|
||||||
|
oc1 as T1
|
||||||
|
oc1 as TP1
|
||||||
|
oc1 as TP2
|
||||||
|
|
||||||
|
t2 as FC1
|
||||||
|
t2 as OC2
|
||||||
|
t4 as OC1
|
||||||
|
t2 as T2
|
||||||
|
t2 <!CAST_NEVER_SUCCEEDS!>as<!> T5
|
||||||
|
t2 as TP2
|
||||||
|
|
||||||
|
tp1 as FC1
|
||||||
|
tp1 as OC1
|
||||||
|
tp1 as OC2
|
||||||
|
tp2 as T2
|
||||||
|
tp2 as T5
|
||||||
|
tp1 as TP3
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// FIR_IDENTICAL
|
|
||||||
// !DIAGNOSTICS: -USELESS_CAST -UNCHECKED_CAST
|
// !DIAGNOSTICS: -USELESS_CAST -UNCHECKED_CAST
|
||||||
interface T1
|
interface T1
|
||||||
interface T2
|
interface T2
|
||||||
|
|||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
// !DIAGNOSTICS: -UNCHECKED_CAST
|
||||||
|
interface Trait1
|
||||||
|
interface Trait2
|
||||||
|
open class OClass1
|
||||||
|
open class OClass2
|
||||||
|
class FClass1
|
||||||
|
class FClass2
|
||||||
|
|
||||||
|
fun <TP1: OClass1, TP2: OClass2> test(
|
||||||
|
t1: Trait1,
|
||||||
|
oc1: OClass1,
|
||||||
|
fc1: FClass1,
|
||||||
|
tp1: TP1
|
||||||
|
) {
|
||||||
|
t1 as Trait2
|
||||||
|
t1 as OClass2
|
||||||
|
t1 <!CAST_NEVER_SUCCEEDS!>as<!> FClass2
|
||||||
|
t1 as TP2
|
||||||
|
|
||||||
|
oc1 as Trait2
|
||||||
|
oc1 as OClass2
|
||||||
|
oc1 <!CAST_NEVER_SUCCEEDS!>as<!> FClass2
|
||||||
|
oc1 as TP2
|
||||||
|
|
||||||
|
fc1 <!CAST_NEVER_SUCCEEDS!>as<!> Trait2
|
||||||
|
fc1 <!CAST_NEVER_SUCCEEDS!>as<!> OClass2
|
||||||
|
fc1 <!CAST_NEVER_SUCCEEDS!>as<!> FClass2
|
||||||
|
fc1 as TP2
|
||||||
|
|
||||||
|
tp1 as Trait2
|
||||||
|
tp1 as OClass2
|
||||||
|
tp1 <!CAST_NEVER_SUCCEEDS!>as<!> FClass2
|
||||||
|
tp1 as TP2
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// FIR_IDENTICAL
|
|
||||||
// !DIAGNOSTICS: -UNCHECKED_CAST
|
// !DIAGNOSTICS: -UNCHECKED_CAST
|
||||||
interface Trait1
|
interface Trait1
|
||||||
interface Trait2
|
interface Trait2
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@ fun <E> foo(x: Any, y: Any) : Any {
|
|||||||
y <!UNCHECKED_CAST!>as Outer<*><!>
|
y <!UNCHECKED_CAST!>as Outer<*><!>
|
||||||
y as <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer<!>
|
y as <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer<!>
|
||||||
|
|
||||||
y <!UNCHECKED_CAST!>as Outer<*>.Inner<!>
|
y <!CAST_NEVER_SUCCEEDS!>as<!> Outer<*>.Inner
|
||||||
y as <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer.Inner<!>
|
y as <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer.Inner<!>
|
||||||
|
|
||||||
return C()
|
return C()
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ public interface I<K> {}
|
|||||||
// FILE: main.kt
|
// FILE: main.kt
|
||||||
fun main(z: I<String>) {
|
fun main(z: I<String>) {
|
||||||
z <!UNCHECKED_CAST!>as Test<Test2<Int, *>><!>
|
z <!UNCHECKED_CAST!>as Test<Test2<Int, *>><!>
|
||||||
z <!UNCHECKED_CAST!>as Test<Test2<Int, <!UNRESOLVED_REFERENCE!>Foo<!>>><!>
|
z as Test<Test2<Int, <!UNRESOLVED_REFERENCE!>Foo<!>>>
|
||||||
z as Test<<!UNRESOLVED_REFERENCE!>Foo<!>>
|
z as Test<<!UNRESOLVED_REFERENCE!>Foo<!>>
|
||||||
z as <!UNRESOLVED_REFERENCE!>Any2<!>
|
z as <!UNRESOLVED_REFERENCE!>Any2<!>
|
||||||
println(z)
|
println(z)
|
||||||
|
|||||||
Vendored
+1
-1
@@ -11,6 +11,6 @@ public class A {
|
|||||||
fun main(a: A, ml: Any) {
|
fun main(a: A, ml: Any) {
|
||||||
if (ml is <!CANNOT_CHECK_FOR_ERASED!>MutableList<String><!>) {
|
if (ml is <!CANNOT_CHECK_FOR_ERASED!>MutableList<String><!>) {
|
||||||
a.foo(<!JAVA_TYPE_MISMATCH!>ml<!>)
|
a.foo(<!JAVA_TYPE_MISMATCH!>ml<!>)
|
||||||
a.foo(ml <!UNCHECKED_CAST!>as List<Any><!>)
|
a.foo(ml as List<Any>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ fun String?.gav() {}
|
|||||||
fun bar(s: String?) {
|
fun bar(s: String?) {
|
||||||
if (s != null) return
|
if (s != null) return
|
||||||
s.gav()
|
s.gav()
|
||||||
s as? String
|
s <!USELESS_CAST!>as? String<!>
|
||||||
s as String?
|
s as String?
|
||||||
s as String
|
s <!CAST_NEVER_SUCCEEDS!>as<!> String
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ fun f(a: SomeClass?) {
|
|||||||
// 'aa' cannot be cast to SomeSubClass
|
// 'aa' cannot be cast to SomeSubClass
|
||||||
aa<!UNSAFE_CALL!>.<!>hashCode()
|
aa<!UNSAFE_CALL!>.<!>hashCode()
|
||||||
aa.<!UNRESOLVED_REFERENCE!>foo<!>
|
aa.<!UNRESOLVED_REFERENCE!>foo<!>
|
||||||
(aa as? SomeSubClass)<!UNSAFE_CALL!>.<!>foo
|
(aa <!USELESS_CAST!>as? SomeSubClass<!>)<!UNSAFE_CALL!>.<!>foo
|
||||||
(aa as SomeSubClass).foo
|
(aa <!CAST_NEVER_SUCCEEDS!>as<!> SomeSubClass).foo
|
||||||
}
|
}
|
||||||
val b = (aa as? SomeSubClass)?.foo
|
val b = (aa as? SomeSubClass)?.foo
|
||||||
aa = null
|
aa = null
|
||||||
@@ -52,8 +52,8 @@ fun f(a: SomeClass?) {
|
|||||||
// 'aa' cannot be cast to SomeSubClass
|
// 'aa' cannot be cast to SomeSubClass
|
||||||
aa<!UNSAFE_CALL!>.<!>hashCode()
|
aa<!UNSAFE_CALL!>.<!>hashCode()
|
||||||
aa.<!UNRESOLVED_REFERENCE!>foo<!>
|
aa.<!UNRESOLVED_REFERENCE!>foo<!>
|
||||||
(aa as? SomeSubClass)<!UNSAFE_CALL!>.<!>foo
|
(aa <!USELESS_CAST!>as? SomeSubClass<!>)<!UNSAFE_CALL!>.<!>foo
|
||||||
(aa as SomeSubClass).foo
|
(aa <!CAST_NEVER_SUCCEEDS!>as<!> SomeSubClass).foo
|
||||||
}
|
}
|
||||||
aa = a
|
aa = a
|
||||||
val c = aa as? SomeSubClass
|
val c = aa as? SomeSubClass
|
||||||
|
|||||||
+1
-1
@@ -55,5 +55,5 @@ fun testMany(a: Any) {
|
|||||||
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a<!>)
|
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a<!>)
|
||||||
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a<!>)
|
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a<!>)
|
||||||
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a as Int<!>)
|
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a as Int<!>)
|
||||||
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a as String<!>)
|
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a <!CAST_NEVER_SUCCEEDS!>as<!> String<!>)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -55,5 +55,5 @@ fun testMany(a: Any) {
|
|||||||
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a<!>)
|
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a<!>)
|
||||||
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a<!>)
|
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a<!>)
|
||||||
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a as Int<!>)
|
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a as Int<!>)
|
||||||
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a as String<!>)
|
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a <!CAST_NEVER_SUCCEEDS!>as<!> String<!>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// !DIAGNOSTICS: -UNCHECKED_CAST
|
// !DIAGNOSTICS: -UNCHECKED_CAST
|
||||||
|
|
||||||
fun <T> Collection<T>.toArray(): Array<T> = this as Array<T>
|
fun <T> Collection<T>.toArray(): Array<T> = this <!CAST_NEVER_SUCCEEDS!>as<!> Array<T>
|
||||||
fun Collection<String>.toArray2(): Array<String> = this as Array<String>
|
fun Collection<String>.toArray2(): Array<String> = this <!CAST_NEVER_SUCCEEDS!>as<!> Array<String>
|
||||||
fun <T> toArray3(x: Collection<T>): Array<T> = x as Array<T>
|
fun <T> toArray3(x: Collection<T>): Array<T> = x <!CAST_NEVER_SUCCEEDS!>as<!> Array<T>
|
||||||
|
|
||||||
class Foo<T> {
|
class Foo<T> {
|
||||||
operator fun plus(x: Foo<T>): Array<T> {
|
operator fun plus(x: Foo<T>): Array<T> {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -55,7 +55,7 @@ class Case8<K> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TESTCASE NUMBER: 9
|
// TESTCASE NUMBER: 9
|
||||||
inline fun <reified L: Nothing?> case_9(x: L) = x!! as Int
|
inline fun <reified L: Nothing?> case_9(x: L) = x!! <!CAST_NEVER_SUCCEEDS!>as<!> Int
|
||||||
|
|
||||||
// TESTCASE NUMBER: 10
|
// TESTCASE NUMBER: 10
|
||||||
fun <K> case_10(x: Nothing) = x as Iterable<K>
|
fun <K> case_10(x: Nothing) = x as Iterable<K>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ fun case_2(x: Int?, y: Nothing?) {
|
|||||||
*/
|
*/
|
||||||
fun case_3(x: Int?) {
|
fun case_3(x: Int?) {
|
||||||
if (x == null) {
|
if (x == null) {
|
||||||
x as Int
|
x <!CAST_NEVER_SUCCEEDS!>as<!> Int
|
||||||
stringArg(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Nothing")!>x<!>)
|
stringArg(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Nothing")!>x<!>)
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Nothing")!>x<!>
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Nothing")!>x<!>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ fun case_10() {
|
|||||||
do {
|
do {
|
||||||
x = null
|
x = null
|
||||||
break
|
break
|
||||||
} while ((x as String).length > 1)
|
} while ((x <!CAST_NEVER_SUCCEEDS!>as<!> String).length > 1)
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing?")!>x<!>
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing?")!>x<!>
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing?")!>x<!><!UNSAFE_CALL!>.<!>length
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing?")!>x<!><!UNSAFE_CALL!>.<!>length
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,10 +106,10 @@ fun case_6() {
|
|||||||
val e: Any?
|
val e: Any?
|
||||||
|
|
||||||
when (if (true) {b = 11} else {b = 12}) {
|
when (if (true) {b = 11} else {b = 12}) {
|
||||||
when (if (true) {b.inv(); c = b; c as ClassLevel1;} else {b.inv(); c = b; c as ClassLevel1;}) {
|
when (if (true) {b.inv(); c = b; c <!CAST_NEVER_SUCCEEDS!>as<!> ClassLevel1;} else {b.inv(); c = b; c <!CAST_NEVER_SUCCEEDS!>as<!> ClassLevel1;}) {
|
||||||
else -> kotlin.Unit
|
else -> kotlin.Unit
|
||||||
} -> when (if (true) {c.test1(); d = c; d as ClassLevel2} else {c.test1(); d = c; d as ClassLevel2}) {
|
} -> when (if (true) {c.test1(); d = c; d <!CAST_NEVER_SUCCEEDS!>as<!> ClassLevel2} else {c.test1(); d = c; d <!CAST_NEVER_SUCCEEDS!>as<!> ClassLevel2}) {
|
||||||
when (if (true) {d.test2(); e = d; e as ClassLevel3} else {d.test2(); e = d; e as ClassLevel3}) {
|
when (if (true) {d.test2(); e = d; e <!CAST_NEVER_SUCCEEDS!>as<!> ClassLevel3} else {d.test2(); e = d; e <!CAST_NEVER_SUCCEEDS!>as<!> ClassLevel3}) {
|
||||||
else -> ClassLevel2()
|
else -> ClassLevel2()
|
||||||
} -> {
|
} -> {
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Int & ClassLevel3")!>e<!>
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Int & ClassLevel3")!>e<!>
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ fun case_11(z: Any?, x: Any?) {
|
|||||||
fun case_12(z: Any?) {
|
fun case_12(z: Any?) {
|
||||||
val y = z.let {
|
val y = z.let {
|
||||||
return@let it as Int
|
return@let it as Int
|
||||||
it as? Float ?: 10f
|
it <!CAST_NEVER_SUCCEEDS!>as?<!> Float ?: 10f
|
||||||
}
|
}
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number & kotlin.Comparable<*>")!>y<!>
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number & kotlin.Comparable<*>")!>y<!>
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number & kotlin.Comparable<*>")!>y<!>.toByte()
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number & kotlin.Comparable<*>")!>y<!>.toByte()
|
||||||
@@ -164,7 +164,7 @@ fun case_13(z: Any?) {
|
|||||||
fun case_14(z: Any?) {
|
fun case_14(z: Any?) {
|
||||||
val y = z.run {
|
val y = z.run {
|
||||||
return@run this as Int
|
return@run this as Int
|
||||||
this as? Float ?: 10f
|
this <!CAST_NEVER_SUCCEEDS!>as?<!> Float ?: 10f
|
||||||
}
|
}
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number & kotlin.Comparable<*>")!>y<!>
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number & kotlin.Comparable<*>")!>y<!>
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number & kotlin.Comparable<*>")!>y<!>.toByte()
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number & kotlin.Comparable<*>")!>y<!>.toByte()
|
||||||
|
|||||||
Reference in New Issue
Block a user