Reserve box/unbox/equals/hashCode methods inside inline classes

#KT-26573 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2018-08-31 17:23:23 +03:00
parent 7f8863bfd0
commit 002a66fec1
10 changed files with 130 additions and 2 deletions
@@ -330,6 +330,7 @@ public interface Errors {
DiagnosticFactory0<PsiElement> INLINE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeReference> INLINE_CLASS_CANNOT_EXTEND_CLASSES = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeReference> INLINE_CLASS_CANNOT_BE_RECURSIVE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, String> RESERVED_MEMBER_INSIDE_INLINE_CLASS = DiagnosticFactory1.create(ERROR);
// Secondary constructors
@@ -661,6 +661,7 @@ public class DefaultErrorMessages {
MAP.put(INLINE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION, "Inline class cannot implement an interface by delegation");
MAP.put(INLINE_CLASS_CANNOT_EXTEND_CLASSES, "Inline class cannot extend classes");
MAP.put(INLINE_CLASS_CANNOT_BE_RECURSIVE, "Inline class cannot be recursive");
MAP.put(RESERVED_MEMBER_INSIDE_INLINE_CLASS, "Member with the name ''{0}'' is reserved for future releases", STRING);
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");
@@ -119,7 +119,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
SuspendOperatorsCheckers,
InlineClassDeclarationChecker,
PropertiesWithBackingFieldsInsideInlineClass(),
AnnotationClassTargetAndRetentionChecker()
AnnotationClassTargetAndRetentionChecker(),
ReservedMembersAndConstructsForInlineClass()
)
private val DEFAULT_CALL_CHECKERS = listOf(
@@ -139,4 +139,24 @@ class PropertiesWithBackingFieldsInsideInlineClass : DeclarationChecker {
context.trace.report(Errors.DELEGATED_PROPERTY_INSIDE_INLINE_CLASS.on(it))
}
}
}
class ReservedMembersAndConstructsForInlineClass : DeclarationChecker {
companion object {
private val reservedFunctions = setOf("box", "unbox", "equals", "hashCode")
}
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
val containingDeclaration = descriptor.containingDeclaration ?: return
if (!containingDeclaration.isInlineClass()) return
if (declaration is KtFunction && descriptor is FunctionDescriptor) {
val functionName = descriptor.name.asString()
if (functionName in reservedFunctions) {
val nameIdentifier = declaration.nameIdentifier ?: return
context.trace.report(Errors.RESERVED_MEMBER_INSIDE_INLINE_CLASS.on(nameIdentifier, functionName))
}
}
}
}
@@ -1,6 +1,8 @@
// !LANGUAGE: +InlineClasses
// IGNORE_BACKEND: JVM_IR
@file:Suppress("RESERVED_MEMBER_INSIDE_INLINE_CLASS")
inline class Z(val data: Int) {
override fun equals(other: Any?): Boolean =
other is Z &&
@@ -0,0 +1,39 @@
// !LANGUAGE: +InlineClasses
// !DIAGNOSTICS: -UNUSED_PARAMETER
inline class IC1(val x: Any) {
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>box<!>() {}
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>box<!>(x: Any) {}
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>unbox<!>() {}
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>unbox<!>(x: Any) {}
override fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>equals<!>(other: Any?): Boolean = true
override fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>hashCode<!>(): Int = 0
}
inline class IC2(val x: Any) {
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>box<!>(x: Any) {}
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>box<!>(): Any = TODO()
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>unbox<!>(x: Any) {}
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>unbox<!>(): Any = TODO()
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>equals<!>(my: Any, other: Any): Boolean = true
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>hashCode<!>(a: Any): Int = 0
}
inline class IC3(val x: Any) {
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>box<!>(x: Any): Any = TODO()
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>unbox<!>(x: Any): Any = TODO()
fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>equals<!>(): Boolean = true
}
interface WithBox {
fun box(): String
}
inline class IC4(val s: String) : WithBox {
override fun <!RESERVED_MEMBER_INSIDE_INLINE_CLASS!>box<!>(): String = ""
}
@@ -0,0 +1,54 @@
package
public final inline class IC1 {
public constructor IC1(/*0*/ x: kotlin.Any)
public final val x: kotlin.Any
public final fun box(): kotlin.Unit
public final fun box(/*0*/ x: kotlin.Any): kotlin.Unit
public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
public final fun unbox(): kotlin.Unit
public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Unit
}
public final inline class IC2 {
public constructor IC2(/*0*/ x: kotlin.Any)
public final val x: kotlin.Any
public final fun box(): kotlin.Any
public final fun box(/*0*/ x: kotlin.Any): kotlin.Unit
public final fun equals(/*0*/ my: kotlin.Any, /*1*/ other: kotlin.Any): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public final fun hashCode(/*0*/ a: kotlin.Any): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
public final fun unbox(): kotlin.Any
public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Unit
}
public final inline class IC3 {
public constructor IC3(/*0*/ x: kotlin.Any)
public final val x: kotlin.Any
public final fun box(/*0*/ x: kotlin.Any): kotlin.Any
public final fun equals(): kotlin.Boolean
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 fun unbox(/*0*/ x: kotlin.Any): kotlin.Any
}
public final inline class IC4 : WithBox {
public constructor IC4(/*0*/ s: kotlin.String)
public final val s: kotlin.String
public open override /*1*/ fun box(): kotlin.String
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 interface WithBox {
public abstract fun box(): 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
}
@@ -10947,6 +10947,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/inlineClasses/recursiveInlineClasses.kt");
}
@TestMetadata("reservedMembersAndConstructsInsideInlineClass.kt")
public void testReservedMembersAndConstructsInsideInlineClass() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/reservedMembersAndConstructsInsideInlineClass.kt");
}
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/unsignedLiteralsWithoutArtifactOnClasspath.kt");
@@ -10947,6 +10947,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/inlineClasses/recursiveInlineClasses.kt");
}
@TestMetadata("reservedMembersAndConstructsInsideInlineClass.kt")
public void testReservedMembersAndConstructsInsideInlineClass() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/reservedMembersAndConstructsInsideInlineClass.kt");
}
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/unsignedLiteralsWithoutArtifactOnClasspath.kt");
@@ -57,4 +57,4 @@ fun PropertyDescriptor.isUnderlyingPropertyOfInlineClass(): Boolean {
if (!containingDeclaration.isInlineClass()) return false
return (containingDeclaration as ClassDescriptor).underlyingRepresentation()?.name == this.name
}
}