Support of custom 'equals' and 'hashCode' in inline classes
'equals' from any made available for overriding in inline classes 'typed' equals made available for definition in inline classes 'typed' equals definition made compulsory if 'untyped' is overridden 'operator' keyword is allowed in 'typed' equals definition ^KT-24874: Fixed
This commit is contained in:
committed by
teamcity
parent
527e8dde27
commit
e0c8142106
+7
@@ -3876,6 +3876,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS) { firDiagnostic ->
|
||||
InefficientEqualsOverridingInInlineClassImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON) { firDiagnostic ->
|
||||
CannotAllUnderImportFromSingletonImpl(
|
||||
firDiagnostic.a,
|
||||
|
||||
+5
@@ -2704,6 +2704,11 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = RedundantInlineSuspendFunctionType::class
|
||||
}
|
||||
|
||||
abstract class InefficientEqualsOverridingInInlineClass : KtFirDiagnostic<KtDeclaration>() {
|
||||
override val diagnosticClass get() = InefficientEqualsOverridingInInlineClass::class
|
||||
abstract val className: String
|
||||
}
|
||||
|
||||
abstract class CannotAllUnderImportFromSingleton : KtFirDiagnostic<KtImportDirective>() {
|
||||
override val diagnosticClass get() = CannotAllUnderImportFromSingleton::class
|
||||
abstract val objectName: Name
|
||||
|
||||
+6
@@ -3261,6 +3261,12 @@ internal class RedundantInlineSuspendFunctionTypeImpl(
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.RedundantInlineSuspendFunctionType(), KtAbstractFirDiagnostic<KtElement>
|
||||
|
||||
internal class InefficientEqualsOverridingInInlineClassImpl(
|
||||
override val className: String,
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.InefficientEqualsOverridingInInlineClass(), KtAbstractFirDiagnostic<KtDeclaration>
|
||||
|
||||
internal class CannotAllUnderImportFromSingletonImpl(
|
||||
override val objectName: Name,
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
|
||||
+12
@@ -17854,6 +17854,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/identityComparisonWithInlineClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("illegalEqualsOverridingInInlineClass.kt")
|
||||
public void testIllegalEqualsOverridingInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassCanImplementInterfaceByDelegation.kt")
|
||||
public void testInlineClassCanImplementInterfaceByDelegation() throws Exception {
|
||||
@@ -17962,6 +17968,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/synchronizedForbidden.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
|
||||
public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
|
||||
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
|
||||
|
||||
+4
-2
@@ -1,3 +1,5 @@
|
||||
// !LANGUAGE: +CustomEqualsInInlineClasses
|
||||
|
||||
<!VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION!>value<!> class BackingFields(val x: Int) {
|
||||
<!PROPERTY_WITH_BACKING_FIELD_INSIDE_VALUE_CLASS!>val y<!> = 0
|
||||
var z: String
|
||||
@@ -16,8 +18,8 @@ inline class ReversedMembers(val x: Int) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>box<!>() {}
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>() {}
|
||||
|
||||
override fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(other: Any?) = true
|
||||
override fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>hashCode<!>() = 1
|
||||
<!INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS!>override fun equals(other: Any?)<!> = true
|
||||
override fun hashCode() = 1
|
||||
}
|
||||
|
||||
inline class SecondaryConstructors(val x: Int) {
|
||||
|
||||
+12
@@ -17854,6 +17854,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/identityComparisonWithInlineClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("illegalEqualsOverridingInInlineClass.kt")
|
||||
public void testIllegalEqualsOverridingInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassCanImplementInterfaceByDelegation.kt")
|
||||
public void testInlineClassCanImplementInterfaceByDelegation() throws Exception {
|
||||
@@ -17962,6 +17968,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/synchronizedForbidden.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
|
||||
public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
|
||||
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
|
||||
|
||||
+12
@@ -17854,6 +17854,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/identityComparisonWithInlineClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("illegalEqualsOverridingInInlineClass.kt")
|
||||
public void testIllegalEqualsOverridingInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassCanImplementInterfaceByDelegation.kt")
|
||||
public void testInlineClassCanImplementInterfaceByDelegation() throws Exception {
|
||||
@@ -17962,6 +17968,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/synchronizedForbidden.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
|
||||
public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
|
||||
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
|
||||
|
||||
+4
@@ -1397,6 +1397,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED by error<KtParameter>()
|
||||
|
||||
val REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE by warning<KtElement>(PositioningStrategy.SUSPEND_MODIFIER)
|
||||
|
||||
val INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS by warning<KtDeclaration>(PositioningStrategy.DECLARATION_SIGNATURE) {
|
||||
parameter<String>("className")
|
||||
}
|
||||
}
|
||||
|
||||
val IMPORTS by object : DiagnosticGroup("Imports") {
|
||||
|
||||
@@ -723,6 +723,7 @@ object FirErrors {
|
||||
val ILLEGAL_INLINE_PARAMETER_MODIFIER by error0<KtElement>(SourceElementPositioningStrategies.INLINE_PARAMETER_MODIFIER)
|
||||
val INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED by error0<KtParameter>()
|
||||
val REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE by warning0<KtElement>(SourceElementPositioningStrategies.SUSPEND_MODIFIER)
|
||||
val INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS by warning1<KtDeclaration, String>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
|
||||
|
||||
// Imports
|
||||
val CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON by error1<KtImportDirective, Name>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
|
||||
|
||||
+20
-2
@@ -32,7 +32,8 @@ import org.jetbrains.kotlin.name.StandardClassIds
|
||||
|
||||
object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
|
||||
|
||||
private val reservedFunctionNames = setOf("box", "unbox", "equals", "hashCode")
|
||||
private val boxAndUnboxNames = setOf("box", "unbox")
|
||||
private val equalsAndHashCodeNames = setOf("equals", "hashCode")
|
||||
private val javaLangFqName = FqName("java.lang")
|
||||
private val cloneableFqName = FqName("Cloneable")
|
||||
|
||||
@@ -92,7 +93,10 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
|
||||
is FirSimpleFunction -> {
|
||||
val functionName = innerDeclaration.name.asString()
|
||||
|
||||
if (functionName in reservedFunctionNames) {
|
||||
if (functionName in boxAndUnboxNames
|
||||
|| (functionName in equalsAndHashCodeNames
|
||||
&& !context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses))
|
||||
) {
|
||||
reporter.reportOn(
|
||||
innerDeclaration.source, FirErrors.RESERVED_MEMBER_INSIDE_VALUE_CLASS, functionName, context
|
||||
)
|
||||
@@ -193,6 +197,20 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses)) {
|
||||
val simpleFunctions = declaration.declarations.filterIsInstance<FirSimpleFunction>()
|
||||
simpleFunctions.singleOrNull() { it.overridesEqualsFromAny() }?.apply {
|
||||
if (declaration.symbol.isInline && simpleFunctions.none { it.isTypedEqualsInInlineClass(context.session) }) {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS,
|
||||
declaration.name.asString(),
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirProperty.isRelatedToParameter(parameter: FirValueParameter?) =
|
||||
|
||||
+19
-4
@@ -10,6 +10,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.Checks.Returns
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.Checks.ValueParametersCount
|
||||
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.containingClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isInline
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isOperator
|
||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
|
||||
@@ -185,10 +187,23 @@ private object OperatorFunctionChecks {
|
||||
checkFor(
|
||||
EQUALS,
|
||||
member,
|
||||
Checks.full("must override ''equals()'' in Any") { ctx, function ->
|
||||
val containingClassSymbol = function.containingClass()?.toFirRegularClassSymbol(ctx.session) ?: return@full true
|
||||
function.overriddenFunctions(containingClassSymbol, ctx).any {
|
||||
it.containingClass()?.classId == StandardClassIds.Any
|
||||
object : Check {
|
||||
override fun check(context: CheckerContext, function: FirSimpleFunction): String? {
|
||||
val containingClassSymbol = function.containingClass()?.toFirRegularClassSymbol(context.session) ?: return null
|
||||
val customEqualsSupported = context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses)
|
||||
|
||||
if (function.overriddenFunctions(containingClassSymbol, context)
|
||||
.any { it.containingClass()?.classId == StandardClassIds.Any }
|
||||
|| (customEqualsSupported && function.isTypedEqualsInInlineClass(context.session))
|
||||
) {
|
||||
return null
|
||||
}
|
||||
return buildString {
|
||||
append("must override ''equals()'' in Any")
|
||||
if (customEqualsSupported && containingClassSymbol.isInline) {
|
||||
append(" or define ''equals(other: ${containingClassSymbol.name}): Boolean''")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
+25
@@ -7,15 +7,23 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.hasModifier
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.modality
|
||||
import org.jetbrains.kotlin.fir.containingClass
|
||||
import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.classId
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.isBoolean
|
||||
import org.jetbrains.kotlin.fir.types.isNullableAny
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
internal fun isInsideExpectClass(containingClass: FirClass, context: CheckerContext): Boolean {
|
||||
return isInsideSpecificClass(containingClass, context) { klass -> klass is FirRegularClass && klass.isExpect }
|
||||
@@ -110,3 +118,20 @@ fun FirClassSymbol<*>.primaryConstructorSymbol(): FirConstructorSymbol? {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun FirSimpleFunction.isTypedEqualsInInlineClass(session: FirSession): Boolean =
|
||||
containingClass()?.toFirRegularClassSymbol(session)?.run {
|
||||
this@isTypedEqualsInInlineClass.contextReceivers.isEmpty()
|
||||
&& this@isTypedEqualsInInlineClass.receiverTypeRef == null
|
||||
&& this@isTypedEqualsInInlineClass.name == OperatorNameConventions.EQUALS
|
||||
&& this@isTypedEqualsInInlineClass.returnTypeRef.isBoolean
|
||||
&& isInline
|
||||
&& this@isTypedEqualsInInlineClass.valueParameters.size == 1
|
||||
&& this@isTypedEqualsInInlineClass.valueParameters[0].returnTypeRef.coneType.classId == classId
|
||||
} ?: false
|
||||
|
||||
fun FirSimpleFunction.overridesEqualsFromAny(): Boolean {
|
||||
return name == OperatorNameConventions.EQUALS && returnTypeRef.isBoolean
|
||||
&& valueParameters.size == 1 && valueParameters[0].returnTypeRef.isNullableAny
|
||||
&& contextReceivers.isEmpty() && receiverTypeRef == null
|
||||
}
|
||||
+7
@@ -134,6 +134,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CYCLIC_CONSTRUCTO
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CYCLIC_GENERIC_UPPER_BOUND
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CYCLIC_INHERITANCE_HIERARCHY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DANGEROUS_CHARACTERS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_NOT_PROPERTY_PARAMETER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_OVERRIDE_CONFLICT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_VARARG_PARAMETER
|
||||
@@ -2003,6 +2004,12 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
"Redundant 'suspend' modifier: lambda parameters of suspend function type uses existing continuation."
|
||||
)
|
||||
|
||||
map.put(
|
||||
INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS,
|
||||
"Overriding ''equals'' from ''Any'' in inline class alongside with lack of ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison",
|
||||
STRING
|
||||
)
|
||||
|
||||
//imports
|
||||
map.put(
|
||||
CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON,
|
||||
|
||||
+36
@@ -21353,6 +21353,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassAsLastExpressionInInLambdaGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassBothEqualsOverride.kt")
|
||||
public void testInlineClassBothEqualsOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassBothEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassEqualityShouldUseTotalOrderForFloatingPointData.kt")
|
||||
public void testInlineClassEqualityShouldUseTotalOrderForFloatingPointData() throws Exception {
|
||||
@@ -21365,6 +21371,24 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualityShouldUseTotalOrderForFloatingPointDataGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassEqualsConsistency.kt")
|
||||
public void testInlineClassEqualsConsistency() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsConsistency.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassEqualsOverride.kt")
|
||||
public void testInlineClassEqualsOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassEqualsOverridenForCollections.kt")
|
||||
public void testInlineClassEqualsOverridenForCollections() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverridenForCollections.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassFieldHandling.kt")
|
||||
public void testInlineClassFieldHandling() throws Exception {
|
||||
@@ -21389,6 +21413,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassFunctionInvokeGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassHashCodeOverride.kt")
|
||||
public void testInlineClassHashCodeOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassImplementsCollection.kt")
|
||||
public void testInlineClassImplementsCollection() throws Exception {
|
||||
@@ -21437,6 +21467,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassPropertyReferenceGetAndSetGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassUntypedEqualsOverriden.kt")
|
||||
public void testInlineClassUntypedEqualsOverriden() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassUntypedEqualsOverriden.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassValueCapturedInInlineLambda.kt")
|
||||
public void testInlineClassValueCapturedInInlineLambda() throws Exception {
|
||||
|
||||
@@ -428,6 +428,8 @@ public interface Errors {
|
||||
DiagnosticFactory0<PsiElement> VALUE_CLASS_CANNOT_BE_CLONEABLE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> INLINE_CLASS_DEPRECATED = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<KtContextReceiverList> INLINE_CLASS_CANNOT_HAVE_CONTEXT_RECEIVERS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<KtDeclaration, String> INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS =
|
||||
DiagnosticFactory1.create(WARNING, DECLARATION_SIGNATURE);
|
||||
|
||||
// Result class
|
||||
|
||||
|
||||
+3
@@ -787,6 +787,9 @@ public class DefaultErrorMessages {
|
||||
MAP.put(VALUE_CLASS_CANNOT_BE_CLONEABLE, "Value class cannot be Cloneable");
|
||||
MAP.put(INLINE_CLASS_DEPRECATED, "'inline' modifier is deprecated. Use 'value' instead");
|
||||
MAP.put(INLINE_CLASS_CANNOT_HAVE_CONTEXT_RECEIVERS, "Inline classes cannot have context receivers");
|
||||
MAP.put(INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS,
|
||||
"Overriding ''equals'' from ''Any'' in inline class alongside with lack of ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison",
|
||||
STRING);
|
||||
|
||||
MAP.put(RESULT_CLASS_IN_RETURN_TYPE, "'kotlin.Result' cannot be used as a return type");
|
||||
MAP.put(RESULT_CLASS_WITH_NULLABLE_OPERATOR, "Expression of type ''kotlin.Result'' cannot be used as a left operand of ''{0}''", STRING);
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.isTypedEqualsInInlineClass
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -44,11 +45,15 @@ object OperatorModifierChecker {
|
||||
|
||||
val checkResult = OperatorChecks.check(functionDescriptor)
|
||||
if (checkResult.isSuccess) {
|
||||
when (functionDescriptor.name) {
|
||||
in REM_TO_MOD_OPERATION_NAMES.keys ->
|
||||
when {
|
||||
functionDescriptor.name in REM_TO_MOD_OPERATION_NAMES.keys ->
|
||||
checkSupportsFeature(LanguageFeature.OperatorRem, languageVersionSettings, diagnosticHolder, modifier)
|
||||
OperatorNameConventions.PROVIDE_DELEGATE ->
|
||||
|
||||
functionDescriptor.name == OperatorNameConventions.PROVIDE_DELEGATE ->
|
||||
checkSupportsFeature(LanguageFeature.OperatorProvideDelegate, languageVersionSettings, diagnosticHolder, modifier)
|
||||
|
||||
functionDescriptor.isTypedEqualsInInlineClass() ->
|
||||
checkSupportsFeature(LanguageFeature.CustomEqualsInInlineClasses, languageVersionSettings, diagnosticHolder, modifier)
|
||||
}
|
||||
|
||||
if (functionDescriptor.name in REM_TO_MOD_OPERATION_NAMES.values &&
|
||||
|
||||
+21
-2
@@ -161,6 +161,21 @@ object InlineClassDeclarationChecker : DeclarationChecker {
|
||||
trace.report(Errors.VALUE_CLASS_CANNOT_BE_CLONEABLE.on(inlineOrValueKeyword))
|
||||
return
|
||||
}
|
||||
|
||||
fun getFunctionDescriptor(declaration: KtNamedFunction): SimpleFunctionDescriptor? =
|
||||
context.trace.bindingContext.get(BindingContext.FUNCTION, declaration)
|
||||
|
||||
fun isUntypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.overridesEqualsFromAny() ?: false
|
||||
fun isTypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.isTypedEqualsInInlineClass() ?: false
|
||||
fun KtClass.namedFunctions() = declarations.filterIsInstance<KtNamedFunction>()
|
||||
|
||||
if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses)) {
|
||||
declaration.namedFunctions().singleOrNull { isUntypedEquals(it) }?.apply {
|
||||
if (declaration.namedFunctions().none { isTypedEquals(it) }) {
|
||||
trace.report(Errors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS.on(this@apply, descriptor.name.asString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinType.isInapplicableParameterType() =
|
||||
@@ -213,7 +228,8 @@ class InnerClassInsideInlineClass : DeclarationChecker {
|
||||
class ReservedMembersAndConstructsForInlineClass : DeclarationChecker {
|
||||
|
||||
companion object {
|
||||
private val reservedFunctions = setOf("box", "unbox", "equals", "hashCode")
|
||||
private val boxAndUnboxNames = setOf("box", "unbox")
|
||||
private val equalsAndHashCodeNames = setOf("equals", "hashCode")
|
||||
}
|
||||
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
@@ -226,7 +242,10 @@ class ReservedMembersAndConstructsForInlineClass : DeclarationChecker {
|
||||
is SimpleFunctionDescriptor -> {
|
||||
val ktFunction = declaration as? KtFunction ?: return
|
||||
val functionName = descriptor.name.asString()
|
||||
if (functionName in reservedFunctions) {
|
||||
if (functionName in boxAndUnboxNames
|
||||
|| (functionName in equalsAndHashCodeNames
|
||||
&& !context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses))
|
||||
) {
|
||||
val nameIdentifier = ktFunction.nameIdentifier ?: return
|
||||
context.trace.report(Errors.RESERVED_MEMBER_INSIDE_VALUE_CLASS.on(nameIdentifier, functionName))
|
||||
}
|
||||
|
||||
+43
-8
@@ -24,14 +24,12 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.transformStatement
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.ir.types.makeNotNull
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions.EQUALS
|
||||
|
||||
val jvmInlineClassPhase = makeIrFilePhase(
|
||||
::JvmInlineClassLowering,
|
||||
@@ -487,15 +485,52 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
||||
val right = function.valueParameters[1]
|
||||
val type = left.type.unboxInlineClass()
|
||||
|
||||
val typedEqualsStaticReplacement = findStaticReplacementForTypedEquals(valueClass)
|
||||
val untypedEquals = valueClass.functions.single { overridesEqualsFromAny(it) }
|
||||
|
||||
function.body = context.createIrBuilder(valueClass.symbol).run {
|
||||
irExprBody(
|
||||
irEquals(
|
||||
coerceInlineClasses(irGet(left), left.type, type),
|
||||
coerceInlineClasses(irGet(right), right.type, type)
|
||||
)
|
||||
if (typedEqualsStaticReplacement == null) {
|
||||
if (untypedEquals.origin == IrDeclarationOrigin.DEFINED) {
|
||||
val boxFunction = this@JvmInlineClassLowering.context.inlineClassReplacements.getBoxFunction(valueClass)
|
||||
|
||||
fun irBox(expr: IrExpression) = irCall(boxFunction).apply { putValueArgument(0, expr) }
|
||||
|
||||
irCall(untypedEquals).apply {
|
||||
dispatchReceiver = irBox(irGet(left))
|
||||
putValueArgument(0, irBox(irGet(right)))
|
||||
}
|
||||
} else {
|
||||
irEquals(coerceInlineClasses(irGet(left), left.type, type), coerceInlineClasses(irGet(right), right.type, type))
|
||||
}
|
||||
} else {
|
||||
irCall(typedEqualsStaticReplacement).apply {
|
||||
putValueArgument(0, irGet(left))
|
||||
putValueArgument(1, irGet(right))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
valueClass.declarations += function
|
||||
}
|
||||
|
||||
private fun overridesEqualsFromAny(irFunction: IrFunction): Boolean {
|
||||
return irFunction.run {
|
||||
name == EQUALS && returnType.isBoolean() && valueParameters.size == 1
|
||||
&& valueParameters[0].type.isNullableAny() && contextReceiverParametersCount == 0 && extensionReceiverParameter == null
|
||||
}
|
||||
}
|
||||
|
||||
private fun findStaticReplacementForTypedEquals(valueClass: IrClass): IrFunction? {
|
||||
fun isTypedEquals(irFunction: IrFunction): Boolean {
|
||||
return irFunction.run {
|
||||
name == EQUALS && returnType.isBoolean() && valueParameters.size == 1
|
||||
&& (valueParameters[0].type.classFqName?.run { valueClass.hasEqualFqName(this) } ?: false)
|
||||
&& contextReceiverParametersCount == 0 && extensionReceiverParameter == null
|
||||
}
|
||||
}
|
||||
return valueClass.functions
|
||||
.singleOrNull { context.inlineClassReplacements.originalFunctionForStaticReplacement[it]?.run { isTypedEquals(this) } ?: false }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +128,17 @@ abstract class DataClassMembersGenerator(
|
||||
fun generateEqualsMethodBody(properties: List<IrProperty>) {
|
||||
val irType = irClass.defaultType
|
||||
|
||||
val typedEqualsFunction = irClass.functions.singleOrNull { it.descriptor.isTypedEqualsInInlineClass() }
|
||||
if (irClass.isSingleFieldValueClass && typedEqualsFunction != null) {
|
||||
+irIfThenReturnFalse(irNotIs(irOther(), irType))
|
||||
val otherCasted = irImplicitCast(irOther(), irType)
|
||||
+irReturn(irCall(typedEqualsFunction).apply {
|
||||
putArgument(typedEqualsFunction.dispatchReceiverParameter!!, irThis())
|
||||
putValueArgument(0, otherCasted)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!irClass.isValue) {
|
||||
+irIfThenReturnTrue(irEqeqeq(irThis(), irOther()))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// WITH_STDLIB
|
||||
// WORKS_WHEN_VALUE_CLASS
|
||||
// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
|
||||
interface I {
|
||||
fun getVal(): Int
|
||||
}
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC1(val x: Int) : I {
|
||||
override fun getVal(): Int {
|
||||
return x
|
||||
}
|
||||
|
||||
fun equals(other: IC1): Boolean {
|
||||
return x == other.x
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is I) {
|
||||
return false
|
||||
}
|
||||
return getVal() == other.getVal()
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return getVal()
|
||||
}
|
||||
}
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC2(val y: Int) : I {
|
||||
override fun getVal(): Int {
|
||||
return y * 10
|
||||
}
|
||||
|
||||
fun equals(other: IC2): Boolean {
|
||||
return y == other.y
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is I) {
|
||||
return false
|
||||
}
|
||||
return getVal() == other.getVal()
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return getVal()
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String = if (setOf(IC1(10), IC2(1)).size == 1) "OK" else "Fail"
|
||||
@@ -0,0 +1,47 @@
|
||||
// WITH_STDLIB
|
||||
// WORKS_WHEN_VALUE_CLASS
|
||||
// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
import java.lang.AssertionError
|
||||
import kotlin.math.abs
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC1(val x: Double) {
|
||||
fun equals(other: IC1): Boolean {
|
||||
return abs(x - other.x) < 0.5
|
||||
}
|
||||
}
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC2(val x: Int) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is IC2) {
|
||||
return false
|
||||
}
|
||||
return abs(x - other.x) < 2
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val a1Typed: IC1 = IC1(1.0)
|
||||
val b1Typed: IC1 = IC1(1.1)
|
||||
val c1Typed: IC1 = IC1(5.0)
|
||||
val a1Untyped: Any = a1Typed
|
||||
val b1Untyped: Any = b1Typed
|
||||
val c1Untyped: Any = c1Typed
|
||||
|
||||
val a2Typed: IC2 = IC2(1)
|
||||
val b2Typed: IC2 = IC2(2)
|
||||
val c2Typed: IC2 = IC2(5)
|
||||
val a2Untyped: Any = a2Typed
|
||||
val b2Untyped: Any = b2Typed
|
||||
val c2Untyped: Any = c2Typed
|
||||
|
||||
if ((a1Typed == b1Typed) != (a1Untyped == b1Untyped)) throw AssertionError()
|
||||
if ((a1Typed == c1Typed) != (a1Untyped == c1Untyped)) throw AssertionError()
|
||||
if ((a2Typed == b2Typed) != (a2Untyped == b2Untyped)) throw AssertionError()
|
||||
if ((a2Typed == c2Typed) != (a2Untyped == c2Untyped)) throw AssertionError()
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// WITH_STDLIB
|
||||
// WORKS_WHEN_VALUE_CLASS
|
||||
// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
import kotlin.math.abs
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC1(val value: Double) {
|
||||
fun equals(other: IC1): Boolean {
|
||||
return abs(value - other.value) < 0.1
|
||||
}
|
||||
}
|
||||
|
||||
interface I {
|
||||
fun equals(param: IC2): Boolean
|
||||
}
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC2(val value: Int) : I {
|
||||
override fun equals(param: IC2): Boolean {
|
||||
return abs(value - param.value) < 2
|
||||
}
|
||||
}
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC3(val value: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun box() =
|
||||
if (IC1(1.0) == IC1(1.05) && IC1(1.0) != IC1(1.2)
|
||||
&& IC2(5) == IC2(6) && IC2(5) != IC2(7)
|
||||
&& IC3(5) == IC3(5) && IC3(5) != IC3(6)
|
||||
&& IC1(1.0) != Any()) "OK" else "Fail"
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// WITH_STDLIB
|
||||
// WORKS_WHEN_VALUE_CLASS
|
||||
// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
import kotlin.math.abs
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC(val x: Double) {
|
||||
fun equals(other: IC): Boolean {
|
||||
return abs(x - other.x) < 0.1
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val set = setOf(IC(1.0), IC(1.5), IC(1.501))
|
||||
return if (set.size == 2) "OK" else "Fail"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// WITH_STDLIB
|
||||
// WORKS_WHEN_VALUE_CLASS
|
||||
// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class A(val value: MyClass) {
|
||||
override fun hashCode() = 42
|
||||
}
|
||||
|
||||
class MyClass() {
|
||||
override fun hashCode() = -1
|
||||
}
|
||||
|
||||
fun box(): String = if (A(MyClass()).hashCode() == 42) "OK" else "Fail"
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// WITH_STDLIB
|
||||
// WORKS_WHEN_VALUE_CLASS
|
||||
// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
import kotlin.math.abs
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC(val x: Int) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is IC) {
|
||||
return false
|
||||
}
|
||||
return abs(x - other.x) < 2
|
||||
}
|
||||
|
||||
override fun hashCode() = 0
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val set = setOf(IC(1), IC(2), IC(5))
|
||||
return if (set.size == 2 && IC(1) == IC(1) && IC(1) == IC(2) && IC(1) != IC(5)) "OK" else "Fail"
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// FIR status: not supported in JVM
|
||||
// IGNORE_BACKEND: JVM
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_STDLIB
|
||||
// WORKS_WHEN_VALUE_CLASS
|
||||
// LANGUAGE: +ValueClasses
|
||||
|
||||
-3
@@ -1,7 +1,4 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// FIR status: not supported in JVM
|
||||
// IGNORE_BACKEND: JVM
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_STDLIB
|
||||
// WORKS_WHEN_VALUE_CLASS
|
||||
// LANGUAGE: +ValueClasses, +GenericInlineClassParameter
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// !DIAGNOSTICS: -DEBUG_INFO_SMARTCAST
|
||||
// LANGUAGE: +CustomEqualsInInlineClasses
|
||||
|
||||
@JvmInline
|
||||
value class IC1(val x: Int) {
|
||||
<!INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS!>override fun equals(other: Any?): Boolean<!> {
|
||||
if (other !is IC1) {
|
||||
return false
|
||||
}
|
||||
return x == other.x
|
||||
}
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class IC2(val x: Int) {
|
||||
override fun hashCode() = 0
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class IC3(val x: Int) {
|
||||
override fun equals(other: Any?) = true
|
||||
|
||||
fun equals(other: IC3) = true
|
||||
|
||||
override fun hashCode() = 0
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC1 {
|
||||
public constructor IC1(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
public open override /*1*/ 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
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC2 {
|
||||
public constructor IC2(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ 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
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC3 {
|
||||
public constructor IC3(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
public final fun equals(/*0*/ other: IC3): kotlin.Boolean
|
||||
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
|
||||
}
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
// !LANGUAGE: +InlineClasses, -JvmInlineValueClasses
|
||||
// !LANGUAGE: +InlineClasses, -JvmInlineValueClasses, +CustomEqualsInInlineClasses
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
inline class IC1(val x: Any) {
|
||||
@@ -8,8 +8,8 @@ inline class IC1(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>() {}
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any) {}
|
||||
|
||||
override fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(other: Any?): Boolean = true
|
||||
override fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>hashCode<!>(): Int = 0
|
||||
<!INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS!>override fun equals(other: Any?): Boolean<!> = true
|
||||
override fun hashCode(): Int = 0
|
||||
}
|
||||
|
||||
inline class IC2(val x: Any) {
|
||||
@@ -19,15 +19,15 @@ inline class IC2(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any) {}
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(): Any = TODO()
|
||||
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(my: Any, other: Any): Boolean = true
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>hashCode<!>(a: Any): Int = 0
|
||||
fun equals(my: Any, other: Any): Boolean = true
|
||||
fun hashCode(a: Any): Int = 0
|
||||
}
|
||||
|
||||
inline class IC3(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>box<!>(x: Any): Any = TODO()
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any): Any = TODO()
|
||||
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(): Boolean = true
|
||||
fun equals(): Boolean = true
|
||||
}
|
||||
|
||||
interface WithBox {
|
||||
|
||||
Vendored
+6
-6
@@ -1,4 +1,4 @@
|
||||
// !LANGUAGE: +InlineClasses, -JvmInlineValueClasses
|
||||
// !LANGUAGE: +InlineClasses, -JvmInlineValueClasses, +CustomEqualsInInlineClasses
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
inline class IC1(val x: Any) {
|
||||
@@ -8,8 +8,8 @@ inline class IC1(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>() {}
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any) {}
|
||||
|
||||
override fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(other: Any?): Boolean = true
|
||||
override fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>hashCode<!>(): Int = 0
|
||||
<!INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS!>override fun equals(other: Any?): Boolean<!> = true
|
||||
override fun hashCode(): Int = 0
|
||||
}
|
||||
|
||||
inline class IC2(val x: Any) {
|
||||
@@ -19,15 +19,15 @@ inline class IC2(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any) {}
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(): Any = TODO()
|
||||
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(my: Any, other: Any): Boolean = true
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>hashCode<!>(a: Any): Int = 0
|
||||
fun equals(my: Any, other: Any): Boolean = true
|
||||
fun hashCode(a: Any): Int = 0
|
||||
}
|
||||
|
||||
inline class IC3(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>box<!>(x: Any): Any = TODO()
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any): Any = TODO()
|
||||
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(): Boolean = true
|
||||
fun equals(): Boolean = true
|
||||
}
|
||||
|
||||
interface WithBox {
|
||||
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// !DIAGNOSTICS: -DEBUG_INFO_SMARTCAST
|
||||
// LANGUAGE: +CustomEqualsInInlineClasses
|
||||
|
||||
|
||||
@JvmInline
|
||||
value class IC1(val x: Int) {
|
||||
override fun equals(other: Any?) = true
|
||||
|
||||
operator fun equals(other: IC1) = true
|
||||
|
||||
override fun hashCode() = 0
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class IC2(val x: Int) {
|
||||
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun equals(other: IC1) = true
|
||||
|
||||
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun equals(other: IC2) {
|
||||
}
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
package
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC1 {
|
||||
public constructor IC1(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
public final operator fun equals(/*0*/ other: IC1): kotlin.Boolean
|
||||
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
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC2 {
|
||||
public constructor IC2(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
public final operator fun equals(/*0*/ other: IC1): kotlin.Boolean
|
||||
public final operator fun equals(/*0*/ other: IC2): kotlin.Unit
|
||||
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
|
||||
}
|
||||
compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.fir.kt
Vendored
+6
-6
@@ -1,5 +1,5 @@
|
||||
// !SKIP_JAVAC
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// !LANGUAGE: +InlineClasses, +CustomEqualsInInlineClasses
|
||||
// ALLOW_KOTLIN_PACKAGE
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
@@ -15,8 +15,8 @@ value class IC1(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>() {}
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any) {}
|
||||
|
||||
override fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(other: Any?): Boolean = true
|
||||
override fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>hashCode<!>(): Int = 0
|
||||
<!INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS!>override fun equals(other: Any?): Boolean<!> = true
|
||||
override fun hashCode(): Int = 0
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
@@ -27,8 +27,8 @@ value class IC2(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any) {}
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(): Any = TODO()
|
||||
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(my: Any, other: Any): Boolean = true
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>hashCode<!>(a: Any): Int = 0
|
||||
fun equals(my: Any, other: Any): Boolean = true
|
||||
fun hashCode(a: Any): Int = 0
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
@@ -36,7 +36,7 @@ value class IC3(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>box<!>(x: Any): Any = TODO()
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any): Any = TODO()
|
||||
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(): Boolean = true
|
||||
fun equals(): Boolean = true
|
||||
}
|
||||
|
||||
interface WithBox {
|
||||
|
||||
Vendored
+6
-6
@@ -1,5 +1,5 @@
|
||||
// !SKIP_JAVAC
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// !LANGUAGE: +InlineClasses, +CustomEqualsInInlineClasses
|
||||
// ALLOW_KOTLIN_PACKAGE
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
@@ -15,8 +15,8 @@ value class IC1(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>() {}
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any) {}
|
||||
|
||||
override fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(other: Any?): Boolean = true
|
||||
override fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>hashCode<!>(): Int = 0
|
||||
<!INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS!>override fun equals(other: Any?): Boolean<!> = true
|
||||
override fun hashCode(): Int = 0
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
@@ -27,8 +27,8 @@ value class IC2(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any) {}
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(): Any = TODO()
|
||||
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(my: Any, other: Any): Boolean = true
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>hashCode<!>(a: Any): Int = 0
|
||||
fun equals(my: Any, other: Any): Boolean = true
|
||||
fun hashCode(a: Any): Int = 0
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
@@ -36,7 +36,7 @@ value class IC3(val x: Any) {
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>box<!>(x: Any): Any = TODO()
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>unbox<!>(x: Any): Any = TODO()
|
||||
|
||||
fun <!RESERVED_MEMBER_INSIDE_VALUE_CLASS!>equals<!>(): Boolean = true
|
||||
fun equals(): Boolean = true
|
||||
}
|
||||
|
||||
interface WithBox {
|
||||
|
||||
Generated
+12
@@ -17860,6 +17860,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/identityComparisonWithInlineClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("illegalEqualsOverridingInInlineClass.kt")
|
||||
public void testIllegalEqualsOverridingInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassCanImplementInterfaceByDelegation.kt")
|
||||
public void testInlineClassCanImplementInterfaceByDelegation() throws Exception {
|
||||
@@ -17968,6 +17974,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/synchronizedForbidden.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
|
||||
public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
|
||||
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
|
||||
|
||||
+6
@@ -20705,6 +20705,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassFunctionInvokeGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassHashCodeOverride.kt")
|
||||
public void testInlineClassHashCodeOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassImplementsCollection.kt")
|
||||
public void testInlineClassImplementsCollection() throws Exception {
|
||||
|
||||
+36
@@ -21353,6 +21353,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassAsLastExpressionInInLambdaGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassBothEqualsOverride.kt")
|
||||
public void testInlineClassBothEqualsOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassBothEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassEqualityShouldUseTotalOrderForFloatingPointData.kt")
|
||||
public void testInlineClassEqualityShouldUseTotalOrderForFloatingPointData() throws Exception {
|
||||
@@ -21365,6 +21371,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualityShouldUseTotalOrderForFloatingPointDataGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassEqualsConsistency.kt")
|
||||
public void testInlineClassEqualsConsistency() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsConsistency.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassEqualsOverride.kt")
|
||||
public void testInlineClassEqualsOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassEqualsOverridenForCollections.kt")
|
||||
public void testInlineClassEqualsOverridenForCollections() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverridenForCollections.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassFieldHandling.kt")
|
||||
public void testInlineClassFieldHandling() throws Exception {
|
||||
@@ -21389,6 +21413,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassFunctionInvokeGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassHashCodeOverride.kt")
|
||||
public void testInlineClassHashCodeOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassImplementsCollection.kt")
|
||||
public void testInlineClassImplementsCollection() throws Exception {
|
||||
@@ -21437,6 +21467,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassPropertyReferenceGetAndSetGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassUntypedEqualsOverriden.kt")
|
||||
public void testInlineClassUntypedEqualsOverriden() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassUntypedEqualsOverriden.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassValueCapturedInInlineLambda.kt")
|
||||
public void testInlineClassValueCapturedInInlineLambda() throws Exception {
|
||||
|
||||
+5
@@ -17420,6 +17420,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassFunctionInvokeGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@TestMetadata("inlineClassHashCodeOverride.kt")
|
||||
public void testInlineClassHashCodeOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@TestMetadata("inlineClassImplementsCollection.kt")
|
||||
public void testInlineClassImplementsCollection() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassImplementsCollection.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
|
||||
@@ -284,6 +284,8 @@ enum class LanguageFeature(
|
||||
DataObjects(KOTLIN_1_9), // KT-4107
|
||||
ProhibitAccessToEnumCompanionMembersInEnumConstructorCall(KOTLIN_1_9, kind = BUG_FIX), // KT-49110
|
||||
ReferencesToSyntheticJavaProperties(KOTLIN_1_9), // KT-8575
|
||||
CustomEqualsInInlineClasses(KOTLIN_1_9, kind = UNSTABLE_FEATURE), // KT-24874
|
||||
|
||||
|
||||
// Disabled for indefinite time. See KT-53751
|
||||
IgnoreNullabilityForErasedValueParameters(sinceVersion = null, kind = BUG_FIX),
|
||||
|
||||
@@ -11,9 +11,13 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
||||
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||
import org.jetbrains.kotlin.types.typeUtil.isBoolean
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNullableAny
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
@@ -88,3 +92,14 @@ fun DeclarationDescriptor.containingPackage(): FqName? {
|
||||
}
|
||||
|
||||
object DeserializedDeclarationsFromSupertypeConflictDataKey : CallableDescriptor.UserDataKey<CallableMemberDescriptor>
|
||||
|
||||
fun FunctionDescriptor.isTypedEqualsInInlineClass(): Boolean = name == OperatorNameConventions.EQUALS
|
||||
&& (returnType?.isBoolean() ?: false) && containingDeclaration.isInlineClass()
|
||||
&& valueParameters.size == 1 && valueParameters[0].type == (containingDeclaration as? ClassDescriptor)?.defaultType
|
||||
&& contextReceiverParameters.isEmpty() && extensionReceiverParameter == null
|
||||
|
||||
|
||||
fun FunctionDescriptor.overridesEqualsFromAny(): Boolean = name == OperatorNameConventions.EQUALS
|
||||
&& (returnType?.isBoolean() ?: false)
|
||||
&& valueParameters.size == 1 && valueParameters[0].type.isNullableAny()
|
||||
&& contextReceiverParameters.isEmpty() && extensionReceiverParameter == null
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
@@ -198,8 +199,14 @@ object OperatorChecks : AbstractModifierChecks() {
|
||||
Checks(RANGE_UNTIL, MemberOrExtension, SingleValueParameter, NoDefaultAndVarargsCheck),
|
||||
Checks(EQUALS, Member) {
|
||||
fun DeclarationDescriptor.isAny() = this is ClassDescriptor && KotlinBuiltIns.isAny(this)
|
||||
ensure(containingDeclaration.isAny() || overriddenDescriptors.any { it.containingDeclaration.isAny() }) {
|
||||
"must override ''equals()'' in Any"
|
||||
ensure(containingDeclaration.isAny() || overriddenDescriptors.any { it.containingDeclaration.isAny() }
|
||||
|| (containingDeclaration.isInlineClass() && isTypedEqualsInInlineClass())) {
|
||||
buildString {
|
||||
append("must override ''equals()'' in Any")
|
||||
if (containingDeclaration.isInlineClass()) {
|
||||
append(" or define ''equals(other: ${containingDeclaration.name}): Boolean''")
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Checks(COMPARE_TO, MemberOrExtension, ReturnsInt, SingleValueParameter, NoDefaultAndVarargsCheck),
|
||||
|
||||
+6
@@ -16291,6 +16291,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassFunctionInvokeGeneric.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassHashCodeOverride.kt")
|
||||
public void testInlineClassHashCodeOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassInInitBlock.kt")
|
||||
public void testInlineClassInInitBlock() throws Exception {
|
||||
|
||||
+6
@@ -16309,6 +16309,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassFunctionInvokeGeneric.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassHashCodeOverride.kt")
|
||||
public void testInlineClassHashCodeOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassInInitBlock.kt")
|
||||
public void testInlineClassInInitBlock() throws Exception {
|
||||
|
||||
+5
@@ -14475,6 +14475,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassFunctionInvokeGeneric.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
}
|
||||
|
||||
@TestMetadata("inlineClassHashCodeOverride.kt")
|
||||
public void testInlineClassHashCodeOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
}
|
||||
|
||||
@TestMetadata("inlineClassInInitBlock.kt")
|
||||
public void testInlineClassInInitBlock() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassInInitBlock.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
|
||||
+8
@@ -16706,6 +16706,7 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
|
||||
register("compiler/testData/codegen/box/inlineClasses/inlineClassFieldHandlingGeneric.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
register("compiler/testData/codegen/box/inlineClasses/inlineClassFunctionInvoke.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
register("compiler/testData/codegen/box/inlineClasses/inlineClassFunctionInvokeGeneric.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
register("compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
register("compiler/testData/codegen/box/inlineClasses/inlineClassInInitBlock.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
register("compiler/testData/codegen/box/inlineClasses/inlineClassInInitBlockGeneric.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
register("compiler/testData/codegen/box/inlineClasses/inlineClassInStringTemplate.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
|
||||
@@ -17935,6 +17936,13 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassFunctionInvokeGeneric.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassHashCodeOverride.kt")
|
||||
public void testInlineClassHashCodeOverride() throws Exception {
|
||||
// There is a registered source transformer for the testcase: TransformersFunctions.getRemoveOptionalJvmInlineAnnotation()
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inlineClassInInitBlock.kt")
|
||||
public void testInlineClassInInitBlock() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user