Support JvmSuppressWildcards and JvmWildcard annotations
#KT-9898 Fixed
This commit is contained in:
@@ -326,9 +326,16 @@ public class JetTypeMapper {
|
||||
return mapType(descriptor.getReturnType(), sw, TypeMappingMode.GENERIC_TYPE);
|
||||
}
|
||||
else {
|
||||
boolean isAnnotationMethod = DescriptorUtils.isAnnotationClass(descriptor.getContainingDeclaration());
|
||||
TypeMappingMode typeMappingModeFromAnnotation =
|
||||
TypeMappingUtil.extractTypeMappingModeFromAnnotation(descriptor, returnType, isAnnotationMethod);
|
||||
if (typeMappingModeFromAnnotation != null) {
|
||||
return mapType(returnType, sw, typeMappingModeFromAnnotation);
|
||||
}
|
||||
|
||||
TypeMappingMode mappingMode = TypeMappingMode.getOptimalModeForReturnType(
|
||||
returnType,
|
||||
/* isAnnotationMethod = */ DescriptorUtils.isAnnotationClass(descriptor.getContainingDeclaration()));
|
||||
/* isAnnotationMethod = */ isAnnotationMethod);
|
||||
|
||||
return mapType(returnType, sw, mappingMode);
|
||||
}
|
||||
@@ -631,11 +638,13 @@ public class JetTypeMapper {
|
||||
signatureVisitor.writeUnboundedWildcard();
|
||||
}
|
||||
else {
|
||||
Variance projectionKind = getVarianceForWildcard(parameter, argument, mode);
|
||||
TypeMappingMode argumentMode = TypeMappingUtil.updateArgumentModeFromAnnotations(mode, argument.getType());
|
||||
Variance projectionKind = getVarianceForWildcard(parameter, argument, argumentMode);
|
||||
|
||||
signatureVisitor.writeTypeArgument(projectionKind);
|
||||
|
||||
mapType(argument.getType(), signatureVisitor,
|
||||
mode.toGenericArgumentMode(
|
||||
argumentMode.toGenericArgumentMode(
|
||||
TypeMappingUtil.getEffectiveVariance(parameter.getVariance(), argument.getProjectionKind())));
|
||||
|
||||
signatureVisitor.writeTypeArgumentEnd();
|
||||
@@ -1204,7 +1213,13 @@ public class JetTypeMapper {
|
||||
|
||||
TypeMappingMode typeMappingMode;
|
||||
|
||||
if (TypeMappingUtil.isMethodWithDeclarationSiteWildcards(callableDescriptor) && !type.getArguments().isEmpty()) {
|
||||
TypeMappingMode typeMappingModeFromAnnotation =
|
||||
TypeMappingUtil.extractTypeMappingModeFromAnnotation(callableDescriptor, type, /* isForAnnotationParameter = */ false);
|
||||
|
||||
if (typeMappingModeFromAnnotation != null) {
|
||||
typeMappingMode = typeMappingModeFromAnnotation;
|
||||
}
|
||||
else if (TypeMappingUtil.isMethodWithDeclarationSiteWildcards(callableDescriptor) && !type.getArguments().isEmpty()) {
|
||||
typeMappingMode = TypeMappingMode.GENERIC_TYPE; // Render all wildcards
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -93,6 +93,16 @@ internal class TypeMappingMode private constructor(
|
||||
skipDeclarationSiteWildcardsIfPossible = true,
|
||||
genericContravariantArgumentMode = contravariantArgumentMode)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun createWithConstantDeclarationSiteWildcardsMode(
|
||||
skipDeclarationSiteWildcards: Boolean,
|
||||
isForAnnotationParameter: Boolean,
|
||||
fallbackMode: TypeMappingMode? = null
|
||||
) = TypeMappingMode(
|
||||
isForAnnotationParameter = isForAnnotationParameter,
|
||||
skipDeclarationSiteWildcards = skipDeclarationSiteWildcards,
|
||||
genericArgumentMode = fallbackMode)
|
||||
}
|
||||
|
||||
fun toGenericArgumentMode(effectiveVariance: Variance): TypeMappingMode =
|
||||
|
||||
@@ -19,9 +19,11 @@ package org.jetbrains.kotlin.codegen.state
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.firstOverridden
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
@@ -83,3 +85,45 @@ private val METHODS_WITH_DECLARATION_SITE_WILDCARDS = setOf(
|
||||
FqName("kotlin.MutableList.addAll"),
|
||||
FqName("kotlin.MutableMap.putAll")
|
||||
)
|
||||
|
||||
internal fun TypeMappingMode.updateArgumentModeFromAnnotations(type: KotlinType): TypeMappingMode {
|
||||
type.suppressWildcardsMode()?.let {
|
||||
return TypeMappingMode.createWithConstantDeclarationSiteWildcardsMode(
|
||||
skipDeclarationSiteWildcards = it, isForAnnotationParameter = isForAnnotationParameter)
|
||||
}
|
||||
|
||||
if (type.annotations.hasAnnotation(JVM_WILDCARD_ANNOTATION_FQ_NAME)) {
|
||||
return TypeMappingMode.createWithConstantDeclarationSiteWildcardsMode(
|
||||
skipDeclarationSiteWildcards = false, isForAnnotationParameter = isForAnnotationParameter, fallbackMode = this)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
internal fun extractTypeMappingModeFromAnnotation(
|
||||
callableDescriptor: CallableDescriptor,
|
||||
outerType: KotlinType,
|
||||
isForAnnotationParameter: Boolean
|
||||
): TypeMappingMode? =
|
||||
(outerType.suppressWildcardsMode() ?: callableDescriptor.suppressWildcardsMode())?.let {
|
||||
if (outerType.arguments.isNotEmpty())
|
||||
TypeMappingMode.createWithConstantDeclarationSiteWildcardsMode(
|
||||
skipDeclarationSiteWildcards = it, isForAnnotationParameter = isForAnnotationParameter)
|
||||
else
|
||||
TypeMappingMode.DEFAULT
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.suppressWildcardsMode(): Boolean? =
|
||||
parentsWithSelf.mapNotNull {
|
||||
it.annotations.findAnnotation(JVM_SUPPRESS_WILDCARDS_ANNOTATION_FQ_NAME)
|
||||
}.firstOrNull().suppressWildcardsMode()
|
||||
|
||||
private fun KotlinType.suppressWildcardsMode(): Boolean? =
|
||||
annotations.findAnnotation(JVM_SUPPRESS_WILDCARDS_ANNOTATION_FQ_NAME).suppressWildcardsMode()
|
||||
|
||||
private fun AnnotationDescriptor?.suppressWildcardsMode(): Boolean? {
|
||||
return (this ?: return null).allValueArguments.values.firstOrNull()?.value as? Boolean ?: true
|
||||
}
|
||||
|
||||
private val JVM_SUPPRESS_WILDCARDS_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmSuppressWildcards")
|
||||
private val JVM_WILDCARD_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmWildcard")
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
public class JavaClass {
|
||||
|
||||
public static class C extends B {
|
||||
public OutPair<String, Integer> foo() {
|
||||
return super.foo();
|
||||
}
|
||||
|
||||
public In<Object> bar() {
|
||||
return super.bar();
|
||||
}
|
||||
}
|
||||
|
||||
public static String test() {
|
||||
A a = new C();
|
||||
|
||||
if (!a.foo().getX().equals("OK")) return "fail 1";
|
||||
if (!a.foo().getY().equals(123)) return "fail 2";
|
||||
|
||||
if (!a.bar().make("123").equals("123")) return "fail 3";
|
||||
|
||||
return "OK";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
class OutPair<out X, out Y>(val x: X, val y: Y)
|
||||
class In<in Z> {
|
||||
fun make(x: Z): String = x.toString()
|
||||
}
|
||||
|
||||
@JvmSuppressWildcards(suppress = false)
|
||||
interface A {
|
||||
fun foo(): OutPair<CharSequence, Number>
|
||||
fun bar(): In<String>
|
||||
}
|
||||
|
||||
abstract class B : A {
|
||||
override fun foo(): OutPair<String, Int> = OutPair("OK", 123)
|
||||
override fun bar(): In<Any> = In()
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
return JavaClass.test();
|
||||
}
|
||||
+2
-1
@@ -7,11 +7,12 @@ public abstract class AImpl {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
@JvmSuppressWildcards(suppress = false)
|
||||
fun addAll(elements: Collection<String>): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
fun addAll(index: Int, elements: Collection<String>): Boolean {
|
||||
fun addAll(index: Int, elements: Collection<@JvmWildcard String>): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
public class JavaClass {
|
||||
|
||||
public static class C extends B {
|
||||
public OutPair<String, Integer> foo() {
|
||||
return super.foo();
|
||||
}
|
||||
|
||||
public In<Object> bar() {
|
||||
return super.bar();
|
||||
}
|
||||
}
|
||||
|
||||
public static String test() {
|
||||
A a = new C();
|
||||
|
||||
if (!a.foo().getX().equals("OK")) return "fail 1";
|
||||
if (!a.foo().getY().equals(123)) return "fail 2";
|
||||
|
||||
if (!a.bar().make("123").equals("123")) return "fail 3";
|
||||
|
||||
return "OK";
|
||||
}
|
||||
}
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
class OutPair<out X, out Y>(val x: X, val y: Y)
|
||||
class In<in Z> {
|
||||
fun make(x: Z): String = x.toString()
|
||||
}
|
||||
|
||||
interface A {
|
||||
fun foo(): OutPair<@JvmWildcard CharSequence, @JvmSuppressWildcards(false) Number>
|
||||
fun bar(): In<@JvmWildcard String>
|
||||
}
|
||||
|
||||
abstract class B : A {
|
||||
override fun foo(): OutPair<String, Int> = OutPair("OK", 123)
|
||||
override fun bar(): In<Any> = In()
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
return JavaClass.test();
|
||||
}
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
class Out<out T>
|
||||
class In<in R>
|
||||
open class Open
|
||||
|
||||
@JvmSuppressWildcards(true)
|
||||
fun deepOpen(x: Out<Out<Out<Open>>>) {}
|
||||
// method: OnFunctionKt::deepOpen
|
||||
// generic signature: (LOut<LOut<LOut<LOpen;>;>;>;)V
|
||||
|
||||
interface A<T> {
|
||||
@JvmSuppressWildcards(true)
|
||||
fun foo(): Out<T>
|
||||
}
|
||||
// method: A::foo
|
||||
// generic signature: ()LOut<TT;>;
|
||||
|
||||
interface B {
|
||||
@JvmSuppressWildcards(true)
|
||||
fun foo(): In<Open>
|
||||
}
|
||||
// method: B::foo
|
||||
// generic signature: ()LIn<LOpen;>;
|
||||
|
||||
@JvmSuppressWildcards(false)
|
||||
fun bar(): Out<Open> = null!!
|
||||
// method: OnFunctionKt::bar
|
||||
// generic signature: ()LOut<+LOpen;>;
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
class OutPair<out T, out E>
|
||||
class Out<out F>
|
||||
class In<in H>
|
||||
|
||||
class X
|
||||
|
||||
fun simpleOut(x: Out<@JvmWildcard X>) {}
|
||||
// method: OnTypesKt::simpleOut
|
||||
// generic signature: (LOut<+LX;>;)V
|
||||
|
||||
fun simpleIn(x: In<@JvmWildcard Any?>) {}
|
||||
// method: OnTypesKt::simpleIn
|
||||
// generic signature: (LIn<-Ljava/lang/Object;>;)V
|
||||
|
||||
fun falseTrueFalse(): @JvmSuppressWildcards(false) OutPair<X, @JvmSuppressWildcards OutPair<Out<X>, Out<@JvmSuppressWildcards(false) X>>> = null!!
|
||||
// method: OnTypesKt::falseTrueFalse
|
||||
// generic signature: ()LOutPair<+LX;LOutPair<LOut<LX;>;LOut<+LX;>;>;>;
|
||||
|
||||
open class Open
|
||||
fun combination(): @JvmSuppressWildcards OutPair< Open, @JvmWildcard OutPair<Open, @JvmWildcard Out<Open>>> = null!!
|
||||
// method: OnTypesKt::combination
|
||||
// generic signature: ()LOutPair<LOpen;+LOutPair<LOpen;+LOut<LOpen;>;>;>;
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
class Out<out T>
|
||||
class In<in E>
|
||||
|
||||
@JvmSuppressWildcards(false)
|
||||
fun foo(x: Boolean, y: Out<Int>): Int = 1
|
||||
// method: PrimitiveTypesKt::foo
|
||||
// generic signature: (ZLOut<+Ljava/lang/Integer;>;)I
|
||||
|
||||
@JvmSuppressWildcards(true)
|
||||
fun bar(x: Boolean, y: In<Long>, z: @JvmSuppressWildcards(false) Long): Int = 1
|
||||
// method: PrimitiveTypesKt::bar
|
||||
// generic signature: (ZLIn<Ljava/lang/Long;>;J)I
|
||||
+12
@@ -35,6 +35,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithJava"), Pattern.compile("^([^\\.]+)$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("allWildcardsOnClass")
|
||||
public void testAllWildcardsOnClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/allWildcardsOnClass/");
|
||||
doTestWithJava(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("annotatedSamFunExpression")
|
||||
public void testAnnotatedSamFunExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/annotatedSamFunExpression/");
|
||||
@@ -59,6 +65,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege
|
||||
doTestWithJava(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("covariantOverrideWithDeclarationSiteProjection")
|
||||
public void testCovariantOverrideWithDeclarationSiteProjection() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/covariantOverrideWithDeclarationSiteProjection/");
|
||||
doTestWithJava(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("deprecatedFieldForObject")
|
||||
public void testDeprecatedFieldForObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/deprecatedFieldForObject/");
|
||||
|
||||
@@ -373,6 +373,33 @@ public class WriteSignatureTestGenerated extends AbstractWriteSignatureTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/writeSignature/declarationSiteVariance/jvmWildcardAnnotations")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class JvmWildcardAnnotations extends AbstractWriteSignatureTest {
|
||||
public void testAllFilesPresentInJvmWildcardAnnotations() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/writeSignature/declarationSiteVariance/jvmWildcardAnnotations"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("onFunction.kt")
|
||||
public void testOnFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/jvmWildcardAnnotations/onFunction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("onTypes.kt")
|
||||
public void testOnTypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/jvmWildcardAnnotations/onTypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("primitiveTypes.kt")
|
||||
public void testPrimitiveTypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/jvmWildcardAnnotations/primitiveTypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -99,3 +99,24 @@ internal annotation class JvmVersion(public val minimum: Int = 6, public val max
|
||||
@MustBeDocumented
|
||||
public annotation class JvmField
|
||||
|
||||
/**
|
||||
* Instructs compiler to generate or omit wildcards for type arguments corresponding to parameters with declaration-site variance.
|
||||
* If the innermost applied @JvmSuppressWildcards has suppress=true, the type is generated as without wildcards.
|
||||
* If the innermost applied @JvmSuppressWildcards has suppress=false, the type is generated as with wildcards.
|
||||
*
|
||||
* It may be helpful only if declaration seems to be inconvenient to use from Java.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.TYPE)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@MustBeDocumented
|
||||
annotation class JvmSuppressWildcards(val suppress: Boolean = true)
|
||||
|
||||
/**
|
||||
* Instructs compiler to generate wildcard for annotated type arguments corresponding to parameters with declaration-site variance.
|
||||
*
|
||||
* It may be helpful only if declaration seems to be inconvenient to use from Java without wildcard.
|
||||
*/
|
||||
@Target(AnnotationTarget.TYPE)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@MustBeDocumented
|
||||
annotation class JvmWildcard
|
||||
|
||||
Reference in New Issue
Block a user