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 {
|
||||
g.or_block(tmp_opt, node.or_block, node.return_type)
|
||||
}
|
||||
// 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