cgen: optionals/autofree fixes
							parent
							
								
									970bb09eca
								
							
						
					
					
						commit
						d39866d4f7
					
				|  | @ -295,9 +295,10 @@ jobs: | |||
|         run: ./v -cc gcc -cflags "-Werror" test-self | ||||
|       - name: Build examples | ||||
|         run: ./v build-examples | ||||
|       - name: Build examples with -autofree | ||||
|       - name: Build examples/certain tests with -autofree | ||||
|         run: | | ||||
|           ./v -autofree -experimental -o tetris examples/tetris/tetris.v | ||||
|           ./v -autofree vlib/v/tests/option_test.v | ||||
|       - name: Build modules | ||||
|         run: | | ||||
|           ./v build-module vlib/os | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ const ( | |||
| ================== | ||||
| C error. This should never happen. | ||||
| 
 | ||||
| If you were not working with C interop, please raise an issue on GitHub: | ||||
| If you were not working with C interop, this is a compiler bug, please raise an issue on GitHub: | ||||
| 
 | ||||
| https://github.com/vlang/v/issues/new/choose
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,108 @@ | |||
| // Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
 | ||||
| // Use of this source code is governed by an MIT license
 | ||||
| // that can be found in the LICENSE file.
 | ||||
| module c | ||||
| 
 | ||||
| import v.ast | ||||
| import v.table | ||||
| 
 | ||||
| fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) { | ||||
| 	mut node := original_assert_statement | ||||
| 	g.writeln('// assert') | ||||
| 	if mut node.expr is ast.InfixExpr { | ||||
| 		if mut node.expr.left is ast.CallExpr { | ||||
| 			node.expr.left = g.new_ctemp_var_then_gen(node.expr.left, node.expr.left_type) | ||||
| 		} | ||||
| 		if mut node.expr.right is ast.CallExpr { | ||||
| 			node.expr.right = g.new_ctemp_var_then_gen(node.expr.right, node.expr.right_type) | ||||
| 		} | ||||
| 	} | ||||
| 	g.inside_ternary++ | ||||
| 	if g.is_test { | ||||
| 		g.write('if (') | ||||
| 		g.expr(node.expr) | ||||
| 		g.write(')') | ||||
| 		g.decrement_inside_ternary() | ||||
| 		g.writeln(' {') | ||||
| 		g.writeln('\tg_test_oks++;') | ||||
| 		metaname_ok := g.gen_assert_metainfo(node) | ||||
| 		g.writeln('\tmain__cb_assertion_ok(&$metaname_ok);') | ||||
| 		g.writeln('} else {') | ||||
| 		g.writeln('\tg_test_fails++;') | ||||
| 		metaname_fail := g.gen_assert_metainfo(node) | ||||
| 		g.writeln('\tmain__cb_assertion_failed(&$metaname_fail);') | ||||
| 		g.writeln('\tlongjmp(g_jump_buffer, 1);') | ||||
| 		g.writeln('\t// TODO') | ||||
| 		g.writeln('\t// Maybe print all vars in a test function if it fails?') | ||||
| 		g.writeln('}') | ||||
| 	} else { | ||||
| 		g.write('if (!(') | ||||
| 		g.expr(node.expr) | ||||
| 		g.write('))') | ||||
| 		g.decrement_inside_ternary() | ||||
| 		g.writeln(' {') | ||||
| 		metaname_panic := g.gen_assert_metainfo(node) | ||||
| 		g.writeln('\t__print_assert_failure(&$metaname_panic);') | ||||
| 		g.writeln('\tv_panic(_SLIT("Assertion failed..."));') | ||||
| 		g.writeln('}') | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) gen_assert_metainfo(node ast.AssertStmt) string { | ||||
| 	mod_path := cestring(g.file.path) | ||||
| 	fn_name := g.fn_decl.name | ||||
| 	line_nr := node.pos.line_nr | ||||
| 	src := cestring(node.expr.str()) | ||||
| 	metaname := 'v_assert_meta_info_$g.new_tmp_var()' | ||||
| 	g.writeln('\tVAssertMetaInfo $metaname = {0};') | ||||
| 	g.writeln('\t${metaname}.fpath = ${ctoslit(mod_path)};') | ||||
| 	g.writeln('\t${metaname}.line_nr = $line_nr;') | ||||
| 	g.writeln('\t${metaname}.fn_name = ${ctoslit(fn_name)};') | ||||
| 	g.writeln('\t${metaname}.src = ${cnewlines(ctoslit(src))};') | ||||
| 	match mut node.expr { | ||||
| 		ast.InfixExpr { | ||||
| 			g.writeln('\t${metaname}.op = ${ctoslit(node.expr.op.str())};') | ||||
| 			g.writeln('\t${metaname}.llabel = ${cnewlines(ctoslit(node.expr.left.str()))};') | ||||
| 			g.writeln('\t${metaname}.rlabel = ${cnewlines(ctoslit(node.expr.right.str()))};') | ||||
| 			g.write('\t${metaname}.lvalue = ') | ||||
| 			g.gen_assert_single_expr(node.expr.left, node.expr.left_type) | ||||
| 			g.writeln(';') | ||||
| 			//
 | ||||
| 			g.write('\t${metaname}.rvalue = ') | ||||
| 			g.gen_assert_single_expr(node.expr.right, node.expr.right_type) | ||||
| 			g.writeln(';') | ||||
| 		} | ||||
| 		ast.CallExpr { | ||||
| 			g.writeln('\t${metaname}.op = _SLIT("call");') | ||||
| 		} | ||||
| 		else {} | ||||
| 	} | ||||
| 	return metaname | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) gen_assert_single_expr(expr ast.Expr, typ table.Type) { | ||||
| 	unknown_value := '*unknown value*' | ||||
| 	match expr { | ||||
| 		ast.CastExpr, ast.IndexExpr, ast.MatchExpr { | ||||
| 			g.write(ctoslit(unknown_value)) | ||||
| 		} | ||||
| 		ast.PrefixExpr { | ||||
| 			if expr.right is ast.CastExpr { | ||||
| 				// TODO: remove this check;
 | ||||
| 				// vlib/builtin/map_test.v (a map of &int, set to &int(0)) fails
 | ||||
| 				// without special casing ast.CastExpr here
 | ||||
| 				g.write(ctoslit(unknown_value)) | ||||
| 			} else { | ||||
| 				g.gen_expr_to_string(expr, typ) | ||||
| 			} | ||||
| 		} | ||||
| 		ast.Type { | ||||
| 			sym := g.table.get_type_symbol(typ) | ||||
| 			g.write(ctoslit('$sym.name')) | ||||
| 		} | ||||
| 		else { | ||||
| 			g.gen_expr_to_string(expr, typ) | ||||
| 		} | ||||
| 	} | ||||
| 	g.write(' /* typeof: ' + expr.type_name() + ' type: ' + typ.str() + ' */ ') | ||||
| } | ||||
|  | @ -1637,111 +1637,10 @@ fn (mut g Gen) gen_attrs(attrs []table.Attr) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) { | ||||
| 	mut node := original_assert_statement | ||||
| 	g.writeln('// assert') | ||||
| 	if mut node.expr is ast.InfixExpr { | ||||
| 		if mut node.expr.left is ast.CallExpr { | ||||
| 			node.expr.left = g.new_ctemp_var_then_gen(node.expr.left, node.expr.left_type) | ||||
| 		} | ||||
| 		if mut node.expr.right is ast.CallExpr { | ||||
| 			node.expr.right = g.new_ctemp_var_then_gen(node.expr.right, node.expr.right_type) | ||||
| 		} | ||||
| 	} | ||||
| 	g.inside_ternary++ | ||||
| 	if g.is_test { | ||||
| 		g.write('if (') | ||||
| 		g.expr(node.expr) | ||||
| 		g.write(')') | ||||
| 		g.decrement_inside_ternary() | ||||
| 		g.writeln(' {') | ||||
| 		g.writeln('\tg_test_oks++;') | ||||
| 		metaname_ok := g.gen_assert_metainfo(node) | ||||
| 		g.writeln('\tmain__cb_assertion_ok(&$metaname_ok);') | ||||
| 		g.writeln('} else {') | ||||
| 		g.writeln('\tg_test_fails++;') | ||||
| 		metaname_fail := g.gen_assert_metainfo(node) | ||||
| 		g.writeln('\tmain__cb_assertion_failed(&$metaname_fail);') | ||||
| 		g.writeln('\tlongjmp(g_jump_buffer, 1);') | ||||
| 		g.writeln('\t// TODO') | ||||
| 		g.writeln('\t// Maybe print all vars in a test function if it fails?') | ||||
| 		g.writeln('}') | ||||
| 	} else { | ||||
| 		g.write('if (!(') | ||||
| 		g.expr(node.expr) | ||||
| 		g.write('))') | ||||
| 		g.decrement_inside_ternary() | ||||
| 		g.writeln(' {') | ||||
| 		metaname_panic := g.gen_assert_metainfo(node) | ||||
| 		g.writeln('\t__print_assert_failure(&$metaname_panic);') | ||||
| 		g.writeln('\tv_panic(_SLIT("Assertion failed..."));') | ||||
| 		g.writeln('}') | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn cnewlines(s string) string { | ||||
| 	return s.replace('\n', r'\n') | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) gen_assert_metainfo(node ast.AssertStmt) string { | ||||
| 	mod_path := cestring(g.file.path) | ||||
| 	fn_name := g.fn_decl.name | ||||
| 	line_nr := node.pos.line_nr | ||||
| 	src := cestring(node.expr.str()) | ||||
| 	metaname := 'v_assert_meta_info_$g.new_tmp_var()' | ||||
| 	g.writeln('\tVAssertMetaInfo $metaname = {0};') | ||||
| 	g.writeln('\t${metaname}.fpath = ${ctoslit(mod_path)};') | ||||
| 	g.writeln('\t${metaname}.line_nr = $line_nr;') | ||||
| 	g.writeln('\t${metaname}.fn_name = ${ctoslit(fn_name)};') | ||||
| 	g.writeln('\t${metaname}.src = ${cnewlines(ctoslit(src))};') | ||||
| 	match mut node.expr { | ||||
| 		ast.InfixExpr { | ||||
| 			g.writeln('\t${metaname}.op = ${ctoslit(node.expr.op.str())};') | ||||
| 			g.writeln('\t${metaname}.llabel = ${cnewlines(ctoslit(node.expr.left.str()))};') | ||||
| 			g.writeln('\t${metaname}.rlabel = ${cnewlines(ctoslit(node.expr.right.str()))};') | ||||
| 			g.write('\t${metaname}.lvalue = ') | ||||
| 			g.gen_assert_single_expr(node.expr.left, node.expr.left_type) | ||||
| 			g.writeln(';') | ||||
| 			//
 | ||||
| 			g.write('\t${metaname}.rvalue = ') | ||||
| 			g.gen_assert_single_expr(node.expr.right, node.expr.right_type) | ||||
| 			g.writeln(';') | ||||
| 		} | ||||
| 		ast.CallExpr { | ||||
| 			g.writeln('\t${metaname}.op = _SLIT("call");') | ||||
| 		} | ||||
| 		else {} | ||||
| 	} | ||||
| 	return metaname | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) gen_assert_single_expr(expr ast.Expr, typ table.Type) { | ||||
| 	unknown_value := '*unknown value*' | ||||
| 	match expr { | ||||
| 		ast.CastExpr, ast.IndexExpr, ast.MatchExpr { | ||||
| 			g.write(ctoslit(unknown_value)) | ||||
| 		} | ||||
| 		ast.PrefixExpr { | ||||
| 			if expr.right is ast.CastExpr { | ||||
| 				// TODO: remove this check;
 | ||||
| 				// vlib/builtin/map_test.v (a map of &int, set to &int(0)) fails
 | ||||
| 				// without special casing ast.CastExpr here
 | ||||
| 				g.write(ctoslit(unknown_value)) | ||||
| 			} else { | ||||
| 				g.gen_expr_to_string(expr, typ) | ||||
| 			} | ||||
| 		} | ||||
| 		ast.Type { | ||||
| 			sym := g.table.get_type_symbol(typ) | ||||
| 			g.write(ctoslit('$sym.name')) | ||||
| 		} | ||||
| 		else { | ||||
| 			g.gen_expr_to_string(expr, typ) | ||||
| 		} | ||||
| 	} | ||||
| 	g.write(' /* typeof: ' + expr.type_name() + ' type: ' + typ.str() + ' */ ') | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) write_fn_ptr_decl(func &table.FnType, ptr_name string) { | ||||
| 	ret_styp := g.typ(func.func.return_type) | ||||
| 	g.write('$ret_styp (*$ptr_name) (') | ||||
|  | @ -1830,8 +1729,9 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { | |||
| 	// return;
 | ||||
| 	// }
 | ||||
| 	// int pos = *(int*)_t190.data;
 | ||||
| 	mut tmp_opt := '' | ||||
| 	is_optional := g.is_autofree && (assign_stmt.op in [.decl_assign, .assign]) | ||||
| 	// mut tmp_opt := ''
 | ||||
| 	/* | ||||
| 	is_optional := false && g.is_autofree && (assign_stmt.op in [.decl_assign, .assign]) | ||||
| 		&& assign_stmt.left_types.len == 1 && assign_stmt.right[0] is ast.CallExpr | ||||
| 	if is_optional { | ||||
| 		// g.write('/* optional assignment */')
 | ||||
|  | @ -1849,6 +1749,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { | |||
| 			// return
 | ||||
| 		} | ||||
| 	} | ||||
| 	*/ | ||||
| 	// json_test failed w/o this check
 | ||||
| 	if return_type != table.void_type && return_type != 0 { | ||||
| 		sym := g.table.get_type_symbol(return_type) | ||||
|  | @ -2175,7 +2076,9 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { | |||
| 				// Unwrap the optional now that the testing code has been prepended.
 | ||||
| 				// `pos := s.index(...
 | ||||
| 				// `int pos = *(int)_t10.data;`
 | ||||
| 				if g.is_autofree { | ||||
| 				// if g.is_autofree {
 | ||||
| 				/* | ||||
| 				if is_optional { | ||||
| 					g.write('*($styp*)') | ||||
| 					g.write(tmp_opt + '.data/*FFz*/') | ||||
| 					g.right_is_opt = false | ||||
|  | @ -2185,6 +2088,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { | |||
| 					} | ||||
| 					return | ||||
| 				} | ||||
| 				*/ | ||||
| 			} | ||||
| 			g.is_shared = var_type.has_flag(.shared_f) | ||||
| 			if !cloned { | ||||
|  | @ -5619,7 +5523,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table. | |||
| 	if is_none_ok { | ||||
| 		g.writeln('if (!${cvar_name}.ok && !${cvar_name}.is_none) {') | ||||
| 	} else { | ||||
| 		g.writeln('if (!${cvar_name}.ok) {') | ||||
| 		g.writeln('if (!${cvar_name}.ok) { /*or block*/ ') | ||||
| 	} | ||||
| 	if or_block.kind == .block { | ||||
| 		if g.inside_or_block { | ||||
|  |  | |||
|  | @ -391,6 +391,7 @@ fn (mut g Gen) fn_args(args []table.Param, is_variadic bool) ([]string, []string | |||
| } | ||||
| 
 | ||||
| fn (mut g Gen) call_expr(node ast.CallExpr) { | ||||
| 	// g.write('/*call expr*/')
 | ||||
| 	// NOTE: everything could be done this way
 | ||||
| 	// see my comment in parser near anon_fn
 | ||||
| 	if node.left is ast.AnonFn { | ||||
|  | @ -408,20 +409,25 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { | |||
| 	defer { | ||||
| 		g.inside_call = false | ||||
| 	} | ||||
| 	gen_or := node.or_block.kind != .absent && !g.is_autofree | ||||
| 	// if gen_or {
 | ||||
| 	// g.writeln('/*start*/')
 | ||||
| 	// }
 | ||||
| 	gen_or := node.or_block.kind != .absent // && !g.is_autofree
 | ||||
| 	is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs | ||||
| 	cur_line := if is_gen_or_and_assign_rhs && !g.is_autofree { | ||||
| 	cur_line := if is_gen_or_and_assign_rhs { // && !g.is_autofree {
 | ||||
| 		// `x := foo() or { ...}`
 | ||||
| 		// cut everything that has been generated to prepend optional variable creation
 | ||||
| 		line := g.go_before_stmt(0) | ||||
| 		g.out.write_string(tabs[g.indent]) | ||||
| 		// g.write('/*is_gen_or_and_assign_rhs*/')
 | ||||
| 		line | ||||
| 	} else { | ||||
| 		'' | ||||
| 	} | ||||
| 	if gen_or && g.pref.autofree && g.inside_return { | ||||
| 		// TODO optional return af hack (tmp_count gets increased in .return_statement())
 | ||||
| 		g.tmp_count-- | ||||
| 	} | ||||
| 	tmp_opt := if gen_or { g.new_tmp_var() } else { '' } | ||||
| 	if gen_or { | ||||
| 	if gen_or && !g.inside_return { | ||||
| 		// if is_gen_or_and_assign_rhs {
 | ||||
| 		styp := g.typ(node.return_type.set_flag(.optional)) | ||||
| 		g.write('$styp $tmp_opt = ') | ||||
| 	} | ||||
|  | @ -437,16 +443,16 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { | |||
| 		g.fn_call(node) | ||||
| 	} | ||||
| 	if gen_or { // && !g.autofree {
 | ||||
| 		if !g.is_autofree { | ||||
| 		// if !g.is_autofree {
 | ||||
| 		g.or_block(tmp_opt, node.or_block, node.return_type) | ||||
| 		} | ||||
| 		//}
 | ||||
| 		if is_gen_or_and_assign_rhs { | ||||
| 			unwrapped_typ := node.return_type.clear_flag(.optional) | ||||
| 			unwrapped_styp := g.typ(unwrapped_typ) | ||||
| 			if unwrapped_typ == table.void_type { | ||||
| 				g.write('\n $cur_line') | ||||
| 			} else if g.table.get_type_symbol(node.return_type).kind == .multi_return { | ||||
| 				g.write('\n $cur_line $tmp_opt') | ||||
| 				g.write('\n $cur_line $tmp_opt /*U*/') | ||||
| 			} else { | ||||
| 				g.write('\n $cur_line *($unwrapped_styp*)${tmp_opt}.data') | ||||
| 			} | ||||
|  |  | |||
|  | @ -50,9 +50,7 @@ fn test_option_for_base_type_without_variable() { | |||
| 		0 | ||||
| 	} | ||||
| 	assert val == 42 | ||||
| 	val = ret_none() or { | ||||
| 		return | ||||
| 	} | ||||
| 	val = ret_none() or { return } | ||||
| 	assert false | ||||
| 	// This is invalid:
 | ||||
| 	// x := 5 or {
 | ||||
|  | @ -101,30 +99,32 @@ fn foo_str() ?string { | |||
| } | ||||
| 
 | ||||
| fn propagate_optional(b bool) ?int { | ||||
| 	a := err_call(b)? | ||||
| 	a := err_call(b) ? | ||||
| 	return a | ||||
| } | ||||
| 
 | ||||
| fn propagate_different_type(b bool) ?bool { | ||||
| 	err_call(b)? | ||||
| 	err_call(b) ? | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| fn test_propagation() { | ||||
| 	a := propagate_optional(true) or { | ||||
| 		0 | ||||
| 	} | ||||
| 	println(1) | ||||
| 	a := propagate_optional(true) or { 0 } | ||||
| 	println(2) | ||||
| 	assert a == 42 | ||||
| 	println(3) | ||||
| 	if _ := propagate_optional(false) { | ||||
| 		assert false | ||||
| 	} | ||||
| 	b := propagate_different_type(true) or { | ||||
| 		false | ||||
| 	} | ||||
| 	println(4) | ||||
| 	b := propagate_different_type(true) or { false } | ||||
| 	assert b == true | ||||
| 	println(5) | ||||
| 	if _ := propagate_different_type(false) { | ||||
| 		assert false | ||||
| 	} | ||||
| 	println(6) | ||||
| } | ||||
| 
 | ||||
| fn test_q() { | ||||
|  | @ -132,23 +132,17 @@ fn test_q() { | |||
| } | ||||
| 
 | ||||
| fn or_return_val() int { | ||||
| 	a := ret_none() or { | ||||
| 		return 1 | ||||
| 	} | ||||
| 	a := ret_none() or { return 1 } | ||||
| 	return a | ||||
| } | ||||
| 
 | ||||
| fn or_return_error() ?int { | ||||
| 	a := ret_none() or { | ||||
| 		return error('Nope') | ||||
| 	} | ||||
| 	a := ret_none() or { return error('Nope') } | ||||
| 	return a | ||||
| } | ||||
| 
 | ||||
| fn or_return_none() ?int { | ||||
| 	a := ret_none() or { | ||||
| 		return none | ||||
| 	} | ||||
| 	a := ret_none() or { return none } | ||||
| 	return a | ||||
| } | ||||
| 
 | ||||
|  | @ -193,9 +187,7 @@ mut: | |||
| } | ||||
| 
 | ||||
| fn test_field_or() { | ||||
| 	name := foo_str() or { | ||||
| 		'nada' | ||||
| 	} | ||||
| 	name := foo_str() or { 'nada' } | ||||
| 	assert name == 'something' | ||||
| 	/* | ||||
| 	QTODO | ||||
|  | @ -225,8 +217,6 @@ mut: | |||
| 	opt ?Thing | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| fn test_opt_field() { | ||||
| 	/* | ||||
| 	QTODO | ||||
|  | @ -251,13 +241,9 @@ fn test_opt_ptr() { | |||
| 	else { | ||||
| 	} | ||||
| 	a := 3 | ||||
| 	mut r := opt_ptr(&a) or { | ||||
| 		&int(0) | ||||
| 	} | ||||
| 	mut r := opt_ptr(&a) or { &int(0) } | ||||
| 	assert r == &a | ||||
| 	r = opt_ptr(&int(0)) or { | ||||
| 		return | ||||
| 	} | ||||
| 	r = opt_ptr(&int(0)) or { return } | ||||
| 	assert false | ||||
| } | ||||
| 
 | ||||
|  | @ -283,34 +269,34 @@ fn test_multi_return_opt() { | |||
| */ | ||||
| 
 | ||||
| fn test_optional_val_with_empty_or() { | ||||
| 	ret_none() or {} | ||||
| 	ret_none() or { } | ||||
| 	assert true | ||||
| } | ||||
| 
 | ||||
| fn test_optional_void_return_types_of_anon_fn() { | ||||
| 	f := fn(i int) ? { | ||||
| 	f := fn (i int) ? { | ||||
| 		if i == 0 { | ||||
| 			return error("0") | ||||
| 			return error('0') | ||||
| 		} | ||||
| 
 | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	f(0) or { | ||||
| 		assert err == "0" | ||||
| 		assert err == '0' | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct Foo { | ||||
| 	f fn(int) ? | ||||
| 	f fn (int) ? | ||||
| } | ||||
| 
 | ||||
| fn test_option_void_return_types_of_anon_fn_in_struct() { | ||||
| 	foo := Foo { | ||||
| 		f: fn(i int) ? { | ||||
| 	foo := Foo{ | ||||
| 		f: fn (i int) ? { | ||||
| 			if i == 0 { | ||||
| 				return error("0") | ||||
| 				return error('0') | ||||
| 			} | ||||
| 
 | ||||
| 			return | ||||
|  | @ -318,7 +304,7 @@ fn test_option_void_return_types_of_anon_fn_in_struct() { | |||
| 	} | ||||
| 
 | ||||
| 	foo.f(0) or { | ||||
| 		assert err == "0" | ||||
| 		assert err == '0' | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
|  | @ -377,3 +363,21 @@ struct MultiOptionalFieldTest { | |||
| 	a ?int | ||||
| 	b ?int | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| fn foo() ?int { | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| fn foo2() ?int { | ||||
| 	for _ in 0 .. 5 { | ||||
| 		return foo() or { continue } | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| fn test_return_or() { | ||||
| 	x := foo2() or { return } | ||||
| 	assert x == 0 | ||||
| } | ||||
| */ | ||||
|  |  | |||
|  | @ -210,15 +210,11 @@ fn if_expr() string { | |||
| } | ||||
| 
 | ||||
| fn return_if_expr() string { | ||||
| 	return if true { | ||||
| 		get_string('a' + 'b') | ||||
| 	} else { | ||||
| 		get_string('c' + 'd') | ||||
| 	} | ||||
| 	return if true { get_string('a' + 'b') } else { get_string('c' + 'd') } | ||||
| } | ||||
| 
 | ||||
| fn loop_map() { | ||||
| 	m := { | ||||
| 	m := map{ | ||||
| 		'UK':     'London' | ||||
| 		'France': 'Paris' | ||||
| 	} | ||||
|  | @ -337,6 +333,9 @@ fn comp_if() { | |||
| 	println(s) | ||||
| } | ||||
| 
 | ||||
| fn anon_fn() { | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	println('start') | ||||
| 	simple() | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue