v: improve C var args interop, allow for fn f(some int, ...) { (#21812)

This commit is contained in:
Felipe Pena 2024-07-08 07:03:08 -03:00 committed by GitHub
parent bf23b2ef46
commit 66ea82605e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 108 additions and 26 deletions

View File

@ -541,6 +541,7 @@ pub:
mod string // 'math.bits' mod string // 'math.bits'
is_deprecated bool is_deprecated bool
is_pub bool is_pub bool
is_c_variadic bool
is_variadic bool is_variadic bool
is_anon bool is_anon bool
is_noreturn bool // true, when [noreturn] is used on a fn is_noreturn bool // true, when [noreturn] is used on a fn
@ -630,6 +631,7 @@ pub:
pub struct Fn { pub struct Fn {
pub: pub:
is_variadic bool is_variadic bool
is_c_variadic bool
language Language language Language
is_pub bool is_pub bool
is_ctor_new bool // `[use_new] fn JS.Array.prototype.constructor()` is_ctor_new bool // `[use_new] fn JS.Array.prototype.constructor()`

View File

@ -214,13 +214,15 @@ fn (t &Table) stringify_fn_after_name(node &FnDecl, mut f strings.Builder, cur_m
if !is_type_only { if !is_type_only {
f.write_string(' ') 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('...')
} }
f.write_string(s) f.write_string(s)
} }
if !is_last_param { if !is_last_param {
f.write_string(',') f.write_string(',')
} else if node.is_c_variadic {
f.write_string(', ...')
} }
nparams_on_pline++ nparams_on_pline++
old_pline = pline old_pline = pline

View File

@ -0,0 +1,23 @@
@[typedef]
struct C.va_list {}
fn C.va_start(voidptr, voidptr)
fn C.va_end(voidptr)
fn C.vfprintf(&C.FILE, &char, C.va_list) int
fn C.printf(&char, ...)
fn t2(fmt voidptr, ...) {
ap := C.va_list{}
C.va_start(ap, fmt)
C.vfprintf(C.stderr, fmt, ap)
C.va_end(ap)
}
fn main() {
a := 1
b := 2.5
t2(c'%s : %d : %.2f\n', c'foo', a, b)
C.printf(c'%s\n', c'foo')
}

View File

@ -224,10 +224,12 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
mut is_call := false mut is_call := false
mut gen_or := false mut gen_or := false
mut blank_assign := false mut blank_assign := false
mut is_va_list := false // C varargs
mut ident := ast.Ident{ mut ident := ast.Ident{
scope: unsafe { nil } scope: unsafe { nil }
} }
left_sym := g.table.sym(g.unwrap_generic(var_type)) 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 { if mut left is ast.Ident {
ident = left ident = left
g.curr_var_name << ident.name g.curr_var_name << ident.name
@ -612,7 +614,8 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
} }
g.write('${ret_styp} (${msvc_call_conv}*${fn_name}) (') g.write('${ret_styp} (${msvc_call_conv}*${fn_name}) (')
def_pos := g.definitions.len 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.definitions.go_back(g.definitions.len - def_pos)
g.write(')${call_conv_attribute_suffix}') g.write(')${call_conv_attribute_suffix}')
} }
@ -693,9 +696,12 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
g.expr(left) g.expr(left)
} }
g.is_assign_lhs = false g.is_assign_lhs = false
if is_fixed_array_var { if is_fixed_array_var || is_va_list {
if is_decl { if is_decl {
g.writeln(';') g.writeln(';')
if is_va_list {
continue
}
} }
} else if !g.is_arraymap_set && !str_add && !op_overloaded { } else if !g.is_arraymap_set && !str_add && !op_overloaded {
g.write(' ${op} ') g.write(' ${op} ')

View File

@ -7750,7 +7750,7 @@ static inline __shared__${interface_name} ${shared_fn_name}(__shared__${cctype}*
...params[0] ...params[0]
typ: st.set_nr_muls(1) 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) mut parameter_name := g.out.cut_last(g.out.len - params_start_pos)
if st.is_ptr() { if st.is_ptr() {

View File

@ -290,7 +290,8 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
g.write(sig) g.write(sig)
g.definitions.write_string(sig) g.definitions.write_string(sig)
} else { } 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(') {') g.writeln(') {')
@ -384,7 +385,8 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
g.write(fn_header) g.write(fn_header)
} }
arg_start_pos := g.out.len 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 { if is_closure {
g.nr_closures++ g.nr_closures++
} }
@ -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 fparams := []string{}
mut fparamtypes := []string{} mut fparamtypes := []string{}
mut heap_promoted := []bool{} mut heap_promoted := []bool{}
@ -720,7 +722,7 @@ fn (mut g Gen) fn_decl_params(params []ast.Param, scope &ast.Scope, is_variadic
func := info.func func := info.func
g.write('${g.typ(func.return_type)} (*${caname})(') g.write('${g.typ(func.return_type)} (*${caname})(')
g.definitions.write_string('${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.write(')')
g.definitions.write_string(')') g.definitions.write_string(')')
fparams << caname fparams << caname
@ -756,7 +758,7 @@ fn (mut g Gen) fn_decl_params(params []ast.Param, scope &ast.Scope, is_variadic
g.definitions.write_string(', ') g.definitions.write_string(', ')
} }
} }
if g.pref.translated && is_variadic { if (g.pref.translated && is_variadic) || is_c_variadic {
g.write(', ... ') g.write(', ... ')
g.definitions.write_string(', ... ') g.definitions.write_string(', ... ')
} }

View File

@ -0,0 +1,2 @@
VV_LOCAL_SYMBOL void main__t2(voidptr fmt, ... );
VV_LOCAL_SYMBOL void main__t2(voidptr fmt, ... ) {

2
vlib/v/gen/c/testdata/c_varargs.out vendored Normal file
View File

@ -0,0 +1,2 @@
foo : 1 : 2.50
foo

23
vlib/v/gen/c/testdata/c_varargs.vv vendored Normal file
View File

@ -0,0 +1,23 @@
@[typedef]
struct C.va_list {}
fn C.va_start(voidptr, voidptr)
fn C.va_end(voidptr)
fn C.vfprintf(&C.FILE, &char, C.va_list) int
fn C.printf(&char, ...)
fn t2(fmt voidptr, ...) {
ap := C.va_list{}
C.va_start(ap, fmt)
C.vfprintf(C.stdout, fmt, ap)
C.va_end(ap)
}
fn main() {
a := 1
b := 2.5
t2(c'%s : %d : %.2f\n', c'foo', a, b)
C.printf(c'%s\n', c'foo')
}

View File

@ -413,7 +413,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
} }
} }
// Params // 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 { if is_c2v_variadic {
is_variadic = true is_variadic = true
} }
@ -563,6 +563,7 @@ run them via `v file.v` instead',
params: params params: params
return_type: return_type return_type: return_type
is_variadic: is_variadic is_variadic: is_variadic
is_c_variadic: is_c_variadic
generic_names: generic_names generic_names: generic_names
is_pub: is_pub is_pub: is_pub
is_deprecated: is_deprecated is_deprecated: is_deprecated
@ -626,6 +627,7 @@ run them via `v file.v` instead',
is_direct_arr: is_direct_arr is_direct_arr: is_direct_arr
is_pub: is_pub is_pub: is_pub
is_variadic: is_variadic is_variadic: is_variadic
is_c_variadic: is_c_variadic
is_main: is_main is_main: is_main
is_test: is_test is_test: is_test
is_keep_alive: is_keep_alive is_keep_alive: is_keep_alive
@ -783,7 +785,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
} }
inherited_vars_name := inherited_vars.map(it.name) inherited_vars_name := inherited_vars.map(it.name)
_, generic_names := p.parse_generic_types() _, generic_names := p.parse_generic_types()
params, _, is_variadic := p.fn_params() params, _, is_variadic, _ := p.fn_params()
for param in params { for param in params {
if param.name == '' && p.table.sym(param.typ).kind != .placeholder { if param.name == '' && p.table.sym(param.typ).kind != .placeholder {
p.error_with_pos('use `_` to name an unused parameter', param.pos) p.error_with_pos('use `_` to name an unused parameter', param.pos)
@ -876,10 +878,11 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
} }
// part of fn declaration // 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) p.check(.lpar)
mut params := []ast.Param{} mut params := []ast.Param{}
mut is_variadic := false mut is_variadic := false
mut is_c_variadic := false
// `int, int, string` (no names, just types) // `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() { param_name := if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() {
p.prepend_mod(p.tok.lit) p.prepend_mod(p.tok.lit)
@ -901,7 +904,7 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
for p.tok.kind != .rpar { for p.tok.kind != .rpar {
if p.tok.kind == .eof { if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.tok.pos()) 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_shared := p.tok.kind == .key_shared
is_atomic := p.tok.kind == .key_atomic is_atomic := p.tok.kind == .key_atomic
@ -918,13 +921,18 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
if p.tok.kind == .ellipsis { if p.tok.kind == .ellipsis {
p.next() p.next()
is_variadic = true 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() pos := p.tok.pos()
mut param_type := p.parse_type() mut param_type := p.parse_type()
type_pos := pos.extend(p.prev_tok.pos()) type_pos := pos.extend(p.prev_tok.pos())
if param_type == 0 { if param_type == 0 {
// error is added in parse_type // error is added in parse_type
return []ast.Param{}, false, false return []ast.Param{}, false, false, false
} }
if is_mut { if is_mut {
if !param_type.has_flag(.generic) { if !param_type.has_flag(.generic) {
@ -941,7 +949,7 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
} }
} else if is_shared || is_atomic { } else if is_shared || is_atomic {
p.error_with_pos('generic object cannot be `atomic`or `shared`', pos) 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_ { if param_type.is_ptr() && p.table.sym(param_type).kind == .struct_ {
param_type = param_type.ref() param_type = param_type.ref()
@ -960,14 +968,14 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
} }
if p.tok.kind == .eof { if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.prev_tok.pos()) p.error_with_pos('expecting `)`', p.prev_tok.pos())
return []ast.Param{}, false, false return []ast.Param{}, false, false, false
} }
if p.tok.kind == .comma { if p.tok.kind == .comma {
if is_variadic { if is_variadic {
p.error_with_pos('cannot use ...(variadic) with non-final parameter no ${param_no}', p.error_with_pos('cannot use ...(variadic) with non-final parameter no ${param_no}',
pos) pos)
return []ast.Param{}, false, false return []ast.Param{}, false, false, false
} }
p.next() p.next()
} }
@ -987,14 +995,14 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
param_no++ param_no++
if param_no > 1024 { if param_no > 1024 {
p.error_with_pos('too many parameters', pos) p.error_with_pos('too many parameters', pos)
return []ast.Param{}, false, false return []ast.Param{}, false, false, false
} }
} }
} else { } else {
for p.tok.kind != .rpar { for p.tok.kind != .rpar {
if p.tok.kind == .eof { if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.tok.pos()) 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_shared := p.tok.kind == .key_shared
is_atomic := p.tok.kind == .key_atomic is_atomic := p.tok.kind == .key_atomic
@ -1002,6 +1010,12 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
if is_mut { if is_mut {
p.next() 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()] mut param_pos := [p.tok.pos()]
name := p.check_name() name := p.check_name()
mut param_names := [name] mut param_names := [name]
@ -1035,13 +1049,18 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
if p.tok.kind == .ellipsis { if p.tok.kind == .ellipsis {
p.next() p.next()
is_variadic = true 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() pos := p.tok.pos()
mut typ := p.parse_type() mut typ := p.parse_type()
type_pos[0] = pos.extend(p.prev_tok.pos()) type_pos[0] = pos.extend(p.prev_tok.pos())
if typ == 0 { if typ == 0 {
// error is added in parse_type // error is added in parse_type
return []ast.Param{}, false, false return []ast.Param{}, false, false, false
} }
if is_mut { if is_mut {
if !typ.has_flag(.generic) { if !typ.has_flag(.generic) {
@ -1059,7 +1078,7 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
} else if is_shared || is_atomic { } else if is_shared || is_atomic {
p.error_with_pos('generic object cannot be `atomic` or `shared`', p.error_with_pos('generic object cannot be `atomic` or `shared`',
pos) pos)
return []ast.Param{}, false, false return []ast.Param{}, false, false, false
} }
if typ.is_ptr() && p.table.sym(typ).kind == .struct_ { if typ.is_ptr() && p.table.sym(typ).kind == .struct_ {
typ = typ.ref() typ = typ.ref()
@ -1097,12 +1116,12 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
if is_variadic && p.tok.kind == .comma && p.peek_tok.kind != .rpar { 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}', p.error_with_pos('cannot use ...(variadic) with non-final parameter ${para_name}',
param_pos[i]) param_pos[i])
return []ast.Param{}, false, false return []ast.Param{}, false, false, false
} }
} }
if p.tok.kind == .eof { if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.prev_tok.pos()) 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 { if p.tok.kind != .rpar {
p.check(.comma) p.check(.comma)
@ -1110,7 +1129,7 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool) {
} }
} }
p.check(.rpar) 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 { fn (mut p Parser) spawn_expr() ast.SpawnExpr {

View File

@ -311,7 +311,7 @@ fn (mut p Parser) parse_fn_type(name string, generic_types []ast.Type) ast.Type
mut has_generic := false mut has_generic := false
line_nr := p.tok.line_nr 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 { for param in params {
if param.typ.has_flag(.generic) { if param.typ.has_flag(.generic) {
has_generic = true has_generic = true
@ -341,6 +341,7 @@ fn (mut p Parser) parse_fn_type(name string, generic_types []ast.Type) ast.Type
name: name name: name
params: params params: params
is_variadic: is_variadic is_variadic: is_variadic
is_c_variadic: is_c_variadic
return_type: return_type return_type: return_type
return_type_pos: return_type_pos return_type_pos: return_type_pos
generic_names: generic_names generic_names: generic_names

View File

@ -655,7 +655,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
p.error_with_pos('duplicate method `${name}`', method_start_pos) p.error_with_pos('duplicate method `${name}`', method_start_pos)
return ast.InterfaceDecl{} 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 := [ mut params := [
ast.Param{ ast.Param{
name: 'x' name: 'x'