From 02965e753ea3348b295ddc78cfc368066d1196c2 Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Wed, 30 Dec 2020 02:14:08 +1100 Subject: [PATCH] all: re-implement variadics using arrays & implement array decomposition to varg (#7689) --- vlib/v/ast/ast.v | 18 ++- vlib/v/checker/check_types.v | 14 ++ vlib/v/checker/checker.v | 33 ++-- vlib/v/checker/tests/fn_args.out | 6 +- .../checker/tests/function_wrong_arg_type.out | 2 +- vlib/v/checker/tests/is_type_not_exist.out | 2 +- .../v/checker/tests/method_wrong_arg_type.out | 2 +- .../tests/non_matching_functional_args.out | 4 +- vlib/v/fmt/fmt.v | 3 + vlib/v/gen/auto_str_methods.v | 25 --- vlib/v/gen/cgen.v | 41 +---- vlib/v/gen/cheaders.v | 2 - vlib/v/gen/fn.v | 49 +++--- vlib/v/gen/js/js.v | 152 +++++++++++------- vlib/v/gen/str.v | 2 - vlib/v/parser/fn.v | 11 +- vlib/v/table/table.v | 4 +- vlib/v/table/types.v | 10 +- vlib/v/tests/fn_variadic_test.v | 9 +- vlib/x/openssl/openssl.v | 4 +- 20 files changed, 210 insertions(+), 183 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index c7811362a2..bdecbdfc5a 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -9,9 +9,9 @@ import v.errors pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl -pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral | CTempVar | - CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | - FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | +pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral | + CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | + EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type | TypeOf | UnsafeExpr @@ -890,6 +890,15 @@ pub mut: 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: 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 { return expr.pos } + ArrayDecompose { + return expr.pos + } IfGuardExpr { return expr.expr.position() } diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 5d686cb468..bab606b2ac 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -7,6 +7,20 @@ import v.table import v.token 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 { if got == expected { return true diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 6184af75e3..87f7afb20b 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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()) continue } - if !c.check_types(got_arg_typ, exp_arg_typ) { - got_arg_sym := c.table.get_type_symbol(got_arg_typ) + c.check_expected_call_arg(got_arg_typ, exp_arg_typ) or { // 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 // } - // 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 { - c.error('cannot use type `$got_arg_sym.name` as type `$exp_arg_sym.name` in argument ${i + - 1} to `${left_type_sym.name}.$method_name`', call_expr.pos) + c.error('$err in argument ${i + 1} to `${left_type_sym.name}.$method_name`', + call_expr.pos) } } 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()) 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 // Passing an int or a string array produces a c error here // 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 { 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 { @@ -2981,6 +2976,18 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { c.cur_fn = keep_fn 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 { return c.array_init(mut node) } diff --git a/vlib/v/checker/tests/fn_args.out b/vlib/v/checker/tests/fn_args.out index 904eb3483f..563aae8251 100644 --- a/vlib/v/checker/tests/fn_args.out +++ b/vlib/v/checker/tests/fn_args.out @@ -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 | 5 | v := 4 6 | ptr(&v) | ~~ 7 | arr([5]!!) 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 6 | ptr(&v) 7 | arr([5]!!) | ~~~~~ 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) 7 | arr([5]!!) 8 | fun(fn(i &int){}) diff --git a/vlib/v/checker/tests/function_wrong_arg_type.out b/vlib/v/checker/tests/function_wrong_arg_type.out index 2e8af639cc..07e4fc856c 100644 --- a/vlib/v/checker/tests/function_wrong_arg_type.out +++ b/vlib/v/checker/tests/function_wrong_arg_type.out @@ -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() { 6 | a := 12.3 7 | q := f(a) diff --git a/vlib/v/checker/tests/is_type_not_exist.out b/vlib/v/checker/tests/is_type_not_exist.out index 84c7c0c2ec..e597f95c7a 100644 --- a/vlib/v/checker/tests/is_type_not_exist.out +++ b/vlib/v/checker/tests/is_type_not_exist.out @@ -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 | 3 | fn main() { 4 | fn_with_sum_type_param(1) diff --git a/vlib/v/checker/tests/method_wrong_arg_type.out b/vlib/v/checker/tests/method_wrong_arg_type.out index bd42ed3630..6d04f558fb 100644 --- a/vlib/v/checker/tests/method_wrong_arg_type.out +++ b/vlib/v/checker/tests/method_wrong_arg_type.out @@ -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 7 | s := Sss{} 8 | s.info(e) diff --git a/vlib/v/checker/tests/non_matching_functional_args.out b/vlib/v/checker/tests/non_matching_functional_args.out index 2247b78129..a89de0059b 100644 --- a/vlib/v/checker/tests/non_matching_functional_args.out +++ b/vlib/v/checker/tests/non_matching_functional_args.out @@ -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 | 26 | fn main() { 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() 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 -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) 30 | }) 31 | sum(xxx) diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 6b147ffa58..0abcf8cd2c 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1138,6 +1138,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) { } f.write('}') } + ast.ArrayDecompose { + f.expr(node.expr) + } } } diff --git a/vlib/v/gen/auto_str_methods.v b/vlib/v/gen/auto_str_methods.v index d7cf3f76c0..cec340dd22 100644 --- a/vlib/v/gen/auto_str_methods.v +++ b/vlib/v/gen/auto_str_methods.v @@ -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) { option_already_generated_key := 'option_$already_generated_key' 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('}') } -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 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 { - if gen_vargs && i == expected_types.len - 1 { + if is_variadic && i == expected_types.len - 1 { break } 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 { g.write(')') } - if i < args.len - 1 || gen_vargs { + if i < args.len - 1 || is_variadic { g.write(', ') } } arg_nr := expected_types.len - 1 - if gen_vargs { + if is_variadic { varg_type := expected_types[expected_types.len - 1] - struct_name := 'varg_' + g.typ(varg_type).replace('*', '_ptr') variadic_count := args.len - arg_nr - varg_type_str := int(varg_type).str() - if variadic_count > g.variadic_args[varg_type_str] { - g.variadic_args[varg_type_str] = variadic_count - } - g.write('($struct_name){.len=$variadic_count,.args={') - 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(', ') - } - } + arr_sym := g.table.get_type_symbol(varg_type) + arr_info := arr_sym.info as table.Array + elem_type := g.typ(arr_info.elem_type) + if args.len > 0 && args[args.len - 1].expr is ast.ArrayDecompose { + g.expr(args[args.len - 1].expr) } else { - // NB: tcc can not handle 0 here, while msvc needs it - g.write('EMPTY_VARG_INITIALIZATION') + if variadic_count > 0 { + 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('}}') } } diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 7b8fcf23d6..924eb147d2 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -16,7 +16,8 @@ const ( 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield', 'Number', 'String', 'Boolean', 'Array', 'Map'] // 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', '\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 { - table &table.Table - pref &pref.Preferences + table &table.Table + pref &pref.Preferences mut: - definitions strings.Builder - ns &Namespace - namespaces map[string]&Namespace - doc &JsDoc - enable_doc bool - file ast.File - tmp_count int - inside_ternary bool - inside_loop bool - inside_map_set bool // map.set(key, value) - inside_builtin bool - generated_builtin bool + definitions strings.Builder + ns &Namespace + namespaces map[string]&Namespace + doc &JsDoc + enable_doc bool + file ast.File + tmp_count int + inside_ternary bool + inside_loop bool + inside_map_set bool // map.set(key, value) + inside_builtin bool + generated_builtin bool inside_def_typ_decl bool - is_test bool - stmt_start_pos int - defer_stmts []ast.DeferStmt - fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 - str_types []string // types that need automatic str() generation - method_fn_decls map[string][]ast.FnDecl - builtin_fns []string // Functions defined in `builtin` - empty_line bool + is_test bool + stmt_start_pos int + defer_stmts []ast.DeferStmt + fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 + str_types []string // types that need automatic str() generation + method_fn_decls map[string][]ast.FnDecl + builtin_fns []string // Functions defined in `builtin` + empty_line bool } 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 += 'const [' for i, typ in v_types { - if i > 0 { out += ', ' } + if i > 0 { + out += ', ' + } out += '$typ' } out += '] = [' 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]\n' @@ -257,14 +262,18 @@ pub fn (mut g JsGen) dec_indent() { [inline] 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.ns.out.write(s) } [inline] 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.ns.out.writeln(s) g.empty_line = true @@ -304,7 +313,13 @@ fn (mut g JsGen) js_name(name_ string) string { is_js = true } 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('.') if !is_js { for i, p in parts { @@ -529,8 +544,10 @@ fn (mut g JsGen) expr(node ast.Expr) { g.gen_string_inter_literal(node) } ast.StringLiteral { - text := node.val.replace('\'', "\\'") - if g.file.mod.name == 'builtin' { g.write('new ') } + text := node.val.replace("\'", "\\'") + if g.file.mod.name == 'builtin' { + g.write('new ') + } g.write("string('$text')") } ast.StructInit { @@ -559,6 +576,7 @@ fn (mut g JsGen) expr(node ast.Expr) { ast.UnsafeExpr { 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) { g.writeln('toString() {') g.inc_indent() - g.write('return `${js_name} {') + g.write('return `$js_name {') 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() { - "string" { g.write('$field.name: "\${this["${field.name}"].toString()}"') } - else { 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()} ') } } } g.writeln('}`') g.dec_indent() g.writeln('}') } - g.dec_indent() g.writeln('};\n') 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 { l := int(left) r := int(right) - lr := [l,r] - - if table.string_type_idx in lr { 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) + lr := [l, r] + if table.string_type_idx in lr { + 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) if should_float { - if table.f64_type_idx in lr { return table.Type(table.f64_type_idx) } - if table.f32_type_idx in lr { return table.Type(table.f32_type_idx) } + if table.f64_type_idx in lr { + 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) } - should_int := (l in table.integer_type_idxs && r in table.integer_type_idxs) if should_int { // cant add to u64 - if (table.u64_type_idx in lr) { return table.Type(table.u64_type_idx) } // just guessing this order - if table.i64_type_idx in lr { return table.Type(table.i64_type_idx) } - if table.u32_type_idx in lr { return table.Type(table.u32_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) } + if table.i64_type_idx in lr { + return table.Type(table.i64_type_idx) + } + if table.u32_type_idx in lr { + return table.Type(table.u32_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(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) { - if g.file.mod.name == 'builtin' { g.write('new ') } + if g.file.mod.name == 'builtin' { + g.write('new ') + } g.write('string(`') for i, val in it.vals { 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) { - is_literal := ( - (it.expr is ast.IntegerLiteral && it.typ in table.integer_type_idxs) || - (it.expr is ast.FloatLiteral && it.typ in table.float_type_idxs) - ) + is_literal := ((it.expr is ast.IntegerLiteral && + it.typ in table.integer_type_idxs) || + (it.expr is ast.FloatLiteral && it.typ in table.float_type_idxs)) typ := g.typ(it.typ) if !is_literal { if !(typ in v_types) || g.ns.name == 'builtin' { diff --git a/vlib/v/gen/str.v b/vlib/v/gen/str.v index c0c3abc938..e39c971948 100644 --- a/vlib/v/gen/str.v +++ b/vlib/v/gen/str.v @@ -224,8 +224,6 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { } else { g.write('*.*s') } - } else if typ.has_flag(.variadic) { - g.write('.*s') } else if typ.is_float() { g.write('$fmt${fspec:c}') } else if typ.is_pointer() { diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 23f813bfdb..c18ec96fb6 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -126,6 +126,13 @@ pub fn (mut p Parser) call_args() []ast.CallArg { mut comments := p.eat_comments() arg_start_pos := p.tok.position() 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 { e.pre_comments << comments comments = []ast.Comment{} @@ -533,7 +540,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) { } } 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 { 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 { - 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 { args << table.Param{ diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index e681ccd3eb..ebb06c5bf6 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -641,7 +641,9 @@ pub fn (t &Table) value_type(typ Type) Type { typ_sym := t.get_type_symbol(typ) if typ.has_flag(.variadic) { // ...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 { // Check index type diff --git a/vlib/v/table/types.v b/vlib/v/table/types.v index 74a8173c73..ae5675e1f4 100644 --- a/vlib/v/table/types.v +++ b/vlib/v/table/types.v @@ -718,9 +718,13 @@ pub fn (table &Table) type_to_str_using_aliases(t Type, import_aliases map[strin if t == array_type { return 'array' } - info := sym.info as Array - elem_str := table.type_to_str_using_aliases(info.elem_type, import_aliases) - res = '[]$elem_str' + if t.has_flag(.variadic) { + res = table.type_to_str_using_aliases(table.value_type(t), import_aliases) + } else { + info := sym.info as Array + elem_str := table.type_to_str_using_aliases(info.elem_type, import_aliases) + res = '[]$elem_str' + } } .array_fixed { info := sym.info as ArrayFixed diff --git a/vlib/v/tests/fn_variadic_test.v b/vlib/v/tests/fn_variadic_test.v index 58355a31aa..391284a070 100644 --- a/vlib/v/tests/fn_variadic_test.v +++ b/vlib/v/tests/fn_variadic_test.v @@ -34,10 +34,10 @@ fn test_fn_variadic_generic() { */ // forwarding 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] a1 := a[1] a2 := a[2] @@ -64,6 +64,11 @@ fn test_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 { } diff --git a/vlib/x/openssl/openssl.v b/vlib/x/openssl/openssl.v index 3fa63aae1c..fcd693593e 100644 --- a/vlib/x/openssl/openssl.v +++ b/vlib/x/openssl/openssl.v @@ -180,13 +180,13 @@ pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr byteptr, len int) ?int { 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)? return res } // write number of bytes to SSL connection -pub fn (mut s SSLConn) write(bytes []Byte) ? { +pub fn (mut s SSLConn) write(bytes []byte) ? { unsafe { mut ptr_base := byteptr(bytes.data) mut total_sent := 0