FIR IDE: workaround deadlock when resolving class members
When listing members in a class, we need to resolve super types. But if a super type is defined in another file, this file may not have been resolved. In this case, listing the members would require resolving the unresolved file, which can only be done in a write lock. Before this change, KtFirScopeProvider only acquire the read lock, which then causes read/write deadlock when the logic tries to further resolve super types defined in other files. This change makes KtFirScopeProvider acquire a write lock in the beginning so resolving can happen correctly later.
This commit is contained in:
committed by
TeamCityServer
parent
7a745cfe5c
commit
9273c6326e
+5
-1
@@ -109,8 +109,12 @@ fun <D : FirDeclaration, R> D.withFirDeclaration(
|
||||
*/
|
||||
fun <D : FirDeclaration, R> D.withFirDeclarationInWriteLock(
|
||||
resolveState: FirModuleResolveState,
|
||||
phase: FirResolvePhase = FirResolvePhase.RAW_FIR,
|
||||
action: (D) -> R,
|
||||
): R = resolveState.withLock(this, DeclarationLockType.WRITE_LOCK, action)
|
||||
): R {
|
||||
resolvedFirToPhase(phase, resolveState)
|
||||
return resolveState.withLock(this, DeclarationLockType.WRITE_LOCK, action)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of Diagnostics compiler finds for given [KtElement]
|
||||
|
||||
+3
-8
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.fir.scopes.*
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.*
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getTowerDataContextUnsafe
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.ValidityTokenOwner
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtScopeContext
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtScopeProvider
|
||||
@@ -25,18 +24,14 @@ import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtSymbolByFirBuilder
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.scopes.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirAnonymousObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirEnumEntrySymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirFileSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirNamedClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.types.KtFirType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.weakRef
|
||||
import org.jetbrains.kotlin.idea.frontend.api.scopes.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFileSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtPackageSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithDeclarations
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithMembers
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.withValidityAssertion
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
@@ -61,8 +56,8 @@ internal class KtFirScopeProvider(
|
||||
private val packageMemberScopeCache = IdentityHashMap<KtPackageSymbol, KtPackageScope>()
|
||||
|
||||
private inline fun <T> KtSymbolWithMembers.withFirForScope(crossinline body: (FirClass<*>) -> T): T? = when (this) {
|
||||
is KtFirNamedClassOrObjectSymbol -> firRef.withFir(FirResolvePhase.TYPES, body)
|
||||
is KtFirAnonymousObjectSymbol -> firRef.withFir(FirResolvePhase.TYPES, body)
|
||||
is KtFirNamedClassOrObjectSymbol -> firRef.withFirWithPossibleResolveInside(FirResolvePhase.TYPES, body)
|
||||
is KtFirAnonymousObjectSymbol -> firRef.withFirWithPossibleResolveInside(FirResolvePhase.TYPES, body)
|
||||
is KtFirEnumEntrySymbol -> firRef.withFir(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) {
|
||||
val initializer = it.initializer
|
||||
check(initializer is FirAnonymousObject)
|
||||
|
||||
+15
-3
@@ -11,8 +11,8 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.withFirDeclaration
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.withFirDeclarationInWriteLock
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.ValidityTokenOwner
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.assertIsValidAndAccessible
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
@@ -58,13 +58,25 @@ internal class FirRefWithValidityCheck<out D : FirDeclaration>(fir: D, resolveSt
|
||||
* Runs [action] with fir element with write action hold
|
||||
* Consider using this then [action] may call some resolve
|
||||
*/
|
||||
inline fun <R> withFirWithPossibleResolveInside(crossinline action: (fir: D) -> R): R {
|
||||
inline fun <R> withFirWithPossibleResolveInside(
|
||||
phase: FirResolvePhase = FirResolvePhase.RAW_FIR,
|
||||
crossinline action: (fir: D) -> R
|
||||
): R {
|
||||
token.assertIsValidAndAccessible()
|
||||
val fir = firWeakRef.get()
|
||||
?: throw EntityWasGarbageCollectedException("FirElement")
|
||||
val resolveState = resolveStateWeakRef.get()
|
||||
?: throw EntityWasGarbageCollectedException("FirModuleResolveState")
|
||||
return fir.withFirDeclarationInWriteLock(resolveState) { action(it) }
|
||||
return when (phase) {
|
||||
FirResolvePhase.BODY_RESOLVE -> {
|
||||
/*
|
||||
The BODY_RESOLVE phase is the maximum possible phase we can resolve our declaration to
|
||||
So there is not need to run whole `action` under write lock
|
||||
*/
|
||||
action(fir.withFirDeclarationInWriteLock(resolveState, phase) { it })
|
||||
}
|
||||
else -> fir.withFirDeclarationInWriteLock(resolveState, phase) { action(it) }
|
||||
}
|
||||
}
|
||||
|
||||
val resolveState
|
||||
|
||||
Reference in New Issue
Block a user