[JS IR BE] Enable IrValidator

This commit is contained in:
Svyatoslav Kuzmich
2018-11-19 18:33:05 +03:00
parent 2172a12df4
commit 39cdee8d6c
4 changed files with 86 additions and 16 deletions
@@ -20,22 +20,31 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrDynamicType
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.isAnnotationClass
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlin.types.KotlinType
typealias ReportError = (element: IrElement, message: String) -> Unit
class CheckIrElementVisitor(val builtIns: KotlinBuiltIns, val reportError: ReportError, val ensureAllNodesAreDifferent: Boolean) : IrElementVisitorVoid {
class CheckIrElementVisitor(
val builtIns: KotlinBuiltIns,
val reportError: ReportError,
val config: IrValidatorConfig
) : IrElementVisitorVoid {
val set = mutableSetOf<IrElement>()
override fun visitElement(element: IrElement) {
if (ensureAllNodesAreDifferent) {
if (config.ensureAllNodesAreDifferent) {
if (set.contains(element))
reportError(element, "Duplicate IR node")
set.add(element)
@@ -44,6 +53,9 @@ class CheckIrElementVisitor(val builtIns: KotlinBuiltIns, val reportError: Repor
}
private fun IrExpression.ensureTypeIs(expectedType: KotlinType) {
if (!config.checkTypes)
return
// TODO: compare IR types instead.
if (expectedType != type.toKotlinType()) {
reportError(this, "unexpected expression.type: expected $expectedType, got ${type.toKotlinType()}")
@@ -51,7 +63,7 @@ class CheckIrElementVisitor(val builtIns: KotlinBuiltIns, val reportError: Repor
}
private fun IrSymbol.ensureBound(expression: IrExpression) {
if (!this.isBound) {
if (!this.isBound && expression.type !is IrDynamicType) {
reportError(expression, "Unbound symbol ${this}")
}
}
@@ -163,8 +175,8 @@ class CheckIrElementVisitor(val builtIns: KotlinBuiltIns, val reportError: Repor
IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF -> builtIns.booleanType
}
if (operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT && typeOperand != builtIns.unitType) {
reportError(expression, "typeOperand is $typeOperand")
if (operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT && !typeOperand.isUnit()) {
reportError(expression, "typeOperand is ${typeOperand.render()}")
}
// TODO: check IMPLICIT_NOTNULL's argument type.
@@ -200,7 +212,7 @@ class CheckIrElementVisitor(val builtIns: KotlinBuiltIns, val reportError: Repor
override fun visitClass(declaration: IrClass) {
super.visitClass(declaration)
if (!declaration.isAnnotationClass) {
if (config.checkDescriptors && !declaration.isAnnotationClass) {
// Check that all functions and properties from memberScope are present in IR
// (including FAKE_OVERRIDE ones).
@@ -218,9 +230,36 @@ class CheckIrElementVisitor(val builtIns: KotlinBuiltIns, val reportError: Repor
}
}
override fun visitFunction(declaration: IrFunction) {
super.visitFunction(declaration)
for ((i, p) in declaration.valueParameters.withIndex()) {
if (p.index != i) {
reportError(declaration, "Inconsistent index of value parameter ${p.index} != $i")
}
}
for ((i, p) in declaration.typeParameters.withIndex()) {
if (p.index != i) {
reportError(declaration, "Inconsistent index of type parameter ${p.index} != $i")
}
}
}
override fun visitDeclarationReference(expression: IrDeclarationReference) {
super.visitDeclarationReference(expression)
// TODO: Fix unbound external declarations
if (expression.descriptor.isEffectivelyExternal())
return
// TODO: Fix unbound dynamic filed declarations
if (expression is IrFieldAccessExpression) {
val receiverType = expression.receiver?.type
if (receiverType is IrDynamicType)
return
}
expression.symbol.ensureBound(expression)
}
@@ -24,18 +24,21 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
fun validateIrFile(context: CommonBackendContext, irFile: IrFile) {
val visitor = IrValidator(context, false)
val visitor = IrValidator(context, IrValidatorConfig(abortOnError = false, ensureAllNodesAreDifferent = false))
irFile.acceptVoid(visitor)
}
fun validateIrModule(context: CommonBackendContext, irModule: IrModuleFragment) {
val visitor = IrValidator(context, true) // TODO: consider taking the boolean from settings.
val visitor = IrValidator(
context,
IrValidatorConfig(abortOnError = false, ensureAllNodesAreDifferent = true)
) // TODO: consider taking the boolean from settings.
irModule.acceptVoid(visitor)
// TODO: also check that all referenced symbol targets are reachable.
}
private fun CommonBackendContext.reportIrValidationError(message: String, irFile: IrFile, irElement: IrElement) {
private fun CommonBackendContext.reportIrValidationError(message: String, irFile: IrFile?, irElement: IrElement) {
try {
this.reportWarning("[IR VALIDATION] $message", irFile, irElement)
} catch (e: Throwable) {
@@ -45,10 +48,17 @@ private fun CommonBackendContext.reportIrValidationError(message: String, irFile
// TODO: throw an exception after fixing bugs leading to invalid IR.
}
private class IrValidator(val context: CommonBackendContext, performHeavyValidations: Boolean) : IrElementVisitorVoid {
data class IrValidatorConfig(
val abortOnError: Boolean,
val ensureAllNodesAreDifferent: Boolean,
val checkTypes: Boolean = true,
val checkDescriptors: Boolean = true
)
class IrValidator(val context: CommonBackendContext, val config: IrValidatorConfig) : IrElementVisitorVoid {
val builtIns = context.builtIns
lateinit var currentFile: IrFile
var currentFile: IrFile? = null
override fun visitFile(declaration: IrFile) {
currentFile = declaration
@@ -58,12 +68,17 @@ private class IrValidator(val context: CommonBackendContext, performHeavyValidat
private fun error(element: IrElement, message: String) {
// TODO: render all element's parents.
context.reportIrValidationError(
"$message\n" +
element.render(),
currentFile, element)
"$message\n" + element.render(),
currentFile,
element
)
if (config.abortOnError) {
error("Validation failed")
}
}
private val elementChecker = CheckIrElementVisitor(builtIns, this::error, performHeavyValidations)
private val elementChecker = CheckIrElementVisitor(builtIns, this::error, config)
override fun visitElement(element: IrElement) {
element.acceptVoid(elementChecker)
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
fun CommonBackendContext.reportWarning(message: String, irFile: IrFile, irElement: IrElement) {
fun CommonBackendContext.reportWarning(message: String, irFile: IrFile?, irElement: IrElement) {
report(irElement, irFile, message, false)
}
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
@@ -101,6 +102,11 @@ private fun JsIrBackendContext.performInlining(moduleFragment: IrModuleFragment)
}
private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependencies: List<IrModuleFragment>) {
val validateIr = {
val visitor = IrValidator(this, validatorConfig)
moduleFragment.acceptVoid(visitor)
}
validateIr()
ThrowableSuccessorsLowering(this).lower(moduleFragment)
TailrecLowering(this).runOnFilesPostfix(moduleFragment)
UnitMaterializationLowering(this).lower(moduleFragment)
@@ -113,6 +119,7 @@ private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependenc
LocalDeclarationsLowering(this).runOnFilesPostfix(moduleFragment)
InnerClassesLowering(this).runOnFilesPostfix(moduleFragment)
InnerClassConstructorCallsLowering(this).runOnFilesPostfix(moduleFragment)
validateIr()
SuspendFunctionsLowering(this).lower(moduleFragment)
CallableReferenceLowering(this).lower(moduleFragment)
DefaultArgumentStubGenerator(this).runOnFilesPostfix(moduleFragment)
@@ -134,15 +141,24 @@ private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependenc
inlineClassDeclarationLowering.runOnFilesPostfix(moduleFragment)
inlineClassUsageLowering.lower(moduleFragment)
}
validateIr()
AutoboxingTransformer(this).lower(moduleFragment)
BlockDecomposerLowering(this).runOnFilesPostfix(moduleFragment)
ClassReferenceLowering(this).lower(moduleFragment)
PrimitiveCompanionLowering(this).lower(moduleFragment)
ConstLowering(this).lower(moduleFragment)
validateIr()
CallsLowering(this).lower(moduleFragment)
}
val validatorConfig = IrValidatorConfig(
abortOnError = true,
ensureAllNodesAreDifferent = true,
checkTypes = false,
checkDescriptors = false
)
private fun FileLoweringPass.lower(moduleFragment: IrModuleFragment) = moduleFragment.files.forEach { lower(it) }
private fun DeclarationContainerLoweringPass.runOnFilesPostfix(moduleFragment: IrModuleFragment) =