From e983d75b64f56615ada1887257bcf59b495727a2 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Mon, 6 Jan 2025 08:23:56 +0200 Subject: [PATCH] markused,builtin,strconv,vlib: reduce generated C sizes for compilers != tcc, for short programs, by simplifying the generation of backtraces, and reducing string interpolations in panics (#23380) --- vlib/builtin/array.v | 42 ++++---- vlib/builtin/array_d_gcboehm_opt.v | 18 ++-- vlib/builtin/backtraces.c.v | 29 +++--- vlib/builtin/backtraces_nix.c.v | 18 ++-- vlib/builtin/backtraces_windows.c.v | 21 ++-- vlib/builtin/builtin.c.v | 26 ++++- vlib/builtin/builtin.v | 8 +- vlib/builtin/builtin_windows.c.v | 5 +- vlib/builtin/int.v | 34 ------- vlib/builtin/js/builtin.v | 12 ++- vlib/builtin/sorted_map.v | 1 + vlib/builtin/string.v | 8 +- vlib/builtin/utf8.v | 9 +- vlib/coroutines/coroutines.c.v | 2 +- vlib/log/log.v | 4 +- vlib/math/limit.v | 4 +- vlib/math/math.v | 2 +- vlib/net/address.c.v | 2 +- vlib/net/http/header.v | 2 +- vlib/os/file_le_be.c.v | 2 +- vlib/os/os.c.v | 2 +- vlib/os/os_test.c.v | 8 +- vlib/os/process.c.v | 4 +- vlib/os/process_windows.c.v | 2 +- vlib/strconv/number_to_base.c.v | 4 +- vlib/strconv/vprintf.c.v | 6 +- vlib/v/markused/markused.v | 145 ++++++++++++++++------------ vlib/v/markused/walker.v | 41 ++++++++ vlib/vweb/assets/assets.v | 4 +- vlib/x/json2/decoder2/decode.v | 2 +- vlib/x/json2/strict/strict.v | 2 +- 31 files changed, 278 insertions(+), 191 deletions(-) diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index 9973b904a559cf..44eddaf79a5257 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -194,7 +194,8 @@ fn (mut a array) ensure_cap(required int) { return } if a.flags.has(.nogrow) { - panic('array.ensure_cap: array with the flag `.nogrow` cannot grow in size, array required new size: ${required}') + panic_n('array.ensure_cap: array with the flag `.nogrow` cannot grow in size, array required new size:', + required) } mut cap := if a.cap > 0 { i64(a.cap) } else { i64(2) } for required > cap { @@ -205,7 +206,8 @@ fn (mut a array) ensure_cap(required int) { // limit the capacity, since bigger values, will overflow the 32bit integer used to store it cap = max_int } else { - panic('array.ensure_cap: array needs to grow to cap = ${cap}, which is > 2^31') + panic_n('array.ensure_cap: array needs to grow to cap (which is > 2^31):', + cap) } } new_size := u64(cap) * u64(a.element_size) @@ -240,7 +242,7 @@ pub fn (a array) repeat(count int) array { @[direct_array_access; unsafe] pub fn (a array) repeat_to_depth(count int, depth int) array { if count < 0 { - panic('array.repeat: count is negative: ${count}') + panic_n('array.repeat: count is negative:', count) } mut size := u64(count) * u64(a.len) * u64(a.element_size) if size == 0 { @@ -293,7 +295,7 @@ pub fn (a array) repeat_to_depth(count int, depth int) array { // ``` pub fn (mut a array) insert(i int, val voidptr) { if i < 0 || i > a.len { - panic('array.insert: index out of range (i == ${i}, a.len == ${a.len})') + panic_n2('array.insert: index out of range (i,a.len):', i, a.len) } if a.len == max_int { panic('array.insert: a.len reached max_int') @@ -313,11 +315,11 @@ pub fn (mut a array) insert(i int, val voidptr) { @[unsafe] fn (mut a array) insert_many(i int, val voidptr, size int) { if i < 0 || i > a.len { - panic('array.insert_many: index out of range (i == ${i}, a.len == ${a.len})') + panic_n2('array.insert_many: index out of range (i,a.len):', i, a.len) } new_len := i64(a.len) + i64(size) if new_len > max_int { - panic('array.insert_many: a.len = ${new_len} will exceed max_int') + panic_n('array.insert_many: max_int will be exceeded by a.len:', new_len) } a.ensure_cap(int(new_len)) elem_size := a.element_size @@ -374,8 +376,12 @@ pub fn (mut a array) delete(i int) { // ``` pub fn (mut a array) delete_many(i int, size int) { if i < 0 || i64(i) + i64(size) > i64(a.len) { - endidx := if size > 1 { '..${i + size}' } else { '' } - panic('array.delete: index out of range (i == ${i}${endidx}, a.len == ${a.len})') + if size > 1 { + panic_n3('array.delete: index out of range (i,i+size,a.len):', i, i + size, + a.len) + } else { + panic_n2('array.delete: index out of range (i,a.len):', i, a.len) + } } if a.flags.all(.noshrink | .noslices) { unsafe { @@ -465,7 +471,7 @@ fn (a array) get_unsafe(i int) voidptr { fn (a array) get(i int) voidptr { $if !no_bounds_checking { if i < 0 || i >= a.len { - panic('array.get: index out of range (i == ${i}, a.len == ${a.len})') + panic_n2('array.get: index out of range (i,a.len):', i, a.len) } } unsafe { @@ -557,13 +563,15 @@ fn (a array) slice(start int, _end int) array { end := if _end == max_int { a.len } else { _end } // max_int $if !no_bounds_checking { if start > end { - panic('array.slice: invalid slice index (${start} > ${end})') + panic('array.slice: invalid slice index (start>end):' + i64(start).str() + ', ' + + i64(end).str()) } if end > a.len { - panic('array.slice: slice bounds out of range (${end} >= ${a.len})') + panic('array.slice: slice bounds out of range (' + i64(end).str() + ' >= ' + + i64(a.len).str() + ')') } if start < 0 { - panic('array.slice: slice bounds out of range (${start} < 0)') + panic('array.slice: slice bounds out of range (start<0):' + start.str()) } } // TODO: integrate reference counting @@ -683,7 +691,7 @@ fn (mut a array) set_unsafe(i int, val voidptr) { fn (mut a array) set(i int, val voidptr) { $if !no_bounds_checking { if i < 0 || i >= a.len { - panic('array.set: index out of range (i == ${i}, a.len == ${a.len})') + panic_n2('array.set: index out of range (i,a.len):', i, a.len) } } unsafe { vmemcpy(&u8(a.data) + u64(a.element_size) * u64(i), val, a.element_size) } @@ -1000,7 +1008,7 @@ pub fn copy(mut dst []u8, src []u8) int { pub fn (mut a array) grow_cap(amount int) { new_cap := i64(amount) + i64(a.cap) if new_cap > max_int { - panic('array.grow_cap: new capacity ${new_cap} will exceed max_int') + panic_n('array.grow_cap: max_int will be exceeded by new cap:', new_cap) } a.ensure_cap(int(new_cap)) } @@ -1013,7 +1021,7 @@ pub fn (mut a array) grow_cap(amount int) { pub fn (mut a array) grow_len(amount int) { new_len := i64(amount) + i64(a.len) if new_len > max_int { - panic('array.grow_len: new len ${new_len} will exceed max_int') + panic_n('array.grow_len: max_int will be exceeded by new len:', new_len) } a.ensure_cap(int(new_len)) a.len = int(new_len) @@ -1053,13 +1061,13 @@ pub fn (data &u8) vbytes(len int) []u8 { @[if !no_bounds_checking ?; inline] fn panic_on_negative_len(len int) { if len < 0 { - panic('negative .len') + panic_n('negative .len:', len) } } @[if !no_bounds_checking ?; inline] fn panic_on_negative_cap(cap int) { if cap < 0 { - panic('negative .cap') + panic_n('negative .cap:', cap) } } diff --git a/vlib/builtin/array_d_gcboehm_opt.v b/vlib/builtin/array_d_gcboehm_opt.v index 05c62774dc62c1..e2bdb044147657 100644 --- a/vlib/builtin/array_d_gcboehm_opt.v +++ b/vlib/builtin/array_d_gcboehm_opt.v @@ -103,7 +103,8 @@ fn (mut a array) ensure_cap_noscan(required int) { return } if a.flags.has(.nogrow) { - panic('array.ensure_cap_noscan: array with the flag `.nogrow` cannot grow in size, array required new size: ${required}') + panic_n('array.ensure_cap_noscan: array with the flag `.nogrow` cannot grow in size, array required new size:', + required) } mut cap := if a.cap > 0 { i64(a.cap) } else { i64(2) } for required > cap { @@ -114,7 +115,8 @@ fn (mut a array) ensure_cap_noscan(required int) { // limit the capacity, since bigger values, will overflow the 32bit integer used to store it cap = max_int } else { - panic('array.ensure_cap_noscan: array needs to grow to cap = ${cap}, which is > 2^31') + panic_n('array.ensure_cap_noscan: array needs to grow to cap (which is > 2^31):', + cap) } } new_size := u64(cap) * u64(a.element_size) @@ -136,7 +138,7 @@ fn (mut a array) ensure_cap_noscan(required int) { @[unsafe] fn (a array) repeat_to_depth_noscan(count int, depth int) array { if count < 0 { - panic('array.repeat: count is negative: ${count}') + panic_n('array.repeat: count is negative:', count) } mut size := u64(count) * u64(a.len) * u64(a.element_size) if size == 0 { @@ -170,7 +172,7 @@ fn (a array) repeat_to_depth_noscan(count int, depth int) array { // insert inserts a value in the array at index `i` fn (mut a array) insert_noscan(i int, val voidptr) { if i < 0 || i > a.len { - panic('array.insert_noscan: index out of range (i == ${i}, a.len == ${a.len})') + panic_n2('array.insert_noscan: index out of range (i,a.len):', i, a.len) } if a.len == max_int { panic('array.insert_noscan: a.len reached max_int') @@ -187,11 +189,11 @@ fn (mut a array) insert_noscan(i int, val voidptr) { @[unsafe] fn (mut a array) insert_many_noscan(i int, val voidptr, size int) { if i < 0 || i > a.len { - panic('array.insert_many: index out of range (i == ${i}, a.len == ${a.len})') + panic_n2('array.insert_many: index out of range (i, a.len):', i, a.len) } new_len := i64(a.len) + i64(size) if new_len > max_int { - panic('array.insert_many_noscan: a.len = ${new_len} will exceed max_int') + panic_n('array.insert_many_noscan: max_int will be exceeded by a.len:', new_len) } a.ensure_cap_noscan(a.len + size) elem_size := a.element_size @@ -328,7 +330,7 @@ fn (a array) reverse_noscan() array { fn (mut a array) grow_cap_noscan(amount int) { new_cap := i64(amount) + i64(a.cap) if new_cap > max_int { - panic('array.grow_cap: new capacity ${new_cap} will exceed max_int') + panic_n('array.grow_cap: max_int will be exceeded by new cap:', new_cap) } a.ensure_cap_noscan(int(new_cap)) } @@ -338,7 +340,7 @@ fn (mut a array) grow_cap_noscan(amount int) { fn (mut a array) grow_len_noscan(amount int) { new_len := i64(amount) + i64(a.len) if new_len > max_int { - panic('array.grow_len: new len ${new_len} will exceed max_int') + panic_n('array.grow_len: max_int will be exceeded by new len:', new_len) } a.ensure_cap_noscan(int(new_len)) a.len = int(new_len) diff --git a/vlib/builtin/backtraces.c.v b/vlib/builtin/backtraces.c.v index 8b85761f5ee74d..a357126ebf9918 100644 --- a/vlib/builtin/backtraces.c.v +++ b/vlib/builtin/backtraces.c.v @@ -10,19 +10,24 @@ pub fn print_backtrace() { $if !no_backtrace ? { $if freestanding { println(bare_backtrace()) + } $else $if native { + // TODO: native backtrace solution + } $else $if tinyc { + C.tcc_backtrace(c'Backtrace') + } $else $if use_libbacktrace ? { + // NOTE: TCC doesn't have the unwind library + print_libbacktrace(1) } $else { - $if native { - // TODO: native backtrace solution - } $else $if tinyc { - C.tcc_backtrace(c'Backtrace') - } $else { - // NOTE: TCC doesn't have the unwind library - $if use_libbacktrace ? { - print_libbacktrace(1) - } $else { - print_backtrace_skipping_top_frames(2) - } - } + print_backtrace_skipping_top_frames(2) + } + } +} + +fn eprint_space_padding(output string, max_len int) { + padding_len := max_len - output.len + if padding_len > 0 { + for _ in 0 .. padding_len { + eprint(' ') } } } diff --git a/vlib/builtin/backtraces_nix.c.v b/vlib/builtin/backtraces_nix.c.v index b3b2355cf731c2..5f9c8df8f79d9a 100644 --- a/vlib/builtin/backtraces_nix.c.v +++ b/vlib/builtin/backtraces_nix.c.v @@ -67,17 +67,14 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool { return false } nr_actual_frames := nr_ptrs - skipframes - mut sframes := []string{} //////csymbols := backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames) csymbols := C.backtrace_symbols(voidptr(&buffer[skipframes]), nr_actual_frames) for i in 0 .. nr_actual_frames { - sframes << unsafe { tos2(&u8(csymbols[i])) } - } - for sframe in sframes { + sframe := unsafe { tos2(&u8(csymbols[i])) } executable := sframe.all_before('(') addr := sframe.all_after('[').all_before(']') beforeaddr := sframe.all_before('[') - cmd := 'addr2line -e ${executable} ${addr}' + cmd := 'addr2line -e ' + executable + ' ' + addr // taken from os, to avoid depending on the os module inside builtin.v f := C.popen(&char(cmd.str), c'r') if f == unsafe { nil } { @@ -92,7 +89,7 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool { output += tos(bp, vstrlen(bp)) } } - output = output.trim_space() + ':' + output = output.trim_chars(' \t\n', .trim_both) + ':' if C.pclose(f) != 0 { eprintln(sframe) continue @@ -104,9 +101,14 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool { // Note: it is shortened here to just d. , just so that it fits, and so // that the common error file:lineno: line format is enforced. output = output.replace(' (discriminator', ': (d.') - eprintln('${output:-55s} | ${addr:14s} | ${beforeaddr}') + eprint(output) + eprint_space_padding(output, 55) + eprint(' | ') + eprint(addr) + eprint(' | ') + eprintln(beforeaddr) } - if sframes.len > 0 { + if nr_actual_frames > 0 { unsafe { C.free(csymbols) } } } diff --git a/vlib/builtin/backtraces_windows.c.v b/vlib/builtin/backtraces_windows.c.v index 23a8f2f81910cc..bc7789fb365b46 100644 --- a/vlib/builtin/backtraces_windows.c.v +++ b/vlib/builtin/backtraces_windows.c.v @@ -76,6 +76,7 @@ pub fn print_backtrace_skipping_top_frames(skipframes int) bool { return false } +@[direct_array_access] fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool { $if msvc { mut offset := u64(0) @@ -116,23 +117,31 @@ fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool { if C.SymGetLineFromAddr64(handle, frame_addr, &offset, &sline64) == 1 { file_name := unsafe { tos3(sline64.f_file_name) } lnumber := sline64.f_line_number - lineinfo = '${file_name}:${lnumber}' + lineinfo = file_name + i64(lnumber).str() } else { // addr: - lineinfo = '?? : address = 0x${(&frame_addr):x}' + lineinfo = '?? : address = 0x' + ptr_str(frame_addr) } sfunc := unsafe { tos3(fname) } - eprintln('${nframe:-2d}: ${sfunc:-25s} ${lineinfo}') + snframe := i64(nframe).str() + eprint_space_padding(snframe, 2) + eprint(': ') + eprint(sfunc) + eprint_space_padding(sfunc, 25) + eprint(' ') + eprint(lineinfo) } else { // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes cerr := int(C.GetLastError()) + eprint('SymFromAddr failure: ') + eprint(i64(cerr).str()) if cerr == 87 { - eprintln('SymFromAddr failure: ${cerr} = The parameter is incorrect)') + eprintln(' = The parameter is incorrect)') } else if cerr == 487 { // probably caused because the .pdb isn't in the executable folder - eprintln('SymFromAddr failure: ${cerr} = Attempt to access invalid address (Verify that you have the .pdb file in the right folder.)') + eprintln(' = Attempt to access invalid address (Verify that you have the .pdb file in the right folder.)') } else { - eprintln('SymFromAddr failure: ${cerr} (see https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes)') + eprintln(' (see https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes)') } } } diff --git a/vlib/builtin/builtin.c.v b/vlib/builtin/builtin.c.v index 831f7d543173b4..32db56a05c2caf 100644 --- a/vlib/builtin/builtin.c.v +++ b/vlib/builtin/builtin.c.v @@ -106,14 +106,14 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) { // It ends the program with a panic. @[noreturn] pub fn panic_option_not_set(s string) { - panic('option not set (${s})') + panic('option not set (' + s + ')') } // panic_result_not_set is called by V, when you use result error propagation in your main function // It ends the program with a panic. @[noreturn] pub fn panic_result_not_set(s string) { - panic('result not set (${s})') + panic('result not set (' + s + ')') } // panic prints a nice error message, then exits the process with exit code of 1. @@ -175,6 +175,24 @@ pub fn c_error_number_str(errnum int) string { return err_msg } +// panic_n prints an error message, followed by the given number, then exits the process with exit code of 1. +@[noreturn] +pub fn panic_n(s string, number1 i64) { + panic(s + number1.str()) +} + +// panic_n2 prints an error message, followed by the given numbers, then exits the process with exit code of 1. +@[noreturn] +pub fn panic_n2(s string, number1 i64, number2 i64) { + panic(s + number1.str() + ', ' + number2.str()) +} + +// panic_n3 prints an error message, followed by the given numbers, then exits the process with exit code of 1. +@[noreturn] +fn panic_n3(s string, number1 i64, number2 i64, number3 i64) { + panic(s + number1.str() + ', ' + number2.str() + ', ' + number2.str()) +} + // panic with a C-API error message matching `errnum` @[noreturn] pub fn panic_error_number(basestr string, errnum int) { @@ -751,8 +769,8 @@ pub fn gc_memory_use() usize { fn v_fixed_index(i int, len int) int { $if !no_bounds_checking { if i < 0 || i >= len { - s := 'fixed array index out of range (index: ${i}, len: ${len})' - panic(s) + panic('fixed array index out of range (index: ' + i64(i).str() + ', len: ' + + i64(len).str() + ')') } } return i diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index 23f45ebd676692..ba6c8c8c07055e 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -10,12 +10,6 @@ pub fn isnil(v voidptr) bool { return v == 0 } -/* -fn on_panic(f fn(int)int) { - // TODO -} -*/ - struct VCastTypeIndexName { tindex int tname string @@ -37,7 +31,7 @@ fn __as_cast(obj voidptr, obj_type int, expected_type int) voidptr { expected_name = x.tname.clone() } } - panic('as cast: cannot cast `${obj_name}` to `${expected_name}`') + panic('as cast: cannot cast `' + obj_name + '` to `' + expected_name + '`') } return obj } diff --git a/vlib/builtin/builtin_windows.c.v b/vlib/builtin/builtin_windows.c.v index 1a119500116616..4f6d46ff79a57f 100644 --- a/vlib/builtin/builtin_windows.c.v +++ b/vlib/builtin/builtin_windows.c.v @@ -67,6 +67,9 @@ fn builtin_init() { $if !no_backtrace ? { add_unhandled_exception_handler() } + // On windows, the default buffering is block based (~4096bytes), which interferes badly with non cmd shells + // It is much better to have it off by default instead. + unbuffer_stdout() } // TODO: copypaste from os @@ -109,7 +112,7 @@ fn unhandled_exception_handler(e &ExceptionPointers) int { return 0 } else { - println('Unhandled Exception 0x${e.exception_record.code:X}') + println('Unhandled Exception 0x' + ptr_str(e.exception_record.code)) print_backtrace_skipping_top_frames(5) } } diff --git a/vlib/builtin/int.v b/vlib/builtin/int.v index 57e333cdfc4c49..4c8e1b1d74009e 100644 --- a/vlib/builtin/int.v +++ b/vlib/builtin/int.v @@ -3,29 +3,18 @@ // that can be found in the LICENSE file. module builtin -// -// ----- value to string functions ----- -// - pub struct VContext { allocator int } -// type u8 = byte type byte = u8 -// type i32 = int - // ptr_str returns the address of `ptr` as a `string`. pub fn ptr_str(ptr voidptr) string { buf1 := u64(ptr).hex() return buf1 } -// pub fn nil_str(x voidptr) string { -// return 'nil' -//} - // str returns string equivalent of x pub fn (x isize) str() string { return i64(x).str() @@ -118,18 +107,7 @@ fn (nn int) str_l(max int) string { } diff := max - index vmemmove(buf, voidptr(buf + index), diff + 1) - /* - // === manual memory move for bare metal === - mut c:= 0 - for c < diff { - buf[c] = buf[c+index] - c++ - } - buf[c] = 0 - */ return tos(buf, diff) - - // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) } } @@ -165,10 +143,6 @@ pub fn (n int) str() string { return n.str_l(12) } -// pub fn int_str(n int) string { -// return i64(n).str() -//} - // str returns the value of the `u32` as a `string`. // Example: assert u32(20000).str() == '20000' @[direct_array_access; inline] @@ -202,8 +176,6 @@ pub fn (nn u32) str() string { diff := max - index vmemmove(buf, voidptr(buf + index), diff + 1) return tos(buf, diff) - - // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) } } @@ -258,7 +230,6 @@ pub fn (nn i64) str() string { diff := max - index vmemmove(buf, voidptr(buf + index), diff + 1) return tos(buf, diff) - // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) } } @@ -295,7 +266,6 @@ pub fn (nn u64) str() string { diff := max - index vmemmove(buf, voidptr(buf + index), diff + 1) return tos(buf, diff) - // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) } } @@ -308,10 +278,6 @@ pub fn (b bool) str() string { return 'false' } -// -// ----- value to hex string functions ----- -// - // u64_to_hex converts the number `nn` to a (zero padded if necessary) hexadecimal `string`. @[direct_array_access; inline] fn u64_to_hex(nn u64, len u8) string { diff --git a/vlib/builtin/js/builtin.v b/vlib/builtin/js/builtin.v index 6c15d7641b0399..5c2fd4676d7044 100644 --- a/vlib/builtin/js/builtin.v +++ b/vlib/builtin/js/builtin.v @@ -6,9 +6,19 @@ module builtin fn (a any) toString() +// panic prints an error message, then exits the process with exit code of 1. @[noreturn] pub fn panic(s string) { - eprintln('V panic: ${s}\n${js_stacktrace()}') + eprintln('V panic: ' + s) + eprintln(js_stacktrace()) + exit(1) +} + +// panic_n prints an error message, followed by the given number, then exits the process with exit code of 1. +@[noreturn] +pub fn panic_n(s string, n i64) { + eprintln('V panic: ' + s) + eprintln(js_stacktrace()) exit(1) } diff --git a/vlib/builtin/sorted_map.v b/vlib/builtin/sorted_map.v index 8c0b0247fcf8b3..7be2dca4a964cc 100644 --- a/vlib/builtin/sorted_map.v +++ b/vlib/builtin/sorted_map.v @@ -158,6 +158,7 @@ fn (mut n mapnode) split_child(child_index int, mut y mapnode) { n.len++ } +@[direct_array_access] fn (m SortedMap) get(key string, out voidptr) bool { mut node := m.root for { diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 252b97ab2f30cb..2056f7c9e42e42 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -1115,7 +1115,8 @@ pub fn (s string) substr(start int, _end int) string { end := if _end == max_int { s.len } else { _end } // max_int $if !no_bounds_checking { if start > end || start > s.len || end > s.len || start < 0 || end < 0 { - panic('substr(${start}, ${end}) out of bounds (len=${s.len}) s="${s}"') + panic('substr(' + start.str() + ', ' + end.str() + ') out of bounds (len=' + + s.len.str() + ') s=' + s) } } len := end - start @@ -1153,7 +1154,8 @@ pub fn (s string) substr_unsafe(start int, _end int) string { pub fn (s string) substr_with_check(start int, _end int) !string { end := if _end == max_int { s.len } else { _end } // max_int if start > end || start > s.len || end > s.len || start < 0 || end < 0 { - return error('substr(${start}, ${end}) out of bounds (len=${s.len})') + return error('substr(' + start.str() + ', ' + end.str() + ') out of bounds (len=' + + s.len.str() + ')') } len := end - start if len == s.len { @@ -1962,7 +1964,7 @@ pub fn (s string) str() string { fn (s string) at(idx int) u8 { $if !no_bounds_checking { if idx < 0 || idx >= s.len { - panic('string index out of range: ${idx} / ${s.len}') + panic_n2('string index out of range(idx,s.len):', idx, s.len) } } return unsafe { s.str[idx] } diff --git a/vlib/builtin/utf8.v b/vlib/builtin/utf8.v index 5755cdda25b40f..2d0afdf6400562 100644 --- a/vlib/builtin/utf8.v +++ b/vlib/builtin/utf8.v @@ -78,14 +78,15 @@ pub fn utf32_decode_to_buffer(code u32, mut buf &u8) int { // it is used in vlib/builtin/string.v, // and also in vlib/v/gen/c/cgen.v pub fn (_rune string) utf32_code() int { - return int(_rune.bytes().utf8_to_utf32() or { - // error('more than one utf-8 rune found in this string') - rune(0) - }) + if res := _rune.bytes().utf8_to_utf32() { + return int(res) + } + return 0 } // convert array of utf8 bytes to single utf32 value // will error if more than 4 bytes are submitted +@[direct_array_access] pub fn (_bytes []u8) utf8_to_utf32() !rune { if _bytes.len == 0 { return 0 diff --git a/vlib/coroutines/coroutines.c.v b/vlib/coroutines/coroutines.c.v index 8ad961ff4a8539..a6277e4894a92f 100644 --- a/vlib/coroutines/coroutines.c.v +++ b/vlib/coroutines/coroutines.c.v @@ -113,6 +113,6 @@ fn init() { } if ret < 0 { - panic('failed to initialize coroutines via photon (ret=${ret})') + panic_n('failed to initialize coroutines via photon ret:', ret) } } diff --git a/vlib/log/log.v b/vlib/log/log.v index 8901da6f909039..803c989da7432c 100644 --- a/vlib/log/log.v +++ b/vlib/log/log.v @@ -72,7 +72,7 @@ pub fn (mut l Log) set_output_path(output_file_path string) { l.output_target = .file l.output_file_name = os.join_path(os.real_path(output_file_path), l.output_label) ofile := os.open_append(l.output_file_name) or { - panic('error while opening log file ${l.output_file_name} for appending') + panic('error while opening log file ' + l.output_file_name + ' for appending') } l.ofile = ofile } @@ -160,7 +160,7 @@ pub fn (mut l Log) fatal(s string) { l.send_output(s, .fatal) l.ofile.close() } - panic('${l.output_label}: ${s}') + panic(l.output_label + ': ' + s) } // error logs line `s` via `send_output` if `Log.level` is greater than or equal to the `Level.error` category. diff --git a/vlib/math/limit.v b/vlib/math/limit.v index 41f52e47d5bd3a..c9b80b6f4333be 100644 --- a/vlib/math/limit.v +++ b/vlib/math/limit.v @@ -36,7 +36,7 @@ pub fn maxof[T]() T { } return int(max_i32) } $else { - panic('A maximum value of the type `${typeof[T]().name}` is not defined.') + panic('A maximum value of the type `' + typeof[T]().name + '` is not defined.') } } @@ -73,6 +73,6 @@ pub fn minof[T]() T { } return int(min_i32) } $else { - panic('A minimum value of the type `${typeof[T]().name}` is not defined.') + panic('A minimum value of the type `' + typeof[T]().name + '` is not defined.') } } diff --git a/vlib/math/math.v b/vlib/math/math.v index 888d83dfaa3e81..46f570ff5f0882 100644 --- a/vlib/math/math.v +++ b/vlib/math/math.v @@ -70,7 +70,7 @@ pub fn digits(num i64, params DigitParams) []int { // set base to 10 initially and change only if base is explicitly set. mut b := params.base if b < 2 { - panic('digits: Cannot find digits of n with base ${b}') + panic_n('digits: Cannot find digits of n with base:', b) } mut n := num mut sign := 1 diff --git a/vlib/net/address.c.v b/vlib/net/address.c.v index 05c3341d03832d..948b3b66877d87 100644 --- a/vlib/net/address.c.v +++ b/vlib/net/address.c.v @@ -254,7 +254,7 @@ pub fn resolve_ipaddrs(addr string, family AddrFamily, typ SocketType) ![]Addr { addresses << new_addr } else { - panic('Unexpected address family ${result.ai_family}') + panic('Unexpected address family ' + result.ai_family.str()) } } } diff --git a/vlib/net/http/header.v b/vlib/net/http/header.v index ae1f39f1b1e00f..f0ecbe82a9b107 100644 --- a/vlib/net/http/header.v +++ b/vlib/net/http/header.v @@ -743,7 +743,7 @@ pub fn (h Header) join(other Header) Header { for v in other.custom_values(k, exact: true) { combined.add_custom(k, v) or { // panic because this should never fail - panic('unexpected error: ${err}') + panic('unexpected error: ' + err.str()) } } } diff --git a/vlib/os/file_le_be.c.v b/vlib/os/file_le_be.c.v index d7c7d7a8258eec..acbfff91d775ff 100644 --- a/vlib/os/file_le_be.c.v +++ b/vlib/os/file_le_be.c.v @@ -93,7 +93,7 @@ fn swap_bytes[T](input T) T { } $else $if T is i64 { return i64(swap_bytes_u64(u64(input))) } $else { - panic('type is not supported: ${typeof[T]()}') + panic('type is not supported: ' + typeof[T]().str()) } } diff --git a/vlib/os/os.c.v b/vlib/os/os.c.v index 63dc6e99c9f6de..08a5ff3d31b54c 100644 --- a/vlib/os/os.c.v +++ b/vlib/os/os.c.v @@ -606,7 +606,7 @@ pub fn read_file_array[T](path string) []T { // On some systems C.ftell can return values in the 64-bit range // that, when cast to `int`, can result in values below 0. if i64(allocate) < fsize { - panic('${fsize} cast to int results in ${int(fsize)})') + panic_n2('cast to int results in (fsize, int(fsize)):', i64(fsize), i64(int(fsize))) } buf := unsafe { malloc_noscan(allocate) diff --git a/vlib/os/os_test.c.v b/vlib/os/os_test.c.v index f9009ade9f1321..f6973baaf44c55 100644 --- a/vlib/os/os_test.c.v +++ b/vlib/os/os_test.c.v @@ -40,7 +40,7 @@ fn test_open_file() { file.write_string(hello) or { panic(err) } file.close() assert u64(hello.len) == os.file_size(filename) - read_hello := os.read_file(filename) or { panic('error reading file ${filename}') } + read_hello := os.read_file(filename) or { panic('error reading file ' + filename) } assert hello == read_hello os.rm(filename) or { panic(err) } } @@ -80,7 +80,7 @@ fn test_open_file_binary() { unsafe { file.write_ptr(bytes.data, bytes.len) } file.close() assert u64(hello.len) == os.file_size(filename) - read_hello := os.read_bytes(filename) or { panic('error reading file ${filename}') } + read_hello := os.read_bytes(filename) or { panic('error reading file ' + filename) } assert bytes == read_hello os.rm(filename) or { panic(err) } } @@ -160,7 +160,7 @@ fn test_write_and_read_string_to_file() { hello := 'hello world!' os.write_file(filename, hello) or { panic(err) } assert u64(hello.len) == os.file_size(filename) - read_hello := os.read_file(filename) or { panic('error reading file ${filename}') } + read_hello := os.read_file(filename) or { panic('error reading file ' + filename) } assert hello == read_hello os.rm(filename) or { panic(err) } } @@ -322,7 +322,7 @@ fn test_cp() { old_file_name := 'cp_example.txt' new_file_name := 'cp_new_example.txt' os.write_file(old_file_name, 'Test data 1 2 3, V is awesome #$%^[]!~⭐') or { panic(err) } - os.cp(old_file_name, new_file_name) or { panic('${err}') } + os.cp(old_file_name, new_file_name) or { panic(err) } old_file := os.read_file(old_file_name) or { panic(err) } new_file := os.read_file(new_file_name) or { panic(err) } assert old_file == new_file diff --git a/vlib/os/process.c.v b/vlib/os/process.c.v index 51321853b38572..9ccb9e452d3549 100644 --- a/vlib/os/process.c.v +++ b/vlib/os/process.c.v @@ -258,10 +258,10 @@ fn (mut p Process) _is_pending(pkind ChildProcessPipeKind) bool { // _check_redirection_call - should be called just by stdxxx methods fn (mut p Process) _check_redirection_call(fn_name string) { if !p.use_stdio_ctl { - panic('Call p.set_redirect_stdio() before calling p.${fn_name}') + panic('Call p.set_redirect_stdio() before calling p.' + fn_name) } if p.status == .not_started { - panic('Call p.${fn_name}() after you have called p.run()') + panic('Call p.' + fn_name + '() after you have called p.run()') } } diff --git a/vlib/os/process_windows.c.v b/vlib/os/process_windows.c.v index 8cb003b2ff16dc..8b7193b992a150 100644 --- a/vlib/os/process_windows.c.v +++ b/vlib/os/process_windows.c.v @@ -198,7 +198,7 @@ fn (mut p Process) win_is_alive() bool { /////////////// fn (mut p Process) win_write_string(idx int, _s string) { - panic('Process.write_string ${idx} is not implemented yet') + panic_n('Process.write_string is not implemented yet, idx:', idx) } fn (mut p Process) win_read_string(idx int, _maxbytes int) (string, int) { diff --git a/vlib/strconv/number_to_base.c.v b/vlib/strconv/number_to_base.c.v index edea9727a915e5..dbdd7e2b346ad1 100644 --- a/vlib/strconv/number_to_base.c.v +++ b/vlib/strconv/number_to_base.c.v @@ -8,7 +8,7 @@ const base_digits = '0123456789abcdefghijklmnopqrstuvwxyz' pub fn format_int(n i64, radix int) string { unsafe { if radix < 2 || radix > 36 { - panic('invalid radix: ${radix} . It should be => 2 and <= 36') + panic_n('invalid radix, it should be => 2 and <= 36, actual:', radix) } if n == 0 { return '0' @@ -45,7 +45,7 @@ pub fn format_int(n i64, radix int) string { pub fn format_uint(n u64, radix int) string { unsafe { if radix < 2 || radix > 36 { - panic('invalid radix: ${radix} . It should be => 2 and <= 36') + panic_n('invalid radix, it should be => 2 and <= 36, actual:', radix) } if n == 0 { return '0' diff --git a/vlib/strconv/vprintf.c.v b/vlib/strconv/vprintf.c.v index b7282299a49dfc..f2ae5e2bff9667 100644 --- a/vlib/strconv/vprintf.c.v +++ b/vlib/strconv/vprintf.c.v @@ -551,7 +551,8 @@ pub fn v_sprintf(str string, pt ...voidptr) string { } if p_index != pt.len { - panic('${p_index} % conversion specifiers, but given ${pt.len} args') + panic_n2('% conversion specifiers number mismatch (expected %, given args)', p_index, + pt.len) } return res.str() @@ -560,7 +561,8 @@ pub fn v_sprintf(str string, pt ...voidptr) string { @[inline] fn v_sprintf_panic(idx int, len int) { if idx >= len { - panic('${idx + 1} % conversion specifiers, but given only ${len} args') + panic_n2('% conversion specifiers number mismatch (expected %, given args)', idx + 1, + len) } } diff --git a/vlib/v/markused/markused.v b/vlib/v/markused/markused.v index c8e9608daf5ab7..58b33cae8bcbad 100644 --- a/vlib/v/markused/markused.v +++ b/vlib/v/markused/markused.v @@ -13,21 +13,48 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a defer { util.timing_measure('MARKUSED') } + + trace_skip_unused := pref_.compile_values['trace_skip_unused'] == 'true' + trace_skip_unused_all_fns := pref_.compile_values['trace_skip_unused_all_fns'] == 'true' + trace_skip_unused_fn_names := pref_.compile_values['trace_skip_unused_fn_names'] == 'true' + trace_skip_unused_interface_methods := pref_.compile_values['trace_skip_unused_interface_methods'] == 'true' + used_fns := pref_.compile_values['used_fns'] + + byteptr_idx_str := ast.byteptr_type_idx.str() + charptr_idx_str := ast.charptr_type_idx.str() + string_idx_str := ast.string_type_idx.str() + array_idx_str := ast.array_type_idx.str() + map_idx_str := ast.map_type_idx.str() + ref_map_idx_str := int(ast.map_type.ref()).str() + ref_densearray_idx_str := int(table.find_type('DenseArray').ref()).str() + ref_array_idx_str := int(ast.array_type.ref()).str() + // Functions that must be generated and can't be skipped mut all_fn_root_names := []string{} + if used_fns != '' { + aused_fns := used_fns.split(',') + all_fns_keys := all_fns.keys() + mut matching := []string{} + for ufn in aused_fns { + if ufn.contains('*') { + matching_fns := all_fns_keys.filter(it.match_glob(ufn)) + if matching_fns.len > 0 { + matching << matching_fns + } + } else { + matching << ufn + } + } + all_fn_root_names << matching + for m in matching { + println('> used_fn, found matching symbol: ${m}') + } + } + if pref_.backend == .native { // Note: this is temporary, until the native backend supports more features! all_fn_root_names << 'main.main' } else { - byteptr_idx_str := '${ast.byteptr_type_idx}' - charptr_idx_str := '${ast.charptr_type_idx}' - string_idx_str := '${ast.string_type_idx}' - array_idx_str := '${ast.array_type_idx}' - map_idx_str := '${ast.map_type_idx}' - ref_map_idx_str := '${int(ast.map_type.ref())}' - ref_densearray_idx_str := '${int(table.find_type('DenseArray').ref())}' - ref_array_idx_str := '${int(ast.array_type.ref())}' - mut include_panic_deps := false mut core_fns := [ 'main.main', @@ -41,23 +68,13 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a 'println', 'ptr_str', ] - - $if debug_used_features ? { - dump(table.used_features) - } if ast.float_literal_type.idx() in table.used_features.print_types || ast.f64_type_idx in table.used_features.print_types || ast.f32_type_idx in table.used_features.print_types { include_panic_deps = true } - $if windows { - if 'no_backtrace' !in pref_.compile_defines { - include_panic_deps = true - } - } $else { - if 'use_libbacktrace' in pref_.compile_defines { - core_fns << 'print_libbacktrace' - } + if 'use_libbacktrace' in pref_.compile_defines { + core_fns << 'print_libbacktrace' } if 'callstack' in pref_.compile_defines { core_fns << ref_array_idx_str + '.push' @@ -69,7 +86,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a if pref_.autofree { core_fns << string_idx_str + '.clone_static' } - if table.used_features.as_cast || table.used_features.auto_str || pref_.is_shared { + if table.used_features.auto_str || pref_.is_shared { include_panic_deps = true core_fns << 'isnil' core_fns << '__new_array' @@ -133,26 +150,19 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a if table.used_features.arr_last { core_fns << array_idx_str + '.last' } - if table.used_features.arr_delete { - include_panic_deps = true - } if table.used_features.arr_insert { core_fns << ref_array_idx_str + '.insert_many' } - if pref_.ccompiler_type != .tinyc && 'no_backtrace' !in pref_.compile_defines { - // with backtrace on gcc/clang more code needs be generated - include_panic_deps = true - } if table.used_features.interpolation { include_panic_deps = true } if table.used_features.dump { include_panic_deps = true - builderptr_idx := int(table.find_type('strings.Builder').ref()) + builderptr_idx := int(table.find_type('strings.Builder').ref()).str() core_fns << [ - '${builderptr_idx}.str', - '${builderptr_idx}.free', - '${builderptr_idx}.write_rune', + builderptr_idx + '.str', + builderptr_idx + '.free', + builderptr_idx + '.write_rune', ] } if table.used_features.arr_init || table.used_features.comptime_for { @@ -205,14 +215,13 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a core_fns << 'v.trace_calls.on_call' } if 'C.cJSON_Parse' in all_fns { - all_fn_root_names << 'tos5' - all_fn_root_names << 'time.unix' // used by json + core_fns << 'tos5' + core_fns << 'time.unix' // used by json table.used_features.used_maps++ // json needs new_map etc include_panic_deps = true } - all_fn_root_names << core_fns if include_panic_deps { - all_fn_root_names << [ + core_fns << [ '__new_array_with_default', '__new_array_with_default_noscan', 'str_intp', @@ -225,6 +234,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a charptr_idx_str + '.vstring_literal', ] } + all_fn_root_names << core_fns } if pref_.is_bare { all_fn_root_names << [ @@ -238,10 +248,11 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a } is_noscan_whitelisted := pref_.gc_mode in [.boehm_full_opt, .boehm_incr_opt] + has_noscan := all_fn_root_names.any(it.contains('noscan') && it !in ['vcalloc_noscan', 'malloc_noscan']) for k, mut mfn in all_fns { - $if trace_skip_unused_all_fns ? { + if trace_skip_unused_all_fns { println('k: ${k} | mfn: ${mfn.name}') } if pref_.translated && mfn.attrs.any(it.name == 'c') { @@ -328,7 +339,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a continue } if k.starts_with('testsuite_') || k.contains('.testsuite_') { - // eprintln('>>> test suite: $k') all_fn_root_names << k continue } @@ -363,10 +373,10 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a all_fn_root_names << 'main.cb_assertion_ok' all_fn_root_names << 'main.cb_assertion_failed' if benched_tests_sym := table.find_sym('main.BenchedTests') { - bts_type := benched_tests_sym.methods[0].params[0].typ - all_fn_root_names << '${bts_type}.testing_step_start' - all_fn_root_names << '${bts_type}.testing_step_end' - all_fn_root_names << '${bts_type}.end_testing' + bts_type := benched_tests_sym.methods[0].params[0].typ.str() + all_fn_root_names << bts_type + '.testing_step_start' + all_fn_root_names << bts_type + '.testing_step_end' + all_fn_root_names << bts_type + '.end_testing' all_fn_root_names << 'main.start_testing' } } @@ -390,16 +400,18 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a interface_types := [ptype, ntype] for method in interface_info.methods { for typ in interface_types { - interface_implementation_method_name := '${int(typ.clear_flags())}.${method.name}' - $if trace_skip_unused_interface_methods ? { + interface_implementation_method_name := int(typ.clear_flags()).str() + '.' + + method.name + if trace_skip_unused_interface_methods { eprintln('>> isym.name: ${isym.name} | interface_implementation_method_name: ${interface_implementation_method_name}') } all_fn_root_names << interface_implementation_method_name } } for embed_method in table.get_embed_methods(table.sym(itype)) { - interface_implementation_method_name := '${int(embed_method.params[0].typ.clear_flags())}.${embed_method.name}' - $if trace_skip_unused_interface_methods ? { + interface_implementation_method_name := + int(embed_method.params[0].typ.clear_flags()).str() + '.' + embed_method.name + if trace_skip_unused_interface_methods { eprintln('>> isym.name: ${isym.name} | interface_implementation_method_name: ${interface_implementation_method_name} (embeded)') } all_fn_root_names << interface_implementation_method_name @@ -420,17 +432,21 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a } } for orm_type in orm_connection_implementations { - typ := int(orm_type) - all_fn_root_names << '${typ}.select' - all_fn_root_names << '${typ}.insert' - all_fn_root_names << '${typ}.update' - all_fn_root_names << '${typ}.delete' - all_fn_root_names << '${typ}.create' - all_fn_root_names << '${typ}.drop' - all_fn_root_names << '${typ}.last_id' + typ := int(orm_type).str() + all_fn_root_names << typ + '.select' + all_fn_root_names << typ + '.insert' + all_fn_root_names << typ + '.update' + all_fn_root_names << typ + '.delete' + all_fn_root_names << typ + '.create' + all_fn_root_names << typ + '.drop' + all_fn_root_names << typ + '.last_id' } } + if 'debug_used_features' in pref_.compile_defines { + dump(table.used_features) + } + mut walker := Walker.new( table: table files: ast_files @@ -483,7 +499,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a } } - $if trace_skip_unused_fn_names ? { + if trace_skip_unused_fn_names { for key, _ in walker.used_fns { println('> used fn key: ${key}') } @@ -504,11 +520,18 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a } } + if table.used_features.range_index { + walker.fn_by_name(string_idx_str + '.substr') + } + if walker.as_cast_type_names.len > 0 { + walker.fn_by_name('new_array_from_c_array') + } + table.used_features.used_fns = walker.used_fns.move() table.used_features.used_consts = walker.used_consts.move() table.used_features.used_globals = walker.used_globals.move() - $if trace_skip_unused ? { + if trace_skip_unused { eprintln('>> t.used_fns: ${table.used_features.used_fns.keys()}') eprintln('>> t.used_consts: ${table.used_features.used_consts.keys()}') eprintln('>> t.used_globals: ${table.used_features.used_globals.keys()}') @@ -554,9 +577,9 @@ fn all_fn_const_and_global(ast_files []&ast.File) (map[string]ast.FnDecl, map[st fn mark_all_methods_used(mut table ast.Table, mut all_fn_root_names []string, typ ast.Type) { sym := table.sym(typ) - styp := '${int(typ)}' + styp := int(typ).str() for method in sym.methods { - all_fn_root_names << '${styp}.${method.name}' + all_fn_root_names << styp + '.' + method.name } } @@ -572,7 +595,7 @@ fn handle_vweb(mut table ast.Table, mut all_fn_root_names []string, result_name mark_all_methods_used(mut table, mut all_fn_root_names, typ_vweb_context) for vgt in table.used_features.used_veb_types { sym_app := table.sym(vgt) - pvgt := '${int(vgt.set_nr_muls(1))}' + pvgt := int(vgt.set_nr_muls(1)).str() for m in sym_app.methods { mut skip := true if m.name == 'before_request' { @@ -586,7 +609,7 @@ fn handle_vweb(mut table ast.Table, mut all_fn_root_names []string, result_name continue } // eprintln('vgt: $vgt | pvgt: $pvgt | sym_app.name: $sym_app.name | m.name: $m.name') - all_fn_root_names << '${pvgt}.${m.name}' + all_fn_root_names << pvgt + '.' + m.name } } } diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index d48f1404414688..09d6644113299c 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -22,6 +22,8 @@ mut: all_fns map[string]ast.FnDecl all_consts map[string]ast.ConstField all_globals map[string]ast.GlobalField + // + as_cast_type_names map[string]string } pub fn Walker.new(params Walker) &Walker { @@ -367,6 +369,10 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.features.used_maps++ } else if sym.kind == .array { w.features.used_arrays++ + } else if sym.kind == .string { + if node.index is ast.RangeExpr { + w.features.range_index = true + } } } ast.InfixExpr { @@ -507,6 +513,7 @@ fn (mut w Walker) expr(node_ ast.Expr) { /// ast.AsCast { w.expr(node.expr) + w.as_cast(node) } ast.AtExpr {} ast.BoolLiteral {} @@ -726,3 +733,37 @@ pub fn (mut w Walker) or_block(node ast.OrExpr) { w.stmts(node.stmts) } } + +pub fn (mut w Walker) as_cast(node ast.AsCast) { + if node.typ == 0 { + return + } + if node.typ.has_flag(.generic) { + w.as_cast_type_names['some_generic_type'] = 'some_generic_name' + return + } + if node.expr_type == 0 { + return + } + if node.expr_type.has_flag(.generic) { + w.as_cast_type_names['some_generic_type'] = 'some_generic_name' + return + } + mut expr_type_sym := w.table.sym(node.expr_type) + if mut expr_type_sym.info is ast.SumType { + w.fill_as_cast_type_names(expr_type_sym.info.variants) + } else if mut expr_type_sym.info is ast.Interface && node.expr_type != node.typ { + w.fill_as_cast_type_names(expr_type_sym.info.types) + } +} + +fn (mut w Walker) fill_as_cast_type_names(types []ast.Type) { + for variant in types { + idx := u32(variant).str() + if idx in w.as_cast_type_names { + continue + } + variant_sym := w.table.sym(variant) + w.as_cast_type_names[idx] = variant_sym.name + } +} diff --git a/vlib/vweb/assets/assets.v b/vlib/vweb/assets/assets.v index 75c6f4cb04bce4..72ad9be0ce16fb 100644 --- a/vlib/vweb/assets/assets.v +++ b/vlib/vweb/assets/assets.v @@ -188,7 +188,7 @@ pub fn (mut am AssetManager) add(asset_type string, file string) bool { } else if asset_type == 'js' { am.js << asset } else { - panic('${unknown_asset_type_error} (${asset_type}).') + panic(unknown_asset_type_error + ' ' + asset_type + ' .') } return true } @@ -205,7 +205,7 @@ fn (am AssetManager) exists(asset_type string, file string) bool { fn (am AssetManager) get_assets(asset_type string) []Asset { if asset_type != 'css' && asset_type != 'js' { - panic('${unknown_asset_type_error} (${asset_type}).') + panic(unknown_asset_type_error + ' ' + asset_type + ' .') } assets := if asset_type == 'css' { am.css } else { am.js } return assets diff --git a/vlib/x/json2/decoder2/decode.v b/vlib/x/json2/decoder2/decode.v index ebd36084294e21..716bebc81f4e3e 100644 --- a/vlib/x/json2/decoder2/decode.v +++ b/vlib/x/json2/decoder2/decode.v @@ -1124,6 +1124,6 @@ pub fn string_buffer_to_generic_number[T](result &T, data []u8) { } *result = T(enumeration) } $else { - panic('unsupported type ${typeof[T]().name}') + panic('unsupported type ' + typeof[T]().name) } } diff --git a/vlib/x/json2/strict/strict.v b/vlib/x/json2/strict/strict.v index 374c7dda3e6213..94cda3eaf9a2ac 100644 --- a/vlib/x/json2/strict/strict.v +++ b/vlib/x/json2/strict/strict.v @@ -39,7 +39,7 @@ pub fn strict_check[T](json_data string) StructCheckResult { field_name := field.name last_key := arrays.find_last(key_struct, fn [field_name] (k KeyStruct) bool { return k.key == field_name - }) or { panic('${field.name} not found') } + }) or { panic('field not found: ' + field.name) } // TODO: get path here from `last_key.key` if last_key.value_type == .map {