JVM: Fix default parameter values handling

When we generate call for 'foo', we make decision about invoking
a 'foo$default' too late, after the call arguments are generated.
If 'foo' was an override, and base class (interface) was generic,
'foo' in base class could have a different Kotlin and JVM
signature, so the arguments we generated could be generated wrong
(primitive or inline class values instead of boxes, see KT-38680).
Also, we always selected first base class in supertypes list,
which caused KT-15971.

Look into resolved call and see if we should actually call
'foo$default' instead of 'foo' when determining actual callable.

Overrides can't introduce default parameter values, and
override-equivalent inherited methods with default parameters
is an error in a child class. Thus, if we are calling a class
member function with a default parameters, there should be one
and only one overridden function that has default parameter values
and overrides nothing.
This commit is contained in:
Dmitry Petrov
2020-05-12 18:42:27 +03:00
parent dc9f64fc9d
commit 2f82c5b6af
21 changed files with 188 additions and 62 deletions
@@ -17,11 +17,13 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.mapToIndex
@@ -123,3 +125,34 @@ private fun CallableDescriptor.defaultValueFromJava(index: Int): Boolean = DFS.i
descriptor.valueParameters[index].declaresDefaultValue()
}
)
fun shouldInvokeDefaultArgumentsStub(resolvedCall: ResolvedCall<*>): Boolean {
val descriptor = resolvedCall.resultingDescriptor
val valueArgumentsByIndex = resolvedCall.valueArgumentsByIndex ?: return false
for (index in valueArgumentsByIndex.indices) {
val resolvedValueArgument = valueArgumentsByIndex[index]
if (resolvedValueArgument is DefaultValueArgument && !descriptor.defaultValueFromJava(index)) {
return true
}
}
return false
}
fun getFunctionWithDefaultArguments(functionDescriptor: FunctionDescriptor): FunctionDescriptor {
if (functionDescriptor.containingDeclaration !is ClassDescriptor) return functionDescriptor
if (functionDescriptor.overriddenDescriptors.isEmpty()) return functionDescriptor
// We are calling a function with some arguments mapped as defaults.
// Multiple override-equivalent functions from different supertypes with (potentially different) default values
// can't be overridden by any function in a subtype.
// Also, a function overriding some other function can't introduce default parameter values.
// Thus, among all overridden functions should be one (and only one) function
// that doesn't override anything and has parameters with default values.
return functionDescriptor.overriddenTreeUniqueAsSequence(true)
.firstOrNull { function ->
function.kind == CallableMemberDescriptor.Kind.DECLARATION &&
function.overriddenDescriptors.isEmpty() &&
function.valueParameters.any { valueParameter -> valueParameter.hasDefaultValue() }
}
?: functionDescriptor
}
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
@@ -2566,7 +2566,19 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return intrinsic.toCallable(fd, superCall, resolvedCall, this);
}
return typeMapper.mapToCallableMethod(SamCodegenUtil.resolveSamAdapter(fd), superCall, null, resolvedCall);
fd = SamCodegenUtil.resolveSamAdapter(fd);
if (ArgumentGeneratorKt.shouldInvokeDefaultArgumentsStub(resolvedCall)) {
// When we invoke a function with some arguments mapped as defaults,
// we later reroute this call to an overridden function in a base class that processes the default arguments.
// If the base class is generic, this overridden function can have a different Kotlin signature
// (see KT-38681 and related issues).
// Here we replace a function with a corresponding overridden function,
// and the rest is figured out by argument generation and type mapper.
fd = ArgumentGeneratorKt.getFunctionWithDefaultArguments(fd);
}
return typeMapper.mapToCallableMethod(fd, superCall, null, resolvedCall);
}
public void invokeMethodWithArguments(
@@ -60,6 +60,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes.*
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.commons.Method
import java.util.*
fun generateIsCheck(
v: InstructionAdapter,