Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v: Improve C var args interop #21812

Merged
merged 7 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions vlib/v/ast/ast.v
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ pub:
mod string // 'math.bits'
is_deprecated bool
is_pub bool
is_c_variadic bool
is_variadic bool
is_anon bool
is_noreturn bool // true, when [noreturn] is used on a fn
Expand Down Expand Up @@ -630,6 +631,7 @@ pub:
pub struct Fn {
pub:
is_variadic bool
is_c_variadic bool
language Language
is_pub bool
is_ctor_new bool // `[use_new] fn JS.Array.prototype.constructor()`
Expand Down
5 changes: 4 additions & 1 deletion vlib/v/ast/str.v
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,13 @@ fn (t &Table) stringify_fn_after_name(node &FnDecl, mut f strings.Builder, cur_m
if !is_type_only {
f.write_string(' ')
}
if node.is_variadic && is_last_param {
if node.is_variadic && is_last_param && !node.is_c_variadic {
f.write_string('...')
}
f.write_string(s)
if is_last_param && node.is_c_variadic {
f.write_string(', ...')
}
}
}
if !is_last_param {
Expand Down
10 changes: 8 additions & 2 deletions vlib/v/gen/c/assign.v
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,12 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
mut is_call := false
mut gen_or := false
mut blank_assign := false
mut is_va_list := false // C varargs
mut ident := ast.Ident{
scope: unsafe { nil }
}
left_sym := g.table.sym(g.unwrap_generic(var_type))
is_va_list = left_sym.language == .c && left_sym.name == 'C.va_list'
if mut left is ast.Ident {
ident = left
g.curr_var_name << ident.name
Expand Down Expand Up @@ -612,7 +614,8 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
}
g.write('${ret_styp} (${msvc_call_conv}*${fn_name}) (')
def_pos := g.definitions.len
g.fn_decl_params(right_sym.info.func.params, unsafe { nil }, false)
g.fn_decl_params(right_sym.info.func.params, unsafe { nil }, false,
false)
g.definitions.go_back(g.definitions.len - def_pos)
g.write(')${call_conv_attribute_suffix}')
}
Expand Down Expand Up @@ -693,9 +696,12 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
g.expr(left)
}
g.is_assign_lhs = false
if is_fixed_array_var {
if is_fixed_array_var || is_va_list {
if is_decl {
g.writeln(';')
if is_va_list {
continue
}
}
} else if !g.is_arraymap_set && !str_add && !op_overloaded {
g.write(' ${op} ')
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -7743,7 +7743,7 @@ static inline __shared__${interface_name} ${shared_fn_name}(__shared__${cctype}*
...params[0]
typ: st.set_nr_muls(1)
}
fargs, _, _ := g.fn_decl_params(params, unsafe { nil }, false)
fargs, _, _ := g.fn_decl_params(params, unsafe { nil }, false, false)
mut parameter_name := g.out.cut_last(g.out.len - params_start_pos)

if st.is_ptr() {
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/gen/c/cheaders.v
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ const c_common_macros = '
#define TCCSKIP(x)
// #include <byteswap.h>
#ifndef _WIN32
int tcc_backtrace(const char *fmt, ...);
int tcc_backtrace(char *fmt, ...);
felipensp marked this conversation as resolved.
Show resolved Hide resolved
#endif
#endif

Expand Down
12 changes: 7 additions & 5 deletions vlib/v/gen/c/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
g.write(sig)
g.definitions.write_string(sig)
} else {
g.fn_decl_params(call_fn.func.params, unsafe { nil }, call_fn.func.is_variadic)
g.fn_decl_params(call_fn.func.params, unsafe { nil }, call_fn.func.is_variadic,
call_fn.func.is_c_variadic)
}

g.writeln(') {')
Expand Down Expand Up @@ -384,7 +385,8 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
g.write(fn_header)
}
arg_start_pos := g.out.len
fargs, fargtypes, heap_promoted := g.fn_decl_params(node.params, node.scope, node.is_variadic)
fargs, fargtypes, heap_promoted := g.fn_decl_params(node.params, node.scope, node.is_variadic,
node.is_c_variadic)
if is_closure {
g.nr_closures++
}
Expand Down Expand Up @@ -695,7 +697,7 @@ fn (mut g Gen) write_defer_stmts_when_needed() {
}
}

fn (mut g Gen) fn_decl_params(params []ast.Param, scope &ast.Scope, is_variadic bool) ([]string, []string, []bool) {
fn (mut g Gen) fn_decl_params(params []ast.Param, scope &ast.Scope, is_variadic bool, is_c_variadic bool) ([]string, []string, []bool) {
mut fparams := []string{}
mut fparamtypes := []string{}
mut heap_promoted := []bool{}
Expand All @@ -720,7 +722,7 @@ fn (mut g Gen) fn_decl_params(params []ast.Param, scope &ast.Scope, is_variadic
func := info.func
g.write('${g.typ(func.return_type)} (*${caname})(')
g.definitions.write_string('${g.typ(func.return_type)} (*${caname})(')
g.fn_decl_params(func.params, unsafe { nil }, func.is_variadic)
g.fn_decl_params(func.params, unsafe { nil }, func.is_variadic, func.is_c_variadic)
g.write(')')
g.definitions.write_string(')')
fparams << caname
Expand Down Expand Up @@ -756,7 +758,7 @@ fn (mut g Gen) fn_decl_params(params []ast.Param, scope &ast.Scope, is_variadic
g.definitions.write_string(', ')
}
}
if g.pref.translated && is_variadic {
if (g.pref.translated && is_variadic) || is_c_variadic {
g.write(', ... ')
g.definitions.write_string(', ... ')
}
Expand Down
49 changes: 34 additions & 15 deletions vlib/v/parser/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
}
}
// Params
params_t, are_params_type_only, mut is_variadic := p.fn_params()
params_t, are_params_type_only, mut is_variadic, mut is_c_variadic := p.fn_params()
if is_c2v_variadic {
is_variadic = true
}
Expand Down Expand Up @@ -563,6 +563,7 @@ run them via `v file.v` instead',
params: params
return_type: return_type
is_variadic: is_variadic
is_c_variadic: is_c_variadic
generic_names: generic_names
is_pub: is_pub
is_deprecated: is_deprecated
Expand Down Expand Up @@ -626,6 +627,7 @@ run them via `v file.v` instead',
is_direct_arr: is_direct_arr
is_pub: is_pub
is_variadic: is_variadic
is_c_variadic: is_c_variadic
is_main: is_main
is_test: is_test
is_keep_alive: is_keep_alive
Expand Down Expand Up @@ -783,7 +785,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
}
inherited_vars_name := inherited_vars.map(it.name)
_, generic_names := p.parse_generic_types()
params, _, is_variadic := p.fn_params()
params, _, is_variadic, _ := p.fn_params()
for param in params {
if param.name == '' && p.table.sym(param.typ).kind != .placeholder {
p.error_with_pos('use `_` to name an unused parameter', param.pos)
Expand Down Expand Up @@ -876,10 +878,11 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
}

// part of fn declaration
fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
fn (mut p Parser) fn_params() ([]ast.Param, bool, bool, bool) {
p.check(.lpar)
mut params := []ast.Param{}
mut is_variadic := false
mut is_c_variadic := false
// `int, int, string` (no names, just types)
param_name := if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() {
p.prepend_mod(p.tok.lit)
Expand All @@ -901,7 +904,7 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
mut comments := p.eat_comments()
if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.tok.pos())
return []ast.Param{}, false, false
return []ast.Param{}, false, false, false
}
is_shared := p.tok.kind == .key_shared
is_atomic := p.tok.kind == .key_atomic
Expand All @@ -918,13 +921,18 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
if p.tok.kind == .ellipsis {
p.next()
is_variadic = true
is_c_variadic = p.tok.kind == .rpar
if is_c_variadic {
p.check(.rpar)
return params, types_only, is_variadic, is_c_variadic
}
}
pos := p.tok.pos()
mut param_type := p.parse_type()
type_pos := pos.extend(p.prev_tok.pos())
if param_type == 0 {
// error is added in parse_type
return []ast.Param{}, false, false
return []ast.Param{}, false, false, false
}
if is_mut {
if !param_type.has_flag(.generic) {
Expand All @@ -941,7 +949,7 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
}
} else if is_shared || is_atomic {
p.error_with_pos('generic object cannot be `atomic`or `shared`', pos)
return []ast.Param{}, false, false
return []ast.Param{}, false, false, false
}
if param_type.is_ptr() && p.table.sym(param_type).kind == .struct_ {
param_type = param_type.ref()
Expand All @@ -960,15 +968,15 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
}
if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.prev_tok.pos())
return []ast.Param{}, false, false
return []ast.Param{}, false, false, false
}
comments << p.eat_comments()

if p.tok.kind == .comma {
if is_variadic {
p.error_with_pos('cannot use ...(variadic) with non-final parameter no ${param_no}',
pos)
return []ast.Param{}, false, false
return []ast.Param{}, false, false, false
}
p.next()
}
Expand All @@ -987,22 +995,28 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
param_no++
if param_no > 1024 {
p.error_with_pos('too many parameters', pos)
return []ast.Param{}, false, false
return []ast.Param{}, false, false, false
}
}
} else {
for p.tok.kind != .rpar {
mut comments := p.eat_comments()
if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.tok.pos())
return []ast.Param{}, false, false
return []ast.Param{}, false, false, false
}
is_shared := p.tok.kind == .key_shared
is_atomic := p.tok.kind == .key_atomic
mut is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
if is_mut {
p.next()
}
if p.tok.kind == .ellipsis && p.peek_tok.kind == .rpar {
p.check(.ellipsis)
p.check(.rpar)
return params, types_only, true, true
}

mut param_pos := [p.tok.pos()]
name := p.check_name()
comments << p.eat_comments()
Expand Down Expand Up @@ -1037,13 +1051,18 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
if p.tok.kind == .ellipsis {
p.next()
is_variadic = true
is_c_variadic = p.tok.kind == .rpar
if is_c_variadic {
p.check(.rpar)
return params, types_only, is_variadic, is_c_variadic
}
}
pos := p.tok.pos()
mut typ := p.parse_type()
type_pos[0] = pos.extend(p.prev_tok.pos())
if typ == 0 {
// error is added in parse_type
return []ast.Param{}, false, false
return []ast.Param{}, false, false, false
}
if is_mut {
if !typ.has_flag(.generic) {
Expand All @@ -1061,7 +1080,7 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
} else if is_shared || is_atomic {
p.error_with_pos('generic object cannot be `atomic` or `shared`',
pos)
return []ast.Param{}, false, false
return []ast.Param{}, false, false, false
}
if typ.is_ptr() && p.table.sym(typ).kind == .struct_ {
typ = typ.ref()
Expand Down Expand Up @@ -1099,20 +1118,20 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
if is_variadic && p.tok.kind == .comma && p.peek_tok.kind != .rpar {
p.error_with_pos('cannot use ...(variadic) with non-final parameter ${para_name}',
param_pos[i])
return []ast.Param{}, false, false
return []ast.Param{}, false, false, false
}
}
if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.prev_tok.pos())
return []ast.Param{}, false, false
return []ast.Param{}, false, false, false
}
if p.tok.kind != .rpar {
p.check(.comma)
}
}
}
p.check(.rpar)
return params, types_only, is_variadic
return params, types_only, is_variadic, is_c_variadic
}

fn (mut p Parser) spawn_expr() ast.SpawnExpr {
Expand Down
3 changes: 2 additions & 1 deletion vlib/v/parser/parse_type.v
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ fn (mut p Parser) parse_fn_type(name string, generic_types []ast.Type) ast.Type

mut has_generic := false
line_nr := p.tok.line_nr
params, _, is_variadic := p.fn_params()
params, _, is_variadic, is_c_variadic := p.fn_params()
for param in params {
if param.typ.has_flag(.generic) {
has_generic = true
Expand Down Expand Up @@ -339,6 +339,7 @@ fn (mut p Parser) parse_fn_type(name string, generic_types []ast.Type) ast.Type
name: name
params: params
is_variadic: is_variadic
is_c_variadic: is_c_variadic
return_type: return_type
return_type_pos: return_type_pos
generic_names: generic_names
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/parser/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
p.error_with_pos('duplicate method `${name}`', method_start_pos)
return ast.InterfaceDecl{}
}
params_t, _, is_variadic := p.fn_params() // TODO: merge ast.Param and ast.Arg to avoid this
params_t, _, is_variadic, _ := p.fn_params() // TODO: merge ast.Param and ast.Arg to avoid this
mut params := [
ast.Param{
name: 'x'
Expand Down