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