[LL FIR] KT-55339 Fix supertype collection in LL FIR supertype resolver

- LL FIR didn't report a `RECURSIVE_TYPEALIAS_EXPANSION` error on `F2`
  in `recursive.fir.kt`. While the resolver correctly associates `F2`
  with a cyclic error supertype in the supertype computation session,
  that error supertype was never assigned to `F2`.
- Why? `F2`'s supertype is a function type, so the supertype's
  `classLikeDeclaration` in `collect` is `Function1`, which has no
  containing file. Thus, the `Function1` was simply skipped, without
  adding `F2` to the designators to which supertypes are later applied
  via `apply`.
- The fix takes supertypes without containing files into account by
  crawling their type arguments, which possibly leads the collector to
  further designations, such as `F2`, which is reachable via the
  function type's type arguments.

^KT-55339 fixed
This commit is contained in:
Marco Pennekamp
2022-12-15 15:47:23 +01:00
committed by teamcity
parent 2cdcdce6ed
commit 34a1569375
3 changed files with 58 additions and 31 deletions
@@ -24,10 +24,13 @@ import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.*
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.fir.types.type
/**
* Transform designation into SUPER_TYPES phase. Affects only for designation, target declaration, it's children and dependents
* Transform designation into SUPER_TYPES phase. Affects only the designation, target declaration, its children, and dependents.
*/
internal class LLFirDesignatedSupertypeResolverTransformer(
private val designation: FirDesignationWithFile,
@@ -70,41 +73,67 @@ internal class LLFirDesignatedSupertypeResolverTransformer(
}
}
private fun collect(designation: FirDesignationWithFile): Collection<FirDesignationWithFile> {
val visited = mutableMapOf<FirElementWithResolvePhase, FirDesignationWithFile>()
val toVisit = mutableListOf<FirDesignationWithFile>()
private inner class DesignationCollector {
private val visited = mutableMapOf<FirElementWithResolvePhase, FirDesignationWithFile>()
private val toVisit = mutableListOf<FirDesignationWithFile>()
toVisit.add(designation)
while (toVisit.isNotEmpty()) {
for (nowVisit in toVisit) {
checkCanceled()
val resolver = DesignatedFirSupertypeResolverVisitor(nowVisit)
nowVisit.firFile.lazyResolveToPhase(FirResolvePhase.IMPORTS)
lockProvider.runCustomResolveUnderLock(nowVisit.firFile) {
nowVisit.firFile.accept(resolver, null)
fun collect(designation: FirDesignationWithFile): Collection<FirDesignationWithFile> {
toVisit.add(designation)
while (toVisit.isNotEmpty()) {
toVisit.forEach(::visitDesignation)
toVisit.clear()
// We want to apply resolved supertypes to as many designations as possible. So we crawl the resolved supertypes of visited
// designations to find more designations to collect.
for (supertypeComputationStatus in supertypeComputationSession.supertypeStatusMap.values) {
if (supertypeComputationStatus !is SupertypeComputationStatus.Computed) continue
supertypeComputationStatus.supertypeRefs.forEach { crawlSupertype(it.type) }
}
resolver.declarationTransformer.ensureDesignationPassed()
visited[nowVisit.target] = nowVisit
}
toVisit.clear()
return visited.values
}
for (value in supertypeComputationSession.supertypeStatusMap.values) {
if (value !is SupertypeComputationStatus.Computed) continue
for (reference in value.supertypeRefs) {
val classLikeDeclaration = reference.type.toSymbol(session)?.fir
if (classLikeDeclaration !is FirClassLikeDeclaration) continue
if (classLikeDeclaration is FirJavaClass) continue
if (visited.containsKey(classLikeDeclaration)) continue
val cache = classLikeDeclaration.llFirResolvableSession?.moduleComponents?.cache ?: continue
val containingFile = cache.getContainerFirFile(classLikeDeclaration) ?: continue
toVisit.add(classLikeDeclaration.collectDesignation(containingFile))
}
private fun visitDesignation(designation: FirDesignationWithFile) {
checkCanceled()
val resolver = DesignatedFirSupertypeResolverVisitor(designation)
designation.firFile.lazyResolveToPhase(FirResolvePhase.IMPORTS)
lockProvider.runCustomResolveUnderLock(designation.firFile) {
designation.firFile.accept(resolver, null)
}
resolver.declarationTransformer.ensureDesignationPassed()
visited[designation.target] = designation
}
private fun crawlSupertype(type: ConeKotlinType) {
val classLikeDeclaration = type.toSymbol(session)?.fir
if (classLikeDeclaration !is FirClassLikeDeclaration) return
if (classLikeDeclaration is FirJavaClass) return
if (visited.containsKey(classLikeDeclaration)) return
val containingFile =
classLikeDeclaration.llFirResolvableSession?.moduleComponents?.cache?.getContainerFirFile(classLikeDeclaration)
if (containingFile != null) {
toVisit.add(classLikeDeclaration.collectDesignation(containingFile))
} else if (type is ConeClassLikeType) {
// The `classLikeDeclaration` is not associated with a file and thus there is no need to resolve it, but it may still point
// to declarations via its type arguments which need to be collected and have a containing file. For example, a `Function1`
// could point to a type alias.
type.typeArguments.forEach { it.type?.let(::crawlSupertype) }
}
}
return visited.values
}
private fun apply(visited: Collection<FirDesignationWithFile>) {
/**
* Resolves the supertypes of [designation] and collects all designations that resolved supertypes should be applied to, including
* [designation] itself and any designations discovered via resolved supertypes.
*/
private fun collect(designation: FirDesignationWithFile): Collection<FirDesignationWithFile> =
DesignationCollector().collect(designation)
/**
* Applies the resolved supertypes in [supertypeComputationSession] to each designation in [designations].
*/
private fun apply(designations: Collection<FirDesignationWithFile>) {
fun applyToFileSymbols(designations: List<FirDesignationWithFile>) {
for (designation in designations) {
checkCanceled()
@@ -114,7 +143,7 @@ internal class LLFirDesignatedSupertypeResolverTransformer(
}
}
val filesToDesignations = visited.groupBy { it.firFile }
val filesToDesignations = designations.groupBy { it.firFile }
for (designationsPerFile in filesToDesignations) {
checkCanceled()
lockProvider.runCustomResolveUnderLock(designationsPerFile.key) {
@@ -1,4 +1,3 @@
// FIR_IDE_IGNORE
typealias R = <!RECURSIVE_TYPEALIAS_EXPANSION!>R<!>
typealias L = <!RECURSIVE_TYPEALIAS_EXPANSION!>List<L><!>
@@ -1,4 +1,3 @@
// FIR_IDE_IGNORE
typealias R = <!RECURSIVE_TYPEALIAS_EXPANSION!>R<!>
typealias L = <!RECURSIVE_TYPEALIAS_EXPANSION!>List<L><!>