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:
+1
-1
@@ -6,4 +6,4 @@ fun f(m: M<X, X>): M<X, X> = throw Exception()
|
||||
|
||||
// method: FunctionTwoTypeParametersKt::f
|
||||
// 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
|
||||
+1
-1
@@ -6,4 +6,4 @@ fun f(p: In<Out<X>>) {}
|
||||
|
||||
// method: InOfOutInInPositionKt::f
|
||||
// jvm signature: (LIn;)V
|
||||
// generic signature: (LIn<-LOut<+LX;>;>;)V
|
||||
// generic signature: (LIn<-LOut<LX;>;>;)V
|
||||
+1
-1
@@ -6,4 +6,4 @@ fun f(): In<Out<X>> = throw Exception()
|
||||
|
||||
// method: InOfOutInOutPositionKt::f
|
||||
// jvm signature: ()LIn;
|
||||
// generic signature: ()LIn<LOut<+LX;>;>;
|
||||
// generic signature: ()LIn<LOut<LX;>;>;
|
||||
+51
@@ -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;>;
|
||||
+58
@@ -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
|
||||
+1
-1
@@ -2,4 +2,4 @@ fun f(p: List<String>) {}
|
||||
|
||||
// method: OutInInPositionKt::f
|
||||
// jvm signature: (Ljava/util/List;)V
|
||||
// generic signature: (Ljava/util/List<+Ljava/lang/String;>;)V
|
||||
// generic signature: (Ljava/util/List<Ljava/lang/String;>;)V
|
||||
+1
-1
@@ -6,4 +6,4 @@ fun f(): Out<In<X>> = throw Exception()
|
||||
|
||||
// method: OutOfInInOutPositionKt::f
|
||||
// jvm signature: ()LOut;
|
||||
// generic signature: ()LOut<LIn<-LX;>;>;
|
||||
// generic signature: ()LOut<LIn<LX;>;>;
|
||||
+1
-1
@@ -5,4 +5,4 @@ fun f(p: Out<Out<X>>) {}
|
||||
|
||||
// method: OutOfOutInInPositionKt::f
|
||||
// jvm signature: (LOut;)V
|
||||
// generic signature: (LOut<+LOut<+LX;>;>;)V
|
||||
// generic signature: (LOut<LOut<LX;>;>;)V
|
||||
+8
-11
@@ -1,18 +1,15 @@
|
||||
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.
|
||||
// Why we want this to be translated to 'Out<Out<X>> f()' instead of 'Out<? extends Out<? extebds X>> f()'
|
||||
// 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.
|
||||
fun f(): Out<Out<X>> = throw Exception()
|
||||
|
||||
// method: OutOfOutInOutPositionKt::f
|
||||
// jvm signature: ()LOut;
|
||||
// generic signature: ()LOut<LOut<+LX;>;>;
|
||||
// generic signature: ()LOut<LOut<LX;>;>;
|
||||
+1
-1
@@ -5,4 +5,4 @@ var p: M<X> = throw Exception()
|
||||
|
||||
// method: PropertySetterOutKt::setP
|
||||
// jvm signature: (LM;)V
|
||||
// generic signature: (LM<+LX;>;)V
|
||||
// generic signature: (LM<LX;>;)V
|
||||
+25
@@ -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
|
||||
+23
@@ -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
|
||||
compiler/testData/writeSignature/declarationSiteVariance/wildcardOptimization/complicatedInBounds.kt
Vendored
+12
@@ -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
|
||||
+12
@@ -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
|
||||
Vendored
+31
@@ -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;>;>;
|
||||
+12
@@ -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
|
||||
+25
@@ -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
|
||||
Vendored
+15
@@ -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
|
||||
Vendored
+24
@@ -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
|
||||
Vendored
+8
@@ -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
|
||||
Reference in New Issue
Block a user