[FIR JS] KT-57200: Allow dynamic varargs

^KT-57200 Fixed
This commit is contained in:
Nikolay Lunyak
2023-03-09 11:08:32 +02:00
committed by Space Team
parent 5d664aac92
commit b385d787cc
9 changed files with 53 additions and 56 deletions
@@ -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<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) {
@@ -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<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)
}
@@ -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.
@@ -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)
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.types.AbstractTypeChecker
object ConeTypeIntersector {
fun intersectTypes(
context: ConeInferenceContext,
types: List<ConeKotlinType>
types: Collection<ConeKotlinType>
): ConeKotlinType {
when (types.size) {
0 -> error("Expected some types")
@@ -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()
}
@@ -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()
}
@@ -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<!>()
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !MARK_DYNAMIC_CALLS
fun test() {