diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt index 08c7f474445..c24a31d348f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt @@ -1,17 +1,6 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.codegen @@ -62,7 +51,7 @@ interface CallGenerator { parameterType: Type, parameterIndex: Int) { val value = codegen.gen(argumentExpression) - value.put(parameterType, codegen.v) + value.put(parameterType, valueParameterDescriptor.original.type, codegen.v) } override fun putCapturedValueOnStack( diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallReceiver.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallReceiver.java index 2801d9447f9..c11aebd4151 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallReceiver.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallReceiver.java @@ -21,21 +21,25 @@ public class CallReceiver extends StackValue { private final StackValue dispatchReceiver; private final StackValue extensionReceiver; private final Type secondReceiverType; + private final KotlinType secondReceiverKotlinType; private CallReceiver( @NotNull StackValue dispatchReceiver, @NotNull StackValue extensionReceiver, @NotNull Type type, - @Nullable Type secondReceiverType + @Nullable KotlinType kotlinType, + @Nullable Type secondReceiverType, + @Nullable KotlinType secondReceiverKotlinType ) { - super(type, dispatchReceiver.canHaveSideEffects() || extensionReceiver.canHaveSideEffects()); + super(type, kotlinType, dispatchReceiver.canHaveSideEffects() || extensionReceiver.canHaveSideEffects()); this.dispatchReceiver = dispatchReceiver; this.extensionReceiver = extensionReceiver; this.secondReceiverType = secondReceiverType; + this.secondReceiverKotlinType = secondReceiverKotlinType; } public StackValue withoutReceiverArgument() { - return new CallReceiver(dispatchReceiver, none(), type, secondReceiverType); + return new CallReceiver(dispatchReceiver, none(), type, kotlinType, secondReceiverType, secondReceiverKotlinType); } public static StackValue generateCallReceiver( @@ -50,30 +54,42 @@ public class CallReceiver extends StackValue { KotlinTypeMapper typeMapper = codegen.typeMapper; GenerationState state = codegen.getState(); - Type type; + AsmTypeAndKotlinType jvmKotlinType; Type secondReceiverType = null; + KotlinType secondReceiverKotlinType = null; if (extensionReceiverParameter != null) { - type = calcExtensionReceiverType(resolvedCall, extensionReceiverParameter, typeMapper, callableMethod, state); + jvmKotlinType = calcExtensionReceiverType( + resolvedCall, extensionReceiverParameter, typeMapper, callableMethod, state + ); if (dispatchReceiverParameter != null) { - secondReceiverType = calcDispatchReceiverType(resolvedCall, dispatchReceiverParameter, typeMapper, callableMethod); + AsmTypeAndKotlinType dispatchReceiverInfo = calcDispatchReceiverType( + resolvedCall, dispatchReceiverParameter, typeMapper, callableMethod + ); + secondReceiverType = dispatchReceiverInfo.getType(); + secondReceiverKotlinType = dispatchReceiverInfo.getKotlinType(); } } else if (dispatchReceiverParameter != null) { - type = calcDispatchReceiverType(resolvedCall, dispatchReceiverParameter, typeMapper, callableMethod); + jvmKotlinType = calcDispatchReceiverType(resolvedCall, dispatchReceiverParameter, typeMapper, callableMethod); } else if (isLocalFunCall(callableMethod)) { - type = callableMethod.getGenerateCalleeType(); + Type calleeType = callableMethod.getGenerateCalleeType(); + assert calleeType != null : "Could not get callee type for " + resolvedCall; + + jvmKotlinType = new AsmTypeAndKotlinType(calleeType, null); } else { - type = Type.VOID_TYPE; + jvmKotlinType = new AsmTypeAndKotlinType(Type.VOID_TYPE, null); } - assert type != null : "Could not map receiver type for " + resolvedCall; - return new CallReceiver(dispatchReceiver, extensionReceiver, type, secondReceiverType); + return new CallReceiver( + dispatchReceiver, extensionReceiver, jvmKotlinType.getType(), + jvmKotlinType.getKotlinType(), secondReceiverType, secondReceiverKotlinType + ); } - private static Type calcDispatchReceiverType( + private static AsmTypeAndKotlinType calcDispatchReceiverType( @NotNull ResolvedCall resolvedCall, @Nullable ReceiverParameterDescriptor dispatchReceiver, @NotNull KotlinTypeMapper typeMapper, @@ -84,27 +100,33 @@ public class CallReceiver extends StackValue { CallableDescriptor descriptor = resolvedCall.getResultingDescriptor(); if (CodegenUtilKt.isJvmStaticInObjectOrClassOrInterface(descriptor)) { - return Type.VOID_TYPE; + return new AsmTypeAndKotlinType(Type.VOID_TYPE, null); } DeclarationDescriptor container = descriptor.getContainingDeclaration(); if (callableMethod != null) { if (InlineClassesUtilsKt.isInlineClass(container)) { - return typeMapper.mapType((ClassDescriptor) container); + ClassDescriptor classDescriptor = (ClassDescriptor) container; + return new AsmTypeAndKotlinType(typeMapper.mapType(classDescriptor), classDescriptor.getDefaultType()); } - return callableMethod.getDispatchReceiverType(); + //noinspection ConstantConditions + return new AsmTypeAndKotlinType(callableMethod.getDispatchReceiverType(), null); } // Extract the receiver from the resolved call, workarounding the fact that ResolvedCall#dispatchReceiver doesn't have // all the needed information, for example there's no way to find out whether or not a smart cast was applied to the receiver. if (container instanceof ClassDescriptor) { - return typeMapper.mapClass((ClassDescriptor) container); + ClassDescriptor classDescriptor = (ClassDescriptor) container; + return new AsmTypeAndKotlinType(typeMapper.mapClass(classDescriptor), classDescriptor.getDefaultType()); } - return typeMapper.mapType(dispatchReceiver); + KotlinType dispatchReceiverType = dispatchReceiver.getReturnType(); + + //noinspection ConstantConditions + return new AsmTypeAndKotlinType(typeMapper.mapType(dispatchReceiverType), dispatchReceiverType); } - private static Type calcExtensionReceiverType( + private static AsmTypeAndKotlinType calcExtensionReceiverType( @NotNull ResolvedCall resolvedCall, @Nullable ReceiverParameterDescriptor extensionReceiver, @NotNull KotlinTypeMapper typeMapper, @@ -121,10 +143,12 @@ public class CallReceiver extends StackValue { ) { ReceiverParameterDescriptor receiverCandidate = descriptor.getExtensionReceiverParameter(); assert receiverCandidate != null; - return typeMapper.mapType(receiverCandidate.getType()); + return new AsmTypeAndKotlinType(typeMapper.mapType(receiverCandidate.getType()), receiverCandidate.getType()); } - return callableMethod != null ? callableMethod.getExtensionReceiverType() : typeMapper.mapType(extensionReceiver.getType()); + return callableMethod != null ? + new AsmTypeAndKotlinType(callableMethod.getExtensionReceiverType(), null) : + new AsmTypeAndKotlinType(typeMapper.mapType(extensionReceiver.getType()), extensionReceiver.getType()); } @Override @@ -132,14 +156,16 @@ public class CallReceiver extends StackValue { StackValue currentExtensionReceiver = extensionReceiver; boolean hasExtensionReceiver = extensionReceiver != none(); if (extensionReceiver instanceof SafeCall) { - currentExtensionReceiver.put(currentExtensionReceiver.type, v); - currentExtensionReceiver = StackValue.onStack(currentExtensionReceiver.type); + currentExtensionReceiver.put(currentExtensionReceiver.type, currentExtensionReceiver.kotlinType, v); + currentExtensionReceiver = StackValue.onStack(currentExtensionReceiver.type, currentExtensionReceiver.kotlinType); } - Type dispatchReceiverType = secondReceiverType != null ? secondReceiverType : - hasExtensionReceiver ? dispatchReceiver.type : - type; - dispatchReceiver.put(dispatchReceiverType, v); + Type dispatchReceiverType = calcDispatchReceiver(secondReceiverType, hasExtensionReceiver, dispatchReceiver.type, type); + KotlinType dispatchReceiverKotlinType = calcDispatchReceiver( + secondReceiverKotlinType, hasExtensionReceiver, dispatchReceiver.kotlinType, kotlinType + ); + + dispatchReceiver.put(dispatchReceiverType, dispatchReceiverKotlinType, v); currentExtensionReceiver .moveToTopOfStack( @@ -150,6 +176,15 @@ public class CallReceiver extends StackValue { ); } + private static T calcDispatchReceiver(T secondType, boolean hasExtensionReceiver, T dispatchReceiverType, T defaultType) { + if (secondType != null) { + return secondType; + } + else { + return hasExtensionReceiver ? dispatchReceiverType : defaultType; + } + } + @Override public void dup(@NotNull InstructionAdapter v, boolean withReceiver) { AsmUtil.dup(v, extensionReceiver.type, dispatchReceiver.type); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 5066ea6b8fc..4544d39c2f4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -177,7 +177,12 @@ public abstract class StackValue { @NotNull public static StackValue onStack(@NotNull Type type) { - return type == Type.VOID_TYPE ? none() : new OnStack(type); + return onStack(type, null); + } + + @NotNull + public static StackValue onStack(@NotNull Type type, @Nullable KotlinType kotlinType) { + return type == Type.VOID_TYPE ? none() : new OnStack(type, kotlinType); } @NotNull @@ -852,7 +857,11 @@ public abstract class StackValue { public static class OnStack extends StackValue { public OnStack(Type type) { - super(type); + this(type, null); + } + + public OnStack(Type type, KotlinType kotlinType) { + super(type, kotlinType); } @Override diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index e252bd13b5b..81911a5f875 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -1,17 +1,6 @@ /* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. */ @@ -279,6 +268,8 @@ fun Collection.filterOutDescriptorsWithSpecialNames() = filt class TypeAndNullability(@JvmField val type: Type, @JvmField val isNullable: Boolean) +class AsmTypeAndKotlinType(val type: Type, val kotlinType: KotlinType?) + fun calcTypeForIEEE754ArithmeticIfNeeded( expression: KtExpression?, bindingContext: BindingContext, diff --git a/compiler/testData/codegen/box/inlineClasses/checkBoxingOnFunctionCalls.kt b/compiler/testData/codegen/box/inlineClasses/checkBoxingOnFunctionCalls.kt new file mode 100644 index 00000000000..16f947ff65e --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/checkBoxingOnFunctionCalls.kt @@ -0,0 +1,21 @@ +// !LANGUAGE: +InlineClasses + +inline class InlineNotNullPrimitive(val x: Int) +inline class InlineNotNullReference(val y: String) + +fun testNotNullPrimitive(a: Any, b: T, c: InlineNotNullPrimitive, d: InlineNotNullPrimitive?) {} +fun testNotNullReference(a: Any, b: T, c: InlineNotNullReference, d: InlineNotNullReference?) {} + +fun test(a: InlineNotNullPrimitive, b: InlineNotNullReference) { + testNotNullPrimitive(a, a, a, a) // 3 box + testNotNullReference(b, b, b, b) // 2 box +} + +fun box(): String { + val a = InlineNotNullPrimitive(10) + val b = InlineNotNullReference("some") + + test(a, b) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnFunctionCall.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnFunctionCall.kt new file mode 100644 index 00000000000..75f848305eb --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnFunctionCall.kt @@ -0,0 +1,17 @@ +// !LANGUAGE: +InlineClasses + +inline class InlineNotNullPrimitive(val x: Int) +inline class InlineNotNullReference(val y: String) + +fun testNotNullPrimitive(a: Any, b: T, c: InlineNotNullPrimitive, d: InlineNotNullPrimitive?) {} +fun testNotNullReference(a: Any, b: T, c: InlineNotNullReference, d: InlineNotNullReference?) {} + +fun test(a: InlineNotNullPrimitive, b: InlineNotNullReference) { + testNotNullPrimitive(a, a, a, a) // 3 box + testNotNullReference(b, b, b, b) // 2 box +} + +// 3 INVOKESTATIC InlineNotNullPrimitive\$Erased.box +// 2 INVOKESTATIC InlineNotNullReference\$Erased.box + +// 0 valueOf \ 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 d469ea59356..4f06cb164b2 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1931,6 +1931,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnAssignment.kt"); doTest(fileName); } + + @TestMetadata("inlineClassBoxingOnFunctionCall.kt") + public void testInlineClassBoxingOnFunctionCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnFunctionCall.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/bytecodeText/interfaces")