checker/cgen: support `return` from nested `or` (#8430)

pull/8441/head
Uwe Krüger 2021-01-30 11:46:36 +01:00 committed by GitHub
parent 5af16f38fc
commit 5e9b528a9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 30 deletions

View File

@ -2299,7 +2299,12 @@ pub fn (mut c Checker) const_decl(mut node ast.ConstDecl) {
for i, field in node.fields {
c.const_decl = field.name
c.const_deps << field.name
typ := c.expr(field.expr)
mut typ := c.expr(field.expr)
if field.expr is ast.CallExpr {
if field.expr.or_block.kind != .absent {
typ = typ.clear_flag(.optional)
}
}
node.fields[i].typ = c.table.mktyp(typ)
for cd in c.const_deps {
for j, f in node.fields {
@ -3840,6 +3845,11 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
mut typ := obj.typ
if typ == 0 {
typ = c.expr(obj.expr)
if obj.expr is ast.CallExpr {
if obj.expr.or_block.kind != .absent {
typ = typ.clear_flag(.optional)
}
}
}
ident.name = name
ident.kind = .constant

View File

@ -2099,8 +2099,8 @@ 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;`
g.write('*($styp*)')
if g.is_autofree {
g.write('*($styp*)')
g.write(tmp_opt + '.data/*FFz*/')
g.right_is_opt = false
g.is_assign_rhs = false
@ -2156,13 +2156,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
}
}
}
if unwrap_optional {
if g.is_autofree {
// g.write(tmp_opt + '/*FF*/')
} else {
g.write('.data')
}
}
if str_add || op_overloaded {
g.write(')')
}
@ -4623,7 +4616,7 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
g.definitions.writeln('$styp _const_$name; // fixed array const')
}
} else {
g.const_decl_init_later(field.mod, name, val, field.typ)
g.const_decl_init_later(field.mod, name, val, field.typ, false)
}
}
ast.StringLiteral {
@ -4635,13 +4628,15 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
ast.CallExpr {
if val.starts_with('Option_') {
g.inits[field.mod].writeln(val)
g.const_decl_init_later(field.mod, name, g.current_tmp_var(), field.typ)
unwrap_option := field.expr.or_block.kind != .absent
g.const_decl_init_later(field.mod, name, g.current_tmp_var(), field.typ,
unwrap_option)
} else {
g.const_decl_init_later(field.mod, name, val, field.typ)
g.const_decl_init_later(field.mod, name, val, field.typ, false)
}
}
else {
g.const_decl_init_later(field.mod, name, val, field.typ)
g.const_decl_init_later(field.mod, name, val, field.typ, false)
}
}
}
@ -4656,7 +4651,7 @@ fn (mut g Gen) const_decl_simple_define(name string, val string) {
g.definitions.writeln(val)
}
fn (mut g Gen) const_decl_init_later(mod string, name string, val string, typ table.Type) {
fn (mut g Gen) const_decl_init_later(mod string, name string, val string, typ table.Type, unwrap_option bool) {
// Initialize more complex consts in `void _vinit/2{}`
// (C doesn't allow init expressions that can't be resolved at compile time).
styp := g.typ(typ)
@ -4669,7 +4664,11 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, val string, typ ta
g.inits[mod].writeln('\t_const_os__args = os__init_os_args(___argc, (byte**)___argv);')
}
} else {
g.inits[mod].writeln('\t$cname = $val;')
if unwrap_option {
g.inits[mod].writeln('\t$cname = *($styp*)${val}.data;')
} else {
g.inits[mod].writeln('\t$cname = $val;')
}
}
if g.is_autofree {
if styp.starts_with('array_') {
@ -5305,15 +5304,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table.
expr_stmt := stmt as ast.ExprStmt
g.stmt_path_pos << g.out.len
g.write('*($mr_styp*) ${cvar_name}.data = ')
is_opt_call := expr_stmt.expr is ast.CallExpr
&& expr_stmt.typ.has_flag(.optional)
if is_opt_call {
g.write('*($mr_styp*) ')
}
g.expr_with_cast(expr_stmt.expr, expr_stmt.typ, return_type.clear_flag(.optional))
if is_opt_call {
g.write('.data')
}
if g.inside_ternary == 0 && !(expr_stmt.expr is ast.IfExpr) {
g.writeln(';')
}
@ -5342,11 +5333,15 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table.
// the defered statements are generated.
g.write_defer_stmts()
// Now that option types are distinct we need a cast here
styp := g.typ(g.fn_decl.return_type)
err_obj := g.new_tmp_var()
g.writeln('\t$styp $err_obj;')
g.writeln('\tmemcpy(&$err_obj, &$cvar_name, sizeof(Option));')
g.writeln('\treturn $err_obj;')
if g.fn_decl.return_type == table.void_type {
g.writeln('\treturn;')
} else {
styp := g.typ(g.fn_decl.return_type)
err_obj := g.new_tmp_var()
g.writeln('\t$styp $err_obj;')
g.writeln('\tmemcpy(&$err_obj, &$cvar_name, sizeof(Option));')
g.writeln('\treturn $err_obj;')
}
}
}
g.write('}')

View File

@ -315,7 +315,15 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
g.or_block(tmp_opt, node.or_block, node.return_type)
}
if is_gen_or_and_assign_rhs {
g.write('\n $cur_line $tmp_opt')
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')
} else {
g.write('\n $cur_line *($unwrapped_styp*)${tmp_opt}.data')
}
// g.write('\n /*call_expr cur_line:*/ $cur_line /*C*/ $tmp_opt /*end*/')
// g.insert_before_stmt('\n /* VVV */ $tmp_opt')
}

View File

@ -0,0 +1,43 @@
fn ret(s string) string {
return s
}
fn raise() ?string {
return none
}
fn xx() {
s := ret(raise() or { return })
println(s)
}
fn test_nested_or() {
xx()
}
struct St {
mut:
z f64
}
fn (mut s St) raise() ?f64 {
return error('some error')
}
fn retf(f f64) f64 {
return f
}
fn (mut s St) aa() {
f := retf(s.raise() or { return })
s.z = 7.5
println(f)
}
fn test_nested_or_method_call() {
mut x := St{
z: 2.25
}
x.aa()
assert x.z == 2.25
}

View File

@ -24,5 +24,5 @@ fn test_semaphore() {
}
elapsed_ms := f64(elapsed)/time.millisecond
println('elapsed: ${elapsed_ms:.1f}ms')
assert elapsed_ms >= 195.0
assert elapsed_ms >= 190.0
}