Give unique names to fields for captured local functions

When a local function is captured, corresponding field accesses are
later transformed by the inliner. It doesn't have enough information to
restore the original semantics completely, so it has to rely on field
names. Local functions can be overloaded or can have names matching
local variable names, in both cases we generated fields with the same
name for captured values.

Now, we use the same '$<local-class-number>' suffix for field names for
local functions as it is present in the corresponding local class name.
This allows to distinguish captured local functions from captured local
variables and between different overloads of a function with the same
name.

 #KT-19827 Fixed
 #KT-18639 Fixed
This commit is contained in:
Dmitry Petrov
2017-10-11 14:07:34 +03:00
parent da99a100cc
commit 7a156e4407
12 changed files with 289 additions and 62 deletions
@@ -106,14 +106,21 @@ public interface LocalLookup {
BindingContext bindingContext = state.getBindingContext();
Type localType = asmTypeForAnonymousClass(bindingContext, vd);
MutableClosure localFunClosure = bindingContext.get(CLOSURE, bindingContext.get(CLASS_FOR_CALLABLE, vd));
ClassDescriptor callableClass = bindingContext.get(CLASS_FOR_CALLABLE, vd);
assert callableClass != null : "No CLASS_FOR_CALLABLE:" + vd;
MutableClosure localFunClosure = bindingContext.get(CLOSURE, callableClass);
if (localFunClosure != null && JvmCodegenUtil.isConst(localFunClosure)) {
// This is an optimization: we can obtain an instance of a const closure simply by GETSTATIC ...$instance
// (instead of passing this instance to the constructor and storing as a field)
return StackValue.field(localType, localType, JvmAbi.INSTANCE_FIELD, true, StackValue.LOCAL_0, vd);
}
String fieldName = "$" + vd.getName();
String localFunClassName = callableClass.getName().asString();
int localClassIndexStart = localFunClassName.lastIndexOf('$');
String localFunSuffix = localClassIndexStart >= 0 ? localFunClassName.substring(localClassIndexStart) : "";
String fieldName = "$" + vd.getName() + localFunSuffix;
StackValue.StackValueWithSimpleReceiver innerValue = StackValue.field(localType, classType, fieldName, false,
StackValue.LOCAL_0, vd);
@@ -0,0 +1,19 @@
fun box(): String {
var s = ""
var foo = "O"
fun foo(x: String) {
s += x
}
fun foo() {
foo("K")
}
run {
foo(foo) // 1st foo is a local fun, second is a captured local var
foo()
}
return s
}
@@ -0,0 +1,8 @@
fun box(): String {
fun foo(x: String) = x
fun foo() = foo("K")
return run {
foo("O") + foo()
}
}
@@ -0,0 +1,18 @@
fun box(): String {
var s = ""
fun foo(x: String) {
s += x
}
fun foo() {
foo("K")
}
run {
foo("O")
foo()
}
return s
}
@@ -0,0 +1,23 @@
fun box(): String {
var s = ""
var foo = "K"
fun foo(x: String, y: Int) {
s += x
}
fun test() {
fun foo(x: String) {
s += x
}
run {
foo("O")
foo(foo, 1)
}
}
test()
return s
}
@@ -0,0 +1,23 @@
fun box(): String {
var s = ""
var foo = "K"
fun foo(x: String, y: Int) {
s += x
}
fun test() {
fun foo(x: String) {
s += x
}
{
foo("O")
foo(foo, 1)
}()
}
test()
return s
}
@@ -0,0 +1,21 @@
fun box(): String {
var s = ""
var foo = "O"
fun foo(x: String, z: Int) {
s += x
}
run {
fun foo(x: String) {
s += x
}
{
foo(foo, 1)
foo("K")
} ()
}
return s
}
@@ -9336,6 +9336,48 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/localFunctionInConstructor.kt");
doTest(fileName);
}
@TestMetadata("localFunctionVsLocalVariable.kt")
public void testLocalFunctionVsLocalVariable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/localFunctionVsLocalVariable.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunWithoutClosure.kt")
public void testOverloadedLocalFunWithoutClosure() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunWithoutClosure.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction.kt")
public void testOverloadedLocalFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction1.kt")
public void testOverloadedLocalFunction1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction1.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction2.kt")
public void testOverloadedLocalFunction2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction2.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction3.kt")
public void testOverloadedLocalFunction3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction3.kt");
doTest(fileName);
}
@TestMetadata("parameterAsDefaultValue.kt")
public void testParameterAsDefaultValue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/parameterAsDefaultValue.kt");
doTest(fileName);
}
}
}
@@ -11283,21 +11325,6 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
}
}
@TestMetadata("compiler/testData/codegen/box/localFunctions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class LocalFunctions extends AbstractIrBlackBoxCodegenTest {
public void testAllFilesPresentInLocalFunctions() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/localFunctions"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("parameterAsDefaultValue.kt")
public void testParameterAsDefaultValue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/localFunctions/parameterAsDefaultValue.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/mangling")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -9336,6 +9336,48 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/localFunctionInConstructor.kt");
doTest(fileName);
}
@TestMetadata("localFunctionVsLocalVariable.kt")
public void testLocalFunctionVsLocalVariable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/localFunctionVsLocalVariable.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunWithoutClosure.kt")
public void testOverloadedLocalFunWithoutClosure() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunWithoutClosure.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction.kt")
public void testOverloadedLocalFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction1.kt")
public void testOverloadedLocalFunction1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction1.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction2.kt")
public void testOverloadedLocalFunction2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction2.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction3.kt")
public void testOverloadedLocalFunction3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction3.kt");
doTest(fileName);
}
@TestMetadata("parameterAsDefaultValue.kt")
public void testParameterAsDefaultValue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/parameterAsDefaultValue.kt");
doTest(fileName);
}
}
}
@@ -11283,21 +11325,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@TestMetadata("compiler/testData/codegen/box/localFunctions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class LocalFunctions extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInLocalFunctions() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/localFunctions"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("parameterAsDefaultValue.kt")
public void testParameterAsDefaultValue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/localFunctions/parameterAsDefaultValue.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/mangling")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -9336,6 +9336,48 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/localFunctionInConstructor.kt");
doTest(fileName);
}
@TestMetadata("localFunctionVsLocalVariable.kt")
public void testLocalFunctionVsLocalVariable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/localFunctionVsLocalVariable.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunWithoutClosure.kt")
public void testOverloadedLocalFunWithoutClosure() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunWithoutClosure.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction.kt")
public void testOverloadedLocalFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction1.kt")
public void testOverloadedLocalFunction1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction1.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction2.kt")
public void testOverloadedLocalFunction2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction2.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction3.kt")
public void testOverloadedLocalFunction3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction3.kt");
doTest(fileName);
}
@TestMetadata("parameterAsDefaultValue.kt")
public void testParameterAsDefaultValue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/parameterAsDefaultValue.kt");
doTest(fileName);
}
}
}
@@ -11283,21 +11325,6 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
}
}
@TestMetadata("compiler/testData/codegen/box/localFunctions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class LocalFunctions extends AbstractLightAnalysisModeTest {
public void testAllFilesPresentInLocalFunctions() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/localFunctions"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("parameterAsDefaultValue.kt")
public void testParameterAsDefaultValue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/localFunctions/parameterAsDefaultValue.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/mangling")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -10188,6 +10188,48 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/localFunctionInConstructor.kt");
doTest(fileName);
}
@TestMetadata("localFunctionVsLocalVariable.kt")
public void testLocalFunctionVsLocalVariable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/localFunctionVsLocalVariable.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunWithoutClosure.kt")
public void testOverloadedLocalFunWithoutClosure() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunWithoutClosure.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction.kt")
public void testOverloadedLocalFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction1.kt")
public void testOverloadedLocalFunction1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction1.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction2.kt")
public void testOverloadedLocalFunction2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction2.kt");
doTest(fileName);
}
@TestMetadata("overloadedLocalFunction3.kt")
public void testOverloadedLocalFunction3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/overloadedLocalFunction3.kt");
doTest(fileName);
}
@TestMetadata("parameterAsDefaultValue.kt")
public void testParameterAsDefaultValue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/localFunctions/parameterAsDefaultValue.kt");
doTest(fileName);
}
}
}
@@ -12483,21 +12525,6 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/localFunctions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class LocalFunctions extends AbstractJsCodegenBoxTest {
public void testAllFilesPresentInLocalFunctions() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/localFunctions"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("parameterAsDefaultValue.kt")
public void testParameterAsDefaultValue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/localFunctions/parameterAsDefaultValue.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/mangling")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)