1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

cgen: multi return/assign optionals

This commit is contained in:
joe-conigliaro 2020-03-31 15:34:59 +11:00
parent 07de351546
commit 71ca553190
No known key found for this signature in database
GPG Key ID: C12F7136C08206F1
8 changed files with 298 additions and 115 deletions

View File

@ -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)

View File

@ -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`

View File

@ -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();

View File

@ -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() {
}

View File

@ -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 */});

View File

@ -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() {
}

View File

@ -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'
}

View File

@ -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
}
}