[FIR JS] KT-57200: Allow dynamic varargs
^KT-57200 Fixed
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 d3a63e4..5bf589a 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.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 @@
return null
}
- /**
- * Collects the upper bounds as [ConeClassLikeType].
- */
- private fun ConeKotlinType?.collectUpperBounds(): Set<ConeClassLikeType> {
- 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<ConeClassLikeType> {
- if (this == null) return emptySet()
- return resolvedBounds.flatMap { it.coneType.collectUpperBounds() }.toSet()
- }
-
private fun ConeKotlinType?.collectLowerBounds(): Set<ConeClassLikeType> {
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 9525275..9ce98c2 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.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 @@
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<ConeClassLikeType> {
+ 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<ConeClassLikeType> {
+ 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 b7892d7..2ee1382 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.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.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 @@
}
}
- 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 4d7c8df..865b9c7 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.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 73d6470..4c5843c 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 @@
object ConeTypeIntersector {
fun intersectTypes(
context: ConeInferenceContext,
- types: List<ConeKotlinType>
+ types: Collection<ConeKotlinType>
): 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 99811d8..e5f5181 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(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> args: dynamic) {
+fun case_1(vararg args: dynamic) {
(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.List<kotlin.Nothing..kotlin.Any?!>")!>listOf(null) + args<!>).toTypedArray()
}
-fun case_2(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> args: dynamic) {
+fun case_2(vararg args: dynamic) {
(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.List<kotlin.Nothing..kotlin.Any?!>")!>listOf<Nothing>() + args<!>).toTypedArray()
}
-fun case_3(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> args: dynamic) {
+fun case_3(vararg args: dynamic) {
(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.List<kotlin.Nothing..kotlin.Any?!>")!>listOf(1) + args<!>).toTypedArray()
}
-fun case_4(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> args: dynamic) {
+fun case_4(vararg args: dynamic) {
(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.List<kotlin.Nothing..kotlin.Any?!>")!>listOf<Any?>() + args<!>).toTypedArray()
}
diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/capturedDynamicNI.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/capturedDynamicNI.fir.kt
index 99811d8..e5f5181 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(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> args: dynamic) {
+fun case_1(vararg args: dynamic) {
(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.List<kotlin.Nothing..kotlin.Any?!>")!>listOf(null) + args<!>).toTypedArray()
}
-fun case_2(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> args: dynamic) {
+fun case_2(vararg args: dynamic) {
(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.List<kotlin.Nothing..kotlin.Any?!>")!>listOf<Nothing>() + args<!>).toTypedArray()
}
-fun case_3(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> args: dynamic) {
+fun case_3(vararg args: dynamic) {
(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.List<kotlin.Nothing..kotlin.Any?!>")!>listOf(1) + args<!>).toTypedArray()
}
-fun case_4(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> args: dynamic) {
+fun case_4(vararg args: dynamic) {
(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.List<kotlin.Nothing..kotlin.Any?!>")!>listOf<Any?>() + 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 30ac939..0000000
--- 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(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> d: dynamic) {
- for (dd in d) {
- dd.<!DEBUG_INFO_DYNAMIC!>foo<!>()
- }
-}
diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/varargs.kt
index 8199005..e31aca8 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() {