From 57eda1c4c2676cd1e042b92fbc864dff28dfee11 Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Fri, 22 Jul 2022 11:53:22 +0200 Subject: [PATCH] FE: Loosen restrictions on noinline children of sealed inline classes Allow noinline class children without constructor or with constructor with zero or more than one parameter. #KT-27576 --- ...CompilerTestFE10TestdataTestGenerated.java | 6 ++ ...irOldFrontendDiagnosticsTestGenerated.java | 6 ++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 ++ .../FirInlineClassDeclarationChecker.kt | 67 ++++++++++--------- .../checkers/InlineClassDeclarationChecker.kt | 24 ++++--- .../sealed-inline-class/noinlineChild.kt | 18 +++++ .../test/runners/DiagnosticTestGenerated.java | 6 ++ 7 files changed, 94 insertions(+), 39 deletions(-) diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 8e0f19e8749a6..43ad0f397cd78 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -17869,6 +17869,12 @@ public void testNested() throws Exception { runTest("compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/nested.kt"); } + @Test + @TestMetadata("noinlineChild.kt") + public void testNoinlineChild() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/noinlineChild.kt"); + } + @Test @TestMetadata("object-child.kt") public void testObject_child() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index d59b204dfb680..fbc2528fbb1b4 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -17869,6 +17869,12 @@ public void testNested() throws Exception { runTest("compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/nested.kt"); } + @Test + @TestMetadata("noinlineChild.kt") + public void testNoinlineChild() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/noinlineChild.kt"); + } + @Test @TestMetadata("object-child.kt") public void testObject_child() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 535803fe8cd25..676ea77fb9287 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -17869,6 +17869,12 @@ public void testNested() throws Exception { runTest("compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/nested.kt"); } + @Test + @TestMetadata("noinlineChild.kt") + public void testNoinlineChild() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/noinlineChild.kt"); + } + @Test @TestMetadata("object-child.kt") public void testObject_child() throws Exception { 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 56179d41af407..923e28fd69d94 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 @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.resolve.fqName import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol @@ -39,6 +40,7 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() { private val reservedFunctionNames = setOf("box", "unbox", "equals", "hashCode") private val javaLangFqName = FqName("java.lang") private val cloneableFqName = FqName("Cloneable") + private val jvmInlineAnnotationFqName = FqName("kotlin.jvm.JvmInline") @Suppress("NAME_SHADOWING") override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) { @@ -157,7 +159,10 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() { } } - if (declaration.modality != Modality.SEALED) { + val isNoinlineChildOfSealedInlineClass = declaration.isChildOfSealedInlineClass(context.session) && + declaration.annotations.none { it.fqName(context.session) == jvmInlineAnnotationFqName } + + if (declaration.modality != Modality.SEALED && !isNoinlineChildOfSealedInlineClass) { if (primaryConstructor?.source?.kind !is KtRealSourceElementKind) { reporter.reportOn(declaration.source, FirErrors.ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_VALUE_CLASS, context) return @@ -171,41 +176,43 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() { reporter.reportOnWithSuppression(primaryConstructor, FirErrors.INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE, context) return } + } - for ((name, primaryConstructorParameter) in primaryConstructorParametersByName) { - withSuppressedDiagnostics(primaryConstructor, context) { context -> - withSuppressedDiagnostics(primaryConstructorParameter, context) { context -> - when { - primaryConstructorParameter.isNotFinalReadOnly(primaryConstructorPropertiesByName[name]) -> - reporter.reportOn( - primaryConstructorParameter.source, - FirErrors.VALUE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER, - context - ) + if (primaryConstructor == null) return - primaryConstructorParameter.returnTypeRef.isInapplicableParameterType() -> { - val inlineClassHasGenericUnderlyingType = primaryConstructorParameter.returnTypeRef.coneType.let { - (it is ConeTypeParameterType || it.isGenericArrayOfTypeParameter()) - } - if (!(context.languageVersionSettings.supportsFeature(LanguageFeature.GenericInlineClassParameter) && - inlineClassHasGenericUnderlyingType) - ) { - reporter.reportOn( - primaryConstructorParameter.returnTypeRef.source, - FirErrors.VALUE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE, - primaryConstructorParameter.returnTypeRef.coneType, - context - ) - } - } + for ((name, primaryConstructorParameter) in primaryConstructorParametersByName) { + withSuppressedDiagnostics(primaryConstructor, context) { context -> + withSuppressedDiagnostics(primaryConstructorParameter, context) { context -> + when { + primaryConstructorParameter.isNotFinalReadOnly(primaryConstructorPropertiesByName[name]) -> + reporter.reportOn( + primaryConstructorParameter.source, + FirErrors.VALUE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER, + context + ) - primaryConstructorParameter.returnTypeRef.coneType.isRecursiveInlineClassType(context.session) -> - reporter.reportOnWithSuppression( - primaryConstructorParameter.returnTypeRef, - FirErrors.VALUE_CLASS_CANNOT_BE_RECURSIVE, + primaryConstructorParameter.returnTypeRef.isInapplicableParameterType() -> { + val inlineClassHasGenericUnderlyingType = primaryConstructorParameter.returnTypeRef.coneType.let { + (it is ConeTypeParameterType || it.isGenericArrayOfTypeParameter()) + } + if (!(context.languageVersionSettings.supportsFeature(LanguageFeature.GenericInlineClassParameter) && + inlineClassHasGenericUnderlyingType) + ) { + reporter.reportOn( + primaryConstructorParameter.returnTypeRef.source, + FirErrors.VALUE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE, + primaryConstructorParameter.returnTypeRef.coneType, context ) + } } + + primaryConstructorParameter.returnTypeRef.coneType.isRecursiveInlineClassType(context.session) -> + reporter.reportOnWithSuppression( + primaryConstructorParameter.returnTypeRef, + FirErrors.VALUE_CLASS_CANNOT_BE_RECURSIVE, + context + ) } } } 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 2a73102d01d15..8d6956fa86311 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.resolve.* 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.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.* import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -52,6 +53,9 @@ object InlineClassDeclarationChecker : DeclarationChecker { val inlineOrValueKeyword = declaration.modifierList?.getModifier(KtTokens.INLINE_KEYWORD) ?: valueKeyword require(inlineOrValueKeyword != null) { "Declaration of inline class must have 'inline' keyword" } + val isNoinlineChildOfSealedInlineClass = descriptor.getSuperClassOrAny().isSealedInlineClass() && + !descriptor.annotations.hasAnnotation(JVM_INLINE_ANNOTATION_FQ_NAME) + if (descriptor.isInner || DescriptorUtils.isLocal(descriptor)) { trace.report(Errors.VALUE_CLASS_NOT_TOP_LEVEL.on(inlineOrValueKeyword)) return @@ -81,7 +85,7 @@ object InlineClassDeclarationChecker : DeclarationChecker { } val primaryConstructor = declaration.primaryConstructor - if (primaryConstructor == null && !isSealed) { + if (primaryConstructor == null && !isSealed && !isNoinlineChildOfSealedInlineClass) { trace.report(Errors.ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_VALUE_CLASS.on(inlineOrValueKeyword)) return } @@ -91,18 +95,20 @@ object InlineClassDeclarationChecker : DeclarationChecker { return } - if (context.languageVersionSettings.supportsFeature(LanguageFeature.ValueClasses) && descriptor.isValueClass()) { - if (primaryConstructor != null && primaryConstructor.valueParameters.isEmpty()) { + if (!isNoinlineChildOfSealedInlineClass) { + if (context.languageVersionSettings.supportsFeature(LanguageFeature.ValueClasses) && descriptor.isValueClass()) { + if (primaryConstructor != null && 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) { (primaryConstructor.valueParameterList ?: declaration).let { - trace.report(Errors.VALUE_CLASS_EMPTY_CONSTRUCTOR.on(it)) + trace.report(Errors.INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE.on(it)) return } } - } else if (!isSealed && primaryConstructor != null && primaryConstructor.valueParameters.size != 1) { - (primaryConstructor.valueParameterList ?: declaration).let { - trace.report(Errors.INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE.on(it)) - return - } } var baseParametersOk = true diff --git a/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/noinlineChild.kt b/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/noinlineChild.kt index e69de29bb2d1d..ba4045d6e87f9 100644 --- a/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/noinlineChild.kt +++ b/compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/noinlineChild.kt @@ -0,0 +1,18 @@ +// FIR_IDENTICAL +// LANGUAGE: +SealedInlineClasses +// SKIP_TXT +// !SKIP_JAVAC + +package kotlin.jvm + +annotation class JvmInline + +@JvmInline +sealed value class Parent + +value class WithoutConstructor: Parent() + +value class WithEmptyConstructor(): Parent() + +value class WithMultipleConstructorParameters(val a: Int, val b: Int): Parent() + diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index ee415695110e7..06a87eb57a525 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -17875,6 +17875,12 @@ public void testNested() throws Exception { runTest("compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/nested.kt"); } + @Test + @TestMetadata("noinlineChild.kt") + public void testNoinlineChild() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/sealed-inline-class/noinlineChild.kt"); + } + @Test @TestMetadata("object-child.kt") public void testObject_child() throws Exception {