From a86e8cf276475d86bcb4d1dc69d838ecd6df0c80 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 30 Apr 2026 21:33:23 +0300 Subject: [PATCH] cgen: fix more tests --- vlib/v/checker/checker.v | 2 +- vlib/v/gen/c/cgen.v | 3 + vlib/v/gen/c/cheaders.v | 6 +- .../gen/c/cheaders_manual_stdlib_decls_test.v | 33 +++++++++ vlib/v/gen/c/fn.v | 73 +++++++++++++++++++ vlib/v/gen/c/struct.v | 15 +++- vlib/v/tests/bench/bench_json_vs_json2.v | 4 +- 7 files changed, 131 insertions(+), 5 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index f4dca2a31..3ca989c5e 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4596,7 +4596,7 @@ fn (mut c Checker) stmts_ending_with_expression(mut stmts []ast.Stmt, expected_o } } c.stmt_level-- - if unreachable.line_nr >= 0 { + if unreachable.line_nr >= 0 && !c.pref.translated && !c.file.is_translated { c.error('unreachable code', unreachable) } c.find_unreachable_statements_after_noreturn_calls(stmts) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index f2f3cc46f..47b1515a5 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -129,6 +129,7 @@ mut: done_typedef_phase bool // set after write_typedef_types() completes late_chan_types shared []string // concrete channel cnames discovered during file generation emitted_chan_types map[string]bool // concrete channel typedefs/helpers already emitted + c_extern_signature_types map[string]bool // C signature-only type decls already emitted chan_pop_options map[string]string // types for `x := <-ch or {...}` chan_push_options map[string]string // types for `ch <- x or {...}` mtxs string // array of mutexes if the `lock` has multiple variables @@ -412,6 +413,7 @@ pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) GenO has_debugger: 'v.debug' in table.modules reflection_strings: &reflection_strings generated_map_key_fns: map[ast.Type]bool{} + c_extern_signature_types: map[string]bool{} boehm_keep_decl: map[string]bool{} boehm_keep_gen: map[string]bool{} boehm_keep_busy: map[string]bool{} @@ -1056,6 +1058,7 @@ fn cgen_process_one_file_cb(mut p pool.PoolProcessor, idx int, wid int) voidptr has_debugger: 'v.debug' in global_g.table.modules reflection_strings: global_g.reflection_strings generated_map_key_fns: map[ast.Type]bool{} + c_extern_signature_types: map[string]bool{} boehm_keep_decl: map[string]bool{} boehm_keep_gen: map[string]bool{} boehm_keep_busy: map[string]bool{} diff --git a/vlib/v/gen/c/cheaders.v b/vlib/v/gen/c/cheaders.v index 65fd1101f..c8b791039 100644 --- a/vlib/v/gen/c/cheaders.v +++ b/vlib/v/gen/c/cheaders.v @@ -532,6 +532,7 @@ V_CRT_LINKAGE int V_CRT_CALL rand(void); V_CRT_LINKAGE void V_CRT_CALL srand(unsigned int seed); V_CRT_LINKAGE int V_CRT_CALL atexit(void (*cb)(void)); V_CRT_LINKAGE void V_CRT_CALL exit(int status); +V_CRT_LINKAGE int V_CRT_CALL abs(int n); V_CRT_LINKAGE int V_CRT_CALL atoi(const char *str); V_CRT_LINKAGE double V_CRT_CALL atof(const char *str); V_CRT_LINKAGE char * V_CRT_CALL getenv(const char *name); @@ -544,8 +545,11 @@ V_CRT_LINKAGE char * V_CRT_CALL realpath(const char *path, char *resolved_path); V_CRT_LINKAGE int V_CRT_CALL mkstemp(char *stemplate); V_CRT_LINKAGE void V_CRT_CALL qsort(void *base, size_t items, size_t item_size, qsort_callback_func cb); V_CRT_LINKAGE int V_CRT_CALL strcmp(const char *left, const char *right); -#if !defined(_WIN32) && !defined(_WIN64) +V_CRT_LINKAGE int V_CRT_CALL strncmp(const char *left, const char *right, size_t n); +#if !defined(_WIN32) && !defined(_WIN64) && !defined(__BIONIC__) V_CRT_LINKAGE char * V_CRT_CALL strdup(const char *str); +#endif +#if !defined(_WIN32) && !defined(_WIN64) V_CRT_LINKAGE int V_CRT_CALL strcasecmp(const char *left, const char *right); V_CRT_LINKAGE int V_CRT_CALL strncasecmp(const char *left, const char *right, size_t n); #endif diff --git a/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v b/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v index 6d12a95ec..7410fd95b 100644 --- a/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v +++ b/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v @@ -27,10 +27,13 @@ fn test_default_c_prelude_uses_manual_stdio_stdlib_string_and_stdarg_decls() { assert generated_c.contains('V_CRT_LINKAGE void V_CRT_CALL perror(const char *str);'), generated_c assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL mkstemp(char *stemplate);'), generated_c assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL strcmp(const char *left, const char *right);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL strncmp(const char *left, const char *right, size_t n);'), generated_c + assert generated_c.contains('#if !defined(_WIN32) && !defined(_WIN64) && !defined(__BIONIC__)'), generated_c assert generated_c.contains('V_CRT_LINKAGE char * V_CRT_CALL strdup(const char *str);'), generated_c assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL rand(void);'), generated_c assert generated_c.contains('V_CRT_LINKAGE void V_CRT_CALL srand(unsigned int seed);'), generated_c assert generated_c.contains('RAND_MAX = 2147483647'), generated_c + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL abs(int n);'), generated_c assert generated_c.contains('V_CRT_LINKAGE double V_CRT_CALL atof(const char *str);'), generated_c assert generated_c.contains('extern FILE* stdout;'), generated_c assert generated_c.contains('#define stdout (__acrt_iob_func(1))'), generated_c @@ -179,6 +182,20 @@ fn test_manual_stdio_decls_allow_direct_atof_calls() { assert res.exit_code == 0, '${cmd}\n${res.output}' } +fn test_manual_stdio_decls_allow_translated_direct_abs_calls() { + tmp_dir := os.join_path(os.vtmp_dir(), 'cheaders_manual_stdlib_abs_${os.getpid()}') + os.mkdir_all(tmp_dir)! + defer { + os.rmdir_all(tmp_dir) or {} + } + source_path := os.join_path(tmp_dir, 'c_abs.v') + os.write_file(source_path, ['fn main() {', '\t_ = C.abs(-7)', '}'].join('\n') + '\n')! + output_path := os.join_path(tmp_dir, 'c_abs') + cmd := '${os.quoted_path(cheaders_manual_stdlib_vexe)} -translated -cc clang -o ${os.quoted_path(output_path)} ${os.quoted_path(source_path)}' + res := os.execute(cmd) + assert res.exit_code == 0, '${cmd}\n${res.output}' +} + fn test_manual_stdio_decls_allow_translated_direct_strdup_calls() { $if windows { return @@ -196,6 +213,22 @@ fn test_manual_stdio_decls_allow_translated_direct_strdup_calls() { assert res.exit_code == 0, '${cmd}\n${res.output}' } +fn test_manual_stdio_decls_allow_translated_direct_strncmp_calls() { + tmp_dir := os.join_path(os.vtmp_dir(), 'cheaders_manual_stdlib_strncmp_${os.getpid()}') + os.mkdir_all(tmp_dir)! + defer { + os.rmdir_all(tmp_dir) or {} + } + source_path := os.join_path(tmp_dir, 'c_strncmp.v') + os.write_file(source_path, + + ['fn main() {', "\t_ = C.strncmp(c'abc', c'abd', 2)", '}'].join('\n') + '\n')! + output_path := os.join_path(tmp_dir, 'c_strncmp') + cmd := '${os.quoted_path(cheaders_manual_stdlib_vexe)} -translated -cc clang -o ${os.quoted_path(output_path)} ${os.quoted_path(source_path)}' + res := os.execute(cmd) + assert res.exit_code == 0, '${cmd}\n${res.output}' +} + fn test_c_prelude_ctype_decls_do_not_conflict_with_later_ctype_includes() { $if !linux { return diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 900d5a56c..8af76b596 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -58,6 +58,7 @@ const c_manual_prelude_decl_names = [ 'srand', 'atexit', 'exit', + 'abs', 'atoi', 'atof', 'getenv', @@ -70,6 +71,10 @@ const c_manual_prelude_decl_names = [ 'mkstemp', 'qsort', 'strcmp', + 'strncmp', + 'strdup', + 'strcasecmp', + 'strncasecmp', 'strlen', 'strerror', 'memcpy', @@ -866,6 +871,73 @@ fn (g &Gen) should_emit_c_fallback_decl(node ast.FnDecl) bool { return node.mod == 'main' || node.source_file.is_test } +fn (g &Gen) should_emit_c_signature_type_decls(node ast.FnDecl) bool { + return node.is_c_extern || g.should_emit_c_fallback_decl(node) + || (node.no_body && node.attrs.contains('c')) +} + +fn (mut g Gen) ensure_c_extern_signature_type_decls(node ast.FnDecl) { + if !g.pref.skip_unused || !g.should_emit_c_signature_type_decls(node) { + return + } + g.ensure_c_extern_signature_type_decl(node.return_type) + for param in node.params { + g.ensure_c_extern_signature_type_decl(param.typ) + } +} + +fn (mut g Gen) ensure_c_extern_signature_type_decl(typ_ ast.Type) { + if typ_ == 0 { + return + } + typ := typ_.clear_option_and_result().set_nr_muls(0).clear_flags(.generic, .variadic) + if typ == 0 { + return + } + sym := g.table.sym(typ) + if sym.is_builtin || sym.name in ['byte', 'i32', 'C.FILE'] { + return + } + if sym.idx in g.table.used_features.used_syms { + return + } + if sym.cname in g.c_extern_signature_types { + return + } + g.c_extern_signature_types[sym.cname] = true + match sym.info { + ast.Alias { + g.ensure_c_extern_signature_type_decl(sym.info.parent_type) + g.write_alias_typesymbol_declaration(sym) + } + ast.FnType { + for param in sym.info.func.params { + g.ensure_c_extern_signature_type_decl(param.typ) + } + g.ensure_c_extern_signature_type_decl(sym.info.func.return_type) + } + ast.Struct { + if sym.language == .c && sym.cname.starts_with('C__') && !sym.info.is_anon { + c_struct_name := sym.cname[3..] + if sym.info.is_typedef { + g.typedefs.writeln('typedef struct ${c_struct_name} ${c_struct_name};') + } else { + g.typedefs.writeln('struct ${c_struct_name};') + } + } else { + g.typedefs.writeln('typedef struct ${sym.cname} ${sym.cname};') + } + } + ast.Array { + g.ensure_c_extern_signature_type_decl(sym.info.elem_type) + } + ast.ArrayFixed { + g.ensure_c_extern_signature_type_decl(sym.info.elem_type) + } + else {} + } +} + fn (mut g Gen) fn_decl(node ast.FnDecl) { $if trace_cgen_fn_decl ? { eprintln('> g.tid: ${g.tid} | g.fid: ${g.fid:3} | g.file.path: ${g.file.path} | fn_decl: ${node.name}') @@ -887,6 +959,7 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) { if !g.is_used_by_main(node) { return } + g.ensure_c_extern_signature_type_decls(node) if g.is_builtin_mod && g.pref.gc_mode == .boehm_leak && node.kind == .malloc { g.definitions.write_string('#define builtin___v_malloc GC_MALLOC\n') return diff --git a/vlib/v/gen/c/struct.v b/vlib/v/gen/c/struct.v index e27517387..038469215 100644 --- a/vlib/v/gen/c/struct.v +++ b/vlib/v/gen/c/struct.v @@ -1084,8 +1084,21 @@ fn (mut g Gen) struct_init_field_value(sfield ast.StructInitField) { field_unwrap_typ := g.unwrap_generic(sfield.typ) field_unwrap_sym := g.table.final_sym(field_unwrap_typ) + expected_unwrap_typ := g.unwrap_generic(sfield.expected_type) + expected_unwrap_sym := g.table.final_sym(expected_unwrap_typ) is_auto_deref_var := sfield.expr.is_auto_deref_var() - if field_unwrap_sym.info is ast.ArrayFixed && !sfield.expected_type.has_flag(.option) { + if expected_unwrap_sym.info is ast.ArrayFixed && sfield.expr is ast.ArrayInit + && !sfield.expr.is_fixed && !sfield.expr.has_len && !sfield.expr.has_init + && sfield.expr.exprs.len > 0 && !sfield.expected_type.has_flag(.option) { + fixed_array_expr := ast.ArrayInit{ + ...sfield.expr + is_fixed: true + has_val: true + elem_type: expected_unwrap_sym.info.elem_type + typ: expected_unwrap_typ + } + g.fixed_array_init(fixed_array_expr, g.unwrap(expected_unwrap_typ), '', false) + } else if field_unwrap_sym.info is ast.ArrayFixed && !sfield.expected_type.has_flag(.option) { match sfield.expr { ast.Ident, ast.SelectorExpr { g.fixed_array_var_init(g.expr_string(ast.Expr(sfield.expr)), is_auto_deref_var, diff --git a/vlib/v/tests/bench/bench_json_vs_json2.v b/vlib/v/tests/bench/bench_json_vs_json2.v index bc390cad5..46ca8e362 100644 --- a/vlib/v/tests/bench/bench_json_vs_json2.v +++ b/vlib/v/tests/bench/bench_json_vs_json2.v @@ -202,8 +202,8 @@ fn benchmark_measure_decode_by_type() ! { b.measure('json2.decode StructType[time.Time]') for _ in 0 .. max_iterations { d := json.decode(StructType[time.Time], vt)! - if d.val.year != 1970 { // note json.decode here is buggy - return error('json2.decode ${d}') + if d.val.year != 2015 { + return error('json.decode ${d}') } } b.measure(' json.decode StructType[time.Time]\n') -- 2.39.5