Improve support of custom equals in inline classes
- Ensure that typed equals parameter's type is a star projection of corresponding inline class - Make possible to declare typed equals that returns 'Nothing' - Forbid type parameters in typed equals operator declaration ^KT-54909 fixed ^KT-54910 fixed
This commit is contained in:
+1
-1
@@ -3884,7 +3884,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
}
|
||||
add(FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS) { firDiagnostic ->
|
||||
InefficientEqualsOverridingInInlineClassImpl(
|
||||
firDiagnostic.a,
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
|
||||
+1
-1
@@ -2710,7 +2710,7 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
|
||||
abstract class InefficientEqualsOverridingInInlineClass : KtFirDiagnostic<KtNamedFunction>() {
|
||||
override val diagnosticClass get() = InefficientEqualsOverridingInInlineClass::class
|
||||
abstract val className: String
|
||||
abstract val type: KtType
|
||||
}
|
||||
|
||||
abstract class CannotAllUnderImportFromSingleton : KtFirDiagnostic<KtImportDirective>() {
|
||||
|
||||
+1
-1
@@ -3267,7 +3267,7 @@ internal class RedundantInlineSuspendFunctionTypeImpl(
|
||||
) : KtFirDiagnostic.RedundantInlineSuspendFunctionType(), KtAbstractFirDiagnostic<KtElement>
|
||||
|
||||
internal class InefficientEqualsOverridingInInlineClassImpl(
|
||||
override val className: String,
|
||||
override val type: KtType,
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.InefficientEqualsOverridingInInlineClass(), KtAbstractFirDiagnostic<KtNamedFunction>
|
||||
|
||||
+3
-3
@@ -18011,9 +18011,9 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
|
||||
public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
|
||||
@TestMetadata("typedEqualsOperatorDeclarationCheck.kt")
|
||||
public void testTypedEqualsOperatorDeclarationCheck() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-3
@@ -18011,9 +18011,9 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
|
||||
public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
|
||||
@TestMetadata("typedEqualsOperatorDeclarationCheck.kt")
|
||||
public void testTypedEqualsOperatorDeclarationCheck() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-3
@@ -18011,9 +18011,9 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
|
||||
public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
|
||||
@TestMetadata("typedEqualsOperatorDeclarationCheck.kt")
|
||||
public void testTypedEqualsOperatorDeclarationCheck() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -1400,7 +1400,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE by warning<KtElement>(PositioningStrategy.SUSPEND_MODIFIER)
|
||||
|
||||
val INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS by warning<KtNamedFunction>(PositioningStrategy.DECLARATION_NAME) {
|
||||
parameter<String>("className")
|
||||
parameter<ConeKotlinType>("type")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -724,7 +724,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<KtNamedFunction, String>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
val INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS by warning1<KtNamedFunction, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
|
||||
// Imports
|
||||
val CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON by error1<KtImportDirective, Name>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
|
||||
|
||||
+14
-4
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.isEquals
|
||||
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
|
||||
@@ -205,19 +206,28 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
|
||||
|
||||
if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses)) {
|
||||
var equalsFromAnyOverriding: FirSimpleFunction? = null
|
||||
var typedEqualsIsDefined = false
|
||||
var typedEquals: FirSimpleFunction? = null
|
||||
declaration.declarations.forEach {
|
||||
if (it !is FirSimpleFunction) {
|
||||
return@forEach
|
||||
}
|
||||
if (it.isEquals()) equalsFromAnyOverriding = it
|
||||
if (it.isTypedEqualsInInlineClass(context.session)) typedEqualsIsDefined = true
|
||||
if (it.isTypedEqualsInInlineClass(context.session)) typedEquals = it
|
||||
}
|
||||
if (equalsFromAnyOverriding != null && !typedEqualsIsDefined) {
|
||||
|
||||
if (typedEquals?.typeParameters?.isNotEmpty() == true) {
|
||||
reporter.reportOn(
|
||||
typedEquals!!.source,
|
||||
FirErrors.TYPE_PARAMETERS_NOT_ALLOWED,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
if (equalsFromAnyOverriding != null && typedEquals == null) {
|
||||
reporter.reportOn(
|
||||
equalsFromAnyOverriding!!.source,
|
||||
FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS,
|
||||
declaration.name.asString(),
|
||||
declaration.defaultType().replaceArgumentsWithStarProjections(),
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
+4
-1
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.fir.containingClassLookupTag
|
||||
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.defaultType
|
||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
@@ -196,7 +197,9 @@ private object OperatorFunctionChecks {
|
||||
return buildString {
|
||||
append("must override ''equals()'' in Any")
|
||||
if (customEqualsSupported && containingClassSymbol.isInline) {
|
||||
append(" or define ''equals(other: ${containingClassSymbol.name}): Boolean''")
|
||||
val expectedParameterTypeRendered =
|
||||
containingClassSymbol.defaultType().replaceArgumentsWithStarProjections().renderReadable();
|
||||
append(" or define ''equals(other: ${expectedParameterTypeRendered}): Boolean''")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-5
@@ -10,16 +10,18 @@ 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.modality
|
||||
import org.jetbrains.kotlin.fir.containingClassLookupTag
|
||||
import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr
|
||||
import org.jetbrains.kotlin.fir.containingClassLookupTag
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
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.isNothing
|
||||
import org.jetbrains.kotlin.fir.types.replaceArgumentsWithStarProjections
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
internal fun isInsideExpectClass(containingClass: FirClass, context: CheckerContext): Boolean {
|
||||
@@ -118,10 +120,13 @@ fun FirClassSymbol<*>.primaryConstructorSymbol(): FirConstructorSymbol? {
|
||||
|
||||
fun FirSimpleFunction.isTypedEqualsInInlineClass(session: FirSession): Boolean =
|
||||
containingClassLookupTag()?.toFirRegularClassSymbol(session)?.run {
|
||||
val inlineClassStarProjection = this@run.defaultType().replaceArgumentsWithStarProjections()
|
||||
with(this@isTypedEqualsInInlineClass) {
|
||||
contextReceivers.isEmpty() && receiverParameter == null && name == OperatorNameConventions.EQUALS
|
||||
&& this@run.isInline && valueParameters.size == 1 && returnTypeRef.isBoolean
|
||||
&& valueParameters[0].returnTypeRef.coneType.classId == this@run.classId
|
||||
contextReceivers.isEmpty() && receiverParameter == null
|
||||
&& name == OperatorNameConventions.EQUALS
|
||||
&& this@run.isInline && valueParameters.size == 1
|
||||
&& (returnTypeRef.isBoolean || returnTypeRef.isNothing)
|
||||
&& valueParameters[0].returnTypeRef.coneType == inlineClassStarProjection
|
||||
}
|
||||
} ?: false
|
||||
|
||||
|
||||
+2
-2
@@ -2011,8 +2011,8 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
|
||||
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
|
||||
"Overriding ''equals'' from ''Any'' in inline class without operator ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison",
|
||||
RENDER_TYPE
|
||||
)
|
||||
|
||||
//imports
|
||||
|
||||
@@ -426,7 +426,7 @@ 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<KtNamedFunction, String> INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS =
|
||||
DiagnosticFactory1<KtNamedFunction, KotlinType> INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS =
|
||||
DiagnosticFactory1.create(WARNING, DECLARATION_NAME);
|
||||
|
||||
// Result class
|
||||
|
||||
+2
-2
@@ -802,8 +802,8 @@ public class DefaultErrorMessages {
|
||||
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);
|
||||
"Overriding ''equals'' from ''Any'' in inline class without operator ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison",
|
||||
RENDER_TYPE);
|
||||
|
||||
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);
|
||||
|
||||
+14
-2
@@ -22,6 +22,7 @@ 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
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
||||
|
||||
private val javaLangCloneable = FqNameUnsafe("java.lang.Cloneable")
|
||||
|
||||
@@ -169,9 +170,20 @@ object InlineClassDeclarationChecker : DeclarationChecker {
|
||||
fun KtClass.namedFunctions() = declarations.filterIsInstance<KtNamedFunction>()
|
||||
|
||||
if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses)) {
|
||||
val typedEquals = declaration.namedFunctions().firstOrNull { isTypedEquals(it) }
|
||||
|
||||
if (typedEquals?.typeParameters?.isNotEmpty() == true) {
|
||||
trace.report(Errors.TYPE_PARAMETERS_NOT_ALLOWED.on(typedEquals))
|
||||
}
|
||||
|
||||
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()))
|
||||
if (typedEquals == null) {
|
||||
trace.report(
|
||||
Errors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS.on(
|
||||
this@apply,
|
||||
descriptor.defaultType.replaceArgumentsWithStarProjections()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-15
@@ -6,11 +6,10 @@
|
||||
package org.jetbrains.kotlin.backend.common.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isHashCode
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -38,23 +37,11 @@ class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irCla
|
||||
|
||||
fun createEqualsMethodDeclaration(): IrSimpleFunction =
|
||||
irClass.addSyntheticFunction("equals", context.irBuiltIns.booleanType).apply {
|
||||
overriddenSymbols = irClass.collectOverridenSymbols { it.isEquals(context) }
|
||||
overriddenSymbols = irClass.collectOverridenSymbols { it.isEquals() }
|
||||
addValueParameter("other", context.irBuiltIns.anyNType)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun IrFunction.isToString(): Boolean =
|
||||
name.asString() == "toString" && extensionReceiverParameter == null && contextReceiverParametersCount == 0 && valueParameters.isEmpty()
|
||||
|
||||
fun IrFunction.isHashCode() =
|
||||
name.asString() == "hashCode" && extensionReceiverParameter == null && contextReceiverParametersCount == 0 && valueParameters.isEmpty()
|
||||
|
||||
fun IrFunction.isEquals(context: BackendContext) =
|
||||
name.asString() == "equals" &&
|
||||
extensionReceiverParameter == null && contextReceiverParametersCount == 0 &&
|
||||
valueParameters.singleOrNull()?.type == context.irBuiltIns.anyNType
|
||||
|
||||
|
||||
fun IrClass.collectOverridenSymbols(predicate: (IrFunction) -> Boolean): List<IrSimpleFunctionSymbol> =
|
||||
superTypes.mapNotNull { it.getClass()?.functions?.singleOrNull(predicate)?.symbol }
|
||||
}
|
||||
|
||||
-1
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.ScopeWithIr
|
||||
import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isHashCode
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
|
||||
|
||||
+1
-3
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isEquals
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
|
||||
@@ -29,7 +28,6 @@ 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.InlineClassDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
|
||||
|
||||
val jvmInlineClassPhase = makeIrFilePhase(
|
||||
@@ -488,7 +486,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
||||
val right = function.valueParameters[1]
|
||||
val type = left.type.unboxInlineClass()
|
||||
|
||||
val untypedEquals = valueClass.functions.single { it.isEquals(context) }
|
||||
val untypedEquals = valueClass.functions.single { it.isEquals() }
|
||||
|
||||
function.body = context.createIrBuilder(valueClass.symbol).run {
|
||||
val context = this@JvmInlineClassLowering.context
|
||||
|
||||
+1
-4
@@ -5,8 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isEquals
|
||||
import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isToString
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irCatch
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
@@ -705,7 +703,6 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
||||
}
|
||||
}
|
||||
if (expression.isSpecializedMFVCEqEq) {
|
||||
val backendContext = context
|
||||
return context.createIrBuilder(getCurrentScopeSymbol()).irBlock {
|
||||
val leftArgument = expression.getValueArgument(0)!!
|
||||
val rightArgument = expression.getValueArgument(1)!!
|
||||
@@ -736,7 +733,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
||||
}
|
||||
} else {
|
||||
// left one is unboxed, right is not
|
||||
val equals = leftClass.functions.single { it.isEquals(backendContext) }
|
||||
val equals = leftClass.functions.single { it.isEquals() }
|
||||
+irCall(equals).apply {
|
||||
copyTypeArgumentsFrom(expression)
|
||||
dispatchReceiver = leftArgument
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
|
||||
function,
|
||||
replacement,
|
||||
when {
|
||||
function.isTypedEquals -> InlineClassAbi.mangledNameFor(function, mangleReturnTypes = false, useOldMangleRules = false)
|
||||
function.isTypedEquals() -> InlineClassAbi.mangledNameFor(function, mangleReturnTypes = false, useOldMangleRules = false)
|
||||
// If the original function has signature which need mangling we still need to replace it with a mangled version.
|
||||
(!function.isFakeOverride || function.findInterfaceImplementation(context.state.jvmDefaultMode) != null) && when (specificMangle) {
|
||||
SpecificMangle.Inline -> function.signatureRequiresMangling(includeInline = true, includeMFVC = false)
|
||||
|
||||
+2
-5
@@ -5,10 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.classFileContainsMethod
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.extensionReceiverName
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isStaticValueClassReplacement
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.parentClassId
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
@@ -64,7 +61,7 @@ class MemoizedInlineClassReplacements(
|
||||
// Mangle all functions in the body of an inline class
|
||||
it.parent.safeAs<IrClass>()?.isSingleFieldValueClass == true ->
|
||||
when {
|
||||
it.isTypedEquals -> createStaticReplacement(it).also {
|
||||
it.isTypedEquals() -> createStaticReplacement(it).also {
|
||||
it.name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
|
||||
specializedEqualsCache.computeIfAbsent(it.parentAsClass) { it }
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.overrides.FakeOverrideBuilderStrategy
|
||||
import org.jetbrains.kotlin.ir.overrides.IrOverridingUtil
|
||||
import org.jetbrains.kotlin.ir.overrides.IrUnimplementedOverridesStrategy
|
||||
import org.jetbrains.kotlin.ir.overrides.IrUnimplementedOverridesStrategy.ProcessAsFakeOverrides
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrPropertySymbolImpl
|
||||
@@ -1311,10 +1310,24 @@ fun IrBuiltIns.getKFunctionType(returnType: IrType, parameterTypes: List<IrType>
|
||||
fun IdSignature?.isComposite(): Boolean =
|
||||
this is IdSignature.CompositeSignature
|
||||
|
||||
val IrFunction.isTypedEquals: Boolean
|
||||
get() {
|
||||
val parentClass = parent as? IrClass ?: return false
|
||||
return name == OperatorNameConventions.EQUALS && returnType.isBoolean() && valueParameters.size == 1
|
||||
&& (valueParameters[0].type.classFqName?.run { parentClass.hasEqualFqName(this) } ?: false)
|
||||
&& contextReceiverParametersCount == 0 && extensionReceiverParameter == null && parentClass.isValue
|
||||
}
|
||||
fun IrFunction.isToString(): Boolean =
|
||||
name.asString() == "toString" && extensionReceiverParameter == null && contextReceiverParametersCount == 0 && valueParameters.isEmpty()
|
||||
|
||||
fun IrFunction.isHashCode() =
|
||||
name.asString() == "hashCode" && extensionReceiverParameter == null && contextReceiverParametersCount == 0 && valueParameters.isEmpty()
|
||||
|
||||
fun IrFunction.isEquals() =
|
||||
name.asString() == "equals" &&
|
||||
extensionReceiverParameter == null && contextReceiverParametersCount == 0 &&
|
||||
valueParameters.singleOrNull()?.type?.isNullableAny() == true
|
||||
|
||||
fun IrFunction.isTypedEquals(): Boolean {
|
||||
val parentClass = parent as? IrClass ?: return false
|
||||
val enclosingClassStartProjection = parentClass.symbol.starProjectedType
|
||||
return name == OperatorNameConventions.EQUALS
|
||||
&& (returnType.isBoolean() || returnType.isNothing())
|
||||
&& valueParameters.size == 1
|
||||
&& (valueParameters[0].type == enclosingClassStartProjection)
|
||||
&& contextReceiverParametersCount == 0 && extensionReceiverParameter == null
|
||||
&& parentClass.isValue
|
||||
}
|
||||
+39
@@ -81,8 +81,47 @@ public final class IC4 {
|
||||
public synthetic final method unbox-impl(): int
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline
|
||||
@kotlin.Metadata
|
||||
public final class IC5 {
|
||||
// source: 'inlineClassEqualsOverride.kt'
|
||||
private final field value: int
|
||||
private synthetic method <init>(p0: int): void
|
||||
public synthetic final static method box-impl(p0: int): IC5
|
||||
public static method constructor-impl(p0: int): int
|
||||
public method equals(p0: java.lang.Object): boolean
|
||||
public static method equals-impl(p0: int, p1: java.lang.Object): boolean
|
||||
public final static @org.jetbrains.annotations.NotNull method equals-impl0(p0: int, p1: int): java.lang.Void
|
||||
public final method getValue(): int
|
||||
public method hashCode(): int
|
||||
public static method hashCode-impl(p0: int): int
|
||||
public method toString(): java.lang.String
|
||||
public static method toString-impl(p0: int): java.lang.String
|
||||
public synthetic final method unbox-impl(): int
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline
|
||||
@kotlin.Metadata
|
||||
public final class IC6 {
|
||||
// source: 'inlineClassEqualsOverride.kt'
|
||||
private final field value: int
|
||||
private synthetic method <init>(p0: int): void
|
||||
public synthetic final static method box-impl(p0: int): IC6
|
||||
public static method constructor-impl(p0: int): int
|
||||
public final @org.jetbrains.annotations.NotNull method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Void
|
||||
public final static @org.jetbrains.annotations.NotNull method equals-impl(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Void
|
||||
public final static method equals-impl0(p0: int, p1: int): boolean
|
||||
public final method getValue(): int
|
||||
public method hashCode(): int
|
||||
public static method hashCode-impl(p0: int): int
|
||||
public method toString(): java.lang.String
|
||||
public static method toString-impl(p0: int): java.lang.String
|
||||
public synthetic final method unbox-impl(): int
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class InlineClassEqualsOverrideKt {
|
||||
// source: 'inlineClassEqualsOverride.kt'
|
||||
public synthetic final static method assertThrows(p0: kotlin.jvm.functions.Function0): boolean
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ interface I {
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC2(val value: Int) : I {
|
||||
override fun equals(param: IC2): Boolean {
|
||||
override operator fun equals(param: IC2): Boolean {
|
||||
return abs(value - param.value) < 2
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,26 @@ value class IC4(val value: Int) {
|
||||
override fun equals(other: Any?) = TODO()
|
||||
}
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC5(val value: Int) {
|
||||
operator fun equals(other: IC5): Nothing = TODO()
|
||||
}
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC6(val value: Int) {
|
||||
override fun equals(other: Any?): Nothing = TODO()
|
||||
}
|
||||
|
||||
inline fun <reified T> assertThrows(block: () -> Unit): Boolean {
|
||||
try {
|
||||
block.invoke()
|
||||
} catch (t: Throwable) {
|
||||
return t is T
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
fun box() = when {
|
||||
IC1(1.0) != IC1(1.05) -> "Fail 1.1"
|
||||
(IC1(1.0) as Any) != IC1(1.05) -> "Fail 1.2"
|
||||
@@ -68,5 +88,9 @@ fun box() = when {
|
||||
IC1(1.0) == Any() -> "Fail 7.1"
|
||||
(IC1(1.0) as Any) == Any() -> "Fail 7.2"
|
||||
|
||||
!assertThrows<NotImplementedError> { IC5(0) == IC5(1) } -> "Fail 8.1"
|
||||
!assertThrows<NotImplementedError> { IC6(0) == IC6(1) } -> "Fail 8.2"
|
||||
|
||||
|
||||
else -> "OK"
|
||||
}
|
||||
@@ -81,8 +81,47 @@ public final class IC4 {
|
||||
public synthetic final method unbox-impl(): int
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline
|
||||
@kotlin.Metadata
|
||||
public final class IC5 {
|
||||
// source: 'inlineClassEqualsOverride.kt'
|
||||
private final field value: int
|
||||
private synthetic method <init>(p0: int): void
|
||||
public synthetic final static method box-impl(p0: int): IC5
|
||||
public static method constructor-impl(p0: int): int
|
||||
public method equals(p0: java.lang.Object): boolean
|
||||
public static method equals-impl(p0: int, p1: java.lang.Object): boolean
|
||||
public final static @org.jetbrains.annotations.NotNull method equals-impl0(p0: int, p1: int): java.lang.Void
|
||||
public final method getValue(): int
|
||||
public method hashCode(): int
|
||||
public static method hashCode-impl(p0: int): int
|
||||
public method toString(): java.lang.String
|
||||
public static method toString-impl(p0: int): java.lang.String
|
||||
public synthetic final method unbox-impl(): int
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline
|
||||
@kotlin.Metadata
|
||||
public final class IC6 {
|
||||
// source: 'inlineClassEqualsOverride.kt'
|
||||
private final field value: int
|
||||
private synthetic method <init>(p0: int): void
|
||||
public synthetic final static method box-impl(p0: int): IC6
|
||||
public static method constructor-impl(p0: int): int
|
||||
public @org.jetbrains.annotations.NotNull method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Void
|
||||
public static @org.jetbrains.annotations.NotNull method equals-impl(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Void
|
||||
public final static method equals-impl0(p0: int, p1: int): boolean
|
||||
public final method getValue(): int
|
||||
public method hashCode(): int
|
||||
public static method hashCode-impl(p0: int): int
|
||||
public method toString(): java.lang.String
|
||||
public static method toString-impl(p0: int): java.lang.String
|
||||
public synthetic final method unbox-impl(): int
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class InlineClassEqualsOverrideKt {
|
||||
// source: 'inlineClassEqualsOverride.kt'
|
||||
public synthetic final static method assertThrows(p0: kotlin.jvm.functions.Function0): boolean
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import kotlin.math.abs
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC(val x: Double) {
|
||||
fun equals(other: IC): Boolean {
|
||||
operator fun equals(other: IC): Boolean {
|
||||
return abs(x - other.x) < 0.1
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -6,7 +6,7 @@
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC1<T : Number>(val x: T) {
|
||||
fun equals(other: Int) = false
|
||||
fun equals(other: IC1<T>) = true
|
||||
operator fun equals(other: IC1<*>) = true
|
||||
}
|
||||
|
||||
class Generic<T, R>(val x: T, val y: R)
|
||||
@@ -14,19 +14,19 @@ class Generic<T, R>(val x: T, val y: R)
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC2<T, R>(val value: Generic<T, R>) {
|
||||
fun equals(other: IC1<Double>) = false
|
||||
fun equals(other: IC2<T, R>) = true
|
||||
operator fun equals(other: IC2<*, *>) = true
|
||||
}
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC3<T>(val value: T) {
|
||||
fun equals(other: Int) = false
|
||||
fun equals(other: IC3<*>) = true
|
||||
operator fun equals(other: IC3<*>) = true
|
||||
}
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC4<T>(val value: T) {
|
||||
fun equals(other: String) = false
|
||||
fun equals(other: IC4<String>) = true
|
||||
operator fun equals(other: IC4<*>) = true
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class A(val x: Int) {
|
||||
fun equals(other: A) = true
|
||||
operator fun equals(other: A) = true
|
||||
}
|
||||
|
||||
class C
|
||||
@@ -20,13 +20,13 @@ value class B2(val x: A?)
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class D1(val x: C) {
|
||||
fun equals(other: D1) = true
|
||||
operator fun equals(other: D1) = true
|
||||
}
|
||||
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class D2(val x: C?) {
|
||||
fun equals(other: D2) = true
|
||||
operator fun equals(other: D2) = true
|
||||
}
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class A(val x: Int) {
|
||||
fun equals(other: A) = x % 5 == other.x % 5
|
||||
operator fun equals(other: A) = x % 5 == other.x % 5
|
||||
}
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
|
||||
Vendored
+10
-1
@@ -22,7 +22,16 @@ value class IC2(val x: Int) {
|
||||
value class IC3(val x: Int) {
|
||||
override fun equals(other: Any?) = true
|
||||
|
||||
fun equals(other: IC3) = true
|
||||
operator fun equals(other: IC3) = true
|
||||
|
||||
override fun hashCode() = 0
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class IC4<T>(val x: Int) {
|
||||
override fun <!INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS!>equals<!>(other: Any?) = true
|
||||
|
||||
fun equals(other: IC4<T>) = true
|
||||
|
||||
override fun hashCode() = 0
|
||||
}
|
||||
Vendored
+11
-1
@@ -19,8 +19,18 @@ package
|
||||
@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 final operator 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
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC4</*0*/ T> {
|
||||
public constructor IC4</*0*/ T>(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
public final fun equals(/*0*/ other: IC4<T>): 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
|
||||
}
|
||||
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// 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) {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class IC3<T>(val x: T) {
|
||||
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun equals(other: IC3<T>) = true
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class IC4<T>(val x: T) {
|
||||
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun equals(other: IC4<String>) = true
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class IC5<T: Number>(val x: T) {
|
||||
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun equals(other: T) = true
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class IC6<T, R>(val x: T) {
|
||||
operator fun<!TYPE_PARAMETERS_NOT_ALLOWED!><S1, S2><!> equals(other: IC6<*, *>) = true
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class IC7<T, R>(val x: T) {
|
||||
operator fun equals(other: IC7<*, *>) = true
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class IC8<T, R>(val x: T) {
|
||||
operator fun equals(other: IC8<*, *>): Nothing = TODO()
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
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
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC3</*0*/ T> {
|
||||
public constructor IC3</*0*/ T>(/*0*/ x: T)
|
||||
public final val x: T
|
||||
public final operator fun equals(/*0*/ other: IC3<T>): 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
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC4</*0*/ T> {
|
||||
public constructor IC4</*0*/ T>(/*0*/ x: T)
|
||||
public final val x: T
|
||||
public final operator fun equals(/*0*/ other: IC4<kotlin.String>): 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
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC5</*0*/ T : kotlin.Number> {
|
||||
public constructor IC5</*0*/ T : kotlin.Number>(/*0*/ x: T)
|
||||
public final val x: T
|
||||
public final operator fun equals(/*0*/ other: T): 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
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC6</*0*/ T, /*1*/ R> {
|
||||
public constructor IC6</*0*/ T, /*1*/ R>(/*0*/ x: T)
|
||||
public final val x: T
|
||||
public final operator fun </*0*/ S1, /*1*/ S2> equals(/*0*/ other: IC6<*, *>): 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
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC7</*0*/ T, /*1*/ R> {
|
||||
public constructor IC7</*0*/ T, /*1*/ R>(/*0*/ x: T)
|
||||
public final val x: T
|
||||
public final operator fun equals(/*0*/ other: IC7<*, *>): 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
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmInline public final value class IC8</*0*/ T, /*1*/ R> {
|
||||
public constructor IC8</*0*/ T, /*1*/ R>(/*0*/ x: T)
|
||||
public final val x: T
|
||||
public final operator fun equals(/*0*/ other: IC8<*, *>): kotlin.Nothing
|
||||
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
|
||||
}
|
||||
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
// 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
@@ -1,20 +0,0 @@
|
||||
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
|
||||
}
|
||||
Generated
+3
-3
@@ -18017,9 +18017,9 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
|
||||
public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
|
||||
@TestMetadata("typedEqualsOperatorDeclarationCheck.kt")
|
||||
public void testTypedEqualsOperatorDeclarationCheck() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -10,14 +10,11 @@ import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
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.classId
|
||||
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.types.typeUtil.*
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
@@ -94,10 +91,16 @@ fun DeclarationDescriptor.containingPackage(): FqName? {
|
||||
|
||||
object DeserializedDeclarationsFromSupertypeConflictDataKey : CallableDescriptor.UserDataKey<CallableMemberDescriptor>
|
||||
|
||||
fun FunctionDescriptor.isTypedEqualsInInlineClass() = name == OperatorNameConventions.EQUALS
|
||||
&& (returnType?.isBoolean() ?: false) && containingDeclaration.isInlineClass()
|
||||
&& valueParameters.size == 1 && valueParameters[0].type.constructor.declarationDescriptor.classId == (containingDeclaration as? ClassDescriptor)?.classId
|
||||
&& contextReceiverParameters.isEmpty() && extensionReceiverParameter == null
|
||||
fun FunctionDescriptor.isTypedEqualsInInlineClass(): Boolean {
|
||||
val inlineClassStarProjection =
|
||||
(containingDeclaration as? ClassDescriptor)?.takeIf { it.isInlineClass() }?.defaultType?.replaceArgumentsWithStarProjections()
|
||||
?: return false
|
||||
val returnType = returnType ?: return false
|
||||
return name == OperatorNameConventions.EQUALS
|
||||
&& (returnType.isBoolean() || returnType.isNothing())
|
||||
&& valueParameters.size == 1 && valueParameters[0].type == inlineClassStarProjection
|
||||
&& contextReceiverParameters.isEmpty() && extensionReceiverParameter == null
|
||||
}
|
||||
|
||||
|
||||
fun FunctionDescriptor.overridesEqualsFromAny(): Boolean = name == OperatorNameConventions.EQUALS
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
|
||||
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
||||
import org.jetbrains.kotlin.util.MemberKindCheck.Member
|
||||
import org.jetbrains.kotlin.util.MemberKindCheck.MemberOrExtension
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions.ASSIGNMENT_OPERATIONS
|
||||
@@ -198,12 +200,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() }
|
||||
|| (containingDeclaration.isInlineClass() && isTypedEqualsInInlineClass())) {
|
||||
ensure(containingDeclaration.isAny() || overriddenDescriptors.any { it.containingDeclaration.isAny() } || isTypedEqualsInInlineClass()) {
|
||||
buildString {
|
||||
append("must override ''equals()'' in Any")
|
||||
if (containingDeclaration.isInlineClass()) {
|
||||
append(" or define ''equals(other: ${containingDeclaration.name}): Boolean''")
|
||||
val expectedParameterTypeRendered = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(
|
||||
(containingDeclaration as ClassDescriptor).defaultType.replaceArgumentsWithStarProjections()
|
||||
)
|
||||
append(" or define ''equals(other: $expectedParameterTypeRendered): Boolean''")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user