IR refactor patch / validate declaration parent

This commit is contained in:
Dmitry Petrov
2022-01-28 12:51:37 +03:00
committed by Space
parent 08a946fd3f
commit 5bc8c1a600
4 changed files with 65 additions and 116 deletions
@@ -18,17 +18,20 @@ package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.DeclarationParentsVisitor
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid
@Suppress("unused")
fun validateIrFile(context: CommonBackendContext, irFile: IrFile) { fun validateIrFile(context: CommonBackendContext, irFile: IrFile) {
val visitor = IrValidator(context, IrValidatorConfig(abortOnError = false, ensureAllNodesAreDifferent = false)) val visitor = IrValidator(context, IrValidatorConfig(abortOnError = false, ensureAllNodesAreDifferent = false))
irFile.acceptVoid(visitor) irFile.acceptVoid(visitor)
} }
@Suppress("unused")
fun validateIrModule(context: CommonBackendContext, irModule: IrModuleFragment) { fun validateIrModule(context: CommonBackendContext, irModule: IrModuleFragment) {
val visitor = IrValidator( val visitor = IrValidator(
context, context,
@@ -92,31 +95,50 @@ class IrValidator(val context: CommonBackendContext, val config: IrValidatorConf
} }
} }
fun IrModuleFragment.checkDeclarationParents() { fun IrElement.checkDeclarationParents() {
this.accept(CheckDeclarationParentsVisitor, null) val checker = CheckDeclarationParentsVisitor()
} acceptVoid(checker)
if (checker.errors.isNotEmpty()) {
object CheckDeclarationParentsVisitor : IrElementVisitor<Unit, IrDeclarationParent?> { val expectedParents = LinkedHashSet<IrDeclarationParent>()
throw AssertionError(
override fun visitElement(element: IrElement, data: IrDeclarationParent?) { buildString {
element.acceptChildren(this, element as? IrDeclarationParent ?: data) append("Declarations with wrong parent: ")
} append(checker.errors.size)
append("\n")
override fun visitDeclaration(declaration: IrDeclarationBase, data: IrDeclarationParent?) { checker.errors.forEach {
checkParent(declaration, data) append("declaration: ")
super.visitDeclaration(declaration, data) append(it.declaration.render())
} append("\n\t")
append(it.declaration)
private fun checkParent(declaration: IrDeclaration, expectedParent: IrDeclarationParent?) { append("\nexpectedParent: ")
val parent = try { append(it.expectedParent.render())
declaration.parent append("\nactualParent: ")
} catch (e: Throwable) { append(it.actualParent?.render())
error("$declaration for ${declaration.render()} has no parent") append("\n")
} expectedParents.add(it.expectedParent)
}
if (parent != expectedParent) { append("\nExpected parents:\n")
error("$declaration for ${declaration.render()} has unexpected parent $parent") expectedParents.forEach {
} append(it.dump())
}
}
)
} }
} }
class CheckDeclarationParentsVisitor : DeclarationParentsVisitor() {
class Error(val declaration: IrDeclaration, val expectedParent: IrDeclarationParent, val actualParent: IrDeclarationParent?)
val errors = ArrayList<Error>()
override fun handleParent(declaration: IrDeclaration, parent: IrDeclarationParent) {
try {
val actualParent = declaration.parent
if (actualParent != parent) {
errors.add(Error(declaration, parent, actualParent))
}
} catch (e: Exception) {
errors.add(Error(declaration, parent, null))
}
}
}
@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.backend.common.phaser package org.jetbrains.kotlin.backend.common.phaser
import org.jetbrains.kotlin.backend.common.CheckDeclarationParentsVisitor import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.IrValidator
import org.jetbrains.kotlin.backend.common.IrValidatorConfig
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.util.dump
@@ -60,7 +57,7 @@ fun <Data, Context> makeVerifyAction(verifier: (Context, Data) -> Unit): Action<
fun dumpIrElement(actionState: ActionState, data: IrElement, @Suppress("UNUSED_PARAMETER") context: Any?): String { fun dumpIrElement(actionState: ActionState, data: IrElement, @Suppress("UNUSED_PARAMETER") context: Any?): String {
val beforeOrAfterStr = actionState.beforeOrAfter.name.toLowerCaseAsciiOnly() val beforeOrAfterStr = actionState.beforeOrAfter.name.toLowerCaseAsciiOnly()
var dumpText: String = "" var dumpText = ""
val elementName: String val elementName: String
val dumpStrategy = System.getProperty("org.jetbrains.kotlin.compiler.ir.dump.strategy") val dumpStrategy = System.getProperty("org.jetbrains.kotlin.compiler.ir.dump.strategy")
@@ -143,7 +140,7 @@ fun <Fragment : IrElement> validationCallback(context: CommonBackendContext, fra
checkProperties = checkProperties, checkProperties = checkProperties,
) )
fragment.accept(IrValidator(context, validatorConfig), null) fragment.accept(IrValidator(context, validatorConfig), null)
fragment.accept(CheckDeclarationParentsVisitor, null) fragment.checkDeclarationParents()
} }
val validationAction = makeVerifyAction(::validationCallback) val validationAction = makeVerifyAction(::validationCallback)
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.checkDeclarationParents
import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
import org.jetbrains.kotlin.backend.common.lower.optimizations.foldConstantLoweringPhase import org.jetbrains.kotlin.backend.common.lower.optimizations.foldConstantLoweringPhase
@@ -19,7 +20,9 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.PatchDeclarationParentsVisitor
import org.jetbrains.kotlin.ir.util.isAnonymousObject
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid
@@ -28,6 +31,7 @@ import org.jetbrains.kotlin.name.NameUtils
private var patchParentPhases = 0 private var patchParentPhases = 0
@Suppress("unused")
private fun makePatchParentsPhase(): NamedCompilerPhase<CommonBackendContext, IrFile> { private fun makePatchParentsPhase(): NamedCompilerPhase<CommonBackendContext, IrFile> {
val number = patchParentPhases++ val number = patchParentPhases++
return makeIrFilePhase( return makeIrFilePhase(
@@ -39,6 +43,7 @@ private fun makePatchParentsPhase(): NamedCompilerPhase<CommonBackendContext, Ir
private var checkParentPhases = 0 private var checkParentPhases = 0
@Suppress("unused")
private fun makeCheckParentsPhase(): NamedCompilerPhase<CommonBackendContext, IrFile> { private fun makeCheckParentsPhase(): NamedCompilerPhase<CommonBackendContext, IrFile> {
val number = checkParentPhases++ val number = checkParentPhases++
return makeIrFilePhase( return makeIrFilePhase(
@@ -56,34 +61,7 @@ private class PatchDeclarationParents : FileLoweringPass {
private class CheckDeclarationParents : FileLoweringPass { private class CheckDeclarationParents : FileLoweringPass {
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
val checker = CheckDeclarationParentsVisitor() irFile.checkDeclarationParents()
irFile.acceptVoid(checker)
if (checker.errors.isNotEmpty()) {
val expectedParents = LinkedHashSet<IrDeclarationParent>()
throw AssertionError(
buildString {
append("Declarations with wrong parent: ")
append(checker.errors.size)
append("\n")
checker.errors.forEach {
append("declaration: ")
append(it.declaration.render())
append("\n\t")
append(it.declaration)
append("\nexpectedParent: ")
append(it.expectedParent.render())
append("\nactualParent: ")
append(it.actualParent?.render())
append("\n")
expectedParents.add(it.expectedParent)
}
append("\nExpected parents:\n")
expectedParents.forEach {
append(it.dump())
}
}
)
}
} }
} }
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid
import java.util.* import java.util.*
import kotlin.collections.ArrayList
fun <T : IrElement> T.patchDeclarationParents(initialParent: IrDeclarationParent? = null) = fun <T : IrElement> T.patchDeclarationParents(initialParent: IrDeclarationParent? = null) =
apply { apply {
@@ -19,13 +18,8 @@ fun <T : IrElement> T.patchDeclarationParents(initialParent: IrDeclarationParent
acceptVoid(visitor) acceptVoid(visitor)
} }
class PatchDeclarationParentsVisitor() : IrElementVisitorVoid { abstract class DeclarationParentsVisitor : IrElementVisitorVoid {
protected val declarationParentsStack = ArrayDeque<IrDeclarationParent>()
constructor(containingDeclaration: IrDeclarationParent) : this() {
declarationParentsStack.push(containingDeclaration)
}
private val declarationParentsStack = ArrayDeque<IrDeclarationParent>()
override fun visitElement(element: IrElement) { override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this) element.acceptChildrenVoid(this)
@@ -38,7 +32,7 @@ class PatchDeclarationParentsVisitor() : IrElementVisitorVoid {
} }
override fun visitDeclaration(declaration: IrDeclarationBase) { override fun visitDeclaration(declaration: IrDeclarationBase) {
patchParent(declaration) handleParent(declaration, declarationParentsStack.peekFirst())
if (declaration is IrDeclarationParent) { if (declaration is IrDeclarationParent) {
declarationParentsStack.push(declaration) declarationParentsStack.push(declaration)
@@ -51,58 +45,16 @@ class PatchDeclarationParentsVisitor() : IrElementVisitorVoid {
} }
} }
private fun patchParent(declaration: IrDeclaration) { protected abstract fun handleParent(declaration: IrDeclaration, parent: IrDeclarationParent)
declaration.parent = declarationParentsStack.peekFirst()
}
} }
class PatchDeclarationParentsVisitor() : DeclarationParentsVisitor() {
class CheckDeclarationParentsVisitor() : IrElementVisitorVoid {
constructor(containingDeclaration: IrDeclarationParent) : this() { constructor(containingDeclaration: IrDeclarationParent) : this() {
declarationParentsStack.push(containingDeclaration) declarationParentsStack.push(containingDeclaration)
} }
private val declarationParentsStack = ArrayDeque<IrDeclarationParent>() override fun handleParent(declaration: IrDeclaration, parent: IrDeclarationParent) {
declaration.parent = parent
class Data(val declaration: IrDeclaration, val expectedParent: IrDeclarationParent, val actualParent: IrDeclarationParent?)
val errors = ArrayList<Data>()
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitPackageFragment(declaration: IrPackageFragment) {
declarationParentsStack.push(declaration)
super.visitPackageFragment(declaration)
declarationParentsStack.pop()
}
override fun visitDeclaration(declaration: IrDeclarationBase) {
checkParent(declaration)
if (declaration is IrDeclarationParent) {
declarationParentsStack.push(declaration)
}
super.visitDeclaration(declaration)
if (declaration is IrDeclarationParent) {
declarationParentsStack.pop()
}
}
private fun checkParent(declaration: IrDeclaration) {
val expectedParent = declarationParentsStack.peekFirst()
try {
val actualParent = declaration.parent
if (actualParent != expectedParent) {
errors.add(Data(declaration, expectedParent, actualParent))
}
} catch (e: Exception) {
errors.add(Data(declaration, expectedParent, null))
}
} }
} }