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:
@@ -325,12 +325,12 @@ public class JetTypeMapper {
|
||||
//noinspection ConstantConditions
|
||||
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 {
|
||||
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());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Type mapType(@NotNull KotlinType jetType, @Nullable BothSignatureWriter signatureVisitor, @NotNull TypeMappingMode mode) {
|
||||
return mapType(jetType, signatureVisitor, mode, Variance.INVARIANT);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Type mapType(
|
||||
@NotNull KotlinType jetType,
|
||||
@Nullable BothSignatureWriter signatureVisitor,
|
||||
@NotNull TypeMappingMode mode,
|
||||
@NotNull Variance howThisTypeIsUsed
|
||||
@NotNull TypeMappingMode mode
|
||||
) {
|
||||
Type builtinType = mapBuiltinType(jetType);
|
||||
|
||||
if (builtinType != null) {
|
||||
Type asmType = mode.getNeedPrimitiveBoxing() ? boxType(builtinType) : builtinType;
|
||||
writeGenericType(jetType, asmType, signatureVisitor, howThisTypeIsUsed, mode);
|
||||
writeGenericType(jetType, asmType, signatureVisitor, mode);
|
||||
return asmType;
|
||||
}
|
||||
|
||||
@@ -440,7 +434,7 @@ public class JetTypeMapper {
|
||||
arrayElementType = boxType(mapType(memberType, mode));
|
||||
if (signatureVisitor != null) {
|
||||
signatureVisitor.writeArrayType();
|
||||
mapType(memberType, signatureVisitor, mode.toGenericArgumentMode());
|
||||
mapType(memberType, signatureVisitor, mode.toGenericArgumentMode(memberProjection.getProjectionKind()));
|
||||
signatureVisitor.writeArrayEnd();
|
||||
}
|
||||
}
|
||||
@@ -452,7 +446,7 @@ public class JetTypeMapper {
|
||||
Type asmType = mode.isForAnnotationParameter() && KotlinBuiltIns.isKClass((ClassDescriptor) descriptor) ?
|
||||
AsmTypes.JAVA_CLASS_TYPE :
|
||||
computeAsmType((ClassDescriptor) descriptor.getOriginal());
|
||||
writeGenericType(jetType, asmType, signatureVisitor, howThisTypeIsUsed, mode);
|
||||
writeGenericType(jetType, asmType, signatureVisitor, mode);
|
||||
return asmType;
|
||||
}
|
||||
|
||||
@@ -573,7 +567,6 @@ public class JetTypeMapper {
|
||||
@NotNull KotlinType type,
|
||||
@NotNull Type asmType,
|
||||
@Nullable BothSignatureWriter signatureVisitor,
|
||||
@NotNull Variance howThisTypeIsUsed,
|
||||
@NotNull TypeMappingMode mode
|
||||
) {
|
||||
if (signatureVisitor != null) {
|
||||
@@ -600,17 +593,14 @@ public class JetTypeMapper {
|
||||
|
||||
writeGenericArguments(
|
||||
signatureVisitor,
|
||||
outermostInnerType.getArguments(), outermostClass.getDeclaredTypeParameters(),
|
||||
howThisTypeIsUsed, mode);
|
||||
outermostInnerType.getArguments(), outermostClass.getDeclaredTypeParameters(), mode);
|
||||
|
||||
for (PossiblyInnerType innerPart : innerTypesAsList.subList(1, innerTypesAsList.size())) {
|
||||
ClassDescriptor classDescriptor = innerPart.getClassDescriptor();
|
||||
signatureVisitor.writeInnerClass(getJvmShortName(classDescriptor));
|
||||
writeGenericArguments(
|
||||
signatureVisitor, innerPart.getArguments(),
|
||||
classDescriptor.getDeclaredTypeParameters(),
|
||||
howThisTypeIsUsed, mode
|
||||
);
|
||||
classDescriptor.getDeclaredTypeParameters(), mode);
|
||||
}
|
||||
|
||||
signatureVisitor.writeClassEnd();
|
||||
@@ -631,7 +621,6 @@ public class JetTypeMapper {
|
||||
@NotNull BothSignatureWriter signatureVisitor,
|
||||
@NotNull List<? extends TypeProjection> arguments,
|
||||
@NotNull List<? extends TypeParameterDescriptor> parameters,
|
||||
@NotNull Variance howThisTypeIsUsed,
|
||||
@NotNull TypeMappingMode mode
|
||||
) {
|
||||
for (Pair<? extends TypeParameterDescriptor, ? extends TypeProjection> item : CollectionsKt.zip(parameters, arguments)) {
|
||||
@@ -642,11 +631,13 @@ public class JetTypeMapper {
|
||||
signatureVisitor.writeUnboundedWildcard();
|
||||
}
|
||||
else {
|
||||
Variance projectionKind =
|
||||
getEffectiveVariance(parameter.getVariance(), argument.getProjectionKind(), howThisTypeIsUsed, mode);
|
||||
Variance projectionKind = getVarianceForWildcard(parameter, argument, mode);
|
||||
signatureVisitor.writeTypeArgument(projectionKind);
|
||||
|
||||
mapType(argument.getType(), signatureVisitor, mode.toGenericArgumentMode());
|
||||
mapType(argument.getType(), signatureVisitor,
|
||||
mode.toGenericArgumentMode(
|
||||
TypeMappingUtil.getEffectiveVariance(parameter.getVariance(), argument.getProjectionKind())));
|
||||
|
||||
signatureVisitor.writeTypeArgumentEnd();
|
||||
}
|
||||
}
|
||||
@@ -670,21 +661,34 @@ public class JetTypeMapper {
|
||||
});
|
||||
}
|
||||
|
||||
private static Variance getEffectiveVariance(
|
||||
Variance parameterVariance,
|
||||
Variance projectionKind,
|
||||
Variance howThisTypeIsUsed,
|
||||
TypeMappingMode mode
|
||||
@NotNull
|
||||
private static Variance getVarianceForWildcard(
|
||||
@NotNull TypeParameterDescriptor parameter,
|
||||
@NotNull TypeProjection projection,
|
||||
@NotNull TypeMappingMode mode
|
||||
) {
|
||||
if (!mode.getWriteDeclarationSiteProjections() && projectionKind == Variance.INVARIANT) return Variance.INVARIANT;
|
||||
Variance projectionKind = projection.getProjectionKind();
|
||||
|
||||
// Return type must not contain wildcards
|
||||
if (howThisTypeIsUsed == Variance.OUT_VARIANCE) return projectionKind;
|
||||
if (mode.getSkipDeclarationSiteWildcards() && projectionKind == Variance.INVARIANT) {
|
||||
return Variance.INVARIANT;
|
||||
}
|
||||
|
||||
Variance parameterVariance = parameter.getVariance();
|
||||
if (parameterVariance == Variance.INVARIANT) {
|
||||
return projectionKind;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (parameterVariance == projectionKind) {
|
||||
@@ -1187,7 +1191,7 @@ public class JetTypeMapper {
|
||||
|
||||
private void writeParameter(@NotNull BothSignatureWriter sw, @NotNull JvmMethodParameterKind kind, @NotNull KotlinType type) {
|
||||
sw.writeParameterType(kind);
|
||||
mapType(type, sw, TypeMappingMode.DEFAULT);
|
||||
mapType(type, sw, TypeMappingMode.getOptimalModeForValueParameter(type));
|
||||
sw.writeParameterTypeEnd();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.state
|
||||
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
internal class TypeMappingMode private constructor(
|
||||
val needPrimitiveBoxing: Boolean = false,
|
||||
val isForAnnotationParameter: Boolean = false,
|
||||
val writeDeclarationSiteProjections: Boolean = true,
|
||||
val genericArgumentMode: TypeMappingMode? = null
|
||||
// Here DeclarationSiteWildcards means wildcard generated because of declaration-site variance
|
||||
val skipDeclarationSiteWildcards: Boolean = false,
|
||||
val skipDeclarationSiteWildcardsIfPossible: Boolean = false,
|
||||
private val genericArgumentMode: TypeMappingMode? = null,
|
||||
private val genericContravariantArgumentMode: TypeMappingMode? = genericArgumentMode
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
@@ -35,13 +41,12 @@ internal class TypeMappingMode private constructor(
|
||||
@JvmField
|
||||
val DEFAULT = TypeMappingMode(genericArgumentMode = GENERIC_TYPE)
|
||||
|
||||
|
||||
/**
|
||||
* kotlin.Int is mapped to Ljava/lang/Integer;
|
||||
* No projections allowed in immediate arguments
|
||||
*/
|
||||
@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
|
||||
@@ -50,9 +55,49 @@ internal class TypeMappingMode private constructor(
|
||||
@JvmField
|
||||
val VALUE_FOR_ANNOTATION = TypeMappingMode(
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user