diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java index 0ae797ffac3..87e31991b7a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java @@ -300,6 +300,10 @@ public abstract class AnnotationCodegen { return null; } + if (classDescriptor.isExpect()) { + return null; + } + innerClassConsumer.addInnerClassInfoFromAnnotation(classDescriptor); String asmTypeDescriptor = typeMapper.mapType(annotationDescriptor.getType()).getDescriptor(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index c46a3aa61f6..014ef4a8e18 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -619,6 +619,9 @@ public interface Errors { DiagnosticFactory2.create(ERROR, ACTUAL_DECLARATION_NAME); DiagnosticFactory0 ACTUAL_MISSING = DiagnosticFactory0.create(ERROR, ACTUAL_DECLARATION_NAME); + DiagnosticFactory0 OPTIONAL_EXPECTATION_NOT_ON_EXPECTED = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY = DiagnosticFactory0.create(ERROR); + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Errors/warnings inside code blocks diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index a6e7c636945..e4eabd665b0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -284,6 +284,9 @@ public class DefaultErrorMessages { NAME, IncompatibleExpectedActualClassScopesRenderer.TEXT); MAP.put(ACTUAL_MISSING, "Declaration must be marked with 'actual'"); + MAP.put(OPTIONAL_EXPECTATION_NOT_ON_EXPECTED, "'@OptionalExpectation' can only be used on an expected annotation class"); + MAP.put(OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY, "Declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry"); + MAP.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties"); MAP.put(SUPERTYPE_NOT_INITIALIZED, "This type has a constructor, and thus must be initialized here"); MAP.put(NOTHING_TO_OVERRIDE, "''{0}'' overrides nothing", NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java index 066dabe405a..998462c391b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java @@ -19,10 +19,7 @@ import org.jetbrains.kotlin.lexer.KtKeywordToken; import org.jetbrains.kotlin.lexer.KtModifierKeywordToken; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker; -import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext; -import org.jetbrains.kotlin.resolve.checkers.PublishedApiUsageChecker; -import org.jetbrains.kotlin.resolve.checkers.UnderscoreChecker; +import org.jetbrains.kotlin.resolve.checkers.*; import java.util.Collection; import java.util.List; @@ -275,6 +272,7 @@ public class ModifiersChecker { } OperatorModifierChecker.INSTANCE.check(declaration, descriptor, trace, languageVersionSettings); PublishedApiUsageChecker.INSTANCE.check(declaration, descriptor, trace); + OptionalExpectationTargetChecker.INSTANCE.check(declaration, descriptor, trace); } public void checkTypeParametersModifiers(@NotNull KtModifierListOwner modifierListOwner) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index a9d7e0e2c7b..490ca764910 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -100,7 +100,8 @@ private val DEFAULT_CALL_CHECKERS = listOf( ) private val DEFAULT_TYPE_CHECKERS = emptyList() private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf( - DeprecatedClassifierUsageChecker(), ApiVersionClassifierUsageChecker, MissingDependencyClassChecker.ClassifierUsage + DeprecatedClassifierUsageChecker(), ApiVersionClassifierUsageChecker, MissingDependencyClassChecker.ClassifierUsage, + OptionalExpectationUsageChecker() ) private val DEFAULT_ANNOTATION_CHECKERS = listOf( ExperimentalMarkerDeclarationAnnotationChecker diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt index 1327a58ea57..d87a6484616 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.incremental.components.ExpectActualTracker +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier import org.jetbrains.kotlin.resolve.BindingContext @@ -41,6 +42,8 @@ import org.jetbrains.kotlin.utils.ifEmpty import java.io.File object ExpectedActualDeclarationChecker : DeclarationChecker { + internal val OPTIONAL_EXPECTATION_FQ_NAME = FqName("kotlin.OptionalExpectation") + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { if (!context.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects)) return @@ -67,6 +70,8 @@ object ExpectedActualDeclarationChecker : DeclarationChecker { val compatibility = ExpectedActualResolver.findActualForExpected(descriptor, platformModule) ?: return + if (compatibility.allStrongIncompatibilities() && isOptionalAnnotationClass(descriptor)) return + val shouldReportError = compatibility.allStrongIncompatibilities() || Compatible !in compatibility && compatibility.values.flatMapTo(hashSetOf()) { it }.all { actual -> @@ -89,6 +94,10 @@ object ExpectedActualDeclarationChecker : DeclarationChecker { } } + internal fun isOptionalAnnotationClass(descriptor: DeclarationDescriptor): Boolean { + return descriptor.annotations.hasAnnotation(OPTIONAL_EXPECTATION_FQ_NAME) + } + private fun ExpectActualTracker.reportExpectActual(expected: MemberDescriptor, actualMembers: Sequence) { if (this is ExpectActualTracker.DoNothing) return diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt index a9ee6a499e0..6f367ca4023 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt @@ -265,18 +265,6 @@ class ExperimentalUsageChecker(project: Project) : CallChecker { } } - private fun PsiElement.isUsageAsAnnotationOrImport(): Boolean { - val parent = parent - - if (parent is KtUserType) { - return parent.parent is KtTypeReference && - parent.parent.parent is KtConstructorCalleeExpression && - parent.parent.parent.parent is KtAnnotationEntry - } - - return parent is KtDotQualifiedExpression && parent.parent is KtImportDirective - } - private fun PsiElement.isUsageAsQualifier(): Boolean { if (this is KtSimpleNameExpression) { val qualifier = getTopmostParentQualifiedExpressionForSelector() ?: this diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationTargetChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationTargetChecker.kt new file mode 100644 index 00000000000..509974b322f --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationTargetChecker.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.resolve.checkers + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.MemberDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace + +object OptionalExpectationTargetChecker { + fun check( + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + trace: BindingTrace + ) { + if (descriptor is MemberDescriptor && descriptor.isExpect) return + + for (entry in declaration.annotationEntries) { + val annotationDescriptor = trace.get(BindingContext.ANNOTATION, entry) ?: continue + if (annotationDescriptor.fqName == ExpectedActualDeclarationChecker.OPTIONAL_EXPECTATION_FQ_NAME) { + trace.report(Errors.OPTIONAL_EXPECTATION_NOT_ON_EXPECTED.on(entry)) + } + } + } +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationUsageChecker.kt new file mode 100644 index 00000000000..a66d05ef054 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationUsageChecker.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.resolve.checkers + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.diagnostics.Errors + +class OptionalExpectationUsageChecker : ClassifierUsageChecker { + override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) { + if (!ExpectedActualDeclarationChecker.isOptionalAnnotationClass(targetDescriptor)) return + + if (!element.isUsageAsAnnotationOrImport()) { + context.trace.report(Errors.OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY.on(element)) + } + } +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/usageCheckerUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/usageCheckerUtils.kt new file mode 100644 index 00000000000..5913c78c06d --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/usageCheckerUtils.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.resolve.checkers + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.psi.* + +internal fun PsiElement.isUsageAsAnnotationOrImport(): Boolean { + val parent = parent + + if (parent is KtUserType) { + return parent.parent is KtTypeReference && + parent.parent.parent is KtConstructorCalleeExpression && + parent.parent.parent.parent is KtAnnotationEntry + } + + return parent is KtDotQualifiedExpression && parent.parent is KtImportDirective +} diff --git a/compiler/testData/codegen/box/jvmName/loadJvmName.kt b/compiler/testData/codegen/box/jvmName/loadJvmName.kt new file mode 100644 index 00000000000..dc094539dbf --- /dev/null +++ b/compiler/testData/codegen/box/jvmName/loadJvmName.kt @@ -0,0 +1,13 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME + +@JvmName("bar") +fun foo() {} + +fun getJvmName(): JvmName? = + Class.forName("LoadJvmNameKt").declaredMethods.single { it.name == "bar" }.getAnnotation(JvmName::class.java) + +fun box(): String { + // JvmName is binary-retained and should not be accessible via reflection + return if (getJvmName() == null) "OK" else "Fail" +} diff --git a/compiler/testData/codegen/box/multiplatform/optionalExpectation.kt b/compiler/testData/codegen/box/multiplatform/optionalExpectation.kt new file mode 100644 index 00000000000..f707f4a305d --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/optionalExpectation.kt @@ -0,0 +1,49 @@ +// !LANGUAGE: +MultiPlatformProjects +// !USE_EXPERIMENTAL: kotlin.ExperimentalMultiplatform +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// FILE: common.kt + +@OptionalExpectation +expect annotation class Anno(val s: String) + +// FILE: jvm.kt + +import java.lang.reflect.AnnotatedElement + +@Anno("Foo") +class Foo @Anno("") constructor(@Anno("x") x: Int) { + @Anno("bar") + fun bar() {} + + @Anno("getX") + var x = x + @Anno("setX") + set + + @Anno("Nested") + interface Nested +} + +private fun check(element: AnnotatedElement) { + check(element.annotations) +} + +private fun check(annotations: Array) { + val filtered = annotations.filterNot { it.annotationClass.java.name == "kotlin.Metadata" } + if (filtered.isNotEmpty()) { + throw AssertionError("Annotations should be empty: $filtered") + } +} + +fun box(): String { + val foo = Foo::class.java + check(foo) + check(Foo.Nested::class.java) + check(foo.declaredMethods.single { it.name == "bar" }) + check(foo.declaredMethods.single { it.name == "getX" }) + check(foo.declaredMethods.single { it.name == "setX" }) + check(foo.constructors.single()) + check(foo.constructors.single().parameterAnnotations.single()) + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.kt b/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.kt new file mode 100644 index 00000000000..0466f86963e --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.kt @@ -0,0 +1,21 @@ +// !LANGUAGE: +MultiPlatformProjects +// !USE_EXPERIMENTAL: kotlin.ExperimentalMultiplatform +// TARGET_BACKEND: JVM +// WITH_RUNTIME + +@OptionalExpectation +expect annotation class Anno(val s: String) + +@Anno("Foo") +class Foo @Anno("") constructor(@Anno("x") x: Int) { + @Anno("bar") + fun bar() {} + + @Anno("getX") + var x = x + @Anno("setX") + set + + @Anno("Nested") + interface Nested +} diff --git a/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.txt b/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.txt new file mode 100644 index 00000000000..f697431ca92 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.txt @@ -0,0 +1,15 @@ +@kotlin.Metadata +public interface Foo$Nested { + inner class Foo$Nested +} + +@kotlin.Metadata +public final class Foo { + private field x: int + inner class Foo$Nested + public method (p0: int): void + public final method bar(): void + public final method getX(): int + public final method setX(p0: int): void + public synthetic deprecated static method x$annotations(): void +} diff --git a/compiler/testData/multiplatform/optionalExpectation/common.kt b/compiler/testData/multiplatform/optionalExpectation/common.kt new file mode 100644 index 00000000000..06d4177a4b3 --- /dev/null +++ b/compiler/testData/multiplatform/optionalExpectation/common.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME +// ADDITIONAL_COMPILER_ARGUMENTS: -Xuse-experimental=kotlin.ExperimentalMultiplatform + +@OptionalExpectation +expect annotation class A() + +expect class C { + @A + fun f() +} diff --git a/compiler/testData/multiplatform/optionalExpectation/js.kt b/compiler/testData/multiplatform/optionalExpectation/js.kt new file mode 100644 index 00000000000..34c8433a32b --- /dev/null +++ b/compiler/testData/multiplatform/optionalExpectation/js.kt @@ -0,0 +1,3 @@ +actual class C { + actual fun f() {} +} diff --git a/compiler/testData/multiplatform/optionalExpectation/jvm.kt b/compiler/testData/multiplatform/optionalExpectation/jvm.kt new file mode 100644 index 00000000000..0fe40c3c1e7 --- /dev/null +++ b/compiler/testData/multiplatform/optionalExpectation/jvm.kt @@ -0,0 +1,6 @@ +actual annotation class A + +actual class C { + @A + actual fun f() {} +} diff --git a/compiler/testData/multiplatform/optionalExpectation/output.txt b/compiler/testData/multiplatform/optionalExpectation/output.txt new file mode 100644 index 00000000000..bebdfbc55e7 --- /dev/null +++ b/compiler/testData/multiplatform/optionalExpectation/output.txt @@ -0,0 +1,12 @@ +-- Common -- +Exit code: OK +Output: + +-- JVM -- +Exit code: OK +Output: + +-- JS -- +Exit code: OK +Output: + diff --git a/compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt new file mode 100644 index 00000000000..37d7f6ad555 --- /dev/null +++ b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt @@ -0,0 +1,20 @@ +// WITH_RUNTIME +// ADDITIONAL_COMPILER_ARGUMENTS: -Xuse-experimental=kotlin.ExperimentalMultiplatform + +@OptionalExpectation +expect annotation class A() + +fun useInSignature(a: A) = a.toString() + +@OptionalExpectation +expect class NotAnAnnotationClass + +@OptionalExpectation +annotation class NotAnExpectedClass + + + +annotation class InOtherAnnotation(val a: A) + +@InOtherAnnotation(A()) +fun useInOtherAnnotation() {} diff --git a/compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt new file mode 100644 index 00000000000..fa2b69af303 --- /dev/null +++ b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt @@ -0,0 +1,6 @@ +fun useInReturnType(): A? = null + +annotation class AnotherAnnotation(val a: A) + +@AnotherAnnotation(A()) +fun useInAnotherAnnotation() {} diff --git a/compiler/testData/multiplatform/optionalExpectationIncorrectUse/output.txt b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/output.txt new file mode 100644 index 00000000000..ed1f94881ab --- /dev/null +++ b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/output.txt @@ -0,0 +1,46 @@ +-- Common -- +Exit code: COMPILATION_ERROR +Output: +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:7:23: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry +fun useInSignature(a: A) = a.toString() + ^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:9:1: error: this annotation is not applicable to target 'class' +@OptionalExpectation +^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:12:1: error: '@OptionalExpectation' can only be used on an expected annotation class +@OptionalExpectation +^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:17:43: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry +annotation class InOtherAnnotation(val a: A) + ^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:19:20: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry +@InOtherAnnotation(A()) + ^ + +-- JVM -- +Exit code: COMPILATION_ERROR +Output: +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:7:23: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry +fun useInSignature(a: A) = a.toString() + ^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:9:1: error: this annotation is not applicable to target 'class' +@OptionalExpectation +^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:12:1: error: '@OptionalExpectation' can only be used on an expected annotation class +@OptionalExpectation +^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:17:43: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry +annotation class InOtherAnnotation(val a: A) + ^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:19:20: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry +@InOtherAnnotation(A()) + ^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt:1:24: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry +fun useInReturnType(): A? = null + ^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt:3:43: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry +annotation class AnotherAnnotation(val a: A) + ^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt:5:20: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry +@AnotherAnnotation(A()) + ^ diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 4e7890483eb..3b3939f51cf 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -12078,6 +12078,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/jvmName/functionName.kt"); } + @TestMetadata("loadJvmName.kt") + public void testLoadJvmName() throws Exception { + runTest("compiler/testData/codegen/box/jvmName/loadJvmName.kt"); + } + @TestMetadata("multifileClass.kt") public void testMultifileClass() throws Exception { runTest("compiler/testData/codegen/box/jvmName/multifileClass.kt"); @@ -13440,6 +13445,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt"); } + @TestMetadata("optionalExpectation.kt") + public void testOptionalExpectation() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/optionalExpectation.kt"); + } + @TestMetadata("compiler/testData/codegen/box/multiplatform/defaultArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 1d603b59fe0..35c299de0a4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -12078,6 +12078,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/jvmName/functionName.kt"); } + @TestMetadata("loadJvmName.kt") + public void testLoadJvmName() throws Exception { + runTest("compiler/testData/codegen/box/jvmName/loadJvmName.kt"); + } + @TestMetadata("multifileClass.kt") public void testMultifileClass() throws Exception { runTest("compiler/testData/codegen/box/jvmName/multifileClass.kt"); @@ -13440,6 +13445,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt"); } + @TestMetadata("optionalExpectation.kt") + public void testOptionalExpectation() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/optionalExpectation.kt"); + } + @TestMetadata("compiler/testData/codegen/box/multiplatform/defaultArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index 20a85d0dce3..0579db9eb86 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -286,6 +286,24 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeListing/multiplatform") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Multiplatform extends AbstractBytecodeListingTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInMultiplatform() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/multiplatform"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("optionalExpectation.kt") + public void testOptionalExpectation() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.kt"); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeListing/specialBridges") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 6c55fda317f..32f0f3bb434 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -12078,6 +12078,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/jvmName/functionName.kt"); } + @TestMetadata("loadJvmName.kt") + public void testLoadJvmName() throws Exception { + runTest("compiler/testData/codegen/box/jvmName/loadJvmName.kt"); + } + @TestMetadata("multifileClass.kt") public void testMultifileClass() throws Exception { runTest("compiler/testData/codegen/box/jvmName/multifileClass.kt"); @@ -13440,6 +13445,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt"); } + @TestMetadata("optionalExpectation.kt") + public void testOptionalExpectation() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/optionalExpectation.kt"); + } + @TestMetadata("compiler/testData/codegen/box/multiplatform/defaultArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java index 8d9fe94b84a..46e11c0f13a 100644 --- a/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java @@ -99,6 +99,16 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform runTest("compiler/testData/multiplatform/missingOverload/"); } + @TestMetadata("optionalExpectation") + public void testOptionalExpectation() throws Exception { + runTest("compiler/testData/multiplatform/optionalExpectation/"); + } + + @TestMetadata("optionalExpectationIncorrectUse") + public void testOptionalExpectationIncorrectUse() throws Exception { + runTest("compiler/testData/multiplatform/optionalExpectationIncorrectUse/"); + } + @TestMetadata("simple") public void testSimple() throws Exception { runTest("compiler/testData/multiplatform/simple/"); @@ -569,6 +579,32 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform } } + @TestMetadata("compiler/testData/multiplatform/optionalExpectation") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class OptionalExpectation extends AbstractMultiPlatformIntegrationTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInOptionalExpectation() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/multiplatform/optionalExpectation"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + } + + @TestMetadata("compiler/testData/multiplatform/optionalExpectationIncorrectUse") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class OptionalExpectationIncorrectUse extends AbstractMultiPlatformIntegrationTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInOptionalExpectationIncorrectUse() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/multiplatform/optionalExpectationIncorrectUse"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + } + @TestMetadata("compiler/testData/multiplatform/regressions") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/libraries/stdlib/common/build.gradle b/libraries/stdlib/common/build.gradle index 967836527f4..a6540b9643d 100644 --- a/libraries/stdlib/common/build.gradle +++ b/libraries/stdlib/common/build.gradle @@ -49,7 +49,8 @@ compileKotlinCommon { compileKotlinCommon { kotlinOptions { freeCompilerArgs = [ - "-module-name", project.name + "-module-name", project.name, + "-Xuse-experimental=kotlin.Experimental" ] } } @@ -94,4 +95,4 @@ task distCommon(type: Copy) { dist.dependsOn distCommon -classes.setDependsOn(classes.dependsOn.findAll { it != "compileJava" }) \ No newline at end of file +classes.setDependsOn(classes.dependsOn.findAll { it != "compileJava" }) diff --git a/libraries/stdlib/common/src/kotlin/annotations/Annotations.kt b/libraries/stdlib/common/src/kotlin/annotations/Annotations.kt new file mode 100644 index 00000000000..db4bc314c78 --- /dev/null +++ b/libraries/stdlib/common/src/kotlin/annotations/Annotations.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin + +import kotlin.annotation.AnnotationTarget.* +import kotlin.internal.RequireKotlin +import kotlin.internal.RequireKotlinVersionKind + +@Experimental +@Target( + CLASS, + ANNOTATION_CLASS, + PROPERTY, + FIELD, + LOCAL_VARIABLE, + VALUE_PARAMETER, + CONSTRUCTOR, + FUNCTION, + PROPERTY_GETTER, + PROPERTY_SETTER, + TYPEALIAS +) +@Retention(AnnotationRetention.BINARY) +@RequireKotlin("1.2.50", versionKind = RequireKotlinVersionKind.COMPILER_VERSION) +public annotation class ExperimentalMultiplatform + +/** + * This annotation is only applicable to `expect` annotation classes in multi-platform projects and marks that class as "optional". + * Optional expected class is allowed to have no corresponding actual class on the platform. Optional annotations can only be used + * to annotate something, not as types in signatures. If an optional annotation has no corresponding actual class on a platform, + * the annotation entries where it's used are simply erased when compiling code on that platform. + */ +@Target(ANNOTATION_CLASS) +@Retention(AnnotationRetention.BINARY) +@ExperimentalMultiplatform +@RequireKotlin("1.2.50", versionKind = RequireKotlinVersionKind.COMPILER_VERSION) +public annotation class OptionalExpectation diff --git a/libraries/stdlib/js/build.gradle b/libraries/stdlib/js/build.gradle index 847e47e7fbc..5c8eca18f54 100644 --- a/libraries/stdlib/js/build.gradle +++ b/libraries/stdlib/js/build.gradle @@ -137,7 +137,10 @@ compileKotlin2Js { outputFile = "${buildDir}/classes/main/kotlin.js" sourceMap = true sourceMapPrefix = "./" - freeCompilerArgs += ["-source-map-base-dirs", [builtinsSrcDir, jsSrcDir, commonSrcDir, commonSrcDir2].collect { file(it).absoluteFile }.join(File.pathSeparator)] + freeCompilerArgs += [ + "-source-map-base-dirs", [builtinsSrcDir, jsSrcDir, commonSrcDir, commonSrcDir2].collect { file(it).absoluteFile }.join(File.pathSeparator), + "-Xuse-experimental=kotlin.Experimental" + ] } } diff --git a/libraries/stdlib/jvm/build.gradle b/libraries/stdlib/jvm/build.gradle index 1cb466f88fc..35f61da1979 100644 --- a/libraries/stdlib/jvm/build.gradle +++ b/libraries/stdlib/jvm/build.gradle @@ -174,7 +174,8 @@ compileKotlin { "-Xallow-kotlin-package", "-Xmultifile-parts-inherit", "-Xnormalize-constructor-calls=enable", - "-module-name", "kotlin-stdlib" + "-module-name", "kotlin-stdlib", + "-Xuse-experimental=kotlin.Experimental" ] } } diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index b21be02f0ef..5f361d51aac 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -31,6 +31,9 @@ public final class kotlin/Experimental$Level : java/lang/Enum { public static fun values ()[Lkotlin/Experimental$Level; } +public abstract interface annotation class kotlin/ExperimentalMultiplatform : java/lang/annotation/Annotation { +} + public abstract interface annotation class kotlin/ExtensionFunctionType : java/lang/annotation/Annotation { } @@ -96,6 +99,9 @@ public final class kotlin/NotImplementedError : java/lang/Error { public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V } +public abstract interface annotation class kotlin/OptionalExpectation : java/lang/annotation/Annotation { +} + public final class kotlin/Pair : java/io/Serializable { public fun (Ljava/lang/Object;Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object;