Change default rules for declaration-site wildcards

Mostly this commit is about skipping wildcards that are redundant in some sense.
The motivation is that they looks `long` in Java code.

There are basically two important parts: return types and value parameters.

1. For return types default behaviour is skipping all declaration-site wildcards.
The intuition behind this rule is simple: return types are basically used in subtype position
(as an argument for another call), and here everything works well in case of 'out'-variance.
For example we have 'Out<Out<T>>>' as subtype both for 'Out<Out<T>>>' and 'Out<? extends Out<? extends T>>>',
so values of such type is more flexible in contrast to `Out<? extends Out<? extends T>>>` that could be used only
for the second case.

But we have choosen to treat `in`-variance in a different way: argument itself
should be rendered without wildcard while nested arguments are rendered by the rules
described further (see second part).

For example: 'In<Out<OpenClass>>' will have generic signature 'In<Out<? extends OpenClass>>'.
If we omit all wildcards here, then value of type 'In<Out<OpenClass>>'
will be impossible to use as argument for function expecting 'In<? super Out<? extends Derived>>'
where Derived <: OpenClass (you can check it manually :]).

And this exception should not be very inconvinient because in-variance is rather rare.

2. For value parameters we decided to skip wildcards if it doesn't make obtained signature weaker
in a sense of set of acceptable arguments.

More precisely:
    a. We write wildcard for 'Out<T>' iff T ``can have subtypes ignoring nullability''
    b. We write wildcard for 'In<T>' iff T is not equal to it's class upper bound (ignoring nullability again)

Definition of ``can have subtypes ignoring nullability'' is straightforward and you can see it in commit.

 #KT-9801 Fixed
 #KT-9890 Fixed
This commit is contained in:
Denis Zharkov
2015-11-23 19:45:48 +03:00
parent 1731cb8b40
commit 406e31f54a
29 changed files with 620 additions and 60 deletions
@@ -325,12 +325,12 @@ public class JetTypeMapper {
//noinspection ConstantConditions //noinspection ConstantConditions
return mapType(descriptor.getReturnType(), sw, TypeMappingMode.GENERIC_TYPE); return mapType(descriptor.getReturnType(), sw, TypeMappingMode.GENERIC_TYPE);
} }
else if (DescriptorUtils.isAnnotationClass(descriptor.getContainingDeclaration())) {
//noinspection ConstantConditions
return mapType(descriptor.getReturnType(), sw, TypeMappingMode.VALUE_FOR_ANNOTATION);
}
else { else {
return mapType(returnType, sw, TypeMappingMode.DEFAULT, Variance.OUT_VARIANCE); TypeMappingMode mappingMode = TypeMappingMode.getOptimalModeForReturnType(
returnType,
/* isAnnotationMethod = */ DescriptorUtils.isAnnotationClass(descriptor.getContainingDeclaration()));
return mapType(returnType, sw, mappingMode);
} }
} }
@@ -379,23 +379,17 @@ public class JetTypeMapper {
return mapType(descriptor.getDefaultType()); return mapType(descriptor.getDefaultType());
} }
@NotNull
private Type mapType(@NotNull KotlinType jetType, @Nullable BothSignatureWriter signatureVisitor, @NotNull TypeMappingMode mode) {
return mapType(jetType, signatureVisitor, mode, Variance.INVARIANT);
}
@NotNull @NotNull
private Type mapType( private Type mapType(
@NotNull KotlinType jetType, @NotNull KotlinType jetType,
@Nullable BothSignatureWriter signatureVisitor, @Nullable BothSignatureWriter signatureVisitor,
@NotNull TypeMappingMode mode, @NotNull TypeMappingMode mode
@NotNull Variance howThisTypeIsUsed
) { ) {
Type builtinType = mapBuiltinType(jetType); Type builtinType = mapBuiltinType(jetType);
if (builtinType != null) { if (builtinType != null) {
Type asmType = mode.getNeedPrimitiveBoxing() ? boxType(builtinType) : builtinType; Type asmType = mode.getNeedPrimitiveBoxing() ? boxType(builtinType) : builtinType;
writeGenericType(jetType, asmType, signatureVisitor, howThisTypeIsUsed, mode); writeGenericType(jetType, asmType, signatureVisitor, mode);
return asmType; return asmType;
} }
@@ -440,7 +434,7 @@ public class JetTypeMapper {
arrayElementType = boxType(mapType(memberType, mode)); arrayElementType = boxType(mapType(memberType, mode));
if (signatureVisitor != null) { if (signatureVisitor != null) {
signatureVisitor.writeArrayType(); signatureVisitor.writeArrayType();
mapType(memberType, signatureVisitor, mode.toGenericArgumentMode()); mapType(memberType, signatureVisitor, mode.toGenericArgumentMode(memberProjection.getProjectionKind()));
signatureVisitor.writeArrayEnd(); signatureVisitor.writeArrayEnd();
} }
} }
@@ -452,7 +446,7 @@ public class JetTypeMapper {
Type asmType = mode.isForAnnotationParameter() && KotlinBuiltIns.isKClass((ClassDescriptor) descriptor) ? Type asmType = mode.isForAnnotationParameter() && KotlinBuiltIns.isKClass((ClassDescriptor) descriptor) ?
AsmTypes.JAVA_CLASS_TYPE : AsmTypes.JAVA_CLASS_TYPE :
computeAsmType((ClassDescriptor) descriptor.getOriginal()); computeAsmType((ClassDescriptor) descriptor.getOriginal());
writeGenericType(jetType, asmType, signatureVisitor, howThisTypeIsUsed, mode); writeGenericType(jetType, asmType, signatureVisitor, mode);
return asmType; return asmType;
} }
@@ -573,7 +567,6 @@ public class JetTypeMapper {
@NotNull KotlinType type, @NotNull KotlinType type,
@NotNull Type asmType, @NotNull Type asmType,
@Nullable BothSignatureWriter signatureVisitor, @Nullable BothSignatureWriter signatureVisitor,
@NotNull Variance howThisTypeIsUsed,
@NotNull TypeMappingMode mode @NotNull TypeMappingMode mode
) { ) {
if (signatureVisitor != null) { if (signatureVisitor != null) {
@@ -600,17 +593,14 @@ public class JetTypeMapper {
writeGenericArguments( writeGenericArguments(
signatureVisitor, signatureVisitor,
outermostInnerType.getArguments(), outermostClass.getDeclaredTypeParameters(), outermostInnerType.getArguments(), outermostClass.getDeclaredTypeParameters(), mode);
howThisTypeIsUsed, mode);
for (PossiblyInnerType innerPart : innerTypesAsList.subList(1, innerTypesAsList.size())) { for (PossiblyInnerType innerPart : innerTypesAsList.subList(1, innerTypesAsList.size())) {
ClassDescriptor classDescriptor = innerPart.getClassDescriptor(); ClassDescriptor classDescriptor = innerPart.getClassDescriptor();
signatureVisitor.writeInnerClass(getJvmShortName(classDescriptor)); signatureVisitor.writeInnerClass(getJvmShortName(classDescriptor));
writeGenericArguments( writeGenericArguments(
signatureVisitor, innerPart.getArguments(), signatureVisitor, innerPart.getArguments(),
classDescriptor.getDeclaredTypeParameters(), classDescriptor.getDeclaredTypeParameters(), mode);
howThisTypeIsUsed, mode
);
} }
signatureVisitor.writeClassEnd(); signatureVisitor.writeClassEnd();
@@ -631,7 +621,6 @@ public class JetTypeMapper {
@NotNull BothSignatureWriter signatureVisitor, @NotNull BothSignatureWriter signatureVisitor,
@NotNull List<? extends TypeProjection> arguments, @NotNull List<? extends TypeProjection> arguments,
@NotNull List<? extends TypeParameterDescriptor> parameters, @NotNull List<? extends TypeParameterDescriptor> parameters,
@NotNull Variance howThisTypeIsUsed,
@NotNull TypeMappingMode mode @NotNull TypeMappingMode mode
) { ) {
for (Pair<? extends TypeParameterDescriptor, ? extends TypeProjection> item : CollectionsKt.zip(parameters, arguments)) { for (Pair<? extends TypeParameterDescriptor, ? extends TypeProjection> item : CollectionsKt.zip(parameters, arguments)) {
@@ -642,11 +631,13 @@ public class JetTypeMapper {
signatureVisitor.writeUnboundedWildcard(); signatureVisitor.writeUnboundedWildcard();
} }
else { else {
Variance projectionKind = Variance projectionKind = getVarianceForWildcard(parameter, argument, mode);
getEffectiveVariance(parameter.getVariance(), argument.getProjectionKind(), howThisTypeIsUsed, mode);
signatureVisitor.writeTypeArgument(projectionKind); signatureVisitor.writeTypeArgument(projectionKind);
mapType(argument.getType(), signatureVisitor, mode.toGenericArgumentMode()); mapType(argument.getType(), signatureVisitor,
mode.toGenericArgumentMode(
TypeMappingUtil.getEffectiveVariance(parameter.getVariance(), argument.getProjectionKind())));
signatureVisitor.writeTypeArgumentEnd(); signatureVisitor.writeTypeArgumentEnd();
} }
} }
@@ -670,21 +661,34 @@ public class JetTypeMapper {
}); });
} }
private static Variance getEffectiveVariance( @NotNull
Variance parameterVariance, private static Variance getVarianceForWildcard(
Variance projectionKind, @NotNull TypeParameterDescriptor parameter,
Variance howThisTypeIsUsed, @NotNull TypeProjection projection,
TypeMappingMode mode @NotNull TypeMappingMode mode
) { ) {
if (!mode.getWriteDeclarationSiteProjections() && projectionKind == Variance.INVARIANT) return Variance.INVARIANT; Variance projectionKind = projection.getProjectionKind();
// Return type must not contain wildcards if (mode.getSkipDeclarationSiteWildcards() && projectionKind == Variance.INVARIANT) {
if (howThisTypeIsUsed == Variance.OUT_VARIANCE) return projectionKind; return Variance.INVARIANT;
}
Variance parameterVariance = parameter.getVariance();
if (parameterVariance == Variance.INVARIANT) { if (parameterVariance == Variance.INVARIANT) {
return projectionKind; return projectionKind;
} }
if (projectionKind == Variance.INVARIANT) { if (projectionKind == Variance.INVARIANT) {
if (mode.getSkipDeclarationSiteWildcardsIfPossible() && !projection.isStarProjection()) {
if (parameterVariance == Variance.OUT_VARIANCE && TypeMappingUtil.isMostPreciseCovariantArgument(projection.getType())){
return Variance.INVARIANT;
}
if (parameterVariance == Variance.IN_VARIANCE
&& TypeMappingUtil.isMostPreciseContravariantArgument(projection.getType(), parameter)) {
return Variance.INVARIANT;
}
}
return parameterVariance; return parameterVariance;
} }
if (parameterVariance == projectionKind) { if (parameterVariance == projectionKind) {
@@ -1187,7 +1191,7 @@ public class JetTypeMapper {
private void writeParameter(@NotNull BothSignatureWriter sw, @NotNull JvmMethodParameterKind kind, @NotNull KotlinType type) { private void writeParameter(@NotNull BothSignatureWriter sw, @NotNull JvmMethodParameterKind kind, @NotNull KotlinType type) {
sw.writeParameterType(kind); sw.writeParameterType(kind);
mapType(type, sw, TypeMappingMode.DEFAULT); mapType(type, sw, TypeMappingMode.getOptimalModeForValueParameter(type));
sw.writeParameterTypeEnd(); sw.writeParameterTypeEnd();
} }
@@ -16,11 +16,17 @@
package org.jetbrains.kotlin.codegen.state package org.jetbrains.kotlin.codegen.state
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
internal class TypeMappingMode private constructor( internal class TypeMappingMode private constructor(
val needPrimitiveBoxing: Boolean = false, val needPrimitiveBoxing: Boolean = false,
val isForAnnotationParameter: Boolean = false, val isForAnnotationParameter: Boolean = false,
val writeDeclarationSiteProjections: Boolean = true, // Here DeclarationSiteWildcards means wildcard generated because of declaration-site variance
val genericArgumentMode: TypeMappingMode? = null val skipDeclarationSiteWildcards: Boolean = false,
val skipDeclarationSiteWildcardsIfPossible: Boolean = false,
private val genericArgumentMode: TypeMappingMode? = null,
private val genericContravariantArgumentMode: TypeMappingMode? = genericArgumentMode
) { ) {
companion object { companion object {
/** /**
@@ -35,13 +41,12 @@ internal class TypeMappingMode private constructor(
@JvmField @JvmField
val DEFAULT = TypeMappingMode(genericArgumentMode = GENERIC_TYPE) val DEFAULT = TypeMappingMode(genericArgumentMode = GENERIC_TYPE)
/** /**
* kotlin.Int is mapped to Ljava/lang/Integer; * kotlin.Int is mapped to Ljava/lang/Integer;
* No projections allowed in immediate arguments * No projections allowed in immediate arguments
*/ */
@JvmField @JvmField
val SUPER_TYPE = TypeMappingMode(needPrimitiveBoxing = true, writeDeclarationSiteProjections = false, genericArgumentMode = GENERIC_TYPE) val SUPER_TYPE = TypeMappingMode(needPrimitiveBoxing = true, skipDeclarationSiteWildcards = true, genericArgumentMode = GENERIC_TYPE)
/** /**
* kotlin.reflect.KClass mapped to java.lang.Class * kotlin.reflect.KClass mapped to java.lang.Class
@@ -50,9 +55,49 @@ internal class TypeMappingMode private constructor(
@JvmField @JvmField
val VALUE_FOR_ANNOTATION = TypeMappingMode( val VALUE_FOR_ANNOTATION = TypeMappingMode(
isForAnnotationParameter = true, isForAnnotationParameter = true,
genericArgumentMode = TypeMappingMode(isForAnnotationParameter = true, needPrimitiveBoxing = true)) genericArgumentMode = TypeMappingMode(isForAnnotationParameter = true, needPrimitiveBoxing = true, genericArgumentMode = GENERIC_TYPE))
@JvmStatic
fun getOptimalModeForValueParameter(
type: KotlinType
) = getOptimalModeForSignaturePart(type, isForAnnotationParameter = false, canBeUsedInSupertypePosition = true)
@JvmStatic
fun getOptimalModeForReturnType(
type: KotlinType,
isAnnotationMethod: Boolean
) = getOptimalModeForSignaturePart(type, isForAnnotationParameter = isAnnotationMethod, canBeUsedInSupertypePosition = false)
private fun getOptimalModeForSignaturePart(
type: KotlinType,
isForAnnotationParameter: Boolean,
canBeUsedInSupertypePosition: Boolean
): TypeMappingMode {
if (type.arguments.isEmpty()) return DEFAULT
val contravariantArgumentMode =
if (!canBeUsedInSupertypePosition)
TypeMappingMode(
needPrimitiveBoxing = true,
isForAnnotationParameter = isForAnnotationParameter,
skipDeclarationSiteWildcards = false,
skipDeclarationSiteWildcardsIfPossible = true)
else
null
return TypeMappingMode(
needPrimitiveBoxing = true,
isForAnnotationParameter = isForAnnotationParameter,
skipDeclarationSiteWildcards = !canBeUsedInSupertypePosition,
skipDeclarationSiteWildcardsIfPossible = true,
genericContravariantArgumentMode = contravariantArgumentMode)
}
} }
fun toGenericArgumentMode(): TypeMappingMode = genericArgumentMode ?: this fun toGenericArgumentMode(effectiveVariance: Variance): TypeMappingMode =
when (effectiveVariance) {
Variance.IN_VARIANCE -> genericContravariantArgumentMode ?: this
else -> genericArgumentMode ?: this
}
} }
@@ -0,0 +1,69 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:JvmName("TypeMappingUtil")
package org.jetbrains.kotlin.codegen.state
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
fun KotlinType.isMostPreciseContravariantArgument(parameter: TypeParameterDescriptor): Boolean =
// TODO: probably class upper bound should be used
KotlinBuiltIns.isAnyOrNullableAny(this)
fun KotlinType.isMostPreciseCovariantArgument(): Boolean = !canHaveSubtypesIgnoringNullability()
private fun KotlinType.canHaveSubtypesIgnoringNullability(): Boolean {
val constructor = constructor
val descriptor = constructor.declarationDescriptor
when (descriptor) {
is TypeParameterDescriptor -> return true
is ClassDescriptor -> if (descriptor.modality.isOverridable) return true
}
for ((parameter, argument) in constructor.parameters.zip(arguments)) {
if (argument.isStarProjection) return true
val projectionKind = argument.projectionKind
val type = argument.type
val effectiveVariance = getEffectiveVariance(parameter.variance, projectionKind)
if (effectiveVariance == Variance.OUT_VARIANCE && !type.isMostPreciseCovariantArgument()) return true
if (effectiveVariance == Variance.IN_VARIANCE && !type.isMostPreciseContravariantArgument(parameter)) return true
}
return false
}
public fun getEffectiveVariance(parameterVariance: Variance, projectionKind: Variance): Variance {
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
}
@@ -0,0 +1,16 @@
interface A {
fun foo(): Collection<Any>
}
abstract class B : A {
override fun foo(): Collection<String> = null!!
}
fun box(): String {
val clazz = B::class.java
if (clazz.declaredMethods.first().genericReturnType.toString() != "java.util.Collection<java.lang.String>") return "fail 1"
if (clazz.methods.filter { it.name == "foo" }.size != 1) return "fail 2"
return "OK"
}
@@ -3,4 +3,4 @@ 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
@@ -6,4 +6,4 @@ fun f(m: M<X, X>): M<X, X> = throw Exception()
// method: FunctionTwoTypeParametersKt::f // method: FunctionTwoTypeParametersKt::f
// jvm signature: (LM;)LM; // jvm signature: (LM;)LM;
// generic signature: (LM<-LX;+LX;>;)LM<LX;LX;>; // generic signature: (LM<-LX;LX;>;)LM<LX;LX;>;
@@ -0,0 +1,40 @@
class Out<out T>
class In<in Z>
interface A<T, E> {
fun foo1(x: Out<T>): Out<T>
fun foo2(x: In<E>): In<E>
var prop: Out<T>
}
// method: A::foo1
// generic signature: (LOut<+TT;>;)LOut<TT;>;
// method: A::foo2
// generic signature: (LIn<-TE;>;)LIn<TE;>;
// method: A::getProp
// generic signature: ()LOut<TT;>;
// method: A::setProp
// generic signature: (LOut<+TT;>;)V
abstract class B : A<String, Any?> {
override final fun foo1(x: Out<String>): Out<String> = null!!
override final fun foo2(x: In<Any?>): In<Any?> = null!!
override final var prop: Out<String> = null!!
}
// method: B::foo1
// generic signature: (LOut<Ljava/lang/String;>;)LOut<Ljava/lang/String;>;
// method: B::foo2
// generic signature: (LIn<Ljava/lang/Object;>;)LIn<Ljava/lang/Object;>;
// method: B::getProp
// generic signature: ()LOut<Ljava/lang/String;>;
// method: B::setProp
// generic signature: (LOut<Ljava/lang/String;>;)V
@@ -6,4 +6,4 @@ fun f(p: In<Out<X>>) {}
// method: InOfOutInInPositionKt::f // method: InOfOutInInPositionKt::f
// jvm signature: (LIn;)V // jvm signature: (LIn;)V
// generic signature: (LIn<-LOut<+LX;>;>;)V // generic signature: (LIn<-LOut<LX;>;>;)V
@@ -6,4 +6,4 @@ fun f(): In<Out<X>> = throw Exception()
// method: InOfOutInOutPositionKt::f // method: InOfOutInOutPositionKt::f
// jvm signature: ()LIn; // jvm signature: ()LIn;
// generic signature: ()LIn<LOut<+LX;>;>; // generic signature: ()LIn<LOut<LX;>;>;
@@ -0,0 +1,51 @@
class OutPair<out X, out Y>
class In<in Z>
interface A {
fun foo1(): OutPair<String, Int>
fun foo2(): OutPair<CharSequence, Int>
fun foo3(): OutPair<OutPair<CharSequence, Number>, Number>
fun foo4(): In<String>
fun foo5(): In<Any>
val prop1: OutPair<String, Int>
val prop2: OutPair<CharSequence, Int>
}
// method: A::foo1
// generic signature: ()LOutPair<Ljava/lang/String;Ljava/lang/Integer;>;
// method: A::foo2
// generic signature: ()LOutPair<Ljava/lang/CharSequence;Ljava/lang/Integer;>;
// method: A::foo3
// generic signature: ()LOutPair<LOutPair<Ljava/lang/CharSequence;Ljava/lang/Number;>;Ljava/lang/Number;>;
// method: A::foo4
// generic signature: ()LIn<Ljava/lang/String;>;
// method: A::foo5
// generic signature: ()LIn<Ljava/lang/Object;>;
// method: A::getProp1
// generic signature: ()LOutPair<Ljava/lang/String;Ljava/lang/Integer;>;
// method: A::getProp2
// generic signature: ()LOutPair<Ljava/lang/CharSequence;Ljava/lang/Integer;>;
abstract class B : A {
override fun foo2(): OutPair<CharSequence, Int> = null!!
override fun foo3(): OutPair<OutPair<String, Int>, Int> = null!!
override val prop2: OutPair<String, Int> = null!!
}
// method: B::foo2
// generic signature: ()LOutPair<Ljava/lang/CharSequence;Ljava/lang/Integer;>;
// method: B::foo3
// generic signature: ()LOutPair<LOutPair<Ljava/lang/String;Ljava/lang/Integer;>;Ljava/lang/Integer;>;
// method: B::getProp2
// generic signature: ()LOutPair<Ljava/lang/String;Ljava/lang/Integer;>;
@@ -0,0 +1,58 @@
class OutPair<out X, out Y>
class In<in Z>
interface A {
fun foo1(x: OutPair<String, Int>)
fun foo2(x: OutPair<CharSequence, Int>)
fun foo3(x: In<String>)
fun foo4(x: In<Any>)
var prop1: OutPair<String, Int>
}
// method: A::foo1
// generic signature: (LOutPair<Ljava/lang/String;Ljava/lang/Integer;>;)V
// method: A::foo2
// generic signature: (LOutPair<+Ljava/lang/CharSequence;Ljava/lang/Integer;>;)V
// method: A::foo3
// generic signature: (LIn<-Ljava/lang/String;>;)V
// method: A::foo4
// generic signature: (LIn<Ljava/lang/Object;>;)V
// method: A::getProp1
// generic signature: ()LOutPair<Ljava/lang/String;Ljava/lang/Integer;>;
// method: A::setProp1
// generic signature: (LOutPair<Ljava/lang/String;Ljava/lang/Integer;>;)V
abstract class B : A {
override final fun foo1(x: OutPair<String, Int>) {}
override final fun foo2(x: OutPair<CharSequence, Int>) {}
override final fun foo3(x: In<String>) {}
override final fun foo4(x: In<Any>) {}
override final var prop1: OutPair<String, Int> = null!!
}
// method: B::foo1
// generic signature: (LOutPair<Ljava/lang/String;Ljava/lang/Integer;>;)V
// method: B::foo2
// generic signature: (LOutPair<+Ljava/lang/CharSequence;Ljava/lang/Integer;>;)V
// method: B::foo3
// generic signature: (LIn<-Ljava/lang/String;>;)V
// method: B::foo4
// generic signature: (LIn<Ljava/lang/Object;>;)V
// method: B::getProp1
// generic signature: ()LOutPair<Ljava/lang/String;Ljava/lang/Integer;>;
// method: A::setProp1
// generic signature: (LOutPair<Ljava/lang/String;Ljava/lang/Integer;>;)V
@@ -2,4 +2,4 @@ fun f(p: List<String>) {}
// method: OutInInPositionKt::f // method: OutInInPositionKt::f
// jvm signature: (Ljava/util/List;)V // jvm signature: (Ljava/util/List;)V
// generic signature: (Ljava/util/List<+Ljava/lang/String;>;)V // generic signature: (Ljava/util/List<Ljava/lang/String;>;)V
@@ -6,4 +6,4 @@ fun f(): Out<In<X>> = throw Exception()
// method: OutOfInInOutPositionKt::f // method: OutOfInInOutPositionKt::f
// jvm signature: ()LOut; // jvm signature: ()LOut;
// generic signature: ()LOut<LIn<-LX;>;>; // generic signature: ()LOut<LIn<LX;>;>;
@@ -5,4 +5,4 @@ fun f(p: Out<Out<X>>) {}
// method: OutOfOutInInPositionKt::f // method: OutOfOutInInPositionKt::f
// jvm signature: (LOut;)V // jvm signature: (LOut;)V
// generic signature: (LOut<+LOut<+LX;>;>;)V // generic signature: (LOut<LOut<LX;>;>;)V
@@ -1,18 +1,15 @@
class Out<out T> class Out<out T>
class X class X
// Why we want this to be translated to 'Out<Out<? extends X>> f()' instead of 'Out<Out<X>> f()' // Why we want this to be translated to 'Out<Out<X>> f()' instead of 'Out<? extends Out<? extebds X>> f()'
// There are two instantiations of 'In' in this test: outer Out<...> and inner Out<X> // For return types default behaviour is skipping all declaration-site wildcards.
// So why do we want to put a wildcard on the inner one and not the outer? // The intuition behind this rule is simple: return types are basically used in subtype position
// People don't want wildcards in return types, because they are _long_. So we try our best to remove wildcards where possible // (as an argument for another call), and here everything works well in case of 'out'-variance.
// Not putting a wildcard on the outer occurrence is not imposing a restriction, actually it is removing one: // For example we have 'Out<Out<T>>>' as subtype both for 'Out<Out<T>>>' and 'Out<? extends Out<? extends T>>>',
// anything that can be done with Out<? extends X> in Java can be done with Out<X> // so values of such type is more flexible in contrast to `Out<? extends Out<? extends T>>>` that could be used only
// But omitting the wildcard on the inner occurrence is restrictive: // for the second case.
// 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() fun f(): Out<Out<X>> = throw Exception()
// method: OutOfOutInOutPositionKt::f // method: OutOfOutInOutPositionKt::f
// jvm signature: ()LOut; // jvm signature: ()LOut;
// generic signature: ()LOut<LOut<+LX;>;>; // generic signature: ()LOut<LOut<LX;>;>;
@@ -5,4 +5,4 @@ var p: M<X> = throw Exception()
// method: PropertySetterOutKt::setP // method: PropertySetterOutKt::setP
// jvm signature: (LM;)V // jvm signature: (LM;)V
// generic signature: (LM<+LX;>;)V // generic signature: (LM<LX;>;)V
@@ -0,0 +1,25 @@
class Out<out T>
class OutPair<out X, out Y>
class In<in Z>
class Final
open class Open
// For value parameters we decided to skip wildcards if it doesn't make obtained signature weaker
// in a sense of set of acceptable arguments.
// More precisely:
//    a. We write wildcard for 'Out<T>' iff T ``can have subtypes ignoring nullability''
//    b. We write wildcard for 'In<T>' iff T is not equal to it's class upper bound (ignoring nullability again)
// Definition of ``can have subtypes ignoring nullability'' is straightforward and you can see it in commit.
fun openClassArgument(x: Out<Open>, y: In<Open>) {}
// method: ArgumentOverridabilityKt::openClassArgument
// generic signature: (LOut<+LOpen;>;LIn<-LOpen;>;)V
fun finalClassArgument(x: Out<Final>, y: In<Final>) {}
// method: ArgumentOverridabilityKt::finalClassArgument
// generic signature: (LOut<LFinal;>;LIn<-LFinal;>;)V
fun oneArgumentFinal(x: OutPair<Final, Open>) {}
// method: ArgumentOverridabilityKt::oneArgumentFinal
// generic signature: (LOutPair<LFinal;+LOpen;>;)V
@@ -0,0 +1,23 @@
class Inv<E>
class Out<out T>
class OutPair<out Final, out Y>
class In<in Z>
class Final
open class Open
fun arrayOfOutOpen(x: Array<Out<Open>>) {}
// method: ArraysKt::arrayOfOutOpen
// generic signature: ([LOut<+LOpen;>;)V
fun arrayOfOutFinal(x: Array<Out<Final>>) {}
// method: ArraysKt::arrayOfOutFinal
// generic signature: ([LOut<LFinal;>;)V
fun outOfArrayOpen(x: Out<Array<Open>>) {}
// method: ArraysKt::outOfArrayOpen
// generic signature: (LOut<[LOpen;>;)V
fun outOfArrayOutOpen(x: Out<Array<out Open>>) {}
// method: ArraysKt::outOfArrayOutOpen
// generic signature: (LOut<+[LOpen;>;)V
@@ -0,0 +1,12 @@
class Out<out T>
class In<in T : F?, F : CharSequence>
fun optimized(x: In<CharSequence, CharSequence>) {}
// method: ComplicatedInBoundsKt::optimized
// generic signature: (LIn<-Ljava/lang/CharSequence;Ljava/lang/CharSequence;>;)V
class In2<in T, F : In2<T, F>>
fun nonOptimized(x: In2<In2<*, *>, *>) {}
// method: ComplicatedInBoundsKt::nonOptimized
// generic signature: (LIn2<-LIn2<**>;*>;)V
@@ -0,0 +1,12 @@
class Out<out T>
class Final
open class Open
fun deepOpen(x: Out<Out<Out<Open>>>) {}
// method: DeepOutKt::deepOpen
// generic signature: (LOut<+LOut<+LOut<+LOpen;>;>;>;)V
fun deepFinal(x: Out<Out<Out<Final>>>) {}
// method: DeepOutKt::deepFinal
// generic signature: (LOut<LOut<LOut<LFinal;>;>;>;)V
@@ -0,0 +1,31 @@
class Out<out T>
class OutPair<out X, out Y>
class In<in Z>
class Inv<E>
open class Open
class Final
fun skipAllOutInvWildcards(): Inv<OutPair<Open, Out<Out<Open>>>> = null!!
// method: FinalReturnTypeKt::skipAllOutInvWildcards
// generic signature: ()LInv<LOutPair<LOpen;LOut<LOut<LOpen;>;>;>;>;
fun notDeepIn(): In<Final> = null!!
// method: FinalReturnTypeKt::notDeepIn
// generic signature: ()LIn<LFinal;>;
fun skipWildcardsUntilIn0(): Out<In<Out<Open>>> = null!!
// method: FinalReturnTypeKt::skipWildcardsUntilIn0
// generic signature: ()LOut<LIn<LOut<+LOpen;>;>;>;
fun skipWildcardsUntilIn1(): Out<In<Out<Final>>> = null!!
// method: FinalReturnTypeKt::skipWildcardsUntilIn1
// generic signature: ()LOut<LIn<LOut<LFinal;>;>;>;
fun skipWildcardsUntilIn2(): Out<In<OutPair<Final, Out<Open>>>> = null!!
// method: FinalReturnTypeKt::skipWildcardsUntilIn2
// generic signature: ()LOut<LIn<LOutPair<LFinal;+LOut<+LOpen;>;>;>;>;
fun skipWildcardsUntilInProjection(): Inv<in Out<Open>> = null!!
// method: FinalReturnTypeKt::skipWildcardsUntilInProjection
// generic signature: ()LInv<-LOut<+LOpen;>;>;
@@ -0,0 +1,12 @@
class Out<out T>
class In<in Z>
class Final
fun outIn(x: Out<In<Final>>) {}
// method: OutInKt::outIn
// generic signature: (LOut<+LIn<-LFinal;>;>;)V
fun outInAny(x: Out<In<Any?>>) {}
// method: OutInKt::outInAny
// generic signature: (LOut<LIn<Ljava/lang/Object;>;>;)V
@@ -0,0 +1,25 @@
class Inv<E>
class Out<out T>
class Final
open class Open
fun invInv(x: Out<Inv<Open>>) {}
// method: OutInvKt::invInv
// generic signature: (LOut<LInv<LOpen;>;>;)V
fun invOut(x: Out<Inv<out Open>>) {}
// method: OutInvKt::invOut
// generic signature: (LOut<+LInv<+LOpen;>;>;)V
fun invOutFinal(x: Out<Inv<out Final>>) {}
// method: OutInvKt::invOutFinal
// generic signature: (LOut<LInv<+LFinal;>;>;)V
fun invIn(x: Out<Inv<in Final>>) {}
// method: OutInvKt::invIn
// generic signature: (LOut<+LInv<-LFinal;>;>;)V
fun invInAny(x: Out<Inv<in Any>>) {}
// method: OutInvKt::invInAny
// generic signature: (LOut<LInv<-Ljava/lang/Object;>;>;)V
@@ -0,0 +1,15 @@
class In<in Z>
class Out<out T>
class Final
fun inFinal(x: In<Final>) {}
// method: TopLevelInKt::inFinal
// generic signature: (LIn<-LFinal;>;)V
fun inAny(x: In<Any>) {}
// method: TopLevelInKt::inAny
// generic signature: (LIn<Ljava/lang/Object;>;)V
fun inOutFinal(x: In<Out<Final>>) {}
// method: TopLevelInKt::inOutFinal
// generic signature: (LIn<-LOut<LFinal;>;>;)V
@@ -0,0 +1,24 @@
class Inv<X>
class Out<out T>
class Final
open class Open
fun invOpen(x: Inv<Open>) {}
// method: TopLevelInvKt::invOpen
// generic signature: (LInv<LOpen;>;)V
fun invFinal(x: Inv<Final>) {}
// method: TopLevelInvKt::invFinal
// generic signature: (LInv<LFinal;>;)V
fun invOutOpen(x: Inv<Out<Open>>) {}
// method: TopLevelInvKt::invOutOpen
// generic signature: (LInv<LOut<+LOpen;>;>;)V
fun invOutFinal(x: Inv<Out<Final>>) {}
// method: TopLevelInvKt::invOutFinal
// generic signature: (LInv<LOut<LFinal;>;>;)V
fun invOutProjectedOutFinal(x: Inv<out Out<Final>>) {}
// method: TopLevelInvKt::invOutProjectedOutFinal
// generic signature: (LInv<+LOut<LFinal;>;>;)V
@@ -0,0 +1,8 @@
class Out<out T>
class In<in Z>
class Final
fun <Q : Final> typeParameter(x: Out<Q>, y: In<Q>) {}
// method: TypeParameterKt::typeParameter
// generic signature: <Q:LFinal;>(LOut<+TQ;>;LIn<-TQ;>;)V
@@ -3696,6 +3696,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/genericSignature"), Pattern.compile("^(.+)\\.kt$"), true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/genericSignature"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@TestMetadata("covariantOverride.kt")
public void testCovariantOverride() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/genericSignature/covariantOverride.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("kt5112.kt") @TestMetadata("kt5112.kt")
public void testKt5112() throws Exception { public void testKt5112() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/genericSignature/kt5112.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/genericSignature/kt5112.kt");
@@ -223,6 +223,12 @@ public class WriteSignatureTestGenerated extends AbstractWriteSignatureTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("GenericOverrides.kt")
public void testGenericOverrides() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/GenericOverrides.kt");
doTest(fileName);
}
@TestMetadata("InInInPosition.kt") @TestMetadata("InInInPosition.kt")
public void testInInInPosition() throws Exception { public void testInInInPosition() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/InInInPosition.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/InInInPosition.kt");
@@ -265,6 +271,18 @@ public class WriteSignatureTestGenerated extends AbstractWriteSignatureTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("OpenMembersReturnType.kt")
public void testOpenMembersReturnType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/OpenMembersReturnType.kt");
doTest(fileName);
}
@TestMetadata("OpenMembersValueParameter.kt")
public void testOpenMembersValueParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/OpenMembersValueParameter.kt");
doTest(fileName);
}
@TestMetadata("OutInField.kt") @TestMetadata("OutInField.kt")
public void testOutInField() throws Exception { public void testOutInField() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/OutInField.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/OutInField.kt");
@@ -354,6 +372,75 @@ public class WriteSignatureTestGenerated extends AbstractWriteSignatureTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/SuperTypeWithVarianceInArguments.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/SuperTypeWithVarianceInArguments.kt");
doTest(fileName); doTest(fileName);
} }
@TestMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class WildcardOptimization extends AbstractWriteSignatureTest {
public void testAllFilesPresentInWildcardOptimization() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("argumentOverridability.kt")
public void testArgumentOverridability() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization/argumentOverridability.kt");
doTest(fileName);
}
@TestMetadata("arrays.kt")
public void testArrays() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization/arrays.kt");
doTest(fileName);
}
@TestMetadata("complicatedInBounds.kt")
public void testComplicatedInBounds() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization/complicatedInBounds.kt");
doTest(fileName);
}
@TestMetadata("deepOut.kt")
public void testDeepOut() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization/deepOut.kt");
doTest(fileName);
}
@TestMetadata("finalReturnType.kt")
public void testFinalReturnType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization/finalReturnType.kt");
doTest(fileName);
}
@TestMetadata("outIn.kt")
public void testOutIn() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization/outIn.kt");
doTest(fileName);
}
@TestMetadata("outInv.kt")
public void testOutInv() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization/outInv.kt");
doTest(fileName);
}
@TestMetadata("topLevelIn.kt")
public void testTopLevelIn() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization/topLevelIn.kt");
doTest(fileName);
}
@TestMetadata("topLevelInv.kt")
public void testTopLevelInv() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization/topLevelInv.kt");
doTest(fileName);
}
@TestMetadata("typeParameter.kt")
public void testTypeParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization/typeParameter.kt");
doTest(fileName);
}
}
} }
@TestMetadata("compiler/testData/writeSignature/nothing") @TestMetadata("compiler/testData/writeSignature/nothing")
@@ -10,7 +10,7 @@ public abstract class C extends KotlinClass {
super(field); super(field);
} }
public List<Object> foo(Collection<String> mutableCollection, Collection<? extends Integer> nullableCollection) { public List<Object> foo(Collection<String> mutableCollection, Collection<Integer> nullableCollection) {
return super.foo(mutableCollection, nullableCollection); return super.foo(mutableCollection, nullableCollection);
} }
} }