[IR] Deal with forward references in default argument lambdas.

Rely on the frontend weeding out cases that are not supported.

In psi2ir, introduce all the parameters before processing default
values.

Change the DefaultArgumentStubGenerator to generate code that
matches the behavior of the current backend.
This commit is contained in:
Mads Ager
2019-10-08 15:41:01 +02:00
committed by max-kammerer
parent 834eeb567b
commit 92cf521e11
13 changed files with 181 additions and 14 deletions
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
@@ -84,6 +83,20 @@ open class DefaultArgumentStubGenerator(
variables[it] = newIrFunction.extensionReceiverParameter!!
}
// In order to deal with forward references in default value lambdas,
// accesses to the parameter before it has been determined if there is
// a default value or not is redirected to the actual parameter of the
// $default function. This is to ensure that examples such as:
//
// fun f(f1: () -> String = { f2() },
// f2: () -> String = { "OK" }) = f1()
//
// works correctly so that `f() { "OK" }` returns "OK" and
// `f()` throws a NullPointerException.
irFunction.valueParameters.associateWithTo(variables) {
newIrFunction.valueParameters[it.index]
}
for (valueParameter in irFunction.valueParameters) {
val parameter = newIrFunction.valueParameters[valueParameter.index]
val remapped = if (valueParameter.defaultValue != null) {
@@ -129,7 +142,6 @@ open class DefaultArgumentStubGenerator(
passTypeArgumentsFrom(newIrFunction.parentAsClass)
passTypeArgumentsFrom(newIrFunction)
dispatchReceiver = newIrFunction.dispatchReceiverParameter?.let { irGet(it) }
params.forEachIndexed { i, variable -> putValueArgument(i, irGet(variable)) }
}
is IrSimpleFunction -> +irReturn(dispatchToImplementation(irFunction, newIrFunction, params))
@@ -289,21 +289,21 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio
}
val bodyGenerator = createBodyGenerator(irFunction.symbol)
// Declare all the value parameters up first.
functionDescriptor.valueParameters.mapTo(irFunction.valueParameters) { valueParameterDescriptor ->
val ktParameter = DescriptorToSourceUtils.getSourceFromDescriptor(valueParameterDescriptor) as? KtParameter
generateValueParameterDeclaration(valueParameterDescriptor, ktParameter, bodyGenerator, withDefaultValues, irFunction)
declareParameter(valueParameterDescriptor, ktParameter, irFunction)
}
}
private fun generateValueParameterDeclaration(
valueParameterDescriptor: ValueParameterDescriptor,
ktParameter: KtParameter?,
bodyGenerator: BodyGenerator,
withDefaultValues: Boolean,
irOwnerElement: IrElement
): IrValueParameter =
declareParameter(valueParameterDescriptor, ktParameter, irOwnerElement).also { irValueParameter ->
if (withDefaultValues) {
// Only after value parameters have been declared, generate default values. This ensures
// that forward references to other parameters works in default value lambdas. For example:
//
// fun f(f1: () -> String = { f2() },
// f2: () -> String) = f1()
if (withDefaultValues) {
irFunction.valueParameters.forEachIndexed { index, irValueParameter ->
val valueParameterDescriptor = functionDescriptor.valueParameters[index]
val ktParameter = DescriptorToSourceUtils.getSourceFromDescriptor(valueParameterDescriptor) as? KtParameter
irValueParameter.defaultValue = ktParameter?.defaultValue?.let { defaultValue ->
val inAnnotation =
valueParameterDescriptor.containingDeclaration.safeAs<ConstructorDescriptor>()?.isAnnotationConstructor() ?: false
@@ -314,6 +314,7 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio
}
}
}
}
private fun generateReceiverParameterDeclaration(
receiverParameterDescriptor: ReceiverParameterDescriptor,