From b385d787cc38c5d761155ac23bf8dce5494dc372 Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Thu, 9 Mar 2023 11:08:32 +0200 Subject: [PATCH] [FIR JS] KT-57200: Allow dynamic varargs ^KT-57200 Fixed --- .../checkers/ConeTypeCompatibilityChecker.kt | 30 ---------------- .../fir/analysis/checkers/FirHelpers.kt | 35 +++++++++++++++++++ .../FirFunctionParameterChecker.kt | 9 +++-- .../kotlin/fir/types/ConeBuiltinTypeUtils.kt | 3 +- .../kotlin/fir/types/ConeTypeIntersector.kt | 2 +- .../dynamicTypes/capturedDynamic.fir.kt | 8 ++--- .../dynamicTypes/capturedDynamicNI.fir.kt | 8 ++--- .../dynamicTypes/varargs.fir.kt | 13 ------- .../testsWithJsStdLib/dynamicTypes/varargs.kt | 1 + 9 files changed, 53 insertions(+), 56 deletions(-) delete mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.fir.kt diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt index d3a63e41e1c..5bf589ab218 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.declarations.fullyExpandedClass import org.jetbrains.kotlin.fir.resolve.getSymbolByLookupTag import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag -import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.ClassId @@ -239,35 +238,6 @@ object ConeTypeCompatibilityChecker { return null } - /** - * Collects the upper bounds as [ConeClassLikeType]. - */ - private fun ConeKotlinType?.collectUpperBounds(): Set { - if (this == null) return emptySet() - return when (this) { - is ConeErrorType -> emptySet() // Ignore error types - is ConeLookupTagBasedType -> when (this) { - is ConeClassLikeType -> setOf(this) - is ConeTypeVariableType -> { - (lookupTag.originalTypeParameter as? ConeTypeParameterLookupTag)?.typeParameterSymbol.collectUpperBounds() - } - is ConeTypeParameterType -> lookupTag.typeParameterSymbol.collectUpperBounds() - else -> throw IllegalStateException("missing branch for ${javaClass.name}") - } - is ConeDefinitelyNotNullType -> original.collectUpperBounds() - is ConeIntersectionType -> intersectedTypes.flatMap { it.collectUpperBounds() }.toSet() - is ConeFlexibleType -> upperBound.collectUpperBounds() - is ConeCapturedType -> constructor.supertypes?.flatMap { it.collectUpperBounds() }?.toSet().orEmpty() - is ConeIntegerConstantOperatorType -> setOf(getApproximatedType()) - is ConeStubType, is ConeIntegerLiteralConstantType -> throw IllegalStateException("$this should not reach here") - } - } - - private fun FirTypeParameterSymbol?.collectUpperBounds(): Set { - if (this == null) return emptySet() - return resolvedBounds.flatMap { it.coneType.collectUpperBounds() }.toSet() - } - private fun ConeKotlinType?.collectLowerBounds(): Set { if (this == null) return emptySet() return when (this) { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index 9525275bd40..9ce98c2c121 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature +import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.* @@ -732,3 +733,37 @@ fun FirElement.isLhsOfAssignment(context: CheckerContext): Boolean { val lastQualified = context.qualifiedAccessOrAssignmentsOrAnnotationCalls.lastOrNull { it != this } ?: return false return lastQualified is FirVariableAssignment && lastQualified.lValue == this } + +/** + * Collects the upper bounds as [ConeClassLikeType]. + */ +fun ConeKotlinType?.collectUpperBounds(): Set { + if (this == null) return emptySet() + return when (this) { + is ConeErrorType -> emptySet() // Ignore error types + is ConeLookupTagBasedType -> when (this) { + is ConeClassLikeType -> setOf(this) + is ConeTypeVariableType -> { + (lookupTag.originalTypeParameter as? ConeTypeParameterLookupTag)?.typeParameterSymbol.collectUpperBounds() + } + is ConeTypeParameterType -> lookupTag.typeParameterSymbol.collectUpperBounds() + else -> throw IllegalStateException("missing branch for ${javaClass.name}") + } + is ConeDefinitelyNotNullType -> original.collectUpperBounds() + is ConeIntersectionType -> intersectedTypes.flatMap { it.collectUpperBounds() }.toSet() + is ConeFlexibleType -> upperBound.collectUpperBounds() + is ConeCapturedType -> constructor.supertypes?.flatMap { it.collectUpperBounds() }?.toSet().orEmpty() + is ConeIntegerConstantOperatorType -> setOf(getApproximatedType()) + is ConeStubType, is ConeIntegerLiteralConstantType -> throw IllegalStateException("$this should not reach here") + } +} + +private fun FirTypeParameterSymbol?.collectUpperBounds(): Set { + if (this == null) return emptySet() + return resolvedBounds.flatMap { it.coneType.collectUpperBounds() }.toSet() +} + +fun ConeKotlinType.leastUpperBound(session: FirSession): ConeKotlinType { + val upperBounds = collectUpperBounds().takeIf { it.isNotEmpty() } ?: return session.builtinTypes.nullableAnyType.type + return ConeTypeIntersector.intersectTypes(session.typeContext, upperBounds) +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirFunctionParameterChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirFunctionParameterChecker.kt index b7892d787f6..2ee138252fb 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirFunctionParameterChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirFunctionParameterChecker.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.isValueClass +import org.jetbrains.kotlin.fir.analysis.checkers.leastUpperBound import org.jetbrains.kotlin.fir.analysis.checkers.valOrVarKeyword import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction @@ -25,7 +26,6 @@ import org.jetbrains.kotlin.fir.references.toResolvedValueParameterSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid -import org.jetbrains.kotlin.types.AbstractTypeChecker object FirFunctionParameterChecker : FirFunctionChecker() { override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) { @@ -61,10 +61,13 @@ object FirFunctionParameterChecker : FirFunctionChecker() { } } - val nullableNothingType = context.session.builtinTypes.nullableNothingType.coneType for (varargParameter in varargParameters) { val varargParameterType = varargParameter.returnTypeRef.coneType.arrayElementType() ?: continue - if (AbstractTypeChecker.isSubtypeOf(context.session.typeContext, varargParameterType, nullableNothingType) || + // LUB is checked to ensure varargParameterType may + // never be anything except `Nothing` or `Nothing?` + // in case it is a complex type that quantifies + // over many other types. + if (varargParameterType.leastUpperBound(context.session).isNothingOrNullableNothing || (varargParameterType.isValueClass(context.session) && !varargParameterType.isUnsignedTypeOrNullableUnsignedType) // Note: comparing with FE1.0, we skip checking if the type is not primitive because primitive types are not inline. That // is any primitive values are already allowed by the inline check. diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeBuiltinTypeUtils.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeBuiltinTypeUtils.kt index 4d7c8df4ada..865b9c7253f 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeBuiltinTypeUtils.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeBuiltinTypeUtils.kt @@ -19,11 +19,12 @@ val ConeKotlinType.isAny: Boolean get() = isBuiltinType(StandardClassIds.Any, fa val ConeKotlinType.isNullableAny: Boolean get() = isBuiltinType(StandardClassIds.Any, true) val ConeKotlinType.isNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, false) val ConeKotlinType.isNullableNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, true) +val ConeKotlinType.isNothingOrNullableNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, null) val ConeKotlinType.isUnit: Boolean get() = isBuiltinType(StandardClassIds.Unit, false) val ConeKotlinType.isBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, false) val ConeKotlinType.isNullableBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, true) -val ConeKotlinType.isBooleanOrNullableBoolean: Boolean get() = isAnyOfBuiltinType(setOf(StandardClassIds.Boolean)) +val ConeKotlinType.isBooleanOrNullableBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, null) val ConeKotlinType.isChar: Boolean get() = isBuiltinType(StandardClassIds.Char, false) val ConeKotlinType.isString: Boolean get() = isBuiltinType(StandardClassIds.String, false) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeIntersector.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeIntersector.kt index 73d64707fac..4c5843c4abe 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeIntersector.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeIntersector.kt @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.types.AbstractTypeChecker object ConeTypeIntersector { fun intersectTypes( context: ConeInferenceContext, - types: List + types: Collection ): ConeKotlinType { when (types.size) { 0 -> error("Expected some types") diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/capturedDynamic.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/capturedDynamic.fir.kt index 99811d81826..e5f51817a19 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/capturedDynamic.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/capturedDynamic.fir.kt @@ -1,18 +1,18 @@ // SKIP_TXT -fun case_1(vararg args: dynamic) { +fun case_1(vararg args: dynamic) { (")!>listOf(null) + args).toTypedArray() } -fun case_2(vararg args: dynamic) { +fun case_2(vararg args: dynamic) { (")!>listOf() + args).toTypedArray() } -fun case_3(vararg args: dynamic) { +fun case_3(vararg args: dynamic) { (")!>listOf(1) + args).toTypedArray() } -fun case_4(vararg args: dynamic) { +fun case_4(vararg args: dynamic) { (")!>listOf() + args).toTypedArray() } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/capturedDynamicNI.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/capturedDynamicNI.fir.kt index 99811d81826..e5f51817a19 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/capturedDynamicNI.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/capturedDynamicNI.fir.kt @@ -1,18 +1,18 @@ // SKIP_TXT -fun case_1(vararg args: dynamic) { +fun case_1(vararg args: dynamic) { (")!>listOf(null) + args).toTypedArray() } -fun case_2(vararg args: dynamic) { +fun case_2(vararg args: dynamic) { (")!>listOf() + args).toTypedArray() } -fun case_3(vararg args: dynamic) { +fun case_3(vararg args: dynamic) { (")!>listOf(1) + args).toTypedArray() } -fun case_4(vararg args: dynamic) { +fun case_4(vararg args: dynamic) { (")!>listOf() + args).toTypedArray() } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.fir.kt deleted file mode 100644 index 30ac9395728..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.fir.kt +++ /dev/null @@ -1,13 +0,0 @@ -// !MARK_DYNAMIC_CALLS - -fun test() { - v() - v(1) - v(1, "") -} - -fun v(vararg d: dynamic) { - for (dd in d) { - dd.foo() - } -} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.kt index 8199005fd1f..e31aca8e21a 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !MARK_DYNAMIC_CALLS fun test() {