martmists
11/10/2025, 4:40 PMCaused by: java.lang.AssertionError: Unexpected IR element found during code generation. Either code generation for it is not implemented, or it should have been lowered:
FUN name:<anonymous> visibility:public modality:FINAL <> (it:kotlin.Any) returnType:kotlin.Any
How do I fix this? I'm trying to build a lambda, and in the parent declaration (see thread) the generated IR largely seems fine to me.martmists
11/10/2025, 4:40 PMCONSTRUCTOR GENERATED[com.martmists.serialization.CodecFIRGenerator.GeneratedForCodec] visibility:private returnType:com.martmists.serialization.TestData.Companion [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in kotlin.Any'
BLOCK type=kotlin.Unit origin=null
SET_FIELD 'FIELD name:CODEC type:kotlin.Any visibility:public declared in com.martmists.serialization.TestData.Companion' type=kotlin.Unit origin=INITIALIZE_FIELD
receiver: GET_VAR '<this>: com.martmists.serialization.TestData.Companion declared in com.martmists.serialization.TestData.Companion' type=com.martmists.serialization.TestData.Companion origin=null
value: CALL 'public open fun create <O> (p0: @[FlexibleNullability] java.util.function.Function<@[FlexibleNullability] com.mojang.serialization.codecs.RecordCodecBuilder.Instance<@[FlexibleNullability] O of com.mojang.serialization.codecs.RecordCodecBuilder.create?>?, out @[FlexibleNullability] <http://com.mojang.datafixers.kinds.App|com.mojang.datafixers.kinds.App><@[FlexibleNullability] <http://com.mojang.serialization.codecs.RecordCodecBuilder.Mu|com.mojang.serialization.codecs.RecordCodecBuilder.Mu><@[FlexibleNullability] O of com.mojang.serialization.codecs.RecordCodecBuilder.create?>?, @[FlexibleNullability] O of com.mojang.serialization.codecs.RecordCodecBuilder.create?>?>?): @[FlexibleNullability] com.mojang.serialization.Codec<@[FlexibleNullability] O of com.mojang.serialization.codecs.RecordCodecBuilder.create?>? declared in com.mojang.serialization.codecs.RecordCodecBuilder' type=@[FlexibleNullability] com.mojang.serialization.Codec<@[FlexibleNullability] O of com.mojang.serialization.codecs.RecordCodecBuilder.create?>? origin=null
TYPE_ARG O: com.martmists.serialization.TestData
ARG p0: BLOCK type=kotlin.Any origin=LAMBDA
FUN name:<anonymous> visibility:public modality:FINAL returnType:kotlin.Any
VALUE_PARAMETER kind:Regular name:it index:0 type:kotlin.Any
EXPRESSION_BODY
CALL 'public open fun apply <R> (p0: @[FlexibleNullability] com.mojang.datafixers.kinds.Applicative<@[FlexibleNullability] F of com.mojang.datafixers.Products.P2?, *>?, p1: @[FlexibleNullability] java.util.function.BiFunction<@[FlexibleNullability] T1 of com.mojang.datafixers.Products.P2?, @[FlexibleNullability] T2 of com.mojang.datafixers.Products.P2?, @[FlexibleNullability] R of com.mojang.datafixers.Products.P2.apply?>?): @[FlexibleNullability] <http://com.mojang.datafixers.kinds.App|com.mojang.datafixers.kinds.App><@[FlexibleNullability] F of com.mojang.datafixers.Products.P2?, @[FlexibleNullability] R of com.mojang.datafixers.Products.P2.apply?>? declared in com.mojang.datafixers.Products.P2' type=@[FlexibleNullability] <http://com.mojang.datafixers.kinds.App|com.mojang.datafixers.kinds.App><@[FlexibleNullability] F of com.mojang.datafixers.Products.P2?, @[FlexibleNullability] R of com.mojang.datafixers.Products.P2.apply?>? origin=null
TYPE_ARG R: com.martmists.serialization.TestData
ARG <this>: CALL 'public open fun group <T1, T2> (p0: @[FlexibleNullability] <http://com.mojang.datafixers.kinds.App|com.mojang.datafixers.kinds.App><@[FlexibleNullability] <http://com.mojang.serialization.codecs.RecordCodecBuilder.Mu|com.mojang.serialization.codecs.RecordCodecBuilder.Mu><@[FlexibleNullability] O of com.mojang.serialization.codecs.RecordCodecBuilder.Instance?>?, @[FlexibleNullability] T1 of com.mojang.serialization.codecs.RecordCodecBuilder.Instance.group?>?, p1: @[FlexibleNullability] <http://com.mojang.datafixers.kinds.App|com.mojang.datafixers.kinds.App><@[FlexibleNullability] <http://com.mojang.serialization.codecs.RecordCodecBuilder.Mu|com.mojang.serialization.codecs.RecordCodecBuilder.Mu><@[FlexibleNullability] O of com.mojang.serialization.codecs.RecordCodecBuilder.Instance?>?, @[FlexibleNullability] T2 of com.mojang.serialization.codecs.RecordCodecBuilder.Instance.group?>?): @[FlexibleNullability] com.mojang.datafixers.Products.P2<@[FlexibleNullability] <http://com.mojang.serialization.codecs.RecordCodecBuilder.Mu|com.mojang.serialization.codecs.RecordCodecBuilder.Mu><@[FlexibleNullability] O of com.mojang.serialization.codecs.RecordCodecBuilder.Instance?>?, @[FlexibleNullability] T1 of com.mojang.serialization.codecs.RecordCodecBuilder.Instance.group?, @[FlexibleNullability] T2 of com.mojang.serialization.codecs.RecordCodecBuilder.Instance.group?>? [fake_override] declared in com.mojang.serialization.codecs.RecordCodecBuilder.Instance' type=@[FlexibleNullability] com.mojang.datafixers.Products.P2<@[FlexibleNullability] <http://com.mojang.serialization.codecs.RecordCodecBuilder.Mu|com.mojang.serialization.codecs.RecordCodecBuilder.Mu><@[FlexibleNullability] O of com.mojang.serialization.codecs.RecordCodecBuilder.Instance?>?, @[FlexibleNullability] T1 of com.mojang.serialization.codecs.RecordCodecBuilder.Instance.group?, @[FlexibleNullability] T2 of com.mojang.serialization.codecs.RecordCodecBuilder.Instance.group?>? origin=null
TYPE_ARG T1: <http://kotlin.Int|kotlin.Int>
TYPE_ARG T2: kotlin.String
ARG <this>: GET_VAR 'it: kotlin.Any declared in com.martmists.serialization.TestData.Companion.<init>.<anonymous>' type=kotlin.Any origin=null
ARG p0: CALL 'public final fun forGetter <O> (p0: @[FlexibleNullability] java.util.function.Function<@[FlexibleNullability] O of com.mojang.serialization.MapCodec.forGetter?, @[FlexibleNullability] A of com.mojang.serialization.MapCodec?>?): @[FlexibleNullability] com.mojang.serialization.codecs.RecordCodecBuilder<@[FlexibleNullability] O of com.mojang.serialization.MapCodec.forGetter?, @[FlexibleNullability] A of com.mojang.serialization.MapCodec?>? declared in com.mojang.serialization.MapCodec' type=@[FlexibleNullability] com.mojang.serialization.codecs.RecordCodecBuilder<@[FlexibleNullability] O of com.mojang.serialization.MapCodec.forGetter?, @[FlexibleNullability] A of com.mojang.serialization.MapCodec?>? origin=null
TYPE_ARG O: com.martmists.serialization.TestData
ARG <this>: CALL 'public open fun fieldOf (p0: @[FlexibleNullability] kotlin.String?): @[FlexibleNullability] com.mojang.serialization.MapCodec<@[FlexibleNullability] A of com.mojang.serialization.Codec?>? declared in com.mojang.serialization.Codec' type=@[FlexibleNullability] com.mojang.serialization.MapCodec<@[FlexibleNullability] A of com.mojang.serialization.Codec?>? origin=null
ARG <this>: GET_FIELD 'FIELD IR_EXTERNAL_JAVA_DECLARATION_STUB name:INT type:@[FlexibleNullability] com.mojang.serialization.codecs.PrimitiveCodec<@[FlexibleNullability] kotlin.Int?>? visibility:public [final,static] declared in com.mojang.serialization.Codec' type=@[FlexibleNullability] com.mojang.serialization.codecs.PrimitiveCodec<@[FlexibleNullability] kotlin.Int?>? origin=null
ARG p0: CONST String type=kotlin.String value="x"
ARG p0: RAW_FUNCTION_REFERENCE 'public final fun <get-x> (): <http://kotlin.Int|kotlin.Int> declared in com.martmists.serialization.TestData' type=kotlin.Any
ARG p1: CALL 'public final fun forGetter <O> (p0: @[FlexibleNullability] java.util.function.Function<@[FlexibleNullability] O of com.mojang.serialization.MapCodec.forGetter?, @[FlexibleNullability] A of com.mojang.serialization.MapCodec?>?): @[FlexibleNullability] com.mojang.serialization.codecs.RecordCodecBuilder<@[FlexibleNullability] O of com.mojang.serialization.MapCodec.forGetter?, @[FlexibleNullability] A of com.mojang.serialization.MapCodec?>? declared in com.mojang.serialization.MapCodec' type=@[FlexibleNullability] com.mojang.serialization.codecs.RecordCodecBuilder<@[FlexibleNullability] O of com.mojang.serialization.MapCodec.forGetter?, @[FlexibleNullability] A of com.mojang.serialization.MapCodec?>? origin=null
TYPE_ARG O: com.martmists.serialization.TestData
ARG <this>: CALL 'public open fun fieldOf (p0: @[FlexibleNullability] kotlin.String?): @[FlexibleNullability] com.mojang.serialization.MapCodec<@[FlexibleNullability] A of com.mojang.serialization.Codec?>? declared in com.mojang.serialization.Codec' type=@[FlexibleNullability] com.mojang.serialization.MapCodec<@[FlexibleNullability] A of com.mojang.serialization.Codec?>? origin=null
ARG <this>: GET_FIELD 'FIELD IR_EXTERNAL_JAVA_DECLARATION_STUB name:STRING type:@[FlexibleNullability] com.mojang.serialization.codecs.PrimitiveCodec<@[FlexibleNullability] kotlin.String?>? visibility:public [final,static] declared in com.mojang.serialization.Codec' type=@[FlexibleNullability] com.mojang.serialization.codecs.PrimitiveCodec<@[FlexibleNullability] kotlin.String?>? origin=null
ARG p0: CONST String type=kotlin.String value="y"
ARG p0: RAW_FUNCTION_REFERENCE 'public final fun <get-y> (): kotlin.String declared in com.martmists.serialization.TestData' type=kotlin.Any
FUNCTION_REFERENCE 'public final fun <anonymous> (it: kotlin.Any): kotlin.Any declared in com.martmists.serialization.TestData.Companion.<init>' type=kotlin.Any origin=LAMBDA reflectionTarget=nullmartmists
11/10/2025, 4:41 PM@OptIn(UnsafeDuringIrConstructionAPI::class)
class CodecIRGenerator : IrGenerationExtension {
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
val recordAnnotation = FqName("com.martmists.serialization.Record")
moduleFragment.files.forEach { file ->
file.declarations.filterIsInstance<IrClass>().forEach { klass ->
if (!klass.annotations.hasAnnotation(recordAnnotation)) return@forEach
if (!klass.isData) {
pluginContext.reportError(
klass,
"@Record can only be applied to data classes"
)
return@forEach
}
val companion = klass.declarations.filterIsInstance<IrClass>().firstOrNull { it.isCompanion } ?: createCompanionObject(klass, pluginContext)
if (companion.declarations.any { it is IrField && it.name.asString() == "CODEC" }) {
pluginContext.reportError(
companion,
"@Record classes should not have a CODEC field"
)
}
addCodecField(klass, companion, pluginContext)
}
}
}
private fun createCompanionObject(
klass: IrClass,
pluginContext: IrPluginContext
): IrClass {
klass.companionObject()
val companion = pluginContext.irFactory.buildClass {
startOffset = klass.startOffset
endOffset = klass.endOffset
name = Name.identifier("Companion")
isCompanion = true
kind = ClassKind.OBJECT
}
companion.createThisReceiverParameter()
companion.addConstructor {
isPrimary = true
}.apply {
body = pluginContext.irFactory.createExpressionBody(
companion.startOffset,
companion.endOffset,
IrBlockBuilder(pluginContext, Scope(symbol), klass.startOffset, klass.endOffset).buildStatement(
klass.startOffset,
klass.endOffset
) {
irReturnUnit()
}
)
}
companion.parent = klass
klass.declarations.add(companion)
return companion
}
private fun addCodecField(
klass: IrClass,
companion: IrClass,
pluginContext: IrPluginContext,
) {
val ctor = klass.primaryConstructor ?: run {
pluginContext.reportError(klass, "@Record requires a primary constructor")
return
}
val field = pluginContext.irFactory.buildField {
startOffset = klass.startOffset
endOffset = klass.endOffset
name = Name.identifier("CODEC")
type = pluginContext.irBuiltIns.anyType
}
val codecBuilderKlass = pluginContext.referenceClass(
ClassId(
FqName("com.mojang.serialization.codecs"),
Name.identifier("RecordCodecBuilder")
)
)!!
val codecBuilderInstanceKlass = pluginContext.referenceClass(
ClassId(
FqName("com.mojang.serialization.codecs"),
FqName("RecordCodecBuilder.Instance"),
false,
)
)!!
val codecKlass = pluginContext.referenceClass(
ClassId(
FqName("com.mojang.serialization"),
Name.identifier("Codec"),
)
)!!
val mapCodecKlass = pluginContext.referenceClass(
ClassId(
FqName("com.mojang.serialization"),
Name.identifier("MapCodec"),
)
)!!
val appKlass = pluginContext.referenceClass(
ClassId(
FqName("com.mojang.datafixers"),
FqName("Products.P${ctor.nonDispatchParameters.size}"),
false,
)
)!!
val create = codecBuilderKlass.functions.first { it.owner.name.asString() == "create" }
val group = codecBuilderInstanceKlass.functions.first {
it.owner.name.asString() == "group" && it.owner.nonDispatchParameters.size == ctor.parameters.size
}
val fieldOf = codecKlass.functions.first { it.owner.name.asString() == "fieldOf" }
val optionalFieldOf = codecKlass.functions.first { it.owner.name.asString() == "optionalFieldOf" }
val forGetter = mapCodecKlass.functions.first { it.owner.name.asString() == "forGetter" }
val apply = appKlass.functions.first { it.owner.name.asString() == "apply" }
field.parent = companion
field.initializer = pluginContext.irFactory.createExpressionBody(
IrBlockBuilder(pluginContext, Scope(companion.symbol), klass.startOffset, klass.endOffset).buildStatement(
klass.startOffset,
klass.endOffset
) {
irCall(create).apply {
typeArguments[0] = klass.defaultType
val lambda = pluginContext.irFactory.addFunction(
companion,
) {
returnType = pluginContext.irBuiltIns.anyType
name = Name.special("<anonymous>")
}
val param = pluginContext.irFactory.buildValueParameter(
IrValueParameterBuilder().apply {
type = pluginContext.irBuiltIns.anyType
name = Name.identifier("it")
},
lambda,
)
lambda.parent = companion
lambda.parameters = listOf(param)
lambda.body = pluginContext.irFactory.createExpressionBody(companion.startOffset, companion.endOffset, IrBlockBuilder(pluginContext, Scope(lambda.symbol), klass.startOffset, klass.endOffset).buildStatement(companion.startOffset, companion.endOffset) {
val builder = irGet(param)
val product = irCall(group).apply {
dispatchReceiver = builder
ctor.nonDispatchParameters.forEachIndexed { index, arg ->
typeArguments[index] = arg.type
}
arguments.addAll(1,
ctor.nonDispatchParameters.map { arg ->
val codec: IrExpression = when {
arg.type == pluginContext.irBuiltIns.intType -> {
irGetField(
null,
codecKlass.owner.declarations.filterIsInstance<IrProperty>()
.first { it.name.asString() == "INT" }.backingField!!
)
}
arg.type == pluginContext.irBuiltIns.stringType -> {
irGetField(
null,
codecKlass.owner.declarations.filterIsInstance<IrProperty>()
.first { it.name.asString() == "STRING" }.backingField!!
)
}
arg.type.erasedUpperBound == pluginContext.irBuiltIns.listClass -> {
TODO()
}
else -> TODO()
}
val field = if (arg.hasDefaultValue()) {
irCall(optionalFieldOf).apply {
dispatchReceiver = codec
arguments.addAll(1,
listOf(
irString(arg.name.asString()),
arg.defaultValue!!.expression,
)
)
}
} else {
irCall(fieldOf).apply {
dispatchReceiver = codec
arguments.addAll(1,
listOf(
irString(arg.name.asString()),
)
)
}
}
irCall(forGetter).apply {
typeArguments[0] = klass.defaultType
dispatchReceiver = field
arguments.addAll(1,
listOf(
irRawFunctionReference(
pluginContext.irBuiltIns.anyType,
klass.properties.first { it.name == arg.name }.getter!!.symbol
)
)
)
}
}
)
}
irCall(apply).apply {
typeArguments[0] = klass.defaultType
dispatchReceiver = product
arguments.addAll(
listOf(
builder,
irRawFunctionReference(pluginContext.irBuiltIns.anyType, ctor.symbol)
)
)
}
})
// TODO: Find the correct irFactory method for this
@Suppress("INVISIBLE_REFERENCE")
arguments[0] = IrFunctionExpressionImpl(null, companion.startOffset, companion.endOffset, pluginContext.irBuiltIns.anyType, IrStatementOrigin.LAMBDA, lambda)
}
}
)
companion.declarations.add(field)
}
private fun IrPluginContext.reportError(target: IrDeclaration, message: String) {
// TODO: <http://diagnosticReporter.at|diagnosticReporter.at>(target).report()
throw Exception(message)
}
}Youssef Shoaib [MOD]
11/10/2025, 5:17 PMmartmists
11/10/2025, 5:19 PMYoussef Shoaib [MOD]
11/10/2025, 5:20 PMIrElement.dump() or something like that. Or just use the debugger and view the IR tree in your IrGenerationExtensionmartmists
11/10/2025, 5:20 PMYoussef Shoaib [MOD]
11/10/2025, 5:22 PMmartmists
11/10/2025, 5:33 PMFUNCTION_REFERENCE 'public final fun <anonymous> (it: com.mojang.serialization.codecs.RecordCodecBuilder.Instance<com.martmists.serialization.TestData>): com.mojang.datafixers.Products.P2<F of com.mojang.datafixers.Products.P2, T1 of com.mojang.datafixers.Products.P2, T2 of com.mojang.datafixers.Products.P2> declared in com.martmists.serialization.TestData.Companion.<init>' type=kotlin.Any origin=LAMBDA reflectionTarget=null
but I don't know how to pass it as parameter without the FUNCTION_REFERENCE being addedmartmists
11/10/2025, 8:50 PMYoussef Shoaib [MOD]
11/10/2025, 8:52 PMmartmists
11/19/2025, 6:27 PMmartmists
11/20/2025, 8:32 PM