Skip to content

Commit fd6d4c3

Browse files
author
Michael Bogdanov
committed
Special enum function support; Fix for KT-10569: Cannot iterate over values of an enum class when it is used as a generic parameter
#KT-10569 Fixed
1 parent 6a3597f commit fd6d4c3

28 files changed

+547
-4
lines changed

compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4445,6 +4445,12 @@ public Unit invoke(InstructionAdapter v) {
44454445

44464446
public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
44474447
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind
4448+
) {
4449+
putReifiedOperationMarkerIfTypeIsReifiedParameter(type, operationKind, v);
4450+
}
4451+
4452+
public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
4453+
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind, @NotNull InstructionAdapter v
44484454
) {
44494455
Pair<TypeParameterDescriptor, ReificationArgument> typeParameterAndReificationArgument = extractReificationArgument(type);
44504456
if (typeParameterAndReificationArgument != null && typeParameterAndReificationArgument.getFirst().isReified()) {

compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
5151
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
5252
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
53+
import org.jetbrains.kotlin.types.KotlinType;
5354
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS;
5455
import org.jetbrains.kotlin.types.expressions.LabelResolver;
5556
import org.jetbrains.org.objectweb.asm.Label;
@@ -171,7 +172,7 @@ public void genCallInner(
171172

172173
SMAPAndMethodNode nodeAndSmap = null;
173174
try {
174-
nodeAndSmap = createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault);
175+
nodeAndSmap = createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault, resolvedCall);
175176
endCall(inlineCall(nodeAndSmap));
176177
}
177178
catch (CompilationException e) {
@@ -227,8 +228,24 @@ static SMAPAndMethodNode createMethodNode(
227228
@NotNull JvmMethodSignature jvmSignature,
228229
@NotNull ExpressionCodegen codegen,
229230
@NotNull CodegenContext context,
230-
boolean callDefault
231+
boolean callDefault,
232+
@Nullable ResolvedCall<?> resolvedCall
231233
) {
234+
if (InlineCodegenUtil.isSpecialEnumMethod(functionDescriptor)) {
235+
assert resolvedCall != null : "Resolved call for " + functionDescriptor + " should be not null";
236+
Map<TypeParameterDescriptor, KotlinType> arguments = resolvedCall.getTypeArguments();
237+
assert arguments.size() == 1 : "Resolved call for " + functionDescriptor + " should have 1 type argument";
238+
KotlinType type = arguments.values().iterator().next();
239+
MethodNode node =
240+
InlineCodegenUtil.createSpecialEnumMethodBody(
241+
codegen,
242+
functionDescriptor.getName().asString(),
243+
type,
244+
codegen.getState().getTypeMapper()
245+
);
246+
return new SMAPAndMethodNode(node, SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1));
247+
}
248+
232249
final GenerationState state = codegen.getState();
233250
final Method asmMethod =
234251
callDefault

compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegenForDefaultBody.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class InlineCodegenForDefaultBody(
6565
}
6666

6767
override fun genCallInner(callableMethod: Callable, resolvedCall: ResolvedCall<*>?, callDefault: Boolean, codegen: ExpressionCodegen) {
68-
val nodeAndSmap = InlineCodegen.createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault)
68+
val nodeAndSmap = InlineCodegen.createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault, null)
6969
val childSourceMapper = InlineCodegen.createNestedSourceMapper(nodeAndSmap, sourceMapper)
7070

7171
val node = nodeAndSmap.node

compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegenUtil.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.jetbrains.annotations.NotNull;
2323
import org.jetbrains.annotations.Nullable;
2424
import org.jetbrains.kotlin.backend.common.output.OutputFile;
25+
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
26+
import org.jetbrains.kotlin.codegen.ExpressionCodegen;
2527
import org.jetbrains.kotlin.codegen.MemberCodegen;
2628
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
2729
import org.jetbrains.kotlin.codegen.context.CodegenContext;
@@ -44,6 +46,7 @@
4446
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
4547
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
4648
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
49+
import org.jetbrains.kotlin.types.KotlinType;
4750
import org.jetbrains.kotlin.util.OperatorNameConventions;
4851
import org.jetbrains.org.objectweb.asm.*;
4952
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
@@ -54,6 +57,7 @@
5457
import java.io.IOException;
5558
import java.io.PrintWriter;
5659
import java.io.StringWriter;
60+
import java.util.List;
5761
import java.util.ListIterator;
5862

5963
public class InlineCodegenUtil {
@@ -509,4 +513,47 @@ public static boolean isFakeLocalVariableForInline(@NotNull String name) {
509513
public static boolean isThis0(@NotNull String name) {
510514
return THIS$0.equals(name);
511515
}
516+
517+
public static boolean isSpecialEnumMethod(@NotNull FunctionDescriptor functionDescriptor) {
518+
DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
519+
if (!(containingDeclaration instanceof PackageFragmentDescriptor)) {
520+
return false;
521+
}
522+
if (!containingDeclaration.getName().equals(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) {
523+
return false;
524+
}
525+
if (functionDescriptor.getTypeParameters().size() != 1) {
526+
return false;
527+
}
528+
String name = functionDescriptor.getName().asString();
529+
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
530+
return "enumValues".equals(name) && parameters.size() == 0 ||
531+
"enumValueOf".equals(name) && parameters.size() == 1 && KotlinBuiltIns.isString(parameters.get(0).getType());
532+
}
533+
534+
public static MethodNode createSpecialEnumMethodBody(
535+
@NotNull ExpressionCodegen codegen,
536+
@NotNull String name,
537+
@NotNull KotlinType type,
538+
@NotNull KotlinTypeMapper typeMapper
539+
) {
540+
boolean isEnumValues = "enumValues".equals(name);
541+
Type invokeType = typeMapper.mapType(type);
542+
String desc = getSpecialEnumFunDescriptor(invokeType, isEnumValues);
543+
544+
MethodNode node = new MethodNode(API, Opcodes.ACC_STATIC, "fake", desc, null, null);
545+
if (!isEnumValues) {
546+
node.visitVarInsn(Opcodes.ALOAD, 0);
547+
}
548+
codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.ENUM_REIFIED, new InstructionAdapter(node));
549+
node.visitMethodInsn(Opcodes.INVOKESTATIC, invokeType.getInternalName(), isEnumValues ? "values" : "valueOf", desc, false);
550+
node.visitInsn(Opcodes.ARETURN);
551+
node.visitMaxs(isEnumValues ? 2 : 3, isEnumValues ? 0 : 1);
552+
return node;
553+
}
554+
555+
public static String getSpecialEnumFunDescriptor(@NotNull Type type, boolean isEnumValues) {
556+
return (isEnumValues ? "()[" : "(Ljava/lang/String;)") + "L" + type.getInternalName() + ";";
557+
}
512558
}
559+

compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class ReificationArgument(
5959

6060
class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?) {
6161
enum class OperationKind {
62-
NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS;
62+
NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS, ENUM_REIFIED;
6363

6464
val id: Int get() = ordinal
6565
}
@@ -139,6 +139,7 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
139139
OperationKind.SAFE_AS -> processAs(insn, instructions, kotlinType, asmType, safe = true)
140140
OperationKind.IS -> processIs(insn, instructions, kotlinType, asmType)
141141
OperationKind.JAVA_CLASS -> processJavaClass(insn, asmType)
142+
OperationKind.ENUM_REIFIED -> processSpecialEnumFunction(insn, asmType)
142143
}) {
143144
instructions.remove(insn.previous.previous!!) // PUSH operation ID
144145
instructions.remove(insn.previous!!) // PUSH type parameter
@@ -219,6 +220,14 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
219220
next.cst = parameter
220221
return true
221222
}
223+
224+
private fun processSpecialEnumFunction(insn: MethodInsnNode, parameter: Type): Boolean {
225+
val next = insn.next
226+
if (next !is MethodInsnNode) return false
227+
next.owner = parameter.internalName
228+
next.desc = InlineCodegenUtil.getSpecialEnumFunDescriptor(parameter, "values" == next.name)
229+
return true
230+
}
222231
}
223232

224233
private val MethodInsnNode.reificationArgument: ReificationArgument?

compiler/testData/builtin-classes/default/kotlin.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ public fun byteArrayOf(/*0*/ vararg elements: kotlin.Byte /*kotlin.ByteArray*/):
77
public fun charArrayOf(/*0*/ vararg elements: kotlin.Char /*kotlin.CharArray*/): kotlin.CharArray
88
public fun doubleArrayOf(/*0*/ vararg elements: kotlin.Double /*kotlin.DoubleArray*/): kotlin.DoubleArray
99
public inline fun </*0*/ reified @kotlin.internal.PureReifiable T> emptyArray(): kotlin.Array<T>
10+
public inline fun </*0*/ reified T : kotlin.Enum<T>> enumValueOf(/*0*/ name: kotlin.String): T
11+
public inline fun </*0*/ reified T : kotlin.Enum<T>> enumValues(): kotlin.Array<T>
1012
public fun floatArrayOf(/*0*/ vararg elements: kotlin.Float /*kotlin.FloatArray*/): kotlin.FloatArray
1113
public fun intArrayOf(/*0*/ vararg elements: kotlin.Int /*kotlin.IntArray*/): kotlin.IntArray
1214
public fun longArrayOf(/*0*/ vararg elements: kotlin.Long /*kotlin.LongArray*/): kotlin.LongArray

compiler/testData/builtin-classes/java6/kotlin.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ public fun byteArrayOf(/*0*/ vararg elements: kotlin.Byte /*kotlin.ByteArray*/):
77
public fun charArrayOf(/*0*/ vararg elements: kotlin.Char /*kotlin.CharArray*/): kotlin.CharArray
88
public fun doubleArrayOf(/*0*/ vararg elements: kotlin.Double /*kotlin.DoubleArray*/): kotlin.DoubleArray
99
public inline fun </*0*/ reified @kotlin.internal.PureReifiable T> emptyArray(): kotlin.Array<T>
10+
public inline fun </*0*/ reified T : kotlin.Enum<T>> enumValueOf(/*0*/ name: kotlin.String): T
11+
public inline fun </*0*/ reified T : kotlin.Enum<T>> enumValues(): kotlin.Array<T>
1012
public fun floatArrayOf(/*0*/ vararg elements: kotlin.Float /*kotlin.FloatArray*/): kotlin.FloatArray
1113
public fun intArrayOf(/*0*/ vararg elements: kotlin.Int /*kotlin.IntArray*/): kotlin.IntArray
1214
public fun longArrayOf(/*0*/ vararg elements: kotlin.Long /*kotlin.LongArray*/): kotlin.LongArray

compiler/testData/builtin-classes/java8/kotlin.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ public fun byteArrayOf(/*0*/ vararg elements: kotlin.Byte /*kotlin.ByteArray*/):
77
public fun charArrayOf(/*0*/ vararg elements: kotlin.Char /*kotlin.CharArray*/): kotlin.CharArray
88
public fun doubleArrayOf(/*0*/ vararg elements: kotlin.Double /*kotlin.DoubleArray*/): kotlin.DoubleArray
99
public inline fun </*0*/ reified @kotlin.internal.PureReifiable T> emptyArray(): kotlin.Array<T>
10+
public inline fun </*0*/ reified T : kotlin.Enum<T>> enumValueOf(/*0*/ name: kotlin.String): T
11+
public inline fun </*0*/ reified T : kotlin.Enum<T>> enumValues(): kotlin.Array<T>
1012
public fun floatArrayOf(/*0*/ vararg elements: kotlin.Float /*kotlin.FloatArray*/): kotlin.FloatArray
1113
public fun intArrayOf(/*0*/ vararg elements: kotlin.Int /*kotlin.IntArray*/): kotlin.IntArray
1214
public fun longArrayOf(/*0*/ vararg elements: kotlin.Long /*kotlin.LongArray*/): kotlin.LongArray

compiler/testData/builtin-classes/newMethods/kotlin.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ public fun byteArrayOf(/*0*/ vararg elements: kotlin.Byte /*kotlin.ByteArray*/):
77
public fun charArrayOf(/*0*/ vararg elements: kotlin.Char /*kotlin.CharArray*/): kotlin.CharArray
88
public fun doubleArrayOf(/*0*/ vararg elements: kotlin.Double /*kotlin.DoubleArray*/): kotlin.DoubleArray
99
public inline fun </*0*/ reified @kotlin.internal.PureReifiable T> emptyArray(): kotlin.Array<T>
10+
public inline fun </*0*/ reified T : kotlin.Enum<T>> enumValueOf(/*0*/ name: kotlin.String): T
11+
public inline fun </*0*/ reified T : kotlin.Enum<T>> enumValues(): kotlin.Array<T>
1012
public fun floatArrayOf(/*0*/ vararg elements: kotlin.Float /*kotlin.FloatArray*/): kotlin.FloatArray
1113
public fun intArrayOf(/*0*/ vararg elements: kotlin.Int /*kotlin.IntArray*/): kotlin.IntArray
1214
public fun longArrayOf(/*0*/ vararg elements: kotlin.Long /*kotlin.LongArray*/): kotlin.LongArray
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// FILE: 1.kt
2+
// WITH_RUNTIME
3+
package test
4+
5+
var result = ""
6+
7+
inline fun <reified T : Enum<T>> renderOptions(render: (T) -> String) {
8+
val values = enumValues<T>()
9+
for (v in values) {
10+
result += render(v)
11+
}
12+
}
13+
14+
enum class Z {
15+
O, K;
16+
17+
val myParam = name
18+
}
19+
20+
21+
// FILE: 2.kt
22+
23+
import test.*
24+
25+
fun box(): String {
26+
renderOptions<Z> {
27+
it.myParam
28+
}
29+
return result
30+
}
31+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// FILE: 1.kt
2+
// WITH_RUNTIME
3+
package test
4+
5+
inline fun <reified T : Enum<T>> myValueOf(): String {
6+
return enumValueOf<T>("OK").name
7+
}
8+
9+
enum class Z {
10+
OK
11+
}
12+
13+
14+
// FILE: 2.kt
15+
16+
import test.*
17+
18+
fun box(): String {
19+
return myValueOf<Z>()
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// FILE: 1.kt
2+
// WITH_RUNTIME
3+
package test
4+
5+
inline fun <reified T : Enum<T>> myValueOf(): String {
6+
return { enumValueOf<T>("OK") }().name
7+
}
8+
9+
enum class Z {
10+
OK
11+
}
12+
13+
14+
// FILE: 2.kt
15+
16+
import test.*
17+
18+
fun box(): String {
19+
return myValueOf<Z>()
20+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// FILE: 1.kt
2+
// WITH_RUNTIME
3+
package test
4+
5+
inline fun <reified T : Enum<T>> myValueOf(): String {
6+
return myValueOf2<T>()
7+
}
8+
9+
inline fun <reified Y : Enum<Y>> myValueOf2(): String {
10+
return enumValueOf<Y>("OK").name
11+
}
12+
13+
14+
enum class Z {
15+
OK
16+
}
17+
18+
19+
// FILE: 2.kt
20+
21+
import test.*
22+
23+
fun box(): String {
24+
return myValueOf<Z>()
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// FILE: 1.kt
2+
// WITH_RUNTIME
3+
package test
4+
5+
inline fun <reified T : Enum<T>> myValueOf(): String {
6+
return myValueOf2<T>()
7+
}
8+
9+
inline fun <reified Y : Enum<Y>> myValueOf2(): String {
10+
return { enumValueOf<Y>("OK").name }()
11+
}
12+
13+
14+
enum class Z {
15+
OK
16+
}
17+
18+
19+
// FILE: 2.kt
20+
21+
import test.*
22+
23+
fun box(): String {
24+
return myValueOf<Z>()
25+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// FILE: 1.kt
2+
// WITH_RUNTIME
3+
package test
4+
5+
inline fun myValueOf(): String {
6+
return enumValueOf<Z>("OK").name
7+
}
8+
9+
enum class Z {
10+
OK
11+
}
12+
13+
14+
// FILE: 2.kt
15+
16+
import test.*
17+
18+
fun box(): String {
19+
return myValueOf()
20+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// FILE: 1.kt
2+
// WITH_RUNTIME
3+
package test
4+
5+
inline fun <reified T : Enum<T>> myValues(): String {
6+
val values = enumValues<T>()
7+
return values.joinToString("")
8+
}
9+
10+
enum class Z {
11+
O, K
12+
}
13+
14+
15+
// FILE: 2.kt
16+
17+
import test.*
18+
19+
fun box(): String {
20+
return myValues<Z>()
21+
}

0 commit comments

Comments
 (0)