aboutsummaryrefslogtreecommitdiff
path: root/base/runtime
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2026-01-26 18:23:29 +0000
committergingerBill <gingerBill@users.noreply.github.com>2026-01-26 18:23:29 +0000
commit3586bda6ae80b1dca1984ae54b023d5fec69067e (patch)
treed1b8bba874b42fae91e12f095f10ad017bef9b34 /base/runtime
parent467954bc7baed8061348b6dc5ddda1a742ea69f4 (diff)
Use `context.assertion_failure_proc` with type assertions when the `context` is available, otherwise use a trivial trap.
Diffstat (limited to 'base/runtime')
-rw-r--r--base/runtime/error_checks.odin159
-rw-r--r--base/runtime/print.odin331
2 files changed, 462 insertions, 28 deletions
diff --git a/base/runtime/error_checks.odin b/base/runtime/error_checks.odin
index 32a895c3f..79245fc0c 100644
--- a/base/runtime/error_checks.odin
+++ b/base/runtime/error_checks.odin
@@ -137,7 +137,22 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
when ODIN_NO_RTTI {
- type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) {
+ type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) {
+ if ok {
+ return
+ }
+ @(cold, no_instrumentation)
+ handle_error :: proc "odin" (file: string, line, column: i32) -> ! {
+ p := context.assertion_failure_proc
+ if p == nil {
+ p = default_assertion_failure_proc
+ }
+ p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""})
+ }
+ handle_error(file, line, column)
+ }
+
+ type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) {
if ok {
return
}
@@ -150,7 +165,7 @@ when ODIN_NO_RTTI {
handle_error(file, line, column)
}
- type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32) {
+ type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) {
if ok {
return
}
@@ -162,8 +177,53 @@ when ODIN_NO_RTTI {
}
handle_error(file, line, column)
}
+
+ type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) {
+ if ok {
+ return
+ }
+ @(cold, no_instrumentation)
+ handle_error :: proc "odin" (file: string, line, column: i32) -> ! {
+ p := context.assertion_failure_proc
+ if p == nil {
+ p = default_assertion_failure_proc
+ }
+ p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""})
+ }
+ handle_error(file, line, column)
+ }
} else {
- type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
+ @(private="file")
+ TYPE_ASSERTION_BUFFER_SIZE :: 1024
+
+ type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid) {
+ if ok {
+ return
+ }
+ @(cold, no_instrumentation)
+ handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid) -> ! {
+ do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to: typeid) -> bool {
+ try_copy_string(i, buf, "Invalid type assertion from ") or_return
+ try_copy_typeid(i, buf, from) or_return
+ try_copy_string(i, buf, " to ") or_return
+ try_copy_typeid(i, buf, to) or_return
+ return true
+ }
+
+ buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
+ i := 0
+ _ = do_msg(&i, buf[:], file, line, column, from, to)
+
+ p := context.assertion_failure_proc
+ if p == nil {
+ p = default_assertion_failure_proc
+ }
+ p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
+ }
+ handle_error(file, line, column, from, to)
+ }
+
+ type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
if ok {
return
}
@@ -180,42 +240,85 @@ when ODIN_NO_RTTI {
handle_error(file, line, column, from, to)
}
- type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
+ @(private="file")
+ type_assertion_variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
+ if id == nil || data == nil {
+ return id
+ }
+ ti := type_info_base(type_info_of(id))
+ #partial switch v in ti.variant {
+ case Type_Info_Any:
+ return (^any)(data).id
+ case Type_Info_Union:
+ if v.tag_type == nil {
+ if (^rawptr)(data)^ == nil {
+ return nil
+ }
+ return v.variants[0].id
+
+ }
+
+ tag_ptr := uintptr(data) + v.tag_offset
+ idx := 0
+ switch v.tag_type.size {
+ case 1: idx = int( (^u8)(tag_ptr)^); if !v.no_nil { idx -= 1 }
+ case 2: idx = int( (^u16)(tag_ptr)^); if !v.no_nil { idx -= 1 }
+ case 4: idx = int( (^u32)(tag_ptr)^); if !v.no_nil { idx -= 1 }
+ case 8: idx = int( (^u64)(tag_ptr)^); if !v.no_nil { idx -= 1 }
+ case 16: idx = int((^u128)(tag_ptr)^); if !v.no_nil { idx -= 1 }
+ }
+ if idx < 0 {
+ return nil
+ } else if idx < len(v.variants) {
+ return v.variants[idx].id
+ }
+ }
+ return id
+ }
+
+ type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
if ok {
return
}
- variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
- if id == nil || data == nil {
- return id
- }
- ti := type_info_base(type_info_of(id))
- #partial switch v in ti.variant {
- case Type_Info_Any:
- return (^any)(data).id
- case Type_Info_Union:
- tag_ptr := uintptr(data) + v.tag_offset
- idx := 0
- switch v.tag_type.size {
- case 1: idx = int((^u8)(tag_ptr)^) - 1
- case 2: idx = int((^u16)(tag_ptr)^) - 1
- case 4: idx = int((^u32)(tag_ptr)^) - 1
- case 8: idx = int((^u64)(tag_ptr)^) - 1
- case 16: idx = int((^u128)(tag_ptr)^) - 1
- }
- if idx < 0 {
- return nil
- } else if idx < len(v.variants) {
- return v.variants[idx].id
+ @(cold, no_instrumentation)
+ handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
+ do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to, actual: typeid) -> bool {
+ try_copy_string(i, buf, "Invalid type assertion from ") or_return
+ try_copy_typeid(i, buf, from) or_return
+ try_copy_string(i, buf, " to ") or_return
+ try_copy_typeid(i, buf, to) or_return
+ if actual != from {
+ try_copy_string(i, buf, ", actual type: ") or_return
+ try_copy_typeid(i, buf, actual) or_return
}
+ return true
}
- return id
+
+ actual := type_assertion_variant_type(from, from_data)
+
+ buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
+ i := 0
+ _ = do_msg(&i, buf[:], file, line, column, from, to, actual)
+
+ p := context.assertion_failure_proc
+ if p == nil {
+ p = default_assertion_failure_proc
+ }
+ p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
+ }
+ handle_error(file, line, column, from, to, from_data)
+ }
+
+ type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
+ if ok {
+ return
}
@(cold, no_instrumentation)
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
- actual := variant_type(from, from_data)
+ actual := type_assertion_variant_type(from, from_data)
print_caller_location(Source_Code_Location{file, line, column, ""})
print_string(" Invalid type assertion from ")
diff --git a/base/runtime/print.odin b/base/runtime/print.odin
index 4ef142037..4a66f9743 100644
--- a/base/runtime/print.odin
+++ b/base/runtime/print.odin
@@ -502,3 +502,334 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
print_type(info.elem)
}
}
+
+
+@(require_results)
+try_copy_string :: proc "contextless" (i: ^int, dst: []byte, src: string) -> bool {
+ if i^ < len(dst) {
+ i^ += copy(dst[i^:], src)
+ return true
+ }
+ return false
+}
+
+
+@(require_results)
+try_copy_byte :: proc "contextless" (i: ^int, dst: []byte, src: byte) -> bool {
+ if i^ < len(dst) {
+ dst[i^] = src
+ i^ += 1
+ return true
+ }
+ return false
+}
+
+
+@(require_results)
+try_copy_u64 :: proc "contextless" (j: ^int, dst: []byte, x: u64) -> bool {
+ if j^ < len(dst) {
+ b :: u64(10)
+ u := x
+
+ a: [129]byte
+ i := len(a)
+ for u >= b {
+ i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
+ u /= b
+ }
+ i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
+
+ return try_copy_string(j, dst, string(a[i:]))
+ }
+ return false
+}
+
+
+@(require_results)
+try_copy_caller_location :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, loc: Source_Code_Location) -> bool {
+ try_copy_string(i, buf, loc.file_path) or_return
+
+ when ODIN_ERROR_POS_STYLE == .Default {
+ try_copy_byte(i, buf, '(') or_return
+ try_copy_u64(i, buf, u64(loc.line)) or_return
+ if loc.column != 0 {
+ try_copy_byte(i, buf, ':') or_return
+ try_copy_u64(i, buf, u64(loc.column)) or_return
+ }
+ try_copy_byte(i, buf, ')') or_return
+ return true
+ } else when ODIN_ERROR_POS_STYLE == .Unix {
+ try_copy_byte(i, buf, ':') or_return
+ try_copy_u64(i, buf, u64(loc.line)) or_return
+ if loc.column != 0 {
+ try_copy_try_copy_bytetring(i, buf, ':') or_return
+ try_copy_u64(i, buf, u64(loc.column)) or_return
+ }
+ try_copy_byte(i, buf, ':') or_return
+ return true
+ } else {
+ #panic("unhandled ODIN_ERROR_POS_STYLE")
+ }
+}
+
+@(require_results)
+try_copy_typeid :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, id: typeid) -> bool {
+ when ODIN_NO_RTTI {
+ if id == nil {
+ try_copy_string(i, buf, "nil") or_return
+ } else {
+ try_copy_string(i, buf, "<unknown type>") or_return
+ }
+ } else {
+ if id == nil {
+ try_copy_string(i, buf, "nil") or_return
+ } else {
+ ti := type_info_of(id)
+ try_copy_write_type(i, buf, ti) or_return
+ }
+ }
+ return true
+}
+
+@(optimization_mode="favor_size")
+try_copy_write_type :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, ti: ^Type_Info) -> bool {
+ if ti == nil {
+ try_copy_string(i, buf, "nil") or_return
+ return true
+ }
+
+ switch info in ti.variant {
+ case Type_Info_Named:
+ try_copy_string(i, buf, info.name) or_return
+ case Type_Info_Integer:
+ switch ti.id {
+ case int: try_copy_string(i, buf, "int") or_return
+ case uint: try_copy_string(i, buf, "uint") or_return
+ case uintptr: try_copy_string(i, buf, "uintptr") or_return
+ case:
+ try_copy_byte(i, buf, 'i' if info.signed else 'u') or_return
+ try_copy_u64(i, buf, u64(8*ti.size)) or_return
+ }
+ case Type_Info_Rune:
+ try_copy_string(i, buf, "rune") or_return
+ case Type_Info_Float:
+ try_copy_byte(i, buf, 'f') or_return
+ try_copy_u64(i, buf, u64(8*ti.size)) or_return
+ case Type_Info_Complex:
+ try_copy_string(i, buf, "complex") or_return
+ try_copy_u64(i, buf, u64(8*ti.size)) or_return
+ case Type_Info_Quaternion:
+ try_copy_string(i, buf, "quaternion") or_return
+ try_copy_u64(i, buf, u64(8*ti.size)) or_return
+ case Type_Info_String:
+ if info.is_cstring {
+ try_copy_byte(i, buf, 'c') or_return
+ }
+ try_copy_string(i, buf, "string") or_return
+ switch info.encoding {
+ case .UTF_8: /**/
+ case .UTF_16: try_copy_string(i, buf, "16") or_return
+ }
+ case Type_Info_Boolean:
+ switch ti.id {
+ case bool: try_copy_string(i, buf, "bool") or_return
+ case:
+ try_copy_byte(i, buf, 'b') or_return
+ try_copy_u64(i, buf, u64(8*ti.size)) or_return
+ }
+ case Type_Info_Any:
+ try_copy_string(i, buf, "any") or_return
+ case Type_Info_Type_Id:
+ try_copy_string(i, buf, "typeid") or_return
+
+ case Type_Info_Pointer:
+ if info.elem == nil {
+ try_copy_string(i, buf, "rawptr") or_return
+ } else {
+ try_copy_string(i, buf, "^") or_return
+ try_copy_write_type(i, buf, info.elem) or_return
+ }
+ case Type_Info_Multi_Pointer:
+ try_copy_string(i, buf, "[^]") or_return
+ try_copy_write_type(i, buf, info.elem) or_return
+ case Type_Info_Soa_Pointer:
+ try_copy_string(i, buf, "#soa ^") or_return
+ try_copy_write_type(i, buf, info.elem) or_return
+ case Type_Info_Procedure:
+ try_copy_string(i, buf, "proc") or_return
+ if info.params == nil {
+ try_copy_string(i, buf, "()") or_return
+ } else {
+ t := info.params.variant.(Type_Info_Parameters)
+ try_copy_byte(i, buf, '(') or_return
+ for t, j in t.types {
+ if j > 0 { try_copy_string(i, buf, ", ") or_return }
+ try_copy_write_type(i, buf, t) or_return
+ }
+ try_copy_string(i, buf, ")") or_return
+ }
+ if info.results != nil {
+ try_copy_string(i, buf, " -> ") or_return
+ try_copy_write_type(i, buf, info.results) or_return
+ }
+ case Type_Info_Parameters:
+ count := len(info.names)
+ if count != 1 { try_copy_byte(i, buf, '(') or_return }
+ for name, j in info.names {
+ if j > 0 { try_copy_string(i, buf, ", ") or_return }
+
+ t := info.types[j]
+
+ if len(name) > 0 {
+ try_copy_string(i, buf, name) or_return
+ try_copy_string(i, buf, ": ") or_return
+ }
+ try_copy_write_type(i, buf, t) or_return
+ }
+ if count != 1 { try_copy_string(i, buf, ")") or_return }
+
+ case Type_Info_Array:
+ try_copy_byte(i, buf, '[') or_return
+ try_copy_u64(i, buf, u64(info.count)) or_return
+ try_copy_byte(i, buf, ']') or_return
+ try_copy_write_type(i, buf, info.elem) or_return
+
+ case Type_Info_Enumerated_Array:
+ if info.is_sparse {
+ try_copy_string(i, buf, "#sparse") or_return
+ }
+ try_copy_byte(i, buf, '[') or_return
+ try_copy_write_type(i, buf, info.index) or_return
+ try_copy_byte(i, buf, ']') or_return
+ try_copy_write_type(i, buf, info.elem) or_return
+
+
+ case Type_Info_Dynamic_Array:
+ try_copy_string(i, buf, "[dynamic]") or_return
+ try_copy_write_type(i, buf, info.elem) or_return
+ case Type_Info_Slice:
+ try_copy_string(i, buf, "[]") or_return
+ try_copy_write_type(i, buf, info.elem) or_return
+
+ case Type_Info_Map:
+ try_copy_string(i, buf, "map[") or_return
+ try_copy_write_type(i, buf, info.key) or_return
+ try_copy_byte(i, buf, ']') or_return
+ try_copy_write_type(i, buf, info.value) or_return
+
+ case Type_Info_Struct:
+ switch info.soa_kind {
+ case .None: // Ignore
+ case .Fixed:
+ try_copy_string(i, buf, "#soa[") or_return
+ try_copy_u64(i, buf, u64(info.soa_len)) or_return
+ try_copy_byte(i, buf, ']') or_return
+ try_copy_write_type(i, buf, info.soa_base_type) or_return
+ return true
+ case .Slice:
+ try_copy_string(i, buf, "#soa[]") or_return
+ try_copy_write_type(i, buf, info.soa_base_type) or_return
+ return true
+ case .Dynamic:
+ try_copy_string(i, buf, "#soa[dynamic]") or_return
+ try_copy_write_type(i, buf, info.soa_base_type) or_return
+ return true
+ }
+
+ try_copy_string(i, buf, "struct ") or_return
+ if .packed in info.flags { try_copy_string(i, buf, "#packed ") or_return }
+ if .raw_union in info.flags { try_copy_string(i, buf, "#raw_union ") or_return }
+ if .all_or_none in info.flags { try_copy_string(i, buf, "#all_or_none ") or_return }
+ if .align in info.flags {
+ try_copy_string(i, buf, "#align(") or_return
+ try_copy_u64(i, buf, u64(ti.align)) or_return
+ try_copy_string(i, buf, ") ") or_return
+ }
+ try_copy_byte(i, buf, '{') or_return
+ for name, j in info.names[:info.field_count] {
+ if j > 0 { try_copy_string(i, buf, ", ") or_return }
+ try_copy_string(i, buf, name) or_return
+ try_copy_string(i, buf, ": ") or_return
+ try_copy_write_type(i, buf, info.types[j]) or_return
+ }
+ try_copy_byte(i, buf, '}') or_return
+
+ case Type_Info_Union:
+ try_copy_string(i, buf, "union ") or_return
+ if info.custom_align {
+ try_copy_string(i, buf, "#align(") or_return
+ try_copy_u64(i, buf, u64(ti.align)) or_return
+ try_copy_string(i, buf, ") ") or_return
+ }
+ if info.no_nil {
+ try_copy_string(i, buf, "#no_nil ") or_return
+ }
+ try_copy_byte(i, buf, '{') or_return
+ for variant, j in info.variants {
+ if j > 0 { try_copy_string(i, buf, ", ") or_return }
+ try_copy_write_type(i, buf, variant) or_return
+ }
+ try_copy_string(i, buf, "}") or_return
+
+ case Type_Info_Enum:
+ try_copy_string(i, buf, "enum ") or_return
+ try_copy_write_type(i, buf, info.base) or_return
+ try_copy_string(i, buf, " {") or_return
+ for name, j in info.names {
+ if j > 0 { try_copy_string(i, buf, ", ") or_return }
+ try_copy_string(i, buf, name) or_return
+ }
+ try_copy_string(i, buf, "}") or_return
+
+ case Type_Info_Bit_Set:
+ try_copy_string(i, buf, "bit_set[") or_return
+
+ #partial switch elem in type_info_base(info.elem).variant {
+ case Type_Info_Enum:
+ try_copy_write_type(i, buf, info.elem) or_return
+ case Type_Info_Rune:
+ print_encoded_rune(rune(info.lower))
+ try_copy_string(i, buf, "..") or_return
+ print_encoded_rune(rune(info.upper))
+ case:
+ print_i64(info.lower)
+ try_copy_string(i, buf, "..") or_return
+ print_i64(info.upper)
+ }
+ if info.underlying != nil {
+ try_copy_string(i, buf, "; ") or_return
+ try_copy_write_type(i, buf, info.underlying) or_return
+ }
+ try_copy_byte(i, buf, ']') or_return
+
+ case Type_Info_Bit_Field:
+ try_copy_string(i, buf, "bit_field ") or_return
+ try_copy_write_type(i, buf, info.backing_type) or_return
+ try_copy_string(i, buf, " {") or_return
+ for name, j in info.names[:info.field_count] {
+ if j > 0 { try_copy_string(i, buf, ", ") or_return }
+ try_copy_string(i, buf, name) or_return
+ try_copy_string(i, buf, ": ") or_return
+ try_copy_write_type(i, buf, info.types[j]) or_return
+ try_copy_string(i, buf, " | ") or_return
+ try_copy_u64(i, buf, u64(info.bit_sizes[j])) or_return
+ }
+ try_copy_byte(i, buf, '}') or_return
+
+
+ case Type_Info_Simd_Vector:
+ try_copy_string(i, buf, "#simd[") or_return
+ try_copy_u64(i, buf, u64(info.count)) or_return
+ try_copy_byte(i, buf, ']') or_return
+ try_copy_write_type(i, buf, info.elem) or_return
+
+ case Type_Info_Matrix:
+ try_copy_string(i, buf, "matrix[") or_return
+ try_copy_u64(i, buf, u64(info.row_count)) or_return
+ try_copy_string(i, buf, ", ") or_return
+ try_copy_u64(i, buf, u64(info.column_count)) or_return
+ try_copy_string(i, buf, "]") or_return
+ try_copy_write_type(i, buf, info.elem) or_return
+ }
+ return true
+} \ No newline at end of file