Reflect Kotlin's declaration-site variance in Java signatures
This commit is contained in:
@@ -260,10 +260,12 @@ public class BothSignatureWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeTypeArgument(Variance variance) {
|
public void writeTypeArgument(Variance projectionKindForKotlin, Variance projectionKindForJava) {
|
||||||
JetSignatureVariance jsVariance = toJetSignatureVariance(variance);
|
push(signatureVisitor().visitTypeArgument(
|
||||||
push(signatureVisitor().visitTypeArgument(jsVariance.getChar()));
|
toJetSignatureVariance(projectionKindForJava).getChar()
|
||||||
jetSignatureWriter.visitTypeArgument(jsVariance);
|
));
|
||||||
|
|
||||||
|
jetSignatureWriter.visitTypeArgument(toJetSignatureVariance(projectionKindForKotlin));
|
||||||
generic = true;
|
generic = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
}
|
}
|
||||||
return AsmTypeConstants.OBJECT_TYPE;
|
return AsmTypeConstants.OBJECT_TYPE;
|
||||||
}
|
}
|
||||||
return mapType(jetType, signatureVisitor, JetTypeMapperMode.VALUE);
|
return mapType(jetType, signatureVisitor, JetTypeMapperMode.VALUE, Variance.OUT_VARIANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -209,6 +209,16 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Type mapType(JetType jetType, @Nullable BothSignatureWriter signatureVisitor, @NotNull JetTypeMapperMode kind) {
|
public Type mapType(JetType jetType, @Nullable BothSignatureWriter signatureVisitor, @NotNull JetTypeMapperMode kind) {
|
||||||
|
return mapType(jetType, signatureVisitor, kind, Variance.INVARIANT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Type mapType(
|
||||||
|
JetType jetType,
|
||||||
|
@Nullable BothSignatureWriter signatureVisitor,
|
||||||
|
@NotNull JetTypeMapperMode kind,
|
||||||
|
@NotNull Variance howThisTypeIsUsed
|
||||||
|
) {
|
||||||
Type known = null;
|
Type known = null;
|
||||||
DeclarationDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
|
DeclarationDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
|
||||||
|
|
||||||
@@ -220,10 +230,10 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
|
|
||||||
if (known != null) {
|
if (known != null) {
|
||||||
if (kind == JetTypeMapperMode.VALUE) {
|
if (kind == JetTypeMapperMode.VALUE) {
|
||||||
return mapKnownAsmType(jetType, known, signatureVisitor);
|
return mapKnownAsmType(jetType, known, signatureVisitor, howThisTypeIsUsed);
|
||||||
}
|
}
|
||||||
else if (kind == JetTypeMapperMode.TYPE_PARAMETER) {
|
else if (kind == JetTypeMapperMode.TYPE_PARAMETER) {
|
||||||
return mapKnownAsmType(jetType, boxType(known), signatureVisitor);
|
return mapKnownAsmType(jetType, boxType(known), signatureVisitor, howThisTypeIsUsed);
|
||||||
}
|
}
|
||||||
else if (kind == JetTypeMapperMode.TRAIT_IMPL) {
|
else if (kind == JetTypeMapperMode.TRAIT_IMPL) {
|
||||||
throw new IllegalStateException("TRAIT_IMPL is not possible for " + jetType);
|
throw new IllegalStateException("TRAIT_IMPL is not possible for " + jetType);
|
||||||
@@ -234,7 +244,7 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
// TODO: enable and fix tests
|
// TODO: enable and fix tests
|
||||||
//throw new IllegalStateException("must not map known type to IMPL when not compiling builtins: " + jetType);
|
//throw new IllegalStateException("must not map known type to IMPL when not compiling builtins: " + jetType);
|
||||||
}
|
}
|
||||||
return mapKnownAsmType(jetType, known, signatureVisitor);
|
return mapKnownAsmType(jetType, known, signatureVisitor, howThisTypeIsUsed);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("unknown kind: " + kind);
|
throw new IllegalStateException("unknown kind: " + kind);
|
||||||
@@ -297,7 +307,7 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
}
|
}
|
||||||
boolean forceReal = KotlinToJavaTypesMap.getInstance().isForceReal(name);
|
boolean forceReal = KotlinToJavaTypesMap.getInstance().isForceReal(name);
|
||||||
|
|
||||||
writeGenericType(signatureVisitor, asmType, jetType, forceReal);
|
writeGenericType(signatureVisitor, asmType, jetType, forceReal, howThisTypeIsUsed);
|
||||||
|
|
||||||
checkValidType(asmType);
|
checkValidType(asmType);
|
||||||
return asmType;
|
return asmType;
|
||||||
@@ -334,28 +344,68 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
parentDeclarationElement != null ? parentDeclarationElement.getText() : "null");
|
parentDeclarationElement != null ? parentDeclarationElement.getText() : "null");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeGenericType(BothSignatureWriter signatureVisitor, Type asmType, JetType jetType, boolean forceReal) {
|
private void writeGenericType(
|
||||||
|
BothSignatureWriter signatureVisitor,
|
||||||
|
Type asmType,
|
||||||
|
JetType jetType,
|
||||||
|
boolean forceReal,
|
||||||
|
Variance howThisTypeIsUsed
|
||||||
|
) {
|
||||||
if (signatureVisitor != null) {
|
if (signatureVisitor != null) {
|
||||||
String kotlinTypeName = getKotlinTypeNameForSignature(jetType, asmType);
|
String kotlinTypeName = getKotlinTypeNameForSignature(jetType, asmType);
|
||||||
signatureVisitor.writeClassBegin(asmType.getInternalName(), jetType.isNullable(), forceReal, kotlinTypeName);
|
signatureVisitor.writeClassBegin(asmType.getInternalName(), jetType.isNullable(), forceReal, kotlinTypeName);
|
||||||
for (TypeProjection proj : jetType.getArguments()) {
|
|
||||||
// TODO: +-
|
List<TypeProjection> arguments = jetType.getArguments();
|
||||||
signatureVisitor.writeTypeArgument(proj.getProjectionKind());
|
for (TypeParameterDescriptor parameter : jetType.getConstructor().getParameters()) {
|
||||||
mapType(proj.getType(), signatureVisitor, JetTypeMapperMode.TYPE_PARAMETER);
|
TypeProjection argument = arguments.get(parameter.getIndex());
|
||||||
|
|
||||||
|
Variance projectionKindForKotlin = argument.getProjectionKind();
|
||||||
|
Variance projectionKindForJava = getEffectiveVariance(
|
||||||
|
parameter.getVariance(),
|
||||||
|
projectionKindForKotlin,
|
||||||
|
howThisTypeIsUsed
|
||||||
|
);
|
||||||
|
signatureVisitor.writeTypeArgument(projectionKindForKotlin, projectionKindForJava);
|
||||||
|
|
||||||
|
mapType(argument.getType(), signatureVisitor, JetTypeMapperMode.TYPE_PARAMETER);
|
||||||
signatureVisitor.writeTypeArgumentEnd();
|
signatureVisitor.writeTypeArgumentEnd();
|
||||||
}
|
}
|
||||||
signatureVisitor.writeClassEnd();
|
signatureVisitor.writeClassEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type mapKnownAsmType(JetType jetType, Type asmType, @Nullable BothSignatureWriter signatureVisitor) {
|
private static Variance getEffectiveVariance(Variance parameterVariance, Variance projectionKind, Variance howThisTypeIsUsed) {
|
||||||
|
// Return type must not contain wildcards
|
||||||
|
if (howThisTypeIsUsed == Variance.OUT_VARIANCE) return projectionKind;
|
||||||
|
|
||||||
|
if (parameterVariance == Variance.INVARIANT) {
|
||||||
|
return projectionKind;
|
||||||
|
}
|
||||||
|
if (projectionKind == Variance.INVARIANT) {
|
||||||
|
return parameterVariance;
|
||||||
|
}
|
||||||
|
if (parameterVariance == projectionKind) {
|
||||||
|
return parameterVariance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In<out X> = In<*>
|
||||||
|
// Out<in X> = Out<*>
|
||||||
|
return Variance.OUT_VARIANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type mapKnownAsmType(
|
||||||
|
JetType jetType,
|
||||||
|
Type asmType,
|
||||||
|
@Nullable BothSignatureWriter signatureVisitor,
|
||||||
|
@NotNull Variance howThisTypeIsUsed
|
||||||
|
) {
|
||||||
if (signatureVisitor != null) {
|
if (signatureVisitor != null) {
|
||||||
if (jetType.getArguments().isEmpty()) {
|
if (jetType.getArguments().isEmpty()) {
|
||||||
String kotlinTypeName = getKotlinTypeNameForSignature(jetType, asmType);
|
String kotlinTypeName = getKotlinTypeNameForSignature(jetType, asmType);
|
||||||
signatureVisitor.writeAsmType(asmType, jetType.isNullable(), kotlinTypeName);
|
signatureVisitor.writeAsmType(asmType, jetType.isNullable(), kotlinTypeName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
writeGenericType(signatureVisitor, asmType, jetType, false);
|
writeGenericType(signatureVisitor, asmType, jetType, false, howThisTypeIsUsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checkValidType(asmType);
|
checkValidType(asmType);
|
||||||
@@ -695,7 +745,7 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
signatureWriter.writeParametersEnd();
|
signatureWriter.writeParametersEnd();
|
||||||
|
|
||||||
signatureWriter.writeReturnType();
|
signatureWriter.writeReturnType();
|
||||||
mapType(descriptor.getType(), signatureWriter, JetTypeMapperMode.VALUE);
|
mapType(descriptor.getType(), signatureWriter, JetTypeMapperMode.VALUE, Variance.OUT_VARIANCE);
|
||||||
signatureWriter.writeReturnTypeEnd();
|
signatureWriter.writeReturnTypeEnd();
|
||||||
|
|
||||||
JvmMethodSignature jvmMethodSignature = signatureWriter.makeJvmMethodSignature(name);
|
JvmMethodSignature jvmMethodSignature = signatureWriter.makeJvmMethodSignature(name);
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ class Question {
|
|||||||
// id2 is to prevent java type parameter type inference
|
// id2 is to prevent java type parameter type inference
|
||||||
static <T> T id2(T p) { return p; }
|
static <T> T id2(T p) { return p; }
|
||||||
{
|
{
|
||||||
java.util.List<String> s = id2(namespace.id(null));
|
java.util.List<? extends String> s = id2(namespace.id(null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ fun foo(p: List<CharSequence>) = 1
|
|||||||
|
|
||||||
// method: namespace::foo
|
// method: namespace::foo
|
||||||
// jvm signature: (Ljava/util/List;)I
|
// jvm signature: (Ljava/util/List;)I
|
||||||
// generic signature: (Ljava/util/List<Ljava/lang/CharSequence;>;)I
|
// generic signature: (Ljava/util/List<+Ljava/lang/CharSequence;>;)I
|
||||||
// kotlin signature: (Ljet/List<Ljava/lang/CharSequence;>;)I // TODO: skip Kotlin signature
|
// kotlin signature: (Ljet/List<Ljava/lang/CharSequence;>;)I // TODO: skip Kotlin signature
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ fun listOfStar(): List<*> = throw Exception()
|
|||||||
|
|
||||||
// method: namespace::listOfStar
|
// method: namespace::listOfStar
|
||||||
// jvm signature: ()Ljava/util/List;
|
// jvm signature: ()Ljava/util/List;
|
||||||
// generic signature: ()Ljava/util/List<+Ljava/lang/Object;>;
|
// generic signature: ()Ljava/util/List<Ljava/lang/Object;>;
|
||||||
// kotlin signature: ()Ljet/List<+?Ljava/lang/Object;>;
|
// kotlin signature: ()Ljet/List<?Ljava/lang/Object;>;
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ class TestingKotlinCollections(val arguments: Collection<String>)
|
|||||||
|
|
||||||
// method: TestingKotlinCollections::<init>
|
// method: TestingKotlinCollections::<init>
|
||||||
// jvm signature: (Ljava/util/Collection;)V
|
// jvm signature: (Ljava/util/Collection;)V
|
||||||
// generic signature: (Ljava/util/Collection<Ljava/lang/String;>;)V
|
// generic signature: (Ljava/util/Collection<+Ljava/lang/String;>;)V
|
||||||
// kotlin signature: (Ljet/Collection<Ljava/lang/String;>;)null
|
// kotlin signature: (Ljet/Collection<Ljava/lang/String;>;)null
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class M<in K, out V>
|
||||||
|
|
||||||
|
class X
|
||||||
|
|
||||||
|
fun f(m: M<X, X>): M<X, X> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: (LM;)LM;
|
||||||
|
// generic signature: (LM<-LX;+LX;>;)LM<LX;LX;>;
|
||||||
|
// kotlin signature: (LM<LX;LX;>;)LM<LX;LX;>;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
class In<in T>
|
||||||
|
|
||||||
|
fun f(p: In<String>) {}
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: (LIn;)V
|
||||||
|
// generic signature: (LIn<-Ljava/lang/String;>;)V
|
||||||
|
// kotlin signature: (LIn<Ljava/lang/String;>;)V
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
class In<in T>
|
||||||
|
|
||||||
|
fun f(): In<String> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: ()LIn;
|
||||||
|
// generic signature: ()LIn<Ljava/lang/String;>;
|
||||||
|
// kotlin signature: ()LIn<Ljava/lang/String;>;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class In<in T>
|
||||||
|
class X
|
||||||
|
|
||||||
|
fun f(p: In<In<X>>) {}
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: (LIn;)V
|
||||||
|
// generic signature: (LIn<-LIn<-LX;>;>;)V
|
||||||
|
// kotlin signature: (LIn<LIn<LX;>;>;)V
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class In<in T>
|
||||||
|
class X
|
||||||
|
|
||||||
|
fun f(): In<In<X>> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: ()LIn;
|
||||||
|
// generic signature: ()LIn<LIn<-LX;>;>;
|
||||||
|
// kotlin signature: ()LIn<LIn<LX;>;>;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class In<in T>
|
||||||
|
class Out<out T>
|
||||||
|
class X
|
||||||
|
|
||||||
|
fun f(p: In<Out<X>>) {}
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: (LIn;)V
|
||||||
|
// generic signature: (LIn<-LOut<+LX;>;>;)V
|
||||||
|
// kotlin signature: (LIn<LOut<LX;>;>;)V
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class In<in T>
|
||||||
|
class Out<out T>
|
||||||
|
class X
|
||||||
|
|
||||||
|
fun f(): In<Out<X>> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: ()LIn;
|
||||||
|
// generic signature: ()LIn<LOut<+LX;>;>;
|
||||||
|
// kotlin signature: ()LIn<LOut<LX;>;>;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
fun f(p: List<String>) {}
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: (Ljava/util/List;)V
|
||||||
|
// generic signature: (Ljava/util/List<+Ljava/lang/String;>;)V
|
||||||
|
// kotlin signature: (Ljet/List<Ljava/lang/String;>;)V
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
fun f(): List<String> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: ()Ljava/util/List;
|
||||||
|
// generic signature: ()Ljava/util/List<Ljava/lang/String;>;
|
||||||
|
// kotlin signature: ()Ljet/List<Ljava/lang/String;>;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class In<in T>
|
||||||
|
class Out<out T>
|
||||||
|
class X
|
||||||
|
|
||||||
|
fun f(p: Out<In<X>>) {}
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: (LOut;)V
|
||||||
|
// generic signature: (LOut<+LIn<-LX;>;>;)V
|
||||||
|
// kotlin signature: (LOut<LIn<LX;>;>;)V
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class In<in T>
|
||||||
|
class Out<out T>
|
||||||
|
class X
|
||||||
|
|
||||||
|
fun f(): Out<In<X>> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: ()LOut;
|
||||||
|
// generic signature: ()LOut<LIn<-LX;>;>;
|
||||||
|
// kotlin signature: ()LOut<LIn<LX;>;>;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class Out<out T>
|
||||||
|
class X
|
||||||
|
|
||||||
|
fun f(p: Out<Out<X>>) {}
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: (LOut;)V
|
||||||
|
// generic signature: (LOut<+LOut<+LX;>;>;)V
|
||||||
|
// kotlin signature: (LOut<LOut<LX;>;>;)V
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
class Out<out T>
|
||||||
|
class X
|
||||||
|
|
||||||
|
// Why we want this to be translated to 'Out<Out<? extends X>> f()' instead of 'Out<Out<X>> f()'
|
||||||
|
// There are two instantiations of 'In' in this test: outer Out<...> and inner Out<X>
|
||||||
|
// So why do we want to put a wildcard on the inner one and not the outer?
|
||||||
|
// People don't want wildcards in return types, because they are _long_. So we try our best to remove wildcards where possible
|
||||||
|
// Not putting a wildcard on the outer occurrence is not imposing a restriction, actually it is removing one:
|
||||||
|
// anything that can be done with Out<? extends X> in Java can be done with Out<X>
|
||||||
|
// But omitting the wildcard on the inner occurrence is restrictive:
|
||||||
|
// one can add a List<String> to a List<List<? extends CharSequence>>,
|
||||||
|
// but not to a List<List<CharSequence>>,
|
||||||
|
// thus removing the wildcard would be restricting the use of the return value of the method, and we don't want do this.
|
||||||
|
fun f(): Out<Out<X>> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::f
|
||||||
|
// jvm signature: ()LOut;
|
||||||
|
// generic signature: ()LOut<LOut<+LX;>;>;
|
||||||
|
// kotlin signature: ()LOut<LOut<LX;>;>;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class M<in V>
|
||||||
|
class X
|
||||||
|
|
||||||
|
val p: M<X> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::getP
|
||||||
|
// jvm signature: ()LM;
|
||||||
|
// generic signature: ()LM<LX;>;
|
||||||
|
// kotlin signature: null
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class M<out V>
|
||||||
|
class X
|
||||||
|
|
||||||
|
val p: M<X> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::getP
|
||||||
|
// jvm signature: ()LM;
|
||||||
|
// generic signature: ()LM<LX;>;
|
||||||
|
// kotlin signature: null
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class M<in K, out V>
|
||||||
|
class X
|
||||||
|
|
||||||
|
val p: M<X, X> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::getP
|
||||||
|
// jvm signature: ()LM;
|
||||||
|
// generic signature: ()LM<LX;LX;>;
|
||||||
|
// kotlin signature: null
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class M<in V>
|
||||||
|
class X
|
||||||
|
|
||||||
|
var p: M<X> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::setP
|
||||||
|
// jvm signature: (LM;)V
|
||||||
|
// generic signature: (LM<-LX;>;)V
|
||||||
|
// kotlin signature: null
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class M<out V>
|
||||||
|
class X
|
||||||
|
|
||||||
|
var p: M<X> = throw Exception()
|
||||||
|
|
||||||
|
// method: namespace::setP
|
||||||
|
// jvm signature: (LM;)V
|
||||||
|
// generic signature: (LM<+LX;>;)V
|
||||||
|
// kotlin signature: null
|
||||||
Reference in New Issue
Block a user