FIR: implement early type alias expansion

This commit is contained in:
Simon Ogorodnik
2018-03-21 18:30:28 +03:00
committed by Mikhail Glukhikh
parent d15fb1963b
commit 25ae12fe07
16 changed files with 187 additions and 90 deletions
@@ -8,9 +8,12 @@ package org.jetbrains.kotlin.descriptors
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirRenderer
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.FirProvider
import org.jetbrains.kotlin.fir.resolve.FirTypeResolver
import org.jetbrains.kotlin.fir.scopes.impl.*
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
import org.jetbrains.kotlin.fir.transformSingle
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeImpl
@@ -21,8 +24,6 @@ import org.jetbrains.kotlin.fir.visitors.compose
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
fun FirElement.render(): String = buildString { this@render.accept(FirRenderer(this)) }
class FirTypeResolveTransformer(val superTypesOnly: Boolean = false) : FirTransformer<Nothing?>() {
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
return if (superTypesOnly) {
@@ -94,11 +95,15 @@ class FirTypeResolveTransformer(val superTypesOnly: Boolean = false) : FirTransf
override fun transformType(type: FirType, data: Nothing?): CompositeTransformResult<FirType> {
val typeResolver = FirTypeResolver.getInstance(type.session)
type.transformChildren(this, data)
type.transformChildren(this, null)
return transformType(type, typeResolver.resolveType(type, scope))
}
private fun transformType(type: FirType, resolvedType: ConeKotlinType): CompositeTransformResult<FirType> {
return FirResolvedTypeImpl(
type.session,
type.psi,
typeResolver.resolveType(type, scope),
resolvedType,
false,
type.annotations
).compose()
@@ -109,16 +114,27 @@ class FirTypeResolveTransformer(val superTypesOnly: Boolean = false) : FirTransf
}
override fun transformTypeProjectionWithVariance(
typeProjectionWithVariance: FirTypeProjectionWithVariance,
data: Nothing?
): CompositeTransformResult<FirTypeProjection> {
typeProjectionWithVariance.transformChildren(this, data)
return typeProjectionWithVariance.compose()
}
private inner class SuperTypeResolver : FirTransformer<Nothing?>() {
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
return element.compose()
}
override fun transformType(type: FirType, data: Nothing?): CompositeTransformResult<FirType> {
val transformedType = this@FirTypeResolveTransformer.transformType(type, data).single as FirResolvedType
val typeResolver = FirTypeResolver.getInstance(type.session)
val symbol = typeResolver.resolveToSymbol(type, scope)
val myTransformer = this@FirTypeResolveTransformer
val classId = (transformedType.type as? ConeClassLikeType)?.symbol?.classId ?: return transformedType.compose()
val firProvider = FirProvider.getInstance(transformedType.session)
if (type !is FirUserType) return myTransformer.transformType(type, data)
val classId = (symbol as? ConeClassLikeSymbol)?.classId ?: return myTransformer.transformType(type, data)
val firProvider = FirProvider.getInstance(type.session)
val classes = generateSequence(classId) { it.outerClassId }.toList()
@@ -132,7 +148,9 @@ class FirTypeResolveTransformer(val superTypesOnly: Boolean = false) : FirTransf
firProvider.getFirClassifierByFqName(it)!!.transformSingle(transformer, data)
}
return transformedType.compose()
type.transformChildren(myTransformer, null)
return myTransformer.transformType(type, typeResolver.resolveUserType(type, symbol, scope))
}
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirNamedDeclaration
import org.jetbrains.kotlin.fir.service
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
import org.jetbrains.kotlin.name.ClassId
@@ -18,7 +19,7 @@ interface FirProvider {
fun getFirClassifierContainerFile(fqName: ClassId): FirFile
fun getFirClassifierBySymbol(symbol: ConeSymbol): FirMemberDeclaration?
fun getFirClassifierBySymbol(symbol: ConeSymbol): FirNamedDeclaration?
companion object {
fun getInstance(session: FirSession): FirProvider = session.service()
@@ -7,15 +7,16 @@ package org.jetbrains.kotlin.fir.resolve
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.service
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirQualifierPart
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
interface FirQualifierResolver {
fun resolveTypeWithPrefix(parts: List<FirQualifierPart>, prefix: ClassId): ConeKotlinType?
fun resolveSymbolWithPrefix(parts: List<FirQualifierPart>, prefix: ClassId): ConeSymbol?
fun resolveType(parts: List<FirQualifierPart>): ConeKotlinType?
fun resolveSymbol(parts: List<FirQualifierPart>): ConeSymbol?
companion object {
fun getInstance(session: FirSession): FirQualifierResolver = session.service()
@@ -8,14 +8,19 @@ package org.jetbrains.kotlin.fir.resolve
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.service
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
import org.jetbrains.kotlin.fir.types.FirType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirUserType
interface FirTypeResolver {
fun resolveType(type: FirType, scope: FirScope): ConeKotlinType
fun resolveToSymbol(type: FirType, scope: FirScope): ConeSymbol?
companion object {
fun getInstance(session: FirSession): FirTypeResolver = session.service()
}
fun resolveUserType(type: FirUserType, symbol: ConeSymbol?, scope: FirScope): ConeKotlinType
}
@@ -17,9 +17,9 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
class FirProviderImpl(val session: FirSession) : FirProvider {
override fun getFirClassifierBySymbol(symbol: ConeSymbol): FirMemberDeclaration? {
override fun getFirClassifierBySymbol(symbol: ConeSymbol): FirNamedDeclaration? {
return when(symbol) {
is FirBasedSymbol<*> -> symbol.fir as? FirMemberDeclaration
is FirBasedSymbol<*> -> symbol.fir as? FirNamedDeclaration
is ConeClassLikeSymbol -> getFirClassifierByFqName(symbol.classId)
else -> error("!")
}
@@ -11,6 +11,8 @@ import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.fir.resolve.FirProvider
import org.jetbrains.kotlin.fir.resolve.FirQualifierResolver
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
import org.jetbrains.kotlin.fir.symbols.toSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.*
@@ -20,43 +22,7 @@ import org.jetbrains.kotlin.types.Variance
class FirQualifierResolverImpl(val session: FirSession) : FirQualifierResolver {
private fun List<FirQualifierPart>.toTypeProjections() = flatMap {
it.typeArguments.map {
when (it) {
is FirStarProjection -> StarProjection
is FirTypeProjectionWithVariance -> {
val type = (it.type as FirResolvedType).type
when (it.variance) {
Variance.INVARIANT -> type
Variance.IN_VARIANCE -> ConeKotlinTypeProjectionInImpl(type)
Variance.OUT_VARIANCE -> ConeKotlinTypeProjectionOutImpl(type)
}
}
else -> error("!")
}
}
}
private fun FirMemberDeclaration.toConeKotlinType(fqName: ClassId, parts: List<FirQualifierPart>): ConeKotlinType? {
return when (this) {
is FirClass -> {
ConeClassTypeImpl(fqName.toSymbol(), parts.toTypeProjections())
}
is FirTypeAlias -> {
val expansion = expandedType.coneTypeSafe<ConeClassLikeType>()
?: return ConeKotlinErrorType("Couldn't resolve expansion")
ConeAbbreviatedTypeImpl(
abbreviationSymbol = fqName.toSymbol(),
typeArguments = parts.toTypeProjections(),
directExpansion = expansion
)
}
else -> error("!")
}
}
override fun resolveTypeWithPrefix(parts: List<FirQualifierPart>, prefix: ClassId): ConeKotlinType? {
override fun resolveSymbolWithPrefix(parts: List<FirQualifierPart>, prefix: ClassId): ConeSymbol? {
val firProvider = FirProvider.getInstance(session)
val fqName = ClassId(
@@ -64,12 +30,12 @@ class FirQualifierResolverImpl(val session: FirSession) : FirQualifierResolver {
parts.drop(1).fold(prefix.relativeClassName) { prefix, suffix -> prefix.child(suffix.name) },
false
)
val foundClassifier = firProvider.getFirClassifierByFqName(fqName)
firProvider.getFirClassifierByFqName(fqName) ?: return null
return foundClassifier?.toConeKotlinType(fqName, parts)
return ConeClassLikeSymbol(fqName)
}
override fun resolveType(parts: List<FirQualifierPart>): ConeKotlinType? {
override fun resolveSymbol(parts: List<FirQualifierPart>): ConeSymbol? {
val firProvider = FirProvider.getInstance(session)
if (parts.isNotEmpty()) {
@@ -84,7 +50,7 @@ class FirQualifierResolverImpl(val session: FirSession) : FirQualifierResolver {
val foundClassifier = firProvider.getFirClassifierByFqName(fqName)
if (foundClassifier != null) {
return foundClassifier.toConeKotlinType(fqName, parts)
return ConeClassLikeSymbol(fqName)
}
}
return null
@@ -5,40 +5,107 @@
package org.jetbrains.kotlin.fir.resolve.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.FirProvider
import org.jetbrains.kotlin.fir.resolve.FirQualifierResolver
import org.jetbrains.kotlin.fir.resolve.FirTypeResolver
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeKotlinErrorType
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.fir.types.impl.*
import org.jetbrains.kotlin.types.Variance
class FirTypeResolverImpl : FirTypeResolver {
override fun resolveType(type: FirType, scope: FirScope): ConeKotlinType {
private fun List<FirQualifierPart>.toTypeProjections() = flatMap {
it.typeArguments.map {
when (it) {
is FirStarProjection -> StarProjection
is FirTypeProjectionWithVariance -> {
val type = (it.type as FirResolvedType).type
when (it.variance) {
Variance.INVARIANT -> type
Variance.IN_VARIANCE -> ConeKotlinTypeProjectionInImpl(type)
Variance.OUT_VARIANCE -> ConeKotlinTypeProjectionOutImpl(type)
}
}
else -> error("!")
}
}
}
private fun ConeSymbol.toConeKotlinType(parts: List<FirQualifierPart>, session: FirSession): ConeKotlinType? {
val firProvider = FirProvider.getInstance(session)
val fir = firProvider.getFirClassifierBySymbol(this) ?: return null
return when (fir) {
is FirTypeParameter -> {
ConeTypeParameterTypeImpl(this as ConeTypeParameterSymbol)
}
is FirClass -> {
ConeClassTypeImpl(this as ConeClassLikeSymbol, parts.toTypeProjections())
}
is FirTypeAlias -> {
val coneTypeSafe = fir.expandedType.coneTypeSafe<ConeClassLikeType>()
val expansion = if (coneTypeSafe != null) coneTypeSafe else {
return ConeKotlinErrorType("Couldn't resolve expansion")
}
ConeAbbreviatedTypeImpl(
abbreviationSymbol = this as ConeClassLikeSymbol,
typeArguments = parts.toTypeProjections(),
directExpansion = expansion
)
}
else -> error("!")
}
}
override fun resolveToSymbol(type: FirType, scope: FirScope): ConeSymbol? {
return when (type) {
is FirResolvedType -> type.type
is FirResolvedType -> type.coneTypeSafe<ConeSymbolBasedType>()?.symbol
is FirUserType -> {
val qualifierResolver = FirQualifierResolver.getInstance(type.session)
var resolvedType: ConeKotlinType? = null
var resolvedSymbol: ConeSymbol? = null
scope.processClassifiersByName(type.qualifier.first().name) { symbol ->
resolvedType = when (symbol) {
resolvedSymbol = when (symbol) {
is ConeClassLikeSymbol -> {
qualifierResolver.resolveTypeWithPrefix(type.qualifier, symbol.classId)
qualifierResolver.resolveSymbolWithPrefix(type.qualifier, symbol.classId)
}
is ConeTypeParameterSymbol -> {
assert(type.qualifier.size == 1)
ConeTypeParameterTypeImpl(symbol)
symbol
}
else -> error("!")
}
resolvedType == null
resolvedSymbol == null
}
// TODO: Imports
resolvedType ?: qualifierResolver.resolveType(type.qualifier) ?: ConeKotlinErrorType("Failed to resolve qualified type")
resolvedSymbol ?: qualifierResolver.resolveSymbol(type.qualifier)
}
else -> null
}
}
override fun resolveUserType(type: FirUserType, symbol: ConeSymbol?, scope: FirScope): ConeKotlinType {
symbol ?: return ConeKotlinErrorType("Symbol not found")
return symbol.toConeKotlinType(type.qualifier, type.session) ?: ConeKotlinErrorType("Failed to resolve qualified type")
}
override fun resolveType(type: FirType, scope: FirScope): ConeKotlinType {
return when (type) {
is FirResolvedType -> type.type
is FirUserType -> {
resolveUserType(type, resolveToSymbol(type, scope), scope)
}
is FirErrorType -> {
ConeKotlinErrorType(type.reason)
@@ -18,6 +18,8 @@ import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.Printer
fun FirElement.render(): String = buildString { this@render.accept(FirRenderer(this)) }
class FirRenderer(builder: StringBuilder) : FirVisitorVoid() {
private val printer = Printer(builder)
@@ -0,0 +1,7 @@
package b
class A
typealias TA = A
// analyzePriority: 1
@@ -0,0 +1,7 @@
package a
import b.TA
class MyClass : TA
// analyzePriority: 0
@@ -0,0 +1,3 @@
FILE: TypeAliasExpansion.kt
(resolved) public? final class MyClass() : R/b/TA = b/A/ {
}
+9 -9
View File
@@ -1,27 +1,27 @@
FILE: simpleClass.kt
(resolved) public? abstract interface SomeInterface() {
public? final? function foo(x: R/error: Failed to resolve qualified type/, y: R/error: Failed to resolve qualified type/): R/error: Failed to resolve qualified type/
public? final? function foo(x: R/error: Symbol not found/, y: R/error: Symbol not found/): R/error: Symbol not found/
public? final? property bar(val): R/error: Failed to resolve qualified type/
public? get(): R/error: Failed to resolve qualified type/
public? final? property bar(val): R/error: Symbol not found/
public? get(): R/error: Symbol not found/
}
(resolved) public? final class SomeClass() : R/SomeInterface/ {
private final? property baz(val): R/error: Not supported: FirImplicitTypeImpl/ = STUB
public? get(): R/error: Not supported: FirImplicitTypeImpl/
public? open? override function foo(x: R/error: Failed to resolve qualified type/, y: R/error: Failed to resolve qualified type/): R/error: Failed to resolve qualified type/ {
public? open? override function foo(x: R/error: Symbol not found/, y: R/error: Symbol not found/): R/error: Symbol not found/ {
}
public? open? override property bar(var): R/error: Failed to resolve qualified type/
public? open? override property bar(var): R/error: Symbol not found/
public? get(): R/error: Not supported: FirImplicitTypeImpl/ {
STUB
}
public? set(value: R/error: Failed to resolve qualified type/): R/error: Not supported: FirImplicitTypeImpl/ {
public? set(value: R/error: Symbol not found/): R/error: Not supported: FirImplicitTypeImpl/ {
}
public? final? property fau(var): R/error: Failed to resolve qualified type/
public? get(): R/error: Failed to resolve qualified type/
public? set(value: R/error: Failed to resolve qualified type/): R/kotlin/Unit/
public? final? property fau(var): R/error: Symbol not found/
public? get(): R/error: Symbol not found/
public? set(value: R/error: Symbol not found/): R/kotlin/Unit/
}
+2 -2
View File
@@ -2,6 +2,6 @@ open class A
interface B<S, T : A>
typealias C<T> = B<T, A>
class D : C<A>
typealias C<T> = B<T, A>
+1 -1
View File
@@ -3,6 +3,6 @@ FILE: typeAliasWithGeneric.kt
}
(resolved) <(resolved) S, (resolved) T : R/A/> public? abstract interface B() {
}
(resolved) <(resolved) T> public? final typealias C = R/B<T, A>/
(resolved) public? final class D() : R/C<A> = B<T, A>/ {
}
(resolved) <(resolved) T> public? final typealias C = R/B<T, A>/
@@ -8,12 +8,14 @@ package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.FirTotalResolveTransformer
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.resolve.FirProvider
import org.jetbrains.kotlin.fir.resolve.FirQualifierResolver
import org.jetbrains.kotlin.fir.resolve.FirTypeResolver
import org.jetbrains.kotlin.fir.resolve.impl.FirProviderImpl
import org.jetbrains.kotlin.fir.resolve.impl.FirQualifierResolverImpl
import org.jetbrains.kotlin.fir.resolve.impl.FirTypeResolverImpl
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
@@ -24,20 +26,7 @@ abstract class AbstractFirResolveTestCase : KotlinTestWithEnvironment() {
return createEnvironmentWithMockJdk(ConfigurationKind.JDK_NO_RUNTIME)
}
fun doTest(path: String) {
val file = File(path)
val allFiles = listOf(file) + file.parentFile.listFiles { sibling ->
sibling.name.removePrefix(file.nameWithoutExtension).removeSuffix(file.extension).matches("\\.[0-9]+\\.".toRegex())
}
val ktFiles = allFiles.map {
val text = KotlinTestUtils.doLoadFile(it)
KotlinTestUtils.createFile(it.name, text, project)
}
fun doCreateAndProcessFir(ktFiles: List<KtFile>): List<FirFile> {
val session = object : FirSessionBase() {
init {
registerComponent(FirProvider::class, FirProviderImpl(this))
@@ -57,6 +46,31 @@ abstract class AbstractFirResolveTestCase : KotlinTestWithEnvironment() {
transformer.processFiles(it)
}
return firFiles
}
fun doTest(path: String) {
val file = File(path)
val allFiles = listOf(file) + file.parentFile.listFiles { sibling ->
sibling.name.removePrefix(file.nameWithoutExtension).removeSuffix(file.extension).matches("\\.[0-9]+\\.".toRegex())
}
val ktFiles =
allFiles.map {
val text = KotlinTestUtils.doLoadFile(it)
it.name to text
}
.sortedBy { (_, text) ->
KotlinTestUtils.parseDirectives(text)["analyzePriority"]?.toInt()
}
.map { (name, text) ->
KotlinTestUtils.createFile(name, text, project)
}
val firFiles = doCreateAndProcessFir(ktFiles)
val firFileDump = StringBuilder().also { firFiles.first().accept(FirRenderer(it), null) }.toString()
val expectedPath = path.replace(".kt", ".txt")
KotlinTestUtils.assertEqualsToFile(File(expectedPath), firFileDump)
@@ -110,5 +110,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/fir/resolve/multifile/simpleImportOuter.kt");
doTest(fileName);
}
@TestMetadata("TypeAliasExpansion.kt")
public void testTypeAliasExpansion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/fir/resolve/multifile/TypeAliasExpansion.kt");
doTest(fileName);
}
}
}