error handling kickoff

This commit is contained in:
2026-06-29 22:55:56 +02:00
parent 98c303d22c
commit ae5af37b85
14 changed files with 1229 additions and 173 deletions
+117 -4
View File
@@ -163,7 +163,7 @@ llvm_type :: proc(value: types.Type, store: ^types.Store = nil) -> string {
return "ptr"
}
return fmt.tprintf("{{ i1, %s }}", llvm_type(item.child, store))
case .Struct, .Union:
case .Struct, .Union, .Fallible:
return fmt.tprintf("%%bro.type.%d", resolved)
}
selected := store.selected if store != nil else target.DEFAULT
@@ -256,7 +256,7 @@ valid_value :: proc(
.Load_Global, .Function_Address, .Address_Of, .Load, .Union_Tag, .Slice, .Length, .Slice_Ptr,
.Extract, .Select, .Unwrap,
.Optional_Is_Some, .Optional_Value, .Orelse,
.Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
.Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
.Neg_Checked, .Add_Checked, .Sub_Checked, .Mul_Checked, .Div_Checked, .Pointer_Add, .Not, .Compare, .Call:
return true
case .Address_Global, .Alloca, .Index_Address, .Field_Address, .Orelse_Begin,
@@ -690,6 +690,8 @@ emit_instruction_stream :: proc(
expected_count = int(item.field_count)
} else if ok && item.kind == .Union {
expected_count = 1
} else if ok && item.kind == .Fallible {
expected_count = 1
} else if ok && item.kind == .Range {
expected_count = 3
} else {
@@ -701,6 +703,56 @@ emit_instruction_stream :: proc(
continue
}
type_name := llvm_type(instruction.type, &emitter.module.types)
if item.kind == .Fallible {
success := item.child
error_type := item.extra
error_path := instruction.integer != 0
payload_offset := types.fallible_payload_offset(instruction.type, &emitter.module.types, emitter.module.target)
fmt.sbprintf(&emitter.builder, " %%fallible_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 %%fallible_slot%d\n", type_name, instruction_index)
if !error_path {
fmt.sbprintf(&emitter.builder, " store i16 0, ptr %%fallible_slot%d\n", instruction_index)
if !types.is_void(success) {
if !valid_value(instructions, instruction.args[0], success, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid fallible success operand")
continue
}
fmt.sbprintf(&emitter.builder, " %%fallible_payload%d = getelementptr i8, ptr %%fallible_slot%d, i64 %d\n", instruction_index, instruction_index, payload_offset)
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(success, &emitter.module.types))
write_operand(&emitter.builder, instructions, instruction.args[0], success, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", ptr %%fallible_payload%d\n", instruction_index)
}
} else if types.is_enum(error_type, &emitter.module.types) {
if !valid_value(instructions, instruction.args[0], error_type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid fallible enum error operand")
continue
}
fmt.sbprintf(&emitter.builder, " store i16 ")
write_operand(&emitter.builder, instructions, instruction.args[0], error_type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", ptr %%fallible_slot%d\n", instruction_index)
} else if types.is_tagged_union(error_type, &emitter.module.types) {
if !valid_value(instructions, instruction.args[0], error_type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid fallible union error operand")
continue
}
error_slot_align := types.alignment_of(error_type, &emitter.module.types, emitter.module.target)
fmt.sbprintf(&emitter.builder, " %%fallible_error_slot%d = alloca %s, align %d\n", instruction_index, llvm_type(error_type, &emitter.module.types), error_slot_align)
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(error_type, &emitter.module.types))
write_operand(&emitter.builder, instructions, instruction.args[0], error_type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", ptr %%fallible_error_slot%d\n", instruction_index)
fmt.sbprintf(&emitter.builder, " %%fallible_error_code%d = load i16, ptr %%fallible_error_slot%d\n", instruction_index, instruction_index)
fmt.sbprintf(&emitter.builder, " store i16 %%fallible_error_code%d, ptr %%fallible_slot%d\n", instruction_index, instruction_index)
error_payload_offset := types.union_payload_offset(error_type, &emitter.module.types, emitter.module.target)
error_payload_size := types.sum_payload_size(error_type, &emitter.module.types, emitter.module.target)
if error_payload_size > 0 {
fmt.sbprintf(&emitter.builder, " %%fallible_error_payload%d = getelementptr i8, ptr %%fallible_error_slot%d, i64 %d\n", instruction_index, instruction_index, error_payload_offset)
fmt.sbprintf(&emitter.builder, " %%fallible_payload%d = getelementptr i8, ptr %%fallible_slot%d, i64 %d\n", instruction_index, instruction_index, payload_offset)
fmt.sbprintf(&emitter.builder, " call void @llvm.memcpy.p0.p0.i64(ptr %%fallible_payload%d, ptr %%fallible_error_payload%d, i64 %d, i1 false)\n", instruction_index, instruction_index, error_payload_size)
}
}
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%fallible_slot%d\n", instruction_index, type_name, instruction_index)
continue
}
if item.kind == .Union {
fields := types.fields_for(&emitter.module.types, instruction.type)
field_index := int(instruction.integer)
@@ -958,6 +1010,10 @@ emit_instruction_stream :: proc(
if types.is_pointer(base_type, &emitter.module.types) {
base_type = types.child_type(base_type, &emitter.module.types)
}
if types.kind(base_type, &emitter.module.types) == .Fallible {
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr i8, ptr %%v%d, i64 %d\n", instruction_index, instruction.a, types.fallible_payload_offset(base_type, &emitter.module.types, emitter.module.target))
continue
}
fields := types.fields_for(&emitter.module.types, base_type)
field_index := int(instruction.integer)
if field_index < 0 || field_index >= len(fields) ||
@@ -1254,6 +1310,45 @@ emit_instruction_stream :: proc(
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, operation, llvm_type(from_type, &emitter.module.types))
write_operand(&emitter.builder, instructions, instruction.a, from_type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type, &emitter.module.types))
case .Sum_Widen:
if !valid_instruction(instructions, instruction.a) ||
!types.can_sum_widen(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid sum widening operand")
continue
}
from_type := instructions[instruction.a].type
if types.is_enum(from_type, &emitter.module.types) && types.is_enum(instruction.type, &emitter.module.types) {
type_name := llvm_type(instruction.type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, %s ", instruction_index, type_name)
write_operand(&emitter.builder, instructions, instruction.a, from_type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", %s zeroinitializer\n", type_name)
continue
}
if types.is_tagged_union(from_type, &emitter.module.types) && types.is_tagged_union(instruction.type, &emitter.module.types) {
from_name := llvm_type(from_type, &emitter.module.types)
to_name := llvm_type(instruction.type, &emitter.module.types)
from_align := types.alignment_of(from_type, &emitter.module.types, emitter.module.target)
to_align := types.alignment_of(instruction.type, &emitter.module.types, emitter.module.target)
fmt.sbprintf(&emitter.builder, " %%sum_from_slot%d = alloca %s, align %d\n", instruction_index, from_name, from_align)
fmt.sbprintf(&emitter.builder, " store %s ", from_name)
write_operand(&emitter.builder, instructions, instruction.a, from_type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", ptr %%sum_from_slot%d\n", instruction_index)
fmt.sbprintf(&emitter.builder, " %%sum_to_slot%d = alloca %s, align %d\n", instruction_index, to_name, to_align)
fmt.sbprintf(&emitter.builder, " store %s zeroinitializer, ptr %%sum_to_slot%d\n", to_name, instruction_index)
fmt.sbprintf(&emitter.builder, " %%sum_tag%d = load i16, ptr %%sum_from_slot%d\n", instruction_index, instruction_index)
fmt.sbprintf(&emitter.builder, " store i16 %%sum_tag%d, ptr %%sum_to_slot%d\n", instruction_index, instruction_index)
from_payload_offset := types.union_payload_offset(from_type, &emitter.module.types, emitter.module.target)
to_payload_offset := types.union_payload_offset(instruction.type, &emitter.module.types, emitter.module.target)
payload_size := types.sum_payload_size(from_type, &emitter.module.types, emitter.module.target)
if payload_size > 0 {
fmt.sbprintf(&emitter.builder, " %%sum_from_payload%d = getelementptr i8, ptr %%sum_from_slot%d, i64 %d\n", instruction_index, instruction_index, from_payload_offset)
fmt.sbprintf(&emitter.builder, " %%sum_to_payload%d = getelementptr i8, ptr %%sum_to_slot%d, i64 %d\n", instruction_index, instruction_index, to_payload_offset)
fmt.sbprintf(&emitter.builder, " call void @llvm.memcpy.p0.p0.i64(ptr %%sum_to_payload%d, ptr %%sum_from_payload%d, i64 %d, i1 false)\n", instruction_index, instruction_index, payload_size)
}
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%sum_to_slot%d\n", instruction_index, to_name, instruction_index)
continue
}
emit_recovery_value(emitter, instruction_index, instruction, "unsupported sum widening operand")
case .C_Coerce:
if !valid_instruction(instructions, instruction.a) ||
!types.can_coerce_c_integer(instructions[instruction.a].type, instruction.type) {
@@ -1814,7 +1909,7 @@ emit_globals :: proc(emitter: ^Emitter) {
emit_types :: proc(emitter: ^Emitter) {
for item, index in emitter.module.types.nodes {
if item.kind != .Struct && item.kind != .Union {
if item.kind != .Struct && item.kind != .Union && item.kind != .Fallible {
continue
}
id := types.DYNAMIC_START+types.Type(index)
@@ -1823,6 +1918,24 @@ emit_types :: proc(emitter: ^Emitter) {
strings.write_string(&emitter.builder, "opaque\n")
continue
}
if item.kind == .Fallible {
payload_offset := types.fallible_payload_offset(id, &emitter.module.types, emitter.module.target)
payload_size := types.fallible_payload_size(id, &emitter.module.types, emitter.module.target)
total_size := types.size(id, &emitter.module.types, emitter.module.target)
strings.write_string(&emitter.builder, "{ i16")
if payload_offset > 2 {
fmt.sbprintf(&emitter.builder, ", [%d x i8]", payload_offset-2)
}
if payload_size > 0 {
fmt.sbprintf(&emitter.builder, ", [%d x i8]", payload_size)
}
used := payload_offset + payload_size
if used < total_size {
fmt.sbprintf(&emitter.builder, ", [%d x i8]", total_size-used)
}
strings.write_string(&emitter.builder, " }\n")
continue
}
if item.kind == .Union {
fields := types.fields_for(&emitter.module.types, id)
carrier := types.INVALID
@@ -1839,7 +1952,7 @@ emit_types :: proc(emitter: ^Emitter) {
}
}
total_size := types.size(id, &emitter.module.types, emitter.module.target)
if !types.is_valid(carrier) {
if !types.is_valid(carrier) || types.is_void(carrier) {
fmt.sbprintf(&emitter.builder, "[%d x i8]\n", total_size)
continue
}