Skip to content

Commit 5d7cf50

Browse files
mcpiromanSpace Team
authored andcommitted
[IR] Fix validating unbound symbols but not tree consistency
This fixes the checkUnboundSymbols() function, which specifies checkUnboundSymbols = true, checkTreeConsistency = false. Because only checkTreeConsistency was tested in IrValidator, it was a no-op. Also, relax the rule for early return in the case of duplicated nodes. There is, unfortunately, a case where IR nodes are duplicated, but not in a way that causes an infinite loop (KT-79807). But in checkUnboundSymbols() we don't yet want to fail on that, as we only check symbols. KT-77819 KT-79807
1 parent 822da80 commit 5d7cf50

File tree

3 files changed

+79
-18
lines changed

3 files changed

+79
-18
lines changed

compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/IrValidator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ private fun performBasicIrValidation(
185185
) {
186186
// Phase 1: Traverse the IR tree to check for structural consistency.
187187
// If any issues are detected, validation stops here to avoid problems like infinite recursion during the next phase.
188-
if (validatorConfig.checkTreeConsistency) {
188+
if (validatorConfig.checkTreeConsistency || validatorConfig.checkUnboundSymbols) {
189189
try {
190190
element.checkTreeConsistency(reportError, validatorConfig)
191191
} catch (_: TreeConsistencyError) {

compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/checkers/CheckTreeConsistencyVisitor.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ private class CheckTreeConsistencyVisitor(val reportError: ReportIrValidationErr
7474
}
7575

7676
private fun handleParent(declaration: IrDeclaration, actualParent: IrDeclarationParent?) {
77+
if (!config.checkTreeConsistency) return
7778
if (actualParent == null) return
7879
try {
7980
val assignedParent = declaration.parent
@@ -102,12 +103,17 @@ private class CheckTreeConsistencyVisitor(val reportError: ReportIrValidationErr
102103

103104
private fun checkDuplicateNode(element: IrElement) {
104105
if (!visitedElements.add(element)) {
105-
val renderString = if (element is IrTypeParameter) element.render() + " of " + element.parent.render() else element.render()
106-
reportError(null, element, "Duplicate IR node: $renderString", parentChain)
106+
if (config.checkTreeConsistency) {
107+
hasInconsistency = true
108+
val renderString = if (element is IrTypeParameter) element.render() + " of " + element.parent.render() else element.render()
109+
reportError(null, element, "Duplicate IR node: $renderString", parentChain)
110+
}
107111

108-
// The IR tree is completely messed up if it includes one element twice. It may not be a tree at all, there may be cycles.
109-
// Give up early to avoid stack overflow.
110-
throw TreeConsistencyError(element)
112+
if (element in parentChain) {
113+
// Not only is the same element twice in the tree, there is some cycle, so it is not a tree at all.
114+
// Give up early to avoid stack overflow.
115+
throw TreeConsistencyError(element)
116+
}
111117
}
112118
}
113119
}

compiler/ir/backend.common/test/org/jetbrains/kotlin/backend/common/IrValidatorTest.kt

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ class IrValidatorTest {
5050

5151
private lateinit var messageCollector: MessageCollectorImpl
5252
private lateinit var module: IrModuleFragment
53+
private val defaultValidationConfig = IrValidatorConfig(
54+
checkTypes = true,
55+
checkValueScopes = true,
56+
checkTypeParameterScopes = true,
57+
checkCrossFileFieldUsage = true,
58+
checkAllKotlinFieldsArePrivate = true,
59+
checkVisibilities = true,
60+
checkVarargTypes = true,
61+
checkUnboundSymbols = true,
62+
checkInlineFunctionUseSites = { it.symbol.owner.name.toString() != "inlineFunctionUseSiteNotPermitted" }
63+
)
5364

5465
@BeforeTest
5566
fun setUp() {
@@ -145,24 +156,19 @@ class IrValidatorTest {
145156
}
146157
}
147158

148-
private fun testValidation(mode: IrVerificationMode, tree: IrElement, expectedMessages: List<Message>) {
159+
private fun testValidation(
160+
mode: IrVerificationMode,
161+
tree: IrElement,
162+
expectedMessages: List<Message>,
163+
config: IrValidatorConfig = defaultValidationConfig,
164+
) {
149165
runValidationAndAssert(mode) {
150166
validateIr(messageCollector, mode) {
151167
performBasicIrValidation(
152168
tree,
153169
TestIrBuiltins,
154170
phaseName = "IrValidatorTest",
155-
IrValidatorConfig(
156-
checkTypes = true,
157-
checkValueScopes = true,
158-
checkTypeParameterScopes = true,
159-
checkCrossFileFieldUsage = true,
160-
checkAllKotlinFieldsArePrivate = true,
161-
checkVisibilities = true,
162-
checkVarargTypes = true,
163-
checkUnboundSymbols = true,
164-
checkInlineFunctionUseSites = { it.symbol.owner.name.toString() != "inlineFunctionUseSiteNotPermitted" }
165-
)
171+
config
166172
)
167173
assertEquals(expectedMessages, messageCollector.messages)
168174
}
@@ -266,6 +272,55 @@ class IrValidatorTest {
266272
)
267273
}
268274

275+
@Test
276+
fun `duplicated nodes do not stop validation if checkTreeConsistency = false`() {
277+
testValidation(
278+
IrVerificationMode.WARNING,
279+
buildInvalidIrTreeWithLocations(),
280+
listOf(
281+
Message(
282+
WARNING,
283+
$$"""
284+
[IR VALIDATION] IrValidatorTest: unexpected type: expected kotlin.String, got kotlin.Any
285+
STRING_CONCATENATION type=kotlin.Any
286+
inside CALL 'public final fun foo ($receiver: kotlin.String, p0: kotlin.Any): kotlin.Unit declared in org.sample' type=kotlin.Any origin=null
287+
inside BLOCK_BODY
288+
inside FUN name:foo visibility:public modality:FINAL <> ($receiver:kotlin.String, p0:kotlin.Any) returnType:kotlin.Unit
289+
inside FILE fqName:org.sample fileName:test.kt
290+
""".trimIndent(),
291+
CompilerMessageLocation.create("test.kt", 1, 10, null),
292+
),
293+
Message(
294+
WARNING,
295+
$$"""
296+
[IR VALIDATION] IrValidatorTest: unexpected type: expected kotlin.String, got kotlin.Any
297+
STRING_CONCATENATION type=kotlin.Any
298+
inside CALL 'public final fun foo ($receiver: kotlin.String, p0: kotlin.Any): kotlin.Unit declared in org.sample' type=kotlin.Any origin=null
299+
inside BLOCK_BODY
300+
inside FUN name:foo visibility:public modality:FINAL <> ($receiver:kotlin.String, p0:kotlin.Any) returnType:kotlin.Unit
301+
inside FILE fqName:org.sample fileName:test.kt
302+
""".trimIndent(),
303+
CompilerMessageLocation.create("test.kt", 1, 10, null),
304+
),
305+
Message(
306+
WARNING,
307+
$$"""
308+
[IR VALIDATION] IrValidatorTest: unexpected type: expected kotlin.Unit, got kotlin.Any
309+
CALL 'public final fun foo ($receiver: kotlin.String, p0: kotlin.Any): kotlin.Unit declared in org.sample' type=kotlin.Any origin=null
310+
inside BLOCK_BODY
311+
inside FUN name:foo visibility:public modality:FINAL <> ($receiver:kotlin.String, p0:kotlin.Any) returnType:kotlin.Unit
312+
inside FILE fqName:org.sample fileName:test.kt
313+
""".trimIndent(),
314+
CompilerMessageLocation.create("test.kt", 1, 7, null),
315+
),
316+
),
317+
config = IrValidatorConfig(
318+
checkTreeConsistency = false,
319+
checkTypes = true,
320+
)
321+
)
322+
}
323+
269324
@Test
270325
fun `no infinite recursion if there are cycles in IR`() {
271326
val klass = IrFactoryImpl.buildClass {

0 commit comments

Comments
 (0)