diff --git a/cmd/tools/fast/fast.v b/cmd/tools/fast/fast.v index 31f386c55587bb..2d80350f9d25aa 100644 --- a/cmd/tools/fast/fast.v +++ b/cmd/tools/fast/fast.v @@ -74,6 +74,11 @@ fn main() { ret := lsystem('${vdir}/v up') if ret != 0 { elog('failed to update V, exit_code: ${ret}') + // A failed `git pull --rebase` (e.g. on a shallow CI checkout) + // leaves the worktree with conflict markers in source files, + // which would break any subsequent V compilation step. Restore + // a clean state before returning. + lsystem('cd ${vdir} && git rebase --abort') return } } diff --git a/vlib/builtin/backtraces_nix.c.v b/vlib/builtin/backtraces_nix.c.v index 07c8b0a85b6519..e1a38a4877a0a8 100644 --- a/vlib/builtin/backtraces_nix.c.v +++ b/vlib/builtin/backtraces_nix.c.v @@ -31,52 +31,56 @@ fn print_backtrace_skipping_top_frames_bsd(skipframes int) bool { eprintln('C.backtrace returned less than 2 frames') return false } - nr_actual_frames := nr_ptrs - skipframes - csymbols := C.backtrace_symbols(voidptr(&buffer[skipframes]), nr_actual_frames) - atos_lines := bsd_backtrace_resolve_atos(&buffer[skipframes], nr_actual_frames) - for i in 0 .. nr_actual_frames { - sframe := unsafe { tos2(&u8(csymbols[i])) } - mut file_line := '' - if i < atos_lines.len { - file_line = atos_lines[i] - } - // macOS format: `0 main 0x00000001047232f8 veb__run_T_main__App_main__Context + 356` - symbol_start := sframe.index('0x') or { -1 } - if symbol_start < 0 { - continue - } - rest := sframe[symbol_start..] - space_after_addr := rest.index(' ') or { -1 } - if space_after_addr < 0 { - continue - } - symbol_and_offset := rest[space_after_addr + 1..] - plus_pos := symbol_and_offset.index(' + ') or { -1 } - mut raw_symbol := symbol_and_offset - if plus_pos >= 0 { - raw_symbol = symbol_and_offset[..plus_pos] - } - // Skip C runtime frames that are not V functions: - if raw_symbol in ['main', 'start', '_main'] { - continue - } - mut demangled := '' - if plus_pos >= 0 { - demangled = demangle_v_symbol(raw_symbol) + symbol_and_offset[plus_pos..] - } else { - demangled = demangle_v_symbol(raw_symbol) + $if detailed_backtraces ? { + nr_actual_frames := nr_ptrs - skipframes + csymbols := C.backtrace_symbols(voidptr(&buffer[skipframes]), nr_actual_frames) + atos_lines := bsd_backtrace_resolve_atos(&buffer[skipframes], nr_actual_frames) + for i in 0 .. nr_actual_frames { + sframe := unsafe { tos2(&u8(csymbols[i])) } + mut file_line := '' + if i < atos_lines.len { + file_line = atos_lines[i] + } + // macOS format: `0 main 0x00000001047232f8 veb__run_T_main__App_main__Context + 356` + symbol_start := sframe.index('0x') or { -1 } + if symbol_start < 0 { + continue + } + rest := sframe[symbol_start..] + space_after_addr := rest.index(' ') or { -1 } + if space_after_addr < 0 { + continue + } + symbol_and_offset := rest[space_after_addr + 1..] + plus_pos := symbol_and_offset.index(' + ') or { -1 } + mut raw_symbol := symbol_and_offset + if plus_pos >= 0 { + raw_symbol = symbol_and_offset[..plus_pos] + } + // Skip C runtime frames that are not V functions: + if raw_symbol in ['main', 'start', '_main'] { + continue + } + mut demangled := '' + if plus_pos >= 0 { + demangled = demangle_v_symbol(raw_symbol) + symbol_and_offset[plus_pos..] + } else { + demangled = demangle_v_symbol(raw_symbol) + } + if file_line.len > 0 { + eprint(file_line) + eprint_space_padding(file_line, 45) + eprint(' | ') + eprintln(demangled) + } else { + eprintln(demangled) + } } - if file_line.len > 0 { - eprint(file_line) - eprint_space_padding(file_line, 45) - eprint(' | ') - eprintln(demangled) - } else { - eprintln(demangled) + if nr_actual_frames > 0 { + unsafe { C.free(csymbols) } } - } - if nr_actual_frames > 0 { - unsafe { C.free(csymbols) } + } $else { + C.backtrace_symbols_fd(&buffer[skipframes], nr_ptrs - skipframes, 2) } } return true diff --git a/vlib/builtin/chan_option_result.v b/vlib/builtin/chan_option_result.v index 3ee6e0d31a59e8..35c66b7d56dbe8 100644 --- a/vlib/builtin/chan_option_result.v +++ b/vlib/builtin/chan_option_result.v @@ -180,6 +180,13 @@ fn _option_clone(current &_option, mut option _option, size int) { } } +@[markused] +fn _result_ok_markused() { + mut res := _result{} + // Keep _result_ok emitted for code that constructs Result directly. + _result_ok(unsafe { nil }, mut res, 0) +} + // const none__ = IError(&None__{}) diff --git a/vlib/crypto/sha3/sha3.v b/vlib/crypto/sha3/sha3.v index dc82dfeab4104f..99bbf244954300 100644 --- a/vlib/crypto/sha3/sha3.v +++ b/vlib/crypto/sha3/sha3.v @@ -77,7 +77,8 @@ struct HashSizeError { } fn (err HashSizeError) msg() string { - return 'Hash size ${err.size} must be ${size_224}, ${size_256}, ${size_384}, or ${size_512}' + return 'Hash size ' + err.size.str() + ' must be ' + size_224.str() + ', ' + size_256.str() + + ', ' + size_384.str() + ', or ' + size_512.str() } struct AbsorptionRateError { @@ -87,7 +88,8 @@ struct AbsorptionRateError { } fn (err AbsorptionRateError) msg() string { - return 'Absorption rate ${err.rate} is not compatible with a hash size of ${err.size}' + return 'Absorption rate ' + err.rate.str() + ' is not compatible with a hash size of ' + + err.size.str() } struct XOFRateError { @@ -96,7 +98,8 @@ struct XOFRateError { } fn (err XOFRateError) msg() string { - return 'Extended output rate ${err.rate} must be ${xof_rate_128} or ${xof_rate_256}' + return 'Extended output rate ' + err.rate.str() + ' must be ' + xof_rate_128.str() + ' or ' + + xof_rate_256.str() } struct XOFSizeError { @@ -105,7 +108,7 @@ struct XOFSizeError { } fn (err XOFSizeError) msg() string { - return 'Extended output size ${err.size} must be > 0' + return 'Extended output size ' + err.size.str() + ' must be > 0' } struct Digest { @@ -146,9 +149,13 @@ pub fn new_digest(absorption_rate int, hash_size int, config PaddingConfig) !&Di .xof { validate_xof(absorption_rate, hash_size)! } } + return new_digest_unchecked(absorption_rate, hash_size, config.padding) +} + +fn new_digest_unchecked(absorption_rate int, hash_size int, padding Padding) &Digest { d := Digest{ rate: absorption_rate - suffix: u8(config.padding) + suffix: u8(padding) output_len: hash_size s: State{} } @@ -232,6 +239,10 @@ fn validate_xof(absorption_rate int, hash_size int) ! { // // This is the absorption phase of the computation. pub fn (mut d Digest) write(data []u8) ! { + d.write_bytes(data) +} + +fn (mut d Digest) write_bytes(data []u8) { // if no data is being added to the hash, // just return if data.len == 0 { @@ -279,10 +290,10 @@ pub fn (mut d Digest) write(data []u8) ! { // checksum finalizes the hash and returns the generated bytes. pub fn (mut d Digest) checksum() []u8 { - return d.checksum_internal() or { panic(err) } + return d.checksum_internal() } -fn (mut d Digest) checksum_internal() ![]u8 { +fn (mut d Digest) checksum_internal() []u8 { // pad the last input bytes to have rate bytes if d.input_buffer.len == d.rate - 1 { // a single byte pad needs to be handled specially @@ -338,56 +349,56 @@ fn (mut d Digest) checksum_internal() ![]u8 { // sum512 returns the sha3 512 bit checksum of the data. pub fn sum512(data []u8) []u8 { - mut d := new512() or { panic(err) } - d.write(data) or { panic(err) } - return d.checksum_internal() or { panic(err) } + mut d := new_digest_unchecked(rate_512, size_512, .sha3) + d.write_bytes(data) + return d.checksum_internal() } // sum384 returns the sha3 384 bit checksum of the data. pub fn sum384(data []u8) []u8 { - mut d := new384() or { panic(err) } - d.write(data) or { panic(err) } - return d.checksum_internal() or { panic(err) } + mut d := new_digest_unchecked(rate_384, size_384, .sha3) + d.write_bytes(data) + return d.checksum_internal() } // sum256 returns the sha3 256 bit checksum of the data. pub fn sum256(data []u8) []u8 { - mut d := new256() or { panic(err) } - d.write(data) or { panic(err) } - return d.checksum_internal() or { panic(err) } + mut d := new_digest_unchecked(rate_256, size_256, .sha3) + d.write_bytes(data) + return d.checksum_internal() } // sum224 returns the sha3 224 bit checksum of the data. pub fn sum224(data []u8) []u8 { - mut d := new224() or { panic(err) } - d.write(data) or { panic(err) } - return d.checksum_internal() or { panic(err) } + mut d := new_digest_unchecked(rate_224, size_224, .sha3) + d.write_bytes(data) + return d.checksum_internal() } // keccak256 returns the keccak 256 bit checksum of the data. pub fn keccak256(data []u8) []u8 { - mut d := new256keccak() or { panic(err) } - d.write(data) or { panic(err) } - return d.checksum_internal() or { panic(err) } + mut d := new_digest_unchecked(rate_256, size_256, .keccak) + d.write_bytes(data) + return d.checksum_internal() } // keccak512 returns the keccak 512 bit checksum of the data. pub fn keccak512(data []u8) []u8 { - mut d := new512keccak() or { panic(err) } - d.write(data) or { panic(err) } - return d.checksum_internal() or { panic(err) } + mut d := new_digest_unchecked(rate_512, size_512, .keccak) + d.write_bytes(data) + return d.checksum_internal() } // shake256 returns the sha3 shake256 bit extended output pub fn shake256(data []u8, output_len int) []u8 { mut d := new256xof(output_len) or { panic(err) } d.write(data) or { panic(err) } - return d.checksum_internal() or { panic(err) } + return d.checksum_internal() } // shake128 returns the sha3 shake128 bit extended output pub fn shake128(data []u8, output_len int) []u8 { mut d := new128xof(output_len) or { panic(err) } d.write(data) or { panic(err) } - return d.checksum_internal() or { panic(err) } + return d.checksum_internal() } diff --git a/vlib/crypto/sha3/sha3_state_generic.v b/vlib/crypto/sha3/sha3_state_generic.v index 73d90b24e8e261..ec92b66a1229e6 100644 --- a/vlib/crypto/sha3/sha3_state_generic.v +++ b/vlib/crypto/sha3/sha3_state_generic.v @@ -8,7 +8,6 @@ // Last updated: August 2015 module sha3 -import encoding.binary import math.bits // when the state is 1600 bits, a lane is 64 bits @@ -20,6 +19,29 @@ mut: a [5][5]Lane } +@[direct_array_access; inline] +fn read_u64_le_at(b []u8, offset int) u64 { + _ = b[offset] + _ = b[offset + 7] + return u64(b[offset]) | (u64(b[offset + 1]) << 8) | (u64(b[offset + 2]) << 16) | (u64(b[ + offset + 3]) << 24) | (u64(b[offset + 4]) << 32) | (u64(b[offset + 5]) << 40) | (u64(b[ + offset + 6]) << 48) | (u64(b[offset + 7]) << 56) +} + +@[direct_array_access; inline] +fn put_u64_le_at(mut b []u8, value u64, offset int) { + _ = b[offset] + _ = b[offset + 7] + b[offset] = u8(value) + b[offset + 1] = u8(value >> 8) + b[offset + 2] = u8(value >> 16) + b[offset + 3] = u8(value >> 24) + b[offset + 4] = u8(value >> 32) + b[offset + 5] = u8(value >> 40) + b[offset + 6] = u8(value >> 48) + b[offset + 7] = u8(value >> 56) +} + // to_bytes converts the state into a byte array // // A 1600 bit state fits into 200 bytes. @@ -35,9 +57,7 @@ fn (s State) to_bytes() []u8 { for y in 0 .. 5 { for x in 0 .. 5 { - unsafe { - binary.little_endian_put_u64_at(mut byte_array, s.a[x][y], index) - } + put_u64_le_at(mut byte_array, s.a[x][y], index) index += 8 } } @@ -57,7 +77,7 @@ fn (mut s State) from_bytes(byte_array []u8) { for y in 0 .. 5 { for x in 0 .. 5 { - s.a[x][y] = binary.little_endian_u64_at(byte_array, index) + s.a[x][y] = read_u64_le_at(byte_array, index) index += 8 } } @@ -79,7 +99,7 @@ fn (mut s State) xor_bytes(byte_array []u8, rate int) { for y in 0 .. 5 { for x in 0 .. 5 { - s.a[x][y] ^= binary.little_endian_u64_at(byte_array, index) + s.a[x][y] ^= read_u64_le_at(byte_array, index) index += 8 if index >= rate { @@ -175,12 +195,3 @@ const iota_round_constants = [u64(0x0000000000000001), 0x0000000000008082, 0x800 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008] - -fn (s State) str() string { - mut output := '\n y = 0 y = 1 y = 2 y = 3 y = 4\n' - for x in 0 .. 5 { - output += 'x = ${x}: ${s.a[x][0]:016x} ${s.a[x][1]:016x} ${s.a[x][2]:016x} ${s.a[x][3]:016x} ${s.a[x][4]:016x}\n' - } - - return output -} diff --git a/vlib/strconv/atof.c.v b/vlib/strconv/atof.c.v index a75dd22a491d3f..9afda7d6e12807 100644 --- a/vlib/strconv/atof.c.v +++ b/vlib/strconv/atof.c.v @@ -429,14 +429,13 @@ fn converter(mut pn PrepNumber) u64 { return result } -@[markused; params] +@[params] pub struct AtoF64Param { pub: allow_extra_chars bool // allow extra characters after number } // atof64 parses the string `s`, and if possible, converts it into a f64 number -@[markused] pub fn atof64(s string, param AtoF64Param) !f64 { if s.len == 0 { return error('expected a number found an empty string') diff --git a/vlib/strconv/atoi.v b/vlib/strconv/atoi.v index 7402d6edf508e7..07689fb9917633 100644 --- a/vlib/strconv/atoi.v +++ b/vlib/strconv/atoi.v @@ -147,7 +147,6 @@ pub fn common_parse_uint2(s string, _base int, _bit_size int) (u64, int) { } // parse_uint is like parse_int but for unsigned numbers. -@[markused] pub fn parse_uint(s string, _base int, _bit_size int) !u64 { return common_parse_uint(s, _base, _bit_size, true, true) } @@ -287,35 +286,31 @@ fn atoi_common(s string, type_min i64, type_max i64) !i64 { // atoi is equivalent to parse_int(s, 10, 0), converted to type int. // It follows V scanner as much as observed. -@[markused] pub fn atoi(s string) !int { return int(atoi_common(s, i64_min_int32, i64_max_int32)!) } // atoi8 is equivalent to atoi(s), converted to type i8. // returns an i8 [-128 .. 127] or an error. -@[markused] pub fn atoi8(s string) !i8 { return i8(atoi_common(s, min_i8, max_i8)!) } // atoi16 is equivalent to atoi(s), converted to type i16. // returns an i16 [-32678 .. 32767] or an error. -@[markused] pub fn atoi16(s string) !i16 { return i16(atoi_common(s, min_i16, max_i16)!) } // atoi32 is equivalent to atoi(s), converted to type i32. // returns an i32 [-2147483648 .. 2147483647] or an error. -@[markused] pub fn atoi32(s string) !i32 { return i32(atoi_common(s, min_i32, max_i32)!) } // atoi64 converts radix 10 string to i64 type. // returns an i64 [-9223372036854775808 .. 9223372036854775807] or an error. -@[direct_array_access; markused] +@[direct_array_access] pub fn atoi64(s string) !i64 { mut sign, mut start_idx := atoi_common_check(s)! mut x := i64(0) diff --git a/vlib/strconv/atou.v b/vlib/strconv/atou.v index d6197a71120b01..ad7a1cb8ae764d 100644 --- a/vlib/strconv/atou.v +++ b/vlib/strconv/atou.v @@ -70,34 +70,29 @@ fn atou_common(s string, type_max u64) !u64 { // atou8 is equivalent to parse_uint(s, 10, 0), converted to type u8. // It returns u8 in range [0..255] or an Error. -@[markused] pub fn atou8(s string) !u8 { return u8(atou_common(s, max_u8)!) } // atou16 is equivalent to parse_uint(s, 10, 0), converted to type u16. // It returns u16 in range [0..65535] or an Error. -@[markused] pub fn atou16(s string) !u16 { return u16(atou_common(s, max_u16)!) } // atou is equivalent to parse_uint(s, 10, 0), converted to type u32. -@[markused] pub fn atou(s string) !u32 { return u32(atou_common(s, max_u32)!) } // atou32 is identical to atou. Here to provide a symmetrical API with atoi/atoi32 // It returns u32 in range [0..4294967295] or an Error. -@[markused] pub fn atou32(s string) !u32 { return u32(atou_common(s, max_u32)!) } // atou64 is equivalent to parse_uint(s, 10, 0), converted to type u64. // It returns u64 in range [0..18446744073709551615] or an Error. -@[markused] pub fn atou64(s string) !u64 { return u64(atou_common(s, max_u64)!) } diff --git a/vlib/time/duration.v b/vlib/time/duration.v index 3afe4474f525a6..8828dd8f53d81f 100644 --- a/vlib/time/duration.v +++ b/vlib/time/duration.v @@ -50,6 +50,23 @@ pub fn (d Duration) days() f64 { return f64(d) / f64(hour * 24) } +fn duration_pad2(n i64) string { + if n < 10 { + return '0' + n.str() + } + return n.str() +} + +fn duration_pad3(n i64) string { + if n < 10 { + return '00' + n.str() + } + if n < 100 { + return '0' + n.str() + } + return n.str() +} + // str pretty prints the duration // // ``` @@ -82,14 +99,22 @@ pub fn (d Duration) str() string { t -= us * microsecond ns := t - return match true { - hr > 0 { '${sign}${hr}:${min:02}:${sec:02}' } - min > 0 { '${sign}${min}:${sec:02}.${ms:03}' } - sec > 0 { '${sign}${sec}.${ms:03}s' } - ms > 0 { '${sign}${ms}.${us:03}ms' } - us > 0 { '${sign}${us}.${ns:03}us' } - else { '${sign}${ns}ns' } + if hr > 0 { + return sign + hr.str() + ':' + duration_pad2(min) + ':' + duration_pad2(sec) + } + if min > 0 { + return sign + min.str() + ':' + duration_pad2(sec) + '.' + duration_pad3(ms) + } + if sec > 0 { + return sign + sec.str() + '.' + duration_pad3(ms) + 's' + } + if ms > 0 { + return sign + ms.str() + '.' + duration_pad3(us) + 'ms' + } + if us > 0 { + return sign + us.str() + '.' + duration_pad3(ns) + 'us' } + return sign + ns.str() + 'ns' } // debug returns a detailed breakdown of the Duration, as: 'Duration: - 50days, 4h, 3m, 7s, 541ms, 78us, 9ns'. @@ -116,9 +141,9 @@ pub fn (d Duration) debug() string { } } if x > 0 { - res << '${x}ns' + res << x.str() + 'ns' } - return 'Duration: ${sign}${res.join(', ')}' + return 'Duration: ' + sign + res.join(', ') } // times allows you to return fractional unit durations, based on an existing duration. diff --git a/vlib/v/gen/c/coutput_test.v b/vlib/v/gen/c/coutput_test.v index 5b6dea1948e815..2770dd9059a153 100644 --- a/vlib/v/gen/c/coutput_test.v +++ b/vlib/v/gen/c/coutput_test.v @@ -266,6 +266,35 @@ fn test_windows_sharedlive_string_interpolation_in_ternary_does_not_emit_inline_ assert compilation.output.contains('builtin__str_intp') } +fn test_simple_string_interpolation_does_not_emit_str_intp_runtime() { + os.chdir(vroot) or {} + test_source := os.join_path(os.vtmp_dir(), 'coutput_simple_interpolation_no_str_intp.vv') + os.write_file(test_source, + "module main\n\nimport time\n\nfn main() {\n\tt := time.now()\n\tprintln('elapsed \${time.since(t)}')\n}\n")! + defer { + os.rm(test_source) or {} + } + cmd := '${os.quoted_path(vexe)} -o - ${os.quoted_path(test_source)}' + compilation := os.execute(cmd) + ensure_compilation_succeeded(compilation, cmd) + assert !compilation.output.contains('builtin__str_intp') + assert !compilation.output.contains('StrIntpData') +} + +fn test_auto_str_float_array_still_emits_str_intp_runtime() { + os.chdir(vroot) or {} + test_source := os.join_path(os.vtmp_dir(), 'coutput_float_array_str_intp.vv') + os.write_file(test_source, 'module main\n\nfn main() {\n\tprintln([f32(1.2), f32(3.4)])\n}\n')! + defer { + os.rm(test_source) or {} + } + cmd := '${os.quoted_path(vexe)} -o - ${os.quoted_path(test_source)}' + compilation := os.execute(cmd) + ensure_compilation_succeeded(compilation, cmd) + assert compilation.output.contains('builtin__str_intp') + assert compilation.output.contains('StrIntpData') +} + fn test_windows_tcc_atomic_postfix_uses_interlocked_helpers() { os.chdir(vroot) or {} cc := windows_tcc_ccompiler_for_coutput_test() diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index acd9bae19f0aed..ad53fea2d24efa 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -7,6 +7,8 @@ module markused import v.ast import v.pref +const simple_string_interpolation_default_precision = 987698 + struct MethodFkeyCacheEntry { fkey string receiver ast.Type @@ -83,6 +85,7 @@ mut: uses_mem_align bool // @[aligned:N] for structs uses_eq bool // has == op uses_interp bool // string interpolation + uses_interp_isnil bool // pointer string interpolation uses_guard bool uses_orm bool uses_str map[ast.Type]bool // has .str() calls, and for which types @@ -1203,6 +1206,8 @@ fn (mut w Walker) expr(node_ ast.Expr) { || (right.idx() in [ast.u32_type_idx, ast.u64_type_idx] && left.is_signed())) } w.uses_eq = w.uses_eq || node.op in [.eq, .ne] + } else if node.op == .plus { + w.mark_string_concat_cast_helpers(node.left_type, right_type) } } ast.IfGuardExpr { @@ -1356,7 +1361,12 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.mark_by_type(node.typ) } ast.StringInterLiteral { - w.uses_interp = true + if w.string_inter_literal_needs_runtime(node) { + w.uses_interp = true + w.uses_interp_isnil = w.uses_interp_isnil || w.string_inter_literal_uses_isnil(node) + } else { + w.mark_simple_string_inter_literal(node) + } w.exprs(node.exprs) for expr in node.fwidth_exprs { if expr !is ast.EmptyExpr { @@ -1820,6 +1830,9 @@ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) { for arg in node.args { w.expr(arg.expr) } + if node.args.any(it.expr is ast.ArrayDecompose) { + w.uses_interp = true + } if node.is_variadic && node.expected_arg_types.last().has_flag(.option) { w.used_option++ } @@ -2674,6 +2687,255 @@ fn (w &Walker) infer_expr_type(expr ast.Expr) ast.Type { } } +fn (mut w Walker) string_inter_literal_needs_runtime(node ast.StringInterLiteral) bool { + if w.pref.autofree || w.pref.gc_mode == .boehm_leak { + return true + } + if w.pref.os == .windows && w.pref.is_liveshared { + return true + } + if node.exprs.len == 0 || node.expr_types.len < node.exprs.len { + return true + } + for i in 0 .. node.exprs.len { + if i >= node.need_fmts.len || node.need_fmts[i] || i >= node.fmts.len { + return true + } + if w.cur_fn_concrete_types.len > 0 && !node.need_fmts[i] { + return true + } + typ := w.resolve_current_generic_type(node.expr_types[i]) + if typ == 0 || typ == ast.void_type || typ.has_flag(.generic) { + return true + } + normalized_expr_type := w.table.fully_unaliased_type(typ) + if normalized_expr_type.is_any_kind_of_pointer() || normalized_expr_type.is_int_valptr() + || normalized_expr_type.is_float_valptr() { + return true + } + expr := node.exprs[i] + if expr is ast.Ident && expr.obj is ast.Var { + expr_var := expr.obj as ast.Var + orig_type := w.resolve_current_generic_type(expr_var.orig_type) + if orig_type != 0 + && w.table.final_sym(w.table.unaliased_type(orig_type)).kind == .interface { + return true + } + } + if w.table.final_sym(typ).kind == .interface { + return true + } + if i < node.fwidths.len && node.fwidths[i] != 0 { + return true + } + if i < node.fwidth_exprs.len && node.fwidth_exprs[i] !is ast.EmptyExpr { + return true + } + if i < node.precisions.len + && node.precisions[i] != simple_string_interpolation_default_precision { + return true + } + if i < node.precision_exprs.len && node.precision_exprs[i] !is ast.EmptyExpr { + return true + } + if i < node.pluss.len && node.pluss[i] { + return true + } + if i < node.fills.len && node.fills[i] { + return true + } + } + return false +} + +fn (mut w Walker) string_inter_literal_uses_isnil(node ast.StringInterLiteral) bool { + for typ in node.expr_types { + resolved_typ := w.table.fully_unaliased_type(w.resolve_current_generic_type(typ)) + if resolved_typ.is_any_kind_of_pointer() || resolved_typ.has_flag(.option_mut_param_t) { + return true + } + } + return false +} + +fn (mut w Walker) mark_simple_string_inter_literal(node ast.StringInterLiteral) { + if node.vals.any(it.len > 0) && !w.uses_str_literal { + w.mark_by_sym_name('string') + w.uses_str_literal = true + } + for i in 0 .. node.exprs.len { + if i >= node.expr_types.len { + break + } + typ := w.resolve_current_generic_type(node.expr_types[i]) + if typ != 0 && typ != ast.void_type && typ != ast.string_type { + w.uses_str[typ] = true + } + } +} + +fn (mut w Walker) mark_string_concat_cast_helpers(left_type ast.Type, right_type ast.Type) { + left := w.table.unaliased_type(w.resolve_current_generic_type(left_type)).clear_flags() + right := w.table.unaliased_type(w.resolve_current_generic_type(right_type)).clear_flags() + if !(left in [ast.string_type, ast.char_type, ast.rune_type] + && right in [ast.string_type, ast.char_type, ast.rune_type] + && (left == ast.string_type || right == ast.string_type)) { + return + } + for typ in [left, right] { + if typ == ast.char_type { + w.fn_by_name('${ast.u8_type_idx}.ascii_str') + } else if typ == ast.rune_type { + w.fn_by_name('${ast.rune_type_idx}.str') + } + } +} + +fn (mut w Walker) auto_str_needs_str_intp() bool { + for typ, _ in w.uses_str { + if w.type_auto_str_needs_str_intp(typ) { + return true + } + } + for typ_idx, _ in w.features.print_types { + if w.type_auto_str_needs_str_intp(ast.Type(u32(typ_idx))) { + return true + } + } + for typ_idx, _ in w.table.dumps { + if w.type_auto_str_needs_str_intp(ast.Type(u32(typ_idx))) { + return true + } + } + return false +} + +fn (mut w Walker) auto_str_needs_isnil() bool { + mut visited := map[int]bool{} + for typ, _ in w.uses_str { + if w.type_auto_str_needs_isnil(typ, mut visited) { + return true + } + } + for typ_idx, _ in w.features.print_types { + if w.type_auto_str_needs_isnil(ast.Type(u32(typ_idx)), mut visited) { + return true + } + } + for typ_idx, _ in w.table.dumps { + if w.type_auto_str_needs_isnil(ast.Type(u32(typ_idx)), mut visited) { + return true + } + } + return false +} + +fn (mut w Walker) type_auto_str_needs_str_intp(typ ast.Type) bool { + raw_typ := w.resolve_current_generic_type(typ) + if raw_typ.has_option_or_result() { + return true + } + resolved_typ := raw_typ.clear_option_and_result() + if resolved_typ == 0 || resolved_typ == ast.void_type || resolved_typ.has_flag(.generic) { + return false + } + if resolved_typ.is_ptr() || resolved_typ.is_pointer() { + return true + } + raw_sym := w.table.sym(resolved_typ) + if raw_sym.info is ast.Alias && !raw_sym.has_method('str') { + return true + } + sym := w.table.final_sym(w.table.unaliased_type(resolved_typ)) + if sym.has_method('str') { + return false + } + match sym.info { + ast.Array { + return w.array_auto_str_needs_str_intp(sym.info.elem_type) + } + ast.ArrayFixed { + return w.array_auto_str_needs_str_intp(sym.info.elem_type) + } + ast.Map { + return true + } + ast.Struct, ast.SumType, ast.Interface { + return true + } + ast.MultiReturn { + return true + } + else { + return false + } + } +} + +fn (mut w Walker) type_auto_str_needs_isnil(typ ast.Type, mut visited map[int]bool) bool { + resolved_typ := w.resolve_current_generic_type(typ).clear_option_and_result() + if resolved_typ == 0 || resolved_typ == ast.void_type || resolved_typ.has_flag(.generic) { + return false + } + if resolved_typ.is_ptr() || resolved_typ.is_pointer() + || resolved_typ.has_flag(.option_mut_param_t) { + return true + } + final_typ := w.table.unaliased_type(resolved_typ) + key := int(final_typ.idx()) + if key in visited { + return false + } + visited[key] = true + sym := w.table.final_sym(final_typ) + match sym.info { + ast.Array { + return w.type_auto_str_needs_isnil(sym.info.elem_type, mut visited) + } + ast.ArrayFixed { + return w.type_auto_str_needs_isnil(sym.info.elem_type, mut visited) + } + ast.Map { + return w.type_auto_str_needs_isnil(sym.info.key_type, mut visited) + || w.type_auto_str_needs_isnil(sym.info.value_type, mut visited) + } + ast.Struct { + return sym.info.fields.any(w.type_auto_str_needs_isnil(it.typ, mut visited)) + } + ast.SumType { + return sym.info.variants.any(w.type_auto_str_needs_isnil(it, mut visited)) + } + ast.Interface { + return true + } + else { + return false + } + } +} + +fn (mut w Walker) array_auto_str_needs_str_intp(elem_type ast.Type) bool { + resolved_with_flags := w.resolve_current_generic_type(elem_type) + if resolved_with_flags.has_flag(.option) || resolved_with_flags.has_flag(.result) { + return true + } + resolved_elem_type := resolved_with_flags.clear_option_and_result() + if resolved_elem_type == 0 || resolved_elem_type == ast.void_type + || resolved_elem_type.has_flag(.generic) { + return false + } + if resolved_elem_type.is_ptr() || resolved_elem_type.is_pointer() { + return true + } + elem_sym := w.table.final_sym(w.table.unaliased_type(resolved_elem_type)) + if elem_sym.kind in [.f32, .f64, .rune, .string] { + return true + } + return + elem_sym.kind in [.array, .array_fixed, .map, .struct, .sum_type, .interface, .multi_return] + && !elem_sym.has_method('str') +} + @[inline] pub fn (mut w Walker) or_block(node ast.OrExpr) { if node.kind == .block { @@ -3331,10 +3593,17 @@ fn (mut w Walker) mark_resource_dependencies() { w.fn_by_name('str_intp') } } - if w.features.auto_str_ptr { + if w.features.auto_str_ptr && (w.features.auto_str || w.features.dump || w.uses_dump) { w.fn_by_name('isnil') w.fn_by_name('tos4') + } + if w.uses_interp_isnil || w.auto_str_needs_isnil() { + w.fn_by_name('isnil') + } + if w.auto_str_needs_str_intp() { w.fn_by_name('str_intp') + w.mark_by_sym_name('StrIntpData') + w.mark_by_sym_name('StrIntpMem') } if w.uses_channel { w.fn_by_name('sync.new_channel_st') @@ -3668,13 +3937,15 @@ pub fn (mut w Walker) finalize(include_panic_deps bool) { w.fn_by_name('__new_array_with_default_noscan') w.fn_by_name(ref_array_idx_str + '.push_noscan') } - w.fn_by_name('str_intp') w.fn_by_name('__new_array_with_default') w.fn_by_name(ref_array_idx_str + '.push') w.fn_by_name(string_idx_str + '.substr') w.fn_by_name('v_fixed_index') - w.mark_by_sym_name('StrIntpData') - w.mark_by_sym_name('StrIntpMem') + if w.uses_asserts || w.uses_debugger || w.uses_interp { + w.fn_by_name('str_intp') + w.mark_by_sym_name('StrIntpData') + w.mark_by_sym_name('StrIntpMem') + } } if w.uses_eq { w.fn_by_name('fast_string_eq') diff --git a/vlib/v/tests/skip_unused/dump_array_of_multi_return.run.out b/vlib/v/tests/skip_unused/dump_array_of_multi_return.run.out new file mode 100644 index 00000000000000..b106a788b4dfc0 --- /dev/null +++ b/vlib/v/tests/skip_unused/dump_array_of_multi_return.run.out @@ -0,0 +1 @@ +[vlib/v/tests/skip_unused/dump_array_of_multi_return.vv:9] xs: [(3.14, 'hello'), (3.14, 'hello')] diff --git a/vlib/v/tests/skip_unused/dump_array_of_multi_return.vv b/vlib/v/tests/skip_unused/dump_array_of_multi_return.vv new file mode 100644 index 00000000000000..603128358b1be6 --- /dev/null +++ b/vlib/v/tests/skip_unused/dump_array_of_multi_return.vv @@ -0,0 +1,10 @@ +module main + +fn pair() (f64, string) { + return 3.14, 'hello' +} + +fn main() { + xs := [pair(), pair()] + dump(xs) +} diff --git a/vlib/v/tests/skip_unused/dump_multi_return.run.out b/vlib/v/tests/skip_unused/dump_multi_return.run.out new file mode 100644 index 00000000000000..78634b56156150 --- /dev/null +++ b/vlib/v/tests/skip_unused/dump_multi_return.run.out @@ -0,0 +1 @@ +[vlib/v/tests/skip_unused/dump_multi_return.vv:8] pair(): (3.14, 'hello') diff --git a/vlib/v/tests/skip_unused/dump_multi_return.vv b/vlib/v/tests/skip_unused/dump_multi_return.vv new file mode 100644 index 00000000000000..eaa83ded3cc96a --- /dev/null +++ b/vlib/v/tests/skip_unused/dump_multi_return.vv @@ -0,0 +1,9 @@ +module main + +fn pair() (f64, string) { + return 3.14, 'hello' +} + +fn main() { + dump(pair()) +}