[LL FIR] improve contract violation checks

Add check into LLFirModuleLazyDeclarationResolver to die fast
and avoid StackOverflowError for jumping phases

^KT-56551
^KT-57802
^KT-57811
This commit is contained in:
Dmitrii Gridin
2023-09-21 19:47:45 +02:00
committed by Space Team
parent 0fb3c033e7
commit 96c45c74e8
10 changed files with 83 additions and 2 deletions
@@ -54,6 +54,21 @@ internal class LLFirLockProvider(private val checker: LLFirLazyResolveContractCh
}
}
/**
* A contract violation check to be sure that we won't request a violated phase later.
* This is useful to catch a contract violation for jumping phases because they may encounter infinite recursion.
*
* Example: we have cycle between phases 'implicit type (1) -> body (2) -> implicit type (3)` and
* we can get [StackOverflowError] because regular phases checks can't catch such case
* because will check only implicit type -> implicit type resolution due to
* sequent resolution requests
*
* @see org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.LLFirModuleLazyDeclarationResolver
*/
fun checkContractViolations(toPhase: FirResolvePhase) {
checker.checkIfCanLazyResolveToPhase(toPhase, isJumpingPhase = true)
}
/**
* Locks an a [FirElementWithResolveState] to resolve from `phase - 1` to [phase] and
* then updates the [FirElementWithResolveState.resolveState] to a [phase].
@@ -23,7 +23,7 @@ internal class LLFirLazyResolveContractChecker {
}
}
private fun checkIfCanLazyResolveToPhase(requestedPhase: FirResolvePhase, isJumpingPhase: Boolean) {
internal fun checkIfCanLazyResolveToPhase(requestedPhase: FirResolvePhase, isJumpingPhase: Boolean) {
val currentPhase = currentTransformerPhase.get() ?: return
if (requestedPhase > currentPhase || !isJumpingPhase && requestedPhase == currentPhase) {
@@ -184,6 +184,11 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
var currentPhase = getMinResolvePhase(targets).coerceAtLeast(FirResolvePhase.IMPORTS)
if (currentPhase >= toPhase) return
val lockProvider = moduleComponents.globalResolveComponents.lockProvider
// to catch a contract violation for jumping phases
lockProvider.checkContractViolations(toPhase)
while (currentPhase < toPhase) {
currentPhase = currentPhase.next
checkCanceled()
@@ -193,7 +198,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
phase = currentPhase,
target = target,
scopeSession = scopeSession,
lockProvider = moduleComponents.globalResolveComponents.lockProvider,
lockProvider = lockProvider,
towerDataContextCollector = towerDataContextCollector,
)
}
@@ -22601,6 +22601,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
runTest("compiler/testData/diagnostics/tests/modifiers/const/kCallable_before.kt");
}
@Test
@TestMetadata("kotlinJavaCycle.kt")
public void testKotlinJavaCycle() throws Exception {
runTest("compiler/testData/diagnostics/tests/modifiers/const/kotlinJavaCycle.kt");
}
@Test
@TestMetadata("kt12248.kt")
public void testKt12248() throws Exception {
@@ -22601,6 +22601,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
runTest("compiler/testData/diagnostics/tests/modifiers/const/kCallable_before.kt");
}
@Test
@TestMetadata("kotlinJavaCycle.kt")
public void testKotlinJavaCycle() throws Exception {
runTest("compiler/testData/diagnostics/tests/modifiers/const/kotlinJavaCycle.kt");
}
@Test
@TestMetadata("kt12248.kt")
public void testKt12248() throws Exception {
@@ -22601,6 +22601,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
runTest("compiler/testData/diagnostics/tests/modifiers/const/kCallable_before.kt");
}
@Test
@TestMetadata("kotlinJavaCycle.kt")
public void testKotlinJavaCycle() throws Exception {
runTest("compiler/testData/diagnostics/tests/modifiers/const/kotlinJavaCycle.kt");
}
@Test
@TestMetadata("kt12248.kt")
public void testKt12248() throws Exception {
@@ -22607,6 +22607,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
runTest("compiler/testData/diagnostics/tests/modifiers/const/kCallable_before.kt");
}
@Test
@TestMetadata("kotlinJavaCycle.kt")
public void testKotlinJavaCycle() throws Exception {
runTest("compiler/testData/diagnostics/tests/modifiers/const/kotlinJavaCycle.kt");
}
@Test
@TestMetadata("kt12248.kt")
public void testKt12248() throws Exception {
@@ -0,0 +1,9 @@
Suppressed: org.jetbrains.kotlin.util.ReenteringLazyValueComputationException
at org.jetbrains.kotlin.types.DeferredType.lambda$static$0(DeferredType.java:35)
at org.jetbrains.kotlin.storage.LockBasedStorageManager$3.recursionDetected(LockBasedStorageManager.java:192)
at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:393)
at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527)
at org.jetbrains.kotlin.types.DeferredType.getDelegate(DeferredType.java:108)
at org.jetbrains.kotlin.types.WrappedType.getAttributes(KotlinType.kt:135)
at org.jetbrains.kotlin.types.KotlinType.getAnnotations(KotlinType.kt:57)
at org.jetbrains.kotlin.types.checker.ClassicTypeSystemContext$DefaultImpls.hasAnnotation(ClassicTypeSystemContext.kt:753)
@@ -0,0 +1,22 @@
// IGNORE_REVERSED_RESOLVE
// IGNORE_CONTRACT_VIOLATIONS
// FIR_IDENTICAL
// FILE: Bar.java
public class Bar {
public static final int BAR = Foo.FOO + 1;
}
// FILE: Test.kt
class Foo {
companion object {
const val FOO = Baz.BAZ + 1
}
}
class Baz {
companion object {
const val BAZ = Bar.BAR + 1
}
}
@@ -22607,6 +22607,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/modifiers/const/kCallable_before.kt");
}
@Test
@TestMetadata("kotlinJavaCycle.kt")
public void testKotlinJavaCycle() throws Exception {
runTest("compiler/testData/diagnostics/tests/modifiers/const/kotlinJavaCycle.kt");
}
@Test
@TestMetadata("kt12248.kt")
public void testKt12248() throws Exception {