From 321a476898f30f4b3c03749cdc05a22de03137e7 Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Tue, 21 Feb 2023 21:50:36 +0100 Subject: [PATCH] [Wasm] Add external inheritance checker. Prohibit non-external types to extend external types. This type of inheritance is not supported in Wasm backend. --- .../wasmTests/jsInterop/inheritance.kt | 22 +++++ .../wasmTests/jsInterop/inheritance.txt | 92 +++++++++++++++++++ .../diagnostics/DefaultErrorMessagesWasm.kt | 24 +++++ .../wasm/resolve/diagnostics/ErrorsWasm.java | 28 ++++++ .../WasmExternalInheritanceChecker.kt | 35 +++++++ .../DiagnosticsWasmTestGenerated.java | 42 +++++++++ 6 files changed, 243 insertions(+) create mode 100644 compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.kt create mode 100644 compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.txt create mode 100644 wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/DefaultErrorMessagesWasm.kt create mode 100644 wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/ErrorsWasm.java create mode 100644 wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/WasmExternalInheritanceChecker.kt create mode 100644 wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsWasmTestGenerated.java diff --git a/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.kt b/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.kt new file mode 100644 index 00000000000..e565171e2ea --- /dev/null +++ b/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.kt @@ -0,0 +1,22 @@ +open class C1 + +interface I1 + +external open class EC1 + +external class EC2 : C1 + +external class EC3 : I1, C1 + +external interface EI1 : I1 + +interface I2 : EI1 + +class C3 : EI1 + +class C4 : EI1, EC1() + +object O1 : EC1() + +val x1: Any = object : EI1 {} +val x2: Any = object : EC1() {} diff --git a/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.txt b/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.txt new file mode 100644 index 00000000000..f15e07c82b7 --- /dev/null +++ b/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.txt @@ -0,0 +1,92 @@ +package + +public val x1: kotlin.Any +public val x2: kotlin.Any + +public open class C1 { + public constructor C1() + invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class C3 : EI1 { + public constructor C3() + invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class C4 : EI1, EC1 { + public constructor C4() + invisible_fake final override /*2*/ /*fake_override*/ var _hashCode: kotlin.Int + invisible_fake final override /*2*/ /*fake_override*/ var typeInfo: kotlin.Int + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open external class EC1 { + public constructor EC1() + invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final external class EC2 : C1 { + public constructor EC2() + invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final external class EC3 : I1, C1 { + public constructor EC3() + invisible_fake final override /*2*/ /*fake_override*/ var _hashCode: kotlin.Int + invisible_fake final override /*2*/ /*fake_override*/ var typeInfo: kotlin.Int + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public external interface EI1 : I1 { + invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface I1 { + invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface I2 : EI1 { + invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public object O1 : EC1 { + private constructor O1() + invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + diff --git a/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/DefaultErrorMessagesWasm.kt b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/DefaultErrorMessagesWasm.kt new file mode 100644 index 00000000000..3aa48526473 --- /dev/null +++ b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/DefaultErrorMessagesWasm.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.wasm.resolve.diagnostics + +import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages +import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap +import org.jetbrains.kotlin.diagnostics.rendering.Renderers + +private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { + DiagnosticFactoryToRendererMap("Wasm").apply { + put( + ErrorsWasm.NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE, + "Non-external type extends external type {0}", + Renderers.RENDER_TYPE + ) + } +} + +class DefaultErrorMessagesWasm : DefaultErrorMessages.Extension { + override fun getMap(): DiagnosticFactoryToRendererMap = DIAGNOSTIC_FACTORY_TO_RENDERER +} \ No newline at end of file diff --git a/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/ErrorsWasm.java b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/ErrorsWasm.java new file mode 100644 index 00000000000..a417eaa76ba --- /dev/null +++ b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/ErrorsWasm.java @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.wasm.resolve.diagnostics; + +import com.intellij.psi.PsiElement; +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0; +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1; +import org.jetbrains.kotlin.diagnostics.Errors; +import org.jetbrains.kotlin.diagnostics.PositioningStrategies; +import org.jetbrains.kotlin.psi.KtElement; +import org.jetbrains.kotlin.types.KotlinType; + +import static org.jetbrains.kotlin.diagnostics.Severity.ERROR; + +public interface ErrorsWasm { + DiagnosticFactory1 NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE = + DiagnosticFactory1.create(ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT); + + @SuppressWarnings("UnusedDeclaration") + Object _initializer = new Object() { + { + Errors.Initializer.initializeFactoryNames(ErrorsWasm.class); + } + }; +} diff --git a/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/WasmExternalInheritanceChecker.kt b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/WasmExternalInheritanceChecker.kt new file mode 100644 index 00000000000..84d3d1a8719 --- /dev/null +++ b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/WasmExternalInheritanceChecker.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.wasm.resolve.diagnostics + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker +import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces +import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal + +// TODO: Implement in K2 +object WasmExternalInheritanceChecker : DeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + if (descriptor is ClassDescriptor && !descriptor.isEffectivelyExternal()) { + val superClasses = listOfNotNull(descriptor.getSuperClassNotAny()) + descriptor.getSuperInterfaces() + for (superClass in superClasses) { + if (superClass.isEffectivelyExternal()) { + context.trace.report( + ErrorsWasm.NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE.on( + declaration as KtClassOrObject, + superClass.defaultType + ) + ) + } + } + } + } +} diff --git a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsWasmTestGenerated.java b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsWasmTestGenerated.java new file mode 100644 index 00000000000..0e9cdb53e2f --- /dev/null +++ b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsWasmTestGenerated.java @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.wasm.test.diagnostics; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateWasmTestsKt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/diagnostics/wasmTests") +@TestDataPath("$PROJECT_ROOT") +public class DiagnosticsWasmTestGenerated extends AbstractDiagnosticsWasmTest { + @Test + public void testAllFilesPresentInWasmTests() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/wasmTests"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @Nested + @TestMetadata("compiler/testData/diagnostics/wasmTests/jsInterop") + @TestDataPath("$PROJECT_ROOT") + public class JsInterop { + @Test + public void testAllFilesPresentInJsInterop() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/wasmTests/jsInterop"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @Test + @TestMetadata("inheritance.kt") + public void testInheritance() throws Exception { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.kt"); + } + } +}