Fix enum entry reference from enum entry initialization context

Enum entries are "special" kind of singletons that should be
referenced as a captured 'this' instance inside during entry
initialization, because corresponding static fields in enum class
are not initialized yet.

 #KT-7257 Fixed
This commit is contained in:
Dmitry Petrov
2017-09-11 11:42:53 +03:00
parent 40e2046e76
commit 6b6d1e80f8
19 changed files with 601 additions and 14 deletions
@@ -52,7 +52,6 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
import org.jetbrains.kotlin.codegen.when.SwitchCodegenProvider;
import org.jetbrains.kotlin.config.ApiVersion;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
@@ -1741,7 +1740,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return StackValue.singleton(classDescriptor, typeMapper);
}
if (isEnumEntry(classDescriptor)) {
return StackValue.enumEntry(classDescriptor, typeMapper);
if (isInsideEnumEntry(classDescriptor)) {
return generateThisOrOuterFromContext(classDescriptor, false, false);
}
else {
return StackValue.enumEntry(classDescriptor, typeMapper);
}
}
ClassDescriptor companionObjectDescriptor = classDescriptor.getCompanionObjectDescriptor();
if (companionObjectDescriptor != null) {
@@ -2534,21 +2538,23 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return generateThisOrOuter(calleeContainingClass, isSuper, false);
}
private boolean isInInnerClassConstructorContext(@NotNull ClassDescriptor calleeContainingClass) {
if (!(context instanceof ConstructorContext)) return false;
private boolean isInsideEnumEntry(@NotNull ClassDescriptor enumEntryDescriptor) {
assert DescriptorUtils.isEnumEntry(enumEntryDescriptor) :
"Enum entry reference expected: " + enumEntryDescriptor;
CallableMemberDescriptor contextDescriptor = context.getContextDescriptor();
assert contextDescriptor instanceof ConstructorDescriptor :
"Constructor descriptor expected: " + contextDescriptor;
ConstructorDescriptor constructorDescriptor = (ConstructorDescriptor) contextDescriptor;
DeclarationDescriptor descriptor = context.getContextDescriptor();
while (descriptor != null) {
if (descriptor == enumEntryDescriptor) return true;
ClassDescriptor classDescriptor = constructorDescriptor.getConstructedClass();
if (!classDescriptor.isInner()) return false;
if (descriptor instanceof ClassDescriptor &&
!(((ClassDescriptor) descriptor).isInner() || DescriptorUtils.isAnonymousObject(descriptor))) {
return false;
}
while (classDescriptor != null && classDescriptor.isInner()) {
classDescriptor = DescriptorUtils.getContainingClass(classDescriptor);
descriptor = descriptor.getContainingDeclaration();
}
return classDescriptor == calleeContainingClass;
return false;
}
@NotNull
@@ -2560,7 +2566,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return StackValue.local(0, typeMapper.mapType(calleeContainingClass));
}
else if (isEnumEntry(calleeContainingClass)) {
if (!isInInnerClassConstructorContext(calleeContainingClass)) {
if (!isInsideEnumEntry(calleeContainingClass)) {
return StackValue.enumEntry(calleeContainingClass, typeMapper);
}
// else fall-through
@@ -2570,6 +2576,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
}
return generateThisOrOuterFromContext(calleeContainingClass, isSuper, forceOuter);
}
private StackValue generateThisOrOuterFromContext(@NotNull ClassDescriptor calleeContainingClass, boolean isSuper, boolean forceOuter) {
CodegenContext cur = context;
Type type = asmType(calleeContainingClass.getDefaultType());
StackValue result = StackValue.local(0, type);