From 39cdee8d6c4289fbb53c9ede5c4186ff4e81dd3b Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Mon, 19 Nov 2018 18:33:05 +0300 Subject: [PATCH] [JS IR BE] Enable IrValidator --- .../backend/common/CheckIrElementVisitor.kt | 51 ++++++++++++++++--- .../kotlin/backend/common/IrValidator.kt | 33 ++++++++---- .../jetbrains/kotlin/backend/common/Utils.kt | 2 +- .../kotlin/ir/backend/js/compiler.kt | 16 ++++++ 4 files changed, 86 insertions(+), 16 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt index 61d24c6ee4e..978e7a41c36 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt @@ -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() 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) } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/IrValidator.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/IrValidator.kt index 6457f235a87..7ad695c1632 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/IrValidator.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/IrValidator.kt @@ -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) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Utils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Utils.kt index 1cec73c0758..6fdb84152fc 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Utils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Utils.kt @@ -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) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index a7c8f215763..6e2d405f838 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -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) { + 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) =