Prohibit recursive inline classes
This commit is contained in:
@@ -329,6 +329,7 @@ public interface Errors {
|
||||
DiagnosticFactory1<KtTypeReference, KotlinType> INLINE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> INLINE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtTypeReference> INLINE_CLASS_CANNOT_EXTEND_CLASSES = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtTypeReference> INLINE_CLASS_CANNOT_BE_RECURSIVE = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
// Secondary constructors
|
||||
|
||||
|
||||
+1
@@ -658,6 +658,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(INLINE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE, "Inline class cannot have value parameter of type ''{0}''", RENDER_TYPE);
|
||||
MAP.put(INLINE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION, "Inline class cannot implement an interface by delegation");
|
||||
MAP.put(INLINE_CLASS_CANNOT_EXTEND_CLASSES, "Inline class cannot extend classes");
|
||||
MAP.put(INLINE_CLASS_CANNOT_BE_RECURSIVE, "Inline class cannot be recursive");
|
||||
|
||||
MAP.put(VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED, "Variance annotations are only allowed for type parameters of classes and interfaces");
|
||||
MAP.put(BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED, "Bounds are not allowed on type alias parameters");
|
||||
|
||||
+10
-8
@@ -12,10 +12,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
|
||||
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||
import org.jetbrains.kotlin.resolve.substitutedUnderlyingType
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
@@ -80,10 +77,15 @@ object InlineClassDeclarationChecker : DeclarationChecker {
|
||||
}
|
||||
|
||||
val baseParameterType = descriptor.safeAs<ClassDescriptor>()?.defaultType?.substitutedUnderlyingType()
|
||||
if (baseParameterType != null && baseParameterType.isInapplicableParameterType()) {
|
||||
val typeReference = baseParameter.typeReference
|
||||
if (typeReference != null) {
|
||||
trace.report(Errors.INLINE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE.on(typeReference, baseParameterType))
|
||||
val baseParameterTypeReference = baseParameter.typeReference
|
||||
if (baseParameterType != null && baseParameterTypeReference != null) {
|
||||
if (baseParameterType.isInapplicableParameterType()) {
|
||||
trace.report(Errors.INLINE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE.on(baseParameterTypeReference, baseParameterType))
|
||||
return
|
||||
}
|
||||
|
||||
if (baseParameterType.isRecursiveInlineClassType()) {
|
||||
trace.report(Errors.INLINE_CLASS_CANNOT_BE_RECURSIVE.on(baseParameterTypeReference))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
|
||||
inline class Test1(val x: <!INLINE_CLASS_CANNOT_BE_RECURSIVE!>Test1<!>)
|
||||
|
||||
inline class Test2A(val x: <!INLINE_CLASS_CANNOT_BE_RECURSIVE!>Test2B<!>)
|
||||
inline class Test2B(val x: <!INLINE_CLASS_CANNOT_BE_RECURSIVE!>Test2A<!>)
|
||||
|
||||
inline class Test3A(val x: <!INLINE_CLASS_CANNOT_BE_RECURSIVE!>Test3B<!>)
|
||||
inline class Test3B(val x: <!INLINE_CLASS_CANNOT_BE_RECURSIVE!>Test3C<!>)
|
||||
inline class Test3C(val x: <!INLINE_CLASS_CANNOT_BE_RECURSIVE!>Test3A<!>)
|
||||
|
||||
inline class TestNullable(val x: <!INLINE_CLASS_CANNOT_BE_RECURSIVE!>TestNullable?<!>)
|
||||
|
||||
inline class TestRecursionInTypeArguments(val x: List<TestRecursionInTypeArguments>)
|
||||
|
||||
inline class TestRecursionInArray(val x: Array<TestRecursionInArray>)
|
||||
@@ -0,0 +1,73 @@
|
||||
package
|
||||
|
||||
public final inline class Test1 {
|
||||
public constructor Test1(/*0*/ x: Test1)
|
||||
public final val x: Test1
|
||||
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 final inline class Test2A {
|
||||
public constructor Test2A(/*0*/ x: Test2B)
|
||||
public final val x: Test2B
|
||||
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 final inline class Test2B {
|
||||
public constructor Test2B(/*0*/ x: Test2A)
|
||||
public final val x: Test2A
|
||||
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 final inline class Test3A {
|
||||
public constructor Test3A(/*0*/ x: Test3B)
|
||||
public final val x: Test3B
|
||||
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 final inline class Test3B {
|
||||
public constructor Test3B(/*0*/ x: Test3C)
|
||||
public final val x: Test3C
|
||||
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 final inline class Test3C {
|
||||
public constructor Test3C(/*0*/ x: Test3A)
|
||||
public final val x: Test3A
|
||||
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 final inline class TestNullable {
|
||||
public constructor TestNullable(/*0*/ x: TestNullable?)
|
||||
public final val x: TestNullable?
|
||||
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 final inline class TestRecursionInArray {
|
||||
public constructor TestRecursionInArray(/*0*/ x: kotlin.Array<TestRecursionInArray>)
|
||||
public final val x: kotlin.Array<TestRecursionInArray>
|
||||
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 final inline class TestRecursionInTypeArguments {
|
||||
public constructor TestRecursionInTypeArguments(/*0*/ x: kotlin.collections.List<TestRecursionInTypeArguments>)
|
||||
public final val x: kotlin.collections.List<TestRecursionInTypeArguments>
|
||||
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
|
||||
}
|
||||
@@ -10917,6 +10917,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/propertiesWithBackingFieldsInsideInlineClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveInlineClasses.kt")
|
||||
public void testRecursiveInlineClasses() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/recursiveInlineClasses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
|
||||
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/unsignedLiteralsWithoutArtifactOnClasspath.kt");
|
||||
|
||||
Generated
+5
@@ -10917,6 +10917,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/propertiesWithBackingFieldsInsideInlineClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveInlineClasses.kt")
|
||||
public void testRecursiveInlineClasses() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/recursiveInlineClasses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
|
||||
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/unsignedLiteralsWithoutArtifactOnClasspath.kt");
|
||||
|
||||
@@ -12,13 +12,10 @@ import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.isInlineClassType
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
import org.jetbrains.kotlin.resolve.substitutedUnderlyingType
|
||||
import org.jetbrains.kotlin.resolve.unsubstitutedUnderlyingType
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
||||
import org.jetbrains.kotlin.utils.DO_NOTHING_3
|
||||
@@ -129,7 +126,8 @@ fun <T : Any> mapType(
|
||||
}
|
||||
|
||||
descriptor is ClassDescriptor -> {
|
||||
if (descriptor.isInline && !mode.needInlineClassWrapping) {
|
||||
// NB if inline class is recursive, it's ok to map it as wrapped
|
||||
if (descriptor.isInline && !mode.needInlineClassWrapping && !kotlinType.isRecursiveInlineClassType()) {
|
||||
val typeForMapping = computeUnderlyingType(kotlinType)
|
||||
if (typeForMapping != null) {
|
||||
val newMode = if (typeForMapping.isInlineClassType()) mode else mode.wrapInlineClassesMode()
|
||||
|
||||
@@ -34,6 +34,17 @@ fun KotlinType.substitutedUnderlyingType(): KotlinType? {
|
||||
return memberScope.getContributedVariables(parameter.name, NoLookupLocation.FOR_ALREADY_TRACKED).singleOrNull()?.type
|
||||
}
|
||||
|
||||
fun KotlinType.isRecursiveInlineClassType() =
|
||||
isRecursiveInlineClassTypeInner(hashSetOf())
|
||||
|
||||
private fun KotlinType.isRecursiveInlineClassTypeInner(visited: HashSet<ClassDescriptor>): Boolean {
|
||||
val descriptor = constructor.declarationDescriptor as? ClassDescriptor ?: return false
|
||||
if (visited.contains(descriptor)) return true
|
||||
if (!descriptor.isInlineClass()) return false
|
||||
visited.add(descriptor)
|
||||
return unsubstitutedUnderlyingType()?.isRecursiveInlineClassTypeInner(visited) ?: false
|
||||
}
|
||||
|
||||
fun KotlinType.isNullableUnderlyingType(): Boolean {
|
||||
if (!isInlineClassType()) return false
|
||||
val underlyingType = unsubstitutedUnderlyingType() ?: return false
|
||||
|
||||
Reference in New Issue
Block a user