diff --git a/cmd/tools/modules/vgit/vgit.v b/cmd/tools/modules/vgit/vgit.v index 5c5175c6654e83..b777269d18fbe0 100644 --- a/cmd/tools/modules/vgit/vgit.v +++ b/cmd/tools/modules/vgit/vgit.v @@ -83,14 +83,14 @@ pub fn clone_or_pull(remote_git_url string, local_worktree_path string) { // Note: after clone_or_pull, the current repo branch is === HEAD === master if os.is_dir(local_worktree_path) && os.is_dir(os.join_path_single(local_worktree_path, '.git')) { // Already existing ... Just pulling in this case is faster usually. - scripting.run('git -C "${local_worktree_path}" checkout --quiet master') - scripting.run('git -C "${local_worktree_path}" pull --quiet ') + scripting.run('git -C "${local_worktree_path}" checkout --quiet master') + scripting.run('git -C "${local_worktree_path}" pull --quiet ') } else { // Clone a fresh local tree. if remote_git_url.starts_with('http') { // cloning an https remote with --filter=blob:none is usually much less bandwidth intensive, at the // expense of doing small network ops later when using checkouts. - scripting.run('git clone --filter=blob:none --quiet "${remote_git_url}" "${local_worktree_path}" ') + scripting.run('git clone --filter=blob:none --quiet "${remote_git_url}" "${local_worktree_path}" ') return } mut is_blobless_clone := false @@ -112,7 +112,7 @@ pub fn clone_or_pull(remote_git_url string, local_worktree_path string) { // at the expense of a little more space usage, which will make the new tree in local_worktree_path, // exactly 1:1 the same, as the one in remote_git_url, just independent from it . copy_cmd := if os.user_os() == 'windows' { 'robocopy /MIR' } else { 'rsync -a' } - scripting.run('${copy_cmd} "${remote_git_url}/" "${local_worktree_path}/"') + scripting.run('${copy_cmd} "${remote_git_url}/" "${local_worktree_path}/"') return } scripting.run('git clone --quiet "${remote_git_url}" "${local_worktree_path}" ') @@ -121,7 +121,8 @@ pub fn clone_or_pull(remote_git_url string, local_worktree_path string) { pub struct VGitContext { pub: - cc string = 'cc' // what compiler to use + cc string = 'cc' // what C compiler to use for bootstrapping + cc_options string // what additional C compiler options to use for bootstrapping workdir string = '/tmp' // the base working folder commit_v string = 'master' // the commit-ish that needs to be prepared path_v string // where is the local working copy v repo @@ -195,12 +196,16 @@ pub fn (mut vgit_context VGitContext) compile_oldv_if_needed() { mut c_flags := '-std=gnu11 -I ./thirdparty/stdatomic/nix -w' mut c_ldflags := '-lm -lpthread' mut vc_source_file_location := os.join_path_single(vgit_context.path_vc, 'v.c') - mut vc_v_bootstrap_flags := '' + mut vc_v_cpermissive_flags := '${vgit_context.cc_options} -Wno-error=incompatible-pointer-types -Wno-error=implicit-function-declaration -Wno-error=int-conversion' // after 85b58b0 2021-09-28, -no-parallel is supported, and can be used to force the cgen stage to be single threaded, which increases the chances of successful bootstraps + mut vc_v_bootstrap_flags := '' if vgit_context.commit_v__ts >= 1632778086 { - vc_v_bootstrap_flags += '-no-parallel' + vc_v_bootstrap_flags += ' -no-parallel' } - scripting.verbose_trace(@FN, 'vc_v_bootstrap_flags: ${vc_v_bootstrap_flags} | vgit_context.commit_v__ts: ${vgit_context.commit_v__ts}') + vc_v_bootstrap_flags = vc_v_bootstrap_flags.trim_space() + scripting.verbose_trace(@FN, 'vc_v_bootstrap_flags: ${vc_v_bootstrap_flags}') + scripting.verbose_trace(@FN, 'vc_v_cpermissive_flags: ${vc_v_cpermissive_flags}') + scripting.verbose_trace(@FN, 'vgit_context.commit_v__ts: ${vgit_context.commit_v__ts}') if 'windows' == os.user_os() { c_flags = '-std=c99 -I ./thirdparty/stdatomic/win -w' @@ -219,11 +224,11 @@ pub fn (mut vgit_context VGitContext) compile_oldv_if_needed() { if vgit_context.commit_v__ts >= 1699341818 && !vgit_context.cc.contains('msvc') { c_flags += '-lws2_32' } - command_for_building_v_from_c_source = '${vgit_context.cc} ${c_flags} -o cv.exe "${vc_source_file_location}" ${c_ldflags}' - command_for_selfbuilding = '.\\cv.exe ${vc_v_bootstrap_flags} -o ${vgit_context.vexename} {SOURCE}' + command_for_building_v_from_c_source = c(vgit_context.cc, '${vc_v_cpermissive_flags} ${c_flags} -o cv.exe "${vc_source_file_location}" ${c_ldflags}') + command_for_selfbuilding = c('.\\cv.exe', '${vc_v_bootstrap_flags} -o ${vgit_context.vexename} {SOURCE}') } else { - command_for_building_v_from_c_source = '${vgit_context.cc} ${c_flags} -o cv "${vc_source_file_location}" ${c_ldflags}' - command_for_selfbuilding = './cv ${vc_v_bootstrap_flags} -o ${vgit_context.vexename} {SOURCE}' + command_for_building_v_from_c_source = c(vgit_context.cc, '${vc_v_cpermissive_flags} ${c_flags} -o cv "${vc_source_file_location}" ${c_ldflags}') + command_for_selfbuilding = c('./cv', '${vc_v_bootstrap_flags} -o ${vgit_context.vexename} {SOURCE}') } scripting.run(command_for_building_v_from_c_source) @@ -233,6 +238,11 @@ pub fn (mut vgit_context VGitContext) compile_oldv_if_needed() { // which should be a valid working V executable. } +fn c(cmd string, params string) string { + // compose a command, while reducing the potential whitespaces, due to all the interpolations of optional flags above + return '${cmd} ${params.trim_space()}' +} + pub struct VGitOptions { pub mut: workdir string = os.temp_dir() // the working folder (typically /tmp), where the tool will write diff --git a/cmd/tools/oldv.v b/cmd/tools/oldv.v index bda9eb96130e76..f3757638da7f5d 100644 --- a/cmd/tools/oldv.v +++ b/cmd/tools/oldv.v @@ -34,12 +34,13 @@ mut: path_v string // the full path to the v folder inside workdir. path_vc string // the full path to the vc folder inside workdir. cmd_to_run string // the command that you want to run *in* the oldv repo + cleanup bool // should the tool run a cleanup first + use_cache bool // use local cached copies for --vrepo and --vcrepo in + fresh_tcc bool // do use `make fresh_tcc` + is_bisect bool // bisect mode; usage: `cmd/tools/oldv -b -c './v run bug.v'` + show_vccommit bool // show the V and VC commits, corresponding to the V commit-ish, that can be used to build V cc string = 'cc' // the C compiler to use for bootstrapping. - cleanup bool // should the tool run a cleanup first - use_cache bool // use local cached copies for --vrepo and --vcrepo in - fresh_tcc bool // do use `make fresh_tcc` - is_bisect bool // bisect mode; usage: `cmd/tools/oldv -b -c './v run bug.v'` - show_vccommit bool // show the V and VC commits, corresponding to the V commit-ish, that can be used to build V + cc_options string // additional options to pass to the C compiler while bootstrapping. } fn (mut c Context) compile_oldv_if_needed() { @@ -48,6 +49,7 @@ fn (mut c Context) compile_oldv_if_needed() { v_repo_url: c.vgo.v_repo_url vc_repo_url: c.vgo.vc_repo_url cc: c.cc + cc_options: c.cc_options commit_v: c.commit_v path_v: c.path_v path_vc: c.path_vc @@ -103,7 +105,11 @@ fn sync_cache() { } fn main() { - scripting.used_tools_must_exist(['git']) + if os.user_os() == 'windows' { + scripting.used_tools_must_exist(['git', 'wc', 'make', 'robocopy']) + } else { + scripting.used_tools_must_exist(['git', 'wc', 'make', 'rsync', 'cc']) + } // Resetting VEXE here allows for `v run cmd/tools/oldv.v'. // the parent V would have set VEXE, which later will @@ -126,19 +132,28 @@ fn main() { context.vgo.v_repo_url = 'https://github.com/vlang/v' context.vgo.vc_repo_url = 'https://github.com/vlang/vc' } - should_sync := fp.bool('cache-sync', `s`, false, 'Update the local cache') - context.is_bisect = fp.bool('bisect', `b`, false, 'Bisect mode. Use the current commit in the repo where oldv is.') - if !should_sync && !context.is_bisect { - fp.limit_free_args(1, 1)! + context.cc = fp.string('cc', 0, 'cc', 'Use this C compiler for bootstrapping v.c (defaults to `cc`).') + context.cc_options = fp.string('ccoptions', 0, '', 'Use these C compiler options for bootstrapping v.c (defaults to ``).') + env_cc_options := os.getenv('OLDV_CCOPTIONS') + if env_cc_options != '' { + context.cc_options = env_cc_options } - //// context.cleanup = fp.bool('clean', 0, false, 'Clean before running (slower).') context.fresh_tcc = fp.bool('fresh_tcc', 0, true, 'Do `make fresh_tcc` when preparing a V compiler.') context.cmd_to_run = fp.string('command', `c`, '', 'Command to run in the old V repo.\n') context.show_vccommit = fp.bool('show_VC_commit', 0, false, 'Show the VC commit, that can be used to compile the given V commit, and exit.\n') + context.is_bisect = fp.bool('bisect', `b`, false, 'Bisect mode. Use the current commit in the repo where oldv is.') + + should_sync := fp.bool('cache-sync', `s`, false, 'Update the local cache') + if !should_sync && !context.is_bisect { + fp.limit_free_args(1, 1)! + } + + //// commits := vgit.add_common_tool_options(mut context.vgo, mut fp) if should_sync { sync_cache() + println('Cache synced, cache folder: ${cache_oldv_folder} .') exit(0) } if context.use_cache { diff --git a/vlib/net/mbedtls/ssl_connection.c.v b/vlib/net/mbedtls/ssl_connection.c.v index 11f97861960458..dec82b2253b6bc 100644 --- a/vlib/net/mbedtls/ssl_connection.c.v +++ b/vlib/net/mbedtls/ssl_connection.c.v @@ -8,6 +8,9 @@ const ctr_drbg = C.mbedtls_ctr_drbg_context{} const entropy = C.mbedtls_entropy_context{} +const mbedtls_client_read_timeout_ms = $d('mbedtls_client_read_timeout_ms', 550) +const mbedtls_server_read_timeout_ms = $d('mbedtls_server_read_timeout_ms', 41_000) + fn init() { $if trace_ssl ? { eprintln(@METHOD) @@ -176,7 +179,10 @@ fn (mut l SSLListener) init() ! { C.mbedtls_net_init(&l.server_fd) C.mbedtls_ssl_init(&l.ssl) C.mbedtls_ssl_config_init(&l.conf) - C.mbedtls_ssl_conf_read_timeout(&l.conf, 41_000) + $if trace_mbedtls_timeouts ? { + dump(mbedtls_server_read_timeout_ms) + } + C.mbedtls_ssl_conf_read_timeout(&l.conf, mbedtls_server_read_timeout_ms) l.certs = &SSLCerts{} C.mbedtls_x509_crt_init(&l.certs.client_cert) C.mbedtls_pk_init(&l.certs.client_key) @@ -371,7 +377,10 @@ fn (mut s SSLConn) init() ! { if ret != 0 { return error_with_code('Failed to set SSL configuration', ret) } - C.mbedtls_ssl_conf_read_timeout(&s.conf, 550) + $if trace_mbedtls_timeouts ? { + dump(mbedtls_client_read_timeout_ms) + } + C.mbedtls_ssl_conf_read_timeout(&s.conf, mbedtls_client_read_timeout_ms) unsafe { C.mbedtls_ssl_conf_rng(&s.conf, C.mbedtls_ctr_drbg_random, &ctr_drbg) diff --git a/vlib/orm/README.md b/vlib/orm/README.md index 5252f89fd598cb..54ec119d8e191c 100644 --- a/vlib/orm/README.md +++ b/vlib/orm/README.md @@ -33,7 +33,8 @@ struct Foo { - `[sql_type: 'SQL TYPE']` explicitly sets the type in SQL - `[default: 'raw_sql']` inserts `raw_sql` verbatim in a "DEFAULT" clause when creating a new table, allowing for SQL functions like `CURRENT_TIME`. For raw strings, - surround `raw_sql` with backticks (`). + surround `raw_sql` with backticks (\`). + - `[fkey: 'parent_id']` sets foreign key for an field which holds an array ## Usage diff --git a/vlib/os/os.v b/vlib/os/os.v index e2cd8d11a5624c..db5f5530261767 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -371,17 +371,24 @@ pub fn get_lines() []string { // get_lines_joined returns a string of the values read from stdin. // reading is stopped when an empty line is read. pub fn get_lines_joined() string { + return get_lines().join('') +} + +// get_raw_lines reads *all* input lines from stdin, as an array of strings. +// Note: unlike os.get_lines, empty lines (that contain only `\r\n` or `\n`), +// will be present in the output. +// Reading is stopped, only on EOF of stdin. +pub fn get_raw_lines() []string { mut line := '' - mut inputstr := '' + mut lines := []string{} for { - line = get_line() + line = get_raw_line() if line.len <= 0 { break } - line = line.trim_space() - inputstr += line + lines << line } - return inputstr + return lines } // get_raw_lines_joined reads *all* input lines from stdin. @@ -390,17 +397,7 @@ pub fn get_lines_joined() string { // the output. // Reading is stopped, only on EOF of stdin. pub fn get_raw_lines_joined() string { - mut line := '' - mut lines := []string{} - for { - line = get_raw_line() - if line.len <= 0 { - break - } - lines << line - } - res := lines.join('') - return res + return get_raw_lines().join('') } // user_os returns the current user's operating system name. diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 33901bbabb7f58..b87aa24ac052a0 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -3637,6 +3637,19 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as } } node.return_type = ast.void_type + } else if method_name == 'delete_many' { + if node.args.len != 2 { + c.error('`.delete_many()` expected 2 arguments, but got ${node.args.len}', + node.pos) + } else { + for i, mut arg in node.args { + arg_typ := c.expr(mut arg.expr) + c.check_expected_call_arg(arg_typ, ast.int_type, node.language, arg) or { + c.error('${err.msg()} in argument ${i + 1} to `.delete_many()`', arg.pos) + } + } + } + node.return_type = ast.void_type } else if method_name == 'reverse' { c.table.used_features.arr_reverse = true } diff --git a/vlib/v/checker/tests/generics_fn_return_generic_closure_err.out b/vlib/v/checker/tests/generics_fn_return_generic_closure_err.out new file mode 100644 index 00000000000000..568c1f6f0bf180 --- /dev/null +++ b/vlib/v/checker/tests/generics_fn_return_generic_closure_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/generics_fn_return_generic_closure_err.vv:2:9: error: cannot use `fn (f64) []f64` as type `fn ([]f64) []f64` in return argument + 1 | fn vectorize[T](op fn (T) T) fn ([]T) []T { + 2 | return fn [op] [T](values T) []T { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 3 | mut result := []T{} + 4 | return result diff --git a/vlib/v/checker/tests/generics_fn_return_generic_closure_err.vv b/vlib/v/checker/tests/generics_fn_return_generic_closure_err.vv new file mode 100644 index 00000000000000..5c321b72c3ed38 --- /dev/null +++ b/vlib/v/checker/tests/generics_fn_return_generic_closure_err.vv @@ -0,0 +1,16 @@ +fn vectorize[T](op fn (T) T) fn ([]T) []T { + return fn [op] [T](values T) []T { + mut result := []T{} + return result + } +} + +fn add_one(x f64) f64 { + return x + 1 +} + +fn main() { + vadd := vectorize[f64](add_one) + v := [1.0, 2, 3, 4] + println(vadd(v)) +} diff --git a/vlib/v/gen/c/array.v b/vlib/v/gen/c/array.v index c2399b94234088..4130bcc5654b26 100644 --- a/vlib/v/gen/c/array.v +++ b/vlib/v/gen/c/array.v @@ -1649,7 +1649,7 @@ fn (mut g Gen) fixed_array_init_with_cast(expr ast.ArrayInit, typ ast.Type) { } } -fn (mut g Gen) fixed_array_update_expr_field(expr_str string, field_type ast.Type, field_name string, is_auto_deref bool, elem_type ast.Type, size int) { +fn (mut g Gen) fixed_array_update_expr_field(expr_str string, field_type ast.Type, field_name string, is_auto_deref bool, elem_type ast.Type, size int, is_update_embed bool) { elem_sym := g.table.sym(elem_type) if !g.inside_array_fixed_struct { g.write('{') @@ -1657,15 +1657,20 @@ fn (mut g Gen) fixed_array_update_expr_field(expr_str string, field_type ast.Typ g.write('}') } } + embed_field := if is_update_embed { + g.get_embed_field_name(field_type, field_name) + } else { + '' + } for i in 0 .. size { if elem_sym.info is ast.ArrayFixed { init_str := if g.inside_array_fixed_struct { '${expr_str}' } else { - '${expr_str}->${c_name(field_name)}[${i}]' + '${expr_str}->${embed_field}${c_name(field_name)}[${i}]' } g.fixed_array_update_expr_field(init_str, field_type, field_name, is_auto_deref, - elem_sym.info.elem_type, elem_sym.info.size) + elem_sym.info.elem_type, elem_sym.info.size, is_update_embed) } else { g.write(expr_str) if !expr_str.ends_with(']') { @@ -1674,11 +1679,12 @@ fn (mut g Gen) fixed_array_update_expr_field(expr_str string, field_type ast.Typ } else { g.write('.') } + if is_update_embed { + g.write(embed_field) + } g.write(c_name(field_name)) } - if !expr_str.starts_with('(') && !expr_str.starts_with('{') { - g.write('[${i}]') - } + g.write('[${i}]') } if i != size - 1 { g.write(', ') diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 0c7075bae9a5cd..b9e54cd18a5293 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -6119,8 +6119,7 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) { g.const_decl_init_later_msvc_string_fixed_array(field.mod, name, field.expr, field.typ) } else { - g.const_decl_init_later(field.mod, name, field.expr, field.typ, false, - false) + g.const_decl_init_later(field.mod, name, field.expr, field.typ, false) } } ast.StringLiteral { @@ -6136,15 +6135,14 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) { ast.CallExpr { if field.expr.return_type.has_flag(.option) || field.expr.return_type.has_flag(.result) { + old_inside_const_opt_or_res := g.inside_const_opt_or_res g.inside_const_opt_or_res = true unwrap_opt_res := field.expr.or_block.kind != .absent - g.const_decl_init_later(field.mod, name, field.expr, field.typ, unwrap_opt_res, - unwrap_opt_res) + g.const_decl_init_later(field.mod, name, field.expr, field.typ, unwrap_opt_res) + g.inside_const_opt_or_res = old_inside_const_opt_or_res } else { - g.const_decl_init_later(field.mod, name, field.expr, field.typ, false, - false) + g.const_decl_init_later(field.mod, name, field.expr, field.typ, false) } - g.inside_const_opt_or_res = false } else { // Note: -usecache uses prebuilt modules, each compiled with: @@ -6184,8 +6182,7 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) { continue } } - g.const_decl_init_later(field.mod, name, field.expr, field.typ, false, - false) + g.const_decl_init_later(field.mod, name, field.expr, field.typ, false) } else if field.expr is ast.InfixExpr { mut has_unwrap_opt_res := false if field.expr.left is ast.CallExpr { @@ -6193,11 +6190,9 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) { } else if field.expr.right is ast.CallExpr { has_unwrap_opt_res = field.expr.right.or_block.kind != .absent } - g.const_decl_init_later(field.mod, name, field.expr, field.typ, false, - has_unwrap_opt_res) + g.const_decl_init_later(field.mod, name, field.expr, field.typ, has_unwrap_opt_res) } else { - g.const_decl_init_later(field.mod, name, field.expr, field.typ, false, - true) + g.const_decl_init_later(field.mod, name, field.expr, field.typ, true) } } } @@ -6359,8 +6354,7 @@ fn (mut g Gen) c_const_name(name string) string { return if g.pref.translated && !g.is_builtin_mod { name } else { '_const_${name}' } } -fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ ast.Type, unwrap_option bool, - surround_cbr bool) { +fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ ast.Type, surround_cbr bool) { // Initialize more complex consts in `void _vinit/2{}` // (C doesn't allow init expressions that can't be resolved at compile time). mut styp := g.styp(typ) @@ -6370,9 +6364,7 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ if surround_cbr { init.writeln('{') } - if unwrap_option { - init.writeln(g.expr_string_surround('\t${cname} = *(${styp}*)', expr, '.data;')) - } else if expr is ast.ArrayInit && (expr as ast.ArrayInit).has_index { + if expr is ast.ArrayInit && (expr as ast.ArrayInit).has_index { init.writeln(g.expr_string_surround('\tmemcpy(&${cname}, &', expr, ', sizeof(${styp}));')) } else if expr is ast.CallExpr && g.table.final_sym(g.unwrap_generic((expr as ast.CallExpr).return_type)).kind == .array_fixed { diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 0f7522ed1386cf..a490f7f58f339e 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -949,6 +949,10 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { tmp_opt := if gen_or || gen_keep_alive { if g.inside_curry_call && g.last_tmp_call_var.len > 0 { g.last_tmp_call_var.pop() + } else if !g.inside_or_block { + new_tmp := g.new_tmp_var() + g.last_tmp_call_var << new_tmp + new_tmp } else { g.new_tmp_var() } @@ -1031,7 +1035,11 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { g.write('\n ${cur_line}') } } else { - g.write('\n ${cur_line} ${tmp_opt}') + if !g.inside_or_block && g.last_tmp_call_var.len > 0 { + g.write('\n\t*(${unwrapped_styp}*)${g.last_tmp_call_var.pop()}.data = ${cur_line}(*(${unwrapped_styp}*)${tmp_opt}.data)') + } else { + g.write('\n ${cur_line}(*(${unwrapped_styp}*)${tmp_opt}.data)') + } } } } else if gen_keep_alive { @@ -1150,12 +1158,16 @@ fn (mut g Gen) gen_array_method_call(node ast.CallExpr, left_type ast.Type, left 'all' { g.gen_array_all(node) } - 'delete', 'drop', 'delete_last' { + 'delete', 'drop', 'delete_last', 'delete_many' { g.write('array_${node.name}(') g.gen_arg_from_type(left_type, node.left) if node.name != 'delete_last' { g.write(', ') g.expr(node.args[0].expr) + if node.name == 'delete_many' { + g.write(', ') + g.expr(node.args[1].expr) + } } g.write(')') } diff --git a/vlib/v/gen/c/json.v b/vlib/v/gen/c/json.v index 6ca497f5576267..4209e9bc5f4106 100644 --- a/vlib/v/gen/c/json.v +++ b/vlib/v/gen/c/json.v @@ -139,7 +139,7 @@ ${enc_fn_dec} { \tcJSON *o;') if is_js_prim(sym.name) && utyp.is_ptr() { g.gen_prim_enc_dec(utyp, mut enc, mut dec) - } else if sym.kind == .array || sym.kind == .array_fixed { + } else if sym.kind in [.array, .array_fixed] { array_size := if sym.kind == .array_fixed { (sym.info as ast.ArrayFixed).size } else { diff --git a/vlib/v/gen/c/reflection.v b/vlib/v/gen/c/reflection.v index 5caf3b9323fcaa..b138e9dc0504b7 100644 --- a/vlib/v/gen/c/reflection.v +++ b/vlib/v/gen/c/reflection.v @@ -95,7 +95,11 @@ fn (g &Gen) gen_attrs_array(attrs []ast.Attr) string { } mut out := 'new_array_from_c_array(${attrs.len},${attrs.len},sizeof(string),' out += '_MOV((string[${attrs.len}]){' - out += attrs.map(if it.has_arg { '_SLIT("${it.name}=${it.arg}")' } else { '_SLIT("${it.name}")' }).join(',') + out += attrs.map(if it.has_arg { + '_SLIT("${it.name}=${escape_quotes(it.arg)}")' + } else { + '_SLIT("${it.name}")' + }).join(',') out += '}))' return out } diff --git a/vlib/v/gen/c/struct.v b/vlib/v/gen/c/struct.v index 91638d53767856..5934f649514c0c 100644 --- a/vlib/v/gen/c/struct.v +++ b/vlib/v/gen/c/struct.v @@ -311,7 +311,8 @@ fn (mut g Gen) struct_init(node ast.StructInit) { is_arr_fixed = true g.fixed_array_update_expr_field(g.expr_string(node.update_expr), node.update_expr_type, field.name, node.update_expr.is_auto_deref_var(), - update_expr_sym.info.elem_type, update_expr_sym.info.size) + update_expr_sym.info.elem_type, update_expr_sym.info.size, + node.is_update_embed) } else { g.write('(') g.expr(node.update_expr) @@ -325,20 +326,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) { g.write('.') } if node.is_update_embed { - update_sym := g.table.sym(node.update_expr_type) - _, embeds := g.table.find_field_from_embeds(update_sym, field.name) or { - ast.StructField{}, []ast.Type{} - } - for embed in embeds { - esym := g.table.sym(embed) - ename := esym.embed_name() - g.write(ename) - if embed.is_ptr() { - g.write('->') - } else { - g.write('.') - } - } + g.write(g.get_embed_field_name(node.update_expr_type, field.name)) } g.write(c_name(field.name)) } @@ -401,6 +389,24 @@ fn (mut g Gen) struct_init(node ast.StructInit) { } } +fn (mut g Gen) get_embed_field_name(field_type ast.Type, field_name string) string { + update_sym := g.table.sym(field_type) + _, embeds := g.table.find_field_from_embeds(update_sym, field_name) or { + ast.StructField{}, []ast.Type{} + } + mut s := '' + for embed in embeds { + esym := g.table.sym(embed) + ename := esym.embed_name() + if embed.is_ptr() { + s += '${ename}->' + } else { + s += '${ename}.' + } + } + return s +} + fn (mut g Gen) zero_struct_field(field ast.StructField) bool { old_inside_cast_in_heap := g.inside_cast_in_heap g.inside_cast_in_heap = 0 diff --git a/vlib/v/markused/markused.v b/vlib/v/markused/markused.v index 0ca5a031626d4f..496a585331285b 100644 --- a/vlib/v/markused/markused.v +++ b/vlib/v/markused/markused.v @@ -242,6 +242,10 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a all_fn_root_names << k continue } + if pref_.translated && mfn.attrs.any(it.name == 'c') { + all_fn_root_names << k + continue + } // _noscan functions/methods are selected when the `-gc boehm` is on: if has_noscan && is_noscan_whitelisted && mfn.name.ends_with('_noscan') { all_fn_root_names << k @@ -497,9 +501,15 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a for kcon, con in all_consts { if pref_.is_shared && con.is_pub { walker.mark_const_as_used(kcon) + continue } if !pref_.is_shared && con.is_pub && con.name.starts_with('main.') { walker.mark_const_as_used(kcon) + continue + } + if pref_.translated && con.attrs.any(it.name == 'export') { + walker.mark_const_as_used(kcon) + continue } } @@ -529,7 +539,9 @@ fn all_fn_const_and_global(ast_files []&ast.File) (map[string]ast.FnDecl, map[st match node { ast.FnDecl { fkey := node.fkey() - all_fns[fkey] = node + if fkey !in all_fns || !node.no_body { + all_fns[fkey] = node + } } ast.ConstDecl { for cfield in node.fields { diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index d7d38fd355173c..3e746fe29eceaa 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -610,6 +610,9 @@ pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) { if w.used_fns[fkey] { return } + if node.no_body { + return + } w.mark_fn_as_used(fkey) w.stmts(node.stmts) w.defer_stmts(node.defer_stmts) diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 146291971257fb..6cf6d0a7b76e1c 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -889,7 +889,11 @@ fn (mut p Parser) anon_fn() ast.AnonFn { p.cur_fn_name = keep_fn_name func.name = name idx := p.table.find_or_register_fn_type(func, true, false) - typ := ast.new_type(idx) + typ := if generic_names.len > 0 { + ast.new_type(idx).set_flag(.generic) + } else { + ast.new_type(idx) + } p.inside_defer = old_inside_defer // name := p.table.get_type_name(typ) return ast.AnonFn{ diff --git a/vlib/v/tests/builtin_arrays/array_delete_many_test.v b/vlib/v/tests/builtin_arrays/array_delete_many_test.v new file mode 100644 index 00000000000000..24ce31e82fb8e2 --- /dev/null +++ b/vlib/v/tests/builtin_arrays/array_delete_many_test.v @@ -0,0 +1,17 @@ +struct Test[T] { +mut: + values []T +} + +fn (mut t Test[T]) delete_many(start int, end int) { + t.values.delete_many(start, end) +} + +fn test_main() { + mut x := Test[int]{} + x.values = []int{len: 5, init: index} + x.delete_many(1, 3) + assert x.values.len == 2 + assert x.values[0] == 0 + assert x.values[1] == 4 +} diff --git a/vlib/v/tests/fixed_array_2_dims_embed_test.v b/vlib/v/tests/fixed_array_2_dims_embed_test.v new file mode 100644 index 00000000000000..8944a3ca5ea18c --- /dev/null +++ b/vlib/v/tests/fixed_array_2_dims_embed_test.v @@ -0,0 +1,48 @@ +module main + +type Mat4 = [16]f32 +type Mat14 = [1][16]f32 + +@[heap] +struct GameObject { +mut: + transform Mat4 + transform2 Mat14 +} + +@[heap] +struct Ship { + GameObject +} + +fn Ship.new() &Ship { + mut ship := &Ship{} + return ship +} + +fn (mut ship Ship) clone() &Ship { + return &Ship{ + ...ship + } +} + +fn test_main() { + mut v1 := Ship.new() + v1.transform[0] = 1.0 + v1.transform[15] = 2.0 + v1.transform2[0][0] = 1.0 + v1.transform2[0][15] = 2.0 + mut v2 := v1.clone() + eprintln('v1=${v1.transform}\nv2=${v2.transform}') + assert v1.transform == v2.transform + assert v1.transform2 == v2.transform2 + assert v1.transform[0] == 1.0 + assert v2.transform[0] == 1.0 + assert v1.transform[15] == 2.0 + assert v2.transform[15] == 2.0 + + assert v1.transform2[0][0] == 1.0 + assert v2.transform2[0][0] == 1.0 + assert v1.transform2[0][15] == 2.0 + assert v2.transform2[0][15] == 2.0 +} diff --git a/vlib/v/tests/fixed_array_update_embed_expr_test.v b/vlib/v/tests/fixed_array_update_embed_expr_test.v new file mode 100644 index 00000000000000..375a6ff0395421 --- /dev/null +++ b/vlib/v/tests/fixed_array_update_embed_expr_test.v @@ -0,0 +1,30 @@ +module main + +type Mat4 = [14]f32 + +struct GameObject { +mut: + transform Mat4 +} + +struct Ship { + GameObject +} + +fn (mut ship Ship) instance() &Ship { + return &Ship{ + ...ship + } +} + +fn test_fixed_array_update_embed_expr() { + mut v := Ship{} + mut v2 := v.instance() + + assert v2.transform.len == 14 + assert v2.transform[0] == 0 + assert v2.transform[13] == 0 + assert v2.transform.all(it == 0) + + dump('V=${v2}') +} diff --git a/vlib/v/tests/fns/const_call_or_expr_test.v b/vlib/v/tests/fns/const_call_or_expr_test.v new file mode 100644 index 00000000000000..6dc49a753c7bc3 --- /dev/null +++ b/vlib/v/tests/fns/const_call_or_expr_test.v @@ -0,0 +1,11 @@ +import os + +const vdir = os.getenv_opt('VDIR') or { os.dir(os.getenv_opt('VEXE') or { os.getwd() }) } +const vdir2 = os.getenv_opt('NON_EXISTENT') +const vdir3 = os.getenv_opt('NON_EXISTENT') or { '' } + +fn test_main() { + assert vdir.len > 0 + assert vdir2 == none + assert vdir3 == '' +} diff --git a/vlib/v/tests/generics/generics_return_closure_test.v b/vlib/v/tests/generics/generics_return_closure_test.v new file mode 100644 index 00000000000000..1e6d2e1d77e993 --- /dev/null +++ b/vlib/v/tests/generics/generics_return_closure_test.v @@ -0,0 +1,29 @@ +fn vectorize[T](op fn (T) T) fn ([]T) []T { + return fn [op] [T](values []T) []T { + mut result := []T{len: values.len} + for i in 0 .. values.len { + result[i] = op(values[i]) + } + return result + } +} + +fn add_one1(x f64) f64 { + return x + 1 +} + +fn add_one2(x int) int { + return x + 1 +} + +fn test_generic_return_generic_closure() { + vadd1 := vectorize[f64](add_one1) + v1 := [1.0, 2, 3, 4] + println(vadd1(v1)) + assert vadd1(v1) == [2.0, 3, 4, 5] + + vadd2 := vectorize[int](add_one2) + v2 := [1, 2, 3, 4] + println(vadd2(v2)) + assert vadd2(v2) == [2, 3, 4, 5] +} diff --git a/vlib/v/tests/reflection_attr_quotes_test.v b/vlib/v/tests/reflection_attr_quotes_test.v new file mode 100644 index 00000000000000..0ecce7f9c85443 --- /dev/null +++ b/vlib/v/tests/reflection_attr_quotes_test.v @@ -0,0 +1,16 @@ +import v.reflection as r + +struct MyParams { + p_fpga_ver string @[long: fp_ver; name: 'FPGA Version'; xdoc: 'String to use as simulated FPGA version in Version responses. Must be in the form "a.bb.cccc"'] + p_cm_ver string @[long: cm_ver; name: 'CM Version'; xdoc: 'String to use as simulated CM version in Version responses. Must be in the form "a.bb.cccc"'] +} + +fn test_main() { + a := MyParams{} + t := r.type_of(a) + if t.sym.info is r.Struct { + assert t.sym.info.fields[0].attrs[2] == 'xdoc=String to use as simulated FPGA version in Version responses. Must be in the form "a.bb.cccc"' + } else { + assert false + } +} diff --git a/vlib/x/json2/decoder2/decode.v b/vlib/x/json2/decoder2/decode.v index c89239dd95e667..633bfe4a36eeb6 100644 --- a/vlib/x/json2/decoder2/decode.v +++ b/vlib/x/json2/decoder2/decode.v @@ -218,10 +218,6 @@ fn (mut checker Decoder) check_json_format(val string) ! { continue } - if val[checker.checker_idx] != `"` { - checker.checker_idx++ - } - // skip whitespace for val[checker.checker_idx] in [` `, `\t`, `\n`] { if checker.checker_idx >= checker_end - 1 { @@ -234,39 +230,21 @@ fn (mut checker Decoder) check_json_format(val string) ! { continue } - match val[checker.checker_idx] { - `"` { - // Object key - checker.check_json_format(val)! + if val[checker.checker_idx] != `"` { + return checker.error('Expecting object key') + } - for val[checker.checker_idx] != `:` { - if checker.checker_idx >= checker_end - 1 { - return checker.error('EOF error: key colon not found') - } - if val[checker.checker_idx] !in [` `, `\t`, `\n`] { - return checker.error('invalid value after object key') - } - checker.checker_idx++ - } - } - `[`, `{`, `0`...`9`, `-`, `n`, `t`, `f` { - // skip - } - `}` { - return - } - `]` { - return checker.error('Expecting key. Found closing bracket') - } - `,` { - return checker.error('invalid object key') - } - `:` { - return checker.error('empty object key') + // Object key + checker.check_json_format(val)! + + for val[checker.checker_idx] != `:` { + if checker.checker_idx >= checker_end - 1 { + return checker.error('EOF error: key colon not found') } - else { - return checker.error('`${[val[checker.checker_idx]].bytestr()}` is an invalid object key') + if val[checker.checker_idx] !in [` `, `\t`, `\n`] { + return checker.error('invalid value after object key') } + checker.checker_idx++ } if val[checker.checker_idx] != `:` { @@ -282,38 +260,31 @@ fn (mut checker Decoder) check_json_format(val string) ! { match val[checker.checker_idx] { `"`, `[`, `{`, `0`...`9`, `-`, `n`, `t`, `f` { - for val[checker.checker_idx] != `}` { - if checker.checker_idx >= checker_end - 1 { - return checker.error('EOF error: object value not closed') - } - checker.check_json_format(val)! - // whitespace + checker.check_json_format(val)! + // whitespace + for val[checker.checker_idx] in [` `, `\t`, `\n`] { + checker.checker_idx++ + } + if val[checker.checker_idx] == `}` { + break + } + if checker.checker_idx >= checker_end - 1 { + return checker.error('EOF error: braces are not closed') + } + + if val[checker.checker_idx] == `,` { + checker.checker_idx++ for val[checker.checker_idx] in [` `, `\t`, `\n`] { checker.checker_idx++ } + if val[checker.checker_idx] != `"` { + return checker.error('Expecting object key') + } + } else { if val[checker.checker_idx] == `}` { break - } - if checker.checker_idx >= checker_end - 1 { - return checker.error('EOF error: braces are not closed') - } - - if val[checker.checker_idx] == `,` { - checker.checker_idx++ - for val[checker.checker_idx] in [` `, `\t`, `\n`] { - checker.checker_idx++ - } - if val[checker.checker_idx] != `"` { - return checker.error('Expecting object key') - } else { - break - } } else { - if val[checker.checker_idx] == `}` { - break - } else { - return - } + return checker.error('invalid object value') } } } @@ -322,9 +293,6 @@ fn (mut checker Decoder) check_json_format(val string) ! { } } } - if checker.checker_idx < checker_end - 1 { - checker.checker_idx++ - } } .array { // check if the JSON string is an empty array @@ -608,6 +576,7 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! { } } $else $if T.unaliased_typ is $sumtype { decoder.decode_sumtype(mut val)! + return } $else $if T.unaliased_typ is time.Time { time_info := decoder.current_node.value @@ -667,6 +636,7 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! { } } } + return } $else $if T.unaliased_typ is bool { value_info := decoder.current_node.value @@ -748,9 +718,12 @@ fn (mut decoder Decoder) decode_map[K, V](mut val map[K]V) ! { mut map_value := V{} - decoder.decode_value(mut map_value)! - - val[key] = map_value + $if V is $map { + val[key] = map_value.move() + } $else { + val[key] = map_value + } + decoder.decode_value(mut val[key])! } } } diff --git a/vlib/x/json2/decoder2/tests/decode_array_test.v b/vlib/x/json2/decoder2/tests/decode_array_test.v index 58d2b3229dff99..87caa6378c99ab 100644 --- a/vlib/x/json2/decoder2/tests/decode_array_test.v +++ b/vlib/x/json2/decoder2/tests/decode_array_test.v @@ -1,5 +1,10 @@ import x.json2.decoder2 as json +struct StructType[T] { +mut: + val T +} + fn test_array_of_strings() { assert json.decode[[]int]('[1, 2, 3]')! == [1, 2, 3] @@ -25,3 +30,20 @@ fn test_array_of_strings() { [false, true], ] } + +fn test_array_of_struct() { + assert json.decode[[]StructType[int]]('[{"val": 1}, {"val": 2}, {"val": 3}, {"val": 4}]')! == [ + StructType{ + val: 1 + }, + StructType{ + val: 2 + }, + StructType{ + val: 3 + }, + StructType{ + val: 4 + }, + ] +} diff --git a/vlib/x/json2/decoder2/tests/decode_object_test.v b/vlib/x/json2/decoder2/tests/decode_object_test.v index 2628f070d889af..809d8b6d688d17 100644 --- a/vlib/x/json2/decoder2/tests/decode_object_test.v +++ b/vlib/x/json2/decoder2/tests/decode_object_test.v @@ -39,14 +39,16 @@ fn test_array_of_strings() { assert json.decode[map[string]string]('{"val": "2"}')! == { 'val': '2' } - // assert json.decode[map[string]int]('{"val": 2}')! == {"val": 2} + assert json.decode[map[string]int]('{"val": 2}')! == { + 'val': 2 + } - // // nested map - // assert json.decode[map[string]map[string]string]('{"val": {"val2": "2"}}')! == { - // 'val': { - // 'val2': '2' - // } - // } + // nested map + assert json.decode[map[string]map[string]string]('{"val": {"val2": "2"}}')! == { + 'val': { + 'val2': '2' + } + } // nested struct assert json.decode[Stru]('{"val": 1, "val2": "lala", "val3": {"a": 2, "brazilian_steak": "leleu"}}')! == Stru{ diff --git a/vlib/x/json2/decoder2/tests/json_sumtype_test.v b/vlib/x/json2/decoder2/tests/json_sumtype_test.v index 27e956525ab985..3c16c2d202e92c 100644 --- a/vlib/x/json2/decoder2/tests/json_sumtype_test.v +++ b/vlib/x/json2/decoder2/tests/json_sumtype_test.v @@ -28,6 +28,21 @@ pub struct Dog { type Sum = int | string | bool | []string +type StructSumTypes = Stru | Stru2 + +pub struct Stru { + val int + val2 string + val3 Stru2 + val4 int + val5 int +} + +pub struct Stru2 { + a int + steak string +} + fn test_simple_sum_type() { assert json.decode[Sum]('1')! == Sum(1) @@ -50,15 +65,14 @@ fn test_any_sum_type() { assert json.decode[json2.Any]('1.1')! == json2.Any(f64(1.1)) - // Uncomment this when #22693 is fixed - // assert json.decode[[]json2.Any]('["1", "2", "3"]')! == [json2.Any('1'), json2.Any('2'), json2.Any('3')] - // assert json.decode[json2.Any]('["1", "2", "3"]')! == json2.Any([json2.Any('1'), json2.Any('2'), - // json2.Any('3')]) + assert json.decode[[]json2.Any]('["1", "2", "3"]')! == [json2.Any('1'), json2.Any('2'), json2.Any('3')] + assert json.decode[json2.Any]('["1", "2", "3"]')! == json2.Any([json2.Any('1'), json2.Any('2'), + json2.Any('3')]) - // assert json.decode[[]json2.Any]('[true, false, true]')! == [json2.Any(true), json2.Any(false), - // json2.Any(true)] - // assert json.decode[json2.Any]('[true, false, true]')! == json2.Any([json2.Any(true), json2.Any(false), - // json2.Any(true)]) + assert json.decode[[]json2.Any]('[true, false, true]')! == [json2.Any(true), json2.Any(false), + json2.Any(true)] + assert json.decode[json2.Any]('[true, false, true]')! == json2.Any([json2.Any(true), json2.Any(false), + json2.Any(true)]) assert json.decode[json2.Any]('{"hello": "world"}')! == json2.Any({ 'hello': json2.Any('world') @@ -68,15 +82,18 @@ fn test_any_sum_type() { 'hello': json2.Any('world') } - // assert json.decode[json2.Any]('{"hello1": {"hello2": "world"}}')! == json2.Any({ - // 'hello1': json2.Any({ - // 'hello2': json2.Any('world') - // }) - // }) + assert json.decode[json2.Any]('{"hello1": {"hello2": "world"}}')! == json2.Any({ + 'hello1': json2.Any({ + 'hello2': json2.Any('world') + }) + }) } fn test_sum_type_struct() { assert json.decode[Animal]('{"cat_name": "Tom"}')! == Animal(Cat{'Tom'}) assert json.decode[Animal]('{"dog_name": "Rex"}')! == Animal(Cat{''}) assert json.decode[Animal]('{"dog_name": "Rex", "_type": "Dog"}')! == Animal(Dog{'Rex'}) + + // struct sumtype in random order + assert json.decode[StructSumTypes]('{"_type": "Stru", "val": 1, "val2": "lala", "val3": {"a": 2, "steak": "leleu"}, "val4": 2147483000, "val5": 2147483000}')! == StructSumTypes(Stru{1, 'lala', Stru2{2, 'leleu'}, 2147483000, 2147483000}) }