IR Scripting: fix capturing of the script parameter in a fun

This commit is contained in:
Ilya Chernikov
2023-06-15 11:47:51 +02:00
committed by Space Team
parent bf3a6f7678
commit 5e3f7184d1
3 changed files with 127 additions and 97 deletions
@@ -608,83 +608,88 @@ class Fir2IrVisitor(
): IrElement = whileAnalysing(session, thisReceiverExpression) {
val calleeReference = thisReceiverExpression.calleeReference
val boundSymbol = calleeReference.boundSymbol
if (boundSymbol is FirClassSymbol) {
// Object case
val firClass = boundSymbol.fir as FirClass
val irClass = if (firClass.origin == FirDeclarationOrigin.Source) {
// We anyway can use 'else' branch as fallback, but
// this is an additional check of FIR2IR invariants
// (source classes should be already built when we analyze bodies)
classifierStorage.getCachedIrClass(firClass)!!
} else {
classifierStorage.getIrClassSymbol(boundSymbol).owner
}
// NB: IR generates anonymous objects as classes, not singleton objects
if (firClass is FirRegularClass && firClass.classKind == ClassKind.OBJECT && !isThisForClassPhysicallyAvailable(irClass)) {
return thisReceiverExpression.convertWithOffsets { startOffset, endOffset ->
IrGetObjectValueImpl(startOffset, endOffset, irClass.defaultType, irClass.symbol)
when (boundSymbol) {
is FirClassSymbol -> {
// Object case
val firClass = boundSymbol.fir as FirClass
val irClass = if (firClass.origin == FirDeclarationOrigin.Source) {
// We anyway can use 'else' branch as fallback, but
// this is an additional check of FIR2IR invariants
// (source classes should be already built when we analyze bodies)
classifierStorage.getCachedIrClass(firClass)!!
} else {
classifierStorage.getIrClassSymbol(boundSymbol).owner
}
// NB: IR generates anonymous objects as classes, not singleton objects
if (firClass is FirRegularClass && firClass.classKind == ClassKind.OBJECT && !isThisForClassPhysicallyAvailable(irClass)) {
return thisReceiverExpression.convertWithOffsets { startOffset, endOffset ->
IrGetObjectValueImpl(startOffset, endOffset, irClass.defaultType, irClass.symbol)
}
}
}
val dispatchReceiver = conversionScope.dispatchReceiverParameter(irClass)
if (dispatchReceiver != null) {
return thisReceiverExpression.convertWithOffsets { startOffset, endOffset ->
val thisRef = IrGetValueImpl(startOffset, endOffset, dispatchReceiver.type, dispatchReceiver.symbol)
if (calleeReference.contextReceiverNumber != -1) {
val constructorForCurrentlyGeneratedDelegatedConstructor =
conversionScope.getConstructorForCurrentlyGeneratedDelegatedConstructor(irClass)
val dispatchReceiver = conversionScope.dispatchReceiverParameter(irClass)
if (dispatchReceiver != null) {
return thisReceiverExpression.convertWithOffsets { startOffset, endOffset ->
val thisRef = IrGetValueImpl(startOffset, endOffset, dispatchReceiver.type, dispatchReceiver.symbol)
if (calleeReference.contextReceiverNumber != -1) {
val constructorForCurrentlyGeneratedDelegatedConstructor =
conversionScope.getConstructorForCurrentlyGeneratedDelegatedConstructor(irClass)
if (constructorForCurrentlyGeneratedDelegatedConstructor != null) {
val constructorParameter =
constructorForCurrentlyGeneratedDelegatedConstructor.valueParameters[calleeReference.contextReceiverNumber]
IrGetValueImpl(startOffset, endOffset, constructorParameter.type, constructorParameter.symbol)
if (constructorForCurrentlyGeneratedDelegatedConstructor != null) {
val constructorParameter =
constructorForCurrentlyGeneratedDelegatedConstructor.valueParameters[calleeReference.contextReceiverNumber]
IrGetValueImpl(startOffset, endOffset, constructorParameter.type, constructorParameter.symbol)
} else {
val contextReceivers =
components.classifierStorage.getFieldsWithContextReceiversForClass(irClass)
?: error("Not defined context receivers for $irClass")
IrGetFieldImpl(
startOffset, endOffset, contextReceivers[calleeReference.contextReceiverNumber].symbol,
thisReceiverExpression.typeRef.toIrType(),
thisRef,
)
}
} else {
val contextReceivers =
components.classifierStorage.getFieldsWithContextReceiversForClass(irClass)
?: error("Not defined context receivers for $irClass")
IrGetFieldImpl(
startOffset, endOffset, contextReceivers[calleeReference.contextReceiverNumber].symbol,
thisReceiverExpression.typeRef.toIrType(),
thisRef,
)
thisRef
}
} else {
thisRef
}
}
}
} else if (boundSymbol is FirScriptSymbol && calleeReference.contextReceiverNumber >= 0) {
val firScript = boundSymbol.fir
val irScript = declarationStorage.getCachedIrScript(firScript) ?: error("IrScript for ${firScript.name} not found")
val receiverParameter = irScript.implicitReceiversParameters.find { it.index == calleeReference.contextReceiverNumber }
if (receiverParameter != null) {
return thisReceiverExpression.convertWithOffsets { startOffset, endOffset ->
IrGetValueImpl(startOffset, endOffset, receiverParameter.type, receiverParameter.symbol)
is FirScriptSymbol -> {
val firScript = boundSymbol.fir
val irScript = declarationStorage.getCachedIrScript(firScript) ?: error("IrScript for ${firScript.name} not found")
val receiverParameter =
irScript.implicitReceiversParameters.find { it.index == calleeReference.contextReceiverNumber } ?: irScript.thisReceiver
if (receiverParameter != null) {
return thisReceiverExpression.convertWithOffsets { startOffset, endOffset ->
IrGetValueImpl(startOffset, endOffset, receiverParameter.type, receiverParameter.symbol)
}
} else {
error("No script receiver found") // TODO: check if any valid situations possible here
}
} else {
error("Expecting implicit receiver") // TODO: check if any valid situations possible here
}
} else if (boundSymbol is FirCallableSymbol) {
val irFunction = when (boundSymbol) {
is FirFunctionSymbol -> declarationStorage.getIrFunctionSymbol(boundSymbol).owner
is FirPropertySymbol -> {
val property = declarationStorage.getIrPropertySymbol(boundSymbol).owner as? IrProperty
property?.let { conversionScope.parentAccessorOfPropertyFromStack(it) }
is FirCallableSymbol -> {
val irFunction = when (boundSymbol) {
is FirFunctionSymbol -> declarationStorage.getIrFunctionSymbol(boundSymbol).owner
is FirPropertySymbol -> {
val property = declarationStorage.getIrPropertySymbol(boundSymbol).owner as? IrProperty
property?.let { conversionScope.parentAccessorOfPropertyFromStack(it) }
}
else -> null
}
else -> null
}
val receiver = irFunction?.let { function ->
if (calleeReference.contextReceiverNumber != -1)
function.valueParameters[calleeReference.contextReceiverNumber]
else
function.extensionReceiverParameter
}
val receiver = irFunction?.let { function ->
if (calleeReference.contextReceiverNumber != -1)
function.valueParameters[calleeReference.contextReceiverNumber]
else
function.extensionReceiverParameter
}
if (receiver != null) {
return thisReceiverExpression.convertWithOffsets { startOffset, endOffset ->
IrGetValueImpl(startOffset, endOffset, receiver.type, receiver.symbol)
if (receiver != null) {
return thisReceiverExpression.convertWithOffsets { startOffset, endOffset ->
IrGetValueImpl(startOffset, endOffset, receiver.type, receiver.symbol)
}
}
}
}
@@ -24,10 +24,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.interpreter.toIrConst
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
@@ -75,7 +72,7 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
}
}
val symbolRemapper = ScriptsToClassesSymbolRemapper(scriptsToClasses)
val symbolRemapper = ScriptsToClassesSymbolRemapper()
for ((irScript, irScriptClass) in scriptsToClasses) {
finalizeScriptClass(irScriptClass, irScript, symbolRemapper)
@@ -237,7 +234,21 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
(this as? IrDeclaration)?.let { defaultContext.copy( topLevelDeclaration = it) } ?: defaultContext
).transform(lambdaPatcher, ScriptFixLambdasTransformerContext())
(irScript.constructor?.patchForClass() as? IrConstructor ?: createConstructor(irScriptClass, irScript)).also { constructor ->
val explicitParametersWithFields = irScript.explicitCallParameters.map { parameter ->
val field = irScriptClass.addField {
startOffset = parameter.startOffset
endOffset = parameter.endOffset
origin = IrDeclarationOrigin.SCRIPT_CALL_PARAMETER
name = parameter.name
type = parameter.type
visibility = DescriptorVisibilities.LOCAL
isFinal = true
}
parameter to field
}
(irScript.constructor?.patchForClass() as? IrConstructor
?: createConstructor(irScriptClass, irScript, implicitReceiversFieldsWithParameters)).also { constructor ->
val explicitParamsStartIndex = if (irScript.earlierScriptsParameter == null) 0 else 1
val explicitParameters = constructor.valueParameters.subList(
explicitParamsStartIndex,
@@ -265,6 +276,12 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
if (earlierScriptField != null) {
+irSetField(irGet(irScriptClass.thisReceiver!!), earlierScriptField, irGet(irScript.earlierScriptsParameter!!))
}
explicitParametersWithFields.forEach { (parameter, field) ->
+irSetField(
irGet(irScriptClass.thisReceiver!!),
field, irGet(parameter.type, explicitParameters.find { it.name == parameter.name }!!.symbol)
)
}
implicitReceiversFieldsWithParameters.forEach { (field, correspondingParameter) ->
+irSetField(
irGet(irScriptClass.thisReceiver!!),
@@ -328,7 +345,8 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
private fun createConstructor(
irScriptClass: IrClass,
irScript: IrScript
irScript: IrScript,
implicitReceiversFieldsWithParameters: ArrayList<Pair<IrField, IrValueParameter>>
): IrConstructor =
with(IrFunctionBuilder().apply {
isPrimary = true
@@ -349,18 +367,23 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
containerSource = containerSource,
)
}.also { irConstructor ->
var parametersIndex = 0
irConstructor.valueParameters = buildList {
addIfNotNull(irScript.earlierScriptsParameter)
irScript.earlierScriptsParameter?.let {
add(it)
++parametersIndex
}
addAll(irScript.explicitCallParameters.map {
IrValueParameterImpl(
it.startOffset, it.endOffset,
IrDeclarationOrigin.SCRIPT_CALL_PARAMETER, IrValueParameterSymbolImpl(),
it.name, index = 0,
type = it.type, varargElementType = null,
isCrossinline = false, isNoinline = false, isHidden = false, isAssignable = false
it.name, index = parametersIndex++, type = it.type,
varargElementType = null, isCrossinline = false, isNoinline = false, isHidden = false, isAssignable = false
).also { it.parent = irScript }
})
addAll(irScript.implicitReceiversParameters)
implicitReceiversFieldsWithParameters.forEach {(_, param) ->
add(param)
}
addAll(irScript.providedPropertiesParameters)
}
irConstructor.parent = irScript
@@ -525,7 +548,7 @@ private class ScriptToClassTransformer(
createThisReceiverParameter(IrDeclarationOrigin.SCRIPT_THIS_RECEIVER, scriptClassReceiver.type)
} else null
}
val isInScriptConstructor = this@transformFunctionChildren is IrConstructor && parent == irScript
val isInScriptConstructor = this@transformFunctionChildren is IrConstructor && (parent == irScript || parent == irScriptClass)
val dataForChildren =
when {
newDispatchReceiverParameter == null -> data
@@ -842,25 +865,30 @@ private class ScriptToClassTransformer(
}
override fun visitGetValue(expression: IrGetValue, data: ScriptToClassTransformerContext): IrExpression {
val getVar = expression.symbol.owner as? IrVariable
if (getVar != null) {
if (irScript.explicitCallParameters.contains(getVar)) {
val correspondingParam = irScriptClass.constructors.single().valueParameters.find {
it.origin == IrDeclarationOrigin.SCRIPT_CALL_PARAMETER && it.name == getVar.name
} ?: error("script explicit parameter ${getVar.name.asString()} not found")
val correspondingVariable = expression.symbol.owner as? IrVariable
val correspondingValueParameter = expression.symbol.owner as? IrValueParameter
when {
correspondingVariable != null && irScript.explicitCallParameters.contains(correspondingVariable) -> {
val builder = context.createIrBuilder(expression.symbol)
val newExpression =
IrGetValueImpl(
expression.startOffset, expression.endOffset,
correspondingParam.type, correspondingParam.symbol,
expression.origin
)
if (data.isInScriptConstructor) {
val correspondingCtorParam = irScriptClass.constructors.single().valueParameters.find {
it.origin == IrDeclarationOrigin.SCRIPT_CALL_PARAMETER && it.name == correspondingVariable.name
} ?: error("script explicit parameter ${correspondingVariable.name.asString()} not found")
builder.irGet(correspondingCtorParam.type, correspondingCtorParam.symbol)
} else {
val correspondingField = irScriptClass.declarations.find {
it is IrField && it.origin == IrDeclarationOrigin.SCRIPT_CALL_PARAMETER && it.name == correspondingVariable.name
} ?: error("script explicit parameter ${correspondingVariable.name.asString()} corresponding property not found")
val scriptReceiver =
getAccessCallForScriptInstance(data, expression.startOffset, expression.endOffset, expression.origin, null)
builder.irGetField(scriptReceiver, correspondingField as IrField)
}
return super.visitExpression(newExpression, data)
}
} else if (irScript.needsReceiverProcessing) {
val getValueParameter = expression.symbol.owner as? IrValueParameter
if (getValueParameter != null && isValidNameForReceiver(getValueParameter.name)) {
correspondingValueParameter != null && irScript.needsReceiverProcessing && isValidNameForReceiver(correspondingValueParameter.name) -> {
val newExpression = getDispatchReceiverExpression(
data, expression, getValueParameter.type, expression.origin, getValueParameter
data, expression, correspondingValueParameter.type, expression.origin, correspondingValueParameter
)
if (newExpression != null) {
return super.visitExpression(newExpression, data)
@@ -945,11 +973,9 @@ private class ScriptFixLambdasTransformer(val irScriptClass: IrClass) : IrElemen
}
private class ScriptsToClassesSymbolRemapper(
val scriptsToClasses: Map<IrScript, IrClass>
) : SymbolRemapper.Empty() {
private class ScriptsToClassesSymbolRemapper : SymbolRemapper.Empty() {
override fun getReferencedClassifier(symbol: IrClassifierSymbol): IrClassifierSymbol =
(symbol.owner as? IrScript)?.let { scriptsToClasses[it] }?.symbol ?: symbol
(symbol.owner as? IrScript)?.targetClass ?: symbol
}
private inline fun IrClass.addAnonymousInitializer(builder: IrFunctionBuilder.() -> Unit = {}): IrAnonymousInitializer =
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_K2: JVM_IR
// param: 10