diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 4ac21a44409..be58c966a39 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -15975,6 +15975,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT public void testSimple() throws Exception { runTest("compiler/testData/codegen/box/extensionClasses/simple.kt"); } + + @Test + @TestMetadata("useFromAnotherModule.kt") + public void testUseFromAnotherModule() throws Exception { + runTest("compiler/testData/codegen/box/extensionClasses/useFromAnotherModule.kt"); + } } @Nested @@ -16217,6 +16223,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/extensionFunctions/contextReceivers/this.kt"); } + @Test + @TestMetadata("useFromAnotherModule.kt") + public void testUseFromAnotherModule() throws Exception { + runTest("compiler/testData/codegen/box/extensionFunctions/contextReceivers/useFromAnotherModule.kt"); + } + @Nested @TestMetadata("compiler/testData/codegen/box/extensionFunctions/contextReceivers/fromKEEP") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyFunctionBase.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyFunctionBase.kt index 9ff50fe267e..1a37266aaeb 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyFunctionBase.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyFunctionBase.kt @@ -8,11 +8,13 @@ package org.jetbrains.kotlin.ir.declarations.lazy import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer import org.jetbrains.kotlin.ir.declarations.IrValueParameter +import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl import org.jetbrains.kotlin.ir.types.IrType -import kotlin.properties.ReadWriteProperty +import org.jetbrains.kotlin.name.Name interface IrLazyFunctionBase : IrLazyDeclarationBase, IrTypeParametersContainer { @OptIn(ObsoleteDescriptorBasedAPI::class) @@ -28,8 +30,17 @@ interface IrLazyFunctionBase : IrLazyDeclarationBase, IrTypeParametersContainer fun createValueParameters(): List = typeTranslator.buildWithScope(this) { - descriptor.valueParameters.mapTo(arrayListOf()) { - stubGenerator.generateValueParameterStub(it).apply { parent = this@IrLazyFunctionBase } + val result = arrayListOf() + descriptor.contextReceiverParameters.mapIndexedTo(result) { i, contextReceiverParameter -> + factory.createValueParameter( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, origin, IrValueParameterSymbolImpl(contextReceiverParameter), + Name.identifier("contextReceiverParameter$i"), i, contextReceiverParameter.type.toIrType(), + null, isCrossinline = false, isNoinline = false, isHidden = false, isAssignable = false + ).apply { parent = this@IrLazyFunctionBase } + } + descriptor.valueParameters.mapTo(result) { + stubGenerator.generateValueParameterStub(it, it.index + descriptor.contextReceiverParameters.size) + .apply { parent = this@IrLazyFunctionBase } } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt index b078e7dc08d..2a8f5bab927 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt @@ -234,7 +234,7 @@ abstract class DeclarationStubGenerator( private fun KotlinType.toIrType() = typeTranslator.translateType(this) - internal fun generateValueParameterStub(descriptor: ValueParameterDescriptor): IrValueParameter = with(descriptor) { + internal fun generateValueParameterStub(descriptor: ValueParameterDescriptor, index: Int): IrValueParameter = with(descriptor) { IrLazyValueParameter(UNDEFINED_OFFSET, UNDEFINED_OFFSET, computeOrigin(this), IrValueParameterSymbolImpl(this), this, name, index, type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline, isHidden = false, isAssignable = false, this@DeclarationStubGenerator, typeTranslator) .also { irValueParameter -> diff --git a/compiler/testData/codegen/box/extensionClasses/useFromAnotherModule.kt b/compiler/testData/codegen/box/extensionClasses/useFromAnotherModule.kt new file mode 100644 index 00000000000..e672d85e874 --- /dev/null +++ b/compiler/testData/codegen/box/extensionClasses/useFromAnotherModule.kt @@ -0,0 +1,25 @@ +// !LANGUAGE: +ContextReceivers +// TARGET_BACKEND: JVM_IR +// IGNORE_BACKEND_FIR: JVM_IR + +// MODULE: lib +// FILE: A.kt + +package a + +class O(val o: String) + +context(O) +class OK(val k: String) { + val result = o + k +} + +// MODULE: main(lib) +// FILE: B.kt + +fun box(): String { + return with(a.O("O")) { + val ok = a.OK("K") + ok.result + } +} diff --git a/compiler/testData/codegen/box/extensionFunctions/contextReceivers/useFromAnotherModule.kt b/compiler/testData/codegen/box/extensionFunctions/contextReceivers/useFromAnotherModule.kt new file mode 100644 index 00000000000..ffe4ed54847 --- /dev/null +++ b/compiler/testData/codegen/box/extensionFunctions/contextReceivers/useFromAnotherModule.kt @@ -0,0 +1,31 @@ +// !LANGUAGE: +ContextReceivers +// TARGET_BACKEND: JVM_IR +// IGNORE_BACKEND_FIR: JVM_IR + +// MODULE: lib +// FILE: A.kt + +package a + +context(Int) +fun one(dummy: Any?) = this@Int + +context(Int) +val two get() = this@Int + +class Foo { + context(Int) + val three get() = this@Int + + context(Int) + fun four(dummy: Any?) = this@Int +} + +// MODULE: main(lib) +// FILE: B.kt + +fun box(): String { + return with(1) { + if (a.one(null) + a.two + a.Foo().three + a.Foo().four(null) == 4) "OK" else "fail" + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index b80f728086f..409be51320d 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -15975,6 +15975,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes public void testSimple() throws Exception { runTest("compiler/testData/codegen/box/extensionClasses/simple.kt"); } + + @Test + @TestMetadata("useFromAnotherModule.kt") + public void testUseFromAnotherModule() throws Exception { + runTest("compiler/testData/codegen/box/extensionClasses/useFromAnotherModule.kt"); + } } @Nested @@ -16217,6 +16223,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/extensionFunctions/contextReceivers/this.kt"); } + @Test + @TestMetadata("useFromAnotherModule.kt") + public void testUseFromAnotherModule() throws Exception { + runTest("compiler/testData/codegen/box/extensionFunctions/contextReceivers/useFromAnotherModule.kt"); + } + @Nested @TestMetadata("compiler/testData/codegen/box/extensionFunctions/contextReceivers/fromKEEP") @TestDataPath("$PROJECT_ROOT") diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt index 6bff82234f2..1d021df57b2 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt @@ -60,7 +60,7 @@ class MemberDeserializer(private val c: DeserializationContext) { proto.receiverType(c.typeTable)?.let(local.typeDeserializer::type)?.let { receiverType -> DescriptorFactory.createExtensionReceiverParameterForCallable(property, receiverType, receiverAnnotations) }, - emptyList() + proto.contextReceiverTypeList.map { it.toContextReceiver(local, property) } ) // Per documentation on Property.getter_flags in metadata.proto, if an accessor flags field is absent, its value should be computed @@ -152,6 +152,7 @@ class MemberDeserializer(private val c: DeserializationContext) { private fun DeserializedSimpleFunctionDescriptor.initializeWithCoroutinesExperimentalityStatus( extensionReceiverParameter: ReceiverParameterDescriptor?, dispatchReceiverParameter: ReceiverParameterDescriptor?, + contextReceiverParameters: List, typeParameters: List, unsubstitutedValueParameters: List, unsubstitutedReturnType: KotlinType?, @@ -162,7 +163,7 @@ class MemberDeserializer(private val c: DeserializationContext) { initialize( extensionReceiverParameter, dispatchReceiverParameter, - emptyList(), // TODO + contextReceiverParameters, typeParameters, unsubstitutedValueParameters, unsubstitutedReturnType, @@ -202,6 +203,7 @@ class MemberDeserializer(private val c: DeserializationContext) { DescriptorFactory.createExtensionReceiverParameterForCallable(function, receiverType, receiverAnnotations) }, getDispatchReceiverParameter(), + proto.contextReceiverTypeList.mapNotNull { it.toContextReceiver(local, function) }, local.typeDeserializer.ownTypeParameters, local.memberDeserializer.valueParameters(proto.valueParameterList, proto, AnnotatedCallableKind.FUNCTION), local.typeDeserializer.type(proto.returnType(c.typeTable)), @@ -341,4 +343,12 @@ class MemberDeserializer(private val c: DeserializationContext) { is DeserializedClassDescriptor -> thisAsProtoContainer else -> null // TODO: support annotations on lambdas and their parameters } + + private fun ProtoBuf.Type.toContextReceiver( + deserializationContext: DeserializationContext, + callableDescriptor: CallableDescriptor + ): ReceiverParameterDescriptor? { + val contextReceiverType = deserializationContext.typeDeserializer.type(this) + return DescriptorFactory.createContextReceiverParameterForCallable(callableDescriptor, contextReceiverType, Annotations.EMPTY) + } } diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt index 7969c27c3cc..25fbc0b92ce 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.AbstractClassDescriptor import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor +import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.record @@ -20,6 +21,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.StaticScopeForKotlinEnum +import org.jetbrains.kotlin.resolve.scopes.receivers.ContextClassReceiver import org.jetbrains.kotlin.serialization.deserialization.* import org.jetbrains.kotlin.types.AbstractClassTypeConstructor import org.jetbrains.kotlin.types.KotlinType @@ -140,6 +142,15 @@ class DeserializedClassDescriptor( override fun getConstructors() = constructors() + override fun getContextReceivers(): List = classProto.contextReceiverTypeList.map { + val contextReceiverType = c.typeDeserializer.type(it) + ReceiverParameterDescriptorImpl( + thisAsReceiverParameter, + ContextClassReceiver(this, contextReceiverType, null), + Annotations.EMPTY + ); + } + private fun computeCompanionObjectDescriptor(): ClassDescriptor? { if (!classProto.hasCompanionObjectName()) return null