Skip to content

Commit

Permalink
FE: Loosen restrictions on noinline children of sealed inline classes
Browse files Browse the repository at this point in the history
Allow noinline class children without constructor or with constructor
with zero or more than one parameter.

 #KT-27576
  • Loading branch information
ilmirus committed Jul 22, 2022
1 parent 66f6ef7 commit 57eda1c
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 39 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand All @@ -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
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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()

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 57eda1c

Please sign in to comment.