From 063bce23d23f7e81817a36787a546527a3466076 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 18 Nov 2016 15:36:04 +0300 Subject: [PATCH] Prohibit constructing projected types via type aliases. --- .../jetbrains/kotlin/diagnostics/Errors.java | 2 + .../rendering/DefaultErrorMessages.java | 2 + .../kotlin/resolve/DescriptorResolver.java | 19 +++++++- .../kotlin/resolve/calls/CandidateResolver.kt | 13 +++++ .../typeAliasConstructorCrazyProjections.kt | 2 +- .../typeAliasConstructorForProjection.kt | 12 +++++ .../typeAliasConstructorForProjection.txt | 18 +++++++ ...iasConstructorForProjectionInSupertypes.kt | 16 +++++++ ...asConstructorForProjectionInSupertypes.txt | 47 +++++++++++++++++++ .../typeAliasConstructorVsFunction.kt | 14 ++++++ .../typeAliasConstructorVsFunction.txt | 14 ++++++ ...typeAliasForProjectionInSuperInterfaces.kt | 12 +++++ ...ypeAliasForProjectionInSuperInterfaces.txt | 46 ++++++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 24 ++++++++++ .../org/jetbrains/kotlin/types/TypeUtils.kt | 5 ++ 15 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjection.kt create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjection.txt create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjectionInSupertypes.kt create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjectionInSupertypes.txt create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasConstructorVsFunction.kt create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasConstructorVsFunction.txt create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasForProjectionInSuperInterfaces.kt create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasForProjectionInSuperInterfaces.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 194371371ba..2c96bc0a789 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -137,6 +137,8 @@ public interface Errors { DiagnosticFactory1 CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 TYPEALIAS_SHOULD_EXPAND_TO_CLASS = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 TYPEALIAS_EXPANDED_TO_MALFORMED_TYPE = DiagnosticFactory2.create(ERROR); + DiagnosticFactory1 EXPANDED_TYPE_CANNOT_BE_CONSTRUCTED = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 EXPANDED_TYPE_CANNOT_BE_INHERITED = DiagnosticFactory1.create(ERROR); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 2c42764f4c1..1a5a8c06e4f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -444,6 +444,8 @@ public class DefaultErrorMessages { MAP.put(TYPEALIAS_SHOULD_EXPAND_TO_CLASS, "Type alias expands to {0}, which is not a class, an interface, or an object", RENDER_TYPE); MAP.put(TYPEALIAS_EXPANDED_TO_MALFORMED_TYPE, "Type alias expanded to malformed type {0}: {1}", RENDER_TYPE, STRING); MAP.put(UNUSED_TYPEALIAS_PARAMETER, "Type alias parameter {0} is not used in the expanded type {1} and does not affect type checking", NAME, RENDER_TYPE); + MAP.put(EXPANDED_TYPE_CANNOT_BE_CONSTRUCTED, "Expanded type {0} contains non-invariant projections in top-level arguments and cannot be constructed", RENDER_TYPE); + MAP.put(EXPANDED_TYPE_CANNOT_BE_INHERITED, "Expanded type {0} contains non-invariant projections in top-level arguments and cannot be inherited from", RENDER_TYPE); MAP.put(TOO_MANY_ARGUMENTS, "Too many arguments for {0}", FQ_NAMES_IN_TYPES); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java index 7dd21eff761..450991fc98e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -57,6 +57,7 @@ import org.jetbrains.kotlin.storage.StorageManager; import org.jetbrains.kotlin.types.*; import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; import org.jetbrains.kotlin.types.expressions.*; +import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; import java.util.*; @@ -195,7 +196,7 @@ public class DescriptorResolver { else { result.add(supertype); KtTypeElement bareSuperType = checkNullableSupertypeAndStripQuestionMarks(trace, typeReference.getTypeElement()); - checkProjectionsInImmediateArguments(trace, bareSuperType); + checkProjectionsInImmediateArguments(trace, bareSuperType, supertype); } } else { @@ -218,16 +219,30 @@ public class DescriptorResolver { return typeElement; } - private static void checkProjectionsInImmediateArguments(@NotNull BindingTrace trace, @Nullable KtTypeElement typeElement) { + private static void checkProjectionsInImmediateArguments( + @NotNull BindingTrace trace, + @Nullable KtTypeElement typeElement, + @NotNull KotlinType type + ) { + if (typeElement == null) return; + + boolean hasProjectionsInWrittenArguments = false; if (typeElement instanceof KtUserType) { KtUserType userType = (KtUserType) typeElement; List typeArguments = userType.getTypeArguments(); for (KtTypeProjection typeArgument : typeArguments) { if (typeArgument.getProjectionKind() != KtProjectionKind.NONE) { trace.report(PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE.on(typeArgument)); + hasProjectionsInWrittenArguments = true; } } } + + if (!type.isError() && SpecialTypesKt.getAbbreviatedType(type) != null && !hasProjectionsInWrittenArguments) { + if (TypeUtilsKt.isInterface(type) && TypeUtilsKt.containsTypeProjectionsInTopLevelArguments(type)) { + trace.report(EXPANDED_TYPE_CANNOT_BE_INHERITED.on(typeElement, type)); + } + } } public static Visibility getDefaultVisibility(KtModifierListOwner modifierListOwner, DeclarationDescriptor containingDescriptor) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index ae96f07377f..66147b39d6f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -53,6 +53,7 @@ import org.jetbrains.kotlin.types.TypeUtils.noExpectedType import org.jetbrains.kotlin.types.checker.ErrorTypesAreEqualToAnything import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver +import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArguments import java.util.* class CandidateResolver( @@ -96,6 +97,7 @@ class CandidateResolver( checkValueArguments() checkAbstractAndSuper() + checkConstructedExpandedType() } private fun CallCandidateResolutionContext<*>.checkValueArguments() = checkAndReport { @@ -310,6 +312,17 @@ class CandidateResolver( } } + private fun CallCandidateResolutionContext<*>.checkConstructedExpandedType() = check { + val descriptor = candidateDescriptor + + if (descriptor is TypeAliasConstructorDescriptor) { + if (descriptor.returnType.containsTypeProjectionsInTopLevelArguments()) { + trace.report(EXPANDED_TYPE_CANNOT_BE_CONSTRUCTED.on(call.callElement, descriptor.returnType)) + candidateCall.addStatus(OTHER_ERROR) + } + } + } + private fun getReceiverSuper(receiver: Receiver?): KtSuperExpression? { if (receiver is ExpressionReceiver) { val expression = receiver.expression diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorCrazyProjections.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorCrazyProjections.kt index 857418f7f00..32a9242a1fe 100644 --- a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorCrazyProjections.kt +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorCrazyProjections.kt @@ -10,4 +10,4 @@ fun listOf(): List = null!! // since it has 'out' type projection in 'in' position. val test1 = BOutIn(listOf(), null!!) -val test2 = BInIn(listOf(), null!!) \ No newline at end of file +val test2 = BInIn(listOf(), null!!) \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjection.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjection.kt new file mode 100644 index 00000000000..e144f1498c8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjection.kt @@ -0,0 +1,12 @@ +class C + +typealias CStar = C<*> +typealias CIn = C +typealias COut = C +typealias CT = C + +val test1 = CStar() +val test2 = CIn() +val test3 = COut() +val test4 = CT<*>() +val test5 = CT>() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjection.txt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjection.txt new file mode 100644 index 00000000000..1fcde7720eb --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjection.txt @@ -0,0 +1,18 @@ +package + +public val test1: C<*> +public val test2: C +public val test3: C +public val test4: C<[ERROR : Star projection in a call]> +public val test5: C /* = C<*> */> + +public final class C { + public constructor C() + 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 typealias CIn = C +public typealias COut = C +public typealias CStar = C<*> +public typealias CT = C diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjectionInSupertypes.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjectionInSupertypes.kt new file mode 100644 index 00000000000..079dda3d220 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjectionInSupertypes.kt @@ -0,0 +1,16 @@ +open class C + +typealias CStar = C<*> +typealias CIn = C +typealias COut = C +typealias CT = C + +class Test1 : CStar() +class Test2 : CIn() +class Test3 : COut() + +class Test4 : CStar { + constructor() : super() +} + +class Test5 : CT<*>() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjectionInSupertypes.txt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjectionInSupertypes.txt new file mode 100644 index 00000000000..92b361b0ac8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjectionInSupertypes.txt @@ -0,0 +1,47 @@ +package + +public open class C { + public constructor C() + 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 final class Test1 : CStar /* = C<*> */ { + public constructor Test1() + 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 final class Test2 : CIn /* = C */ { + public constructor Test2() + 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 final class Test3 : COut /* = C */ { + public constructor Test3() + 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 final class Test4 : CStar /* = C<*> */ { + public constructor Test4() + 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 final class Test5 : CT<*> /* = C<*> */ { + public constructor Test5() + 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 typealias CIn = C +public typealias COut = C +public typealias CStar = C<*> +public typealias CT = C diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorVsFunction.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorVsFunction.kt new file mode 100644 index 00000000000..bef167d4c92 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorVsFunction.kt @@ -0,0 +1,14 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE +// !CHECK_TYPE +// FILE: a.kt +class B(x: String) + +typealias A1 = B +private typealias A2 = B +private typealias A3 = B + +fun A3(x: Any) = "OK" + +fun bar() { + A3("") checkType { _() } +} diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorVsFunction.txt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorVsFunction.txt new file mode 100644 index 00000000000..91eb554ac21 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorVsFunction.txt @@ -0,0 +1,14 @@ +package + +public fun A3(/*0*/ x: kotlin.Any): kotlin.String +public fun bar(): kotlin.Unit + +public final class B { + public constructor B(/*0*/ x: kotlin.String) + 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 typealias A1 = B +private typealias A2 = B +private typealias A3 = B diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasForProjectionInSuperInterfaces.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasForProjectionInSuperInterfaces.kt new file mode 100644 index 00000000000..ad3fbc07221 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasForProjectionInSuperInterfaces.kt @@ -0,0 +1,12 @@ +interface I + +typealias IStar = I<*> +typealias IIn = I +typealias IOut = I +typealias IT = I + +class Test1 : IStar +class Test2 : IIn +class Test3 : IOut +class Test4 : IT<*> +class Test5 : IT> \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasForProjectionInSuperInterfaces.txt b/compiler/testData/diagnostics/tests/typealias/typeAliasForProjectionInSuperInterfaces.txt new file mode 100644 index 00000000000..ff2d20367cb --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasForProjectionInSuperInterfaces.txt @@ -0,0 +1,46 @@ +package + +public interface I { + 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 final class Test1 : IStar /* = I<*> */ { + public constructor Test1() + 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 final class Test2 : IIn /* = I */ { + public constructor Test2() + 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 final class Test3 : IOut /* = I */ { + public constructor Test3() + 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 final class Test4 : IT<*> /* = I<*> */ { + public constructor Test4() + 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 final class Test5 : IT /* = I<*> */> /* = I /* = I<*> */> */ { + public constructor Test5() + 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 typealias IIn = I +public typealias IOut = I +public typealias IStar = I<*> +public typealias IT = I diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 41f618b8f94..a999be41237 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -21166,6 +21166,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("typeAliasConstructorForProjection.kt") + public void testTypeAliasConstructorForProjection() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjection.kt"); + doTest(fileName); + } + + @TestMetadata("typeAliasConstructorForProjectionInSupertypes.kt") + public void testTypeAliasConstructorForProjectionInSupertypes() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasConstructorForProjectionInSupertypes.kt"); + doTest(fileName); + } + @TestMetadata("typeAliasConstructorInSuperCall.kt") public void testTypeAliasConstructorInSuperCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasConstructorInSuperCall.kt"); @@ -21202,6 +21214,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("typeAliasConstructorVsFunction.kt") + public void testTypeAliasConstructorVsFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasConstructorVsFunction.kt"); + doTest(fileName); + } + @TestMetadata("typeAliasConstructorWrongClass.kt") public void testTypeAliasConstructorWrongClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasConstructorWrongClass.kt"); @@ -21220,6 +21238,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("typeAliasForProjectionInSuperInterfaces.kt") + public void testTypeAliasForProjectionInSuperInterfaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasForProjectionInSuperInterfaces.kt"); + doTest(fileName); + } + @TestMetadata("typeAliasInAnonymousObjectType.kt") public void testTypeAliasInAnonymousObjectType() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasInAnonymousObjectType.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index 8ec661e88ae..c84d423bdb4 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -203,3 +203,8 @@ fun KotlinType.requiresTypeAliasExpansion(): Boolean = } ?: false } +fun KotlinType.containsTypeProjectionsInTopLevelArguments(): Boolean { + if (isError) return false + val possiblyInnerType = buildPossiblyInnerType() ?: return false + return possiblyInnerType.arguments.any { it.isStarProjection || it.projectionKind != Variance.INVARIANT } +} \ No newline at end of file