cgen: fix variadics called with 0 vargs

pull/4121/head
Joe Conigliaro 2020-03-26 21:09:59 +11:00
parent f2c9592b86
commit 9c6ac7cb71
3 changed files with 63 additions and 51 deletions
vlib/v

View File

@ -171,17 +171,18 @@ pub:
pub struct CallExpr { pub struct CallExpr {
pub: pub:
// tok token.Token // tok token.Token
pos token.Position pos token.Position
mut: mut:
// func Expr // func Expr
name string name string
args []CallArg args []CallArg
is_c bool exp_arg_types []table.Type
muts []bool is_c bool
or_block OrExpr muts []bool
or_block OrExpr
// has_or_block bool // has_or_block bool
return_type table.Type return_type table.Type
} }
pub struct MethodCallExpr { pub struct MethodCallExpr {
@ -193,6 +194,7 @@ pub:
args []CallArg args []CallArg
or_block OrExpr or_block OrExpr
mut: mut:
exp_arg_types []table.Type
expr_type table.Type // type of `user` expr_type table.Type // type of `user`
receiver_type table.Type // User receiver_type table.Type // User
return_type table.Type return_type table.Type
@ -200,11 +202,10 @@ mut:
pub struct CallArg { pub struct CallArg {
pub: pub:
is_mut bool is_mut bool
expr Expr expr Expr
mut: mut:
typ table.Type typ table.Type
expected_type table.Type
} }
pub struct Return { pub struct Return {

View File

@ -232,8 +232,9 @@ pub fn (c mut Checker) call_expr(call_expr mut ast.CallExpr) table.Type {
} }
return f.return_type return f.return_type
} }
if call_expr.args.len < f.args.len { min_required_args := if f.is_variadic { f.args.len - 1 } else { f.args.len }
c.error('too few arguments in call to `$fn_name`', call_expr.pos) if call_expr.args.len < min_required_args {
c.error('too few arguments in call to `$fn_name` ($call_expr.args.len instead of $min_required_args)', call_expr.pos)
} }
else if !f.is_variadic && call_expr.args.len > f.args.len { else if !f.is_variadic && call_expr.args.len > f.args.len {
c.error('too many arguments in call to `$fn_name` ($call_expr.args.len instead of $f.args.len)', call_expr.pos) c.error('too many arguments in call to `$fn_name` ($call_expr.args.len instead of $f.args.len)', call_expr.pos)
@ -244,12 +245,17 @@ pub fn (c mut Checker) call_expr(call_expr mut ast.CallExpr) table.Type {
call_expr.args[0].typ = c.expr(call_expr.args[0].expr) call_expr.args[0].typ = c.expr(call_expr.args[0].expr)
return f.return_type return f.return_type
} }
// TODO: typ optimize.. this node can get processed more than once
if call_expr.exp_arg_types.len == 0 {
for arg in f.args {
call_expr.exp_arg_types << arg.typ
}
}
for i, call_arg in call_expr.args { for i, call_arg in call_expr.args {
arg := if f.is_variadic && i >= f.args.len - 1 { f.args[f.args.len - 1] } else { f.args[i] } arg := if f.is_variadic && i >= f.args.len - 1 { f.args[f.args.len - 1] } else { f.args[i] }
c.expected_type = arg.typ c.expected_type = arg.typ
typ := c.expr(call_arg.expr) typ := c.expr(call_arg.expr)
call_expr.args[i].typ = typ call_expr.args[i].typ = typ
call_expr.args[i].expected_type = arg.typ
typ_sym := c.table.get_type_symbol(typ) typ_sym := c.table.get_type_symbol(typ)
arg_typ_sym := c.table.get_type_symbol(arg.typ) arg_typ_sym := c.table.get_type_symbol(arg.typ)
if !c.table.check(typ, arg.typ) { if !c.table.check(typ, arg.typ) {
@ -320,9 +326,14 @@ pub fn (c mut Checker) method_call_expr(method_call_expr mut ast.MethodCallExpr)
// } // }
for i, arg in method_call_expr.args { for i, arg in method_call_expr.args {
c.expected_type = method.args[i + 1].typ c.expected_type = method.args[i + 1].typ
method_call_expr.args[i].expected_type = c.expected_type
method_call_expr.args[i].typ = c.expr(arg.expr) method_call_expr.args[i].typ = c.expr(arg.expr)
} }
// TODO: typ optimize.. this node can get processed more than once
if method_call_expr.exp_arg_types.len == 0 {
for i in 1 .. method.args.len {
method_call_expr.exp_arg_types << method.args[i].typ
}
}
method_call_expr.receiver_type = method.args[0].typ method_call_expr.receiver_type = method.args[0].typ
method_call_expr.return_type = method.return_type method_call_expr.return_type = method.return_type
return method.return_type return method.return_type

View File

@ -1021,7 +1021,7 @@ fn (g mut Gen) expr(node ast.Expr) {
} }
*/ */
// /////// // ///////
g.call_args(it.args) g.call_args(it.args, it.exp_arg_types)
g.write(')') g.write(')')
} }
ast.None { ast.None {
@ -1729,55 +1729,60 @@ fn (g mut Gen) assoc(node ast.Assoc) {
} }
} }
fn (g mut Gen) call_args(args []ast.CallArg) { fn (g mut Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
for i, arg in args { is_variadic := expected_types.len > 0 && table.type_is_variadic(expected_types[expected_types.len - 1])
if table.type_is_variadic(arg.expected_type) { mut arg_no := 0
struct_name := 'varg_' + g.typ(arg.expected_type).replace('*', '_ptr') for arg in args {
len := args.len - i if is_variadic && arg_no == expected_types.len - 1 {
varg_type_str := int(arg.expected_type).str()
if len > g.variadic_args[varg_type_str] {
g.variadic_args[varg_type_str] = len
}
g.write('($struct_name){.len=$len,.args={')
for j in i .. args.len {
g.ref_or_deref_arg(args[j], args[j].expr, false)
// g.expr(args[j].expr)
if j < args.len - 1 {
g.write(', ')
}
}
g.write('}}')
break break
} }
// some c fn definitions dont have args (cfns.v) or are not updated in checker // some c fn definitions dont have args (cfns.v) or are not updated in checker
if arg.expected_type != 0 { // when these are fixed we wont beed this check
g.ref_or_deref_arg(arg, arg.expr, true) if arg_no < expected_types.len {
// g.expr_with_cast(arg.expr, arg.typ, arg.expected_type) g.ref_or_deref_arg(arg, expected_types[arg_no])
} }
else { else {
g.expr(arg.expr) g.expr(arg.expr)
} }
if i != args.len - 1 { if arg_no < args.len - 1 || is_variadic {
g.write(', ') g.write(', ')
} }
arg_no++
}
if is_variadic {
varg_type := expected_types[expected_types.len - 1]
struct_name := 'varg_' + g.typ(varg_type).replace('*', '_ptr')
len := args.len - arg_no
varg_type_str := int(varg_type).str()
if len > g.variadic_args[varg_type_str] {
g.variadic_args[varg_type_str] = len
}
g.write('($struct_name){.len=$len,.args={')
for j in arg_no .. args.len {
g.ref_or_deref_arg(args[j], varg_type)
if j < args.len - 1 {
g.write(', ')
}
}
g.write('}}')
} }
} }
[inline] [inline]
fn (g mut Gen) ref_or_deref_arg(arg ast.CallArg, expr ast.Expr, with_cast bool) { fn (g mut Gen) ref_or_deref_arg(arg ast.CallArg, expected_type table.Type) {
arg_is_ptr := table.type_is_ptr(arg.expected_type) || table.type_idx(arg.expected_type) in table.pointer_type_idxs arg_is_ptr := table.type_is_ptr(expected_type) || table.type_idx(expected_type) in table.pointer_type_idxs
expr_is_ptr := table.type_is_ptr(arg.typ) || table.type_idx(arg.typ) in table.pointer_type_idxs expr_is_ptr := table.type_is_ptr(arg.typ) || table.type_idx(arg.typ) in table.pointer_type_idxs
if arg.is_mut && !arg_is_ptr { if arg.is_mut && !arg_is_ptr {
g.write('&/*mut*/') g.write('&/*mut*/')
} }
else if arg_is_ptr && !expr_is_ptr { else if arg_is_ptr && !expr_is_ptr {
if arg.is_mut { if arg.is_mut {
sym := g.table.get_type_symbol(arg.expected_type) sym := g.table.get_type_symbol(arg.typ)
if sym.kind == .array { if sym.kind == .array {
// Special case for mutable arrays. We can't `&` function // Special case for mutable arrays. We can't `&` function
// results, have to use `(array[]){ expr }[0]` hack. // results, have to use `(array[]){ expr }[0]` hack.
g.write('&/*111*/(array[]){') g.write('&/*111*/(array[]){')
g.expr(expr) g.expr(arg.expr)
g.write('}[0]') g.write('}[0]')
return return
} }
@ -1788,12 +1793,7 @@ fn (g mut Gen) ref_or_deref_arg(arg ast.CallArg, expr ast.Expr, with_cast bool)
// Dereference a pointer if a value is required // Dereference a pointer if a value is required
g.write('*/*d*/') g.write('*/*d*/')
} }
if with_cast { g.expr_with_cast(arg.expr, arg.typ, expected_type)
g.expr_with_cast(arg.expr, arg.typ, arg.expected_type)
}
else {
g.expr(arg.expr)
}
} }
fn verror(s string) { fn verror(s string) {
@ -2102,7 +2102,7 @@ fn (g mut Gen) call_expr(it ast.CallExpr) {
} }
else { else {
g.write('${name}(') g.write('${name}(')
g.call_args(it.args) g.call_args(it.args, it.exp_arg_types)
g.write(')') g.write(')')
} }
if it.or_block.stmts.len > 0 { if it.or_block.stmts.len > 0 {