[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.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.* import org.jetbrains.kotlin.fir.resolve.transformers.*
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase 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.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( internal class LLFirDesignatedSupertypeResolverTransformer(
private val designation: FirDesignationWithFile, private val designation: FirDesignationWithFile,
@@ -70,41 +73,67 @@ internal class LLFirDesignatedSupertypeResolverTransformer(
} }
} }
private fun collect(designation: FirDesignationWithFile): Collection<FirDesignationWithFile> { private inner class DesignationCollector {
val visited = mutableMapOf<FirElementWithResolvePhase, FirDesignationWithFile>() private val visited = mutableMapOf<FirElementWithResolvePhase, FirDesignationWithFile>()
val toVisit = mutableListOf<FirDesignationWithFile>() private val toVisit = mutableListOf<FirDesignationWithFile>()
toVisit.add(designation) fun collect(designation: FirDesignationWithFile): Collection<FirDesignationWithFile> {
while (toVisit.isNotEmpty()) { toVisit.add(designation)
for (nowVisit in toVisit) { while (toVisit.isNotEmpty()) {
checkCanceled() toVisit.forEach(::visitDesignation)
val resolver = DesignatedFirSupertypeResolverVisitor(nowVisit) toVisit.clear()
nowVisit.firFile.lazyResolveToPhase(FirResolvePhase.IMPORTS)
lockProvider.runCustomResolveUnderLock(nowVisit.firFile) { // We want to apply resolved supertypes to as many designations as possible. So we crawl the resolved supertypes of visited
nowVisit.firFile.accept(resolver, null) // 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) { private fun visitDesignation(designation: FirDesignationWithFile) {
if (value !is SupertypeComputationStatus.Computed) continue checkCanceled()
for (reference in value.supertypeRefs) { val resolver = DesignatedFirSupertypeResolverVisitor(designation)
val classLikeDeclaration = reference.type.toSymbol(session)?.fir designation.firFile.lazyResolveToPhase(FirResolvePhase.IMPORTS)
if (classLikeDeclaration !is FirClassLikeDeclaration) continue lockProvider.runCustomResolveUnderLock(designation.firFile) {
if (classLikeDeclaration is FirJavaClass) continue designation.firFile.accept(resolver, null)
if (visited.containsKey(classLikeDeclaration)) continue }
val cache = classLikeDeclaration.llFirResolvableSession?.moduleComponents?.cache ?: continue resolver.declarationTransformer.ensureDesignationPassed()
val containingFile = cache.getContainerFirFile(classLikeDeclaration) ?: continue visited[designation.target] = designation
toVisit.add(classLikeDeclaration.collectDesignation(containingFile)) }
}
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>) { fun applyToFileSymbols(designations: List<FirDesignationWithFile>) {
for (designation in designations) { for (designation in designations) {
checkCanceled() checkCanceled()
@@ -114,7 +143,7 @@ internal class LLFirDesignatedSupertypeResolverTransformer(
} }
} }
val filesToDesignations = visited.groupBy { it.firFile } val filesToDesignations = designations.groupBy { it.firFile }
for (designationsPerFile in filesToDesignations) { for (designationsPerFile in filesToDesignations) {
checkCanceled() checkCanceled()
lockProvider.runCustomResolveUnderLock(designationsPerFile.key) { lockProvider.runCustomResolveUnderLock(designationsPerFile.key) {
@@ -1,4 +1,3 @@
// FIR_IDE_IGNORE
typealias R = <!RECURSIVE_TYPEALIAS_EXPANSION!>R<!> typealias R = <!RECURSIVE_TYPEALIAS_EXPANSION!>R<!>
typealias L = <!RECURSIVE_TYPEALIAS_EXPANSION!>List<L><!> typealias L = <!RECURSIVE_TYPEALIAS_EXPANSION!>List<L><!>
@@ -1,4 +1,3 @@
// FIR_IDE_IGNORE
typealias R = <!RECURSIVE_TYPEALIAS_EXPANSION!>R<!> typealias R = <!RECURSIVE_TYPEALIAS_EXPANSION!>R<!>
typealias L = <!RECURSIVE_TYPEALIAS_EXPANSION!>List<L><!> typealias L = <!RECURSIVE_TYPEALIAS_EXPANSION!>List<L><!>