From 131078128bbc68bf018d08f8a537b6880089ee38 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Wed, 16 Oct 2024 00:59:33 -0300 Subject: [PATCH] v: allow for `C.va_arg(Type, va)` (fix #20636) (#22530) --- vlib/v/checker/fn.v | 28 +++++++++++++++++----------- vlib/v/gen/c/fn.v | 8 ++++++++ vlib/v/parser/fn.v | 2 +- vlib/v/tests/va_arg_test.v | 22 ++++++++++++++++++++++ 4 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 vlib/v/tests/va_arg_test.v diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 738a152cc..6334d850b 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -797,6 +797,8 @@ fn (mut c Checker) needs_unwrap_generic_type(typ ast.Type) bool { } fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.Type { + is_va_arg := node.name == 'C.va_arg' + is_json_decode := node.name == 'json.decode' mut fn_name := node.name if index := node.name.index('__static__') { // resolve static call T.name() @@ -874,10 +876,15 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } else if node.args.len > 0 && node.args[0].typ.has_flag(.shared_f) && fn_name == 'json.encode' { c.error('json.encode cannot handle shared data', node.pos) return ast.void_type - } else if node.args.len > 0 && fn_name == 'json.decode' { + } else if node.args.len > 0 && (is_va_arg || is_json_decode) { if node.args.len != 2 { - c.error("json.decode expects 2 arguments, a type and a string (e.g `json.decode(T, '')`)", - node.pos) + if is_json_decode { + c.error("json.decode expects 2 arguments, a type and a string (e.g `json.decode(T, '')`)", + node.pos) + } else { + c.error('C.va_arg expects 2 arguments, a type and va_list (e.g `C.va_arg(int, va)`)', + node.pos) + } return ast.void_type } mut expr := node.args[0].expr @@ -894,27 +901,26 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. if sym.info is ast.Alias { kind = c.table.sym(sym.info.parent_type).kind } - if kind !in [.struct, .sum_type, .map, .array] { - c.error('json.decode: expected sum type, struct, map or array, found ${kind}', + if is_json_decode && kind !in [.struct, .sum_type, .map, .array] { + c.error('${fn_name}: expected sum type, struct, map or array, found ${kind}', expr.pos) } } else { - c.error('json.decode: unknown type `${sym.name}`', node.pos) + c.error('${fn_name}: unknown type `${sym.name}`', node.pos) } } else { typ := expr.type_name() - c.error('json.decode: first argument needs to be a type, got `${typ}`', node.pos) + c.error('${fn_name}: first argument needs to be a type, got `${typ}`', node.pos) return ast.void_type } c.expected_type = ast.string_type node.args[1].typ = c.expr(mut node.args[1].expr) - if node.args[1].typ != ast.string_type { + if is_json_decode && node.args[1].typ != ast.string_type { c.error('json.decode: second argument needs to be a string', node.pos) } typ := expr as ast.TypeNode - ret_type := typ.typ.set_flag(.result) - node.return_type = ret_type - return ret_type + node.return_type = if is_json_decode { typ.typ.set_flag(.result) } else { typ.typ } + return node.return_type } else if fn_name == '__addr' { if !c.inside_unsafe { c.error('`__addr` must be called from an unsafe block', node.pos) diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 2f70b0206..9d9c01c4f 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1976,6 +1976,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { is_json_encode_pretty := name == 'json.encode_pretty' is_json_decode := name == 'json.decode' is_json_fn := is_json_encode || is_json_encode_pretty || is_json_decode + is_va_arg := name == 'C.va_arg' mut json_type_str := '' mut json_obj := '' if is_json_fn { @@ -2022,6 +2023,13 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { g.write('\n${cur_line}') name = '' json_obj = tmp2 + } else if is_va_arg { + ast_type := node.args[0].expr as ast.TypeNode + typ := g.typ(ast_type.typ) + g.write('va_arg(') + g.expr(node.args[1].expr) + g.write(', ${typ})') + return } if name == '__addr' { name = '&' diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index d0ae4e8ae..e40631150 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -32,7 +32,7 @@ fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr { p.check_for_impure_v(language, first_pos) } mut or_kind := ast.OrKind.absent - if fn_name == 'json.decode' { + if fn_name == 'json.decode' || fn_name == 'C.va_arg' { p.expecting_type = true // Makes name_expr() parse the type `User` in `json.decode(User, txt)` } diff --git a/vlib/v/tests/va_arg_test.v b/vlib/v/tests/va_arg_test.v new file mode 100644 index 000000000..5ad4a5bd9 --- /dev/null +++ b/vlib/v/tests/va_arg_test.v @@ -0,0 +1,22 @@ +module main + +@[typedef] +struct C.va_list {} + +fn C.va_start(voidptr, voidptr) +fn C.va_arg(voidptr, voidptr) voidptr +fn C.va_end(voidptr) + +fn sum(qtd int, ...) int { + va := C.va_list{} + mut s := 0 + C.va_start(va, qtd) + for i := 0; i < qtd; i++ { + s += C.va_arg(int, va) + } + return s +} + +fn test_main() { + assert sum(3, 4, 5, 6) == 15 +} -- 2.39.5