[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:
committed by
Space Team
parent
0fb3c033e7
commit
96c45c74e8
+15
@@ -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].
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+6
-1
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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
|
||||
}
|
||||
}
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user