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

checker, cgen: fix nested if expr with optional or result (fix #15735) (#15743)

This commit is contained in:
yuyi 2022-09-13 15:09:12 +08:00 committed by GitHub
parent e51f0be6db
commit adc3b25f52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 141 additions and 76 deletions

View File

@ -179,88 +179,92 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
c.smartcast_cond_pos = token.Pos{}
}
if expr_required {
if branch.stmts.len > 0 && branch.stmts.last() is ast.ExprStmt {
mut last_expr := branch.stmts.last() as ast.ExprStmt
expr := last_expr.expr
if expr is ast.ConcatExpr {
for val in expr.vals {
c.check_expr_opt_call(val, c.expr(val))
if branch.stmts.len > 0 {
mut stmt := branch.stmts.last()
if mut stmt is ast.ExprStmt {
if mut stmt.expr is ast.ConcatExpr {
for val in stmt.expr.vals {
c.check_expr_opt_call(val, c.expr(val))
}
}
}
c.expected_type = former_expected_type
if c.table.type_kind(c.expected_type) == .sum_type
&& c.table.is_sumtype_or_in_variant(c.expected_type, node.typ) {
node.is_expr = true
node.typ = c.expected_type
}
if c.expected_type.has_flag(.optional) || c.expected_type.has_flag(.result) {
if node.typ == ast.void_type {
c.expected_type = former_expected_type
if c.table.type_kind(c.expected_type) == .sum_type
&& c.table.is_sumtype_or_in_variant(c.expected_type, node.typ) {
node.is_expr = true
node.typ = c.expected_type
}
}
if c.expected_type.has_flag(.generic) {
if node.typ == ast.void_type {
node.is_expr = true
node.typ = c.unwrap_generic(c.expected_type)
}
continue
}
last_expr.typ = c.expr(last_expr.expr)
if c.table.type_kind(c.expected_type) == .multi_return
&& c.table.type_kind(last_expr.typ) == .multi_return {
if node.typ == ast.void_type {
node.is_expr = true
node.typ = c.expected_type
}
}
if last_expr.typ == ast.void_type && !is_noreturn_callexpr(last_expr.expr)
&& !c.skip_flags {
// cannot return void type and use it as expr in any circumstances
// (e.g. argument expression, variable declaration / assignment)
c.error('the final expression in `if` or `match`, must have a value of a non-void type',
last_expr.pos)
continue
}
if !c.check_types(last_expr.typ, node.typ) {
if node.typ == ast.void_type {
// first branch of if expression
node.is_expr = true
node.typ = last_expr.typ
continue
} else if node.typ in [ast.float_literal_type, ast.int_literal_type] {
if node.typ == ast.int_literal_type {
if last_expr.typ.is_int() || last_expr.typ.is_float() {
node.typ = last_expr.typ
continue
}
} else { // node.typ == float_literal
if last_expr.typ.is_float() {
node.typ = last_expr.typ
continue
}
if c.expected_type.has_flag(.optional) || c.expected_type.has_flag(.result) {
if node.typ == ast.void_type {
node.is_expr = true
node.typ = c.expected_type
}
}
if last_expr.typ in [ast.float_literal_type, ast.int_literal_type] {
if last_expr.typ == ast.int_literal_type {
if node.typ.is_int() || node.typ.is_float() {
continue
}
} else { // expr_type == float_literal
if node.typ.is_float() {
continue
}
if c.expected_type.has_flag(.generic) {
if node.typ == ast.void_type {
node.is_expr = true
node.typ = c.unwrap_generic(c.expected_type)
}
continue
}
stmt.typ = c.expr(stmt.expr)
if c.table.type_kind(c.expected_type) == .multi_return
&& c.table.type_kind(stmt.typ) == .multi_return {
if node.typ == ast.void_type {
node.is_expr = true
node.typ = c.expected_type
}
}
if node.is_expr && c.table.sym(former_expected_type).kind == .sum_type {
node.typ = former_expected_type
if stmt.typ == ast.void_type && !is_noreturn_callexpr(stmt.expr)
&& !c.skip_flags {
// cannot return void type and use it as expr in any circumstances
// (e.g. argument expression, variable declaration / assignment)
c.error('the final expression in `if` or `match`, must have a value of a non-void type',
stmt.pos)
continue
}
if is_noreturn_callexpr(last_expr.expr) {
continue
if !c.check_types(stmt.typ, node.typ) {
if node.typ == ast.void_type {
// first branch of if expression
node.is_expr = true
node.typ = stmt.typ
continue
} else if node.typ in [ast.float_literal_type, ast.int_literal_type] {
if node.typ == ast.int_literal_type {
if stmt.typ.is_int() || stmt.typ.is_float() {
node.typ = stmt.typ
continue
}
} else { // node.typ == float_literal
if stmt.typ.is_float() {
node.typ = stmt.typ
continue
}
}
}
if stmt.typ in [ast.float_literal_type, ast.int_literal_type] {
if stmt.typ == ast.int_literal_type {
if node.typ.is_int() || node.typ.is_float() {
continue
}
} else { // expr_type == float_literal
if node.typ.is_float() {
continue
}
}
}
if node.is_expr && c.table.sym(former_expected_type).kind == .sum_type {
node.typ = former_expected_type
continue
}
if is_noreturn_callexpr(stmt.expr) {
continue
}
c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(stmt.typ)}`',
node.pos)
}
c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(last_expr.typ)}`',
node.pos)
} else if !node.is_comptime {
c.error('`$if_kind` expression requires an expression as the last statement of every branch',
branch.pos)
}
} else if !node.is_comptime {
c.error('`$if_kind` expression requires an expression as the last statement of every branch',

View File

@ -139,10 +139,19 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
needs_tmp_var := g.need_tmp_var_in_if(node)
tmp := if needs_tmp_var { g.new_tmp_var() } else { '' }
mut cur_line := ''
mut raw_state := false
if needs_tmp_var {
if node.typ.has_flag(.optional) {
raw_state = g.inside_if_optional
defer {
g.inside_if_optional = raw_state
}
g.inside_if_optional = true
} else if node.typ.has_flag(.result) {
raw_state = g.inside_if_result
defer {
g.inside_if_result = raw_state
}
g.inside_if_result = true
}
styp := g.typ(node.typ)
@ -325,9 +334,4 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
g.empty_line = false
g.write('$cur_line $tmp')
}
if node.typ.has_flag(.optional) {
g.inside_if_optional = false
} else if node.typ.has_flag(.result) {
g.inside_if_result = false
}
}

View File

@ -0,0 +1,57 @@
// optional
pub fn foo1() bool {
return false
}
pub fn bar1(i int) ?int {
if i < 0 {
return none
}
return if i == 0 {
if foo1() {
1
} else {
2
}
} else {
3
}
}
// result
pub fn foo2() bool {
return false
}
pub fn bar2(i int) !int {
if i < 0 {
return error('')
}
return if i == 0 {
if foo2() {
1
} else {
2
}
} else {
3
}
}
fn test_if_expr_nested_with_optional_result() {
ret11 := bar1(0) or { 0 }
println(ret11)
assert ret11 == 2
ret12 := bar1(1) or { 0 }
println(ret12)
assert ret12 == 3
ret21 := bar2(0) or { 0 }
println(ret21)
assert ret21 == 2
ret22 := bar2(1) or { 0 }
println(ret22)
assert ret22 == 3
}