KT-5347 VerifyError on local data class with closure
- generated copy() should push captured fields on stack - refactor context lookup - handle vars in local classes using instance fields #KT-5347 Fixed
This commit is contained in:
@@ -1528,7 +1528,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
if (sharedVarType == null) {
|
||||
sharedVarType = typeMapper.mapType((VariableDescriptor) entry.getKey());
|
||||
}
|
||||
StackValue capturedVar = entry.getValue().getOuterValue(this);
|
||||
StackValue capturedVar = lookupOuterValue(entry.getValue());
|
||||
callGenerator.putCapturedValueOnStack(capturedVar, sharedVarType, paramIndex++);
|
||||
}
|
||||
|
||||
@@ -1551,6 +1551,17 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
return generateBlock(expression.getStatements(), isStatement, null, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public StackValue lookupOuterValue(EnclosedValueDescriptor d) {
|
||||
DeclarationDescriptor descriptor = d.getDescriptor();
|
||||
for (LocalLookup.LocalLookupCase aCase : LocalLookup.LocalLookupCase.values()) {
|
||||
if (aCase.isCase(descriptor)) {
|
||||
return aCase.outerValue(d, this);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Can't get outer value in " + this + " for " + d);
|
||||
}
|
||||
|
||||
private StackValue generateBlock(
|
||||
List<JetExpression> statements,
|
||||
boolean isStatement,
|
||||
|
||||
@@ -727,12 +727,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
|
||||
MutableClosure closure = ImplementationBodyCodegen.this.context.closure;
|
||||
if (closure != null) {
|
||||
ClassDescriptor captureThis = closure.getCaptureThis();
|
||||
if (captureThis != null) {
|
||||
iv.load(0, classAsmType);
|
||||
Type type = typeMapper.mapType(captureThis);
|
||||
iv.getfield(classAsmType.getInternalName(), CAPTURED_THIS_FIELD, type.getDescriptor());
|
||||
}
|
||||
pushCapturedFieldsOnStack(iv, closure);
|
||||
}
|
||||
|
||||
int parameterIndex = 1; // localVariable 0 = this
|
||||
@@ -747,6 +742,33 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
|
||||
iv.areturn(thisDescriptorType);
|
||||
}
|
||||
|
||||
private void pushCapturedFieldsOnStack(InstructionAdapter iv, MutableClosure closure) {
|
||||
ClassDescriptor captureThis = closure.getCaptureThis();
|
||||
if (captureThis != null) {
|
||||
iv.load(0, classAsmType);
|
||||
Type type = typeMapper.mapType(captureThis);
|
||||
iv.getfield(classAsmType.getInternalName(), CAPTURED_THIS_FIELD, type.getDescriptor());
|
||||
}
|
||||
|
||||
JetType captureReceiver = closure.getCaptureReceiverType();
|
||||
if (captureReceiver != null) {
|
||||
iv.load(0, classAsmType);
|
||||
Type type = typeMapper.mapType(captureReceiver);
|
||||
iv.getfield(classAsmType.getInternalName(), CAPTURED_RECEIVER_FIELD, type.getDescriptor());
|
||||
}
|
||||
|
||||
for (Map.Entry<DeclarationDescriptor, EnclosedValueDescriptor> entry : closure.getCaptureVariables().entrySet()) {
|
||||
DeclarationDescriptor declarationDescriptor = entry.getKey();
|
||||
EnclosedValueDescriptor enclosedValueDescriptor = entry.getValue();
|
||||
StackValue capturedValue = enclosedValueDescriptor.getInstanceValue();
|
||||
Type sharedVarType = typeMapper.getSharedVarType(declarationDescriptor);
|
||||
if (sharedVarType == null) {
|
||||
sharedVarType = typeMapper.mapType((VariableDescriptor) declarationDescriptor);
|
||||
}
|
||||
capturedValue.put(sharedVarType, iv);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
functionCodegen.generateDefaultIfNeeded(
|
||||
|
||||
@@ -412,15 +412,23 @@ public abstract class StackValue {
|
||||
return None.INSTANCE;
|
||||
}
|
||||
|
||||
public static FieldForSharedVar fieldForSharedVar(
|
||||
public static Field receiverWithRefWrapper(
|
||||
@NotNull Type localType,
|
||||
@NotNull Type classType,
|
||||
@NotNull String fieldName,
|
||||
@NotNull StackValue receiver,
|
||||
@Nullable DeclarationDescriptor descriptor
|
||||
) {
|
||||
Field receiverWithRefWrapper = field(sharedTypeForType(localType), classType, fieldName, false, receiver, descriptor);
|
||||
return new FieldForSharedVar(localType, classType, fieldName, receiverWithRefWrapper);
|
||||
return field(sharedTypeForType(localType), classType, fieldName, false, receiver, descriptor);
|
||||
}
|
||||
|
||||
public static FieldForSharedVar fieldForSharedVar(
|
||||
@NotNull Type localType,
|
||||
@NotNull Type classType,
|
||||
@NotNull String fieldName,
|
||||
@NotNull Field refWrapper
|
||||
) {
|
||||
return new FieldForSharedVar(localType, classType, fieldName, refWrapper);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+20
-10
@@ -27,6 +27,7 @@ public final class EnclosedValueDescriptor {
|
||||
private final String fieldName;
|
||||
private final DeclarationDescriptor descriptor;
|
||||
private final StackValue.StackValueWithSimpleReceiver innerValue;
|
||||
private final StackValue instanceValue;
|
||||
private final Type type;
|
||||
|
||||
public EnclosedValueDescriptor(
|
||||
@@ -38,6 +39,21 @@ public final class EnclosedValueDescriptor {
|
||||
this.fieldName = fieldName;
|
||||
this.descriptor = descriptor;
|
||||
this.innerValue = innerValue;
|
||||
this.instanceValue = innerValue;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public EnclosedValueDescriptor(
|
||||
@NotNull String name,
|
||||
@Nullable DeclarationDescriptor descriptor,
|
||||
@NotNull StackValue.StackValueWithSimpleReceiver innerValue,
|
||||
@NotNull StackValue.Field instanceValue,
|
||||
@NotNull Type type
|
||||
) {
|
||||
this.fieldName = name;
|
||||
this.descriptor = descriptor;
|
||||
this.innerValue = innerValue;
|
||||
this.instanceValue = instanceValue;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@@ -57,19 +73,13 @@ public final class EnclosedValueDescriptor {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Type getType() {
|
||||
return type;
|
||||
public StackValue getInstanceValue() {
|
||||
return instanceValue;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public StackValue getOuterValue(@NotNull ExpressionCodegen codegen) {
|
||||
for (LocalLookup.LocalLookupCase aCase : LocalLookup.LocalLookupCase.values()) {
|
||||
if (aCase.isCase(descriptor)) {
|
||||
return aCase.outerValue(this, codegen);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Can't get outer value in " + codegen + " for " + this);
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -61,12 +61,21 @@ public interface LocalLookup {
|
||||
|
||||
String fieldName = "$" + vd.getName();
|
||||
StackValue.Local thiz = StackValue.LOCAL_0;
|
||||
StackValue.StackValueWithSimpleReceiver innerValue = sharedVarType != null
|
||||
? StackValue.fieldForSharedVar(localType, classType, fieldName, thiz, vd)
|
||||
: StackValue.field(type, classType, fieldName, false, thiz, vd);
|
||||
|
||||
StackValue.StackValueWithSimpleReceiver innerValue;
|
||||
EnclosedValueDescriptor enclosedValueDescriptor;
|
||||
if (sharedVarType != null) {
|
||||
StackValue.Field wrapperValue = StackValue.receiverWithRefWrapper(localType, classType, fieldName, thiz, vd);
|
||||
innerValue = StackValue.fieldForSharedVar(localType, classType, fieldName, wrapperValue);
|
||||
enclosedValueDescriptor = new EnclosedValueDescriptor(fieldName, d, innerValue, wrapperValue, type);
|
||||
}
|
||||
else {
|
||||
innerValue = StackValue.field(type, classType, fieldName, false, thiz, vd);
|
||||
enclosedValueDescriptor = new EnclosedValueDescriptor(fieldName, d, innerValue, type);
|
||||
}
|
||||
|
||||
closure.recordField(fieldName, type);
|
||||
closure.captureVariable(new EnclosedValueDescriptor(fieldName, d, innerValue, type));
|
||||
closure.captureVariable(enclosedValueDescriptor);
|
||||
|
||||
return innerValue;
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
fun test1(str: String): String {
|
||||
@data class A(val x: Int) {
|
||||
fun foo() = str
|
||||
}
|
||||
return A(0).copy().foo()
|
||||
}
|
||||
|
||||
class TestClass(val x: String) {
|
||||
fun foo(): String {
|
||||
@data class A(val x: Int) {
|
||||
fun foo() = this@TestClass.x
|
||||
}
|
||||
return A(0).copy().foo()
|
||||
}
|
||||
}
|
||||
|
||||
fun test2(str: String): String = TestClass(str).foo()
|
||||
|
||||
fun test3(str: String): String {
|
||||
var xx = ""
|
||||
@data class A(val x: Int) {
|
||||
fun foo(): String { xx = str; return xx }
|
||||
}
|
||||
return A(0).copy().foo()
|
||||
}
|
||||
|
||||
fun test4(str: String): String {
|
||||
var xx = ""
|
||||
fun bar(s: String): String { xx = s; return xx }
|
||||
@data class A(val x: Int) {
|
||||
fun foo(): String = bar(str)
|
||||
}
|
||||
return A(0).copy().foo()
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
return when {
|
||||
test1("test1") != "test1" -> "Failed #1 (parameter capture)"
|
||||
test2("test2") != "test2" -> "Failed #2 ('this' capture)"
|
||||
test3("test3") != "test3" -> "Failed #3 ('var' capture)"
|
||||
test4("test4") != "test4" -> "Failed #4 (local function capture)"
|
||||
else -> "OK"
|
||||
}
|
||||
}
|
||||
+6
@@ -1378,6 +1378,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt5347.kt")
|
||||
public void testKt5347() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/kt5347.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt6136.kt")
|
||||
public void testKt6136() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/kt6136.kt");
|
||||
|
||||
Reference in New Issue
Block a user