[FIR] Resolve arguments of error types during type resolution

This commit is contained in:
Dmitriy Novozhilov
2022-09-23 14:46:01 +03:00
committed by Space Team
parent bc93556dbb
commit bd26c29229
13 changed files with 109 additions and 63 deletions
@@ -1,2 +1,2 @@
KtType: suspend (kotlin.Int) -> kotlin.Unit
PsiType: PsiType:Function2<? super Integer, ? super NonExistentClass, ? extends Object>
PsiType: PsiType:Function2<? super Integer, ? super NonExistentClass<Unit>, ? extends Object>
@@ -1,2 +1,2 @@
Resolved to:
0: (in Controller) suspend fun suspendHere(x: ERROR_TYPE(Symbol not found for Continuation))
0: (in Controller) suspend fun suspendHere(x: ERROR_TYPE(Symbol not found for Continuation))
@@ -12,11 +12,12 @@ import org.jetbrains.kotlin.analysis.api.KtAllowAnalysisOnEdt
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.lifetime.allowAnalysisOnEdt
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.psi.KtElement
internal fun PsiElement.nonExistentType(): PsiType =
JavaPsiFacade.getElementFactory(project).createTypeFromText("error.NonExistentClass", this)
JavaPsiFacade.getElementFactory(project).createTypeFromText(StandardNames.NON_EXISTENT_CLASS.asString(), this)
@OptIn(KtAllowAnalysisOnEdt::class)
private inline fun <E> allowLightClassesOnEdt(crossinline action: () -> E): E = allowAnalysisOnEdt(action)
@@ -59,7 +59,6 @@ import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.isPublishedApi
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.DEFAULT_CONSTRUCTOR_MARKER
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
import org.jetbrains.kotlin.resolve.jvm.JAVA_LANG_RECORD_FQ_NAME
@@ -76,6 +75,7 @@ import org.jetbrains.kotlin.types.checker.convertVariance
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.*
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.zipWithNulls
import org.jetbrains.org.objectweb.asm.Opcodes.*
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method
@@ -1446,10 +1446,10 @@ class KotlinTypeMapper @JvmOverloads constructor(
SimpleClassicTypeSystemContext.getVarianceForWildcard(parameter, projection, mode)
private fun TypeSystemCommonBackendContext.getVarianceForWildcard(
parameter: TypeParameterMarker, projection: TypeArgumentMarker, mode: TypeMappingMode
parameter: TypeParameterMarker?, projection: TypeArgumentMarker, mode: TypeMappingMode
): Variance {
val projectionKind = projection.getVariance().convertVariance()
val parameterVariance = parameter.getVariance().convertVariance()
val parameterVariance = parameter?.getVariance()?.convertVariance() ?: Variance.INVARIANT
if (parameterVariance == Variance.INVARIANT) {
return projectionKind
@@ -1465,7 +1465,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
return Variance.INVARIANT
}
if (parameterVariance == Variance.IN_VARIANCE && isMostPreciseContravariantArgument(projection.getType(), parameter)) {
if (parameterVariance == Variance.IN_VARIANCE && isMostPreciseContravariantArgument(projection.getType())) {
return Variance.INVARIANT
}
}
@@ -1484,10 +1484,11 @@ class KotlinTypeMapper @JvmOverloads constructor(
mode: TypeMappingMode,
mapType: (KotlinTypeMarker, JvmSignatureWriter, TypeMappingMode) -> Type
) {
for ((parameter, argument) in parameters.zip(arguments)) {
for ((parameter, argument) in parameters.zipWithNulls(arguments)) {
if (argument == null) break
if (argument.isStarProjection() ||
// In<Nothing, Foo> == In<*, Foo> -> In<?, Foo>
argument.getType().isNothing() && parameter.getVariance() == TypeVariance.IN
argument.getType().isNothing() && parameter?.getVariance() == TypeVariance.IN
) {
signatureVisitor.writeUnboundedWildcard()
} else {
@@ -1496,10 +1497,11 @@ class KotlinTypeMapper @JvmOverloads constructor(
signatureVisitor.writeTypeArgument(projectionKind)
val parameterVariance = parameter?.getVariance()?.convertVariance() ?: Variance.INVARIANT
mapType(
argument.getType(), signatureVisitor,
argumentMode.toGenericArgumentMode(
getEffectiveVariance(parameter.getVariance().convertVariance(), argument.getVariance().convertVariance())
getEffectiveVariance(parameterVariance, argument.getVariance().convertVariance())
)
)
@@ -37,11 +37,8 @@ import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext
import org.jetbrains.kotlin.types.checker.convertVariance
import org.jetbrains.kotlin.types.getEffectiveVariance
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeParameterMarker
// TODO: probably class upper bound should be used
@Suppress("UNUSED_PARAMETER")
fun TypeSystemCommonBackendContext.isMostPreciseContravariantArgument(type: KotlinTypeMarker, parameter: TypeParameterMarker): Boolean =
fun TypeSystemCommonBackendContext.isMostPreciseContravariantArgument(type: KotlinTypeMarker): Boolean =
type.typeConstructor().isAnyConstructor()
fun TypeSystemCommonBackendContext.isMostPreciseCovariantArgument(type: KotlinTypeMarker): Boolean =
@@ -62,7 +59,7 @@ private fun TypeSystemCommonBackendContext.canHaveSubtypesIgnoringNullability(ko
val effectiveVariance = getEffectiveVariance(parameter.getVariance().convertVariance(), projectionKind)
if (effectiveVariance == Variance.OUT_VARIANCE && !isMostPreciseCovariantArgument(type)) return true
if (effectiveVariance == Variance.IN_VARIANCE && !isMostPreciseContravariantArgument(type, parameter)) return true
if (effectiveVariance == Variance.IN_VARIANCE && !isMostPreciseContravariantArgument(type)) return true
}
return false
@@ -43,14 +43,12 @@ class ConeClassLikeErrorLookupTag(override val classId: ClassId) : ConeClassLike
class ConeErrorType(
val diagnostic: ConeDiagnostic,
val isUninferredParameter: Boolean = false,
override val typeArguments: Array<out ConeTypeProjection> = EMPTY_ARRAY,
override val attributes: ConeAttributes = ConeAttributes.Empty
) : ConeClassLikeType() {
override val lookupTag: ConeClassLikeLookupTag
get() = ConeClassLikeErrorLookupTag(ClassId.fromString("<error>"))
override val typeArguments: Array<out ConeTypeProjection>
get() = EMPTY_ARRAY
override val nullability: ConeNullability
get() = ConeNullability.UNKNOWN
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.backend.jvm
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
@@ -43,9 +45,16 @@ import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.TypeParameterMarker
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.addToStdlib.runUnless
import org.jetbrains.org.objectweb.asm.Type
class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatureWriter>, FirSessionComponent {
companion object {
val NON_EXISTENT_ID = ClassId.topLevel(StandardNames.NON_EXISTENT_CLASS)
private val typeForNonExistentClass = ConeClassLikeLookupTagImpl(NON_EXISTENT_ID)
.constructClassType(emptyArray(), isNullable = false)
}
override val typeContext = ConeTypeSystemCommonBackendContextForTypeMapping(session.typeContext)
fun mapType(type: ConeKotlinType, mode: TypeMappingMode = TypeMappingMode.DEFAULT, sw: JvmSignatureWriter? = null): Type {
@@ -67,7 +76,7 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
return
}
val possiblyInnerType = type.buildPossiblyInnerType() ?: error("possiblyInnerType with arguments should not be null")
val possiblyInnerType = type.buildPossiblyInnerType()
val innerTypesAsList = possiblyInnerType.segments()
@@ -78,7 +87,7 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
} else {
val outerType = innerTypesAsList[indexOfParameterizedType]
writeOuterClassBegin(asmType, mapType(outerType.classifier.fir.defaultType()).internalName)
writeOuterClassBegin(asmType, mapType(outerType.classifier?.fir?.defaultType() ?: typeForNonExistentClass).internalName)
writeGenericArguments(this, outerType, mode)
writeInnerParts(
@@ -96,8 +105,12 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
typeContext.hasNothingInNonContravariantPosition(type)
}
private fun ConeKotlinType.buildPossiblyInnerType(): PossiblyInnerConeType? {
if (this !is ConeClassLikeType) return null
private fun ConeKotlinType.buildPossiblyInnerType(): PossiblyInnerConeType {
fun createForError(): PossiblyInnerConeType {
return PossiblyInnerConeType(classifier = null, typeArguments.toList(), outerType = null)
}
if (this !is ConeClassLikeType) return createForError()
return when (val symbol = lookupTag.toSymbol(session)) {
is FirRegularClassSymbol -> buildPossiblyInnerType(symbol, 0)
@@ -107,7 +120,7 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
classSymbol?.let { expandedType.buildPossiblyInnerType(it, 0) }
}
else -> null
}
} ?: createForError()
}
private fun ConeClassLikeType.parentClassOrNull(): FirRegularClassSymbol? {
@@ -135,7 +148,7 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
}
private class PossiblyInnerConeType(
val classifier: FirRegularClassSymbol,
val classifier: FirRegularClassSymbol?,
val arguments: List<ConeTypeProjection>,
private val outerType: PossiblyInnerConeType?
) {
@@ -147,9 +160,9 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
type: PossiblyInnerConeType,
mode: TypeMappingMode
) {
val classifier = type.classifier.fir
val defaultType = classifier.defaultType()
val parameters = classifier.typeParameters.map { it.symbol }
val classifier = type.classifier?.fir
val defaultType = classifier?.defaultType() ?: typeForNonExistentClass
val parameters = classifier?.typeParameters.orEmpty().map { it.symbol }
val arguments = type.arguments
if ((defaultType.isFunctionalType(session) && arguments.size > BuiltInFunctionArity.BIG_ARITY)
@@ -183,16 +196,20 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
index: Int
) {
for (innerPart in innerTypesAsList.subList(index, innerTypesAsList.size)) {
sw.writeInnerClass(getJvmShortName(innerPart.classifier.fir))
sw.writeInnerClass(getJvmShortName(innerPart.classifier?.classId ?: NON_EXISTENT_ID))
writeGenericArguments(sw, innerPart, mode)
}
}
internal fun getJvmShortName(klass: FirRegularClass): String {
val result = runIf(!klass.isLocal) {
klass.classId.asSingleFqName().toUnsafe().let { JavaToKotlinClassMap.mapKotlinToJava(it)?.shortClassName?.asString() }
return getJvmShortName(klass.classId)
}
internal fun getJvmShortName(classId: ClassId): String {
val result = runUnless(classId.isLocal) {
classId.asSingleFqName().toUnsafe().let { JavaToKotlinClassMap.mapKotlinToJava(it)?.shortClassName?.asString() }
}
return result ?: SpecialNames.safeIdentifier(klass.name).identifier
return result ?: SpecialNames.safeIdentifier(classId.shortClassName).identifier
}
}
@@ -118,7 +118,7 @@ fun <T : ConeKotlinType> T.withArguments(arguments: Array<out ConeTypeProjection
@Suppress("UNCHECKED_CAST")
return when (this) {
is ConeErrorType -> this
is ConeErrorType -> ConeErrorType(diagnostic, isUninferredParameter, arguments, attributes) as T
is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, arguments, nullability.isNullable) as T
is ConeDefinitelyNotNullType -> ConeDefinitelyNotNullType(original.withArguments(arguments)) as T
else -> error("Not supported: $this: ${this.renderForDebugging()}")
@@ -127,7 +127,7 @@ private class CalculatorForNestedCall(
) {
val unwrappedType = type.lowerBoundIfFlexible()
val typeArgumentsCount = unwrappedType.argumentsCount()
if (typeArgumentsCount > 0) {
if (typeArgumentsCount > 0 && !unwrappedType.isError()) {
for (position in 0 until typeArgumentsCount) {
val argument = unwrappedType.getArgument(position)
val parameter = unwrappedType.typeConstructor().getParameter(position)
@@ -235,34 +235,6 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
TypeResolutionResult.Unresolved -> null to null
}
if (symbol == null || symbol !is FirClassifierSymbol<*>) {
val diagnostic = when {
symbol?.fir is FirEnumEntry -> {
if (isOperandOfIsOperator) {
ConeSimpleDiagnostic("'is' operator can not be applied to an enum entry.", DiagnosticKind.IsEnumEntry)
} else {
ConeSimpleDiagnostic("An enum entry should not be used as a type.", DiagnosticKind.EnumEntryAsType)
}
}
result is TypeResolutionResult.Ambiguity -> {
ConeAmbiguityError(typeRef.qualifier.last().name, result.typeCandidates.first().applicability, result.typeCandidates)
}
else -> {
ConeUnresolvedTypeQualifierError(typeRef.qualifier, isNullable = typeRef.isMarkedNullable)
}
}
return ConeErrorType(diagnostic, attributes = typeRef.annotations.computeTypeAttributes(session))
}
if (symbol is FirTypeParameterSymbol) {
for (part in typeRef.qualifier) {
if (part.typeArgumentList.typeArguments.isNotEmpty()) {
return ConeErrorType(
ConeUnexpectedTypeArgumentsError("Type arguments not allowed", part.typeArgumentList.source)
)
}
}
}
val allTypeArguments = mutableListOf<ConeTypeProjection>()
var typeArgumentsCount = 0
@@ -351,8 +323,44 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
}
}
val resultingArguments = allTypeArguments.toTypedArray()
if (symbol == null || symbol !is FirClassifierSymbol<*>) {
val diagnostic = when {
symbol?.fir is FirEnumEntry -> {
if (isOperandOfIsOperator) {
ConeSimpleDiagnostic("'is' operator can not be applied to an enum entry.", DiagnosticKind.IsEnumEntry)
} else {
ConeSimpleDiagnostic("An enum entry should not be used as a type.", DiagnosticKind.EnumEntryAsType)
}
}
result is TypeResolutionResult.Ambiguity -> {
ConeAmbiguityError(typeRef.qualifier.last().name, result.typeCandidates.first().applicability, result.typeCandidates)
}
else -> {
ConeUnresolvedTypeQualifierError(typeRef.qualifier, isNullable = typeRef.isMarkedNullable)
}
}
return ConeErrorType(
diagnostic,
typeArguments = resultingArguments,
attributes = typeRef.annotations.computeTypeAttributes(session)
)
}
if (symbol is FirTypeParameterSymbol) {
for (part in typeRef.qualifier) {
if (part.typeArgumentList.typeArguments.isNotEmpty()) {
return ConeErrorType(
ConeUnexpectedTypeArgumentsError("Type arguments not allowed", part.typeArgumentList.source),
typeArguments = resultingArguments
)
}
}
}
return symbol.constructType(
allTypeArguments.toTypedArray(),
resultingArguments,
typeRef.isMarkedNullable,
typeRef.annotations.computeTypeAttributes(session)
).also {
@@ -17,6 +17,7 @@ import org.jetbrains.annotations.NotNull
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
@@ -142,7 +143,7 @@ internal open class KtUltraLightFieldImpl protected constructor(
get() = type
private val _type: PsiType by lazyPub {
fun nonExistent() = JavaPsiFacade.getElementFactory(project).createTypeFromText("error.NonExistentClass", declaration)
fun nonExistent() = JavaPsiFacade.getElementFactory(project).createTypeFromText(StandardNames.NON_EXISTENT_CLASS.asString(), declaration)
when {
(declaration is KtProperty && declaration.hasDelegate()) || declaration is KtEnumEntry || declaration is KtObjectDeclaration ->
@@ -79,6 +79,8 @@ object StandardNames {
@JvmField
val KOTLIN_INTERNAL_FQ_NAME = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier("internal"))
val NON_EXISTENT_CLASS = FqName("error.NonExistentClass")
@JvmField
val BUILT_INS_PACKAGE_FQ_NAMES = setOf(
BUILT_INS_PACKAGE_FQ_NAME,
@@ -300,3 +300,23 @@ fun <T : Enum<T>> enumSetOf(element: T, vararg elements: T): EnumSet<T> = EnumSe
fun shouldNotBeCalled(message: String = "should not be called"): Nothing {
error(message)
}
private inline fun <T, R> Iterable<T>.zipWithDefault(other: Iterable<R>, leftDefault: () -> T, rightDefault: () -> R): List<Pair<T, R>> {
val leftIterator = this.iterator()
val rightIterator = other.iterator()
return buildList {
while (leftIterator.hasNext() && rightIterator.hasNext()) {
add(leftIterator.next() to rightIterator.next())
}
while (leftIterator.hasNext()) {
add(leftIterator.next() to rightDefault())
}
while (rightIterator.hasNext()) {
add(leftDefault() to rightIterator.next())
}
}
}
fun <T, R> Iterable<T>.zipWithNulls(other: Iterable<R>): List<Pair<T?, R?>> {
return zipWithDefault(other, { null }, { null })
}