Reflect Kotlin's declaration-site variance in Java signatures

This commit is contained in:
Andrey Breslav
2012-11-21 22:30:59 +04:00
parent da2f886bee
commit 889ea107f6
24 changed files with 243 additions and 22 deletions
@@ -260,10 +260,12 @@ public class BothSignatureWriter {
}
}
public void writeTypeArgument(Variance variance) {
JetSignatureVariance jsVariance = toJetSignatureVariance(variance);
push(signatureVisitor().visitTypeArgument(jsVariance.getChar()));
jetSignatureWriter.visitTypeArgument(jsVariance);
public void writeTypeArgument(Variance projectionKindForKotlin, Variance projectionKindForJava) {
push(signatureVisitor().visitTypeArgument(
toJetSignatureVariance(projectionKindForJava).getChar()
));
jetSignatureWriter.visitTypeArgument(toJetSignatureVariance(projectionKindForKotlin));
generic = true;
}
@@ -184,7 +184,7 @@ public class JetTypeMapper extends BindingTraceAware {
}
return AsmTypeConstants.OBJECT_TYPE;
}
return mapType(jetType, signatureVisitor, JetTypeMapperMode.VALUE);
return mapType(jetType, signatureVisitor, JetTypeMapperMode.VALUE, Variance.OUT_VARIANCE);
}
@NotNull
@@ -209,6 +209,16 @@ public class JetTypeMapper extends BindingTraceAware {
@NotNull
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;
DeclarationDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
@@ -220,10 +230,10 @@ public class JetTypeMapper extends BindingTraceAware {
if (known != null) {
if (kind == JetTypeMapperMode.VALUE) {
return mapKnownAsmType(jetType, known, signatureVisitor);
return mapKnownAsmType(jetType, known, signatureVisitor, howThisTypeIsUsed);
}
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) {
throw new IllegalStateException("TRAIT_IMPL is not possible for " + jetType);
@@ -234,7 +244,7 @@ public class JetTypeMapper extends BindingTraceAware {
// TODO: enable and fix tests
//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 {
throw new IllegalStateException("unknown kind: " + kind);
@@ -297,7 +307,7 @@ public class JetTypeMapper extends BindingTraceAware {
}
boolean forceReal = KotlinToJavaTypesMap.getInstance().isForceReal(name);
writeGenericType(signatureVisitor, asmType, jetType, forceReal);
writeGenericType(signatureVisitor, asmType, jetType, forceReal, howThisTypeIsUsed);
checkValidType(asmType);
return asmType;
@@ -334,28 +344,68 @@ public class JetTypeMapper extends BindingTraceAware {
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) {
String kotlinTypeName = getKotlinTypeNameForSignature(jetType, asmType);
signatureVisitor.writeClassBegin(asmType.getInternalName(), jetType.isNullable(), forceReal, kotlinTypeName);
for (TypeProjection proj : jetType.getArguments()) {
// TODO: +-
signatureVisitor.writeTypeArgument(proj.getProjectionKind());
mapType(proj.getType(), signatureVisitor, JetTypeMapperMode.TYPE_PARAMETER);
List<TypeProjection> arguments = jetType.getArguments();
for (TypeParameterDescriptor parameter : jetType.getConstructor().getParameters()) {
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.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 (jetType.getArguments().isEmpty()) {
String kotlinTypeName = getKotlinTypeNameForSignature(jetType, asmType);
signatureVisitor.writeAsmType(asmType, jetType.isNullable(), kotlinTypeName);
}
else {
writeGenericType(signatureVisitor, asmType, jetType, false);
writeGenericType(signatureVisitor, asmType, jetType, false, howThisTypeIsUsed);
}
}
checkValidType(asmType);
@@ -695,7 +745,7 @@ public class JetTypeMapper extends BindingTraceAware {
signatureWriter.writeParametersEnd();
signatureWriter.writeReturnType();
mapType(descriptor.getType(), signatureWriter, JetTypeMapperMode.VALUE);
mapType(descriptor.getType(), signatureWriter, JetTypeMapperMode.VALUE, Variance.OUT_VARIANCE);
signatureWriter.writeReturnTypeEnd();
JvmMethodSignature jvmMethodSignature = signatureWriter.makeJvmMethodSignature(name);
@@ -2,6 +2,6 @@ class Question {
// id2 is to prevent java type parameter type inference
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
// 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
@@ -3,5 +3,5 @@ fun listOfStar(): List<*> = throw Exception()
// method: namespace::listOfStar
// jvm signature: ()Ljava/util/List;
// generic signature: ()Ljava/util/List<+Ljava/lang/Object;>;
// kotlin signature: ()Ljet/List<+?Ljava/lang/Object;>;
// generic signature: ()Ljava/util/List<Ljava/lang/Object;>;
// kotlin signature: ()Ljet/List<?Ljava/lang/Object;>;
@@ -3,5 +3,5 @@ class TestingKotlinCollections(val arguments: Collection<String>)
// method: TestingKotlinCollections::<init>
// 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
@@ -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