diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineClassDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineClassDeclarationChecker.kt index 960a69c9988..601b8b94511 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineClassDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineClassDeclarationChecker.kt @@ -221,20 +221,15 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() { isRecursiveInlineClassType(hashSetOf(), session) private fun ConeKotlinType.isRecursiveInlineClassType(visited: HashSet, session: FirSession): Boolean { - if (!visited.add(this)) return true - val asRegularClass = this.toRegularClassSymbol(session) ?: return false - - if (!asRegularClass.isInlineOrValueClass()) return false + val asRegularClass = this.toRegularClassSymbol(session)?.takeIf { it.isInlineOrValueClass() } ?: return false val primaryConstructor = asRegularClass.declarationSymbols .firstOrNull { it is FirConstructorSymbol && it.isPrimary } as FirConstructorSymbol? ?: return false - return primaryConstructor - .valueParameterSymbols - .firstOrNull() - ?.resolvedReturnTypeRef - ?.coneType - ?.isRecursiveInlineClassType(visited, session) == true + + return !visited.add(this) || primaryConstructor.valueParameterSymbols.any { + it.resolvedReturnTypeRef.coneType.isRecursiveInlineClassType(visited, session) + }.also { visited.remove(this) } } private fun FirRegularClass.isSubtypeOfCloneable(session: FirSession): Boolean { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt index 823fe63de5e..3da2a90956b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt @@ -270,7 +270,7 @@ class DeclarationsChecker( if (declaration is KtPrimaryConstructor && !DescriptorUtils.isAnnotationClass(constructorDescriptor.constructedClass) && - !constructorDescriptor.constructedClass.isInlineClass() + !constructorDescriptor.constructedClass.isInlineOrValueClass() ) { for (parameter in declaration.valueParameters) { if (parameter.hasValOrVar()) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt index b85afc405ca..d7748774d41 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt @@ -8,7 +8,6 @@ package org.jetbrains.kotlin.resolve.checkers import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.config.LanguageFeature -import org.jetbrains.kotlin.contracts.parsing.isEqualsDescriptor import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.KtTokens @@ -16,12 +15,10 @@ import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.modalityModifier import org.jetbrains.kotlin.resolve.* -import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.types.typeUtil.isUnit @@ -80,7 +77,7 @@ object InlineClassDeclarationChecker : DeclarationChecker { return } - if (context.languageVersionSettings.supportsFeature(LanguageFeature.ValueClasses)) { + if (context.languageVersionSettings.supportsFeature(LanguageFeature.ValueClasses) && descriptor.isValueClass()) { if (primaryConstructor.valueParameters.isEmpty()) { (primaryConstructor.valueParameterList ?: declaration).let { trace.report(Errors.VALUE_CLASS_EMPTY_CONSTRUCTOR.on(it)) @@ -95,7 +92,9 @@ object InlineClassDeclarationChecker : DeclarationChecker { } var baseParametersOk = true - for (baseParameter in primaryConstructor.valueParameters) { + val baseParameterTypes = descriptor.safeAs()?.defaultType?.substitutedUnderlyingTypes() ?: emptyList() + + for ((baseParameter, baseParameterType) in primaryConstructor.valueParameters zip baseParameterTypes) { if (!isParameterAcceptableForInlineClass(baseParameter)) { trace.report(Errors.VALUE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER.on(baseParameter)) @@ -103,7 +102,6 @@ object InlineClassDeclarationChecker : DeclarationChecker { continue } - val baseParameterType = descriptor.safeAs()?.defaultType?.substitutedUnderlyingType() val baseParameterTypeReference = baseParameter.typeReference if (baseParameterType != null && baseParameterTypeReference != null) { if (baseParameterType.isInapplicableParameterType()) { @@ -112,7 +110,7 @@ object InlineClassDeclarationChecker : DeclarationChecker { continue } - if (baseParameterType.isRecursiveInlineClassType()) { // todo check work for MF VC + if (baseParameterType.isRecursiveInlineOrValueClassType()) { trace.report(Errors.VALUE_CLASS_CANNOT_BE_RECURSIVE.on(baseParameterTypeReference)) baseParametersOk = false continue @@ -176,7 +174,7 @@ class PropertiesWithBackingFieldsInsideInlineClass : DeclarationChecker { if (declaration !is KtProperty) return if (descriptor !is PropertyDescriptor) return - if (!descriptor.containingDeclaration.isInlineClass()) return + if (!descriptor.containingDeclaration.isInlineOrValueClass()) return if (context.trace.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor) == true) { context.trace.report(Errors.PROPERTY_WITH_BACKING_FIELD_INSIDE_VALUE_CLASS.on(declaration)) @@ -194,7 +192,7 @@ class InnerClassInsideInlineClass : DeclarationChecker { if (descriptor !is ClassDescriptor) return if (!descriptor.isInner) return - if (!descriptor.containingDeclaration.isInlineClass()) return + if (!descriptor.containingDeclaration.isInlineOrValueClass()) return context.trace.report(Errors.INNER_CLASS_INSIDE_VALUE_CLASS.on(declaration.modifierList!!.getModifier(KtTokens.INNER_KEYWORD)!!)) } @@ -208,7 +206,7 @@ class ReservedMembersAndConstructsForInlineClass : DeclarationChecker { override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { val containingDeclaration = descriptor.containingDeclaration ?: return - if (!containingDeclaration.isInlineClass()) return + if (!containingDeclaration.isInlineOrValueClass()) return if (descriptor !is FunctionDescriptor) return diff --git a/compiler/testData/codegen/box/valueClasses/equality.kt b/compiler/testData/codegen/box/valueClasses/equality.kt index 012f794ef1d..03ab4578f2d 100644 --- a/compiler/testData/codegen/box/valueClasses/equality.kt +++ b/compiler/testData/codegen/box/valueClasses/equality.kt @@ -23,6 +23,8 @@ value class F6(val x: String) OPTIONAL_JVM_INLINE_ANNOTATION value class A( + val f1: F1, + val f2: F2, val f3: F3, val f4: F4, val f5: F5, @@ -46,7 +48,7 @@ fun box(): String { val f4 = F4(5) val f5 = F5(UInt.MAX_VALUE.dec()) val f6 = F6("678") - val a1 = A(f3, f4, f5, f6, 9, UInt.MAX_VALUE - 2U, "0") + val a1 = A(f1, f2, f3, f4, f5, f6, 9, UInt.MAX_VALUE - 2U, "0") val a2 = a1 val b = B(a1, a2) @@ -60,6 +62,26 @@ fun box(): String { assert(f5.x == UInt.MAX_VALUE - 1U) assert(f6.x == "678") + assert(f1 == a1.f1) + assert(f1.hashCode() == a1.f1.hashCode()) + assert(f1.toString() == a1.f1.toString()) + assert(f1 == a2.f1) + assert(f1.hashCode() == a2.f1.hashCode()) + assert(f1.toString() == a2.f1.toString()) + assert(a1.f1 == a2.f1) + assert(a1.f1.hashCode() == a2.f1.hashCode()) + assert(a1.f1.toString() == a2.f1.toString()) + + assert(f2 == a1.f2) + assert(f2.hashCode() == a1.f2.hashCode()) + assert(f2.toString() == a1.f2.toString()) + assert(f2 == a2.f2) + assert(f2.hashCode() == a2.f2.hashCode()) + assert(f2.toString() == a2.f2.toString()) + assert(a1.f2 == a2.f2) + assert(a1.f2.hashCode() == a2.f2.hashCode()) + assert(a1.f2.toString() == a2.f2.toString()) + assert(f1 == a1.f3.x) assert(f1.hashCode() == a1.f3.x.hashCode()) assert(f1.toString() == a1.f3.x.toString()) @@ -159,15 +181,17 @@ fun box(): String { assert(b.toString() == b.toString()) assert(b.hashCode() == b.hashCode()) + fun String.assertGoodBasicToString(expected: String) = assert(matches(Regex("^([_a-zA-Z]+[.])*$expected@[0-9a-f]+$"))) { this } + assert(f1.toString() == "F1(x=1)") { f1.toString() } assert(f2.toString() == "F2(x=4294967295)") { f2.toString() } - assert(f3.toString().startsWith("F3@")) { f3.toString() } // not data class yet + f3.toString().assertGoodBasicToString("F3") // not data class yet assert(f4.toString() == "F4(x=5)") { f4.toString() } assert(f5.toString() == "F5(x=4294967294)") { f5.toString() } assert(f6.toString() == "F6(x=678)") { f6.toString() } - assert(a1.toString().startsWith("A@")) { a1.toString() } // not data class yet - assert(a2.toString().startsWith("A@")) { a2.toString() } // not data class yet - assert(b.toString().let { it.startsWith("OverridenBToString(a1 = A@") && ", a2 = A@" in it }) { b.toString() } // not data class yet + a1.toString().assertGoodBasicToString("A") // not data class yet + a2.toString().assertGoodBasicToString("A") // not data class yet + assert(b.toString() == "OverridenBToString(a1 = $a1, a2 = $a2)") { b.toString() } return "OK" } diff --git a/compiler/testData/diagnostics/tests/valueClasses/recursiveMultiFieldValueClasses.kt b/compiler/testData/diagnostics/tests/valueClasses/recursiveMultiFieldValueClasses.kt index 2759cd833ec..6d33765bc08 100644 --- a/compiler/testData/diagnostics/tests/valueClasses/recursiveMultiFieldValueClasses.kt +++ b/compiler/testData/diagnostics/tests/valueClasses/recursiveMultiFieldValueClasses.kt @@ -38,6 +38,12 @@ value class C4(val x: D4?, val y: D4?, val y: C4?) +OPTIONAL_JVM_INLINE_ANNOTATION +value class E4(val x: E4?, val y: Int) + +OPTIONAL_JVM_INLINE_ANNOTATION +value class F4(val x: Int, val y: F4?) + OPTIONAL_JVM_INLINE_ANNOTATION @@ -66,3 +72,27 @@ value class A8T>>(val x: T>>(val x: T?, val y: T?) + +interface I1 +interface I2 + +OPTIONAL_JVM_INLINE_ANNOTATION +value class A( + val t1: List, + val t2: UInt, + val t3: List, + val t4: UInt, + val t5: C, + val t6: Int, + val t7: B, + val t8: String, + val t9: T, + val t10: Char, + val t11: T?, +) where T : I1, T : B?, T : I2 + +OPTIONAL_JVM_INLINE_ANNOTATION +value class B(val x: UInt, val a: A) : I1, I2 + +OPTIONAL_JVM_INLINE_ANNOTATION +value class C(val x: UInt, val a: A) diff --git a/compiler/testData/diagnostics/tests/valueClasses/recursiveMultiFieldValueClasses.txt b/compiler/testData/diagnostics/tests/valueClasses/recursiveMultiFieldValueClasses.txt index 516571b0222..dbe78d37167 100644 --- a/compiler/testData/diagnostics/tests/valueClasses/recursiveMultiFieldValueClasses.txt +++ b/compiler/testData/diagnostics/tests/valueClasses/recursiveMultiFieldValueClasses.txt @@ -1,5 +1,23 @@ package +@kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class A where T : B?, T : I2 { + public constructor A(/*0*/ t1: kotlin.collections.List, /*1*/ t2: kotlin.UInt, /*2*/ t3: kotlin.collections.List, /*3*/ t4: kotlin.UInt, /*4*/ t5: C, /*5*/ t6: kotlin.Int, /*6*/ t7: B, /*7*/ t8: kotlin.String, /*8*/ t9: T, /*9*/ t10: kotlin.Char, /*10*/ t11: T?) where T : B?, T : I2 + public final val t1: kotlin.collections.List + public final val t10: kotlin.Char + public final val t11: T? + public final val t2: kotlin.UInt + public final val t3: kotlin.collections.List + public final val t4: kotlin.UInt + public final val t5: C + public final val t6: kotlin.Int + public final val t7: B + public final val t8: kotlin.String + public final val t9: T + 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.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class A1 { public constructor A1(/*0*/ x: A1) public final val x: A1 @@ -28,9 +46,9 @@ package public constructor A4(/*0*/ x: B4, /*1*/ y: B4) public final val x: B4 public final val y: B4 - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class A5> { @@ -45,45 +63,54 @@ package public constructor A6>(/*0*/ x: T, /*1*/ y: T) public final val x: T public final val y: T - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class A7> { public constructor A7>(/*0*/ x: T, /*1*/ y: T) public final val x: T public final val y: T - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class A8> { public constructor A8>(/*0*/ x: T?, /*1*/ y: T?) public final val x: T? public final val y: T? - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class B : I1, I2 { + public constructor B(/*0*/ x: kotlin.UInt, /*1*/ a: A) + public final val a: A + public final val x: kotlin.UInt + public open override /*2*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*synthesized*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class B1 { public constructor B1(/*0*/ x: B1, /*1*/ y: B1) public final val x: B1 public final val y: B1 - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class B2 { public constructor B2(/*0*/ x: A2, /*1*/ y: A2) public final val x: A2 public final val y: A2 - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class B3 { @@ -98,18 +125,18 @@ package public constructor B4(/*0*/ x: A4, /*1*/ y: A4) public final val x: A4 public final val y: A4 - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class B5> { public constructor B5>(/*0*/ x: T, /*1*/ y: T) public final val x: T public final val y: T - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class B6> { @@ -124,15 +151,24 @@ package public constructor B7>(/*0*/ x: T, /*1*/ y: T) public final val x: T public final val y: T - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class B8> { public constructor B8>(/*0*/ x: T?, /*1*/ y: T?) public final val x: T? public final val y: T? + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class C { + public constructor C(/*0*/ x: kotlin.UInt, /*1*/ a: A) + public final val a: A + public final val x: kotlin.UInt 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 @@ -142,16 +178,47 @@ package public constructor C4(/*0*/ x: D4?, /*1*/ y: D4?) public final val x: D4? public final val y: D4? - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class D4 { public constructor D4(/*0*/ x: D4?, /*1*/ y: C4?) public final val x: D4? public final val y: C4? - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + +@kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class E4 { + public constructor E4(/*0*/ x: E4?, /*1*/ y: kotlin.Int) + public final val x: E4? + public final val y: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class F4 { + public constructor F4(/*0*/ x: kotlin.Int, /*1*/ y: F4?) + public final val x: kotlin.Int + public final val y: F4? + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface I1 { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface I2 { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + diff --git a/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.txt b/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.txt index 3441adc1500..1bc7e756382 100644 --- a/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.txt +++ b/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.txt @@ -45,17 +45,17 @@ package kotlin { public constructor A5(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) public final val x: kotlin.Int public final val y: kotlin.Int - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.jvm.JvmInline public final value class A6 { public constructor A6(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) public final val y: kotlin.Int - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.jvm.JvmInline public final value class A7 { diff --git a/compiler/testData/diagnostics/tests/valueClasses/valueClassWithForbiddenUnderlyingTypeMultiField.kt b/compiler/testData/diagnostics/tests/valueClasses/valueClassWithForbiddenUnderlyingTypeMultiField.kt index ffa0a5b40e9..ff57e0d3ebe 100644 --- a/compiler/testData/diagnostics/tests/valueClasses/valueClassWithForbiddenUnderlyingTypeMultiField.kt +++ b/compiler/testData/diagnostics/tests/valueClasses/valueClassWithForbiddenUnderlyingTypeMultiField.kt @@ -39,5 +39,11 @@ value class BarNullable(val u: Unit?, val y: Unit?) OPTIONAL_JVM_INLINE_ANNOTATION value class Baz(val u: Nothing, val y: Nothing) +OPTIONAL_JVM_INLINE_ANNOTATION +value class Baz1(val u: Nothing, val y: Int) + +OPTIONAL_JVM_INLINE_ANNOTATION +value class Baz2(val u: Int, val y: Nothing) + OPTIONAL_JVM_INLINE_ANNOTATION value class BazNullable(val u: Nothing?, val y: Nothing?) diff --git a/compiler/testData/diagnostics/tests/valueClasses/valueClassWithForbiddenUnderlyingTypeMultiField.txt b/compiler/testData/diagnostics/tests/valueClasses/valueClassWithForbiddenUnderlyingTypeMultiField.txt index bd88e1dedcd..24a9100915a 100644 --- a/compiler/testData/diagnostics/tests/valueClasses/valueClassWithForbiddenUnderlyingTypeMultiField.txt +++ b/compiler/testData/diagnostics/tests/valueClasses/valueClassWithForbiddenUnderlyingTypeMultiField.txt @@ -4,36 +4,54 @@ package public constructor Bar(/*0*/ u: kotlin.Unit, /*1*/ y: kotlin.Unit) public final val u: kotlin.Unit public final val y: 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 + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class BarNullable { public constructor BarNullable(/*0*/ u: kotlin.Unit?, /*1*/ y: kotlin.Unit?) public final val u: kotlin.Unit? public final val y: 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 + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class Baz { public constructor Baz(/*0*/ u: kotlin.Nothing, /*1*/ y: kotlin.Nothing) public final val u: kotlin.Nothing public final val y: 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 + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class Baz1 { + public constructor Baz1(/*0*/ u: kotlin.Nothing, /*1*/ y: kotlin.Int) + public final val u: kotlin.Nothing + public final val y: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class Baz2 { + public constructor Baz2(/*0*/ u: kotlin.Int, /*1*/ y: kotlin.Nothing) + public final val u: kotlin.Int + public final val y: kotlin.Nothing + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class BazNullable { public constructor BazNullable(/*0*/ u: kotlin.Nothing?, /*1*/ y: kotlin.Nothing?) public final val u: kotlin.Nothing? public final val y: 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 + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class Empty { @@ -47,52 +65,53 @@ package public constructor Foo(/*0*/ x: T, /*1*/ y: T) public final val x: T public final val y: T - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class FooGenericArray { public constructor FooGenericArray(/*0*/ x: kotlin.Array, /*1*/ y: kotlin.Array) public final val x: kotlin.Array public final val y: kotlin.Array - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class FooGenericArray2 { public constructor FooGenericArray2(/*0*/ x: kotlin.Array>, /*1*/ y: kotlin.Array>) public final val x: kotlin.Array> public final val y: kotlin.Array> - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class FooNullable { public constructor FooNullable(/*0*/ x: T?, /*1*/ y: T?) public final val x: T? public final val y: T? - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class FooStarProjectedArray { public constructor FooStarProjectedArray(/*0*/ x: kotlin.Array<*>, /*1*/ y: kotlin.Array<*>) public final val x: kotlin.Array<*> public final val y: kotlin.Array<*> - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @kotlin.Suppress(names = {"OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE"}) @kotlin.jvm.JvmInline public final value class FooStarProjectedArray2 { public constructor FooStarProjectedArray2(/*0*/ x: kotlin.Array>, /*1*/ y: kotlin.Array>) public final val x: kotlin.Array> public final val y: kotlin.Array> - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt index cfc5c8e886b..d02524a9255 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt @@ -19,34 +19,51 @@ val JVM_INLINE_ANNOTATION_CLASS_ID = ClassId.topLevel(JVM_INLINE_ANNOTATION_FQ_N // FIXME: DeserializedClassDescriptor in reflection do not have @JvmInline annotation, that we // FIXME: would like to check as well. -fun DeclarationDescriptor.isInlineClass(): Boolean = - this is ClassDescriptor && (isInline || isValue) && unsubstitutedPrimaryConstructor?.valueParameters?.size == 1 +fun DeclarationDescriptor.isInlineClass(): Boolean = when { + this !is ClassDescriptor -> false + isInline -> true + else -> isValue && (unsubstitutedPrimaryConstructor?.valueParameters?.size?.let { it == 1 } ?: true) +} + +fun DeclarationDescriptor.isValueClass(): Boolean = + this is ClassDescriptor && isValue + +fun DeclarationDescriptor.isInlineOrValueClass(): Boolean = isInlineClass() || isValueClass() fun KotlinType.unsubstitutedUnderlyingType(): KotlinType? = constructor.declarationDescriptor.safeAs()?.inlineClassRepresentation?.underlyingType +fun KotlinType.unsubstitutedUnderlyingTypes(): List { + val declarationDescriptor = constructor.declarationDescriptor.safeAs() ?: return emptyList() + return when { + declarationDescriptor.isInlineClass() -> listOfNotNull(unsubstitutedUnderlyingType()) + declarationDescriptor.isValueClass() -> + declarationDescriptor.unsubstitutedPrimaryConstructor?.valueParameters?.map { it.type } ?: emptyList() + else -> emptyList() + } +} + + fun KotlinType.isInlineClassType(): Boolean = constructor.declarationDescriptor?.isInlineClass() ?: false fun KotlinType.substitutedUnderlyingType(): KotlinType? = unsubstitutedUnderlyingType()?.let { TypeSubstitutor.create(this).substitute(it, Variance.INVARIANT) } -fun KotlinType.isRecursiveInlineClassType(): Boolean = - isRecursiveInlineClassTypeInner(hashSetOf()) +fun KotlinType.substitutedUnderlyingTypes(): List = + unsubstitutedUnderlyingTypes().map { TypeSubstitutor.create(this).substitute(it, Variance.INVARIANT) } -private fun KotlinType.isRecursiveInlineClassTypeInner(visited: HashSet): Boolean { - val descriptor = constructor.declarationDescriptor?.original ?: return false +fun KotlinType.isRecursiveInlineOrValueClassType(): Boolean = + isRecursiveInlineOrValueClassTypeInner(hashSetOf()) - if (!visited.add(descriptor)) return true - - return when (descriptor) { - is ClassDescriptor -> - descriptor.isInlineClass() && - unsubstitutedUnderlyingType()?.isRecursiveInlineClassTypeInner(visited) == true - - is TypeParameterDescriptor -> - descriptor.upperBounds.any { it.isRecursiveInlineClassTypeInner(visited) } - - else -> false +private fun KotlinType.isRecursiveInlineOrValueClassTypeInner(visited: HashSet): Boolean { + val types = when (val descriptor = constructor.declarationDescriptor?.original?.takeIf { it.isInlineOrValueClass() }) { + is ClassDescriptor -> if (descriptor.isInlineOrValueClass()) unsubstitutedUnderlyingTypes() else emptyList() + is TypeParameterDescriptor -> descriptor.upperBounds + else -> emptyList() + } + return types.any { + val classifier = it.constructor.declarationDescriptor?.original ?: return@any false + !visited.add(classifier) || it.isRecursiveInlineOrValueClassTypeInner(visited).also { visited.remove(classifier) } } }