From ee9a174c1f210fb22f012251caec49e5aca190c0 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 20 Jan 2017 14:47:42 +0300 Subject: [PATCH] KT-7897 Do not require to call enum constructor for each entry if all parameters have default values Do not report an error on enum entry without initializer if all parameters have default values (error is still reported if there is no such constructor, or if the constructor call is ambiguous). Record resolved call on KtEnumEntry. NB is the enum entry has a corresponding subclass, we still have to generate the "default" constructor call, because FE doesn't know about the platform-specific representation of that class and its constructors. See also KT-14097, KT-15900 --- .../codegen/ImplementationBodyCodegen.java | 6 ++ .../kotlin/resolve/BodyResolver.java | 45 ++++++++- .../kotlin/resolve/DeclarationsChecker.kt | 7 +- .../kotlin/resolve/calls/util/CallMaker.java | 71 ++++++++++++++ .../kotlin/psi2ir/generators/BodyGenerator.kt | 18 +++- .../constructorWithDefaultArguments.kt | 6 ++ .../enum/defaultCtor/constructorWithVararg.kt | 9 ++ ...econdaryConstructorWithDefaultArguments.kt | 10 ++ .../secondaryConstructorWithVararg.kt | 13 +++ .../constructorWithDefaultArguments.txt | 15 +++ .../defaultCtor/constructorWithVararg.txt | 14 +++ ...condaryConstructorWithDefaultArguments.txt | 18 ++++ .../secondaryConstructorWithVararg.txt | 17 ++++ .../constructorWithDefaultParametersOnly.kt | 21 +++++ .../constructorWithDefaultParametersOnly.txt | 92 +++++++++++++++++++ compiler/testData/ir/irText/classes/enum.kt | 4 + compiler/testData/ir/irText/classes/enum.txt | 27 ++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 33 +++++++ .../checkers/DiagnosticsTestGenerated.java | 6 ++ .../codegen/BlackBoxCodegenTestGenerated.java | 33 +++++++ ...LightAnalysisModeCodegenTestGenerated.java | 33 +++++++ .../kotlin/resolve/DescriptorUtils.kt | 6 ++ .../semantics/JsCodegenBoxTestGenerated.java | 51 ++++++++++ 23 files changed, 539 insertions(+), 16 deletions(-) create mode 100644 compiler/testData/codegen/box/enum/defaultCtor/constructorWithDefaultArguments.kt create mode 100644 compiler/testData/codegen/box/enum/defaultCtor/constructorWithVararg.kt create mode 100644 compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt create mode 100644 compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithVararg.kt create mode 100644 compiler/testData/codegen/light-analysis/enum/defaultCtor/constructorWithDefaultArguments.txt create mode 100644 compiler/testData/codegen/light-analysis/enum/defaultCtor/constructorWithVararg.txt create mode 100644 compiler/testData/codegen/light-analysis/enum/defaultCtor/secondaryConstructorWithDefaultArguments.txt create mode 100644 compiler/testData/codegen/light-analysis/enum/defaultCtor/secondaryConstructorWithVararg.txt create mode 100644 compiler/testData/diagnostics/tests/enum/constructorWithDefaultParametersOnly.kt create mode 100644 compiler/testData/diagnostics/tests/enum/constructorWithDefaultParametersOnly.txt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 0045f7abc34..6dde314b579 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -29,6 +29,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.backend.common.CodegenUtil; import org.jetbrains.kotlin.backend.common.DataClassMethodGenerator; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; +import org.jetbrains.kotlin.codegen.binding.CodegenBinding; import org.jetbrains.kotlin.codegen.binding.MutableClosure; import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension; @@ -1593,6 +1594,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { iv.iconst(ordinal); List delegationSpecifiers = enumEntry.getSuperTypeListEntries(); + ResolvedCall defaultArgumentsConstructorCall = CallUtilKt.getResolvedCall(enumEntry, bindingContext); + boolean enumEntryHasSubclass = CodegenBinding.enumEntryNeedSubclass(bindingContext, classDescriptor); if (delegationSpecifiers.size() == 1 && !enumEntryNeedSubclass(bindingContext, enumEntry)) { ResolvedCall resolvedCall = CallUtilKt.getResolvedCallWithAssert(delegationSpecifiers.get(0), bindingContext); @@ -1600,6 +1603,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { codegen.invokeMethodWithArguments(method, resolvedCall, StackValue.none()); } + else if (defaultArgumentsConstructorCall != null && !enumEntryHasSubclass) { + codegen.invokeFunction(defaultArgumentsConstructorCall, StackValue.none()).put(Type.VOID_TYPE, iv); + } else { iv.invokespecial(implClass.getInternalName(), "", "(Ljava/lang/String;I)V", false); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java index f26edc4c1c8..8c64429b8cc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.resolve; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.util.containers.Queue; import kotlin.Unit; @@ -39,6 +40,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; import org.jetbrains.kotlin.resolve.calls.util.CallMaker; +import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil; import org.jetbrains.kotlin.resolve.scopes.*; import org.jetbrains.kotlin.types.*; @@ -58,6 +60,7 @@ import static org.jetbrains.kotlin.resolve.BindingContext.*; import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE; public class BodyResolver { + @NotNull private final Project project; @NotNull private final AnnotationChecker annotationChecker; @NotNull private final ExpressionTypingServices expressionTypingServices; @NotNull private final CallResolver callResolver; @@ -74,6 +77,7 @@ public class BodyResolver { @NotNull private final LanguageVersionSettings languageVersionSettings; public BodyResolver( + @NotNull Project project, @NotNull AnnotationResolver annotationResolver, @NotNull BodyResolveCache bodyResolveCache, @NotNull CallResolver callResolver, @@ -89,6 +93,7 @@ public class BodyResolver { @NotNull OverloadChecker overloadChecker, @NotNull LanguageVersionSettings languageVersionSettings ) { + this.project = project; this.annotationResolver = annotationResolver; this.bodyResolveCache = bodyResolveCache; this.callResolver = callResolver; @@ -262,7 +267,7 @@ public class BodyResolver { public void resolveSuperTypeEntryList( @NotNull final DataFlowInfo outerDataFlowInfo, - @NotNull KtClassOrObject jetClass, + @NotNull KtClassOrObject ktClass, @NotNull final ClassDescriptor descriptor, @Nullable final ConstructorDescriptor primaryConstructor, @NotNull LexicalScope scopeForConstructorResolution, @@ -380,19 +385,49 @@ public class BodyResolver { } }; - for (KtSuperTypeListEntry delegationSpecifier : jetClass.getSuperTypeListEntries()) { + if (ktClass instanceof KtEnumEntry && DescriptorUtils.isEnumEntry(descriptor) && ktClass.getSuperTypeListEntries().isEmpty()) { + assert scopeForConstructor != null : "Scope for enum class constructor should be non-null: " + descriptor; + resolveConstructorCallForEnumEntryWithoutInitializer((KtEnumEntry) ktClass, descriptor, scopeForConstructor, outerDataFlowInfo); + } + + for (KtSuperTypeListEntry delegationSpecifier : ktClass.getSuperTypeListEntries()) { delegationSpecifier.accept(visitor); } - if (DescriptorUtils.isAnnotationClass(descriptor) && jetClass.getSuperTypeList() != null) { - trace.report(SUPERTYPES_FOR_ANNOTATION_CLASS.on(jetClass.getSuperTypeList())); + if (DescriptorUtils.isAnnotationClass(descriptor) && ktClass.getSuperTypeList() != null) { + trace.report(SUPERTYPES_FOR_ANNOTATION_CLASS.on(ktClass.getSuperTypeList())); } if (primaryConstructorDelegationCall[0] != null && primaryConstructor != null) { recordConstructorDelegationCall(trace, primaryConstructor, primaryConstructorDelegationCall[0]); } - checkSupertypeList(descriptor, supertypes, jetClass); + checkSupertypeList(descriptor, supertypes, ktClass); + } + + private void resolveConstructorCallForEnumEntryWithoutInitializer( + @NotNull KtEnumEntry ktEnumEntry, + @NotNull ClassDescriptor enumEntryDescriptor, + @NotNull LexicalScope scopeForConstructor, + @NotNull DataFlowInfo outerDataFlowInfo + ) { + assert enumEntryDescriptor.getKind() == ClassKind.ENUM_ENTRY : "Enum entry expected: " + enumEntryDescriptor; + ClassDescriptor enumClassDescriptor = (ClassDescriptor) enumEntryDescriptor.getContainingDeclaration(); + if (enumClassDescriptor.getKind() != ClassKind.ENUM_CLASS) return; + if (enumClassDescriptor.isHeader()) return; + + List applicableConstructors = DescriptorUtilsKt.getConstructorForEmptyArgumentsList(enumClassDescriptor); + if (applicableConstructors.size() != 1) { + trace.report(ENUM_ENTRY_SHOULD_BE_INITIALIZED.on(ktEnumEntry)); + return; + } + + KtInitializerList ktInitializerList = new KtPsiFactory(project).createEnumEntryInitializerList(); + KtSuperTypeCallEntry ktCallEntry = (KtSuperTypeCallEntry) ktInitializerList.getInitializers().get(0); + Call call = CallMaker.makeConstructorCallWithoutTypeArguments(ktCallEntry); + trace.record(BindingContext.TYPE, ktCallEntry.getTypeReference(), enumClassDescriptor.getDefaultType()); + trace.record(BindingContext.CALL, ktEnumEntry, call); + callResolver.resolveFunctionCall(trace, scopeForConstructor, call, NO_EXPECTED_TYPE, outerDataFlowInfo, false); } // Returns a set of enum or sealed types of which supertypeOwner is an entry or a member diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt index 848e065f7d9..3fc14e13de7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.BindingContext.* import org.jetbrains.kotlin.resolve.DescriptorUtils.classCanHaveAbstractMembers import org.jetbrains.kotlin.resolve.DescriptorUtils.classCanHaveOpenMembers import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator +import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.kotlin.types.* @@ -882,9 +883,6 @@ class DeclarationsChecker( trace.report(HEADER_ENUM_ENTRY_WITH_BODY.on(enumEntry)) } } - else if (!enumEntry.hasInitializer() && !hasDefaultConstructor(enumClass)) { - trace.report(ENUM_ENTRY_SHOULD_BE_INITIALIZED.on(enumEntry)) - } } else { assert(DescriptorUtils.isInterface(enumClass)) { "Enum entry should be declared in enum class: " + enumEntryClass } @@ -969,9 +967,6 @@ class DeclarationsChecker( return !modifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD) } - private fun hasDefaultConstructor(classDescriptor: ClassDescriptor) = - classDescriptor.constructors.any { it.valueParameters.isEmpty() } - private fun PropertyDescriptor.hasAccessorImplementation(): Boolean { getter?.let { if (it.hasBody()) return true } setter?.let { if (it.hasBody()) return true } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java index 54811533434..c214f979be1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java @@ -282,6 +282,77 @@ public class CallMaker { }; } + @NotNull + public static Call makeConstructorCallForEnumEntryWithoutInitializer(@NotNull final KtSuperTypeCallEntry callElement) { + return new Call() { + @Nullable + @Override + public ASTNode getCallOperationNode() { + return null; + } + + @Nullable + @Override + public Receiver getExplicitReceiver() { + return null; + } + + @Nullable + @Override + public ReceiverValue getDispatchReceiver() { + return null; + } + + @Nullable + @Override + public KtExpression getCalleeExpression() { + return callElement.getCalleeExpression(); + } + + @Nullable + @Override + public KtValueArgumentList getValueArgumentList() { + return callElement.getValueArgumentList(); + } + + @NotNull + @Override + public List getValueArguments() { + return callElement.getValueArguments(); + } + + @NotNull + @Override + public List getFunctionLiteralArguments() { + return Collections.emptyList(); + } + + @NotNull + @Override + public List getTypeArguments() { + return Collections.emptyList(); + } + + @Nullable + @Override + public KtTypeArgumentList getTypeArgumentList() { + return null; + } + + @NotNull + @Override + public KtElement getCallElement() { + return callElement; + } + + @NotNull + @Override + public CallType getCallType() { + return CallType.DEFAULT; + } + }; + } + @NotNull public static Call makeCall(@Nullable final Receiver explicitReceiver, @Nullable final ASTNode callOperationNode, @NotNull final KtCallElement callElement) { return new Call() { diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt index 4da8e3e7f3e..3f463c912e9 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import java.lang.AssertionError import java.util.* @@ -267,18 +268,25 @@ class BodyGenerator(val scopeOwner: DeclarationDescriptor, override val context: val statementGenerator = createStatementGenerator() // Entry constructor with argument(s) - ktEnumEntry.getSuperTypeListEntries().firstOrNull()?.let { ktSuperCallElement -> - val enumConstructorCall = statementGenerator.pregenerateCall(getResolvedCall(ktSuperCallElement)!!) - return CallGenerator(statementGenerator).generateEnumConstructorSuperCall( - ktEnumEntry.startOffset, ktEnumEntry.endOffset, - enumConstructorCall) + val ktSuperCallElement = ktEnumEntry.superTypeListEntries.firstOrNull() + if (ktSuperCallElement != null) { + return statementGenerator.generateEnumConstructorCall(getResolvedCall(ktSuperCallElement)!!, ktEnumEntry) } + val enumDefaultConstructorCall = getResolvedCall(ktEnumEntry) + if (enumDefaultConstructorCall != null) { + return statementGenerator.generateEnumConstructorCall(enumDefaultConstructorCall, ktEnumEntry) + } + // No-argument enum entry constructor val enumClassConstructor = enumClassDescriptor.constructors.find { it.valueParameters.isEmpty() }!! return IrEnumConstructorCallImpl(ktEnumEntry.startOffset, ktEnumEntry.endOffset, enumClassConstructor) } + private fun StatementGenerator.generateEnumConstructorCall(constructorCall: ResolvedCall, ktEnumEntry: KtEnumEntry) = + CallGenerator(this).generateEnumConstructorSuperCall(ktEnumEntry.startOffset, ktEnumEntry.endOffset, + pregenerateCall(constructorCall)) + } diff --git a/compiler/testData/codegen/box/enum/defaultCtor/constructorWithDefaultArguments.kt b/compiler/testData/codegen/box/enum/defaultCtor/constructorWithDefaultArguments.kt new file mode 100644 index 00000000000..fcdaf5b62ae --- /dev/null +++ b/compiler/testData/codegen/box/enum/defaultCtor/constructorWithDefaultArguments.kt @@ -0,0 +1,6 @@ +enum class Test(val str: String = "OK") { + OK +} + +fun box(): String = + Test.OK.str \ No newline at end of file diff --git a/compiler/testData/codegen/box/enum/defaultCtor/constructorWithVararg.kt b/compiler/testData/codegen/box/enum/defaultCtor/constructorWithVararg.kt new file mode 100644 index 00000000000..be8f86f66a4 --- /dev/null +++ b/compiler/testData/codegen/box/enum/defaultCtor/constructorWithVararg.kt @@ -0,0 +1,9 @@ +// IGNORE_BACKEND: JS + +enum class Test(vararg xs: Int) { + OK; + val values = xs +} + +fun box(): String = + if (Test.OK.values.size == 0) "OK" else "Fail" \ No newline at end of file diff --git a/compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt b/compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt new file mode 100644 index 00000000000..58b0be6c9f0 --- /dev/null +++ b/compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt @@ -0,0 +1,10 @@ +// IGNORE_BACKEND: JS +// see KT-14097 + +enum class Test(val x: Int, val str: String) { + OK; + constructor(x: Int = 0) : this(x, "OK") +} + +fun box(): String = + Test.OK.str \ No newline at end of file diff --git a/compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithVararg.kt b/compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithVararg.kt new file mode 100644 index 00000000000..9bee8603d4a --- /dev/null +++ b/compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithVararg.kt @@ -0,0 +1,13 @@ +// IGNORE_BACKEND: JS +// see KT-14097 + +enum class Test(val x: Int, val str: String) { + OK; + constructor(vararg xs: Int) : this(xs.size + 42, "OK") +} + +fun box(): String = + if (Test.OK.x == 42) + Test.OK.str + else + "Fail" \ No newline at end of file diff --git a/compiler/testData/codegen/light-analysis/enum/defaultCtor/constructorWithDefaultArguments.txt b/compiler/testData/codegen/light-analysis/enum/defaultCtor/constructorWithDefaultArguments.txt new file mode 100644 index 00000000000..5a114b5eef7 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/enum/defaultCtor/constructorWithDefaultArguments.txt @@ -0,0 +1,15 @@ +@kotlin.Metadata +public final class ConstructorWithDefaultArgumentsKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} + +@kotlin.Metadata +public enum class Test { + public final static field OK: Test + private final @org.jetbrains.annotations.NotNull field str: java.lang.String + protected method (@java.lang.Synthetic p0: java.lang.String, @java.lang.Synthetic p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): void + synthetic method (p0: java.lang.String, p1: int, p2: java.lang.String, p3: int, p4: kotlin.jvm.internal.DefaultConstructorMarker): void + public final @org.jetbrains.annotations.NotNull method getStr(): java.lang.String + public static method valueOf(p0: java.lang.String): Test + public static method values(): Test[] +} diff --git a/compiler/testData/codegen/light-analysis/enum/defaultCtor/constructorWithVararg.txt b/compiler/testData/codegen/light-analysis/enum/defaultCtor/constructorWithVararg.txt new file mode 100644 index 00000000000..72f6cca9d74 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/enum/defaultCtor/constructorWithVararg.txt @@ -0,0 +1,14 @@ +@kotlin.Metadata +public final class ConstructorWithVarargKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} + +@kotlin.Metadata +public enum class Test { + public final static field OK: Test + private final @org.jetbrains.annotations.NotNull field values: int[] + protected method (@java.lang.Synthetic p0: java.lang.String, @java.lang.Synthetic p1: int, @org.jetbrains.annotations.NotNull p2: int[]): void + public final @org.jetbrains.annotations.NotNull method getValues(): int[] + public static method valueOf(p0: java.lang.String): Test + public static method values(): Test[] +} diff --git a/compiler/testData/codegen/light-analysis/enum/defaultCtor/secondaryConstructorWithDefaultArguments.txt b/compiler/testData/codegen/light-analysis/enum/defaultCtor/secondaryConstructorWithDefaultArguments.txt new file mode 100644 index 00000000000..b89ecebfafd --- /dev/null +++ b/compiler/testData/codegen/light-analysis/enum/defaultCtor/secondaryConstructorWithDefaultArguments.txt @@ -0,0 +1,18 @@ +@kotlin.Metadata +public final class SecondaryConstructorWithDefaultArgumentsKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} + +@kotlin.Metadata +public enum class Test { + public final static field OK: Test + private final @org.jetbrains.annotations.NotNull field str: java.lang.String + private final field x: int + protected method (@java.lang.Synthetic p0: java.lang.String, @java.lang.Synthetic p1: int, p2: int, @org.jetbrains.annotations.NotNull p3: java.lang.String): void + protected method (p0: java.lang.String, p1: int, p2: int): void + synthetic method (p0: java.lang.String, p1: int, p2: int, p3: int, p4: kotlin.jvm.internal.DefaultConstructorMarker): void + public final @org.jetbrains.annotations.NotNull method getStr(): java.lang.String + public final method getX(): int + public static method valueOf(p0: java.lang.String): Test + public static method values(): Test[] +} diff --git a/compiler/testData/codegen/light-analysis/enum/defaultCtor/secondaryConstructorWithVararg.txt b/compiler/testData/codegen/light-analysis/enum/defaultCtor/secondaryConstructorWithVararg.txt new file mode 100644 index 00000000000..e30b7f28fc7 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/enum/defaultCtor/secondaryConstructorWithVararg.txt @@ -0,0 +1,17 @@ +@kotlin.Metadata +public final class SecondaryConstructorWithVarargKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} + +@kotlin.Metadata +public enum class Test { + public final static field OK: Test + private final @org.jetbrains.annotations.NotNull field str: java.lang.String + private final field x: int + protected method (@java.lang.Synthetic p0: java.lang.String, @java.lang.Synthetic p1: int, @org.jetbrains.annotations.NotNull p2: int[]): void + protected method (@java.lang.Synthetic p0: java.lang.String, @java.lang.Synthetic p1: int, p2: int, @org.jetbrains.annotations.NotNull p3: java.lang.String): void + public final @org.jetbrains.annotations.NotNull method getStr(): java.lang.String + public final method getX(): int + public static method valueOf(p0: java.lang.String): Test + public static method values(): Test[] +} diff --git a/compiler/testData/diagnostics/tests/enum/constructorWithDefaultParametersOnly.kt b/compiler/testData/diagnostics/tests/enum/constructorWithDefaultParametersOnly.kt new file mode 100644 index 00000000000..3c5286d1bba --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/constructorWithDefaultParametersOnly.kt @@ -0,0 +1,21 @@ +enum class TestOk(val x: String = "OK") { + TEST1, + TEST2(), + TEST3("Hello") +} + +enum class TestErrors(val x: String) { + TEST1, + TEST2(), + TEST3("Hello") +} + +enum class TestMultipleConstructors(val x: String = "", val y: Int = 0) { + TEST; + constructor(x: String = "") : this(x, 0) +} + +enum class TestVarargs(val x: Int) { + TEST; + constructor(vararg xs: Any) : this(xs.size) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/enum/constructorWithDefaultParametersOnly.txt b/compiler/testData/diagnostics/tests/enum/constructorWithDefaultParametersOnly.txt new file mode 100644 index 00000000000..0e7b5efbe97 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/constructorWithDefaultParametersOnly.txt @@ -0,0 +1,92 @@ +package + +public final enum class TestErrors : kotlin.Enum { + enum entry TEST1 + + enum entry TEST2 + + enum entry TEST3 + + private constructor TestErrors(/*0*/ x: kotlin.String) + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + public final val x: kotlin.String + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: TestErrors): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): TestErrors + public final /*synthesized*/ fun values(): kotlin.Array +} + +public final enum class TestMultipleConstructors : kotlin.Enum { + enum entry TEST + + private constructor TestMultipleConstructors(/*0*/ x: kotlin.String = ...) + private constructor TestMultipleConstructors(/*0*/ x: kotlin.String = ..., /*1*/ y: kotlin.Int = ...) + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + public final val x: kotlin.String + public final val y: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: TestMultipleConstructors): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): TestMultipleConstructors + public final /*synthesized*/ fun values(): kotlin.Array +} + +public final enum class TestOk : kotlin.Enum { + enum entry TEST1 + + enum entry TEST2 + + enum entry TEST3 + + private constructor TestOk(/*0*/ x: kotlin.String = ...) + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + public final val x: kotlin.String + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: TestOk): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): TestOk + public final /*synthesized*/ fun values(): kotlin.Array +} + +public final enum class TestVarargs : kotlin.Enum { + enum entry TEST + + private constructor TestVarargs(/*0*/ vararg xs: kotlin.Any /*kotlin.Array*/) + private constructor TestVarargs(/*0*/ x: kotlin.Int) + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + public final val x: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: TestVarargs): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): TestVarargs + public final /*synthesized*/ fun values(): kotlin.Array +} diff --git a/compiler/testData/ir/irText/classes/enum.kt b/compiler/testData/ir/irText/classes/enum.kt index d0e2b8962f7..72cd8431719 100644 --- a/compiler/testData/ir/irText/classes/enum.kt +++ b/compiler/testData/ir/irText/classes/enum.kt @@ -37,4 +37,8 @@ enum class TestEnum4(val x: Int) { } ; abstract fun foo() +} + +enum class TestEnum5(val x: Int = 0) { + TEST1, TEST2(), TEST3(0) } \ No newline at end of file diff --git a/compiler/testData/ir/irText/classes/enum.txt b/compiler/testData/ir/irText/classes/enum.txt index b773c985e1b..652b889c98d 100644 --- a/compiler/testData/ir/irText/classes/enum.txt +++ b/compiler/testData/ir/irText/classes/enum.txt @@ -116,3 +116,30 @@ FILE /enum.kt SYNTHETIC_BODY kind=ENUM_VALUES FUN ENUM_CLASS_SPECIAL_MEMBER public final fun valueOf(value: kotlin.String): TestEnum4 SYNTHETIC_BODY kind=ENUM_VALUEOF + CLASS ENUM_CLASS TestEnum5 + CONSTRUCTOR private constructor TestEnum5(x: kotlin.Int = ...) + x: EXPRESSION_BODY + CONST Int type=kotlin.Int value='0' + BLOCK_BODY + ENUM_CONSTRUCTOR_CALL 'constructor Enum(String, Int)' + INSTANCE_INITIALIZER_CALL classDescriptor='TestEnum5' + PROPERTY public final val x: kotlin.Int + FIELD PROPERTY_BACKING_FIELD public final val x: kotlin.Int + EXPRESSION_BODY + GET_VAR 'value-parameter x: Int = ...' type=kotlin.Int origin=INITIALIZE_PROPERTY_FROM_PARAMETER + FUN DEFAULT_PROPERTY_ACCESSOR public final fun (): kotlin.Int + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): Int' + GET_FIELD 'x: Int' type=kotlin.Int origin=null + receiver: GET_VAR '' type=TestEnum5 origin=null + ENUM_ENTRY enum entry TEST1 + init: ENUM_CONSTRUCTOR_CALL 'constructor TestEnum5(Int = ...)' + ENUM_ENTRY enum entry TEST2 + init: ENUM_CONSTRUCTOR_CALL 'constructor TestEnum5(Int = ...)' + ENUM_ENTRY enum entry TEST3 + init: ENUM_CONSTRUCTOR_CALL 'constructor TestEnum5(Int = ...)' + x: CONST Int type=kotlin.Int value='0' + FUN ENUM_CLASS_SPECIAL_MEMBER public final fun values(): kotlin.Array + SYNTHETIC_BODY kind=ENUM_VALUES + FUN ENUM_CLASS_SPECIAL_MEMBER public final fun valueOf(value: kotlin.String): TestEnum5 + SYNTHETIC_BODY kind=ENUM_VALUEOF diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 6cd2342b0d8..274f20405be 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -7018,6 +7018,39 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/valueof.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/box/enum/defaultCtor") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DefaultCtor extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInDefaultCtor() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/enum/defaultCtor"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("constructorWithDefaultArguments.kt") + public void testConstructorWithDefaultArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/constructorWithDefaultArguments.kt"); + doTest(fileName); + } + + @TestMetadata("constructorWithVararg.kt") + public void testConstructorWithVararg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/constructorWithVararg.kt"); + doTest(fileName); + } + + @TestMetadata("secondaryConstructorWithDefaultArguments.kt") + public void testSecondaryConstructorWithDefaultArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt"); + doTest(fileName); + } + + @TestMetadata("secondaryConstructorWithVararg.kt") + public void testSecondaryConstructorWithVararg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithVararg.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/codegen/box/evaluate") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 25f6c8268aa..d34de29b303 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -7327,6 +7327,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("constructorWithDefaultParametersOnly.kt") + public void testConstructorWithDefaultParametersOnly() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/enum/constructorWithDefaultParametersOnly.kt"); + doTest(fileName); + } + @TestMetadata("dontCreatePackageTypeForEnumEntry.kt") public void testDontCreatePackageTypeForEnumEntry() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/enum/dontCreatePackageTypeForEnumEntry.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index ab09a310f85..5ea47d3c4ad 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -7018,6 +7018,39 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/valueof.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/box/enum/defaultCtor") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DefaultCtor extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInDefaultCtor() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/enum/defaultCtor"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("constructorWithDefaultArguments.kt") + public void testConstructorWithDefaultArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/constructorWithDefaultArguments.kt"); + doTest(fileName); + } + + @TestMetadata("constructorWithVararg.kt") + public void testConstructorWithVararg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/constructorWithVararg.kt"); + doTest(fileName); + } + + @TestMetadata("secondaryConstructorWithDefaultArguments.kt") + public void testSecondaryConstructorWithDefaultArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt"); + doTest(fileName); + } + + @TestMetadata("secondaryConstructorWithVararg.kt") + public void testSecondaryConstructorWithVararg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithVararg.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/codegen/box/evaluate") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index 820e5f664c3..42a7e8ca6cd 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -7018,6 +7018,39 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/valueof.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/box/enum/defaultCtor") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DefaultCtor extends AbstractLightAnalysisModeCodegenTest { + public void testAllFilesPresentInDefaultCtor() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/enum/defaultCtor"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("constructorWithDefaultArguments.kt") + public void testConstructorWithDefaultArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/constructorWithDefaultArguments.kt"); + doTest(fileName); + } + + @TestMetadata("constructorWithVararg.kt") + public void testConstructorWithVararg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/constructorWithVararg.kt"); + doTest(fileName); + } + + @TestMetadata("secondaryConstructorWithDefaultArguments.kt") + public void testSecondaryConstructorWithDefaultArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt"); + doTest(fileName); + } + + @TestMetadata("secondaryConstructorWithVararg.kt") + public void testSecondaryConstructorWithVararg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithVararg.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/codegen/box/evaluate") diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt index 51ce93e09f8..f8d000b3322 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt @@ -391,3 +391,9 @@ fun computeSealedSubclasses(sealedClass: ClassDescriptor): Collection = + constructors.filter { it.valueParameters.all { it.hasDefaultValue() || it.varargElementType != null } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 37314141e1e..f975e85ae53 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -7937,6 +7937,57 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/valueof.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/box/enum/defaultCtor") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DefaultCtor extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInDefaultCtor() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/enum/defaultCtor"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("constructorWithDefaultArguments.kt") + public void testConstructorWithDefaultArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/constructorWithDefaultArguments.kt"); + doTest(fileName); + } + + @TestMetadata("constructorWithVararg.kt") + public void testConstructorWithVararg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/constructorWithVararg.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("secondaryConstructorWithDefaultArguments.kt") + public void testSecondaryConstructorWithDefaultArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("secondaryConstructorWithVararg.kt") + public void testSecondaryConstructorWithVararg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithVararg.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + } } @TestMetadata("compiler/testData/codegen/box/evaluate")