From e800be18c6199dba1befa6c507b3251769f21c65 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 7 Oct 2015 10:28:11 +0300 Subject: [PATCH] Introduce UnsafeVariance annotation --- .../jetbrains/kotlin/resolve/VarianceChecker.kt | 4 +++- compiler/testData/builtin-classes.txt | 4 ++++ .../tests/generics/suppressVarianceConflict.kt | 17 +++++++++++++++++ .../tests/generics/suppressVarianceConflict.txt | 13 +++++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 6 ++++++ core/builtins/src/kotlin/Annotations.kt | 8 ++++++++ .../kotlin/builtins/KotlinBuiltIns.java | 1 + .../descriptors/annotations/Annotations.kt | 2 ++ 8 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.kt create mode 100644 compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/VarianceChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VarianceChecker.kt index 955dbd86759..323f3c2a3cb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/VarianceChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VarianceChecker.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.typeBinding.TypeBinding import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.resolve.typeBinding.createTypeBinding import org.jetbrains.kotlin.resolve.typeBinding.createTypeBindingForReturnType import org.jetbrains.kotlin.psi.JetCallableDeclaration @@ -161,7 +162,8 @@ class VarianceChecker(private val trace: BindingTrace) { val classifierDescriptor = jetType.getConstructor().getDeclarationDescriptor() if (classifierDescriptor is TypeParameterDescriptor) { val declarationVariance = classifierDescriptor.getVariance() - if (!declarationVariance.allowsPosition(position)) { + if (!declarationVariance.allowsPosition(position) + && !jetType.annotations.hasAnnotation(KotlinBuiltIns.FQ_NAMES.unsafeVariance)) { diagnosticSink.report( Errors.TYPE_VARIANCE_CONFLICT.on( psiElement, diff --git a/compiler/testData/builtin-classes.txt b/compiler/testData/builtin-classes.txt index 16eedc2303f..145264f54be 100644 --- a/compiler/testData/builtin-classes.txt +++ b/compiler/testData/builtin-classes.txt @@ -1272,6 +1272,10 @@ public object Unit { /*primary*/ private constructor Unit() } +@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.TYPE}) @kotlin.annotation.Retention(value = AnnotationRetention.SOURCE) @kotlin.annotation.MustBeDocumented() @kotlin.annotation.annotation() public final class UnsafeVariance : kotlin.Annotation { + /*primary*/ public constructor UnsafeVariance() +} + @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.VALUE_PARAMETER}) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented() @kotlin.annotation.annotation() private final class crossinline : kotlin.Annotation { /*primary*/ public constructor crossinline() } diff --git a/compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.kt b/compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.kt new file mode 100644 index 00000000000..553af5275bc --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.kt @@ -0,0 +1,17 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE + +class A { + fun foo(x: @UnsafeVariance T) {} + fun foo(x: @UnsafeVariance T, y: List<@UnsafeVariance T>): @UnsafeVariance E = null!! + + fun bar(): List<@UnsafeVariance E> = null!! +} + +fun foo(x: A, cs: CharSequence, ls: List) { + val y: A = x + + y.foo(cs) + val s: String = y.foo(cs, ls) + + val ls2: List = y.bar() +} diff --git a/compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.txt b/compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.txt new file mode 100644 index 00000000000..d79c73aa4b1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.txt @@ -0,0 +1,13 @@ +package + +public fun foo(/*0*/ x: A, /*1*/ cs: kotlin.CharSequence, /*2*/ ls: kotlin.List): kotlin.Unit + +public final class A { + public constructor A() + public final fun bar(): kotlin.List<@kotlin.UnsafeVariance() E> + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ x: @kotlin.UnsafeVariance() T): kotlin.Unit + public final fun foo(/*0*/ x: @kotlin.UnsafeVariance() T, /*1*/ y: kotlin.List<@kotlin.UnsafeVariance() T>): @kotlin.UnsafeVariance() E + 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/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index f30b05e627f..7ca8803a9f7 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -6399,6 +6399,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("suppressVarianceConflict.kt") + public void testSuppressVarianceConflict() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/diagnostics/tests/generics/nullability") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/builtins/src/kotlin/Annotations.kt b/core/builtins/src/kotlin/Annotations.kt index 2b3664873e9..b0ce1e7021b 100644 --- a/core/builtins/src/kotlin/Annotations.kt +++ b/core/builtins/src/kotlin/Annotations.kt @@ -119,6 +119,14 @@ public annotation class HiddenDeclaration @MustBeDocumented private annotation class external +/** + * Suppresses errors about variance conflict + */ +@Target(TYPE) +@Retention(SOURCE) +@MustBeDocumented +public annotation class UnsafeVariance + /** * Specifies that the corresponding type should be ignored during type inference. */ diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 2a1cd6f4228..73428160445 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -151,6 +151,7 @@ public abstract class KotlinBuiltIns { public final FqName retention = annotationName("Retention"); public final FqName repeatable = annotationName("Repeatable"); public final FqName mustBeDocumented = annotationName("MustBeDocumented"); + public final FqName unsafeVariance = fqName("UnsafeVariance"); public final FqName mutableList = fqName("MutableList"); public final FqName mutableSet = fqName("MutableSet"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt index 3634329319a..23231df3b99 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt @@ -26,6 +26,8 @@ public interface Annotations : Iterable { public fun findAnnotation(fqName: FqName): AnnotationDescriptor? + public fun hasAnnotation(fqName: FqName) = findAnnotation(fqName) != null + public fun findExternalAnnotation(fqName: FqName): AnnotationDescriptor? public fun getUseSiteTargetedAnnotations(): List