diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirWasmDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirWasmDiagnosticsList.kt index 8a04b7194ee..fd22818110c 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirWasmDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirWasmDiagnosticsList.kt @@ -70,4 +70,8 @@ object WASM_DIAGNOSTICS_LIST : DiagnosticList("FirWasmErrors") { val WASI_EXTERNAL_NOT_TOP_LEVEL_FUNCTION by error() val WASI_EXTERNAL_FUNCTION_WITHOUT_IMPORT by error() } + + val ASSOCIATED_OBJECTS by object : DiagnosticGroup("Associated object") { + val ASSOCIATED_OBJECT_INVALID_BINDING by error() + } } diff --git a/compiler/fir/checkers/checkers.wasm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrors.kt b/compiler/fir/checkers/checkers.wasm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrors.kt index 18dceea14f4..30ce627d261 100644 --- a/compiler/fir/checkers/checkers.wasm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrors.kt +++ b/compiler/fir/checkers/checkers.wasm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrors.kt @@ -55,6 +55,9 @@ object FirWasmErrors { val WASI_EXTERNAL_NOT_TOP_LEVEL_FUNCTION: KtDiagnosticFactory0 by error0() val WASI_EXTERNAL_FUNCTION_WITHOUT_IMPORT: KtDiagnosticFactory0 by error0() + // Associated object + val ASSOCIATED_OBJECT_INVALID_BINDING: KtDiagnosticFactory0 by error0() + init { RootDiagnosticRendererFactory.registerFactory(FirWasmErrorsDefaultMessages) } diff --git a/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrorsDefaultMessages.kt index f41a6d09cd2..d708731848c 100644 --- a/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrorsDefaultMessages.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap import org.jetbrains.kotlin.diagnostics.KtDiagnosticRenderers.TO_STRING import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers +import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.ASSOCIATED_OBJECT_INVALID_BINDING import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.JSCODE_INVALID_PARAMETER_NAME @@ -42,6 +43,7 @@ object FirWasmErrorsDefaultMessages : BaseDiagnosticRendererFactory() { NESTED_JS_MODULE_PROHIBITED, "'@JsModule' cannot appear here since the file is already marked by either '@JsModule'." ) + map.put( NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE, "Non-external type extends external type ''{0}''", @@ -100,5 +102,7 @@ object FirWasmErrorsDefaultMessages : BaseDiagnosticRendererFactory() { map.put(WASI_EXTERNAL_NOT_TOP_LEVEL_FUNCTION, "Only top-level functions can be external.") map.put(WASI_EXTERNAL_FUNCTION_WITHOUT_IMPORT, "External functions should be annotated with '@WasmImport'.") + + map.put(ASSOCIATED_OBJECT_INVALID_BINDING, "Invalid associated object binding.") } } diff --git a/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/WasmDeclarationCheckers.kt b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/WasmDeclarationCheckers.kt index dc279539b70..13c5de252b0 100644 --- a/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/WasmDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/WasmDeclarationCheckers.kt @@ -34,6 +34,7 @@ object WasmJsDeclarationCheckers : DeclarationCheckers() { FirJsExportAnnotationChecker, FirWasmJsModuleChecker, FirWasmExternalFileChecker, + FirWasmJsAssociatedObjectChecker, ) } diff --git a/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/declaration/FirWasmJsAssociatedObjectChecker.kt b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/declaration/FirWasmJsAssociatedObjectChecker.kt new file mode 100644 index 00000000000..b480fb51c4a --- /dev/null +++ b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/declaration/FirWasmJsAssociatedObjectChecker.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2024 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.fir.analysis.wasm.checkers.declaration + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.ASSOCIATED_OBJECT_INVALID_BINDING +import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.hasAnnotation +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.toSymbol +import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.fir.declarations.utils.isEffectivelyExternal + +object FirWasmJsAssociatedObjectChecker : FirBasicDeclarationChecker(MppCheckerKind.Common) { + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration !is FirClass) return + if (!declaration.symbol.isEffectivelyExternal(context.session)) return + + for (annotationCall in declaration.annotations) { + val annotations = annotationCall.annotationTypeRef.coneType.toSymbol(context.session)?.annotations ?: continue + if (annotations.hasAnnotation(StandardClassIds.Annotations.AssociatedObjectKey, context.session)) { + reporter.reportOn(annotationCall.source, ASSOCIATED_OBJECT_INVALID_BINDING, context) + } + } + } +} \ No newline at end of file diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt index d002e1f8ec8..7405dd97669 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt @@ -678,6 +678,8 @@ val loweringList = listOf( wasmStringSwitchOptimizerLowering, + associatedObjectsLowering, + complexExternalDeclarationsToTopLevelFunctionsLowering, complexExternalDeclarationsUsagesLowering, @@ -724,8 +726,6 @@ val loweringList = listOf( eraseVirtualDispatchReceiverParametersTypes, bridgesConstructionPhase, - associatedObjectsLowering, - objectDeclarationLoweringPhase, genericReturnTypeLowering, unitToVoidLowering, diff --git a/compiler/testData/diagnostics/wasmTests/jsInterop/associatedObjects.kt b/compiler/testData/diagnostics/wasmTests/jsInterop/associatedObjects.kt new file mode 100644 index 00000000000..2e59e072148 --- /dev/null +++ b/compiler/testData/diagnostics/wasmTests/jsInterop/associatedObjects.kt @@ -0,0 +1,21 @@ +// FIR_IDENTICAL +@file:OptIn(ExperimentalAssociatedObjects::class) + +import kotlin.reflect.AssociatedObjectKey +import kotlin.reflect.ExperimentalAssociatedObjects +import kotlin.reflect.KClass +import kotlin.reflect.findAssociatedObject + +@AssociatedObjectKey +@Retention(AnnotationRetention.BINARY) +annotation class X(val kClass: KClass<*>) + +object Promise + +@X(Promise::class) +external class Y + +external class OuterExternal { + @X(Promise::class) + class NestedExternal +} diff --git a/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/WasmPlatformConfigurator.kt b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/WasmPlatformConfigurator.kt index 57ab4119539..a4eda131e7e 100644 --- a/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/WasmPlatformConfigurator.kt +++ b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/WasmPlatformConfigurator.kt @@ -31,6 +31,7 @@ object WasmJsPlatformConfigurator : PlatformConfiguratorBase( WasmJsFunAnnotationChecker, WasmJsInteropTypesChecker, WasmJsExportChecker, + FirWasmJsAssociatedObjectChecker, ), additionalCallCheckers = listOf( JsModuleCallChecker, 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 index 5840cf48ea2..bb77b0e3543 100644 --- 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 @@ -31,6 +31,8 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { put(ErrorsWasm.WASI_EXTERNAL_NOT_TOP_LEVEL_FUNCTION, "Only top-level functions can be external") put(ErrorsWasm.WASI_EXTERNAL_FUNCTION_WITHOUT_IMPORT, "External functions should be annotated with @WasmImport") + put(ErrorsWasm.ASSOCIATED_OBJECT_INVALID_BINDING, "Invalid associated object binding.") + put(ErrorsWasm.NESTED_WASM_IMPORT, "Only top-level functions can be imported with @WasmImport") put(ErrorsWasm.WASM_IMPORT_ON_NON_EXTERNAL_DECLARATION, "Functions annotated with @WasmImport must be external") put(ErrorsWasm.WASM_IMPORT_EXPORT_PARAMETER_DEFAULT_VALUE, "Default parameter values are not supported with @WasmImport and @WasmExport") 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 index 23306b6a2eb..dd66e870cc2 100644 --- 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 @@ -26,6 +26,8 @@ public interface ErrorsWasm { DiagnosticFactory0 WASI_EXTERNAL_NOT_TOP_LEVEL_FUNCTION = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 WASI_EXTERNAL_FUNCTION_WITHOUT_IMPORT = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 ASSOCIATED_OBJECT_INVALID_BINDING = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 NESTED_WASM_IMPORT = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 WASM_IMPORT_ON_NON_EXTERNAL_DECLARATION = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 WASM_IMPORT_EXPORT_PARAMETER_DEFAULT_VALUE = DiagnosticFactory0.create(ERROR); diff --git a/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/FirWasmJsAssociatedObjectChecker.kt b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/FirWasmJsAssociatedObjectChecker.kt new file mode 100644 index 00000000000..fc371a353da --- /dev/null +++ b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/FirWasmJsAssociatedObjectChecker.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2024 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.name.StandardClassIds +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.annotationClass +import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal +import org.jetbrains.kotlin.resolve.source.getPsi + +object FirWasmJsAssociatedObjectChecker : DeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + if (descriptor !is ClassDescriptor) return + if (!descriptor.isEffectivelyExternal()) return + + for (annotationCall in descriptor.annotations) { + val annotation = annotationCall.annotationClass ?: continue + if (annotation.annotations.hasAnnotation(StandardClassIds.Annotations.AssociatedObjectKey.asSingleFqName())) { + context.trace.report(ErrorsWasm.ASSOCIATED_OBJECT_INVALID_BINDING.on(annotationCall.source.getPsi() ?: declaration)) + } + } + } +} \ No newline at end of file diff --git a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsFirWasmTestGenerated.java b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsFirWasmTestGenerated.java index d77a04763c7..c7db9f13761 100644 --- a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsFirWasmTestGenerated.java +++ b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsFirWasmTestGenerated.java @@ -39,6 +39,12 @@ public class DiagnosticsFirWasmTestGenerated extends AbstractDiagnosticsFirWasmT runTest("compiler/testData/diagnostics/wasmTests/jsInterop/anonymousInitializer.kt"); } + @Test + @TestMetadata("associatedObjects.kt") + public void testAssociatedObjects() { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/associatedObjects.kt"); + } + @Test @TestMetadata("body.kt") public void testBody() { 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 index d45768fb2ad..ee969c84e16 100644 --- 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 @@ -39,6 +39,12 @@ public class DiagnosticsWasmTestGenerated extends AbstractDiagnosticsWasmTest { runTest("compiler/testData/diagnostics/wasmTests/jsInterop/anonymousInitializer.kt"); } + @Test + @TestMetadata("associatedObjects.kt") + public void testAssociatedObjects() { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/associatedObjects.kt"); + } + @Test @TestMetadata("body.kt") public void testBody() {