Skip to content

Commit 4157529

Browse files
authored
Merge pull request #6181 from odin-lang/bill/type-assert-behaviour
Use `context.assertion_failure_proc` with type assertions when possible
2 parents 467954b + b85f29d commit 4157529

File tree

5 files changed

+515
-42
lines changed

5 files changed

+515
-42
lines changed

base/runtime/error_checks.odin

Lines changed: 137 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ bounds_trap :: proc "contextless" () -> ! {
1212
}
1313

1414
@(no_instrumentation)
15-
type_assertion_trap :: proc "contextless" () -> ! {
15+
type_assertion_trap_contextless :: proc "contextless" () -> ! {
1616
when ODIN_OS == .Windows {
1717
windows_trap_type_assertion()
1818
} else when ODIN_OS == .Orca {
@@ -137,33 +137,94 @@ 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
}
144159
@(cold, no_instrumentation)
145160
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
146161
print_caller_location(Source_Code_Location{file, line, column, ""})
147162
print_string(" Invalid type assertion\n")
148-
type_assertion_trap()
163+
type_assertion_trap_contextless()
164+
}
165+
handle_error(file, line, column)
166+
}
167+
168+
type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) {
169+
if ok {
170+
return
149171
}
172+
@(cold, no_instrumentation)
173+
handle_error :: proc "odin" (file: string, line, column: i32) -> ! {
174+
p := context.assertion_failure_proc
175+
if p == nil {
176+
p = default_assertion_failure_proc
177+
}
178+
p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""})
179+
}
180+
150181
handle_error(file, line, column)
151182
}
152183

153-
type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32) {
184+
type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) {
154185
if ok {
155186
return
156187
}
157188
@(cold, no_instrumentation)
158189
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
159190
print_caller_location(Source_Code_Location{file, line, column, ""})
160191
print_string(" Invalid type assertion\n")
161-
type_assertion_trap()
192+
type_assertion_trap_contextless()
162193
}
163194
handle_error(file, line, column)
164195
}
165196
} else {
166-
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
197+
@(private="file")
198+
TYPE_ASSERTION_BUFFER_SIZE :: 1024
199+
200+
type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid) {
201+
if ok {
202+
return
203+
}
204+
@(cold, no_instrumentation)
205+
handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid) -> ! {
206+
do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to: typeid) -> bool {
207+
try_copy_string(i, buf, "Invalid type assertion from ") or_return
208+
try_copy_typeid(i, buf, from) or_return
209+
try_copy_string(i, buf, " to ") or_return
210+
try_copy_typeid(i, buf, to) or_return
211+
return true
212+
}
213+
214+
buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
215+
i := 0
216+
_ = do_msg(&i, buf[:], file, line, column, from, to)
217+
218+
p := context.assertion_failure_proc
219+
if p == nil {
220+
p = default_assertion_failure_proc
221+
}
222+
p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
223+
}
224+
handle_error(file, line, column, from, to)
225+
}
226+
227+
type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
167228
if ok {
168229
return
169230
}
@@ -175,47 +236,90 @@ when ODIN_NO_RTTI {
175236
print_string(" to ")
176237
print_typeid(to)
177238
print_byte('\n')
178-
type_assertion_trap()
239+
type_assertion_trap_contextless()
179240
}
180241
handle_error(file, line, column, from, to)
181242
}
182243

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

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
285+
@(cold, no_instrumentation)
286+
handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
287+
do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to, actual: typeid) -> bool {
288+
try_copy_string(i, buf, "Invalid type assertion from ") or_return
289+
try_copy_typeid(i, buf, from) or_return
290+
try_copy_string(i, buf, " to ") or_return
291+
try_copy_typeid(i, buf, to) or_return
292+
if actual != from {
293+
try_copy_string(i, buf, ", actual type: ") or_return
294+
try_copy_typeid(i, buf, actual) or_return
210295
}
296+
return true
211297
}
212-
return id
298+
299+
actual := type_assertion_variant_type(from, from_data)
300+
301+
buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
302+
i := 0
303+
_ = do_msg(&i, buf[:], file, line, column, from, to, actual)
304+
305+
p := context.assertion_failure_proc
306+
if p == nil {
307+
p = default_assertion_failure_proc
308+
}
309+
p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
310+
}
311+
handle_error(file, line, column, from, to, from_data)
312+
}
313+
314+
type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
315+
if ok {
316+
return
213317
}
214318

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

218-
actual := variant_type(from, from_data)
322+
actual := type_assertion_variant_type(from, from_data)
219323

220324
print_caller_location(Source_Code_Location{file, line, column, ""})
221325
print_string(" Invalid type assertion from ")
@@ -227,7 +331,7 @@ when ODIN_NO_RTTI {
227331
print_typeid(actual)
228332
}
229333
print_byte('\n')
230-
type_assertion_trap()
334+
type_assertion_trap_contextless()
231335
}
232336
handle_error(file, line, column, from, to, from_data)
233337
}

0 commit comments

Comments
 (0)