From 291bc74676aff7ca1be73831ad533ec43c192f87 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Wed, 15 Sep 2021 20:44:47 +0300 Subject: [PATCH] [FIR] Add CANNOT_CHECK_FOR_ERASED --- .../diagnostics/KtFirDataClassConverters.kt | 7 + .../api/fir/diagnostics/KtFirDiagnostics.kt | 5 + .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 8 + .../checkers/CommonExpressionCheckers.kt | 1 + .../checkers/FirCastDiagnosticsHelpers.kt | 213 ++++++++++++++++++ .../fir/analysis/checkers/TypeUnifier.kt | 157 +++++++++++++ .../FirCannotCheckForErasedChecker.kt | 31 +++ .../expression/FirCastOperatorsChecker.kt | 49 ++++ .../FirUselessTypeOperationCallChecker.kt | 56 +---- .../diagnostics/FirDefaultErrorMessages.kt | 0 .../kotlin/fir/types/ConeTypeUtils.kt | 9 + ...asedAllowForTypeWithIrrelevantMixin.fir.kt | 9 + ...IsErasedAllowForTypeWithIrrelevantMixin.kt | 1 - ...sallowDifferentArgInvariantPosition.fir.kt | 1 - ...edDisallowDifferentArgInvariantPosition.kt | 1 + ...sErasedDisallowForOverridenVariance.fir.kt | 8 - .../IsErasedDisallowForOverridenVariance.kt | 1 + ...rasedDisallowForTypeWithConstraints.fir.kt | 6 - .../IsErasedDisallowForTypeWithConstraints.kt | 1 + .../tests/cast/IsErasedDisallowFromAny.fir.kt | 2 - .../tests/cast/IsErasedDisallowFromAny.kt | 1 + .../tests/cast/IsErasedDisallowFromIn.fir.kt | 1 - .../tests/cast/IsErasedDisallowFromIn.kt | 1 + .../tests/cast/IsErasedDisallowFromOut.fir.kt | 2 - .../tests/cast/IsErasedDisallowFromOut.kt | 1 + .../IsErasedDisallowFromOutAtClass.fir.kt | 1 - .../cast/IsErasedDisallowFromOutAtClass.kt | 1 + ...peMappedToTwoParamsWithFirstInvalid.fir.kt | 8 - ...ubtypeMappedToTwoParamsWithFirstInvalid.kt | 1 + ...eMappedToTwoParamsWithSecondInvalid.fir.kt | 8 - ...btypeMappedToTwoParamsWithSecondInvalid.kt | 1 + .../cast/IsErasedUpcastToNonReified.fir.kt | 2 +- .../tests/cast/IsRecursionSustainable.fir.kt | 4 +- .../cast/WhenErasedDisallowFromAny.fir.kt | 5 - .../tests/cast/WhenErasedDisallowFromAny.kt | 1 + .../genericVarianceViolation/smartCast.fir.kt | 2 +- .../tests/regressions/kt398.fir.kt | 11 - .../diagnostics/tests/regressions/kt398.kt | 1 + .../tests/regressions/kt399.fir.kt | 11 - .../diagnostics/tests/regressions/kt399.kt | 1 + .../testsWithStdLib/cast/IsArray.fir.kt | 5 - .../testsWithStdLib/cast/IsArray.kt | 1 + .../p-1/neg/1.1.fir.kt | 4 +- .../p-1/neg/1.2.fir.kt | 8 +- 44 files changed, 514 insertions(+), 134 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/TypeUnifier.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCannotCheckForErasedChecker.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCastOperatorsChecker.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt create mode 100644 compiler/testData/diagnostics/tests/cast/IsErasedAllowForTypeWithIrrelevantMixin.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/cast/IsErasedDisallowDifferentArgInvariantPosition.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/cast/IsErasedDisallowForOverridenVariance.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/cast/IsErasedDisallowForTypeWithConstraints.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromAny.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromIn.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOut.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOutAtClass.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithFirstInvalid.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithSecondInvalid.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/regressions/kt398.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/regressions/kt399.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/cast/IsArray.fir.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index d3f9a94942e..a414d3100b6 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -3097,6 +3097,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.CANNOT_CHECK_FOR_ERASED) { firDiagnostic -> + CannotCheckForErasedImpl( + firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.USELESS_CAST) { firDiagnostic -> UselessCastImpl( firDiagnostic as KtPsiDiagnostic, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 2250ce0a0b2..160e97f2870 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2168,6 +2168,11 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = UselessElvisRightIsNull::class } + abstract class CannotCheckForErased : KtFirDiagnostic() { + override val diagnosticClass get() = CannotCheckForErased::class + abstract val type: KtType + } + abstract class UselessCast : KtFirDiagnostic() { override val diagnosticClass get() = UselessCast::class } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index d74bbd1b039..ebf78dbd06f 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -2610,6 +2610,14 @@ internal class UselessElvisRightIsNullImpl( override val token: ValidityToken, ) : KtFirDiagnostic.UselessElvisRightIsNull(), KtAbstractFirDiagnostic +internal class CannotCheckForErasedImpl( + override val type: KtType, + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.CannotCheckForErased(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class UselessCastImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: ValidityToken, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt index 347f92cc5cc..4bc70960f9a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt @@ -132,6 +132,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { override val typeOperatorCallCheckers: Set get() = setOf( FirUselessTypeOperationCallChecker, + FirCannotCheckForErasedChecker ) override val resolvedQualifierCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt new file mode 100644 index 00000000000..015a1f990bf --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt @@ -0,0 +1,213 @@ +/* + * Copyright 2010-2021 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.fir.analysis.checkers + +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.resolve.platformClassMapper +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.typeContext +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.types.AbstractTypeChecker +import org.jetbrains.kotlin.types.AbstractTypeChecker.findCorrespondingSupertypes +import org.jetbrains.kotlin.types.model.typeConstructor + +fun isCastErased(supertype: ConeKotlinType, subtype: ConeKotlinType, context: CheckerContext): Boolean { + val typeContext = context.session.typeContext + + val isNonReifiedTypeParameter = subtype.isNonReifiedTypeParameter() + val isUpcast = isUpcast(context, supertype, subtype) + + // here we want to restrict cases such as `x is T` for x = T?, when T might have nullable upper bound + if (isNonReifiedTypeParameter && !isUpcast) { + // hack to save previous behavior in case when `x is T`, where T is not nullable, see IsErasedNullableTasT.kt + val nullableToDefinitelyNotNull = !subtype.canBeNull && supertype.withNullability(ConeNullability.NOT_NULL, typeContext) == subtype + if (!nullableToDefinitelyNotNull) { + return true + } + } + + // cast between T and T? is always OK + if (supertype.isMarkedNullable || subtype.isMarkedNullable) { + return isCastErased( + supertype.withNullability(ConeNullability.NOT_NULL, typeContext), + subtype.withNullability(ConeNullability.NOT_NULL, typeContext), + context + ) + } + + // if it is a upcast, it's never erased + if (isUpcast) return false + + // downcasting to a non-reified type parameter is always erased + if (isNonReifiedTypeParameter) return true + + // Check that we are actually casting to a generic type + // NOTE: this does not account for 'as Array>' + if (subtype.allParameterReified()) return false + + val staticallyKnownSubtype = findStaticallyKnownSubtype(supertype, subtype, context).first ?: return true + + // If the substitution failed, it means that the result is an impossible type, e.g. something like Out + // In this case, we can't guarantee anything, so the cast is considered to be erased + + // If the type we calculated is a subtype of the cast target, it's OK to use the cast target instead. + // If not, it's wrong to use it + return !AbstractTypeChecker.isSubtypeOf(context.session.typeContext, staticallyKnownSubtype, subtype) +} + +private fun ConeKotlinType.allParameterReified(): Boolean { + return typeArguments.all { (it.type as? ConeTypeParameterType)?.lookupTag?.typeParameterSymbol?.isReified == true } +} + +/** + * Remember that we are trying to cast something of type `supertype` to `subtype`. + + * Since at runtime we can only check the class (type constructor), the rest of the subtype should be known statically, from supertype. + * This method reconstructs all static information that can be obtained from supertype. + + * Example 1: + * supertype = Collection + * subtype = List<...> + * result = List, all arguments are inferred + + * Example 2: + * supertype = Any + * subtype = List<...> + * result = List<*>, some arguments were not inferred, replaced with '*' + */ +fun findStaticallyKnownSubtype( + supertype: ConeKotlinType, + subtype: ConeKotlinType, + context: CheckerContext +): Pair { + assert(!supertype.isMarkedNullable) { "This method only makes sense for non-nullable types" } + + val session = context.session + val typeContext = session.typeContext + + // Assume we are casting an expression of type Collection to List + // First, let's make List, where T is a type variable + val subtypeWithVariables = subtype.toRegularClassSymbol(session)!! + val subtypeWithVariablesType = subtypeWithVariables.defaultType() + + // Now, let's find a supertype of List that is a Collection of something, + // in this case it will be Collection + val typeCheckerState = context.session.typeContext.newTypeCheckerState( + errorTypesEqualToAnything = false, + stubTypesEqualToAnything = true + ) + + fun getFirstNotIntersectedType(type: ConeKotlinType): ConeKotlinType? { + if (type is ConeIntersectionType) { + for (intersectionType in type.intersectedTypes) { + val result = getFirstNotIntersectedType(intersectionType) + if (result != null) { + return result + } + } + return null + } + return type + } + + // Obtaining not intersected type to get not null typeConstructor. + // Not sure if it's correct + val notIntersectedSupertype = getFirstNotIntersectedType(supertype) ?: supertype + val supertypeWithVariables = + findCorrespondingSupertypes( + typeCheckerState, + subtypeWithVariablesType, + notIntersectedSupertype.typeConstructor(typeContext) + ).firstOrNull() + + val variables = subtypeWithVariables.typeParameterSymbols + + val substitution = if (supertypeWithVariables != null) { + // Now, let's try to unify Collection and Collection solution is a map from T to Foo + val typeUnifier = TypeUnifier(session, variables) + val unificationResult = typeUnifier.unify(supertype, supertypeWithVariables as ConeKotlinTypeProjection) + unificationResult.substitution.toMutableMap() + } else { + mutableMapOf() + } + + // If some of the parameters are not determined by unification, it means that these parameters are lost, + // let's put stars instead, so that we can only cast to something like List<*>, e.g. (a: Any) as List<*> + var allArgumentsInferred = true + for (variable in variables) { + val value = substitution[variable] + if (value == null) { + substitution[variable] = context.session.builtinTypes.nullableAnyType.type + allArgumentsInferred = false + } + } + + // At this point we have values for all type parameters of List + // Let's make a type by substituting them: List -> List + val substitutor = ConeSubstitutorByMap(substitution, session) + val substituted = substitutor.substituteOrSelf(subtypeWithVariablesType) + + return Pair(substituted, allArgumentsInferred) +} + +fun ConeKotlinType.isNonReifiedTypeParameter(): Boolean { + return this is ConeTypeParameterType && !this.lookupTag.typeParameterSymbol.isReified +} + +@Suppress("UNUSED_PARAMETER") +fun shouldCheckForExactType(expression: FirTypeOperatorCall, context: CheckerContext): Boolean { + return when (expression.operation) { + FirOperation.IS, FirOperation.NOT_IS -> false + // TODO: differentiate if this expression defines the enclosing thing's type + // e.g., + // val c1 get() = 1 as Number + // val c2: Number get() = 1 as Number + FirOperation.AS, FirOperation.SAFE_AS -> true + else -> throw AssertionError("Should not be here: ${expression.operation}") + } +} + +fun isRefinementUseless( + context: CheckerContext, + candidateType: ConeKotlinType, + targetType: ConeKotlinType, + shouldCheckForExactType: Boolean, + arg: FirExpression, +): Boolean { + return if (shouldCheckForExactType) { + if (arg is FirFunctionCall) { + val functionSymbol = arg.toResolvedCallableSymbol() as? FirFunctionSymbol<*> + if (functionSymbol != null && functionSymbol.isFunctionForExpectTypeFromCastFeature()) return false + } + + isExactTypeCast(context, candidateType, targetType) + } else { + isUpcast(context, candidateType, targetType) + } +} + +private fun isExactTypeCast(context: CheckerContext, candidateType: ConeKotlinType, targetType: ConeKotlinType): Boolean { + if (!AbstractTypeChecker.equalTypes(context.session.typeContext, candidateType, targetType, stubTypesEqualToAnything = false)) + return false + // See comments at [isUpcast] why we need to check the existence of @ExtensionFunctionType + return candidateType.isExtensionFunctionType == targetType.isExtensionFunctionType +} + +private fun isUpcast(context: CheckerContext, candidateType: ConeKotlinType, targetType: ConeKotlinType): Boolean { + if (!AbstractTypeChecker.isSubtypeOf(context.session.typeContext, candidateType, targetType, stubTypesEqualToAnything = false)) + return false + + // E.g., foo(p1: (X) -> Y), where p1 has a functional type whose receiver type is X and return type is Y. + // For bar(p2: X.() -> Y), p2 has the same functional type (with same receiver and return types). + // The only difference is the existence of type annotation, @ExtensionFunctionType, + // which indicates that the annotated type represents an extension function. + // If one casts p1 to p2 (or vice versa), it is _not_ up cast, i.e., not redundant, yet meaningful. + return candidateType.isExtensionFunctionType == targetType.isExtensionFunctionType +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/TypeUnifier.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/TypeUnifier.kt new file mode 100644 index 00000000000..916161f4ac3 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/TypeUnifier.kt @@ -0,0 +1,157 @@ +/* + * Copyright 2010-2021 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.fir.analysis.checkers + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.types.AbstractTypeChecker + +class TypeUnifier(private val session: FirSession, private val typeParameterSymbols: List) { + /** + * Finds a substitution S that turns {@code projectWithVariables} to {@code knownProjection}. + * + * Example: + * known = List + * withVariables = List + * variables = {X} + * + * result = X -> String + * + * Only types accepted by {@code isVariable} are considered variables. + */ + fun unify(knownProjection: ConeKotlinTypeProjection, projectWithVariables: ConeKotlinTypeProjection): UnificationResult { + val result = UnificationResult() + doUnify(knownProjection, projectWithVariables, result) + return result + } + + fun doUnify(knownProjection: ConeTypeProjection, projectWithVariables: ConeTypeProjection, result: UnificationResult) { + val firstType = knownProjection.type + + if (firstType is ConeIntersectionType) { + val intersectionResult = mutableMapOf() + + for (intersectedType in firstType.intersectedTypes) { + val localResult = UnificationResult() + doUnify(intersectedType, projectWithVariables, localResult) + + for ((typeParameterSymbol, typeParameterType) in localResult.substitution) { + val existingTypeParameterType = intersectionResult[typeParameterSymbol] + if (existingTypeParameterType == null || + AbstractTypeChecker.isSubtypeOf(session.typeContext, typeParameterType, existingTypeParameterType) + ) { + intersectionResult[typeParameterSymbol] = typeParameterType + } + } + } + + for ((typeParameterSymbol, typeParameterType) in intersectionResult) { + result.put(typeParameterSymbol, typeParameterType) + } + + return + } + + val known = firstType?.lowerBoundIfFlexible() ?: session.builtinTypes.nullableAnyType.type + val withVariables = projectWithVariables.type?.lowerBoundIfFlexible() ?: session.builtinTypes.nullableAnyType.type + + // in Foo ~ in X => Foo ~ X + val knownProjectionKind = knownProjection.kind + val withVariablesProjectionKind = projectWithVariables.kind + if (knownProjectionKind == withVariablesProjectionKind && knownProjectionKind != ProjectionKind.INVARIANT) { + doUnify(known, withVariables, result) + return + } + + // Foo? ~ X? => Foo ~ X + if (known.isMarkedNullable && withVariables.isMarkedNullable) { + doUnify( + known.withNullability(ConeNullability.NOT_NULL, session.typeContext).toTypeProjection( + knownProjectionKind + ) as ConeKotlinTypeProjection, + withVariables.withNullability(ConeNullability.NOT_NULL, session.typeContext).toTypeProjection( + withVariablesProjectionKind + ) as ConeKotlinTypeProjection, + result + ) + } + + // in Foo ~ out X => fail + // in Foo ~ X => may be OK + if (knownProjectionKind != withVariablesProjectionKind && withVariablesProjectionKind != ProjectionKind.INVARIANT) { + result.fail() + return + } + + // Foo ~ X? => fail + if (!known.isMarkedNullable && withVariables.isMarkedNullable) { + result.fail() + return + } + + // Foo ~ X => x |-> Foo + // * ~ X => x |-> * + val maybeVariable = withVariables.toSymbol(session) + if (maybeVariable is FirTypeParameterSymbol && typeParameterSymbols.contains(maybeVariable)) { + result.put(maybeVariable, known) + return + } + + // Foo? ~ Foo || in Foo ~ Foo || Foo ~ Bar + val structuralMismatch = known.isMarkedNullable != withVariables.isMarkedNullable || + knownProjectionKind != withVariablesProjectionKind || + known.toSymbol(session) != maybeVariable + if (structuralMismatch) { + result.fail() + return + } + + // Foo ~ Foo + if (known.typeArguments.size != withVariables.typeArguments.size) { + result.fail() + return + } + + // Foo ~ Foo + if (known.typeArguments.isEmpty()) { + return + } + + // Foo<...> ~ Foo<...> + val knownArguments = known.typeArguments + val withVariablesArguments = withVariables.typeArguments + for (index in knownArguments.indices) { + val knownArg = knownArguments[index] + val withVariablesArg = withVariablesArguments[index] + doUnify(knownArg, withVariablesArg, result) + } + } +} + +class UnificationResult { + private var success: Boolean = true + private var failedVariables: MutableSet = mutableSetOf() + private val _substitution: MutableMap = mutableMapOf() + val substitution: Map + get() = _substitution + + fun fail() { + success = false + } + + fun put(key: FirTypeParameterSymbol, value: ConeKotlinType) { + if (failedVariables.contains(key)) return + + if (substitution.containsKey(key)) { + _substitution.remove(key) + failedVariables.add(key) + fail() + } else { + _substitution[key] = value + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCannotCheckForErasedChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCannotCheckForErasedChecker.kt new file mode 100644 index 00000000000..8e53b8ef6a3 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCannotCheckForErasedChecker.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2021 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.fir.analysis.checkers.expression + +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.isCastErased +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.expressions.FirOperation +import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.types.coneType + +object FirCannotCheckForErasedChecker : FirTypeOperatorCallChecker() { + override fun check(expression: FirTypeOperatorCall, context: CheckerContext, reporter: DiagnosticReporter) { + if (expression.operation != FirOperation.IS) return + + val session = context.session + val subjectType = expression.argumentList.arguments[0].typeRef.coneType.fullyExpandedType(session) + val conversionTypeRef = expression.conversionTypeRef + val targetType = conversionTypeRef.coneType.fullyExpandedType(session) + + if (isCastErased(subjectType, targetType, context)) { + reporter.reportOn(conversionTypeRef.source, FirErrors.CANNOT_CHECK_FOR_ERASED, targetType, context) + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCastOperatorsChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCastOperatorsChecker.kt new file mode 100644 index 00000000000..487e0f948b8 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCastOperatorsChecker.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2021 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.fir.analysis.checkers.expression + +import org.jetbrains.kotlin.fir.analysis.checkers.CastingType +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.isCastErased +import org.jetbrains.kotlin.fir.analysis.checkers.checkCasting +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast +import org.jetbrains.kotlin.fir.expressions.FirOperation +import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.types.coneType + +object FirCastOperatorsChecker : FirTypeOperatorCallChecker() { + override fun check(expression: FirTypeOperatorCall, context: CheckerContext, reporter: DiagnosticReporter) { + val session = context.session + val firstArgument = expression.argumentList.arguments[0] + val actualType = (if (firstArgument is FirExpressionWithSmartcast) { + firstArgument.originalType.coneType + } else { + firstArgument.typeRef.coneType + }).fullyExpandedType(session) + val conversionTypeRef = expression.conversionTypeRef + val targetType = conversionTypeRef.coneType.fullyExpandedType(session) + + val isSafeAs = expression.operation == FirOperation.SAFE_AS + if (expression.operation == FirOperation.AS || isSafeAs) { + val castType = checkCasting(actualType, targetType, isSafeAs, context) + if (castType == CastingType.Impossible) { + reporter.reportOn(expression.source, FirErrors.CAST_NEVER_SUCCEEDS, context) + } else if (castType == CastingType.Always) { + 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) + } + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUselessTypeOperationCallChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUselessTypeOperationCallChecker.kt index d5a158d0029..475d4b8542f 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUselessTypeOperationCallChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUselessTypeOperationCallChecker.kt @@ -6,15 +6,14 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.fir.analysis.checkers.isFunctionForExpectTypeFromCastFeature import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.isRefinementUseless +import org.jetbrains.kotlin.fir.analysis.checkers.shouldCheckForExactType import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.resolve.fullyExpandedType -import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.types.AbstractTypeChecker // See .../types/CastDiagnosticsUtil.kt for counterparts, including isRefinementUseless, isExactTypeCast, isUpcast. object FirUselessTypeOperationCallChecker : FirTypeOperatorCallChecker() { @@ -44,55 +43,4 @@ object FirUselessTypeOperationCallChecker : FirTypeOperatorCallChecker() { } } } - - @Suppress("UNUSED_PARAMETER") - private fun shouldCheckForExactType(expression: FirTypeOperatorCall, context: CheckerContext): Boolean { - return when (expression.operation) { - FirOperation.IS, FirOperation.NOT_IS -> false - // TODO: differentiate if this expression defines the enclosing thing's type - // e.g., - // val c1 get() = 1 as Number - // val c2: Number get() = 1 as Number - FirOperation.AS, FirOperation.SAFE_AS -> true - else -> throw AssertionError("Should not be here: ${expression.operation}") - } - } - - private fun isRefinementUseless( - context: CheckerContext, - candidateType: ConeKotlinType, - targetType: ConeKotlinType, - shouldCheckForExactType: Boolean, - arg: FirExpression, - ): Boolean { - return if (shouldCheckForExactType) { - if (arg is FirFunctionCall) { - val functionSymbol = arg.toResolvedCallableSymbol() as? FirFunctionSymbol<*> - if (functionSymbol != null && functionSymbol.isFunctionForExpectTypeFromCastFeature()) return false - } - - isExactTypeCast(context, candidateType, targetType) - } else { - isUpcast(context, candidateType, targetType) - } - } - - private fun isExactTypeCast(context: CheckerContext, candidateType: ConeKotlinType, targetType: ConeKotlinType): Boolean { - if (!AbstractTypeChecker.equalTypes(context.session.typeContext, candidateType, targetType, stubTypesEqualToAnything = false)) - return false - // See comments at [isUpcast] why we need to check the existence of @ExtensionFunctionType - return candidateType.isExtensionFunctionType == targetType.isExtensionFunctionType - } - - private fun isUpcast(context: CheckerContext, candidateType: ConeKotlinType, targetType: ConeKotlinType): Boolean { - if (!AbstractTypeChecker.isSubtypeOf(context.session.typeContext, candidateType, targetType, stubTypesEqualToAnything = false)) - return false - - // E.g., foo(p1: (X) -> Y), where p1 has a functional type whose receiver type is X and return type is Y. - // For bar(p2: X.() -> Y), p2 has the same functional type (with same receiver and return types). - // The only difference is the existence of type annotation, @ExtensionFunctionType, - // which indicates that the annotated type represents an extension function. - // If one casts p1 to p2 (or vice versa), it is _not_ up cast, i.e., not redundant, yet meaningful. - return candidateType.isExtensionFunctionType == targetType.isExtensionFunctionType - } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt index 40960df03ea..26cc2714169 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt @@ -65,6 +65,15 @@ fun ConeKotlinType.toTypeProjection(variance: Variance): ConeTypeProjection = Variance.OUT_VARIANCE -> ConeKotlinTypeProjectionOut(this) } +fun ConeKotlinType.toTypeProjection(projectionKind: ProjectionKind): ConeTypeProjection { + return when (projectionKind) { + ProjectionKind.INVARIANT -> this + ProjectionKind.IN -> ConeKotlinTypeProjectionIn(this) + ProjectionKind.OUT -> ConeKotlinTypeProjectionOut(this) + ProjectionKind.STAR -> ConeStarProjection + } +} + fun ConeClassLikeType.replaceArgumentsWithStarProjections(): ConeClassLikeType { if (typeArguments.isEmpty()) return this val newArguments = Array(typeArguments.size) { ConeStarProjection } diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedAllowForTypeWithIrrelevantMixin.fir.kt b/compiler/testData/diagnostics/tests/cast/IsErasedAllowForTypeWithIrrelevantMixin.fir.kt new file mode 100644 index 00000000000..0ca00c0f869 --- /dev/null +++ b/compiler/testData/diagnostics/tests/cast/IsErasedAllowForTypeWithIrrelevantMixin.fir.kt @@ -0,0 +1,9 @@ +interface A +interface B: A +interface D + +interface BaseSuper +interface BaseImpl: BaseSuper +interface DerivedSuper: BaseSuper, BaseImpl + +fun test(t: BaseSuper) = t is DerivedSuper diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedAllowForTypeWithIrrelevantMixin.kt b/compiler/testData/diagnostics/tests/cast/IsErasedAllowForTypeWithIrrelevantMixin.kt index 2fe07a78dc0..296ae7582aa 100644 --- a/compiler/testData/diagnostics/tests/cast/IsErasedAllowForTypeWithIrrelevantMixin.kt +++ b/compiler/testData/diagnostics/tests/cast/IsErasedAllowForTypeWithIrrelevantMixin.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL interface A interface B: A interface D diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowDifferentArgInvariantPosition.fir.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowDifferentArgInvariantPosition.fir.kt deleted file mode 100644 index 0a56fe22872..00000000000 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowDifferentArgInvariantPosition.fir.kt +++ /dev/null @@ -1 +0,0 @@ -fun f(a: MutableList) = a is MutableList \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowDifferentArgInvariantPosition.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowDifferentArgInvariantPosition.kt index 430c47fdc50..2a24a55669e 100644 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowDifferentArgInvariantPosition.kt +++ b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowDifferentArgInvariantPosition.kt @@ -1 +1,2 @@ +// FIR_IDENTICAL fun f(a: MutableList) = a is MutableList \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForOverridenVariance.fir.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForOverridenVariance.fir.kt deleted file mode 100644 index 7960af40eff..00000000000 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForOverridenVariance.fir.kt +++ /dev/null @@ -1,8 +0,0 @@ -open class A -open class B: A() - -open class Base -open class SubBase : Base() - - -fun ff(l: Base) = l is SubBase diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForOverridenVariance.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForOverridenVariance.kt index c7eb75aebb4..8fd8a1806d7 100644 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForOverridenVariance.kt +++ b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForOverridenVariance.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL open class A open class B: A() diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForTypeWithConstraints.fir.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForTypeWithConstraints.fir.kt deleted file mode 100644 index afba616ee44..00000000000 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForTypeWithConstraints.fir.kt +++ /dev/null @@ -1,6 +0,0 @@ -interface A -interface B: A - -interface Base - -fun test(a: Base) where T: Base = a is T \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForTypeWithConstraints.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForTypeWithConstraints.kt index a82c71eafef..9367378ed54 100644 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForTypeWithConstraints.kt +++ b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowForTypeWithConstraints.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL interface A interface B: A diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromAny.fir.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromAny.fir.kt deleted file mode 100644 index 5d98c89982a..00000000000 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromAny.fir.kt +++ /dev/null @@ -1,2 +0,0 @@ - -fun ff(l: Any) = l is MutableList diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromAny.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromAny.kt index 1e01248664e..0b66093a4dc 100644 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromAny.kt +++ b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromAny.kt @@ -1,2 +1,3 @@ +// FIR_IDENTICAL fun ff(l: Any) = l is MutableList diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromIn.fir.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromIn.fir.kt deleted file mode 100644 index f1894cfe1b3..00000000000 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromIn.fir.kt +++ /dev/null @@ -1 +0,0 @@ -fun f(a: MutableList) = a is MutableList \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromIn.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromIn.kt index 88768eca603..d56e5c75e28 100644 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromIn.kt +++ b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromIn.kt @@ -1 +1,2 @@ +// FIR_IDENTICAL fun f(a: MutableList) = a is MutableList \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOut.fir.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOut.fir.kt deleted file mode 100644 index 0e3c75f4889..00000000000 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOut.fir.kt +++ /dev/null @@ -1,2 +0,0 @@ - -fun f(a : MutableList) = a is MutableList diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOut.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOut.kt index cf4ea9e6946..b934d776694 100644 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOut.kt +++ b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOut.kt @@ -1,2 +1,3 @@ +// FIR_IDENTICAL fun f(a : MutableList) = a is MutableList diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOutAtClass.fir.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOutAtClass.fir.kt deleted file mode 100644 index 47977766298..00000000000 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOutAtClass.fir.kt +++ /dev/null @@ -1 +0,0 @@ -fun f(a: List) = a is List diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOutAtClass.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOutAtClass.kt index 9c8ae7059dc..268037a1da0 100644 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOutAtClass.kt +++ b/compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOutAtClass.kt @@ -1 +1,2 @@ +// FIR_IDENTICAL fun f(a: List) = a is List diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithFirstInvalid.fir.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithFirstInvalid.fir.kt deleted file mode 100644 index 631525fddac..00000000000 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithFirstInvalid.fir.kt +++ /dev/null @@ -1,8 +0,0 @@ -open class A -open class B: A() -open class D - -open class Base -open class Derived: Base() - -fun test(a: Base) = a is Derived \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithFirstInvalid.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithFirstInvalid.kt index 74370d950ae..9c53e9be871 100644 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithFirstInvalid.kt +++ b/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithFirstInvalid.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL open class A open class B: A() open class D diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithSecondInvalid.fir.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithSecondInvalid.fir.kt deleted file mode 100644 index 959156243a0..00000000000 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithSecondInvalid.fir.kt +++ /dev/null @@ -1,8 +0,0 @@ -open class A -open class B: A() -open class D - -open class Base -open class Derived: Base() - -fun test(a: Base) = a is Derived \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithSecondInvalid.kt b/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithSecondInvalid.kt index 8b0c5de4329..2c7a6de373b 100644 --- a/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithSecondInvalid.kt +++ b/compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithSecondInvalid.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL open class A open class B: A() open class D diff --git a/compiler/testData/diagnostics/tests/cast/IsErasedUpcastToNonReified.fir.kt b/compiler/testData/diagnostics/tests/cast/IsErasedUpcastToNonReified.fir.kt index 9edec42d143..8efa6897a5b 100644 --- a/compiler/testData/diagnostics/tests/cast/IsErasedUpcastToNonReified.fir.kt +++ b/compiler/testData/diagnostics/tests/cast/IsErasedUpcastToNonReified.fir.kt @@ -1,5 +1,5 @@ fun test(x: T?, y: S, z: T) { - x is T + x is T x is T? y is T diff --git a/compiler/testData/diagnostics/tests/cast/IsRecursionSustainable.fir.kt b/compiler/testData/diagnostics/tests/cast/IsRecursionSustainable.fir.kt index 390ec1a0842..1607290440b 100644 --- a/compiler/testData/diagnostics/tests/cast/IsRecursionSustainable.fir.kt +++ b/compiler/testData/diagnostics/tests/cast/IsRecursionSustainable.fir.kt @@ -2,5 +2,5 @@ open class RecA: RecB() open class RecB: RecA() open class SelfR: SelfR() -fun test(f: SelfR) = f is RecA -fun test(f: RecB) = f is RecA +fun test(f: SelfR) = f is RecA +fun test(f: RecB) = f is RecA diff --git a/compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.fir.kt b/compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.fir.kt deleted file mode 100644 index b073dff8735..00000000000 --- a/compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.fir.kt +++ /dev/null @@ -1,5 +0,0 @@ - -fun ff(l: Any) = when(l) { - is MutableList -> 1 - else 2 -} diff --git a/compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.kt b/compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.kt index 32a378d94a3..6a2f27b34be 100644 --- a/compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.kt +++ b/compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL fun ff(l: Any) = when(l) { is MutableList -> 1 diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/smartCast.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/smartCast.fir.kt index cfedd5af10a..20d6abb457d 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/smartCast.fir.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/smartCast.fir.kt @@ -9,7 +9,7 @@ public class A { // FILE: main.kt fun main(a: A, ml: Any) { - if (ml is MutableList) { + if (ml is MutableList) { a.foo(ml) a.foo(ml as List) } diff --git a/compiler/testData/diagnostics/tests/regressions/kt398.fir.kt b/compiler/testData/diagnostics/tests/regressions/kt398.fir.kt deleted file mode 100644 index 4302b861f9a..00000000000 --- a/compiler/testData/diagnostics/tests/regressions/kt398.fir.kt +++ /dev/null @@ -1,11 +0,0 @@ -// KT-398 Internal error when property initializes with function - -class X() { - val check = { a : Any -> a is T } -} - -fun box() : String { - if(X().check(10)) return "fail" - if(!X().check("lala")) return "fail" - return "OK" -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/regressions/kt398.kt b/compiler/testData/diagnostics/tests/regressions/kt398.kt index ebf54e29543..5aeb9e9fe22 100644 --- a/compiler/testData/diagnostics/tests/regressions/kt398.kt +++ b/compiler/testData/diagnostics/tests/regressions/kt398.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // KT-398 Internal error when property initializes with function class X() { diff --git a/compiler/testData/diagnostics/tests/regressions/kt399.fir.kt b/compiler/testData/diagnostics/tests/regressions/kt399.fir.kt deleted file mode 100644 index 0fc1d91fffc..00000000000 --- a/compiler/testData/diagnostics/tests/regressions/kt399.fir.kt +++ /dev/null @@ -1,11 +0,0 @@ -// KT-399 Type argument inference not implemented for CALL_EXPRESSION - -fun getSameTypeChecker(obj: T) : Function1 { - return { a : Any -> a is T } -} - -fun box() : String { - if(getSameTypeChecker("lala")(10)) return "fail" - if(!getSameTypeChecker("mama")("lala")) return "fail" - return "OK" -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/regressions/kt399.kt b/compiler/testData/diagnostics/tests/regressions/kt399.kt index 7cfbea68622..6e242b3e086 100644 --- a/compiler/testData/diagnostics/tests/regressions/kt399.kt +++ b/compiler/testData/diagnostics/tests/regressions/kt399.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // KT-399 Type argument inference not implemented for CALL_EXPRESSION fun getSameTypeChecker(obj: T) : Function1 { diff --git a/compiler/testData/diagnostics/testsWithStdLib/cast/IsArray.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/cast/IsArray.fir.kt deleted file mode 100644 index b8db0800695..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/cast/IsArray.fir.kt +++ /dev/null @@ -1,5 +0,0 @@ -fun f(a: Array) = a.isArrayOf() - -fun f1(a: Array) = a is Array<*> - -fun f2(a: Array) = a is Array diff --git a/compiler/testData/diagnostics/testsWithStdLib/cast/IsArray.kt b/compiler/testData/diagnostics/testsWithStdLib/cast/IsArray.kt index 2e41788a05b..219a5d04f86 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/cast/IsArray.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/cast/IsArray.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL fun f(a: Array) = a.isArrayOf() fun f1(a: Array) = a is Array<*> diff --git a/compiler/tests-spec/testData/diagnostics/linked/type-system/type-contexts-and-scopes/inner-and-nested-type-contexts/p-1/neg/1.1.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/type-system/type-contexts-and-scopes/inner-and-nested-type-contexts/p-1/neg/1.1.fir.kt index ebc9c6c177a..2a1aecaf916 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/type-system/type-contexts-and-scopes/inner-and-nested-type-contexts/p-1/neg/1.1.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/type-system/type-contexts-and-scopes/inner-and-nested-type-contexts/p-1/neg/1.1.fir.kt @@ -9,14 +9,14 @@ class Case1(val x: AT) { inner class C() { fun case1a(x: Any) { - if (x is AT) { + if (x is AT) { "" } } fun case1b(x: Any) { when (x) { - is AT -> println("at") + is AT -> println("at") } } } diff --git a/compiler/tests-spec/testData/diagnostics/linked/type-system/type-contexts-and-scopes/inner-and-nested-type-contexts/p-1/neg/1.2.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/type-system/type-contexts-and-scopes/inner-and-nested-type-contexts/p-1/neg/1.2.fir.kt index 6a9b80610f4..65f4729d047 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/type-system/type-contexts-and-scopes/inner-and-nested-type-contexts/p-1/neg/1.2.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/type-system/type-contexts-and-scopes/inner-and-nested-type-contexts/p-1/neg/1.2.fir.kt @@ -9,13 +9,13 @@ class Case1(val x: AT) { inner class C() { fun case1a(x: Any) { - if (x is AT) { + if (x is AT) { "" } } fun case1b(x: Any) = when (x) { - is AT -> println("at") + is AT -> println("at") else -> "" } @@ -28,14 +28,14 @@ class Case2(val x: AT) { inner class C() { fun case2a(x: CharSequence) { - if (x is AT) { + if (x is AT) { "" } } fun case2b(x: CharSequence) { when (x) { - is AT -> "" + is AT -> "" } } }