tagged unions

This commit is contained in:
2026-06-28 22:54:20 +02:00
parent f328c44154
commit 981ccb047a
7 changed files with 396 additions and 14 deletions
+36 -4
View File
@@ -711,9 +711,27 @@ emit_instruction_stream :: proc(
}
fmt.sbprintf(&emitter.builder, " %%union_slot%d = alloca %s, align %d\n", instruction_index, type_name, types.alignment_of(instruction.type, &emitter.module.types, emitter.module.target))
fmt.sbprintf(&emitter.builder, " store %s zeroinitializer, ptr %%union_slot%d\n", type_name, instruction_index)
// A tagged union writes the discriminant (the tag enum member matching the
// active variant's name) at offset 0, then the payload after it; untagged
// unions write the payload at offset 0.
payload_ptr := fmt.tprintf("%%union_slot%d", instruction_index)
tag_enum := types.union_tag_enum(instruction.type, &emitter.module.types)
if types.is_valid(tag_enum) {
tag_value: i128 = 0
for member in types.enum_members_for(&emitter.module.types, tag_enum) {
if member.name == fields[field_index].name {
tag_value = member.value
break
}
}
fmt.sbprintf(&emitter.builder, " store %s %d, ptr %%union_slot%d\n", llvm_type(tag_enum, &emitter.module.types), tag_value, instruction_index)
offset := types.union_payload_offset(instruction.type, &emitter.module.types, emitter.module.target)
fmt.sbprintf(&emitter.builder, " %%union_payload%d = getelementptr i8, ptr %%union_slot%d, i64 %d\n", instruction_index, instruction_index, offset)
payload_ptr = fmt.tprintf("%%union_payload%d", instruction_index)
}
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(fields[field_index].type, &emitter.module.types))
write_operand(&emitter.builder, instructions, instruction.args[0], fields[field_index].type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", ptr %%union_slot%d\n", instruction_index)
fmt.sbprintf(&emitter.builder, ", ptr %s\n", payload_ptr)
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%union_slot%d\n", instruction_index, type_name, instruction_index)
continue
}
@@ -941,7 +959,7 @@ emit_instruction_stream :: proc(
continue
}
if types.is_union(base_type, &emitter.module.types) {
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr i8, ptr %%v%d, i64 0\n", instruction_index, instruction.a)
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr i8, ptr %%v%d, i64 %d\n", instruction_index, instruction.a, types.union_payload_offset(base_type, &emitter.module.types, emitter.module.target))
} else {
fmt.sbprintf(
&emitter.builder,
@@ -1804,10 +1822,24 @@ emit_types :: proc(emitter: ^Emitter) {
fmt.sbprintf(&emitter.builder, "[%d x i8]\n", total_size)
continue
}
// A tagged union lays out `{ tag, [pad], carrier, [pad] }`; the explicit i8
// padding makes the LLVM type's size and field offsets match the byte offsets
// used by construction and field access. Untagged unions have offset 0 and no tag.
payload_offset := types.union_payload_offset(id, &emitter.module.types, emitter.module.target)
strings.write_string(&emitter.builder, "{ ")
if types.is_tagged_union(id, &emitter.module.types) {
tag_type := types.union_tag_enum(id, &emitter.module.types)
tag_size := types.size(tag_type, &emitter.module.types, emitter.module.target)
strings.write_string(&emitter.builder, llvm_type(tag_type, &emitter.module.types))
if payload_offset > tag_size {
fmt.sbprintf(&emitter.builder, ", [%d x i8]", payload_offset-tag_size)
}
strings.write_string(&emitter.builder, ", ")
}
strings.write_string(&emitter.builder, llvm_type(carrier, &emitter.module.types))
if carrier_size < total_size {
fmt.sbprintf(&emitter.builder, ", [%d x i8]", total_size-carrier_size)
used := payload_offset + carrier_size
if used < total_size {
fmt.sbprintf(&emitter.builder, ", [%d x i8]", total_size-used)
}
strings.write_string(&emitter.builder, " }\n")
continue