IR: refactor generateDefaultsFunction

The check for whether a function needs a default stub should run fewer
times now, and in particular is no longer quadratic. Also, this fixes
a weirdness in that certain inheritance hierarchies would produce
a default stub that points to a non-stub through its overriddenSymbols
field.
This commit is contained in:
pyos
2019-11-13 10:25:32 +01:00
committed by Georgy Bronnikov
parent 29a14e2330
commit 7a2715da44
@@ -11,28 +11,20 @@ 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.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildFunWithDescriptorForInlining
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.descriptors.WrappedClassConstructorDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedFunctionDescriptorWithContainerSource
import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
// TODO: fix expect/actual default parameters
@@ -54,24 +46,12 @@ open class DefaultArgumentStubGenerator(
private val symbols get() = context.ir.symbols
private fun lower(irFunction: IrFunction): List<IrFunction> {
if (!irFunction.needsDefaultArgumentsLowering(skipInlineMethods, skipExternalMethods))
return listOf(irFunction)
val bodies = irFunction.valueParameters.mapNotNull { it.defaultValue }
log { "detected ${irFunction.name.asString()} has got #${bodies.size} default expressions" }
if (bodies.isEmpty()) {
// Fake override
val newIrFunction = irFunction.generateDefaultsFunction(context, IrDeclarationOrigin.FAKE_OVERRIDE, skipInlineMethods, skipExternalMethods)
val newIrFunction = irFunction.generateDefaultsFunction(context, skipInlineMethods, skipExternalMethods)
?: return listOf(irFunction)
if (newIrFunction.origin == IrDeclarationOrigin.FAKE_OVERRIDE) {
return listOf(irFunction, newIrFunction)
}
val newIrFunction =
irFunction.generateDefaultsFunction(context, IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER, skipInlineMethods, skipExternalMethods)
log { "$irFunction -> $newIrFunction" }
val builder = context.createIrBuilder(newIrFunction.symbol)
@@ -230,16 +210,11 @@ open class DefaultParameterInjector(
expression: IrFunctionAccessExpression,
builder: (IrFunctionSymbol) -> IrFunctionAccessExpression
): IrExpression {
val functionDeclaration = expression.symbol.owner
if (!functionDeclaration.needsDefaultArgumentsLowering(skipInline, skipExternalMethods))
val argumentsCount = (0 until expression.valueArgumentsCount).count { expression.getValueArgument(it) != null }
if (argumentsCount == expression.symbol.owner.valueParameters.size)
return expression
val argumentsCount = argumentCount(expression)
if (argumentsCount == functionDeclaration.valueParameters.size)
return expression
val (symbol, params) = parametersForCall(expression)
val (symbol, params) = parametersForCall(expression) ?: return expression
val descriptor = symbol.descriptor
val declaration = symbol.owner
@@ -322,31 +297,12 @@ open class DefaultParameterInjector(
}
}
private fun IrFunction.findSuperMethodWithDefaultArguments(): IrFunction? {
if (!needsDefaultArgumentsLowering(skipInline, skipExternalMethods)) return null
if (this !is IrSimpleFunction) return this
for (s in overriddenSymbols) {
s.owner.findSuperMethodWithDefaultArguments()?.let { return it }
}
return this
}
private fun parametersForCall(
expression: IrFunctionAccessExpression
): Pair<IrFunctionSymbol, List<Pair<IrValueParameter, IrExpression?>>> {
): Pair<IrFunctionSymbol, List<Pair<IrValueParameter, IrExpression?>>>? {
val declaration = expression.symbol.owner
val keyFunction = declaration.findSuperMethodWithDefaultArguments()!!
val realFunction =
keyFunction.generateDefaultsFunction(
context,
IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER,
skipInline,
skipExternalMethods
)
val keyFunction = declaration.generateDefaultsFunction(context, skipInline, skipExternalMethods) ?: return null
val realFunction = if (keyFunction is IrSimpleFunction) keyFunction.resolveFakeOverride()!! else keyFunction
log { "$declaration -> $realFunction" }
val maskValues = Array((declaration.valueParameters.size + 31) / 32) { 0 }
@@ -386,14 +342,6 @@ open class DefaultParameterInjector(
return Pair(realFunction.symbol, params)
}
private fun argumentCount(expression: IrMemberAccessExpression): Int {
var result = 0
for (i in 0 until expression.valueArgumentsCount) {
expression.getValueArgument(i)?.run { ++result }
}
return result
}
protected open fun nullConst(startOffset: Int, endOffset: Int, irParameter: IrValueParameter): IrExpression? =
if (irParameter.varargElementType != null) {
null
@@ -415,181 +363,82 @@ class DefaultParameterCleaner constructor(val context: CommonBackendContext) : F
}
}
// TODO this implementation is exponential
private fun IrFunction.needsDefaultArgumentsLowering(skipInlineMethods: Boolean, skipExternalMethods: Boolean): Boolean {
if (isInline && skipInlineMethods) return false
if (skipExternalMethods && isExternalOrInheritedFromExternal()) return false
if (valueParameters.any { it.defaultValue != null }) return true
if (this !is IrSimpleFunction) return false
fun IrSimpleFunction.inheritsDefaultValues(): Boolean =
valueParameters.any { it.defaultValue != null } || overriddenSymbols.any { it.owner.inheritsDefaultValues() }
return inheritsDefaultValues()
}
private fun IrFunction.generateDefaultsFunctionImpl(
context: CommonBackendContext,
origin: IrDeclarationOrigin,
skipInlineMethods: Boolean,
skipExternalMethods: Boolean
): IrFunction {
val newFunction = buildFunctionDeclaration(this, origin)
val syntheticParameters = MutableList((valueParameters.size + 31) / 32) { i ->
newFunction.valueParameter(valueParameters.size + i, parameterMaskName(i), context.irBuiltIns.intType)
}
if (this is IrConstructor) {
syntheticParameters += newFunction.valueParameter(
syntheticParameters.last().index + 1,
kConstructorMarkerName,
context.ir.symbols.defaultConstructorMarker.owner.defaultType.makeNullable()
)
} else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) {
syntheticParameters += newFunction.valueParameter(
syntheticParameters.last().index + 1,
"handler".synthesizedName,
context.irBuiltIns.anyNType
)
}
newFunction.copyTypeParametersFrom(this)
val newValueParameters = valueParameters.map { it.copyMaybeNullableTo(newFunction, context) } + syntheticParameters
newValueParameters.forEach {
it.defaultValue = null
}
newFunction.returnType = returnType
newFunction.dispatchReceiverParameter = dispatchReceiverParameter?.copyTo(newFunction)
newFunction.extensionReceiverParameter = extensionReceiverParameter?.copyTo(newFunction)
newFunction.valueParameters += newValueParameters
annotations.mapTo(newFunction.annotations) { it.deepCopyWithSymbols() }
if (origin == IrDeclarationOrigin.FAKE_OVERRIDE) {
for (baseFunSymbol in (this as IrSimpleFunction).overriddenSymbols) {
val baseFun = baseFunSymbol.owner
if (baseFun.needsDefaultArgumentsLowering(skipInlineMethods, skipExternalMethods)) {
val baseOrigin = if (baseFun.valueParameters.any { it.defaultValue != null }) {
IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER
} else {
IrDeclarationOrigin.FAKE_OVERRIDE
}
val defaultsBaseFun = baseFun.generateDefaultsFunction(context, baseOrigin, skipInlineMethods, skipExternalMethods)
(newFunction as IrSimpleFunction).overriddenSymbols.add((defaultsBaseFun as IrSimpleFunction).symbol)
}
}
}
return newFunction
}
private fun buildFunctionDeclaration(irFunction: IrFunction, origin: IrDeclarationOrigin): IrFunction {
when (irFunction) {
is IrConstructor -> {
val descriptor = WrappedClassConstructorDescriptor()
return IrConstructorImpl(
irFunction.startOffset,
irFunction.endOffset,
origin,
IrConstructorSymbolImpl(descriptor),
irFunction.name,
irFunction.visibility,
irFunction.returnType,
isInline = irFunction.isInline,
isExternal = false,
isPrimary = false,
isExpect = false
).also {
descriptor.bind(it)
it.parent = irFunction.parent
}
}
is IrSimpleFunction -> {
val descriptor = irFunction.descriptor.safeAs<DescriptorWithContainerSource>()?.let {
WrappedFunctionDescriptorWithContainerSource(it.containerSource)
} ?: WrappedSimpleFunctionDescriptor()
val name = Name.identifier("${irFunction.name}\$default")
return IrFunctionImpl(
irFunction.startOffset,
irFunction.endOffset,
origin,
IrSimpleFunctionSymbolImpl(descriptor),
name,
irFunction.visibility,
Modality.FINAL,
irFunction.returnType,
isInline = irFunction.isInline,
isExternal = false,
isTailrec = false,
isSuspend = irFunction.isSuspend,
isExpect = irFunction.isExpect,
isFakeOverride = origin == IrDeclarationOrigin.FAKE_OVERRIDE
).also {
descriptor.bind(it)
it.parent = irFunction.parent
}
}
else -> throw IllegalStateException("Unknown function type")
}
}
private fun IrFunction.generateDefaultsFunction(
context: CommonBackendContext,
origin: IrDeclarationOrigin,
skipInlineMethods: Boolean,
skipExternalMethods: Boolean
): IrFunction =
context.ir.defaultParameterDeclarationsCache.getOrPut(this) {
generateDefaultsFunctionImpl(context, origin, skipInlineMethods, skipExternalMethods)
}
private fun IrFunction.valueParameter(index: Int, name: Name, type: IrType): IrValueParameter {
val parameterDescriptor = WrappedValueParameterDescriptor()
return IrValueParameterImpl(
startOffset,
endOffset,
IrDeclarationOrigin.DEFINED,
IrValueParameterSymbolImpl(parameterDescriptor),
name,
index,
type,
null,
isCrossinline = false,
isNoinline = false
).also {
parameterDescriptor.bind(it)
it.parent = this
): IrFunction? = when {
skipInlineMethods && isInline -> null
skipExternalMethods && isExternalOrInheritedFromExternal() -> null
valueParameters.any { it.defaultValue != null } ->
generateDefaultsFunctionImpl(context, IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER)
this is IrSimpleFunction -> {
// If this is an override of a function with default arguments, produce a fake override of a default stub.
val overriddenStubs = overriddenSymbols.mapNotNull {
it.owner.generateDefaultsFunction(context, skipInlineMethods, skipExternalMethods)?.symbol as IrSimpleFunctionSymbol?
}
if (overriddenStubs.isNotEmpty())
generateDefaultsFunctionImpl(context, IrDeclarationOrigin.FAKE_OVERRIDE).also {
(it as IrSimpleFunction).overriddenSymbols.addAll(overriddenStubs)
}
else
null
}
else -> null
}
private fun IrFunction.generateDefaultsFunctionImpl(context: CommonBackendContext, newOrigin: IrDeclarationOrigin): IrFunction =
context.ir.defaultParameterDeclarationsCache.getOrPut(this) {
val newFunction = when (this) {
is IrConstructor ->
buildConstructor {
updateFrom(this@generateDefaultsFunctionImpl)
origin = newOrigin
isExternal = false
isPrimary = false
isExpect = false
}
is IrSimpleFunction ->
buildFunWithDescriptorForInlining(descriptor) {
updateFrom(this@generateDefaultsFunctionImpl)
name = Name.identifier("${this@generateDefaultsFunctionImpl.name}\$default")
origin = newOrigin
modality = Modality.FINAL
isExternal = false
isTailrec = false
}
else -> throw IllegalStateException("Unknown function type")
}
newFunction.copyTypeParametersFrom(this)
newFunction.parent = parent
newFunction.returnType = returnType.remapTypeParameters(classIfConstructor, newFunction.classIfConstructor)
newFunction.dispatchReceiverParameter = dispatchReceiverParameter?.copyTo(newFunction)
newFunction.extensionReceiverParameter = extensionReceiverParameter?.copyTo(newFunction)
valueParameters.mapTo(newFunction.valueParameters) {
val newType = it.type.remapTypeParameters(classIfConstructor, newFunction.classIfConstructor)
val makeNullable = it.defaultValue != null &&
(context.ir.unfoldInlineClassType(it.type) ?: it.type) !in context.irBuiltIns.primitiveIrTypes
it.copyTo(newFunction, type = if (makeNullable) newType.makeNullable() else newType, defaultValue = null)
}
for (i in 0 until (valueParameters.size + 31) / 32) {
newFunction.addValueParameter(parameterMaskName(i).asString(), context.irBuiltIns.intType)
}
if (this is IrConstructor) {
newFunction.addValueParameter(
kConstructorMarkerName.asString(),
context.ir.symbols.defaultConstructorMarker.owner.defaultType.makeNullable()
)
} else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) {
newFunction.addValueParameter("handler".synthesizedString, context.irBuiltIns.anyNType)
}
// TODO some annotations are needed (e.g. @JvmStatic), others need different values (e.g. @JvmName), the rest are redundant.
annotations.mapTo(newFunction.annotations) { it.deepCopyWithSymbols() }
newFunction
}
internal val kConstructorMarkerName = "marker".synthesizedName
private fun parameterMaskName(number: Int) = "mask$number".synthesizedName
private fun IrValueParameter.copyMaybeNullableTo(irFunction: IrFunction, context: CommonBackendContext): IrValueParameter {
if (defaultValue == null) return copyTo(irFunction)
val underlyingType = context.ir.unfoldInlineClassType(type) ?: type
if (underlyingType in context.irBuiltIns.primitiveIrTypes) return copyTo(irFunction)
val newType = type.remapTypeParameters(
(parent as IrTypeParametersContainer).classIfConstructor,
irFunction.classIfConstructor
).makeNullable()
val descriptor = WrappedValueParameterDescriptor(symbol.descriptor.annotations, symbol.descriptor.source)
return IrValueParameterImpl(
startOffset, endOffset, origin, IrValueParameterSymbolImpl(descriptor),
name, index, newType, varargElementType, isCrossinline, isNoinline
).also {
descriptor.bind(it)
it.parent = irFunction
it.defaultValue = null
it.annotations.addAll(annotations.map { it.deepCopyWithSymbols() })
}
}