T
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 6a5e923..5ae3715 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml
@@ -8,4 +8,7 @@ <component name="KotlinCompilerSettings"> <option name="additionalArguments" value="-version -Xallow-kotlin-package -Xskip-metadata-version-check" /> </component> + <component name="KotlinJpsPluginSettings"> + <option name="version" value="1.8.0-dev-446" /> + </component> </project> \ No newline at end of file
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index d9965bf..f202b55 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt
@@ -1372,8 +1372,8 @@ token, ) } - add(FirErrors.SEALED_INLINE_CLASS_WITH_UNDERLYING_VALUE) { firDiagnostic -> - SealedInlineClassWithUnderlyingValueImpl( + add(FirErrors.SEALED_INLINE_CLASS_WITH_PRIMARY_CONSTRUCTOR) { firDiagnostic -> + SealedInlineClassWithPrimaryConstructorImpl( firDiagnostic as KtPsiDiagnostic, token, )
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 32f3789..090a93a 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt
@@ -990,8 +990,8 @@ override val diagnosticClass get() = ValueObjectNotSealedInlineChild::class } - abstract class SealedInlineClassWithUnderlyingValue : KtFirDiagnostic<KtDeclaration>() { - override val diagnosticClass get() = SealedInlineClassWithUnderlyingValue::class + abstract class SealedInlineClassWithPrimaryConstructor : KtFirDiagnostic<KtDeclaration>() { + override val diagnosticClass get() = SealedInlineClassWithPrimaryConstructor::class } abstract class SealedInlineChildNotValue : KtFirDiagnostic<KtDeclaration>() {
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 6775868..0c4b3d5 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt
@@ -1187,10 +1187,10 @@ override val token: KtLifetimeToken, ) : KtFirDiagnostic.ValueObjectNotSealedInlineChild(), KtAbstractFirDiagnostic<KtDeclaration> -internal class SealedInlineClassWithUnderlyingValueImpl( +internal class SealedInlineClassWithPrimaryConstructorImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, -) : KtFirDiagnostic.SealedInlineClassWithUnderlyingValue(), KtAbstractFirDiagnostic<KtDeclaration> +) : KtFirDiagnostic.SealedInlineClassWithPrimaryConstructor(), KtAbstractFirDiagnostic<KtDeclaration> internal class SealedInlineChildNotValueImpl( override val firDiagnostic: KtPsiDiagnostic,
diff --git a/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassDeclaration.kt b/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassDeclaration.kt index 2eaf012..09d0fa2 100644 --- a/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassDeclaration.kt +++ b/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassDeclaration.kt
@@ -9,7 +9,7 @@ <!VALUE_CLASS_NOT_FINAL!>open<!> inline class NotFinalClass1(val x: Int) <!VALUE_CLASS_NOT_FINAL!>abstract<!> inline class NotFinalClass2(val x: Int) -<!VALUE_CLASS_NOT_FINAL!>sealed<!> inline class NotFinalClass3(val x: Int) +<!UNSUPPORTED_FEATURE!>sealed<!> inline class NotFinalClass3(val x: Int) <!VALUE_CLASS_CANNOT_BE_CLONEABLE, VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION!>value<!> class CloneableClass1(val x: Int): Cloneable <!VALUE_CLASS_CANNOT_BE_CLONEABLE!>inline<!> class CloneableClass2(val x: Int): <!PLATFORM_CLASS_MAPPED_TO_KOTLIN!>java.lang.Cloneable<!>
diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 9ffd2f3..c953bdf 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt
@@ -15,8 +15,6 @@ import org.jetbrains.kotlin.descriptors.EffectiveVisibility import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.diagnostics.WhenMissingCase -import org.jetbrains.kotlin.diagnostics.deprecationError2 -import org.jetbrains.kotlin.diagnostics.error0 import org.jetbrains.kotlin.fir.FirModuleData import org.jetbrains.kotlin.fir.PrivateForInline import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.* @@ -426,7 +424,7 @@ val INNER_CLASS_INSIDE_VALUE_CLASS by error<KtDeclaration>(PositioningStrategy.INNER_MODIFIER) val VALUE_CLASS_CANNOT_BE_CLONEABLE by error<KtDeclaration>(PositioningStrategy.INLINE_OR_VALUE_MODIFIER) val VALUE_OBJECT_NOT_SEALED_INLINE_CHILD by error<KtDeclaration>(PositioningStrategy.INLINE_OR_VALUE_MODIFIER) - val SEALED_INLINE_CLASS_WITH_UNDERLYING_VALUE by error<KtDeclaration>(PositioningStrategy.INLINE_OR_VALUE_MODIFIER) + val SEALED_INLINE_CLASS_WITH_PRIMARY_CONSTRUCTOR by error<KtDeclaration>(PositioningStrategy.INLINE_OR_VALUE_MODIFIER) val SEALED_INLINE_CHILD_NOT_VALUE by error<KtDeclaration>() val SEALED_INLINE_CHILD_OVERLAPPING_TYPE by error<KtDeclaration>(PositioningStrategy.INLINE_OR_VALUE_MODIFIER) }
diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 96fc6f2..923ffd7 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt
@@ -322,7 +322,7 @@ val INNER_CLASS_INSIDE_VALUE_CLASS by error0<KtDeclaration>(SourceElementPositioningStrategies.INNER_MODIFIER) val VALUE_CLASS_CANNOT_BE_CLONEABLE by error0<KtDeclaration>(SourceElementPositioningStrategies.INLINE_OR_VALUE_MODIFIER) val VALUE_OBJECT_NOT_SEALED_INLINE_CHILD by error0<KtDeclaration>(SourceElementPositioningStrategies.INLINE_OR_VALUE_MODIFIER) - val SEALED_INLINE_CLASS_WITH_UNDERLYING_VALUE by error0<KtDeclaration>(SourceElementPositioningStrategies.INLINE_OR_VALUE_MODIFIER) + val SEALED_INLINE_CLASS_WITH_PRIMARY_CONSTRUCTOR by error0<KtDeclaration>(SourceElementPositioningStrategies.INLINE_OR_VALUE_MODIFIER) val SEALED_INLINE_CHILD_NOT_VALUE by error0<KtDeclaration>() val SEALED_INLINE_CHILD_OVERLAPPING_TYPE by error0<KtDeclaration>(SourceElementPositioningStrategies.INLINE_OR_VALUE_MODIFIER)
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 1d0f907..fffc4e5 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
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.getModifier import org.jetbrains.kotlin.fir.analysis.checkers.hasModifier import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors @@ -52,11 +53,19 @@ reporter.reportOn(declaration.source, FirErrors.VALUE_CLASS_NOT_TOP_LEVEL, context) } - if (declaration.modality != Modality.FINAL && - !(declaration.modality == Modality.SEALED && - context.languageVersionSettings.supportsFeature(LanguageFeature.SealedInlineClasses)) - ) { - reporter.reportOn(declaration.source, FirErrors.VALUE_CLASS_NOT_FINAL, context) + if (declaration.modality != Modality.FINAL) { + if (declaration.modality == Modality.SEALED) { + if (!context.languageVersionSettings.supportsFeature(LanguageFeature.SealedInlineClasses)) { + reporter.reportOn( + declaration.getModifier(KtTokens.SEALED_KEYWORD)?.source, + FirErrors.UNSUPPORTED_FEATURE, + LanguageFeature.SealedInlineClasses to context.languageVersionSettings, + context + ) + } + } else { + reporter.reportOn(declaration.source, FirErrors.VALUE_CLASS_NOT_FINAL, context) + } } for (supertypeEntry in declaration.superTypeRefs) { @@ -182,7 +191,7 @@ context.languageVersionSettings.supportsFeature(LanguageFeature.SealedInlineClasses) && primaryConstructor?.source?.kind is KtRealSourceElementKind ) { - reporter.reportOn(declaration.source, FirErrors.SEALED_INLINE_CLASS_WITH_UNDERLYING_VALUE, context) + reporter.reportOn(declaration.source, FirErrors.SEALED_INLINE_CLASS_WITH_PRIMARY_CONSTRUCTOR, context) return }
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index e903a91..6b55e75 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt
@@ -461,7 +461,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_INHERITOR_IN_DIFFERENT_PACKAGE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_INLINE_CHILD_NOT_VALUE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_INLINE_CHILD_OVERLAPPING_TYPE -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_INLINE_CLASS_WITH_UNDERLYING_VALUE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_INLINE_CLASS_WITH_PRIMARY_CONSTRUCTOR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_SUPERTYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_SUPERTYPE_IN_LOCAL_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS @@ -1242,7 +1242,7 @@ map.put(VALUE_OBJECT_NOT_SEALED_INLINE_CHILD, "value object must be child of sealed inline class") map.put(SEALED_INLINE_CHILD_NOT_VALUE, "child of sealed inline class must be value"); map.put(SEALED_INLINE_CHILD_OVERLAPPING_TYPE, "Sealed inline class child cannot be distinguished from another one"); - map.put(SEALED_INLINE_CLASS_WITH_UNDERLYING_VALUE, "Sealed inline class cannot have underlying value"); + map.put(SEALED_INLINE_CLASS_WITH_PRIMARY_CONSTRUCTOR, "Sealed inline class cannot have primary constructor"); // Inline map.put(
diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index b954069..cc7c8e3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java
@@ -11,7 +11,6 @@ import kotlin.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.config.LanguageFeature; -import org.jetbrains.kotlin.config.LanguageVersion; import org.jetbrains.kotlin.config.LanguageVersionSettings; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.diagnostics.rendering.DeclarationWithDiagnosticComponents; @@ -27,7 +26,6 @@ import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData; import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData; -import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier; import org.jetbrains.kotlin.resolve.calls.util.BuilderLambdaLabelingInfo; @@ -43,7 +41,6 @@ import java.util.Map; import java.util.Set; -import static org.jetbrains.kotlin.config.LanguageFeature.ReportTypeVarianceConflictOnQualifierArguments; import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.ACTUAL_DECLARATION_NAME; import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.INCOMPATIBLE_DECLARATION; import static org.jetbrains.kotlin.diagnostics.PositioningStrategies.*; @@ -412,7 +409,7 @@ DiagnosticFactory0<PsiElement> VALUE_CLASS_NOT_TOP_LEVEL = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<PsiElement> VALUE_CLASS_NOT_FINAL = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<PsiElement> SEALED_INLINE_CLASS_WRONG_MODIFIER = DiagnosticFactory0.create(WARNING); - DiagnosticFactory0<PsiElement> SEALED_INLINE_CLASS_WITH_UNDERLYING_VALUE = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0<PsiElement> SEALED_INLINE_CLASS_WITH_PRIMARY_CONSTRUCTOR = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<PsiElement> ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_VALUE_CLASS = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<KtElement> INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<KtElement> VALUE_CLASS_EMPTY_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 3ed003f..08d49f6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java
@@ -767,8 +767,8 @@ MAP.put(NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED, "Constructor must be private or protected in sealed class"); MAP.put(VALUE_CLASS_NOT_TOP_LEVEL, "Value classes cannot be local or inner"); - MAP.put(VALUE_CLASS_NOT_FINAL, "Value classes can be only final or sealed"); - MAP.put(SEALED_INLINE_CLASS_WITH_UNDERLYING_VALUE, "Sealed inline class cannot have underlying value"); + MAP.put(VALUE_CLASS_NOT_FINAL, "Value classes must be final or sealed"); + MAP.put(SEALED_INLINE_CLASS_WITH_PRIMARY_CONSTRUCTOR, "Sealed inline class cannot have primary constructor"); MAP.put(SEALED_INLINE_CLASS_WRONG_MODIFIER, "Sealed inline class should use 'value' modifier instead of 'inline'"); MAP.put(ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_VALUE_CLASS, "Primary constructor is required for value class"); MAP.put(INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE, "Inline class must have exactly one primary constructor parameter");
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 8d6956f..c0531f8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt
@@ -80,7 +80,12 @@ } if (modalityModifier != null && isSealed && !context.languageVersionSettings.supportsFeature(LanguageFeature.SealedInlineClasses)) { - trace.report(Errors.VALUE_CLASS_NOT_FINAL.on(modalityModifier)) + trace.report( + Errors.UNSUPPORTED_FEATURE.on( + modalityModifier, + LanguageFeature.SealedInlineClasses to context.languageVersionSettings + ) + ) return } @@ -91,19 +96,19 @@ } if (isSealed && primaryConstructor != null) { - trace.report(Errors.SEALED_INLINE_CLASS_WITH_UNDERLYING_VALUE.on(primaryConstructor)) + trace.report(Errors.SEALED_INLINE_CLASS_WITH_PRIMARY_CONSTRUCTOR.on(primaryConstructor)) return } - if (!isNoinlineChildOfSealedInlineClass) { - if (context.languageVersionSettings.supportsFeature(LanguageFeature.ValueClasses) && descriptor.isValueClass()) { - if (primaryConstructor != null && primaryConstructor.valueParameters.isEmpty()) { + if (!isNoinlineChildOfSealedInlineClass && primaryConstructor != null) { + if (context.languageVersionSettings.supportsFeature(LanguageFeature.ValueClasses)) { + if (primaryConstructor.valueParameters.isEmpty()) { (primaryConstructor.valueParameterList ?: declaration).let { trace.report(Errors.VALUE_CLASS_EMPTY_CONSTRUCTOR.on(it)) return } } - } else if (!isSealed && primaryConstructor != null && primaryConstructor.valueParameters.size != 1) { + } else if (primaryConstructor.valueParameters.size != 1) { (primaryConstructor.valueParameterList ?: declaration).let { trace.report(Errors.INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE.on(it)) return @@ -114,7 +119,7 @@ var baseParametersOk = true val baseParameterTypes = descriptor.safeAs<ClassDescriptor>()?.defaultType?.substitutedUnderlyingTypes() ?: emptyList() - for ((baseParameter, baseParameterType) in (primaryConstructor?.valueParameters ?: emptyList()) zip baseParameterTypes) { + for ((baseParameter, baseParameterType) in primaryConstructor?.valueParameters.orEmpty() zip baseParameterTypes) { if (!isParameterAcceptableForInlineClass(baseParameter)) { trace.report(Errors.VALUE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER.on(baseParameter)) baseParametersOk = false
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt index 7e0ed41..344e516 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt
@@ -7,7 +7,6 @@ import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.compilationException -import org.jetbrains.kotlin.backend.common.DefaultInlineClassesUtils.getInlineClassUnderlyingType import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.declarations.IrClass
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt index fe17bb1..f614742 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
@@ -41,7 +41,6 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.popLast val jvmInlineClassPhase = makeIrFilePhase( @@ -118,11 +117,8 @@ val tmp = irTemporary(transformed.argument) +irIfNull( context.irBuiltIns.booleanType, irGet(tmp), irFalse(), - irCall(isCheck.symbol).also { - it.putValueArgument( - 0, - coerceInlineClasses(irGet(tmp), top.defaultType, context.irBuiltIns.anyNType) - ) + irCall(isCheck.symbol).apply { + putValueArgument(0, coerceFromSealedInlineClass(top, irGet(tmp))) } ) } @@ -135,21 +131,16 @@ val tmp = irTemporary(transformed.argument) +irIfNull( context.irBuiltIns.booleanType, irGet(tmp), irTrue(), - irNot( - irCall(isCheck.symbol).also { - it.putValueArgument( - 0, - coerceInlineClasses(irGet(tmp), top.defaultType, context.irBuiltIns.anyNType) - ) - } - ) + irNot(irCall(isCheck.symbol).apply { + putValueArgument(0, coerceFromSealedInlineClass(top, irGet(tmp))) + }) ) } } } IrTypeOperator.CAST -> { // if (Top.is-Child(top)) top else CCE - return generateAsCheck(currentScopeSymbol, transformed, top, isCheck, underlyingType) { + return generateAsCheckForSealedInlineClass(currentScopeSymbol, transformed, top, isCheck, underlyingType) { irCall(this@JvmInlineClassLowering.context.ir.symbols.throwTypeCastException).also { it.putValueArgument( 0, @@ -160,7 +151,7 @@ } IrTypeOperator.SAFE_CAST -> { // if (Top.is-Child(top)) top else null - return generateAsCheck(currentScopeSymbol, transformed, top, isCheck, underlyingType) { irNull() } + return generateAsCheckForSealedInlineClass(currentScopeSymbol, transformed, top, isCheck, underlyingType) { irNull() } } else -> { return transformed @@ -170,7 +161,7 @@ return super.visitTypeOperator(expression) } - private fun generateAsCheck( + private fun generateAsCheckForSealedInlineClass( currentScopeSymbol: IrSymbol, expression: IrTypeOperatorCall, top: IrClass, @@ -184,18 +175,14 @@ +irIfNull( expression.type, irGet(tmp), onFail(), irBlock { - val unboxedTmp = irTemporary(coerceInlineClasses(irGet(tmp), top.defaultType, context.irBuiltIns.anyNType)) + val unboxedTmp = irTemporary(coerceFromSealedInlineClass(top, irGet(tmp))) +irWhen( expression.type, listOf( irBranch( irCall(isCheck.symbol).also { it.putValueArgument(0, irGet(unboxedTmp)) }, - coerceInlineClasses( - irImplicitCast(irGet(unboxedTmp), underlyingType), - underlyingType, - expression.typeOperand - ) + coerceInlineClasses(irGet(unboxedTmp), underlyingType, expression.typeOperand) ), - irBranch(irTrue(), onFail()) + irElseBranch(onFail()) ) ) } @@ -204,6 +191,12 @@ } } + /* For `is` and `as` checks, we cannot use INSTANCEOF and CHECKCAST, since the underlying type of inline child can be `Any` + * instead, we check, whether it is not noinline first, and then use INSTANCEOF underlying type. Since we do not want to generate + * these switch-cases for each `is` and `as` check, we generate is-<Child> methods, which we call in `is` and `as` checks. + * + * These methods we generate only for inline subclasses, for noinline subclasses we simply use INSTANCEOF. + */ private fun buildIsMethodsForSealedInlineClass(info: SealedInlineClassInfo) { for (childInfo in info.inlineSubclasses + info.sealedInlineSubclasses.dropLast(1)) { val child = childInfo.owner @@ -225,18 +218,12 @@ for (noinline in info.noinlineSubclasses) { branches += irBranch( - irIs( - coerceInlineClasses(param(), context.irBuiltIns.anyNType, info.top.defaultType), - noinline.owner.defaultType - ), + irIs(coerceToSealedInlineClass(param(), info.top), noinline.owner.defaultType), irReturn(irFalse()) ) } - branches += irBranch( - irTrue(), - irReturn(irIs(param(), underlyingType)) - ) + branches += irElseBranch(irReturn(irIs(param(), underlyingType))) +irReturn(irWhen(context.irBuiltIns.booleanType, branches)) } @@ -457,7 +444,7 @@ } irExprBody( if (returnBoxedSealedInlineClass) { - coerceInlineClasses(call, context.irBuiltIns.anyNType, source.returnType) + coerceToSealedInlineClass(call, source.returnType.getClass()!!) } else { call } @@ -692,37 +679,22 @@ if (left.type.isInlineChildOfSealedInlineClass()) left.type.findTopSealedInlineSuperClass() else left.type.classOrNull!!.owner - val rightVar = if ( - right.type.isNoinlineChildOfSealedInlineClass() || right.type.isInlineChildOfSealedInlineClass() || - right.type.classOrNull?.owner?.modality == Modality.SEALED - ) { - val rightTop = - if (right.type.classOrNull?.owner?.modality == Modality.SEALED) right.type.classOrNull!!.owner - else right.type.findTopSealedInlineSuperClass() - - irTemporary( - coerceInlineClasses( - irImplicitCast(right, context.irBuiltIns.anyNType), - rightTop.defaultType, - context.irBuiltIns.anyNType, - ) - ) - } else { - irTemporary(right) - } + val rightVar = irTemporary( + if (right.type.classOrNull?.owner?.let { it.modality == Modality.SEALED || it.isChildOfSealedInlineClass() } == true) + coerceFromSealedInlineClass(right.type.findTopSealedInlineSuperClass(), right) + else right + ) val leftVar: IrVariable - val equalsMethod = if (rightIsUnboxed || right.type.isNoinlineChildOfSealedInlineClass()) { - leftVar = irTemporary( - coerceInlineClasses( - left, leftTop.defaultType, context.irBuiltIns.anyNType, - ) - ) - this@JvmInlineClassLowering.context.inlineClassReplacements.getSpecializedEqualsMethod(leftTop, context.irBuiltIns) + val equalsMethod: IrSimpleFunction + if (rightIsUnboxed || right.type.isNoinlineChildOfSealedInlineClass()) { + leftVar = irTemporary(coerceFromSealedInlineClass(leftTop, left)) + equalsMethod = + this@JvmInlineClassLowering.context.inlineClassReplacements.getSpecializedEqualsMethod(leftTop, context.irBuiltIns) } else { leftVar = irTemporary(left) val equals = leftTop.functions.single { it.name.asString() == "equals" && it.overriddenSymbols.isNotEmpty() } - this@JvmInlineClassLowering.context.inlineClassReplacements.getReplacementFunction(equals)!! + equalsMethod = this@JvmInlineClassLowering.context.inlineClassReplacements.getReplacementFunction(equals)!! } +irCall(equalsMethod).apply { @@ -777,15 +749,9 @@ val equals = this@JvmInlineClassLowering.context.inlineClassReplacements.getSpecializedEqualsMethod( top, context.irBuiltIns ) - val leftVar = irTemporary( - coerceInlineClasses(left, top.defaultType, context.irBuiltIns.anyNType) - ) - val rightVal = irTemporary( - coerceInlineClasses(right, top.defaultType, context.irBuiltIns.anyNType) - ) +irCall(equals).also { - it.putValueArgument(0, irGet(leftVar)) - it.putValueArgument(1, irGet(rightVal)) + it.putValueArgument(0, coerceFromSealedInlineClass(top, left)) + it.putValueArgument(1, coerceFromSealedInlineClass(top, right)) } } else { val equals = this@JvmInlineClassLowering.context.inlineClassReplacements.getReplacementFunction( @@ -976,52 +942,7 @@ private fun rewriteOpenAndAbstractMethodsForSealed(info: SealedInlineClassInfo) { for ((methodSymbol, retargets, addToClass) in info.methods) { if (methodSymbol.owner.isEquals(context)) { - val function = replacements.getReplacementFunction(methodSymbol.owner)!! - val equals = context.inlineClassReplacements.getSpecializedEqualsMethod(info.top, context.irBuiltIns) - - with(context.createIrBuilder(function.symbol)) { - function.body = irBlockBody { - +irReturn( - irWhen( - context.irBuiltIns.booleanType, listOf( - irBranch(irIs(irGet(function.valueParameters[1]), info.top.defaultType), - irBlock { - val left = irTemporary( - coerceInlineClasses( - irGet(function.valueParameters[0]), - info.top.defaultType, - context.irBuiltIns.anyNType, - ) - ) - val right = irTemporary( - coerceInlineClasses( - irGet(function.valueParameters[1]), - info.top.defaultType, - context.irBuiltIns.anyNType, - ) - ) - +irCall(equals).also { - it.putValueArgument(0, irGet(left)) - it.putValueArgument(1, irGet(right)) - } - } - ), - irBranch(irTrue(), irFalse()) - ) - ) - ) - } - } - - // Replace fake override with generated function - val fakeOverride = info.top.functions.single { - it.origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT && - it.name.asString() == "equals-impl" && it.valueParameters.size == 2 && - it.valueParameters[0].type.classOrNull == info.top.symbol && - it.valueParameters[1].type == context.irBuiltIns.anyNType - } - info.top.declarations.remove(fakeOverride) - info.top.addMember(function) + generateEqualsMethodForSealedInlineClass(info.top, replacements.getReplacementFunction(methodSymbol.owner)!!) continue } @@ -1034,79 +955,29 @@ ) ?: error("Cannot find replacement for ${methodSymbol.owner.render()}") val oldBody = function.body - fun findFakeOverrideOfInterfaceMethod(): IrSimpleFunction? = info.top.functions.find { - it.isFakeOverride && - replacements.getSealedInlineClassChildFunctionInTop(info.top to it.withoutReceiver()).symbol == methodSymbol - } - with(context.createIrBuilder(function.symbol)) { function.body = irBlockBody { - var counter = 0 fun copyOldBody() = when (oldBody) { - is IrExpressionBody -> irReturn(oldBody.expression.deepCopySavingMetadata()) + is IrExpressionBody -> irReturn(oldBody.expression.deepCopySavingMetadata(function)) is IrBlockBody -> irBlock { for (stmt in oldBody.statements) { - +stmt.deepCopySavingMetadata() + +stmt.deepCopySavingMetadata(function) } } + null -> { - var expression: IrExpression? = null - if (methodSymbol.owner.origin == JvmLoweredDeclarationOrigin.GENERATED_SEALED_INLINE_CLASS_METHOD) { - val fakeOverride = findFakeOverrideOfInterfaceMethod() - if (fakeOverride != null) { - val defaultMethod = fakeOverride.overriddenSymbols.find { - it.owner.parentAsClass.isInterface && it.owner.modality != Modality.ABSTRACT + callDefaultMethodOfSealedInlineClassSuperinterface(info.top, methodSymbol, function) + ?: irThrow( + irCall(this@JvmInlineClassLowering.context.ir.symbols.illegalStateExceptionCtorString).apply { + putValueArgument(0, irString("Invalid sealed inline class method call")) } - if (defaultMethod != null) { - val callDefaultImpls = this@JvmInlineClassLowering.context.state.jvmDefaultMode - .let { it.isEnabled && !it.forAllMethodsWithBody } - if (callDefaultImpls) { - val defaultImplsMethod = - this@JvmInlineClassLowering.context.cachedDeclarations - .getDefaultImplsFunction(defaultMethod.owner) - expression = irCall(defaultImplsMethod.symbol).also { - for ((target, source) in defaultImplsMethod.explicitParameters - .zip(function.explicitParameters) - ) { - it.putArgument(target, irGet(source)) - } - } - } else { - expression = IrCallImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, - defaultMethod.owner.returnType, - defaultMethod, - defaultMethod.owner.valueParameters.size, - defaultMethod.owner.typeParameters.size, - null, - defaultMethod.owner.parentAsClass.symbol - ).also { - it.dispatchReceiver = irImplicitCast( - irGet(function.valueParameters[0]), defaultMethod.owner.parentAsClass.defaultType - ) - for ((target, source) in defaultMethod.owner.explicitParameters - .zip(function.explicitParameters).drop(1) - ) { - it.putArgument(target, irGet(source)) - } - } - } - } - } - } - expression ?: irThrow( - irCall(this@JvmInlineClassLowering.context.ir.symbols.illegalStateExceptionCtorString).also { - it.putValueArgument(0, irString("${counter++}")) - } - ) + ) } + else -> error("oldBody is not a function body: ${oldBody.dump()}") } - val receiver = irTemporary( - coerceInlineClasses(irGet(function.valueParameters[0]), info.top.defaultType, context.irBuiltIns.anyNType), - "anyN_receiver" - ) + val receiver = irTemporary(coerceFromSealedInlineClass(info.top, irGet(function.valueParameters[0]))) val branches = mutableListOf<IrBranch>() @@ -1154,18 +1025,14 @@ irBranch( irIs(irGet(receiver), underlyingType), if (retargetClass.symbol == info.top.symbol) copyOldBody() - else irReturn(irCall(toCall).apply { + else irCall(toCall).apply { putValueArgument( - 0, coerceInlineClasses( - irImplicitCast(irGet(receiver), underlyingType), - underlyingType, - inlineSubclass.owner.defaultType - ) + 0, coerceInlineClasses(irGet(receiver), underlyingType, inlineSubclass.owner.defaultType) ) for ((target, source) in toCall.owner.explicitParameters.zip(function.explicitParameters).drop(1)) { putArgument(target, irGet(source)) } - }) + } ) } @@ -1175,95 +1042,173 @@ retarget = replacements.getReplacementFunction(retarget.owner)?.symbol } - if (retarget != null) { - branches += irBranch( - irTrue(), - if (retarget.owner.parentAsClass.symbol == info.top.symbol) copyOldBody() - else irReturn(irCall(retarget).apply { - putValueArgument( - 0, coerceInlineClasses( - irImplicitCast(irGet(receiver), context.irBuiltIns.anyNType), - context.irBuiltIns.anyNType, - sealedInlineSubclass.owner.defaultType.findTopSealedInlineSuperClass().defaultType - ) - ) + branches += irElseBranch( + if (retarget != null && retarget.owner.parentAsClass.symbol != info.top.symbol) { + irCall(retarget).apply { + putValueArgument(0, coerceToSealedInlineClass(irGet(receiver), info.top)) for ((target, source) in retarget.owner.explicitParameters.zip(function.explicitParameters).drop(1)) { putArgument(target, irGet(source)) } - }) - ) - } else { - branches += irBranch(irTrue(), copyOldBody()) - } + } + } else { + copyOldBody() + } + ) +irReturn(irWhen(function.returnType, branches)) } } if (addToClass) { - if (methodSymbol.owner.modality != Modality.ABSTRACT) { - info.top.addMember(function) - } +// info.top.addFunctionToSealedInlineClass(methodSymbol, function, retargets) + } + } + } - val allOverridden = retargets - .flatMap { (_, retarget) -> retarget.owner.allOverridden() } - .toSet() + fun IrClass.addFunctionToSealedInlineClass( + methodSymbol: IrSimpleFunctionSymbol, + function: IrSimpleFunction, + retargets: Map<IrClassSymbol, IrSimpleFunctionSymbol> + ) { + if (methodSymbol.owner.modality != Modality.ABSTRACT) { + addMember(function) + } - if (retargets.any { (_, retarget) -> - retarget.owner.parentAsClass.let { it.isInline && it.modality == Modality.SEALED } - } || allOverridden.any { it.parentAsClass.isInterface } - ) { - val bridge = context.irFactory.buildFun { - updateFrom(methodSymbol.owner) - origin = methodSymbol.owner.origin - modality = Modality.OPEN - name = methodSymbol.owner.name - returnType = methodSymbol.owner.returnType - }.apply { - copyParameterDeclarationsFrom(methodSymbol.owner) - annotations = methodSymbol.owner.annotations - parent = info.top + val allOverridden = retargets + .flatMap { (_, retarget) -> retarget.owner.allOverridden() } + .toSet() + + if (retargets.any { (_, retarget) -> + retarget.owner.parentAsClass.let { it.isInline && it.modality == Modality.SEALED } + } || allOverridden.any { it.parentAsClass.isInterface } + ) { + val bridge = context.irFactory.buildFun { + updateFrom(methodSymbol.owner) + origin = methodSymbol.owner.origin + modality = Modality.OPEN + name = methodSymbol.owner.name + returnType = methodSymbol.owner.returnType + }.apply { + copyParameterDeclarationsFrom(methodSymbol.owner) + annotations = methodSymbol.owner.annotations + parent = this + } + addMember(bridge) + + val returnBoxed = bridge.returnType.isInlineClassType() && !bridge.returnType.isNullable() && + allOverridden.any { + it.returnType.isNullable() || it.returnType.isAny() || + it.returnType.isNullableAny() || it.returnType.isTypeParameter() } - info.top.addMember(bridge) - val returnBoxed = bridge.returnType.isInlineClassType() && !bridge.returnType.isNullable() && - allOverridden.any { - it.returnType.isNullable() || it.returnType.isAny() || - it.returnType.isNullableAny() || it.returnType.isTypeParameter() + createBridgeBody(bridge, function, returnBoxed) + } + + for ((_, retarget) in retargets) { + val override = if (retarget.owner.origin != JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT) { + retarget.owner.overriddenSymbols += methodSymbol + replacements.getReplacementFunction(retarget.owner) ?: retarget.owner + } else retarget.owner + + override.overriddenSymbols += function.symbol + } + + // Remove fake overrides of default methods, we generated its replacement ourselves + if (methodSymbol.owner.origin == JvmLoweredDeclarationOrigin.GENERATED_SEALED_INLINE_CLASS_METHOD) { + val fakeOverride = findFakeOverrideOfInterfaceMethodInSealedInlineClass(methodSymbol) + if (fakeOverride != null && fakeOverride.allOverridden().any { + it.parentAsClass.isInterface && it.modality != Modality.ABSTRACT + }) { + declarations.remove(fakeOverride) + } + } + } + + fun IrClass.findFakeOverrideOfInterfaceMethodInSealedInlineClass(methodSymbol: IrSimpleFunctionSymbol): IrSimpleFunction? = + functions.find { + it.isFakeOverride && context.inlineClassReplacements + .getSealedInlineClassChildFunctionInTop(this to it.withoutReceiver()).symbol == methodSymbol + } + + fun IrBuilderWithScope.callDefaultMethodOfSealedInlineClassSuperinterface( + top: IrClass, + methodSymbol: IrSimpleFunctionSymbol, + function: IrSimpleFunction + ): IrExpression? { + if (methodSymbol.owner.origin == JvmLoweredDeclarationOrigin.GENERATED_SEALED_INLINE_CLASS_METHOD) { + val fakeOverride = top.findFakeOverrideOfInterfaceMethodInSealedInlineClass(methodSymbol) + if (fakeOverride != null) { + val defaultMethod = fakeOverride.overriddenSymbols.find { + it.owner.parentAsClass.isInterface && it.owner.modality != Modality.ABSTRACT + } + if (defaultMethod != null) { + val callDefaultImpls = this@JvmInlineClassLowering.context.state.jvmDefaultMode + .let { it.isEnabled && !it.forAllMethodsWithBody } + if (callDefaultImpls) { + val defaultImplsMethod = + this@JvmInlineClassLowering.context.cachedDeclarations + .getDefaultImplsFunction(defaultMethod.owner) + return irCall(defaultImplsMethod.symbol).apply { + for ((target, source) in defaultImplsMethod.explicitParameters.zip(function.explicitParameters)) { + putArgument(target, irGet(source)) } - - createBridgeBody(bridge, function, returnBoxed) - } - - for ((_, retarget) in retargets) { - val override = if (retarget.owner.origin != JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT) { - retarget.owner.overriddenSymbols += methodSymbol - replacements.getReplacementFunction(retarget.owner) ?: retarget.owner - } else retarget.owner - - override.overriddenSymbols += function.symbol - } - - // Remove fake overrides of default methods, we generated its replacement ourselves - if (methodSymbol.owner.origin == JvmLoweredDeclarationOrigin.GENERATED_SEALED_INLINE_CLASS_METHOD) { - val fakeOverride = findFakeOverrideOfInterfaceMethod() - if (fakeOverride != null && fakeOverride.allOverridden().any { - it.parentAsClass.isInterface && it.modality != Modality.ABSTRACT - }) { - info.top.declarations.remove(fakeOverride) + } + } else { + return irCall(defaultMethod).apply { + dispatchReceiver = irGet(function.valueParameters[0]) + for ((target, source) in defaultMethod.owner.explicitParameters.zip(function.explicitParameters).drop(1)) { + putArgument(target, irGet(source)) + } + } } } } } + return null } + /* The equals function for sealed inline classes looks like + * (given the class is named Top) + * + * fun equals(other: Any?) = + * other is Top && equal-impl0(this.$value, other.$value) + */ + private fun generateEqualsMethodForSealedInlineClass(top: IrClass, function: IrSimpleFunction) { + val equals = context.inlineClassReplacements.getSpecializedEqualsMethod(top, context.irBuiltIns) + + with(context.createIrBuilder(function.symbol)) { + function.body = irExprBody(irAndand( + irIs(irGet(function.valueParameters[1]), top.defaultType), + irCall(equals).apply { + putValueArgument(0, coerceFromSealedInlineClass(top, irGet(function.valueParameters[0]))) + putValueArgument(1, coerceFromSealedInlineClass(top, irGet(function.valueParameters[1]))) + } + )) + } + + // Replace fake override with generated function + val fakeOverride = top.functions.single { + it.origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT && + it.name.asString() == "equals-impl" && it.valueParameters.size == 2 && + it.valueParameters[0].type.classOrNull == top.symbol && + it.valueParameters[1].type == context.irBuiltIns.anyNType + } + top.declarations.remove(fakeOverride) + top.addMember(function) + } + + private fun coerceFromSealedInlineClass(top: IrClass, expr: IrExpression): IrExpression = + coerceInlineClasses(expr, top.defaultType, context.irBuiltIns.anyNType) + + private fun coerceToSealedInlineClass(expr: IrExpression, top: IrClass): IrExpression = + coerceInlineClasses(expr, context.irBuiltIns.anyNType, top.defaultType) + private fun buildUnboxFunction(irClass: IrClass) { val function = context.inlineClassReplacements.getUnboxFunction(irClass) val field = getInlineClassBackingField(irClass) - function.body = context.createIrBuilder(function.symbol).irBlockBody { - val thisVal = irGet(function.dispatchReceiverParameter!!) - +irReturn(irGetField(thisVal, field)) + function.body = with(context.createIrBuilder(function.symbol)) { + irExprBody(irGetField(irGet(function.dispatchReceiverParameter!!), field)) } irClass.declarations += function @@ -1275,7 +1220,7 @@ val right = function.valueParameters[1] val type = left.type.unboxInlineClass() - function.body = context.createIrBuilder(valueClass.symbol).run { + function.body = with(context.createIrBuilder(valueClass.symbol)) { irExprBody( irEquals( coerceInlineClasses(irGet(left), left.type, type), @@ -1287,83 +1232,76 @@ valueClass.declarations += function } + /* For sealed inline classes, inside `equals-impl0` we check for types of parameters and then call respective `equals` of `equals-impl0` + * functions. + * + * For example, given the code + * + * @JvmInline + * sealed value class Top { + * @JvmInline + * value class I(val i: Int) + * @JvmInline + * value class L(val l: Long) + * + * value class S(...) + * } + * + * First, we generate checks for noinline subclasses, and then for inline ones. + * For noinline classes we just generate reference equals, since we do not support user-defined equals for value classes yet. + * + * So, we generate the following function + * + * static fun Top.equals-impl0($this: Any?, other: Any?): Boolean = when { + * $this is S -> $this === other + * $this is Int && other is Int -> I.equals-impl0($this as Int?, other as Int?) + * $this is Long && other is Long -> L.equals-impl0($this as Long?, other as Long?) + * else -> false + * } + */ private fun buildSpecializedEqualsMethodForSealed(info: SealedInlineClassInfo) { val irClass = info.top val inlineSubclasses = info.inlineSubclasses val noinlineSubclasses = info.noinlineSubclasses - val boolAnd = context.ir.symbols.getBinaryOperator( - OperatorNameConventions.AND, context.irBuiltIns.booleanType, context.irBuiltIns.booleanType - ) - val function = context.inlineClassReplacements.getSpecializedEqualsMethod(irClass, context.irBuiltIns) with(context.createIrBuilder(function.symbol)) { function.body = irBlockBody { val left = irTemporary( - coerceInlineClasses( - irGet(function.valueParameters[0]), - this@JvmInlineClassLowering.context.irBuiltIns.anyNType, - irClass.defaultType - ) + coerceToSealedInlineClass(irGet(function.valueParameters[0]), irClass) ) val right = irTemporary( - coerceInlineClasses( - irGet(function.valueParameters[1]), - this@JvmInlineClassLowering.context.irBuiltIns.anyNType, - irClass.defaultType - ) + coerceToSealedInlineClass(irGet(function.valueParameters[1]), irClass) ) val branches = mutableListOf<IrBranch>() for (noinlineSubclass in noinlineSubclasses) { branches += irBranch( - irCallOp( - boolAnd, context.irBuiltIns.booleanType, - irIs(irGet(left), noinlineSubclass.owner.defaultType), - irIs(irGet(right), noinlineSubclass.owner.defaultType), - ), - // TODO: Revisit, when we will support user-defined equals. - irReturn(irTrue()) + irIs(irGet(left), noinlineSubclass.owner.defaultType), + irEqeqeq(irGet(left), irGet(right)) ) } for (inlineSubclass in inlineSubclasses) { - val unboxedLeft = coerceInlineClasses( - irGet(left), - irClass.defaultType, - this@JvmInlineClassLowering.context.irBuiltIns.anyNType, - ) - val unboxedRight = coerceInlineClasses( - irGet(right), - irClass.defaultType, - this@JvmInlineClassLowering.context.irBuiltIns.anyNType, - ) val eq = this@JvmInlineClassLowering.context.inlineClassReplacements .getSpecializedEqualsMethod(inlineSubclass.owner, context.irBuiltIns) - val underlyingType = getInlineClassUnderlyingType(inlineSubclass.owner) + val underlyingType = context.irBuiltIns.getInlineClassUnderlyingType(inlineSubclass.owner) branches += irBranch( - irCallOp( - boolAnd, context.irBuiltIns.booleanType, - irIs(unboxedLeft, underlyingType), - irIs(unboxedRight, underlyingType), + irAndand( + irIs(irGet(function.valueParameters[0]), underlyingType), + irIs(irGet(function.valueParameters[1]), underlyingType) ), - irReturn( - irCall(eq).apply { - putValueArgument( - 0, irImplicitCast(irGet(left), underlyingType) - ) - putValueArgument( - 1, irImplicitCast(irGet(right), underlyingType) - ) - } - ) + irCall(eq).apply { + putValueArgument(0, irGet(left)) + putValueArgument(1, irGet(right)) + } ) } - branches += irBranch(irTrue(), irReturn(irFalse())) + branches += irElseBranch(irFalse()) +irReturn(irWhen(context.irBuiltIns.booleanType, branches)) }
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt index 37e1f1f..c162f3c 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt
@@ -578,7 +578,7 @@ if (erasedAdapteeClass.isSingleFieldValueClass) { // Inline classes mapped to non-null reference types are a special case because they can't be boxed trivially. // TODO consider adding a special type annotation to force boxing on an inline class type regardless of its underlying type. - val underlyingAdapteeType = getInlineClassUnderlyingType(erasedAdapteeClass) + val underlyingAdapteeType = context.irBuiltIns.getInlineClassUnderlyingType(erasedAdapteeClass) if (!underlyingAdapteeType.isNullable() && !underlyingAdapteeType.isPrimitiveType()) { return TypeAdaptationConstraint.CONFLICT }
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt index 0f4ddc0..be457cb 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt
@@ -5,7 +5,6 @@ package org.jetbrains.kotlin.backend.jvm -import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.backend.jvm.ir.* import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.descriptors.DescriptorVisibilities @@ -153,9 +152,7 @@ copyTypeParametersFrom(irClass) addValueParameter { name = InlineClassDescriptorResolver.BOXING_VALUE_PARAMETER_NAME - type = - if (irClass.modality == Modality.SEALED) context.irBuiltIns.anyNType - else getInlineClassUnderlyingType(irClass) + type = context.irBuiltIns.getInlineClassUnderlyingType(irClass) } } } @@ -170,9 +167,7 @@ irFactory.buildFun { name = Name.identifier(KotlinTypeMapper.UNBOX_JVM_METHOD_NAME) origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER - returnType = - if (irClass.modality == Modality.SEALED) context.irBuiltIns.anyNType - else getInlineClassUnderlyingType(irClass) + returnType = context.irBuiltIns.getInlineClassUnderlyingType(irClass) if (irClass.modality == Modality.SEALED) { modality = Modality.OPEN } @@ -257,18 +252,18 @@ }.apply { parent = irClass // We ignore type arguments here, since there is no good way to go from type arguments to types in the IR anyway. - val typeArgument = + val argumentType = if (irClass.modality == Modality.SEALED) context.irBuiltIns.anyNType else IrSimpleTypeImpl( irClass.symbol, false, List(irClass.typeParameters.size) { IrStarProjectionImpl }, listOf() ) addValueParameter { name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_FIRST_PARAMETER_NAME - type = typeArgument + type = argumentType } addValueParameter { name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_SECOND_PARAMETER_NAME - type = typeArgument + type = argumentType } } }
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt index d9e0920..3fab431 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt
@@ -161,6 +161,9 @@ fun IrBuilderWithScope.irEqeqeq(arg1: IrExpression, arg2: IrExpression) = context.eqeqeq(startOffset, endOffset, arg1, arg2) +fun IrBuilderWithScope.irAndand(arg1: IrExpression, arg2: IrExpression) = + context.andand(startOffset, endOffset, arg1, arg2) + fun IrBuilderWithScope.irNull() = irNull(context.irBuiltIns.nothingNType)
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/InlineClasses.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/InlineClasses.kt index 91479a3..f118084 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/InlineClasses.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/InlineClasses.kt
@@ -5,15 +5,17 @@ package org.jetbrains.kotlin.ir.util +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrField import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.inlineClassRepresentation -import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.types.IrType -fun getInlineClassUnderlyingType(irClass: IrClass): IrSimpleType { +fun IrBuiltIns.getInlineClassUnderlyingType(irClass: IrClass): IrType { val representation = irClass.inlineClassRepresentation ?: error("Not an inline class: ${irClass.render()}") - return representation.underlyingType + return if (irClass.modality == Modality.SEALED) anyNType else representation.underlyingType } fun getInlineClassBackingField(irClass: IrClass): IrField {
diff --git a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.fir.kt b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.fir.kt index 5ad1a24..95ccb21 100644 --- a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.fir.kt +++ b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.fir.kt
@@ -35,6 +35,6 @@ final inline class D0(val x: Int) <!VALUE_CLASS_NOT_FINAL!>open<!> inline class D1(val x: Int) <!VALUE_CLASS_NOT_FINAL!>abstract<!> inline class D2(val x: Int) -<!VALUE_CLASS_NOT_FINAL!>sealed<!> inline class D3(val x: Int) +<!UNSUPPORTED_FEATURE!>sealed<!> inline class D3(val x: Int) <!INCOMPATIBLE_MODIFIERS!>inline<!> <!INCOMPATIBLE_MODIFIERS!>data<!> class D4(val x: String)
diff --git a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt index 224c5c1..69d83da 100644 --- a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt +++ b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt
@@ -35,6 +35,6 @@ final inline class D0(val x: Int) <!VALUE_CLASS_NOT_FINAL!>open<!> inline class D1(val x: Int) <!VALUE_CLASS_NOT_FINAL!>abstract<!> inline class D2(val x: Int) -<!VALUE_CLASS_NOT_FINAL!>sealed<!> <!SEALED_INLINE_CLASS_WRONG_MODIFIER!>inline<!> class D3(val x: Int) +<!UNSUPPORTED_FEATURE!>sealed<!> <!SEALED_INLINE_CLASS_WRONG_MODIFIER!>inline<!> class D3(val x: Int) <!INCOMPATIBLE_MODIFIERS!>inline<!> <!INCOMPATIBLE_MODIFIERS!>data<!> class <!CONFLICTING_JVM_DECLARATIONS, CONFLICTING_JVM_DECLARATIONS, CONFLICTING_JVM_DECLARATIONS!>D4(val x: String)<!>
diff --git a/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/with-value.fir.kt b/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/with-value.fir.kt index 025b593..4b54c82 100644 --- a/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/with-value.fir.kt +++ b/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/with-value.fir.kt
@@ -6,4 +6,4 @@ annotation class JvmInline @JvmInline -sealed <!SEALED_INLINE_CLASS_WITH_UNDERLYING_VALUE!>value<!> class Result(val value: Any) +sealed <!SEALED_INLINE_CLASS_WITH_PRIMARY_CONSTRUCTOR!>value<!> class Result(val value: Any)
diff --git a/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/with-value.kt b/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/with-value.kt index 1e5220c..07112c8 100644 --- a/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/with-value.kt +++ b/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/with-value.kt
@@ -6,4 +6,4 @@ annotation class JvmInline @JvmInline -sealed value class Result<!SEALED_INLINE_CLASS_WITH_UNDERLYING_VALUE!>(val value: Any)<!> +sealed value class Result<!SEALED_INLINE_CLASS_WITH_PRIMARY_CONSTRUCTOR!>(val value: Any)<!>
diff --git a/compiler/testData/diagnostics/tests/valueClasses/sealedValueClass.kt b/compiler/testData/diagnostics/tests/valueClasses/sealedValueClass.kt index efb6668..b1bc92f 100644 --- a/compiler/testData/diagnostics/tests/valueClasses/sealedValueClass.kt +++ b/compiler/testData/diagnostics/tests/valueClasses/sealedValueClass.kt
@@ -2,4 +2,4 @@ // !SKIP_JAVAC // !LANGUAGE: -SealedInlineClasses -<!VALUE_CLASS_NOT_FINAL!>sealed<!> <!VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION!>value<!> class SVC +<!UNSUPPORTED_FEATURE!>sealed<!> <!VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION!>value<!> class SVC
diff --git a/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.kt b/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.kt index ecfec1b..ed47380 100644 --- a/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.kt +++ b/compiler/testData/diagnostics/tests/valueClasses/valueClassDeclarationCheck.kt
@@ -48,6 +48,6 @@ final value class D0(val x: Int) <!VALUE_CLASS_NOT_FINAL!>open<!> <!VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION!>value<!> class D1(val x: Int) <!VALUE_CLASS_NOT_FINAL!>abstract<!> <!VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION!>value<!> class D2(val x: Int) -<!VALUE_CLASS_NOT_FINAL!>sealed<!> <!VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION!>value<!> class D3(val x: Int) +<!UNSUPPORTED_FEATURE!>sealed<!> <!VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION!>value<!> class D3(val x: Int) <!INCOMPATIBLE_MODIFIERS, VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION!>value<!> <!INCOMPATIBLE_MODIFIERS!>data<!> class D4(val x: String)