all: re-implement variadics using arrays & implement array decomposition to varg (#7689)

pull/7735/head
joe-conigliaro 2020-12-30 02:14:08 +11:00 committed by GitHub
parent 6cf3b96a37
commit 02965e753e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 210 additions and 183 deletions

View File

@ -9,9 +9,9 @@ import v.errors
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral | CTempVar | pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral |
CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr |
FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral |
Likely | LockExpr | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | Likely | LockExpr | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr |
RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
StructInit | Type | TypeOf | UnsafeExpr StructInit | Type | TypeOf | UnsafeExpr
@ -890,6 +890,15 @@ pub mut:
typ table.Type // array type typ table.Type // array type
} }
pub struct ArrayDecompose {
pub:
expr Expr
pos token.Position
pub mut:
expr_type table.Type
arg_type table.Type
}
pub struct ChanInit { pub struct ChanInit {
pub: pub:
pos token.Position pos token.Position
@ -1129,6 +1138,9 @@ pub fn (expr Expr) position() token.Position {
ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, EnumVal, FloatLiteral, Ident, IfExpr, IndexExpr, IntegerLiteral, Likely, LockExpr, MapInit, MatchExpr, None, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, Type, TypeOf, UnsafeExpr { ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, EnumVal, FloatLiteral, Ident, IfExpr, IndexExpr, IntegerLiteral, Likely, LockExpr, MapInit, MatchExpr, None, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, Type, TypeOf, UnsafeExpr {
return expr.pos return expr.pos
} }
ArrayDecompose {
return expr.pos
}
IfGuardExpr { IfGuardExpr {
return expr.expr.position() return expr.expr.position()
} }

View File

@ -7,6 +7,20 @@ import v.table
import v.token import v.token
import v.ast import v.ast
pub fn (mut c Checker) check_expected_call_arg(got table.Type, expected_ table.Type) ? {
mut expected := expected_
// variadic
if expected.has_flag(.variadic) {
exp_type_sym := c.table.get_type_symbol(expected_)
exp_info := exp_type_sym.info as table.Array
expected = exp_info.elem_type
}
if c.check_types(got, expected) {
return
}
return error('cannot use `${c.table.type_to_str(got.clear_flag(.variadic))}` as `${c.table.type_to_str(expected.clear_flag(.variadic))}`')
}
pub fn (mut c Checker) check_basic(got table.Type, expected table.Type) bool { pub fn (mut c Checker) check_basic(got table.Type, expected table.Type) bool {
if got == expected { if got == expected {
return true return true

View File

@ -1333,21 +1333,16 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position()) c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position())
continue continue
} }
if !c.check_types(got_arg_typ, exp_arg_typ) { c.check_expected_call_arg(got_arg_typ, exp_arg_typ) or {
got_arg_sym := c.table.get_type_symbol(got_arg_typ)
// str method, allow type with str method if fn arg is string // str method, allow type with str method if fn arg is string
// if exp_arg_sym.kind == .string && got_arg_sym.has_method('str') { // Passing an int or a string array produces a c error here
// Deleting this condition results in propper V error messages
// if arg_typ_sym.kind == .string && typ_sym.has_method('str') {
// continue // continue
// } // }
// same ancestor? let it be
if exp_arg_sym.parent_idx == got_arg_sym.parent_idx {
if got_arg_sym.parent_idx != 0 {
continue
}
}
if got_arg_typ != table.void_type { if got_arg_typ != table.void_type {
c.error('cannot use type `$got_arg_sym.name` as type `$exp_arg_sym.name` in argument ${i + c.error('$err in argument ${i + 1} to `${left_type_sym.name}.$method_name`',
1} to `${left_type_sym.name}.$method_name`', call_expr.pos) call_expr.pos)
} }
} }
param := if method.is_variadic && i >= method.params.len - 1 { method.params[method.params.len - param := if method.is_variadic && i >= method.params.len - 1 { method.params[method.params.len -
@ -1660,7 +1655,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
c.type_implements(typ, arg.typ, call_arg.expr.position()) c.type_implements(typ, arg.typ, call_arg.expr.position())
continue continue
} }
c.check_expected(typ, arg.typ) or { c.check_expected_call_arg(typ, arg.typ) or {
// str method, allow type with str method if fn arg is string // str method, allow type with str method if fn arg is string
// Passing an int or a string array produces a c error here // Passing an int or a string array produces a c error here
// Deleting this condition results in propper V error messages // Deleting this condition results in propper V error messages
@ -1673,7 +1668,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
if f.is_generic { if f.is_generic {
continue continue
} }
c.error('invalid argument ${i + 1} to `$fn_name`: $err', call_arg.pos) c.error('$err in argument ${i + 1} to `$fn_name`', call_arg.pos)
} }
} }
if f.is_generic && call_expr.generic_type == table.void_type { if f.is_generic && call_expr.generic_type == table.void_type {
@ -2981,6 +2976,18 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
c.cur_fn = keep_fn c.cur_fn = keep_fn
return node.typ return node.typ
} }
ast.ArrayDecompose {
typ := c.expr(node.expr)
type_sym := c.table.get_type_symbol(typ)
if type_sym.kind != .array {
c.error('expected array', node.pos)
}
array_info := type_sym.info as table.Array
elem_type := array_info.elem_type.set_flag(.variadic)
node.expr_type = typ
node.arg_type = elem_type
return elem_type
}
ast.ArrayInit { ast.ArrayInit {
return c.array_init(mut node) return c.array_init(mut node)
} }

View File

@ -1,17 +1,17 @@
vlib/v/checker/tests/fn_args.vv:6:5: error: invalid argument 1 to `ptr`: expected `byte`, not `&int` vlib/v/checker/tests/fn_args.vv:6:5: error: cannot use `&int` as `byte` in argument 1 to `ptr`
4 | 4 |
5 | v := 4 5 | v := 4
6 | ptr(&v) 6 | ptr(&v)
| ~~ | ~~
7 | arr([5]!!) 7 | arr([5]!!)
8 | fun(fn(i &int){}) 8 | fun(fn(i &int){})
vlib/v/checker/tests/fn_args.vv:7:5: error: invalid argument 1 to `arr`: expected `[]int`, not `[1]int` vlib/v/checker/tests/fn_args.vv:7:5: error: cannot use `[1]int` as `[]int` in argument 1 to `arr`
5 | v := 4 5 | v := 4
6 | ptr(&v) 6 | ptr(&v)
7 | arr([5]!!) 7 | arr([5]!!)
| ~~~~~ | ~~~~~
8 | fun(fn(i &int){}) 8 | fun(fn(i &int){})
vlib/v/checker/tests/fn_args.vv:8:5: error: invalid argument 1 to `fun`: expected `fn (int)`, not `fn (&int)` vlib/v/checker/tests/fn_args.vv:8:5: error: cannot use `fn (&int)` as `fn (int)` in argument 1 to `fun`
6 | ptr(&v) 6 | ptr(&v)
7 | arr([5]!!) 7 | arr([5]!!)
8 | fun(fn(i &int){}) 8 | fun(fn(i &int){})

View File

@ -1,4 +1,4 @@
vlib/v/checker/tests/function_wrong_arg_type.vv:7:9: error: invalid argument 1 to `f`: expected `int`, not `f64` vlib/v/checker/tests/function_wrong_arg_type.vv:7:9: error: cannot use `f64` as `int` in argument 1 to `f`
5 | fn main() { 5 | fn main() {
6 | a := 12.3 6 | a := 12.3
7 | q := f(a) 7 | q := f(a)

View File

@ -1,4 +1,4 @@
vlib/v/checker/tests/is_type_not_exist.vv:4:25: error: invalid argument 1 to `fn_with_sum_type_param`: expected `Integer`, not `any_int` vlib/v/checker/tests/is_type_not_exist.vv:4:25: error: cannot use `any_int` as `Integer` in argument 1 to `fn_with_sum_type_param`
2 | 2 |
3 | fn main() { 3 | fn main() {
4 | fn_with_sum_type_param(1) 4 | fn_with_sum_type_param(1)

View File

@ -1,4 +1,4 @@
vlib/v/checker/tests/method_wrong_arg_type.vv:8:4: error: cannot use type `MyEnum` as type `string` in argument 1 to `Sss.info` vlib/v/checker/tests/method_wrong_arg_type.vv:8:4: error: cannot use `MyEnum` as `string` in argument 1 to `Sss.info`
6 | e := MyEnum.x 6 | e := MyEnum.x
7 | s := Sss{} 7 | s := Sss{}
8 | s.info(e) 8 | s.info(e)

View File

@ -1,4 +1,4 @@
vlib/v/checker/tests/non_matching_functional_args.vv:27:6: error: invalid argument 1 to `sum`: expected `fn (Table)`, not `fn (mut Table)` vlib/v/checker/tests/non_matching_functional_args.vv:27:6: error: cannot use `fn (mut Table)` as `fn (Table)` in argument 1 to `sum`
25 | 25 |
26 | fn main() { 26 | fn main() {
27 | sum(fn (mut t Table) { 27 | sum(fn (mut t Table) {
@ -6,7 +6,7 @@ vlib/v/checker/tests/non_matching_functional_args.vv:27:6: error: invalid argume
28 | t.rename() 28 | t.rename()
29 | println(t.name) 29 | println(t.name)
details: `main.MyFn`'s expected fn argument: `zzzz` is NOT a pointer, but the passed fn argument: `t` is a pointer details: `main.MyFn`'s expected fn argument: `zzzz` is NOT a pointer, but the passed fn argument: `t` is a pointer
vlib/v/checker/tests/non_matching_functional_args.vv:31:6: error: invalid argument 1 to `sum`: expected `fn (Table)`, not `fn (mut Table)` vlib/v/checker/tests/non_matching_functional_args.vv:31:6: error: cannot use `fn (mut Table)` as `fn (Table)` in argument 1 to `sum`
29 | println(t.name) 29 | println(t.name)
30 | }) 30 | })
31 | sum(xxx) 31 | sum(xxx)

View File

@ -1138,6 +1138,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
} }
f.write('}') f.write('}')
} }
ast.ArrayDecompose {
f.expr(node.expr)
}
} }
} }

View File

@ -63,15 +63,6 @@ fn (mut g Gen) gen_str_for_type(typ table.Type) string {
} }
} }
} }
// if varg, generate str for varg
if typ.has_flag(.variadic) {
varg_already_generated_key := 'varg_$already_generated_key'
if varg_already_generated_key !in g.str_types {
g.gen_str_for_varg(styp, str_fn_name, sym_has_str_method)
g.str_types << varg_already_generated_key
}
return 'varg_$str_fn_name'
}
if typ.has_flag(.optional) { if typ.has_flag(.optional) {
option_already_generated_key := 'option_$already_generated_key' option_already_generated_key := 'option_$already_generated_key'
if option_already_generated_key !in g.str_types { if option_already_generated_key !in g.str_types {
@ -305,22 +296,6 @@ fn (mut g Gen) gen_str_for_map(info table.Map, styp string, str_fn_name string)
g.auto_str_funcs.writeln('}') g.auto_str_funcs.writeln('}')
} }
fn (mut g Gen) gen_str_for_varg(styp string, str_fn_name string, has_str_method bool) {
g.definitions.writeln('static string varg_${str_fn_name}(varg_$styp it); // auto')
g.auto_str_funcs.writeln('static string varg_${str_fn_name}(varg_$styp it) {')
g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(it.len);')
g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, _SLIT("["));')
g.auto_str_funcs.writeln('\tfor(int i=0; i<it.len; ++i) {')
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${str_fn_name}(it.args[i]));')
g.auto_str_funcs.writeln('\t\tif (i < it.len-1) {')
g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, _SLIT(", "));')
g.auto_str_funcs.writeln('\t\t}')
g.auto_str_funcs.writeln('\t}')
g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, _SLIT("]"));')
g.auto_str_funcs.writeln('\treturn strings__Builder_str(&sb);')
g.auto_str_funcs.writeln('}')
}
fn (mut g Gen) gen_str_for_multi_return(info table.MultiReturn, styp string, str_fn_name string) { fn (mut g Gen) gen_str_for_multi_return(info table.MultiReturn, styp string, str_fn_name string) {
for typ in info.types { for typ in info.types {
sym := g.table.get_type_symbol(typ) sym := g.table.get_type_symbol(typ)

View File

@ -56,7 +56,6 @@ mut:
last_fn_c_name string last_fn_c_name string
tmp_count int // counter for unique tmp vars (_tmp1, tmp2 etc) tmp_count int // counter for unique tmp vars (_tmp1, tmp2 etc)
tmp_count2 int // a separate tmp var counter for autofree fn calls tmp_count2 int // a separate tmp var counter for autofree fn calls
variadic_args map[string]int
is_c_call bool // e.g. `C.printf("v")` is_c_call bool // e.g. `C.printf("v")`
is_assign_lhs bool // inside left part of assign expr (for array_set(), etc) is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
is_assign_rhs bool // inside right part of assign after `=` (val expr) is_assign_rhs bool // inside right part of assign after `=` (val expr)
@ -246,7 +245,6 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
g.definitions.writeln('int _v_type_idx_${typ.cname}() { return $idx; };') g.definitions.writeln('int _v_type_idx_${typ.cname}() { return $idx; };')
} }
} }
g.write_variadic_types()
// g.write_str_definitions() // g.write_str_definitions()
// v files are finished, what remains is pure C code // v files are finished, what remains is pure C code
g.gen_vlines_reset() g.gen_vlines_reset()
@ -704,21 +702,6 @@ pub fn (mut g Gen) write_multi_return_types() {
g.type_definitions.writeln('// END_multi_return_structs\n') g.type_definitions.writeln('// END_multi_return_structs\n')
} }
pub fn (mut g Gen) write_variadic_types() {
g.type_definitions.writeln('\n//BEGIN_variadic_structs')
for type_str, arg_len in g.variadic_args {
typ := table.Type(type_str.int())
type_name := g.typ(typ)
struct_name := 'varg_' + type_name.replace('*', '_ptr')
g.type_definitions.writeln('struct $struct_name {')
g.type_definitions.writeln('\tint len;')
g.type_definitions.writeln('\t$type_name args[$arg_len];')
g.type_definitions.writeln('};\n')
g.typedefs.writeln('typedef struct $struct_name $struct_name;')
}
g.type_definitions.writeln('// END_variadic_structs\n')
}
pub fn (mut g Gen) write(s string) { pub fn (mut g Gen) write(s string) {
$if trace_gen ? { $if trace_gen ? {
eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | write: $s') eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | write: $s')
@ -1324,16 +1307,6 @@ fn (mut g Gen) for_in(it ast.ForInStmt) {
g.writeln('\t${it.label}__break: {}') g.writeln('\t${it.label}__break: {}')
} }
return return
} else if it.cond_type.has_flag(.variadic) {
g.writeln('// FOR IN cond_type/variadic')
i := if it.key_var in ['', '_'] { g.new_tmp_var() } else { it.key_var }
styp := g.typ(it.cond_type)
g.write('for (int $i = 0; $i < ')
g.expr(it.cond)
g.writeln('.len; ++$i) {')
g.write('\t$styp ${c_name(it.val_var)} = ')
g.expr(it.cond)
g.writeln('.args[$i];')
} else if it.kind == .string { } else if it.kind == .string {
i := if it.key_var in ['', '_'] { g.new_tmp_var() } else { it.key_var } i := if it.key_var in ['', '_'] { g.new_tmp_var() } else { it.key_var }
g.write('for (int $i = 0; $i < ') g.write('for (int $i = 0; $i < ')
@ -2374,6 +2347,9 @@ fn (mut g Gen) expr(node ast.Expr) {
fsym := g.table.get_type_symbol(node.typ) fsym := g.table.get_type_symbol(node.typ)
g.write(fsym.name) g.write(fsym.name)
} }
ast.ArrayDecompose {
g.expr(node.expr)
}
ast.ArrayInit { ast.ArrayInit {
g.array_init(node) g.array_init(node)
} }
@ -2775,7 +2751,8 @@ fn (mut g Gen) typeof_expr(node ast.TypeOf) {
info := sym.info as table.FnType info := sym.info as table.FnType
g.write('_SLIT("${g.fn_decl_str(info)}")') g.write('_SLIT("${g.fn_decl_str(info)}")')
} else if node.expr_type.has_flag(.variadic) { } else if node.expr_type.has_flag(.variadic) {
g.write('_SLIT("...${util.strip_main_name(sym.name)}")') varg_elem_type_sym := g.table.get_type_symbol(g.table.value_type(node.expr_type))
g.write('_SLIT("...${util.strip_main_name(varg_elem_type_sym.name)}")')
} else { } else {
g.write('_SLIT("${util.strip_main_name(sym.name)}")') g.write('_SLIT("${util.strip_main_name(sym.name)}")')
} }
@ -3835,13 +3812,7 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
else { else {
sym := g.table.get_type_symbol(node.left_type) sym := g.table.get_type_symbol(node.left_type)
left_is_ptr := node.left_type.is_ptr() left_is_ptr := node.left_type.is_ptr()
if node.left_type.has_flag(.variadic) { if sym.kind == .array {
g.expr(node.left)
g.write('.args')
g.write('[')
g.expr(node.index)
g.write(']')
} else if sym.kind == .array {
info := sym.info as table.Array info := sym.info as table.Array
elem_type_str := g.typ(info.elem_type) elem_type_str := g.typ(info.elem_type)
elem_typ := g.table.get_type_symbol(info.elem_type) elem_typ := g.table.get_type_symbol(info.elem_type)

View File

@ -52,10 +52,8 @@ const (
#endif #endif
#ifdef __TINYC__ #ifdef __TINYC__
#undef EMPTY_VARG_INITIALIZATION
#undef EMPTY_STRUCT_DECLARATION #undef EMPTY_STRUCT_DECLARATION
#undef EMPTY_STRUCT_INITIALIZATION #undef EMPTY_STRUCT_INITIALIZATION
#define EMPTY_VARG_INITIALIZATION
#define EMPTY_STRUCT_DECLARATION char _dummy #define EMPTY_STRUCT_DECLARATION char _dummy
#define EMPTY_STRUCT_INITIALIZATION 0 #define EMPTY_STRUCT_INITIALIZATION 0
#undef EMPTY_ARRAY_OF_ELEMS #undef EMPTY_ARRAY_OF_ELEMS

View File

@ -215,14 +215,6 @@ fn (mut g Gen) fn_args(args []table.Param, is_variadic bool) ([]string, []string
typ := g.unwrap_generic(arg.typ) typ := g.unwrap_generic(arg.typ)
arg_type_sym := g.table.get_type_symbol(typ) arg_type_sym := g.table.get_type_symbol(typ)
mut arg_type_name := g.typ(typ) // util.no_dots(arg_type_sym.name) mut arg_type_name := g.typ(typ) // util.no_dots(arg_type_sym.name)
is_varg := i == args.len - 1 && is_variadic
if is_varg {
varg_type_str := int(arg.typ).str()
if varg_type_str !in g.variadic_args {
g.variadic_args[varg_type_str] = 0
}
arg_type_name = 'varg_' + g.typ(arg.typ).replace('*', '_ptr')
}
if arg_type_sym.kind == .function { if arg_type_sym.kind == .function {
info := arg_type_sym.info as table.FnType info := arg_type_sym.info as table.FnType
func := info.func func := info.func
@ -764,10 +756,8 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
args := if g.is_js_call { node.args[1..] } else { node.args } args := if g.is_js_call { node.args[1..] } else { node.args }
expected_types := node.expected_arg_types expected_types := node.expected_arg_types
is_variadic := expected_types.len > 0 && expected_types[expected_types.len - 1].has_flag(.variadic) is_variadic := expected_types.len > 0 && expected_types[expected_types.len - 1].has_flag(.variadic)
is_forwarding_varg := args.len > 0 && args[args.len - 1].typ.has_flag(.variadic)
gen_vargs := is_variadic && !is_forwarding_varg
for i, arg in args { for i, arg in args {
if gen_vargs && i == expected_types.len - 1 { if is_variadic && i == expected_types.len - 1 {
break break
} }
use_tmp_var_autofree := g.autofree && arg.typ == table.string_type && arg.is_tmp_autofree && use_tmp_var_autofree := g.autofree && arg.typ == table.string_type && arg.is_tmp_autofree &&
@ -818,32 +808,33 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
if is_interface { if is_interface {
g.write(')') g.write(')')
} }
if i < args.len - 1 || gen_vargs { if i < args.len - 1 || is_variadic {
g.write(', ') g.write(', ')
} }
} }
arg_nr := expected_types.len - 1 arg_nr := expected_types.len - 1
if gen_vargs { if is_variadic {
varg_type := expected_types[expected_types.len - 1] varg_type := expected_types[expected_types.len - 1]
struct_name := 'varg_' + g.typ(varg_type).replace('*', '_ptr')
variadic_count := args.len - arg_nr variadic_count := args.len - arg_nr
varg_type_str := int(varg_type).str() arr_sym := g.table.get_type_symbol(varg_type)
if variadic_count > g.variadic_args[varg_type_str] { arr_info := arr_sym.info as table.Array
g.variadic_args[varg_type_str] = variadic_count elem_type := g.typ(arr_info.elem_type)
} if args.len > 0 && args[args.len - 1].expr is ast.ArrayDecompose {
g.write('($struct_name){.len=$variadic_count,.args={') g.expr(args[args.len - 1].expr)
if variadic_count > 0 {
for j in arg_nr .. args.len {
g.ref_or_deref_arg(args[j], varg_type)
if j < args.len - 1 {
g.write(', ')
}
}
} else { } else {
// NB: tcc can not handle 0 here, while msvc needs it if variadic_count > 0 {
g.write('EMPTY_VARG_INITIALIZATION') g.write('new_array_from_c_array($variadic_count, $variadic_count, sizeof($elem_type), _MOV(($elem_type[$variadic_count]){')
for j in arg_nr .. args.len {
g.ref_or_deref_arg(args[j], arr_info.elem_type)
if j < args.len - 1 {
g.write(', ')
}
}
g.write('}))')
} else {
g.write('__new_array_with_default(0, 0, sizeof($elem_type), 0)')
}
} }
g.write('}}')
} }
} }

View File

@ -16,7 +16,8 @@ const (
'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void', 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void',
'while', 'with', 'yield', 'Number', 'String', 'Boolean', 'Array', 'Map'] 'while', 'with', 'yield', 'Number', 'String', 'Boolean', 'Array', 'Map']
// used to generate type structs // used to generate type structs
v_types = ['i8', 'i16', 'int', 'i64', 'byte', 'u16', 'u32', 'u64', 'f32', 'f64', 'any_int', 'any_float', 'size_t', 'bool', 'string', 'map', 'array'] v_types = ['i8', 'i16', 'int', 'i64', 'byte', 'u16', 'u32', 'u64', 'f32', 'f64', 'any_int',
'any_float', 'size_t', 'bool', 'string', 'map', 'array']
tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t', tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t',
'\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t'] '\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t']
) )
@ -32,30 +33,30 @@ mut:
} }
struct JsGen { struct JsGen {
table &table.Table table &table.Table
pref &pref.Preferences pref &pref.Preferences
mut: mut:
definitions strings.Builder definitions strings.Builder
ns &Namespace ns &Namespace
namespaces map[string]&Namespace namespaces map[string]&Namespace
doc &JsDoc doc &JsDoc
enable_doc bool enable_doc bool
file ast.File file ast.File
tmp_count int tmp_count int
inside_ternary bool inside_ternary bool
inside_loop bool inside_loop bool
inside_map_set bool // map.set(key, value) inside_map_set bool // map.set(key, value)
inside_builtin bool inside_builtin bool
generated_builtin bool generated_builtin bool
inside_def_typ_decl bool inside_def_typ_decl bool
is_test bool is_test bool
stmt_start_pos int stmt_start_pos int
defer_stmts []ast.DeferStmt defer_stmts []ast.DeferStmt
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
str_types []string // types that need automatic str() generation str_types []string // types that need automatic str() generation
method_fn_decls map[string][]ast.FnDecl method_fn_decls map[string][]ast.FnDecl
builtin_fns []string // Functions defined in `builtin` builtin_fns []string // Functions defined in `builtin`
empty_line bool empty_line bool
} }
pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string { pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string {
@ -158,12 +159,16 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
out += '// builtin type casts\n' out += '// builtin type casts\n'
out += 'const [' out += 'const ['
for i, typ in v_types { for i, typ in v_types {
if i > 0 { out += ', ' } if i > 0 {
out += ', '
}
out += '$typ' out += '$typ'
} }
out += '] = [' out += '] = ['
for i, typ in v_types { for i, typ in v_types {
if i > 0 { out += ',' } if i > 0 {
out += ','
}
out += '\n\tfunction(val) { return new builtin.${typ}(val) }' out += '\n\tfunction(val) { return new builtin.${typ}(val) }'
} }
out += '\n]\n' out += '\n]\n'
@ -257,14 +262,18 @@ pub fn (mut g JsGen) dec_indent() {
[inline] [inline]
pub fn (mut g JsGen) write(s string) { pub fn (mut g JsGen) write(s string) {
if g.ns == 0 { verror('g.write: not in a namespace') } if g.ns == 0 {
verror('g.write: not in a namespace')
}
g.gen_indent() g.gen_indent()
g.ns.out.write(s) g.ns.out.write(s)
} }
[inline] [inline]
pub fn (mut g JsGen) writeln(s string) { pub fn (mut g JsGen) writeln(s string) {
if g.ns == 0 { verror('g.writeln: not in a namespace') } if g.ns == 0 {
verror('g.writeln: not in a namespace')
}
g.gen_indent() g.gen_indent()
g.ns.out.writeln(s) g.ns.out.writeln(s)
g.empty_line = true g.empty_line = true
@ -304,7 +313,13 @@ fn (mut g JsGen) js_name(name_ string) string {
is_js = true is_js = true
} }
ns := get_ns(name) ns := get_ns(name)
name = if g.ns == 0 { name } else if ns == g.ns.name { name.split('.').last() } else { g.get_alias(name) } name = if g.ns == 0 {
name
} else if ns == g.ns.name {
name.split('.').last()
} else {
g.get_alias(name)
}
mut parts := name.split('.') mut parts := name.split('.')
if !is_js { if !is_js {
for i, p in parts { for i, p in parts {
@ -529,8 +544,10 @@ fn (mut g JsGen) expr(node ast.Expr) {
g.gen_string_inter_literal(node) g.gen_string_inter_literal(node)
} }
ast.StringLiteral { ast.StringLiteral {
text := node.val.replace('\'', "\\'") text := node.val.replace("\'", "\\'")
if g.file.mod.name == 'builtin' { g.write('new ') } if g.file.mod.name == 'builtin' {
g.write('new ')
}
g.write("string('$text')") g.write("string('$text')")
} }
ast.StructInit { ast.StructInit {
@ -559,6 +576,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
ast.UnsafeExpr { ast.UnsafeExpr {
g.expr(node.expr) g.expr(node.expr)
} }
ast.ArrayDecompose {}
} }
} }
@ -1012,19 +1030,22 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
if !('toString' in fn_names) { if !('toString' in fn_names) {
g.writeln('toString() {') g.writeln('toString() {')
g.inc_indent() g.inc_indent()
g.write('return `${js_name} {') g.write('return `$js_name {')
for i, field in node.fields { for i, field in node.fields {
g.write(if i == 0 { ' ' } else { ', ' }) g.write(if i == 0 {
' '
} else {
', '
})
match g.typ(field.typ).split('.').last() { match g.typ(field.typ).split('.').last() {
"string" { g.write('$field.name: "\${this["${field.name}"].toString()}"') } 'string' { g.write('$field.name: "\${this["$field.name"].toString()}"') }
else { g.write('$field.name: \${this["${field.name}"].toString()} ') } else { g.write('$field.name: \${this["$field.name"].toString()} ') }
} }
} }
g.writeln('}`') g.writeln('}`')
g.dec_indent() g.dec_indent()
g.writeln('}') g.writeln('}')
} }
g.dec_indent() g.dec_indent()
g.writeln('};\n') g.writeln('};\n')
if node.is_pub { if node.is_pub {
@ -1378,31 +1399,49 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
fn (mut g JsGen) greater_typ(left table.Type, right table.Type) table.Type { fn (mut g JsGen) greater_typ(left table.Type, right table.Type) table.Type {
l := int(left) l := int(left)
r := int(right) r := int(right)
lr := [l,r] lr := [l, r]
if table.string_type_idx in lr {
if table.string_type_idx in lr { return table.Type(table.string_type_idx) } return table.Type(table.string_type_idx)
}
should_float := (l in table.integer_type_idxs && r in table.float_type_idxs) || (r in table.integer_type_idxs && l in table.float_type_idxs) should_float := (l in table.integer_type_idxs &&
r in table.float_type_idxs) ||
(r in table.integer_type_idxs && l in table.float_type_idxs)
if should_float { if should_float {
if table.f64_type_idx in lr { return table.Type(table.f64_type_idx) } if table.f64_type_idx in lr {
if table.f32_type_idx in lr { return table.Type(table.f32_type_idx) } return table.Type(table.f64_type_idx)
}
if table.f32_type_idx in lr {
return table.Type(table.f32_type_idx)
}
return table.Type(table.any_flt_type) return table.Type(table.any_flt_type)
} }
should_int := (l in table.integer_type_idxs && r in table.integer_type_idxs) should_int := (l in table.integer_type_idxs && r in table.integer_type_idxs)
if should_int { if should_int {
// cant add to u64 - if (table.u64_type_idx in lr) { return table.Type(table.u64_type_idx) } // cant add to u64 - if (table.u64_type_idx in lr) { return table.Type(table.u64_type_idx) }
// just guessing this order // just guessing this order
if table.i64_type_idx in lr { return table.Type(table.i64_type_idx) } if table.i64_type_idx in lr {
if table.u32_type_idx in lr { return table.Type(table.u32_type_idx) } return table.Type(table.i64_type_idx)
if table.int_type_idx in lr { return table.Type(table.int_type_idx) } }
if table.u16_type_idx in lr { return table.Type(table.u16_type_idx) } if table.u32_type_idx in lr {
if table.i16_type_idx in lr { return table.Type(table.i16_type_idx) } return table.Type(table.u32_type_idx)
if table.byte_type_idx in lr { return table.Type(table.byte_type_idx) } }
if table.i8_type_idx in lr { return table.Type(table.i8_type_idx) } if table.int_type_idx in lr {
return table.Type(table.int_type_idx)
}
if table.u16_type_idx in lr {
return table.Type(table.u16_type_idx)
}
if table.i16_type_idx in lr {
return table.Type(table.i16_type_idx)
}
if table.byte_type_idx in lr {
return table.Type(table.byte_type_idx)
}
if table.i8_type_idx in lr {
return table.Type(table.i8_type_idx)
}
return table.Type(table.any_int_type_idx) return table.Type(table.any_int_type_idx)
} }
return table.Type(l) return table.Type(l)
} }
@ -1439,7 +1478,9 @@ fn (mut g JsGen) gen_selector_expr(it ast.SelectorExpr) {
} }
fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) { fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
if g.file.mod.name == 'builtin' { g.write('new ') } if g.file.mod.name == 'builtin' {
g.write('new ')
}
g.write('string(`') g.write('string(`')
for i, val in it.vals { for i, val in it.vals {
escaped_val := val.replace('`', '\\`') escaped_val := val.replace('`', '\\`')
@ -1517,10 +1558,9 @@ fn (mut g JsGen) gen_typeof_expr(it ast.TypeOf) {
} }
fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) { fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) {
is_literal := ( is_literal := ((it.expr is ast.IntegerLiteral &&
(it.expr is ast.IntegerLiteral && it.typ in table.integer_type_idxs) || it.typ in table.integer_type_idxs) ||
(it.expr is ast.FloatLiteral && it.typ in table.float_type_idxs) (it.expr is ast.FloatLiteral && it.typ in table.float_type_idxs))
)
typ := g.typ(it.typ) typ := g.typ(it.typ)
if !is_literal { if !is_literal {
if !(typ in v_types) || g.ns.name == 'builtin' { if !(typ in v_types) || g.ns.name == 'builtin' {

View File

@ -224,8 +224,6 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
} else { } else {
g.write('*.*s') g.write('*.*s')
} }
} else if typ.has_flag(.variadic) {
g.write('.*s')
} else if typ.is_float() { } else if typ.is_float() {
g.write('$fmt${fspec:c}') g.write('$fmt${fspec:c}')
} else if typ.is_pointer() { } else if typ.is_pointer() {

View File

@ -126,6 +126,13 @@ pub fn (mut p Parser) call_args() []ast.CallArg {
mut comments := p.eat_comments() mut comments := p.eat_comments()
arg_start_pos := p.tok.position() arg_start_pos := p.tok.position()
mut e := p.expr(0) mut e := p.expr(0)
if p.tok.kind == .ellipsis {
p.next()
e = ast.ArrayDecompose{
expr: e
pos: p.tok.position()
}
}
if mut e is ast.StructInit { if mut e is ast.StructInit {
e.pre_comments << comments e.pre_comments << comments
comments = []ast.Comment{} comments = []ast.Comment{}
@ -533,7 +540,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
} }
} }
if is_variadic { if is_variadic {
arg_type = arg_type.set_flag(.variadic) arg_type = table.new_type(p.table.find_or_register_array(arg_type, 1)).set_flag(.variadic)
} }
if p.tok.kind == .eof { if p.tok.kind == .eof {
p.error_with_pos('expecting `)`', p.prev_tok.position()) p.error_with_pos('expecting `)`', p.prev_tok.position())
@ -621,7 +628,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
} }
} }
if is_variadic { if is_variadic {
typ = typ.set_flag(.variadic) typ = table.new_type(p.table.find_or_register_array(typ, 1)).set_flag(.variadic)
} }
for i, arg_name in arg_names { for i, arg_name in arg_names {
args << table.Param{ args << table.Param{

View File

@ -641,7 +641,9 @@ pub fn (t &Table) value_type(typ Type) Type {
typ_sym := t.get_type_symbol(typ) typ_sym := t.get_type_symbol(typ)
if typ.has_flag(.variadic) { if typ.has_flag(.variadic) {
// ...string => string // ...string => string
return typ.clear_flag(.variadic) // return typ.clear_flag(.variadic)
array_info := typ_sym.info as Array
return array_info.elem_type
} }
if typ_sym.kind == .array { if typ_sym.kind == .array {
// Check index type // Check index type

View File

@ -718,9 +718,13 @@ pub fn (table &Table) type_to_str_using_aliases(t Type, import_aliases map[strin
if t == array_type { if t == array_type {
return 'array' return 'array'
} }
info := sym.info as Array if t.has_flag(.variadic) {
elem_str := table.type_to_str_using_aliases(info.elem_type, import_aliases) res = table.type_to_str_using_aliases(table.value_type(t), import_aliases)
res = '[]$elem_str' } else {
info := sym.info as Array
elem_str := table.type_to_str_using_aliases(info.elem_type, import_aliases)
res = '[]$elem_str'
}
} }
.array_fixed { .array_fixed {
info := sym.info as ArrayFixed info := sym.info as ArrayFixed

View File

@ -34,10 +34,10 @@ fn test_fn_variadic_generic() {
*/ */
// forwarding // forwarding
fn variadic_forward_a(a ...string) string { fn variadic_forward_a(a ...string) string {
return variadic_forward_b(a) return variadic_fn_a(a...)
} }
fn variadic_forward_b(a ...string) string { fn variadic_fn_a(a ...string) string {
a0 := a[0] a0 := a[0]
a1 := a[1] a1 := a[1]
a2 := a[2] a2 := a[2]
@ -64,6 +64,11 @@ fn test_variadic_only_with_no_vargs() {
fn_variadic_only_with_no_vargs() fn_variadic_only_with_no_vargs()
} }
fn test_array_decomposition_to_vargs() {
a := ['a', 'b', 'c']
assert variadic_fn_a(a...) == 'abc'
}
struct VaTestStruct { struct VaTestStruct {
} }

View File

@ -180,13 +180,13 @@ pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr byteptr, len int) ?int {
return res return res
} }
pub fn (mut s SSLConn) read_into(mut buffer []Byte) ?int { pub fn (mut s SSLConn) read_into(mut buffer []byte) ?int {
res := s.socket_read_into_ptr(byteptr(buffer.data), buffer.len)? res := s.socket_read_into_ptr(byteptr(buffer.data), buffer.len)?
return res return res
} }
// write number of bytes to SSL connection // write number of bytes to SSL connection
pub fn (mut s SSLConn) write(bytes []Byte) ? { pub fn (mut s SSLConn) write(bytes []byte) ? {
unsafe { unsafe {
mut ptr_base := byteptr(bytes.data) mut ptr_base := byteptr(bytes.data)
mut total_sent := 0 mut total_sent := 0