diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java index 0892f70a311..f6a02b0f66c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java @@ -438,13 +438,25 @@ public class ClosureCodegen extends MemberCodegen { mv.visitCode(); InstructionAdapter iv = new InstructionAdapter(mv); - Pair receiverIndexAndType = + Pair receiverIndexAndFieldInfo = CallableReferenceUtilKt.generateClosureFieldsInitializationFromParameters(iv, closure, args); - if (shouldHaveBoundReferenceReceiver && receiverIndexAndType == null) { + if (shouldHaveBoundReferenceReceiver && receiverIndexAndFieldInfo == null) { throw new AssertionError("No bound reference receiver in constructor parameters: " + args); } - int boundReferenceReceiverParameterIndex = shouldHaveBoundReferenceReceiver ? receiverIndexAndType.getFirst() : -1; - Type boundReferenceReceiverType = shouldHaveBoundReferenceReceiver ? receiverIndexAndType.getSecond() : null; + + int boundReceiverParameterIndex; + Type boundReceiverType; + KotlinType boundReceiverKotlinType; + if (shouldHaveBoundReferenceReceiver) { + boundReceiverParameterIndex = receiverIndexAndFieldInfo.getFirst(); + boundReceiverType = receiverIndexAndFieldInfo.getSecond().getFieldType(); + boundReceiverKotlinType = receiverIndexAndFieldInfo.getSecond().getFieldKotlinType(); + } + else { + boundReceiverParameterIndex = -1; + boundReceiverType = null; + boundReceiverKotlinType = null; + } iv.load(0, superClassAsmType); @@ -454,7 +466,7 @@ public class ClosureCodegen extends MemberCodegen { int arity = calculateArity(); iv.iconst(arity); if (shouldHaveBoundReferenceReceiver) { - CallableReferenceUtilKt.loadBoundReferenceReceiverParameter(iv, boundReferenceReceiverParameterIndex, boundReferenceReceiverType); + CallableReferenceUtilKt.loadBoundReferenceReceiverParameter(iv, boundReceiverParameterIndex, boundReceiverType, boundReceiverKotlinType); superClassConstructorDescriptor = "(ILjava/lang/Object;)V"; } else { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 789943b95e4..06a95752a93 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -3053,7 +3053,9 @@ public class ExpressionCodegen extends KtVisitor impleme FunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression); if (functionDescriptor != null) { FunctionReferenceGenerationStrategy strategy = new FunctionReferenceGenerationStrategy( - state, functionDescriptor, resolvedCall, receiver != null ? receiver.type : null, null, false + state, functionDescriptor, resolvedCall, + receiver != null ? new JvmKotlinType(receiver.type, receiver.kotlinType) : null, + null, false ); return genClosure( @@ -3092,10 +3094,10 @@ public class ExpressionCodegen extends KtVisitor impleme element.getContainingFile() ); - Type receiverAsmType = receiverValue != null ? receiverValue.type : null; PropertyReferenceCodegen codegen = new PropertyReferenceCodegen( state, parentCodegen, context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION), - element, classBuilder, variableDescriptor, target, receiverAsmType + element, classBuilder, variableDescriptor, target, + receiverValue != null ? new JvmKotlinType(receiverValue.type, receiverValue.kotlinType) : null ); codegen.generate(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java index a834c3bfb31..b387360b728 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.resolve.calls.util.CallMaker; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; +import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; @@ -51,6 +52,7 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat private final FunctionDescriptor referencedFunction; private final FunctionDescriptor functionDescriptor; private final Type receiverType; // non-null for bound references + private final KotlinType receiverKotlinType; private final StackValue receiverValue; private final boolean isInliningStrategy; @@ -58,7 +60,7 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat @NotNull GenerationState state, @NotNull FunctionDescriptor functionDescriptor, @NotNull ResolvedCall resolvedCall, - @Nullable Type receiverType, + @Nullable JvmKotlinType receiverJvmKotlinType, @Nullable StackValue receiverValue, boolean isInliningStrategy ) { @@ -73,7 +75,8 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat this.referencedFunction = referencedFunction; this.functionDescriptor = functionDescriptor; } - this.receiverType = receiverType; + this.receiverType = receiverJvmKotlinType != null ? receiverJvmKotlinType.getType() : null; + this.receiverKotlinType = receiverJvmKotlinType != null ? receiverJvmKotlinType.getKotlinType() : null; this.receiverValue = receiverValue; this.isInliningStrategy = isInliningStrategy; assert receiverType != null || receiverValue == null @@ -240,7 +243,7 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat if (receiverType != null) { ClassDescriptor classDescriptor = (ClassDescriptor) codegen.getContext().getParentContext().getContextDescriptor(); Type asmType = codegen.getState().getTypeMapper().mapClass(classDescriptor); - return CallableReferenceUtilKt.capturedBoundReferenceReceiver(asmType, receiverType, isInliningStrategy); + return CallableReferenceUtilKt.capturedBoundReferenceReceiver(asmType, receiverType, receiverKotlinType, isInliningStrategy); } // 0 is this (the callable reference class), 1 is the invoke() method's first parameter diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt index 63e46cd5ebe..1783148b999 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt @@ -31,7 +31,9 @@ import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.resolve.DescriptorFactory import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.PropertyImportedFromObject +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny +import org.jetbrains.kotlin.resolve.isUnderlyingPropertyOfInlineClass import org.jetbrains.kotlin.resolve.jvm.AsmTypes.* import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature @@ -52,8 +54,11 @@ class PropertyReferenceCodegen( classBuilder: ClassBuilder, private val localVariableDescriptorForReference: VariableDescriptor, private val target: VariableDescriptor, - private val receiverType: Type? + private val boundReceiverJvmKotlinType: JvmKotlinType? ) : MemberCodegen(state, parentCodegen, context, expression, classBuilder) { + + private val boundReceiverType = boundReceiverJvmKotlinType?.type + private val classDescriptor = context.contextDescriptor private val asmType = typeMapper.mapClass(classDescriptor) @@ -72,9 +77,9 @@ class PropertyReferenceCodegen( private val wrapperMethod = getWrapperMethodForPropertyReference(target, getFunction.valueParameters.size) private val closure = bindingContext.get(CodegenBinding.CLOSURE, classDescriptor)!!.apply { - assert((capturedReceiverFromOuterContext != null) == (receiverType != null)) { + assert((capturedReceiverFromOuterContext != null) == (boundReceiverType != null)) { "Bound property reference can only be generated with the type of the receiver. " + - "Captured type = $capturedReceiverFromOuterContext, actual type = $receiverType" + "Captured type = $capturedReceiverFromOuterContext, actual type = $boundReceiverType" } } @@ -129,16 +134,16 @@ class PropertyReferenceCodegen( private fun generateConstructor() { generateMethod("property reference init", 0, constructor) { val shouldHaveBoundReferenceReceiver = closure.isForBoundCallableReference() - val receiverIndexAndType = generateClosureFieldsInitializationFromParameters(closure, constructorArgs) + val receiverIndexAndFieldInfo = generateClosureFieldsInitializationFromParameters(closure, constructorArgs) - if (receiverIndexAndType == null) { + if (receiverIndexAndFieldInfo == null) { assert(!shouldHaveBoundReferenceReceiver) { "No bound reference receiver in constructor parameters: $constructorArgs" } load(0, OBJECT_TYPE) invokespecial(superAsmType.internalName, "", "()V", false) } else { - val (receiverIndex, receiverType) = receiverIndexAndType + val (receiverIndex, receiverFieldInfo) = receiverIndexAndFieldInfo load(0, OBJECT_TYPE) - loadBoundReferenceReceiverParameter(receiverIndex, receiverType) + loadBoundReferenceReceiverParameter(receiverIndex, receiverFieldInfo.fieldType, receiverFieldInfo.fieldKotlinType) invokespecial(superAsmType.internalName, "", "(Ljava/lang/Object;)V", false) } } @@ -155,7 +160,7 @@ class PropertyReferenceCodegen( getFunction, target, asmType, - receiverType, + boundReceiverJvmKotlinType, element, state, false @@ -176,7 +181,7 @@ class PropertyReferenceCodegen( setFunction, target, asmType, - receiverType, + boundReceiverJvmKotlinType, element, state, false @@ -284,19 +289,39 @@ class PropertyReferenceCodegen( } class PropertyReferenceGenerationStrategy( - val isGetter: Boolean, + private val isGetter: Boolean, private val originalFunctionDesc: FunctionDescriptor, - val target: VariableDescriptor, - val asmType: Type, - val receiverType: Type?, - val expression: KtElement, + private val target: VariableDescriptor, + private val asmType: Type, + boundReceiverJvmKotlinType: JvmKotlinType?, + private val expression: KtElement, state: GenerationState, private val isInliningStrategy: Boolean ) : FunctionGenerationStrategy.CodegenBased(state) { + + private val boundReceiverType = boundReceiverJvmKotlinType?.type + private val boundReceiverKotlinType = boundReceiverJvmKotlinType?.kotlinType + + private val expectedReceiverKotlinType = + target.extensionReceiverParameter?.type + ?: (target.containingDeclaration as? ClassDescriptor)?.defaultType + + private val expectedReceiverType = + if (expectedReceiverKotlinType != null) + state.typeMapper.mapType(expectedReceiverKotlinType) + else { + assert(boundReceiverType == null) { + "$target: no expected receiver, boundReceiverType is $boundReceiverType" + } + null + } + override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { val v = codegen.v val typeMapper = state.typeMapper + val targetKotlinType = target.type + if (target is PropertyImportedFromObject) { val containingObject = target.containingObject StackValue @@ -304,31 +329,51 @@ class PropertyReferenceCodegen( .put(typeMapper.mapClass(containingObject), containingObject.defaultType, v) } - if (receiverType != null) { - val expectedReceiver = - target.extensionReceiverParameter?.type ?: (target.containingDeclaration as? ClassDescriptor)?.defaultType - val expectedReceiverType = if (expectedReceiver != null) typeMapper.mapType(expectedReceiver) else receiverType - capturedBoundReferenceReceiver(asmType, expectedReceiverType, isInliningStrategy).put(expectedReceiverType, v) + if (boundReceiverType != null) { + capturedBoundReferenceReceiver(asmType, boundReceiverType, boundReceiverKotlinType, isInliningStrategy) + .put(expectedReceiverType!!, expectedReceiverKotlinType, v) } else { val receivers = originalFunctionDesc.valueParameters.dropLast(if (isGetter) 0 else 1) receivers.forEachIndexed { i, valueParameterDescriptor -> - StackValue.local(i + 1, OBJECT_TYPE).put(typeMapper.mapType(valueParameterDescriptor), valueParameterDescriptor.type, v) + val nullableAny = valueParameterDescriptor.builtIns.nullableAnyType + StackValue.local(i + 1, OBJECT_TYPE, nullableAny) + .put(typeMapper.mapType(valueParameterDescriptor), valueParameterDescriptor.type, v) } } - val value = if (target is LocalVariableDescriptor) { - codegen.findLocalOrCapturedValue(target)!! - } else { - codegen.intermediateValueForProperty(target as PropertyDescriptor, false, null, StackValue.none()) + val value = when { + target is LocalVariableDescriptor -> codegen.findLocalOrCapturedValue(target)!! + + target.isUnderlyingPropertyOfInlineClass() -> { + if (expectedReceiverType == null) + throw AssertionError("$target: boundReceiverType=$boundReceiverType, expectedReceiverType is null") + + val receiver = + if (boundReceiverType != null) + StackValue.onStack(expectedReceiverType, expectedReceiverKotlinType) + else + StackValue.none() + + StackValue.underlyingValueOfInlineClass(typeMapper.mapType(targetKotlinType), targetKotlinType, receiver) + } + + else -> codegen.intermediateValueForProperty(target as PropertyDescriptor, false, null, StackValue.none()) } codegen.markStartLineNumber(expression) - val type = target.type + if (isGetter) { - value.put(OBJECT_TYPE, type, v) + value.put(OBJECT_TYPE, targetKotlinType, v) } else { - val functionDescriptor = codegen.context.functionDescriptor - value.store(StackValue.local(codegen.frameMap.getIndex(functionDescriptor.valueParameters.last()), OBJECT_TYPE, type), v) + value.store( + StackValue.local( + codegen.frameMap.getIndex( + codegen.context.functionDescriptor.valueParameters.last() + ), + OBJECT_TYPE, targetKotlinType + ), + v + ) } v.areturn(signature.returnType) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt index acad038362b..647c6b2c402 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt @@ -24,10 +24,17 @@ import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.source.KotlinSourceElement +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.builtIns import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter -fun capturedBoundReferenceReceiver(ownerType: Type, expectedReceiverType: Type, isInliningStrategy: Boolean): StackValue = +fun capturedBoundReferenceReceiver( + ownerType: Type, + expectedReceiverType: Type, + expectedReceiverKotlinType: KotlinType?, + isInliningStrategy: Boolean +): StackValue = StackValue.operation(expectedReceiverType) { iv -> iv.load(0, ownerType) iv.getfield( @@ -36,7 +43,8 @@ fun capturedBoundReferenceReceiver(ownerType: Type, expectedReceiverType: Type, if (isInliningStrategy) AsmUtil.CAPTURED_RECEIVER_FIELD else AsmUtil.BOUND_REFERENCE_RECEIVER, AsmTypes.OBJECT_TYPE.descriptor ) - StackValue.coerce(AsmTypes.OBJECT_TYPE, expectedReceiverType, iv) + val nullableAny = expectedReceiverKotlinType?.run { builtIns.nullableAnyType } + StackValue.coerce(AsmTypes.OBJECT_TYPE, nullableAny, expectedReceiverType, expectedReceiverKotlinType, iv) } fun ClassDescriptor.isSyntheticClassForCallableReference(): Boolean = @@ -49,9 +57,10 @@ fun CalculatedClosure.isForCallableReference(): Boolean = fun CalculatedClosure.isForBoundCallableReference(): Boolean = isForCallableReference() && capturedReceiverFromOuterContext != null -fun InstructionAdapter.loadBoundReferenceReceiverParameter(index: Int, type: Type) { +fun InstructionAdapter.loadBoundReferenceReceiverParameter(index: Int, type: Type, kotlinType: KotlinType?) { load(index, type) - StackValue.coerce(type, AsmTypes.OBJECT_TYPE, this) + val nullableAny = kotlinType?.run { builtIns.nullableAnyType } + StackValue.coerce(type, kotlinType, AsmTypes.OBJECT_TYPE, nullableAny, this) } fun CalculatedClosure.isBoundReferenceReceiverField(fieldInfo: FieldInfo): Boolean = @@ -61,26 +70,21 @@ fun CalculatedClosure.isBoundReferenceReceiverField(fieldInfo: FieldInfo): Boole fun InstructionAdapter.generateClosureFieldsInitializationFromParameters( closure: CalculatedClosure, args: List -): Pair? { +): Pair? { var k = 1 var boundReferenceReceiverParameterIndex = -1 - var boundReferenceReceiverType: Type? = null + var boundReferenceReceiverFieldInfo: FieldInfo? = null for (fieldInfo in args) { if (closure.isBoundReferenceReceiverField(fieldInfo)) { boundReferenceReceiverParameterIndex = k - boundReferenceReceiverType = fieldInfo.fieldType + boundReferenceReceiverFieldInfo = fieldInfo k += fieldInfo.fieldType.size continue } k = AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, k, this) } - return when { - boundReferenceReceiverType != null -> - Pair(boundReferenceReceiverParameterIndex, boundReferenceReceiverType) - else -> - null - } + return boundReferenceReceiverFieldInfo?.let { Pair(boundReferenceReceiverParameterIndex, it) } } fun computeExpectedNumberOfReceivers(referencedFunction: FunctionDescriptor, isBound: Boolean): Int { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt index 8f9c06fbd78..6c0047551b7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt @@ -172,17 +172,20 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid val strategy = when (expression) { is KtCallableReferenceExpression -> { val resolvedCall = expression.callableReference.getResolvedCallWithAssert(state.bindingContext) - val receiverType = JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)?.type?.let(state.typeMapper::mapType) + val receiverKotlinType = JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)?.type + val receiverType = receiverKotlinType?.let(state.typeMapper::mapType) + val boundReceiverJvmKotlinType = receiverType?.let { JvmKotlinType(receiverType, receiverKotlinType) } if (isLambda && lambdaInfo!!.isPropertyReference) { val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor) val info = lambdaInfo.propertyReferenceInfo PropertyReferenceCodegen.PropertyReferenceGenerationStrategy( - true, info!!.getFunction, info.target, asmType, receiverType, + true, info!!.getFunction, info.target, asmType, + boundReceiverJvmKotlinType, lambdaInfo.functionWithBodyOrCallableReference, state, true ) } else { - FunctionReferenceGenerationStrategy(state, descriptor, resolvedCall, receiverType, null, true) + FunctionReferenceGenerationStrategy(state, descriptor, resolvedCall, boundReceiverJvmKotlinType, null, true) } } is KtFunctionLiteral -> ClosureGenerationStrategy(state, expression as KtDeclarationWithBody) diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt index e46f41349b8..1b13bc05c8e 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt @@ -58,7 +58,7 @@ fun KotlinType.isNullableUnderlyingType(): Boolean { return TypeUtils.isNullableType(underlyingType) } -fun PropertyDescriptor.isUnderlyingPropertyOfInlineClass(): Boolean { +fun VariableDescriptor.isUnderlyingPropertyOfInlineClass(): Boolean { val containingDeclaration = this.containingDeclaration if (!containingDeclaration.isInlineClass()) return false