From 71ca55319048389fdf55fcc48ee267e92775bc95 Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Tue, 31 Mar 2020 15:34:59 +1100 Subject: [PATCH] cgen: multi return/assign optionals --- vlib/v/checker/checker.v | 33 +++--- vlib/v/gen/cgen.v | 227 +++++++++++++++++++++++---------------- vlib/v/gen/tests/1.c | 3 + vlib/v/gen/tests/2.c | 68 ++++++++++++ vlib/v/gen/tests/3.c | 14 ++- vlib/v/gen/tests/4.c | 47 +++++++- vlib/v/gen/tests/4.vv | 18 ++++ vlib/v/parser/parser.v | 3 +- 8 files changed, 298 insertions(+), 115 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index ea496e3381..be99e20a73 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -423,20 +423,23 @@ pub fn (c mut Checker) assign_stmt(assign_stmt mut ast.AssignStmt) { c.expected_type = table.none_type // TODO a hack to make `x := if ... work` // multi return if assign_stmt.left.len > assign_stmt.right.len { - right := c.expr(assign_stmt.right[0]) - right_sym := c.table.get_type_symbol(right) - mr_info := right_sym.mr_info() - if right_sym.kind != .multi_return { + match assign_stmt.right[0] { + ast.CallExpr {} + else { + c.error('assign_stmt: expected call', assign_stmt.pos) + } + } + right_type := c.expr(assign_stmt.right[0]) + right_type_sym := c.table.get_type_symbol(right_type) + mr_info := right_type_sym.mr_info() + if right_type_sym.kind != .multi_return { c.error('wrong number of vars', assign_stmt.pos) } mut scope := c.file.scope.innermost(assign_stmt.pos.pos) for i, _ in assign_stmt.left { mut ident := assign_stmt.left[i] + mut ident_var_info := ident.var_info() val_type := mr_info.types[i] - mut var_info := ident.var_info() - var_info.typ = val_type - ident.info = var_info - assign_stmt.left[i] = ident if assign_stmt.op == .assign { var_type := c.expr(ident) assign_stmt.left_types << var_type @@ -446,8 +449,11 @@ pub fn (c mut Checker) assign_stmt(assign_stmt mut ast.AssignStmt) { c.error('assign stmt: cannot use `$val_type_sym.name` as `$var_type_sym.name`', assign_stmt.pos) } } + ident_var_info.typ = val_type + ident.info = ident_var_info + assign_stmt.left[i] = ident assign_stmt.right_types << val_type - scope.update_var_type(ident.name, mr_info.types[i]) + scope.update_var_type(ident.name, val_type) } } // `a := 1` | `a,b := 1,2` @@ -469,10 +475,10 @@ pub fn (c mut Checker) assign_stmt(assign_stmt mut ast.AssignStmt) { c.error('assign stmt: cannot use `$val_type_sym.name` as `$var_type_sym.name`', assign_stmt.pos) } } - assign_stmt.right_types << val_type ident_var_info.typ = val_type ident.info = ident_var_info assign_stmt.left[i] = ident + assign_stmt.right_types << val_type scope.update_var_type(ident.name, val_type) } } @@ -538,7 +544,6 @@ fn (c mut Checker) stmt(node ast.Stmt) { } ast.AssignStmt { c.assign_stmt(mut it) - c.expected_type = table.void_type } ast.Block { c.stmts(it.stmts) @@ -946,11 +951,11 @@ pub fn (c mut Checker) if_expr(node mut ast.IfExpr) table.Type { node.typ = table.void_type for i, branch in node.branches { match branch.cond { - ast.ParExpr{ - c.error('unnecessary `()` in an if condition. use `if expr {` instead of `if (expr) {`.', node.pos) + ast.ParExpr { + c.error('unnecessary `()` in an if condition. use `if expr {` instead of `if (expr) {`.', node.pos) } else {} - } + } typ := c.expr(branch.cond) if i < node.branches.len - 1 || !node.has_else { typ_sym := c.table.get_type_symbol(typ) diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 4ea70d113a..38ce57e08f 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -32,6 +32,7 @@ mut: tmp_count int variadic_args map[string]int is_c_call bool // e.g. `C.printf("v")` + is_assign bool // inside right part of assign after `=` (val expr) is_assign_expr bool // inside left part of assign expr (for array_set(), etc) is_array_set bool is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc @@ -43,7 +44,6 @@ mut: indent int empty_line bool is_test bool - expr_var_name string assign_op token.Kind // *=, =, etc (for array_set) defer_stmts []ast.DeferStmt defer_ifdef string @@ -122,7 +122,7 @@ pub fn (g mut Gen) write_typeof_functions() { for typ in g.table.types { if typ.kind == .sum_type { sum_info := typ.info as table.SumType - tidx := g.table.find_type_idx( typ.name ) + tidx := g.table.find_type_idx(typ.name) g.writeln('char * v_typeof_sumtype_${tidx}(int sidx) { /* ${typ.name} */ ') g.writeln(' switch(sidx) {') g.writeln(' case $tidx: return "$typ.name";') @@ -163,12 +163,6 @@ pub fn (g mut Gen) typ(t table.Type) string { return styp } -/* -pub fn (g &Gen) styp(t string) string { - return t.replace('.', '__') -} -*/ - // pub fn (g mut Gen) write_typedef_types() { for typ in g.table.types { @@ -213,7 +207,7 @@ pub fn (g mut Gen) write_typedef_types() { else { continue } - } + } } } @@ -578,28 +572,24 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { // g.write('/*assign_stmt*/') if assign_stmt.left.len > assign_stmt.right.len { // multi return + mut or_stmts := []ast.Stmt mut return_type := table.void_type match assign_stmt.right[0] { ast.CallExpr { + or_stmts = it.or_block.stmts return_type = it.return_type } - else { - panic('expected call') - } + else {} } + is_optional := table.type_is_optional(return_type) mr_var_name := 'mr_$assign_stmt.pos.pos' - g.expr_var_name = mr_var_name - if table.type_is_optional(return_type) { - return_type = table.type_clear_extra(return_type) - mr_styp := g.typ(return_type) - g.write('$mr_styp $mr_var_name = (*(${mr_styp}*)') - g.expr(assign_stmt.right[0]) - g.write('.data)') - } - else { - mr_styp := g.typ(return_type) - g.write('$mr_styp $mr_var_name = ') - g.expr(assign_stmt.right[0]) + mr_styp := g.typ(return_type) + g.write('$mr_styp $mr_var_name = ') + g.is_assign = true + g.expr(assign_stmt.right[0]) + g.is_assign = false + if is_optional { + g.or_block(mr_var_name, or_stmts, return_type) } g.writeln(';') for i, ident in assign_stmt.left { @@ -612,7 +602,13 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.write('$styp ') } g.expr(ident) - g.writeln(' = ${mr_var_name}.arg$i;') + if is_optional { + mr_styp2 := mr_styp[7..] // remove Option_ + g.writeln(' = (*(${mr_styp2}*)${mr_var_name}.data).arg$i;') + } + else { + g.writeln(' = ${mr_var_name}.arg$i;') + } } } // `a := 1` | `a,b := 1,2` @@ -621,8 +617,21 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { val := assign_stmt.right[i] ident_var_info := ident.var_info() styp := g.typ(ident_var_info.typ) + mut is_call := false + mut or_stmts := []ast.Stmt + mut return_type := table.void_type + match val { + ast.CallExpr { + is_call = true + or_stmts = it.or_block.stmts + return_type = it.return_type + } + else {} + } + gen_or := is_call && table.type_is_optional(return_type) + g.is_assign = true if ident.kind == .blank_ident { - if ast.expr_is_call(val) { + if is_call { g.expr(val) } else { @@ -648,7 +657,6 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { if is_decl { g.write('$styp ') } - g.expr_var_name = ident.name g.expr(ident) if g.autofree && right_sym.kind == .array && is_ident { // `arr1 = arr2` => `arr1 = arr2.clone()` @@ -674,11 +682,14 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { else if is_fixed_array_init { g.write('= {0}') } + if gen_or { + g.or_block(ident.name, or_stmts, return_type) + } } + g.is_assign = false g.writeln(';') } } - g.expr_var_name = '' } fn (g mut Gen) gen_fn_decl(it ast.FnDecl) { @@ -778,8 +789,8 @@ fn (g mut Gen) free_scope_vars(pos int) { // println(var.name) // println(var.typ) // if var.typ == 0 { - // // TODO why 0? - // continue + // // TODO why 0? + // continue // } sym := g.table.get_type_symbol(var.typ) if sym.kind == .array && !table.type_is_optional(var.typ) { @@ -794,9 +805,12 @@ fn (g mut Gen) free_scope_vars(pos int) { continue } else { + // NOTE/TODO: assign_stmt multi returns variables have no expr + // since the type comes from the called fns return type g.writeln('// other ' + t) + continue } - } + } g.writeln('string_free($var.name); // autofreed') } } @@ -888,49 +902,7 @@ fn (g mut Gen) expr(node ast.Expr) { } } ast.AssignExpr { - // g.write('/*assign_expr*/') - if ast.expr_is_blank_ident(it.left) { - if ast.expr_is_call(it.val) { - g.expr(it.val) - } - else { - g.write('{${g.typ(it.left_type)} _ = ') - g.expr(it.val) - g.writeln(';}') - } - } - else { - g.is_assign_expr = true - if table.type_is_optional(it.right_type) { - g.right_is_opt = true - } - mut str_add := false - if it.left_type == table.string_type_idx && it.op == .plus_assign { - // str += str2 => `str = string_add(str, str2)` - g.expr(it.left) - g.write(' = string_add(') - str_add = true - } - g.assign_op = it.op - g.expr(it.left) - // arr[i] = val => `array_set(arr, i, val)`, not `array_get(arr, i) = val` - if !g.is_array_set && !str_add { - g.write(' $it.op.str() ') - } - else if str_add { - g.write(', ') - } - g.is_assign_expr = false - g.expr_with_cast(it.val, it.right_type, it.left_type) - if g.is_array_set { - g.write(' })') - g.is_array_set = false - } - else if str_add { - g.write(')') - } - g.right_is_opt = false - } + g.assign_expr(it) } ast.Assoc { g.assoc(it) @@ -1139,15 +1111,84 @@ fn (g mut Gen) typeof_expr(node ast.TypeOf) { if sym.kind == .sum_type { // When encountering a .sum_type, typeof() should be done at runtime, // because the subtype of the expression may change: - sum_type_idx := table.type_idx( node.expr_type ) + sum_type_idx := table.type_idx(node.expr_type) g.write('tos3( /* ${sym.name} */ v_typeof_sumtype_${sum_type_idx}( (') g.expr(node.expr) g.write(').typ ))') - }else{ + } + else { g.write('tos3("${sym.name}")') } } +fn (g mut Gen) assign_expr(node ast.AssignExpr) { + // g.write('/*assign_expr*/') + mut is_call := false + mut or_stmts := []ast.Stmt + mut return_type := table.void_type + match node.val { + ast.CallExpr { + is_call = true + or_stmts = it.or_block.stmts + return_type = it.return_type + } + else {} + } + gen_or := is_call && table.type_is_optional(return_type) + tmp_opt := if gen_or { g.new_tmp_var() } else { '' } + if gen_or { + rstyp := g.typ(return_type) + g.write('$rstyp $tmp_opt =') + } + g.is_assign = true + if ast.expr_is_blank_ident(node.left) { + if is_call { + g.expr(node.val) + } + else { + g.write('{${g.typ(node.left_type)} _ = ') + g.expr(node.val) + g.writeln(';}') + } + } + else { + g.is_assign_expr = true + if table.type_is_optional(node.right_type) { + g.right_is_opt = true + } + mut str_add := false + if node.left_type == table.string_type_idx && node.op == .plus_assign { + // str += str2 => `str = string_add(str, str2)` + g.expr(node.left) + g.write(' = string_add(') + str_add = true + } + g.assign_op = node.op + g.expr(node.left) + // arr[i] = val => `array_set(arr, i, val)`, not `array_get(arr, i) = val` + if !g.is_array_set && !str_add { + g.write(' $node.op.str() ') + } + else if str_add { + g.write(', ') + } + g.is_assign_expr = false + g.expr_with_cast(node.val, node.right_type, node.left_type) + if g.is_array_set { + g.write(' })') + g.is_array_set = false + } + else if str_add { + g.write(')') + } + g.right_is_opt = false + } + if gen_or { + g.or_block(tmp_opt, or_stmts, return_type) + } + g.is_assign = false +} + fn (g mut Gen) infix_expr(node ast.InfixExpr) { // println('infix_expr() op="$node.op.str()" line_nr=$node.pos.line_nr') // g.write('/*infix*/') @@ -1638,7 +1679,7 @@ fn (g mut Gen) return_statement(node ast.Return) { } g.write('}') if fn_return_is_optional { - g.writeln(' }, sizeof($styp));') + g.write(' }, sizeof($styp))') } } // normal return @@ -2146,6 +2187,12 @@ fn (g mut Gen) insert_before(s string) { } fn (g mut Gen) call_expr(node ast.CallExpr) { + gen_or := !g.is_assign && node.or_block.stmts.len > 0 + tmp_opt := if gen_or { g.new_tmp_var() } else { '' } + if gen_or { + styp := g.typ(node.return_type) + g.write('$styp $tmp_opt = ') + } if node.is_method { // TODO: there are still due to unchecked exprs (opt/some fn arg) if node.left_type == 0 { @@ -2206,9 +2253,6 @@ fn (g mut Gen) call_expr(node ast.CallExpr) { // /////// g.call_args(node.args, node.exp_arg_types) g.write(')') - if node.or_block.stmts.len > 0 { - g.or_block(node.or_block.stmts, node.return_type) - } } else { mut name := node.name @@ -2267,29 +2311,24 @@ fn (g mut Gen) call_expr(node ast.CallExpr) { g.call_args(node.args, node.exp_arg_types) g.write(')') } - if node.or_block.stmts.len > 0 { - g.or_block(node.or_block.stmts, node.return_type) - } g.is_c_call = false } + if gen_or { + g.or_block(tmp_opt, node.or_block.stmts, node.return_type) + } } -fn (g mut Gen) or_block(stmts []ast.Stmt, return_type table.Type) { - // `foo() or { return }` - var_name := if g.expr_var_name != '' { g.expr_var_name } else { g.new_tmp_var() } - if g.expr_var_name == '' { - // The user is not using the optional return value. We need to use a temp var - // to access its fields (`.ok`, `.error` etc) - // `os.cp(...)` => `Option bool tmp = os__cp(...); if (!tmp.ok) { ... }` - styp := g.typ(return_type) - g.insert_before('$styp $var_name = ') - } +// If user is accessing the return value eg. in assigment, pass the variable name. +// If the user is not using the optional return value. We need to pass a temp var +// to access its fields (`.ok`, `.error` etc) +// `os.cp(...)` => `Option bool tmp = os__cp(...); if (!tmp.ok) { ... }` +fn (g mut Gen) or_block(var_name string, stmts []ast.Stmt, return_type table.Type) { g.writeln(';') // or') g.writeln('if (!${var_name}.ok) {') g.writeln('string err = ${var_name}.v_error;') g.writeln('int errcode = ${var_name}.ecode;') g.stmts(stmts) - g.writeln('}') + g.write('}') } // `a in [1,2,3]` => `a == 1 || a == 2 || a == 3` diff --git a/vlib/v/gen/tests/1.c b/vlib/v/gen/tests/1.c index 24c1f43e27..d1b36b1f63 100644 --- a/vlib/v/gen/tests/1.c +++ b/vlib/v/gen/tests/1.c @@ -54,6 +54,9 @@ struct varg_int { int args[0]; }; +// >> typeof() support for sum types +// << typeof() support for sum types + // int main(int argc, char** argv) { _vinit(); diff --git a/vlib/v/gen/tests/2.c b/vlib/v/gen/tests/2.c index bf1a3f1abc..bebf4b8ffd 100644 --- a/vlib/v/gen/tests/2.c +++ b/vlib/v/gen/tests/2.c @@ -12,7 +12,75 @@ void puts(string s); void function2(); void init_array(); void end(); +// >> typeof() support for sum types +// << typeof() support for sum types +int function1() { + int a = 10 + 1; + int b = a + 1; + return 0; +} +void foo(int a) { +} +void init_user() { + User user = (User){ + .name = tos3("Bob"), + }; +} +User get_user() { + User user = (User){ + .name = tos3(""), + }; + return user; +} + +void puts(string s) { +} + +void function2() { + int x = 0; + f64 f = 10.1; + string s = tos3("hi"); + int m = 10; + x += 10; + x += 1; + m += 2; + function1(); + if (true) { + foo(10); + x += 8; + } + if (false) { + foo(1); + } else { + puts(tos3("else")); + foo(100); + } + while (true) { + init_user(); + } + bool e = 1 + 2 > 0; + bool e2 = 1 + 2 < 0; + int j = 0; +} + +void init_array() { + array_int nums = new_array_from_c_array(3, 3, sizeof(int), (int[3]){ + 4, 2, 3, + }); +} + +void end() { +} + +int main(int argc, char** argv) { + _vinit(); + return 0; +} + +void _vinit() { + +} diff --git a/vlib/v/gen/tests/3.c b/vlib/v/gen/tests/3.c index f385f865b3..00116bf186 100644 --- a/vlib/v/gen/tests/3.c +++ b/vlib/v/gen/tests/3.c @@ -22,6 +22,17 @@ Option_int get_opt(); void User_foo(User* u); void println(string s); void handle_expr(Expr e); +// >> typeof() support for sum types +char * v_typeof_sumtype_28(int sidx) { /* Expr */ + switch(sidx) { + case 28: return "Expr"; + case 26: return "IfExpr"; + case 27: return "IntegerLiteral"; + default: return "unknown Expr"; + } +} +// << typeof() support for sum types + // TypeDecl Option_int get_opt() { @@ -76,8 +87,7 @@ int main(int argc, char** argv) { string err = n.v_error; int errcode = n.ecode; return 0; - } - ; + }; int a = /*opt*/(*(int*)n.data) + 3; handle_expr(/* sum type cast */ (Expr) {.obj = memdup(&(IfExpr[]) {(IfExpr){ 0}}, sizeof(IfExpr)), .typ = 26 /* IfExpr */}); diff --git a/vlib/v/gen/tests/4.c b/vlib/v/gen/tests/4.c index c61f05df33..7b38e36b3e 100644 --- a/vlib/v/gen/tests/4.c +++ b/vlib/v/gen/tests/4.c @@ -14,6 +14,8 @@ typedef struct { } multi_return_int_string; // end of definitions #endif +typedef Option Option_string; +typedef Option Option_multi_return_int_string; multi_return_int_string mr_test(); int testa(); string testb(int a); @@ -21,6 +23,11 @@ int testc(int a); int Foo_testa(Foo* f); int Foo_testb(Foo* f); int Bar_testa(Bar* b); +Option_string optional_a(); +Option_string optional_b(); +Option_multi_return_int_string optional_mr(); +// >> typeof() support for sum types +// << typeof() support for sum types int main(int argc, char** argv) { _vinit(); @@ -57,9 +64,28 @@ int main(int argc, char** argv) { map_string_int m2 = new_map_init(2, sizeof(int), (string[2]){tos3("v"), tos3("lang"), }, (int[2]){1, 2, }); string ma1 = tos3("hello"); string ma2 = tos3("vlang"); - multi_return_int_string mr_578 = mr_test(); - int mr1 = mr_578.arg0; - string mr2 = mr_578.arg1; + multi_return_int_string mr_566 = mr_test(); + int mr1 = mr_566.arg0; + string mr2 = mr_566.arg1; + string opt1 = tos3("opt1"); + Option_string opt2 = optional_a(); + if (!opt2.ok) { + string err = opt2.v_error; + int errcode = opt2.ecode; + }; + string opt3 = tos3("opt3"); + Option_string opt4 = optional_b(); + if (!opt4.ok) { + string err = opt4.v_error; + int errcode = opt4.ecode; + }; + Option_multi_return_int_string mr_669 = optional_mr(); + if (!mr_669.ok) { + string err = mr_669.v_error; + int errcode = mr_669.ecode; + }; + int opt_mr1 = (*(multi_return_int_string*)mr_669.data).arg0; + string opt_mr12 = (*(multi_return_int_string*)mr_669.data).arg1; return 0; } @@ -93,5 +119,18 @@ int Bar_testa(Bar* b) { return 4; } -void _vinit() { +Option_string optional_a() { + return opt_ok(& (string []) { tos3("111") }, sizeof(string)); +} + +Option_string optional_b() { + return opt_ok(& (string []) { tos3("222") }, sizeof(string)); +} + +Option_multi_return_int_string optional_mr() { + return opt_ok(& (multi_return_int_string []) { (multi_return_int_string){.arg0=1,.arg1=tos3("111")} }, sizeof(multi_return_int_string)); +} + +void _vinit() { + } diff --git a/vlib/v/gen/tests/4.vv b/vlib/v/gen/tests/4.vv index ec5555fc74..26031b83b8 100644 --- a/vlib/v/gen/tests/4.vv +++ b/vlib/v/gen/tests/4.vv @@ -42,6 +42,11 @@ fn main() { ma1, ma2 := 'hello', 'vlang' mr1, mr2 := mr_test() + + opt1, opt2, opt3, opt4 := 'opt1', optional_a(), 'opt3', optional_b() + opt_mr1, opt_mr12 := optional_mr() or { + // err + } } fn mr_test() (int, string) { @@ -82,3 +87,16 @@ struct Foo{ a string b Bar } + +fn optional_a() ?string { + return '111' +} + +fn optional_b() ?string { + return '222' +} + +fn optional_mr() ?(int, string) { + return 1, '111' +} + diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 60b537e7df..e71b5d528d 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1646,6 +1646,7 @@ fn (p mut Parser) parse_assign_rhs() []ast.Expr { fn (p mut Parser) assign_stmt() ast.Stmt { idents := p.parse_assign_lhs() + pos := p.tok.position() op := p.tok.kind p.next() // :=, = exprs := p.parse_assign_rhs() @@ -1676,7 +1677,7 @@ fn (p mut Parser) assign_stmt() ast.Stmt { left: idents right: exprs op: op - pos: p.tok.position() + pos: pos } }