Skip to content

Commit 3586bda

Browse files
committed
Use context.assertion_failure_proc with type assertions when the context is available, otherwise use a trivial trap.
1 parent 467954b commit 3586bda

File tree

5 files changed

+509
-37
lines changed

5 files changed

+509
-37
lines changed

base/runtime/error_checks.odin

Lines changed: 131 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,22 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
137137

138138

139139
when ODIN_NO_RTTI {
140-
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) {
140+
type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) {
141+
if ok {
142+
return
143+
}
144+
@(cold, no_instrumentation)
145+
handle_error :: proc "odin" (file: string, line, column: i32) -> ! {
146+
p := context.assertion_failure_proc
147+
if p == nil {
148+
p = default_assertion_failure_proc
149+
}
150+
p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""})
151+
}
152+
handle_error(file, line, column)
153+
}
154+
155+
type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) {
141156
if ok {
142157
return
143158
}
@@ -150,7 +165,7 @@ when ODIN_NO_RTTI {
150165
handle_error(file, line, column)
151166
}
152167

153-
type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32) {
168+
type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) {
154169
if ok {
155170
return
156171
}
@@ -162,8 +177,53 @@ when ODIN_NO_RTTI {
162177
}
163178
handle_error(file, line, column)
164179
}
180+
181+
type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) {
182+
if ok {
183+
return
184+
}
185+
@(cold, no_instrumentation)
186+
handle_error :: proc "odin" (file: string, line, column: i32) -> ! {
187+
p := context.assertion_failure_proc
188+
if p == nil {
189+
p = default_assertion_failure_proc
190+
}
191+
p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""})
192+
}
193+
handle_error(file, line, column)
194+
}
165195
} else {
166-
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
196+
@(private="file")
197+
TYPE_ASSERTION_BUFFER_SIZE :: 1024
198+
199+
type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid) {
200+
if ok {
201+
return
202+
}
203+
@(cold, no_instrumentation)
204+
handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid) -> ! {
205+
do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to: typeid) -> bool {
206+
try_copy_string(i, buf, "Invalid type assertion from ") or_return
207+
try_copy_typeid(i, buf, from) or_return
208+
try_copy_string(i, buf, " to ") or_return
209+
try_copy_typeid(i, buf, to) or_return
210+
return true
211+
}
212+
213+
buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
214+
i := 0
215+
_ = do_msg(&i, buf[:], file, line, column, from, to)
216+
217+
p := context.assertion_failure_proc
218+
if p == nil {
219+
p = default_assertion_failure_proc
220+
}
221+
p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
222+
}
223+
handle_error(file, line, column, from, to)
224+
}
225+
226+
type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
167227
if ok {
168228
return
169229
}
@@ -180,42 +240,85 @@ when ODIN_NO_RTTI {
180240
handle_error(file, line, column, from, to)
181241
}
182242

183-
type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
243+
@(private="file")
244+
type_assertion_variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
245+
if id == nil || data == nil {
246+
return id
247+
}
248+
ti := type_info_base(type_info_of(id))
249+
#partial switch v in ti.variant {
250+
case Type_Info_Any:
251+
return (^any)(data).id
252+
case Type_Info_Union:
253+
if v.tag_type == nil {
254+
if (^rawptr)(data)^ == nil {
255+
return nil
256+
}
257+
return v.variants[0].id
258+
259+
}
260+
261+
tag_ptr := uintptr(data) + v.tag_offset
262+
idx := 0
263+
switch v.tag_type.size {
264+
case 1: idx = int( (^u8)(tag_ptr)^); if !v.no_nil { idx -= 1 }
265+
case 2: idx = int( (^u16)(tag_ptr)^); if !v.no_nil { idx -= 1 }
266+
case 4: idx = int( (^u32)(tag_ptr)^); if !v.no_nil { idx -= 1 }
267+
case 8: idx = int( (^u64)(tag_ptr)^); if !v.no_nil { idx -= 1 }
268+
case 16: idx = int((^u128)(tag_ptr)^); if !v.no_nil { idx -= 1 }
269+
}
270+
if idx < 0 {
271+
return nil
272+
} else if idx < len(v.variants) {
273+
return v.variants[idx].id
274+
}
275+
}
276+
return id
277+
}
278+
279+
type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
184280
if ok {
185281
return
186282
}
187283

188-
variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
189-
if id == nil || data == nil {
190-
return id
191-
}
192-
ti := type_info_base(type_info_of(id))
193-
#partial switch v in ti.variant {
194-
case Type_Info_Any:
195-
return (^any)(data).id
196-
case Type_Info_Union:
197-
tag_ptr := uintptr(data) + v.tag_offset
198-
idx := 0
199-
switch v.tag_type.size {
200-
case 1: idx = int((^u8)(tag_ptr)^) - 1
201-
case 2: idx = int((^u16)(tag_ptr)^) - 1
202-
case 4: idx = int((^u32)(tag_ptr)^) - 1
203-
case 8: idx = int((^u64)(tag_ptr)^) - 1
204-
case 16: idx = int((^u128)(tag_ptr)^) - 1
205-
}
206-
if idx < 0 {
207-
return nil
208-
} else if idx < len(v.variants) {
209-
return v.variants[idx].id
284+
@(cold, no_instrumentation)
285+
handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
286+
do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to, actual: typeid) -> bool {
287+
try_copy_string(i, buf, "Invalid type assertion from ") or_return
288+
try_copy_typeid(i, buf, from) or_return
289+
try_copy_string(i, buf, " to ") or_return
290+
try_copy_typeid(i, buf, to) or_return
291+
if actual != from {
292+
try_copy_string(i, buf, ", actual type: ") or_return
293+
try_copy_typeid(i, buf, actual) or_return
210294
}
295+
return true
211296
}
212-
return id
297+
298+
actual := type_assertion_variant_type(from, from_data)
299+
300+
buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
301+
i := 0
302+
_ = do_msg(&i, buf[:], file, line, column, from, to, actual)
303+
304+
p := context.assertion_failure_proc
305+
if p == nil {
306+
p = default_assertion_failure_proc
307+
}
308+
p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
309+
}
310+
handle_error(file, line, column, from, to, from_data)
311+
}
312+
313+
type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
314+
if ok {
315+
return
213316
}
214317

215318
@(cold, no_instrumentation)
216319
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
217320

218-
actual := variant_type(from, from_data)
321+
actual := type_assertion_variant_type(from, from_data)
219322

220323
print_caller_location(Source_Code_Location{file, line, column, ""})
221324
print_string(" Invalid type assertion from ")

0 commit comments

Comments
 (0)