diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 9843e033fa4..d57c43acca4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -58,6 +58,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; +import org.jetbrains.kotlin.resolve.scopes.receivers.*; import org.jetbrains.kotlin.serialization.*; import org.jetbrains.kotlin.serialization.deserialization.NameResolver; import org.jetbrains.kotlin.serialization.jvm.BitEncoding; @@ -75,6 +76,7 @@ import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass; import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration; import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; +import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getResolvedCall; import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getBuiltIns; import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getSecondaryConstructors; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*; @@ -1281,12 +1283,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { public void visitSimpleNameExpression(@NotNull JetSimpleNameExpression expr) { DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expr); - DeclarationDescriptor toLookup; if (isLocalFunction(descriptor)) { - toLookup = descriptor; + lookupInContext(descriptor); } else if (descriptor instanceof CallableMemberDescriptor) { - toLookup = descriptor.getContainingDeclaration(); + ResolvedCall call = getResolvedCall(expr, bindingContext); + if (call != null) { + lookupReceiver(call.getDispatchReceiver()); + lookupReceiver(call.getExtensionReceiver()); + } } else if (descriptor instanceof VariableDescriptor) { if (descriptor.getContainingDeclaration() instanceof ConstructorDescriptor) { @@ -1294,10 +1299,26 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { (ClassDescriptor) descriptor.getContainingDeclaration().getContainingDeclaration(); if (classDescriptor == ImplementationBodyCodegen.this.descriptor) return; } - toLookup = descriptor; + lookupInContext(descriptor); } - else return; + } + private void lookupReceiver(@NotNull ReceiverValue value) { + if (value instanceof ThisReceiver) { + if (value instanceof ExtensionReceiver) { + ReceiverParameterDescriptor parameter = + ((ExtensionReceiver) value).getDeclarationDescriptor().getExtensionReceiverParameter(); + assert parameter != null : "Extension receiver should exist: " + ((ExtensionReceiver) value).getDeclarationDescriptor(); + lookupInContext(parameter); + } + else { + lookupInContext(((ThisReceiver) value).getDeclarationDescriptor()); + } + } + } + + + private void lookupInContext(@NotNull DeclarationDescriptor toLookup) { context.lookupInContext(toLookup, StackValue.LOCAL_0, state, true); } @@ -1307,13 +1328,13 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { assert descriptor instanceof CallableDescriptor || descriptor instanceof ClassDescriptor : "'This' reference target should be class or callable descriptor but was " + descriptor; if (descriptor instanceof ClassDescriptor) { - context.lookupInContext(descriptor, StackValue.LOCAL_0, state, true); + lookupInContext(descriptor); } if (descriptor instanceof CallableDescriptor) { ReceiverParameterDescriptor parameter = ((CallableDescriptor) descriptor).getExtensionReceiverParameter(); if (parameter != null) { - context.lookupInContext(parameter, StackValue.LOCAL_0, state, true); + lookupInContext(parameter); } } } diff --git a/compiler/testData/codegen/box/closures/captureExtensionReceiver.kt b/compiler/testData/codegen/box/closures/captureExtensionReceiver.kt new file mode 100644 index 00000000000..7018c749d54 --- /dev/null +++ b/compiler/testData/codegen/box/closures/captureExtensionReceiver.kt @@ -0,0 +1,40 @@ +interface B { + val bar: T +} + +fun String.foo() = object : B { + override val bar: String = length().toString() +} + +class C { + + fun String.extension() = this.length() + + fun String.fooInClass() = object : B { + override val bar: String = extension().toString() + } + + fun String.fooInClassNoReceiver() = object : B { + override val bar: String = "123".extension().toString() + } + + fun fooInClass(s: String) = s.fooInClass().bar + + fun fooInClassNoReceiver(s: String) = s.fooInClassNoReceiver().bar +} + +fun box(): String { + var result = "Hello, world!".foo().bar + if (result != "13") return "fail 1: $result" + + result = C().fooInClass("Hello, world!") + + if (result != "13") return "fail 2: $result" + + result = C().fooInClassNoReceiver("Hello, world!") + + if (result != "3") return "fail 3: $result" + + return "OK" +} + diff --git a/compiler/testData/codegen/bytecodeText/kt7769.kt b/compiler/testData/codegen/bytecodeText/kt7769.kt new file mode 100644 index 00000000000..9e1f161a0a7 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/kt7769.kt @@ -0,0 +1,22 @@ +interface B { + val bar: T +} + +class S(val value: String) { + + fun bar() = value + + fun foo(): B { + val p = S("OK"); + return object : B { + //we shouldn't capture this@S in such case + override val bar: String = p.bar() + } + } +} + +fun box(): String { + return S("fail").foo().bar +} + +// 0 this$0 \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 0d69120b512..770a07fb593 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -161,6 +161,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } + @TestMetadata("kt7769.kt") + public void testKt7769() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/kt7769.kt"); + doTest(fileName); + } + @TestMetadata("noFlagAnnotations.kt") public void testNoFlagAnnotations() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/noFlagAnnotations.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index 820c881fb38..b4510fcfec8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -1537,6 +1537,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/closures"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("captureExtensionReceiver.kt") + public void testCaptureExtensionReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/captureExtensionReceiver.kt"); + doTest(fileName); + } + @TestMetadata("capturedLocalGenericFun.kt") public void testCapturedLocalGenericFun() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedLocalGenericFun.kt");