From 05d9ad6ed9780e13dcbc2b692e3a0bb9d9c59473 Mon Sep 17 00:00:00 2001 From: Elena Lepilkina Date: Mon, 17 Feb 2020 12:39:13 +0300 Subject: [PATCH] Added Native-specific frontend checker for `@SharedImmutable` --- .../nativeTests/sharedImmutable.kt | 139 ++++++++++++++++++ .../nativeTests/sharedImmutable.txt | 110 ++++++++++++++ .../DiagnosticsNativeTestGenerated.java | 5 + .../diagnostics/DefaultErrorMessagesNative.kt | 2 + .../resolve/konan/diagnostics/ErrorsNative.kt | 4 + .../NativeSharedImmutableChecker.kt | 49 ++++++ .../platform/NativePlatformConfigurator.kt | 3 +- 7 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/diagnostics/nativeTests/sharedImmutable.kt create mode 100644 compiler/testData/diagnostics/nativeTests/sharedImmutable.txt create mode 100644 native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeSharedImmutableChecker.kt diff --git a/compiler/testData/diagnostics/nativeTests/sharedImmutable.kt b/compiler/testData/diagnostics/nativeTests/sharedImmutable.kt new file mode 100644 index 0000000000000..6dc9b2c813f5f --- /dev/null +++ b/compiler/testData/diagnostics/nativeTests/sharedImmutable.kt @@ -0,0 +1,139 @@ +// FILE: annotation.kt +package kotlin.native.concurrent + +import kotlin.reflect.KProperty + +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.BINARY) +annotation class SharedImmutable + +@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.BINARY) +annotation class ThreadLocal + +// FILE: test.kt +import kotlin.native.concurrent.SharedImmutable +import kotlin.native.concurrent.ThreadLocal +import kotlin.reflect.KProperty + +fun println(value: Int) {} +fun println(value: String) {} +fun println(value: Point) {} + +data class Point(val x: Double, val y: Double) +@SharedImmutable +val point1 = Point(1.0, 1.0) + +@SharedImmutable +var point2 = Point(2.0, 2.0) + +class Date(@SharedImmutable val month: Int, @SharedImmutable var day:Int) +class Person(val name: String) { + @SharedImmutable + var surname: String? = null +} + +class Figure { + @SharedImmutable + val cornerPoint: Point + get() = point1 +} + +@SharedImmutable +var age = 20 +get() { + println("Age is: $field") + return field +} +set(value) { + println(value) +} + +var globalAge = 30 +@SharedImmutable +var age1 = 20 + get() { + println("Age is: $field") + return field + } + set(value) { + globalAge = value + } + +// Can't +@SharedImmutable +var point3: Point + get() = point2 + set(value) { + point2 = value + } + +@SharedImmutable +var point4: Point + get() = point2 + set(value) { + println(value) + } + +@ThreadLocal +var point0 = Point(2.0, 2.0) + +@SharedImmutable +var point5: Point + get() = point0 + set(value) { + point0 = value + } + + +class Delegate { + var value = 20 + operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { + println("Get") + return value + } + + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { + println("Set") + } +} + +@SharedImmutable +var property: Int by Delegate() + +class Delegate1 { + var value = 20 + operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { + return value + } + + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { + this.value = value + } +} + +@SharedImmutable +var property1: Int by Delegate1() + +var globalValue: Int = 20 + +class Delegate2 { + + operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { + return globalValue + } + + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { + println(value) + } +} + +@SharedImmutable +var property2: Int by Delegate2() + +@SharedImmutable +val someValue: Int + get() = 20 + +@SharedImmutable +val someValueWithDelegate by Delegate() \ No newline at end of file diff --git a/compiler/testData/diagnostics/nativeTests/sharedImmutable.txt b/compiler/testData/diagnostics/nativeTests/sharedImmutable.txt new file mode 100644 index 0000000000000..71c277274b653 --- /dev/null +++ b/compiler/testData/diagnostics/nativeTests/sharedImmutable.txt @@ -0,0 +1,110 @@ +package + +@kotlin.native.concurrent.SharedImmutable public var age: kotlin.Int +@kotlin.native.concurrent.SharedImmutable public var age1: kotlin.Int +public var globalAge: kotlin.Int +public var globalValue: kotlin.Int +@kotlin.native.concurrent.ThreadLocal public var point0: Point +@kotlin.native.concurrent.SharedImmutable public val point1: Point +@kotlin.native.concurrent.SharedImmutable public var point2: Point +@kotlin.native.concurrent.SharedImmutable public var point3: Point +@kotlin.native.concurrent.SharedImmutable public var point4: Point +@kotlin.native.concurrent.SharedImmutable public var point5: Point +@kotlin.native.concurrent.SharedImmutable public var property: kotlin.Int +@kotlin.native.concurrent.SharedImmutable public var property1: kotlin.Int +@kotlin.native.concurrent.SharedImmutable public var property2: kotlin.Int +@kotlin.native.concurrent.SharedImmutable public val someValue: kotlin.Int +@kotlin.native.concurrent.SharedImmutable public val someValueWithDelegate: kotlin.Int +public fun println(/*0*/ value: Point): kotlin.Unit +public fun println(/*0*/ value: kotlin.Int): kotlin.Unit +public fun println(/*0*/ value: kotlin.String): kotlin.Unit + +public final class Date { + public constructor Date(/*0*/ month: kotlin.Int, /*1*/ day: kotlin.Int) + @kotlin.native.concurrent.SharedImmutable public final var day: kotlin.Int + @kotlin.native.concurrent.SharedImmutable public final val month: 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 Delegate { + public constructor Delegate() + public final var value: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ property: kotlin.reflect.KProperty<*>): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun setValue(/*0*/ thisRef: kotlin.Any?, /*1*/ property: kotlin.reflect.KProperty<*>, /*2*/ value: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Delegate1 { + public constructor Delegate1() + public final var value: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ property: kotlin.reflect.KProperty<*>): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun setValue(/*0*/ thisRef: kotlin.Any?, /*1*/ property: kotlin.reflect.KProperty<*>, /*2*/ value: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Delegate2 { + public constructor Delegate2() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ property: kotlin.reflect.KProperty<*>): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun setValue(/*0*/ thisRef: kotlin.Any?, /*1*/ property: kotlin.reflect.KProperty<*>, /*2*/ value: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Figure { + public constructor Figure() + @kotlin.native.concurrent.SharedImmutable public final val cornerPoint: Point + 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 Person { + public constructor Person(/*0*/ name: kotlin.String) + public final val name: kotlin.String + @kotlin.native.concurrent.SharedImmutable public final var surname: kotlin.String? + 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 data class Point { + public constructor Point(/*0*/ x: kotlin.Double, /*1*/ y: kotlin.Double) + public final val x: kotlin.Double + public final val y: kotlin.Double + public final operator /*synthesized*/ fun component1(): kotlin.Double + public final operator /*synthesized*/ fun component2(): kotlin.Double + public final /*synthesized*/ fun copy(/*0*/ x: kotlin.Double = ..., /*1*/ y: kotlin.Double = ...): Point + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + +package kotlin { + + package kotlin.native { + + package kotlin.native.concurrent { + + @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class SharedImmutable : kotlin.Annotation { + public constructor SharedImmutable() + 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 + } + + @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.CLASS}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class ThreadLocal : kotlin.Annotation { + public constructor ThreadLocal() + 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/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsNativeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsNativeTestGenerated.java index 6da036c98b4f3..0ffca1fc5b898 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsNativeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsNativeTestGenerated.java @@ -28,6 +28,11 @@ public void testAllFilesPresentInNativeTests() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/nativeTests"), Pattern.compile("^(.+)\\.kt$"), null, true); } + @TestMetadata("sharedImmutable.kt") + public void testSharedImmutable() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/sharedImmutable.kt"); + } + @TestMetadata("throws.kt") public void testThrows() throws Exception { runTest("compiler/testData/diagnostics/nativeTests/throws.kt"); diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt index b9f4ef561ef8e..57f60ba0d5871 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt @@ -20,6 +20,8 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { ErrorsNative.INCOMPATIBLE_THROWS_INHERITED, "Member inherits different @Throws filters from {0}", Renderers.commaSeparated(Renderers.NAME) ) + put(ErrorsNative.INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY, "@SharedImmutable isn't applicable to current property") + put(ErrorsNative.INAPPLICABLE_SHARED_IMMUTABLE_TOP_LEVEL, "@SharedImmutable is applicable only to top level declarations") } } diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt index 1c0b8f000ca9b..8c5102bc9d1d1 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt @@ -20,6 +20,10 @@ object ErrorsNative { val INCOMPATIBLE_THROWS_OVERRIDE = DiagnosticFactory1.create(Severity.ERROR) @JvmField val INCOMPATIBLE_THROWS_INHERITED = DiagnosticFactory1.create>(Severity.ERROR) + @JvmField + val INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY = DiagnosticFactory0.create(Severity.ERROR) + @JvmField + val INAPPLICABLE_SHARED_IMMUTABLE_TOP_LEVEL = DiagnosticFactory0.create(Severity.ERROR) init { Errors.Initializer.initializeFactoryNames(ErrorsNative::class.java) diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeSharedImmutableChecker.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeSharedImmutableChecker.kt new file mode 100644 index 0000000000000..0a81bae648a43 --- /dev/null +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeSharedImmutableChecker.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2020 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.resolve.konan.diagnostics + + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0 +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker +import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext +import org.jetbrains.kotlin.resolve.hasBackingField + +object NativeSharedImmutableChecker : DeclarationChecker { + private val sharedImmutableFqName = FqName("kotlin.native.concurrent.SharedImmutable") + + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + check(declaration, descriptor, context, ErrorsNative.INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY) { + val isVariable = descriptor is VariableDescriptor && descriptor.isVar + val hasBackingField = descriptor is PropertyDescriptor && descriptor.hasBackingField(context.trace.bindingContext) + val hasDelegate = declaration is KtProperty && declaration.delegate != null + !isVariable && hasBackingField || hasDelegate + } + check(declaration, descriptor, context, ErrorsNative.INAPPLICABLE_SHARED_IMMUTABLE_TOP_LEVEL) { + DescriptorUtils.isTopLevelDeclaration(descriptor) + } + } + + fun check( + declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext, + error: DiagnosticFactory0, successCondition: (DeclarationDescriptor) -> Boolean + ) { + if (successCondition(descriptor)) return + val sharedImmutableAnnotation = descriptor.annotations.findAnnotation(sharedImmutableFqName) + sharedImmutableAnnotation?.let { + val reportLocation = DescriptorToSourceUtils.getSourceFromAnnotation(it) ?: declaration + context.trace.report(error.on(reportLocation)) + } + } +} diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt index b5ee4c2314274..47d3af98d55d6 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt @@ -14,11 +14,12 @@ import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker import org.jetbrains.kotlin.resolve.inline.ReasonableInlineRule import org.jetbrains.kotlin.resolve.jvm.checkers.SuperCallWithDefaultArgumentsChecker +import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeSharedImmutableChecker import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeThrowsChecker object NativePlatformConfigurator : PlatformConfiguratorBase( additionalCallCheckers = listOf(SuperCallWithDefaultArgumentsChecker()), - additionalDeclarationCheckers = listOf(NativeThrowsChecker) + additionalDeclarationCheckers = listOf(NativeThrowsChecker, NativeSharedImmutableChecker) ) { override fun configureModuleComponents(container: StorageComponentContainer) { container.useInstance(NativeInliningRule)