From 12d6b6e7e4bbe1b989663b11fd2c3ace95ac2e5a Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 22 Oct 2015 23:26:37 +0300 Subject: [PATCH] Use "Any?" as out-type for type parameters instead of intersection of bounds Also refactor checkSubtypeForTheSameConstructor to compute everything in the best order --- ...umentForTypeParameterWithMultipleBounds.kt | 18 ++++++++ ...mentForTypeParameterWithMultipleBounds.txt | 43 +++++++++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 6 +++ .../types/checker/TypeCheckingProcedure.java | 46 +++++++++++-------- 4 files changed, 93 insertions(+), 20 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/subtyping/invariantArgumentForTypeParameterWithMultipleBounds.kt create mode 100644 compiler/testData/diagnostics/tests/subtyping/invariantArgumentForTypeParameterWithMultipleBounds.txt diff --git a/compiler/testData/diagnostics/tests/subtyping/invariantArgumentForTypeParameterWithMultipleBounds.kt b/compiler/testData/diagnostics/tests/subtyping/invariantArgumentForTypeParameterWithMultipleBounds.kt new file mode 100644 index 00000000000..1f48345b327 --- /dev/null +++ b/compiler/testData/diagnostics/tests/subtyping/invariantArgumentForTypeParameterWithMultipleBounds.kt @@ -0,0 +1,18 @@ +import java.io.Serializable + +class A where T : Cloneable, T : Serializable + +interface CS1 : Cloneable, Serializable +interface CS2 : CS1 + +interface I1 { + fun foo(): A +} + +interface I2 : I1 { + override fun foo(): A +} + +interface I3 : I1 { + override fun foo(): A +} diff --git a/compiler/testData/diagnostics/tests/subtyping/invariantArgumentForTypeParameterWithMultipleBounds.txt b/compiler/testData/diagnostics/tests/subtyping/invariantArgumentForTypeParameterWithMultipleBounds.txt new file mode 100644 index 00000000000..1daf2433102 --- /dev/null +++ b/compiler/testData/diagnostics/tests/subtyping/invariantArgumentForTypeParameterWithMultipleBounds.txt @@ -0,0 +1,43 @@ +package + +public final class A where T : java.io.Serializable { + public constructor A() where T : java.io.Serializable + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface CS1 : kotlin.Cloneable, java.io.Serializable { + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface CS2 : CS1 { + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface I1 { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(): A + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface I2 : I1 { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*1*/ fun foo(): A + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface I3 : I1 { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*1*/ fun foo(): A + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 43e67460bce..58e0db3afeb 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -15657,6 +15657,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("invariantArgumentForTypeParameterWithMultipleBounds.kt") + public void testInvariantArgumentForTypeParameterWithMultipleBounds() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/subtyping/invariantArgumentForTypeParameterWithMultipleBounds.kt"); + doTest(fileName); + } + @TestMetadata("kt2069.kt") public void testKt2069() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/subtyping/kt2069.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java b/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java index 37986e075c0..58df2192059 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java @@ -20,9 +20,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor; +import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.types.*; +import java.util.Collection; +import java.util.Collections; import java.util.List; import static org.jetbrains.kotlin.types.Variance.*; @@ -43,12 +46,14 @@ public class TypeCheckingProcedure { return UtilsKt.findCorrespondingSupertype(subtype, supertype, typeCheckingProcedureCallbacks); } - public static KotlinType getOutType(TypeParameterDescriptor parameter, TypeProjection argument) { - boolean isOutProjected = argument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE; - return isOutProjected ? parameter.getUpperBoundsAsType() : argument.getType(); + @NotNull + private static KotlinType getOutType(@NotNull TypeParameterDescriptor parameter, @NotNull TypeProjection argument) { + boolean isInProjected = argument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE; + return isInProjected ? DescriptorUtilsKt.getBuiltIns(parameter).getNullableAnyType() : argument.getType(); } - public static KotlinType getInType(@NotNull TypeParameterDescriptor parameter, @NotNull TypeProjection argument) { + @NotNull + private static KotlinType getInType(@NotNull TypeParameterDescriptor parameter, @NotNull TypeProjection argument) { boolean isOutProjected = argument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE; return isOutProjected ? DescriptorUtilsKt.getBuiltIns(parameter).getNothingType() : argument.getType(); } @@ -229,30 +234,31 @@ public class TypeCheckingProcedure { TypeParameterDescriptor parameter = parameters.get(i); TypeProjection superArgument = superArguments.get(i); - if (superArgument.isStarProjection()) continue; - - KotlinType superIn = getInType(parameter, superArgument); - KotlinType superOut = getOutType(parameter, superArgument); - TypeProjection subArgument = subArguments.get(i); - KotlinType subIn = getInType(parameter, subArgument); - KotlinType subOut = getOutType(parameter, subArgument); + + if (superArgument.isStarProjection()) continue; if (capture(subArgument, superArgument, parameter)) continue; boolean argumentIsErrorType = subArgument.getType().isError() || superArgument.getType().isError(); - if (!argumentIsErrorType && parameter.getVariance() == INVARIANT - && subArgument.getProjectionKind() == INVARIANT && superArgument.getProjectionKind() == INVARIANT) { + if (!argumentIsErrorType && parameter.getVariance() == INVARIANT && + subArgument.getProjectionKind() == INVARIANT && superArgument.getProjectionKind() == INVARIANT) { if (!constraints.assertEqualTypes(subArgument.getType(), superArgument.getType(), this)) return false; + continue; + } + + KotlinType superOut = getOutType(parameter, superArgument); + KotlinType subOut = getOutType(parameter, subArgument); + if (!constraints.assertSubtype(subOut, superOut, this)) return false; + + KotlinType superIn = getInType(parameter, superArgument); + KotlinType subIn = getInType(parameter, subArgument); + + if (superArgument.getProjectionKind() != Variance.OUT_VARIANCE) { + if (!constraints.assertSubtype(superIn, subIn, this)) return false; } else { - if (!constraints.assertSubtype(subOut, superOut, this)) return false; - if (superArgument.getProjectionKind() != Variance.OUT_VARIANCE) { - if (!constraints.assertSubtype(superIn, subIn, this)) return false; - } - else { - assert KotlinBuiltIns.isNothing(superIn) : "In component must be Nothing for out-projection"; - } + assert KotlinBuiltIns.isNothing(superIn) : "In component must be Nothing for out-projection"; } } return true;