From b7df36643b885b3b24f55d6517a11f508674b991 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 10 Aug 2018 16:58:34 +0300 Subject: [PATCH] Implement additional declaration checks for inline classes - Implementation by delegation is prohibited - Delegated properties are prohibited --- .../jetbrains/kotlin/diagnostics/Errors.java | 2 + .../rendering/DefaultErrorMessages.java | 2 + .../checkers/InlineClassDeclarationChecker.kt | 29 ++++++++++-- .../delegatedPropertyInInlineClass.kt | 28 +++++++++++ .../delegatedPropertyInInlineClass.txt | 47 +++++++++++++++++++ ...assCannotImplementInterfaceByDelegation.kt | 9 ++++ ...ssCannotImplementInterfaceByDelegation.txt | 30 ++++++++++++ .../inlineClassWithForbiddenUnderlyingType.kt | 6 +++ ...inlineClassWithForbiddenUnderlyingType.txt | 32 +++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 10 ++++ .../DiagnosticsUsingJavacTestGenerated.java | 10 ++++ 11 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inlineClasses/delegatedPropertyInInlineClass.kt create mode 100644 compiler/testData/diagnostics/tests/inlineClasses/delegatedPropertyInInlineClass.txt create mode 100644 compiler/testData/diagnostics/tests/inlineClasses/inlineClassCannotImplementInterfaceByDelegation.kt create mode 100644 compiler/testData/diagnostics/tests/inlineClasses/inlineClassCannotImplementInterfaceByDelegation.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 1937713420f..babe0946a5d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -325,7 +325,9 @@ public interface Errors { DiagnosticFactory0 INLINE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 INLINE_CLASS_WITH_INITIALIZER = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 PROPERTY_WITH_BACKING_FIELD_INSIDE_INLINE_CLASS = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); + DiagnosticFactory0 DELEGATED_PROPERTY_INSIDE_INLINE_CLASS = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 INLINE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE = DiagnosticFactory1.create(ERROR); + DiagnosticFactory0 INLINE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION = DiagnosticFactory0.create(ERROR); // Secondary constructors 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 b4b842363d9..dc22b0dac37 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -651,7 +651,9 @@ public class DefaultErrorMessages { MAP.put(INLINE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER, "Inline class primary constructor must have only final read-only (val) property parameter"); MAP.put(INLINE_CLASS_WITH_INITIALIZER, "Inline class cannot have an initializer block"); MAP.put(PROPERTY_WITH_BACKING_FIELD_INSIDE_INLINE_CLASS, "Inline class cannot have properties with backing fields"); + MAP.put(DELEGATED_PROPERTY_INSIDE_INLINE_CLASS, "Inline class cannot have delegated properties"); MAP.put(INLINE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE, "Inline class cannot have value parameter of type ''{0}''", RENDER_TYPE); + MAP.put(INLINE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION, "Inline class cannot implement an interface by delegation"); MAP.put(VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED, "Variance annotations are only allowed for type parameters of classes and interfaces"); MAP.put(BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED, "Bounds are not allowed on type alias parameters"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt index e802b714320..218ff6d9b10 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.resolve.checkers +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.KtTokens @@ -15,6 +16,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.isInlineClass import org.jetbrains.kotlin.resolve.substitutedUnderlyingType +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.types.typeUtil.isUnit @@ -78,15 +80,32 @@ object InlineClassDeclarationChecker : DeclarationChecker { } val baseParameterType = descriptor.safeAs()?.defaultType?.substitutedUnderlyingType() - if (baseParameterType != null && - (baseParameterType.isUnit() || baseParameterType.isNothing() || baseParameterType.isTypeParameter()) - ) { + if (baseParameterType != null && baseParameterType.isInapplicableParameterType()) { val typeReference = baseParameter.typeReference if (typeReference != null) { trace.report(Errors.INLINE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE.on(typeReference, baseParameterType)) return } } + + for (supertypeEntry in declaration.superTypeListEntries) { + if (supertypeEntry is KtDelegatedSuperTypeEntry) { + trace.report(Errors.INLINE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION.on(supertypeEntry)) + return + } + } + } + + private fun KotlinType.isInapplicableParameterType() = + isUnit() || isNothing() || isTypeParameter() || isGenericArrayOfTypeParameter() + + private fun KotlinType.isGenericArrayOfTypeParameter(): Boolean { + if (!KotlinBuiltIns.isArray(this)) return false + val argument0 = arguments[0] + if (argument0.isStarProjection) return false + val argument0type = argument0.type + return argument0type.isTypeParameter() || + argument0type.isGenericArrayOfTypeParameter() } private fun isParameterAcceptableForInlineClass(parameter: KtParameter): Boolean { @@ -105,5 +124,9 @@ class PropertiesWithBackingFieldsInsideInlineClass : DeclarationChecker { if (context.trace.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor) == true) { context.trace.report(Errors.PROPERTY_WITH_BACKING_FIELD_INSIDE_INLINE_CLASS.on(declaration)) } + + declaration.delegate?.let { + context.trace.report(Errors.DELEGATED_PROPERTY_INSIDE_INLINE_CLASS.on(it)) + } } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inlineClasses/delegatedPropertyInInlineClass.kt b/compiler/testData/diagnostics/tests/inlineClasses/delegatedPropertyInInlineClass.kt new file mode 100644 index 00000000000..8a64831f52d --- /dev/null +++ b/compiler/testData/diagnostics/tests/inlineClasses/delegatedPropertyInInlineClass.kt @@ -0,0 +1,28 @@ +// !LANGUAGE: +InlineClasses + +class Val { + operator fun getValue(thisRef: Any?, kProp: Any?) = 1 +} + +class Var { + operator fun getValue(thisRef: Any?, kProp: Any?) = 2 + operator fun setValue(thisRef: Any?, kProp: Any?, value: Int) {} +} + + +object ValObject { + operator fun getValue(thisRef: Any?, kProp: Any?) = 1 +} + +object VarObject { + operator fun getValue(thisRef: Any?, kProp: Any?) = 2 + operator fun setValue(thisRef: Any?, kProp: Any?, value: Int) {} +} + +inline class Z(val data: Int) { + val testVal by Val() + var testVar by Var() + + val testValBySingleton by ValObject + var testVarBySingleton by VarObject +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inlineClasses/delegatedPropertyInInlineClass.txt b/compiler/testData/diagnostics/tests/inlineClasses/delegatedPropertyInInlineClass.txt new file mode 100644 index 00000000000..60de7828884 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inlineClasses/delegatedPropertyInInlineClass.txt @@ -0,0 +1,47 @@ +package + +public final class Val { + public constructor Val() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ kProp: kotlin.Any?): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public object ValObject { + private constructor ValObject() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ kProp: kotlin.Any?): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Var { + public constructor Var() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ kProp: kotlin.Any?): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun setValue(/*0*/ thisRef: kotlin.Any?, /*1*/ kProp: kotlin.Any?, /*2*/ value: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public object VarObject { + private constructor VarObject() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ kProp: kotlin.Any?): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun setValue(/*0*/ thisRef: kotlin.Any?, /*1*/ kProp: kotlin.Any?, /*2*/ value: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final inline class Z { + public constructor Z(/*0*/ data: kotlin.Int) + public final val data: kotlin.Int + public final val testVal: kotlin.Int + public final val testValBySingleton: kotlin.Int + public final var testVar: kotlin.Int + public final var testVarBySingleton: kotlin.Int + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassCannotImplementInterfaceByDelegation.kt b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassCannotImplementInterfaceByDelegation.kt new file mode 100644 index 00000000000..916d2148c9b --- /dev/null +++ b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassCannotImplementInterfaceByDelegation.kt @@ -0,0 +1,9 @@ +// !LANGUAGE: +InlineClasses + +interface IFoo + +object FooImpl : IFoo + +inline class Test1(val x: Any) : IFoo by FooImpl + +inline class Test2(val x: IFoo) : IFoo by x \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassCannotImplementInterfaceByDelegation.txt b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassCannotImplementInterfaceByDelegation.txt new file mode 100644 index 00000000000..fd87bcda27f --- /dev/null +++ b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassCannotImplementInterfaceByDelegation.txt @@ -0,0 +1,30 @@ +package + +public object FooImpl : IFoo { + private constructor FooImpl() + 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 IFoo { + 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 inline class Test1 : IFoo { + public constructor Test1(/*0*/ x: kotlin.Any) + public final val x: kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + +public final inline class Test2 : IFoo { + public constructor Test2(/*0*/ x: IFoo) + public final val x: IFoo + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassWithForbiddenUnderlyingType.kt b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassWithForbiddenUnderlyingType.kt index 036413bf0d5..0103c2b62fc 100644 --- a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassWithForbiddenUnderlyingType.kt +++ b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassWithForbiddenUnderlyingType.kt @@ -3,6 +3,12 @@ inline class Foo(val x: T) inline class FooNullable(val x: T?) +inline class FooGenericArray(val x: Array) +inline class FooGenericArray2(val x: Array>) + +inline class FooStarProjectedArray(val x: Array<*>) +inline class FooStarProjectedArray2(val x: Array>) + inline class Bar(val u: Unit) inline class BarNullable(val u: Unit?) diff --git a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassWithForbiddenUnderlyingType.txt b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassWithForbiddenUnderlyingType.txt index 4ab20769e03..df80a75d649 100644 --- a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassWithForbiddenUnderlyingType.txt +++ b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassWithForbiddenUnderlyingType.txt @@ -40,6 +40,22 @@ public final inline class Foo { public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String } +public final inline class FooGenericArray { + public constructor FooGenericArray(/*0*/ x: kotlin.Array) + public final val x: kotlin.Array + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + +public final inline class FooGenericArray2 { + public constructor FooGenericArray2(/*0*/ x: kotlin.Array>) + public final val x: kotlin.Array> + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + public final inline class FooNullable { public constructor FooNullable(/*0*/ x: T?) public final val x: T? @@ -47,3 +63,19 @@ public final inline class FooNullable { public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String } + +public final inline class FooStarProjectedArray { + public constructor FooStarProjectedArray(/*0*/ x: kotlin.Array<*>) + public final val x: kotlin.Array<*> + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + +public final inline class FooStarProjectedArray2 { + public constructor FooStarProjectedArray2(/*0*/ x: kotlin.Array>) + public final val x: kotlin.Array> + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index c41f0f81b21..bf990665121 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -10857,11 +10857,21 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/inlineClasses/basicInlineClassDeclarationDisabled.kt"); } + @TestMetadata("delegatedPropertyInInlineClass.kt") + public void testDelegatedPropertyInInlineClass() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/delegatedPropertyInInlineClass.kt"); + } + @TestMetadata("identityComparisonWithInlineClasses.kt") public void testIdentityComparisonWithInlineClasses() throws Exception { runTest("compiler/testData/diagnostics/tests/inlineClasses/identityComparisonWithInlineClasses.kt"); } + @TestMetadata("inlineClassCannotImplementInterfaceByDelegation.kt") + public void testInlineClassCannotImplementInterfaceByDelegation() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/inlineClassCannotImplementInterfaceByDelegation.kt"); + } + @TestMetadata("inlineClassDeclarationCheck.kt") public void testInlineClassDeclarationCheck() throws Exception { runTest("compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index fef89033790..83412c16fbc 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -10857,11 +10857,21 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/inlineClasses/basicInlineClassDeclarationDisabled.kt"); } + @TestMetadata("delegatedPropertyInInlineClass.kt") + public void testDelegatedPropertyInInlineClass() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/delegatedPropertyInInlineClass.kt"); + } + @TestMetadata("identityComparisonWithInlineClasses.kt") public void testIdentityComparisonWithInlineClasses() throws Exception { runTest("compiler/testData/diagnostics/tests/inlineClasses/identityComparisonWithInlineClasses.kt"); } + @TestMetadata("inlineClassCannotImplementInterfaceByDelegation.kt") + public void testInlineClassCannotImplementInterfaceByDelegation() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/inlineClassCannotImplementInterfaceByDelegation.kt"); + } + @TestMetadata("inlineClassDeclarationCheck.kt") public void testInlineClassDeclarationCheck() throws Exception { runTest("compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt");