Integrate nullability into cone types, add & use FIR flexible type

FIR fake overrides are rendered now more precisely to test this process
This commit is contained in:
Mikhail Glukhikh
2019-02-11 19:15:37 +03:00
parent 3d77f3d129
commit f8e165dbe4
15 changed files with 137 additions and 30 deletions
@@ -40,6 +40,18 @@ class ConeKotlinTypeProjectionOut(override val type: ConeKotlinType) : ConeKotli
get() = ProjectionKind.OUT get() = ProjectionKind.OUT
} }
enum class ConeNullability(val suffix: String) {
NULLABLE("?"),
UNKNOWN("!"),
NOT_NULL("");
val isNullable: Boolean get() = this != NOT_NULL
companion object {
fun create(isNullable: Boolean) = if (isNullable) NULLABLE else NOT_NULL
}
}
// We assume type IS an invariant type projection to prevent additional wrapper here // We assume type IS an invariant type projection to prevent additional wrapper here
// (more exactly, invariant type projection contains type) // (more exactly, invariant type projection contains type)
sealed class ConeKotlinType : ConeKotlinTypeProjection(), ConeTypedProjection { sealed class ConeKotlinType : ConeKotlinTypeProjection(), ConeTypedProjection {
@@ -50,12 +62,17 @@ sealed class ConeKotlinType : ConeKotlinTypeProjection(), ConeTypedProjection {
override val type: ConeKotlinType override val type: ConeKotlinType
get() = this get() = this
abstract val nullability: ConeNullability
} }
class ConeKotlinErrorType(val reason: String) : ConeKotlinType() { class ConeKotlinErrorType(val reason: String) : ConeKotlinType() {
override val typeArguments: Array<out ConeKotlinTypeProjection> override val typeArguments: Array<out ConeKotlinTypeProjection>
get() = EMPTY_ARRAY get() = EMPTY_ARRAY
override val nullability: ConeNullability
get() = ConeNullability.UNKNOWN
override fun toString(): String { override fun toString(): String {
return "<ERROR TYPE: $reason>" return "<ERROR TYPE: $reason>"
} }
@@ -68,6 +85,9 @@ class ConeClassErrorType(val reason: String) : ConeClassLikeType() {
override val typeArguments: Array<out ConeKotlinTypeProjection> override val typeArguments: Array<out ConeKotlinTypeProjection>
get() = EMPTY_ARRAY get() = EMPTY_ARRAY
override val nullability: ConeNullability
get() = ConeNullability.UNKNOWN
override fun toString(): String { override fun toString(): String {
return "<ERROR CLASS: $reason>" return "<ERROR CLASS: $reason>"
} }
@@ -99,3 +119,11 @@ abstract class ConeFunctionType : ConeClassLikeType() {
abstract val parameterTypes: List<ConeKotlinType> abstract val parameterTypes: List<ConeKotlinType>
abstract val returnType: ConeKotlinType abstract val returnType: ConeKotlinType
} }
class ConeFlexibleType(val lowerBound: ConeKotlinType, val upperBound: ConeKotlinType) : ConeKotlinType() {
override val typeArguments: Array<out ConeKotlinTypeProjection>
get() = emptyArray()
override val nullability: ConeNullability
get() = lowerBound.nullability.takeIf { it == upperBound.nullability } ?: ConeNullability.UNKNOWN
}
@@ -9,13 +9,17 @@ import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol
import org.jetbrains.kotlin.fir.types.ConeFunctionType import org.jetbrains.kotlin.fir.types.ConeFunctionType
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeKotlinTypeProjection import org.jetbrains.kotlin.fir.types.ConeKotlinTypeProjection
import org.jetbrains.kotlin.fir.types.ConeNullability
class ConeFunctionTypeImpl( class ConeFunctionTypeImpl(
override val receiverType: ConeKotlinType?, override val receiverType: ConeKotlinType?,
override val parameterTypes: List<ConeKotlinType>, override val parameterTypes: List<ConeKotlinType>,
override val returnType: ConeKotlinType, override val returnType: ConeKotlinType,
override val symbol: ConeClassLikeSymbol override val symbol: ConeClassLikeSymbol,
isNullable: Boolean
) : ConeFunctionType() { ) : ConeFunctionType() {
override val typeArguments: Array<out ConeKotlinTypeProjection> override val typeArguments: Array<out ConeKotlinTypeProjection>
get() = EMPTY_ARRAY get() = EMPTY_ARRAY
override val nullability: ConeNullability = ConeNullability.create(isNullable)
} }
@@ -11,19 +11,30 @@ import org.jetbrains.kotlin.fir.types.*
open class ConeClassTypeImpl( open class ConeClassTypeImpl(
override val symbol: ConeClassLikeSymbol, override val symbol: ConeClassLikeSymbol,
override val typeArguments: Array<ConeKotlinTypeProjection> override val typeArguments: Array<ConeKotlinTypeProjection>,
) : ConeClassLikeType() isNullable: Boolean
) : ConeClassLikeType() {
override val nullability: ConeNullability = ConeNullability.create(isNullable)
}
class ConeAbbreviatedTypeImpl( class ConeAbbreviatedTypeImpl(
override val abbreviationSymbol: ConeClassLikeSymbol, override val abbreviationSymbol: ConeClassLikeSymbol,
override val typeArguments: Array<ConeKotlinTypeProjection>, override val typeArguments: Array<ConeKotlinTypeProjection>,
override val directExpansion: ConeClassLikeType override val directExpansion: ConeClassLikeType,
isNullable: Boolean
) : ConeAbbreviatedType() { ) : ConeAbbreviatedType() {
override val symbol: ConeClassLikeSymbol override val symbol: ConeClassLikeSymbol
get() = abbreviationSymbol get() = abbreviationSymbol
override val nullability: ConeNullability = ConeNullability.create(isNullable)
} }
class ConeTypeParameterTypeImpl(override val symbol: ConeTypeParameterSymbol) : ConeTypeParameterType() { class ConeTypeParameterTypeImpl(
override val symbol: ConeTypeParameterSymbol,
isNullable: Boolean
) : ConeTypeParameterType() {
override val typeArguments: Array<out ConeKotlinTypeProjection> override val typeArguments: Array<out ConeKotlinTypeProjection>
get() = EMPTY_ARRAY get() = EMPTY_ARRAY
override val nullability: ConeNullability = ConeNullability.create(isNullable)
} }
@@ -23,10 +23,7 @@ import org.jetbrains.kotlin.fir.symbols.ConeClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.ConeClassErrorType import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType
import org.jetbrains.kotlin.fir.types.ConeKotlinTypeProjection
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
@@ -53,7 +50,7 @@ class JavaSymbolProvider(
annotationTypeRef = FirResolvedTypeRefImpl( annotationTypeRef = FirResolvedTypeRefImpl(
session = session, session = session,
psi = null, psi = null,
type = ConeClassTypeImpl(FirClassSymbol(classId!!), emptyArray()), type = ConeClassTypeImpl(FirClassSymbol(classId!!), emptyArray(), isNullable = false),
isMarkedNullable = true, isMarkedNullable = true,
annotations = emptyList() annotations = emptyList()
) )
@@ -87,18 +84,24 @@ class JavaSymbolProvider(
} }
} }
private fun flexibleType(create: (isNullable: Boolean) -> ConeKotlinType): ConeFlexibleType {
return ConeFlexibleType(create(false), create(true))
}
private fun JavaClassifierType.toFirResolvedTypeRef(): FirResolvedTypeRef { private fun JavaClassifierType.toFirResolvedTypeRef(): FirResolvedTypeRef {
val coneType = when (val classifier = classifier) { val coneType = when (val classifier = classifier) {
is JavaClass -> { is JavaClass -> {
val symbol = session.service<FirSymbolProvider>().getClassLikeSymbolByFqName(classifier.classId!!) as? ConeClassSymbol val symbol = session.service<FirSymbolProvider>().getClassLikeSymbolByFqName(classifier.classId!!) as? ConeClassSymbol
if (symbol == null) ConeKotlinErrorType("Symbol not found, for `${classifier.classId}`") if (symbol == null) ConeKotlinErrorType("Symbol not found, for `${classifier.classId}`")
else ConeClassTypeImpl(symbol, typeArguments = typeArguments.map { it.toConeProjection() }.toTypedArray()) else flexibleType { isNullable ->
ConeClassTypeImpl(symbol, typeArguments.map { it.toConeProjection() }.toTypedArray(), isNullable)
}
} }
is JavaTypeParameter -> { is JavaTypeParameter -> {
// TODO: it's unclear how to identify type parameter by the symbol // TODO: it's unclear how to identify type parameter by the symbol
// TODO: some type parameter cache (provider?) // TODO: some type parameter cache (provider?)
val symbol = createTypeParameterSymbol(classifier.name) val symbol = createTypeParameterSymbol(classifier.name)
ConeTypeParameterTypeImpl(symbol) flexibleType { isNullable -> ConeTypeParameterTypeImpl(symbol, isNullable) }
} }
else -> ConeClassErrorType(reason = "Unexpected classifier: $classifier") else -> ConeClassErrorType(reason = "Unexpected classifier: $classifier")
} }
@@ -46,7 +46,8 @@ class FirTypeDeserializer(
val id = nameResolver.getString(proto.flexibleTypeCapabilitiesId) val id = nameResolver.getString(proto.flexibleTypeCapabilitiesId)
val lowerBound = classLikeType(proto) val lowerBound = classLikeType(proto)
val upperBound = classLikeType(proto.flexibleUpperBound(typeTable)!!) val upperBound = classLikeType(proto.flexibleUpperBound(typeTable)!!)
return ConeKotlinErrorType("Not supported: Flexible types")//c.components.flexibleTypeDeserializer.create(proto, id, lowerBound, upperBound) return ConeFlexibleType(lowerBound!!, upperBound!!)
//c.components.flexibleTypeDeserializer.create(proto, id, lowerBound, upperBound)
} }
return classLikeType(proto) ?: ConeKotlinErrorType("?!id:0") return classLikeType(proto) ?: ConeKotlinErrorType("?!id:0")
@@ -97,12 +98,12 @@ class FirTypeDeserializer(
//createSuspendFunctionType(annotations, constructor, arguments, proto.nullable) //createSuspendFunctionType(annotations, constructor, arguments, proto.nullable)
ConeClassErrorType("createSuspendFunctionType not supported") ConeClassErrorType("createSuspendFunctionType not supported")
} else { } else {
ConeClassTypeImpl(constructor, arguments) ConeClassTypeImpl(constructor, arguments, isNullable = false)
} }
val abbreviatedTypeProto = proto.abbreviatedType(typeTable) ?: return simpleType val abbreviatedTypeProto = proto.abbreviatedType(typeTable) ?: return simpleType
return ConeAbbreviatedTypeImpl(typeSymbol(abbreviatedTypeProto) as ConeClassLikeSymbol, arguments, simpleType) return ConeAbbreviatedTypeImpl(typeSymbol(abbreviatedTypeProto) as ConeClassLikeSymbol, arguments, simpleType, isNullable = false)
} }
@@ -42,20 +42,21 @@ class FirTypeResolverImpl : FirTypeResolver {
} }
}.toTypedArray() }.toTypedArray()
private fun ConeSymbol.toConeKotlinType(parts: List<FirQualifierPart>): ConeKotlinType? { private fun ConeSymbol.toConeKotlinType(parts: List<FirQualifierPart>, isNullable: Boolean): ConeKotlinType? {
return when (this) { return when (this) {
is ConeTypeParameterSymbol -> { is ConeTypeParameterSymbol -> {
ConeTypeParameterTypeImpl(this) ConeTypeParameterTypeImpl(this, isNullable)
} }
is ConeClassSymbol -> { is ConeClassSymbol -> {
ConeClassTypeImpl(this, parts.toTypeProjections()) ConeClassTypeImpl(this, parts.toTypeProjections(), isNullable)
} }
is FirTypeAliasSymbol -> { is FirTypeAliasSymbol -> {
ConeAbbreviatedTypeImpl( ConeAbbreviatedTypeImpl(
abbreviationSymbol = this as ConeClassLikeSymbol, abbreviationSymbol = this as ConeClassLikeSymbol,
typeArguments = parts.toTypeProjections(), typeArguments = parts.toTypeProjections(),
directExpansion = fir.expandedConeType ?: ConeClassErrorType("Unresolved expansion") directExpansion = fir.expandedConeType ?: ConeClassErrorType("Unresolved expansion"),
isNullable = isNullable
) )
} }
else -> error("!") else -> error("!")
@@ -118,7 +119,8 @@ class FirTypeResolverImpl : FirTypeResolver {
override fun resolveUserType(typeRef: FirUserTypeRef, symbol: ConeSymbol?, scope: FirScope): ConeKotlinType { override fun resolveUserType(typeRef: FirUserTypeRef, symbol: ConeSymbol?, scope: FirScope): ConeKotlinType {
symbol ?: return ConeKotlinErrorType("Symbol not found, for `${typeRef.render()}`") symbol ?: return ConeKotlinErrorType("Symbol not found, for `${typeRef.render()}`")
return symbol.toConeKotlinType(typeRef.qualifier) ?: ConeKotlinErrorType("Failed to resolve qualified type") return symbol.toConeKotlinType(typeRef.qualifier, typeRef.isMarkedNullable)
?: ConeKotlinErrorType("Failed to resolve qualified type")
} }
override fun resolveType( override fun resolveType(
@@ -139,11 +141,12 @@ class FirTypeResolverImpl : FirTypeResolver {
(typeRef.receiverTypeRef as FirResolvedTypeRef?)?.type, (typeRef.receiverTypeRef as FirResolvedTypeRef?)?.type,
typeRef.valueParameters.map { it.returnTypeRef.coneTypeUnsafe() }, typeRef.valueParameters.map { it.returnTypeRef.coneTypeUnsafe() },
typeRef.returnTypeRef.coneTypeUnsafe(), typeRef.returnTypeRef.coneTypeUnsafe(),
resolveBuiltInQualified(KotlinBuiltIns.getFunctionClassId(typeRef.parametersCount), typeRef.session) resolveBuiltInQualified(KotlinBuiltIns.getFunctionClassId(typeRef.parametersCount), typeRef.session),
typeRef.isMarkedNullable
) )
} }
is FirImplicitBuiltinTypeRef -> { is FirImplicitBuiltinTypeRef -> {
resolveToSymbol(typeRef, scope, position)!!.toConeKotlinType(emptyList())!! resolveToSymbol(typeRef, scope, position)!!.toConeKotlinType(emptyList(), isNullable = false)!!
} }
is FirDynamicTypeRef, is FirImplicitTypeRef, is FirDelegatedTypeRef -> { is FirDynamicTypeRef, is FirImplicitTypeRef, is FirDelegatedTypeRef -> {
ConeKotlinErrorType("Not supported: ${typeRef::class.simpleName}") ConeKotlinErrorType("Not supported: ${typeRef::class.simpleName}")
@@ -63,14 +63,16 @@ class FirClassSubstitutionScope(
return when (this) { return when (this) {
is ConeKotlinErrorType -> error("Trying to substitute arguments for error type") is ConeKotlinErrorType -> error("Trying to substitute arguments for error type")
is ConeTypeParameterType -> error("Trying to substitute arguments for type parameter") is ConeTypeParameterType -> error("Trying to substitute arguments for type parameter")
is ConeClassTypeImpl -> ConeClassTypeImpl(symbol, newArguments as Array<ConeKotlinTypeProjection>) is ConeClassTypeImpl -> ConeClassTypeImpl(symbol, newArguments as Array<ConeKotlinTypeProjection>, nullability.isNullable)
is ConeAbbreviatedTypeImpl -> ConeAbbreviatedTypeImpl( is ConeAbbreviatedTypeImpl -> ConeAbbreviatedTypeImpl(
abbreviationSymbol, abbreviationSymbol,
newArguments as Array<ConeKotlinTypeProjection>, newArguments as Array<ConeKotlinTypeProjection>,
directExpansion.substitute() as? ConeClassLikeType ?: directExpansion directExpansion.substitute() as? ConeClassLikeType ?: directExpansion,
nullability.isNullable
) )
is ConeFunctionType -> TODO("Substitute function type properly") is ConeFunctionType -> TODO("Substitute function type properly")
is ConeClassLikeType -> error("Unknown class-like type to substitute: $this, ${this::class}") is ConeClassLikeType -> error("Unknown class-like type to substitute: $this, ${this::class}")
is ConeFlexibleType -> error("Trying to substitute arguments for flexible type")
} }
} }
return null return null
+1 -1
View File
@@ -1,7 +1,7 @@
FILE: genericFunctions.kt FILE: genericFunctions.kt
public abstract interface Any { public abstract interface Any {
} }
<reified T : R|Any|> public final inline function safeAs R|Any|.(): R|T| { <reified T : R|Any|> public final inline function safeAs R|Any|.(): R|T|? {
return@@@safeAs as?/R|T|(this#) return@@@safeAs as?/R|T|(this#)
} }
public abstract class Summator { public abstract class Summator {
@@ -15,7 +15,7 @@ FILE: simpleFakeOverride.kt
public constructor(): super<R|A<Some>|>() public constructor(): super<R|A<Some>|>()
public final function test(): R|kotlin/Unit| { public final function test(): R|kotlin/Unit| {
R|FakeOverride</A.foo>|(<Unresolved name: Some>#()) R|FakeOverride</A.foo: R|T|>|(<Unresolved name: Some>#())
} }
} }
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.expressions.impl.*
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.ConeSymbol import org.jetbrains.kotlin.fir.symbols.ConeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef
@@ -652,6 +653,15 @@ class FirRenderer(builder: StringBuilder) : FirVisitorVoid() {
append(returnType.asString()) append(returnType.asString())
} }
} }
is ConeFlexibleType -> {
buildString {
append("ft<")
append(lowerBound.asString())
append(", ")
append(upperBound.asString())
append(">")
}
}
} }
} }
@@ -661,7 +671,9 @@ class FirRenderer(builder: StringBuilder) : FirVisitorVoid() {
val coneType = resolvedTypeRef.type val coneType = resolvedTypeRef.type
print(coneType.asString()) print(coneType.asString())
print("|") print("|")
visitTypeRefWithNullability(resolvedTypeRef) if (coneType !is ConeKotlinErrorType && coneType !is ConeClassErrorType) {
print(coneType.nullability.suffix)
}
} }
override fun visitUserTypeRef(userTypeRef: FirUserTypeRef) { override fun visitUserTypeRef(userTypeRef: FirUserTypeRef) {
@@ -704,8 +716,19 @@ class FirRenderer(builder: StringBuilder) : FirVisitorVoid() {
if (isFakeOverride) { if (isFakeOverride) {
print("FakeOverride<") print("FakeOverride<")
} }
print(resolvedCallableReference.callableSymbol.callableId) val symbol = resolvedCallableReference.callableSymbol
print(symbol.callableId)
if (isFakeOverride) { if (isFakeOverride) {
when (symbol) {
is FirFunctionSymbol -> {
print(": ")
symbol.fir.returnTypeRef.accept(this)
}
is FirPropertySymbol -> {
print(": ")
symbol.fir.returnTypeRef.accept(this)
}
}
print(">") print(">")
} }
print("|") print("|")
@@ -0,0 +1,5 @@
public class A<T> {
public T foo(T t) {
return t;
}
}
@@ -0,0 +1,9 @@
class Some
class B : A<Some>() {
fun test() {
foo(Some())
}
}
@@ -0,0 +1,13 @@
FILE: simpleFakeOverride.kt
public final class Some {
public constructor(): super<R|kotlin/Any|>()
}
public final class B : R|A<Some>| {
public constructor(): super<R|A<Some>|>()
public final function test(): R|kotlin/Unit| {
R|FakeOverride</A.foo: R|ft<T, T>|!>|(<Unresolved name: Some>#())
}
}
+2 -2
View File
@@ -14,8 +14,8 @@ FILE: jvm.kt
public constructor(): super<R|C|>() public constructor(): super<R|C|>()
public final function test(): R|kotlin/Unit| { public final function test(): R|kotlin/Unit| {
R|FakeOverride</A.foo>|(String()) R|FakeOverride</A.foo: R|kotlin/Unit|>|(String())
R|FakeOverride</A.bar>|(String()) R|FakeOverride</A.bar: R|T|>|(String())
} }
} }
@@ -39,6 +39,11 @@ public class FirMultiModuleResolveTestGenerated extends AbstractFirMultiModuleRe
runTest("idea/testData/fir/multiModule/basicWithJava/"); runTest("idea/testData/fir/multiModule/basicWithJava/");
} }
@TestMetadata("basicWithJavaFakeOverride")
public void testBasicWithJavaFakeOverride() throws Exception {
runTest("idea/testData/fir/multiModule/basicWithJavaFakeOverride/");
}
@TestMetadata("mppFakeOverrides") @TestMetadata("mppFakeOverrides")
public void testMppFakeOverrides() throws Exception { public void testMppFakeOverrides() throws Exception {
runTest("idea/testData/fir/multiModule/mppFakeOverrides/"); runTest("idea/testData/fir/multiModule/mppFakeOverrides/");