FIR tree gen: refactor interface/class solver code
To be able to reuse it in the upcoming IR tree generator.
This commit is contained in:
+71
-51
@@ -22,46 +22,67 @@ import org.jetbrains.kotlin.fir.tree.generator.model.KindOwner
|
|||||||
* if `P1` is a class then `P2` can not be a class (because both of them a parents of E`
|
* if `P1` is a class then `P2` can not be a class (because both of them a parents of E`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
interface Node {
|
||||||
|
val parents: List<Node>
|
||||||
|
val origin: Node
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NodeImpl(val element: KindOwner) : Node {
|
||||||
|
override val parents: List<Node>
|
||||||
|
get() = element.allParents.map(::NodeImpl)
|
||||||
|
|
||||||
|
override val origin: NodeImpl
|
||||||
|
get() = if (element.origin == element) this else NodeImpl(element.origin)
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean =
|
||||||
|
other is NodeImpl && element == other.element
|
||||||
|
|
||||||
|
override fun hashCode(): Int =
|
||||||
|
element.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
fun configureInterfacesAndAbstractClasses(builder: AbstractFirTreeBuilder) {
|
fun configureInterfacesAndAbstractClasses(builder: AbstractFirTreeBuilder) {
|
||||||
val elements = collectElements(builder)
|
val elements = collectElements(builder)
|
||||||
val elementMapping = ElementMapping(elements)
|
val solution = solveGraphForClassVsInterface(
|
||||||
|
elements,
|
||||||
val solution = solve2sat(elements, elementMapping)
|
elements.filter { it.element.kind?.isInterface == true },
|
||||||
processRequirementsFromConfig(solution, elementMapping)
|
elements.filter { it.element.kind?.isInterface == false },
|
||||||
updateKinds(solution, elementMapping)
|
)
|
||||||
|
updateKinds(elements, solution)
|
||||||
updateSealedKinds(elements)
|
updateSealedKinds(elements)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ElementMapping(val elements: Collection<KindOwner>) {
|
fun solveGraphForClassVsInterface(
|
||||||
private val varToElements: Map<Int, KindOwner> = elements.mapIndexed { index, element -> 2 * index to element.origin }.toMap() +
|
elements: List<Node>, requiredInterfaces: Collection<Node>, requiredClasses: Collection<Node>,
|
||||||
|
): List<Boolean> {
|
||||||
|
val elementMapping = ElementMapping(elements)
|
||||||
|
val solution = solve2sat(elements, elementMapping)
|
||||||
|
processRequirementsFromConfig(solution, elementMapping, requiredInterfaces, requiredClasses)
|
||||||
|
return solution
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ElementMapping(val elements: Collection<Node>) {
|
||||||
|
private val varToElements: Map<Int, Node> = elements.mapIndexed { index, element -> 2 * index to element.origin }.toMap() +
|
||||||
elements.mapIndexed { index, element -> 2 * index + 1 to element }.toMap()
|
elements.mapIndexed { index, element -> 2 * index + 1 to element }.toMap()
|
||||||
private val elementsToVar: Map<KindOwner, Int> = elements.mapIndexed { index, element -> element.origin to index }.toMap()
|
private val elementsToVar: Map<Node, Int> = elements.mapIndexed { index, element -> element.origin to index }.toMap()
|
||||||
private val hasInheritors = elements.map { it to false }.toMap(mutableMapOf()).also {
|
|
||||||
for (element in elements) {
|
|
||||||
for (parent in element.allParents) {
|
|
||||||
it[parent.origin] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(element: KindOwner): Int = elementsToVar.getValue(element)
|
operator fun get(element: Node): Int = elementsToVar.getValue(element)
|
||||||
operator fun get(index: Int): KindOwner = varToElements.getValue(index)
|
operator fun get(index: Int): Node = varToElements.getValue(index)
|
||||||
|
|
||||||
fun hasInheritors(element: KindOwner): Boolean {
|
|
||||||
return hasInheritors[element]!!
|
|
||||||
}
|
|
||||||
|
|
||||||
val size: Int = elements.size
|
val size: Int = elements.size
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectElements(builder: AbstractFirTreeBuilder): List<KindOwner> {
|
private fun collectElements(builder: AbstractFirTreeBuilder): List<NodeImpl> {
|
||||||
return (builder.elements + builder.elements.flatMap { it.allImplementations }).map { it.origin }
|
return (builder.elements + builder.elements.flatMap { it.allImplementations }).map { NodeImpl(it.origin) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateKinds(solution: List<Boolean>, elementMapping: ElementMapping) {
|
private fun updateKinds(nodes: List<NodeImpl>, solution: List<Boolean>) {
|
||||||
|
val allParents = nodes.flatMapTo(mutableSetOf()) { element -> element.parents.map { it.origin } }
|
||||||
|
|
||||||
for (index in solution.indices) {
|
for (index in solution.indices) {
|
||||||
val isClass = solution[index]
|
val isClass = solution[index]
|
||||||
val element = elementMapping[index * 2].origin
|
val node = nodes[index].origin
|
||||||
|
val element = node.element
|
||||||
val existingKind = element.kind
|
val existingKind = element.kind
|
||||||
if (isClass) {
|
if (isClass) {
|
||||||
if (existingKind == Implementation.Kind.Interface)
|
if (existingKind == Implementation.Kind.Interface)
|
||||||
@@ -70,7 +91,7 @@ private fun updateKinds(solution: List<Boolean>, elementMapping: ElementMapping)
|
|||||||
if (existingKind == null) {
|
if (existingKind == null) {
|
||||||
element.kind = when (element) {
|
element.kind = when (element) {
|
||||||
is Implementation -> {
|
is Implementation -> {
|
||||||
if (elementMapping.hasInheritors(element))
|
if (node in allParents)
|
||||||
Implementation.Kind.AbstractClass
|
Implementation.Kind.AbstractClass
|
||||||
else
|
else
|
||||||
Implementation.Kind.FinalClass
|
Implementation.Kind.FinalClass
|
||||||
@@ -85,8 +106,9 @@ private fun updateKinds(solution: List<Boolean>, elementMapping: ElementMapping)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSealedKinds(elements: Collection<KindOwner>) {
|
private fun updateSealedKinds(nodes: Collection<NodeImpl>) {
|
||||||
for (element in elements) {
|
for (node in nodes) {
|
||||||
|
val element = node.element
|
||||||
if (element is Element) {
|
if (element is Element) {
|
||||||
if (element.isSealed) {
|
if (element.isSealed) {
|
||||||
element.kind = when (element.kind) {
|
element.kind = when (element.kind) {
|
||||||
@@ -99,17 +121,22 @@ private fun updateSealedKinds(elements: Collection<KindOwner>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processRequirementsFromConfig(solution: MutableList<Boolean>, elementMapping: ElementMapping) {
|
private fun processRequirementsFromConfig(
|
||||||
fun forceParentsToBeInterfaces(element: KindOwner) {
|
solution: MutableList<Boolean>,
|
||||||
|
elementMapping: ElementMapping,
|
||||||
|
requiredInterfaces: Collection<Node>,
|
||||||
|
requiredClasses: Collection<Node>,
|
||||||
|
) {
|
||||||
|
fun forceParentsToBeInterfaces(element: Node) {
|
||||||
val origin = element.origin
|
val origin = element.origin
|
||||||
val index = elementMapping[origin]
|
val index = elementMapping[origin]
|
||||||
if (!solution[index]) return
|
if (!solution[index]) return
|
||||||
solution[index] = false
|
solution[index] = false
|
||||||
origin.allParents.forEach { forceParentsToBeInterfaces(it) }
|
origin.parents.forEach { forceParentsToBeInterfaces(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun forceInheritorsToBeClasses(element: KindOwner) {
|
fun forceInheritorsToBeClasses(element: Node) {
|
||||||
val queue = ArrayDeque<KindOwner>()
|
val queue = ArrayDeque<Node>()
|
||||||
queue.add(element)
|
queue.add(element)
|
||||||
while (queue.isNotEmpty()) {
|
while (queue.isNotEmpty()) {
|
||||||
val e = queue.removeFirst().origin
|
val e = queue.removeFirst().origin
|
||||||
@@ -117,25 +144,18 @@ private fun processRequirementsFromConfig(solution: MutableList<Boolean>, elemen
|
|||||||
if (solution[index]) continue
|
if (solution[index]) continue
|
||||||
solution[index] = true
|
solution[index] = true
|
||||||
for (inheritor in elementMapping.elements) {
|
for (inheritor in elementMapping.elements) {
|
||||||
if (e in inheritor.allParents.map { it.origin }) {
|
if (e in inheritor.parents.map { it.origin }) {
|
||||||
queue.add(inheritor)
|
queue.add(inheritor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (index in solution.indices) {
|
requiredInterfaces.forEach(::forceParentsToBeInterfaces)
|
||||||
val element = elementMapping[index * 2]
|
requiredClasses.forEach(::forceInheritorsToBeClasses)
|
||||||
val kind = element.kind ?: continue
|
|
||||||
if (kind.isInterface) {
|
|
||||||
forceParentsToBeInterfaces(element)
|
|
||||||
} else {
|
|
||||||
forceInheritorsToBeClasses(element)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun solve2sat(elements: Collection<KindOwner>, elementsToVar: ElementMapping): MutableList<Boolean> {
|
private fun solve2sat(elements: Collection<Node>, elementsToVar: ElementMapping): MutableList<Boolean> {
|
||||||
val (g, gt) = buildGraphs(elements, elementsToVar)
|
val (g, gt) = buildGraphs(elements, elementsToVar)
|
||||||
|
|
||||||
val used = g.indices.mapTo(mutableListOf()) { false }
|
val used = g.indices.mapTo(mutableListOf()) { false }
|
||||||
@@ -188,27 +208,27 @@ private fun solve2sat(elements: Collection<KindOwner>, elementsToVar: ElementMap
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun buildGraphs(elements: Collection<KindOwner>, elementMapping: ElementMapping): Pair<List<List<Int>>, List<List<Int>>> {
|
private fun buildGraphs(elements: Collection<Node>, elementMapping: ElementMapping): Pair<List<List<Int>>, List<List<Int>>> {
|
||||||
val g = (1..elementMapping.size * 2).map { mutableListOf<Int>() }
|
val g = (1..elementMapping.size * 2).map { mutableListOf<Int>() }
|
||||||
val gt = (1..elementMapping.size * 2).map { mutableListOf<Int>() }
|
val gt = (1..elementMapping.size * 2).map { mutableListOf<Int>() }
|
||||||
|
|
||||||
fun Int.direct(): Int = this
|
fun Int.direct(): Int = this
|
||||||
fun Int.invert(): Int = this + 1
|
fun Int.invert(): Int = this + 1
|
||||||
|
|
||||||
fun extractIndex(element: KindOwner) = elementMapping[element] * 2
|
fun extractIndex(element: Node) = elementMapping[element] * 2
|
||||||
|
|
||||||
for (element in elements) {
|
for (element in elements) {
|
||||||
val elementVar = extractIndex(element)
|
val elementVar = extractIndex(element)
|
||||||
for (parent in element.allParents) {
|
for (parent in element.parents) {
|
||||||
val parentVar = extractIndex(parent.origin)
|
val parentVar = extractIndex(parent.origin)
|
||||||
// parent -> element
|
// parent -> element
|
||||||
g[parentVar.direct()] += elementVar.direct()
|
g[parentVar.direct()] += elementVar.direct()
|
||||||
g[elementVar.invert()] += parentVar.invert()
|
g[elementVar.invert()] += parentVar.invert()
|
||||||
}
|
}
|
||||||
for (i in 0 until element.allParents.size) {
|
for (i in 0 until element.parents.size) {
|
||||||
for (j in i + 1 until element.allParents.size) {
|
for (j in i + 1 until element.parents.size) {
|
||||||
val firstParentVar = extractIndex(element.allParents[i].origin)
|
val firstParentVar = extractIndex(element.parents[i].origin)
|
||||||
val secondParentVar = extractIndex(element.allParents[j].origin)
|
val secondParentVar = extractIndex(element.parents[j].origin)
|
||||||
// firstParent -> !secondParent
|
// firstParent -> !secondParent
|
||||||
g[firstParentVar.direct()] += secondParentVar.invert()
|
g[firstParentVar.direct()] += secondParentVar.invert()
|
||||||
g[secondParentVar.direct()] += firstParentVar.invert()
|
g[secondParentVar.direct()] += firstParentVar.invert()
|
||||||
|
|||||||
Reference in New Issue
Block a user