diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 4803055bab..26be1f1f70 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -121,7 +121,7 @@ pub: pub struct Return { pub: - expr Expr + exprs []Expr } /* diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 1879087f0e..55c7898441 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -4,6 +4,7 @@ import ( strings v.ast v.table + v.types term ) @@ -11,6 +12,8 @@ struct Gen { out strings.Builder definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) table &table.Table +mut: + fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 } pub fn cgen(files []ast.File, table &table.Table) string { @@ -19,6 +22,7 @@ pub fn cgen(files []ast.File, table &table.Table) string { out: strings.new_builder(100) definitions: strings.new_builder(100) table: table + fn_decl: 0 } for file in files { for stmt in file.stmts { @@ -45,6 +49,7 @@ fn (g mut Gen) stmt(node ast.Stmt) { match node { ast.Import {} ast.FnDecl { + g.fn_decl = &it is_main := it.name == 'main' if is_main { g.write('int ${it.name}(') @@ -53,8 +58,11 @@ fn (g mut Gen) stmt(node ast.Stmt) { g.write('$it.ti.name ${it.name}(') g.definitions.write('$it.ti.name ${it.name}(') } - for arg in it.args { + for i, arg in it.args { g.write(arg.ti.name + ' ' + arg.name) + if i < it.args.len - 1 { + g.write(', ') + } g.definitions.write(arg.ti.name + ' ' + arg.name) } g.writeln(') { ') @@ -68,10 +76,26 @@ fn (g mut Gen) stmt(node ast.Stmt) { g.writeln('return 0;') } g.writeln('}') + g.fn_decl = 0 } ast.Return { g.write('return ') - g.expr(it.expr) + // multiple returns + if it.exprs.len > 1 { + g.write('($g.fn_decl.ti.name){') + for i, expr in it.exprs { + g.write('.arg$i=') + g.expr(expr) + if i < it.exprs.len - 1 { + g.write(',') + } + } + g.write('}') + } + // normal return + else { + g.expr(it.exprs[0]) + } g.writeln(';') } ast.VarDecl { diff --git a/vlib/v/gen/jsgen.v b/vlib/v/gen/jsgen.v index e531b3e922..96dea1cbb6 100644 --- a/vlib/v/gen/jsgen.v +++ b/vlib/v/gen/jsgen.v @@ -46,7 +46,12 @@ fn (g mut JsGen) stmt(node ast.Stmt) { } ast.Return { g.write('return ') - g.expr(it.expr) + if it.exprs.len > 0 { + + } + else { + g.expr(it.exprs[0]) + } g.writeln(';') } ast.VarDecl { diff --git a/vlib/v/gen/tests/1.c b/vlib/v/gen/tests/1.c index 0d320bc0c6..cdd73e8154 100644 --- a/vlib/v/gen/tests/1.c +++ b/vlib/v/gen/tests/1.c @@ -2,6 +2,7 @@ void foo(int a); int get_int(string a); int get_int2(); void myuser(); +multi_return_int_string multi_return(); void variadic(variadic_int a); typedef struct { @@ -49,5 +50,9 @@ void myuser() { bool b2 = user.age > 0; } +multi_return_int_string multi_return() { +return (multi_return_int_string){.arg0=4,.arg1=tos3("four")}; +} + void variadic(variadic_int a) { } diff --git a/vlib/v/gen/tests/1.vv b/vlib/v/gen/tests/1.vv index b206d8d027..4fc4cea9de 100644 --- a/vlib/v/gen/tests/1.vv +++ b/vlib/v/gen/tests/1.vv @@ -51,5 +51,10 @@ fn myuser() { b2 := user.age > 0 } +fn multi_return() (int,string) { + return 4, 'four' +} + fn variadic(a ...int) { } + diff --git a/vlib/v/parser/a_type.v b/vlib/v/parser/a_type.v index d737d6ef40..71bc023f2d 100644 --- a/vlib/v/parser/a_type.v +++ b/vlib/v/parser/a_type.v @@ -1,5 +1,4 @@ module parser - // Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. @@ -40,10 +39,10 @@ pub fn (p mut Parser) parse_map_ti(nr_muls int) types.TypeIdent { pub fn (p mut Parser) parse_multi_return_ti() types.TypeIdent { p.check(.lpar) - mut mr_tis := []&types.TypeIdent + mut mr_tis := []types.TypeIdent for { mr_ti := p.parse_ti() - mr_tis << &mr_ti + mr_tis << mr_ti if p.tok.kind == .comma { p.check(.comma) } @@ -99,52 +98,52 @@ pub fn (p mut Parser) parse_ti() types.TypeIdent { return p.parse_map_ti(nr_muls) } 'voidptr' { - return types.new_base_ti(._voidptr, nr_muls) + return types.new_builtin_ti(._voidptr, nr_muls) } 'byteptr' { - return types.new_base_ti(._byteptr, nr_muls) + return types.new_builtin_ti(._byteptr, nr_muls) } 'charptr' { - return types.new_base_ti(._charptr, nr_muls) + return types.new_builtin_ti(._charptr, nr_muls) } 'i8' { - return types.new_base_ti(._i8, nr_muls) + return types.new_builtin_ti(._i8, nr_muls) } 'i16' { - return types.new_base_ti(._i16, nr_muls) + return types.new_builtin_ti(._i16, nr_muls) } 'int' { - return types.new_base_ti(._int, nr_muls) + return types.new_builtin_ti(._int, nr_muls) } 'i64' { - return types.new_base_ti(._i64, nr_muls) + return types.new_builtin_ti(._i64, nr_muls) } 'byte' { - return types.new_base_ti(._byte, nr_muls) + return types.new_builtin_ti(._byte, nr_muls) } 'u16' { - return types.new_base_ti(._u16, nr_muls) + return types.new_builtin_ti(._u16, nr_muls) } 'u32' { - return types.new_base_ti(._u32, nr_muls) + return types.new_builtin_ti(._u32, nr_muls) } 'u64' { - return types.new_base_ti(._u64, nr_muls) + return types.new_builtin_ti(._u64, nr_muls) } 'f32' { - return types.new_base_ti(._f32, nr_muls) + return types.new_builtin_ti(._f32, nr_muls) } 'f64' { - return types.new_base_ti(._f64, nr_muls) + return types.new_builtin_ti(._f64, nr_muls) } 'string' { - return types.new_base_ti(._string, nr_muls) + return types.new_builtin_ti(._string, nr_muls) } 'char' { - return types.new_base_ti(._char, nr_muls) + return types.new_builtin_ti(._char, nr_muls) } 'bool' { - return types.new_base_ti(._bool, nr_muls) + return types.new_builtin_ti(._bool, nr_muls) } // struct / enum / placeholder else { @@ -156,7 +155,7 @@ pub fn (p mut Parser) parse_ti() types.TypeIdent { } return types.new_ti(._placeholder, name, idx, nr_muls) } - } + } } } } diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index b7cb7da7b8..7a4de43766 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -120,6 +120,9 @@ fn (p mut Parser) fn_decl() ast.FnDecl { ti: ti name: arg_name } + if ti.kind == ._variadic && p.tok.kind == .comma { + p.error('cannot use ...(variadic) with non-final parameter $arg_name') + } } if p.tok.kind != .rpar { p.check(.comma) @@ -128,7 +131,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl { p.check(.rpar) // Return type mut ti := types.void_ti - if p.tok.kind == .name { + if p.tok.kind in [.name, .lpar] { ti = p.parse_ti() p.return_ti = ti } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index a35f092132..e2c25d9ad2 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -630,19 +630,19 @@ fn (p mut Parser) array_init() (ast.Expr,types.TypeIdent) { fn (p mut Parser) parse_number_literal() (ast.Expr,types.TypeIdent) { lit := p.tok.lit mut node := ast.Expr{} - mut ti := types.new_base_ti(._int, 0) + mut ti := types.int_ti if lit.contains('.') { node = ast.FloatLiteral{ // val: lit.f64() val: lit } - ti = types.new_base_ti(._f64, 0) + ti = types.new_builtin_ti(._f64, 0) } else { node = ast.IntegerLiteral{ val: lit.int() } - // ti = types.new_base_ti(._int, 0) + // ti = types.int_ti } p.next() return node,ti @@ -709,12 +709,37 @@ fn (p mut Parser) struct_decl() ast.StructDecl { fn (p mut Parser) return_stmt() ast.Return { p.next() - expr,t := p.expr(0) - if !types.check(p.return_ti, t) { - p.warn('cannot use `$t.name` as type `$p.return_ti.name` in return argument') + // return expressions + mut exprs := []ast.Expr + // return type idents + mut got_tis := []types.TypeIdent + for { + expr,ti := p.expr(0) + exprs << expr + got_tis << ti + if p.tok.kind == .comma { + p.check(.comma) + } + else { + break + } + } + mut expected_tis := [p.return_ti] + if p.return_ti.kind == ._multi_return { + mr_type := p.table.types[p.return_ti.idx] as types.MultiReturn + expected_tis = mr_type.tis + } + if expected_tis.len != got_tis.len { + p.error('wrong number of return arguments:\n\texpected: $expected_tis.str()\n\tgot: $got_tis.str()') + } + for i, exp_ti in expected_tis { + got_ti := got_tis[i] + if !types.check(exp_ti, got_ti) { + p.error('cannot use `$got_ti.name` as type `$exp_ti.name` in return argument') + } } return ast.Return{ - expr: expr + exprs: exprs } } diff --git a/vlib/v/table/type.v b/vlib/v/table/type.v index 2bcca9d0a5..2a0c81a802 100644 --- a/vlib/v/table/type.v +++ b/vlib/v/table/type.v @@ -20,7 +20,6 @@ pub fn (t &Table) find_type(name string) ?types.Type { } pub fn (t mut Table) register_struct(typ types.Struct) int { - mut struct_type := types.Type{} // existing existing_idx := t.type_idxs[typ.name] if existing_idx > 0 { @@ -29,6 +28,7 @@ pub fn (t mut Table) register_struct(typ types.Struct) int { types.Placeholder { // override placeholder println('overriding type placeholder `$it.name` with struct') + mut struct_type := types.Type{} struct_type = { typ | idx:existing_idx @@ -42,12 +42,13 @@ pub fn (t mut Table) register_struct(typ types.Struct) int { else { panic('cannot register type `$typ.name`, another type with this name exists') } - } + } } // register println('registering: $typ.name') idx := t.types.len t.type_idxs[typ.name] = idx + mut struct_type := types.Type{} struct_type = { typ | idx:idx @@ -121,14 +122,10 @@ pub fn (t mut Table) find_or_register_array_fixed(elem_ti &types.TypeIdent, size return idx,name } -pub fn (t mut Table) find_or_register_multi_return(mr_tis []&types.TypeIdent) (int,string) { +pub fn (t mut Table) find_or_register_multi_return(mr_tis []types.TypeIdent) (int,string) { mut name := 'multi_return' - mut mr_type_kinds := []types.Kind - mut mr_type_idxs := []int for mr_ti in mr_tis { name += '_$mr_ti.name' - mr_type_kinds << mr_ti.kind - mr_type_idxs << mr_ti.idx } // existing existing_idx := t.type_idxs[name] @@ -141,8 +138,7 @@ pub fn (t mut Table) find_or_register_multi_return(mr_tis []&types.TypeIdent) (i mr_type = types.MultiReturn{ idx: idx name: name - type_kinds: mr_type_kinds - type_idxs: mr_type_idxs + tis: mr_tis } t.type_idxs[name] = idx t.types << mr_type @@ -161,8 +157,7 @@ pub fn (t mut Table) find_or_register_variadic(variadic_ti &types.TypeIdent) (in mut variadic_type := types.Type{} variadic_type = types.Variadic{ idx: idx - type_kind: variadic_ti.kind - type_idx: variadic_ti.idx + ti: variadic_ti } t.type_idxs[name] = idx t.types << variadic_type diff --git a/vlib/v/types/types.v b/vlib/v/types/types.v index ec4ba9a597..8308168a55 100644 --- a/vlib/v/types/types.v +++ b/vlib/v/types/types.v @@ -54,8 +54,9 @@ pub fn new_ti(kind Kind, name string, idx int, nr_muls int) TypeIdent { } [inline] -pub fn new_base_ti(kind Kind, nr_muls int) TypeIdent { +pub fn new_builtin_ti(kind Kind, nr_muls int) TypeIdent { return TypeIdent{ + idx: -int(kind) kind: kind name: kind.str() nr_muls: nr_muls @@ -83,7 +84,11 @@ pub fn (ti &TypeIdent) is_number() bool { } pub fn (ti &TypeIdent) str() string { - return '$ti.kind.str() $ti.idx: $ti.name ($ti.nr_muls)' + mut muls := '' + for _ in 0 .. ti.nr_muls { + muls += '&' + } + return '$muls$ti.name' } pub fn check(got, expected &TypeIdent) bool { @@ -275,17 +280,15 @@ pub: pub struct MultiReturn { pub: - idx int - name string - type_kinds []Kind - type_idxs []int + idx int + name string + tis []TypeIdent } pub struct Variadic { pub: - idx int - type_kind Kind - type_idx int + idx int + ti TypeIdent } pub fn (t Void) str() string { @@ -353,7 +356,7 @@ pub fn (t MultiReturn) str() string { } pub fn (t Variadic) str() string { - return 'variadic_$t.type_kind.str()' + return 'variadic_$t.ti.kind.str()' } pub const ( @@ -387,8 +390,8 @@ pub const ( ) pub const ( - void_ti = new_base_ti(._void, 0) - int_ti = new_base_ti(._int, 0) - string_ti = new_base_ti(._string, 0) - bool_ti = new_base_ti(._bool, 0) + void_ti = new_builtin_ti(._void, 0) + int_ti = new_builtin_ti(._int, 0) + string_ti = new_builtin_ti(._string, 0) + bool_ti = new_builtin_ti(._bool, 0) )