From 2cc890c9f97c2ceeddec1fb8b0468c04382fd285 Mon Sep 17 00:00:00 2001 From: shove70 Date: Sat, 11 Nov 2023 17:20:44 +0800 Subject: [PATCH 1/2] checker, cgen: fix match branches return type ref mismatch, when return type exists interface or sumtype(fix #16203) --- vlib/v/checker/checker.v | 10 +++- vlib/v/checker/match.v | 57 ++++++++++++++----- .../tests/match_return_mismatch_type_err.out | 28 +++++++++ .../tests/match_return_mismatch_type_err.vv | 36 ++++++++++++ vlib/v/gen/c/cgen.v | 10 +++- vlib/v/gen/c/match.v | 11 ++++ vlib/v/tests/match_test.v | 20 +++++++ 7 files changed, 152 insertions(+), 20 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index be8825d8d881a7..531e5065929201 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3564,9 +3564,7 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { typ = c.expr(mut obj.expr) } } - if c.inside_casting_to_str && obj.orig_type != 0 - && c.table.sym(obj.orig_type).kind == .interface_ - && c.table.sym(obj.smartcasts.last()).kind != .interface_ { + if c.inside_casting_to_str && c.is_interface_var(obj) { typ = typ.deref() } is_option := typ.has_flag(.option) || typ.has_flag(.result) @@ -4028,6 +4026,12 @@ pub fn (mut c Checker) is_comptime_var(node ast.Expr) bool { && (node.obj as ast.Var).ct_type_var != .no_comptime } +[inline] +fn (mut c Checker) is_interface_var(var ast.ScopeObject) bool { + return var is ast.Var && var.orig_type != 0 && c.table.sym(var.orig_type).kind == .interface_ + && c.table.sym(var.smartcasts.last()).kind != .interface_ +} + fn (mut c Checker) mark_as_referenced(mut node ast.Expr, as_interface bool) { match mut node { ast.Ident { diff --git a/vlib/v/checker/match.v b/vlib/v/checker/match.v index d8302e79225f85..21f08a9de3d60c 100644 --- a/vlib/v/checker/match.v +++ b/vlib/v/checker/match.v @@ -74,22 +74,51 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { ret_type = node.expected_type } else { ret_type = expr_type + if expr_type.is_ptr() { + if stmt.expr is ast.Ident && stmt.expr.obj is ast.Var + && c.is_interface_var(stmt.expr.obj) { + ret_type = expr_type.deref() + } else if mut stmt.expr is ast.PrefixExpr + && stmt.expr.right is ast.Ident { + ident := stmt.expr.right as ast.Ident + if ident.obj is ast.Var && c.is_interface_var(ident.obj) { + ret_type = expr_type.deref() + } + } + } + } + } else { + if node.is_expr && ret_type.idx() != expr_type.idx() { + if (node.expected_type.has_flag(.option) + || node.expected_type.has_flag(.result)) + && c.table.sym(stmt.typ).kind == .struct_ + && c.type_implements(stmt.typ, ast.error_type, node.pos) { + stmt.expr = ast.CastExpr{ + expr: stmt.expr + typname: 'IError' + typ: ast.error_type + expr_type: stmt.typ + pos: node.pos + } + stmt.typ = ast.error_type + } else { + c.check_match_branch_last_stmt(stmt, ret_type, expr_type) + } } - } else if node.is_expr && ret_type.idx() != expr_type.idx() { - if (node.expected_type.has_flag(.option) - || node.expected_type.has_flag(.result)) - && c.table.sym(stmt.typ).kind == .struct_ - && c.type_implements(stmt.typ, ast.error_type, node.pos) { - stmt.expr = ast.CastExpr{ - expr: stmt.expr - typname: 'IError' - typ: ast.error_type - expr_type: stmt.typ - pos: node.pos + if node.is_expr && stmt.typ != ast.error_type { + ret_sym := c.table.sym(ret_type) + stmt_sym := c.table.sym(stmt.typ) + if ret_sym.kind !in [.sum_type, .interface_] + && stmt_sym.kind in [.sum_type, .interface_] { + c.error('return type mismatch, it should be `${ret_sym.name}`', + stmt.pos) + } + if ret_type.nr_muls() != stmt.typ.nr_muls() + && stmt.typ.idx() !in [ast.voidptr_type_idx, ast.nil_type_idx] { + type_name := '&'.repeat(ret_type.nr_muls()) + ret_sym.name + c.error('return type mismatch, it should be `${type_name}`', + stmt.pos) } - stmt.typ = ast.error_type - } else { - c.check_match_branch_last_stmt(stmt, ret_type, expr_type) } } } else if stmt !in [ast.Return, ast.BranchStmt] { diff --git a/vlib/v/checker/tests/match_return_mismatch_type_err.out b/vlib/v/checker/tests/match_return_mismatch_type_err.out index 3a8e8fe15c42d2..1bcd963845b8ea 100644 --- a/vlib/v/checker/tests/match_return_mismatch_type_err.out +++ b/vlib/v/checker/tests/match_return_mismatch_type_err.out @@ -5,3 +5,31 @@ vlib/v/checker/tests/match_return_mismatch_type_err.vv:4:10: error: return type | ~~ 5 | } 6 | println(a) +vlib/v/checker/tests/match_return_mismatch_type_err.vv:18:10: error: return type mismatch, it should be `&string` + 16 | _ = match any { + 17 | string { &any } + 18 | else { variable } + | ~~~~~~~~ + 19 | } + 20 | +vlib/v/checker/tests/match_return_mismatch_type_err.vv:23:10: error: return type mismatch, it should be `string` + 21 | _ = match any { + 22 | string { any } + 23 | else { &variable } + | ^ + 24 | } + 25 | } +vlib/v/checker/tests/match_return_mismatch_type_err.vv:36:10: error: return type mismatch, it should be `&string` + 34 | _ = match any { + 35 | string { &any } + 36 | else { variable } + | ~~~~~~~~ + 37 | } + 38 | +vlib/v/checker/tests/match_return_mismatch_type_err.vv:41:10: error: return type mismatch, it should be `string` + 39 | _ = match any { + 40 | string { any } + 41 | else { &variable } + | ^ + 42 | } + 43 | } diff --git a/vlib/v/checker/tests/match_return_mismatch_type_err.vv b/vlib/v/checker/tests/match_return_mismatch_type_err.vv index c422126014ced2..24c8ebecca33a0 100644 --- a/vlib/v/checker/tests/match_return_mismatch_type_err.vv +++ b/vlib/v/checker/tests/match_return_mismatch_type_err.vv @@ -5,3 +5,39 @@ fn main() { } println(a) } + +// for test the returns both interface or non-interface +interface IAny {} + +fn returns_both_interface_and_non_interface() { + any := IAny('abc') + variable := '' + + _ = match any { + string { &any } + else { variable } + } + + _ = match any { + string { any } + else { &variable } + } +} + +// for test the returns both sumtype or non-sumtype +type SAny = int | string + +fn returns_both_sumtype_and_non_sumtype() { + any := SAny('abc') + variable := '' + + _ = match any { + string { &any } + else { variable } + } + + _ = match any { + string { any } + else { &variable } + } +} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 3771bcb0e5898e..225e3cf5983fdd 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4349,6 +4349,12 @@ fn (mut g Gen) get_const_name(node ast.Ident) string { } } +[inline] +fn (mut g Gen) is_interface_var(var ast.ScopeObject) bool { + return var is ast.Var && var.orig_type != 0 && g.table.sym(var.orig_type).kind == .interface_ + && g.table.sym(var.smartcasts.last()).kind != .interface_ +} + fn (mut g Gen) ident(node ast.Ident) { prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once g.prevent_sum_type_unwrapping_once = false @@ -4485,9 +4491,7 @@ fn (mut g Gen) ident(node ast.Ident) { g.write('(') if obj_sym.kind == .sum_type && !is_auto_heap { g.write('*') - } else if g.inside_casting_to_str && node.obj.orig_type != 0 - && g.table.sym(node.obj.orig_type).kind == .interface_ - && g.table.sym(node.obj.smartcasts.last()).kind != .interface_ { + } else if g.inside_casting_to_str && g.is_interface_var(node.obj) { g.write('*') } } diff --git a/vlib/v/gen/c/match.v b/vlib/v/gen/c/match.v index 3b9aa3a17e57f7..a4fee09ad8a52d 100644 --- a/vlib/v/gen/c/match.v +++ b/vlib/v/gen/c/match.v @@ -230,7 +230,18 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str if is_expr && tmp_var.len > 0 && g.table.sym(node.return_type).kind == .sum_type { g.expected_cast_type = node.return_type } + inside_casting_to_str_old := g.inside_casting_to_str + if is_expr && branch.stmts.len > 0 { + mut stmt := branch.stmts.last() + if mut stmt is ast.ExprStmt { + if mut stmt.expr is ast.Ident && stmt.expr.obj is ast.Var + && g.is_interface_var(stmt.expr.obj) { + g.inside_casting_to_str = true + } + } + } g.stmts_with_tmp_var(branch.stmts, tmp_var) + g.inside_casting_to_str = inside_casting_to_str_old g.expected_cast_type = 0 if g.inside_ternary == 0 { g.writeln('}') diff --git a/vlib/v/tests/match_test.v b/vlib/v/tests/match_test.v index e0c579d471af37..8e069472f696e2 100644 --- a/vlib/v/tests/match_test.v +++ b/vlib/v/tests/match_test.v @@ -301,3 +301,23 @@ fn test_noreturn() { } } } + +// for test the returns both interface and non-interface +interface Any {} + +fn test_returns_both_interface_and_non_interface() { + any := Any('abc') + + mut res := match any { + string { any } + else { 'literal' } + } + assert res == 'abc' + + variable := '' + res = match any { + string { any } + else { variable } + } + assert res == 'abc' +} From 0cc109386460b9fa81f148225609e0d0f3f0df79 Mon Sep 17 00:00:00 2001 From: shove70 Date: Sat, 11 Nov 2023 18:09:01 +0800 Subject: [PATCH 2/2] test --- .../tests/match_return_mismatch_type_err.out | 33 +++++++++++-------- .../tests/match_return_mismatch_type_err.vv | 7 ++++ vlib/v/gen/c/match.v | 5 +++ 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/vlib/v/checker/tests/match_return_mismatch_type_err.out b/vlib/v/checker/tests/match_return_mismatch_type_err.out index 1bcd963845b8ea..9c4bc3cfa41109 100644 --- a/vlib/v/checker/tests/match_return_mismatch_type_err.out +++ b/vlib/v/checker/tests/match_return_mismatch_type_err.out @@ -1,3 +1,10 @@ +vlib/v/checker/tests/match_return_mismatch_type_err.vv:27:6: warning: cannot assign a reference to a value (this will be an error soon) left=string false right=string true ptr=true + 25 | + 26 | mut res := '' + 27 | res = match any { + | ^ + 28 | string { &any } + 29 | else { &variable } vlib/v/checker/tests/match_return_mismatch_type_err.vv:4:10: error: return type mismatch, it should be `string` 2 | a := match 1 { 3 | 1 { 'aa' } @@ -18,18 +25,18 @@ vlib/v/checker/tests/match_return_mismatch_type_err.vv:23:10: error: return type 23 | else { &variable } | ^ 24 | } - 25 | } -vlib/v/checker/tests/match_return_mismatch_type_err.vv:36:10: error: return type mismatch, it should be `&string` - 34 | _ = match any { - 35 | string { &any } - 36 | else { variable } + 25 | +vlib/v/checker/tests/match_return_mismatch_type_err.vv:43:10: error: return type mismatch, it should be `&string` + 41 | _ = match any { + 42 | string { &any } + 43 | else { variable } | ~~~~~~~~ - 37 | } - 38 | -vlib/v/checker/tests/match_return_mismatch_type_err.vv:41:10: error: return type mismatch, it should be `string` - 39 | _ = match any { - 40 | string { any } - 41 | else { &variable } + 44 | } + 45 | +vlib/v/checker/tests/match_return_mismatch_type_err.vv:48:10: error: return type mismatch, it should be `string` + 46 | _ = match any { + 47 | string { any } + 48 | else { &variable } | ^ - 42 | } - 43 | } + 49 | } + 50 | } diff --git a/vlib/v/checker/tests/match_return_mismatch_type_err.vv b/vlib/v/checker/tests/match_return_mismatch_type_err.vv index 24c8ebecca33a0..48c547d8dc8bdf 100644 --- a/vlib/v/checker/tests/match_return_mismatch_type_err.vv +++ b/vlib/v/checker/tests/match_return_mismatch_type_err.vv @@ -22,6 +22,13 @@ fn returns_both_interface_and_non_interface() { string { any } else { &variable } } + + mut res := '' + res = match any { + string { &any } + else { &variable } + } + println(res) } // for test the returns both sumtype or non-sumtype diff --git a/vlib/v/gen/c/match.v b/vlib/v/gen/c/match.v index a4fee09ad8a52d..00747b1b4083f1 100644 --- a/vlib/v/gen/c/match.v +++ b/vlib/v/gen/c/match.v @@ -237,6 +237,11 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str if mut stmt.expr is ast.Ident && stmt.expr.obj is ast.Var && g.is_interface_var(stmt.expr.obj) { g.inside_casting_to_str = true + } else if mut stmt.expr is ast.PrefixExpr && stmt.expr.right is ast.Ident { + ident := stmt.expr.right as ast.Ident + if ident.obj is ast.Var && g.is_interface_var(ident.obj) { + g.inside_casting_to_str = true + } } } }