Skip to content

Commit c9df064

Browse files
authored
checker, cgen: fix match branches return type ref mismatch, when the return type exists as interface or sumtype (fix #16203) (#19806)
1 parent bcc2bfa commit c9df064

File tree

7 files changed

+171
-20
lines changed

7 files changed

+171
-20
lines changed

vlib/v/checker/checker.v

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3564,9 +3564,7 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
35643564
typ = c.expr(mut obj.expr)
35653565
}
35663566
}
3567-
if c.inside_casting_to_str && obj.orig_type != 0
3568-
&& c.table.sym(obj.orig_type).kind == .interface_
3569-
&& c.table.sym(obj.smartcasts.last()).kind != .interface_ {
3567+
if c.inside_casting_to_str && c.is_interface_var(obj) {
35703568
typ = typ.deref()
35713569
}
35723570
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 {
40284026
&& (node.obj as ast.Var).ct_type_var != .no_comptime
40294027
}
40304028

4029+
[inline]
4030+
fn (mut c Checker) is_interface_var(var ast.ScopeObject) bool {
4031+
return var is ast.Var && var.orig_type != 0 && c.table.sym(var.orig_type).kind == .interface_
4032+
&& c.table.sym(var.smartcasts.last()).kind != .interface_
4033+
}
4034+
40314035
fn (mut c Checker) mark_as_referenced(mut node ast.Expr, as_interface bool) {
40324036
match mut node {
40334037
ast.Ident {

vlib/v/checker/match.v

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,51 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
7474
ret_type = node.expected_type
7575
} else {
7676
ret_type = expr_type
77+
if expr_type.is_ptr() {
78+
if stmt.expr is ast.Ident && stmt.expr.obj is ast.Var
79+
&& c.is_interface_var(stmt.expr.obj) {
80+
ret_type = expr_type.deref()
81+
} else if mut stmt.expr is ast.PrefixExpr
82+
&& stmt.expr.right is ast.Ident {
83+
ident := stmt.expr.right as ast.Ident
84+
if ident.obj is ast.Var && c.is_interface_var(ident.obj) {
85+
ret_type = expr_type.deref()
86+
}
87+
}
88+
}
89+
}
90+
} else {
91+
if node.is_expr && ret_type.idx() != expr_type.idx() {
92+
if (node.expected_type.has_flag(.option)
93+
|| node.expected_type.has_flag(.result))
94+
&& c.table.sym(stmt.typ).kind == .struct_
95+
&& c.type_implements(stmt.typ, ast.error_type, node.pos) {
96+
stmt.expr = ast.CastExpr{
97+
expr: stmt.expr
98+
typname: 'IError'
99+
typ: ast.error_type
100+
expr_type: stmt.typ
101+
pos: node.pos
102+
}
103+
stmt.typ = ast.error_type
104+
} else {
105+
c.check_match_branch_last_stmt(stmt, ret_type, expr_type)
106+
}
77107
}
78-
} else if node.is_expr && ret_type.idx() != expr_type.idx() {
79-
if (node.expected_type.has_flag(.option)
80-
|| node.expected_type.has_flag(.result))
81-
&& c.table.sym(stmt.typ).kind == .struct_
82-
&& c.type_implements(stmt.typ, ast.error_type, node.pos) {
83-
stmt.expr = ast.CastExpr{
84-
expr: stmt.expr
85-
typname: 'IError'
86-
typ: ast.error_type
87-
expr_type: stmt.typ
88-
pos: node.pos
108+
if node.is_expr && stmt.typ != ast.error_type {
109+
ret_sym := c.table.sym(ret_type)
110+
stmt_sym := c.table.sym(stmt.typ)
111+
if ret_sym.kind !in [.sum_type, .interface_]
112+
&& stmt_sym.kind in [.sum_type, .interface_] {
113+
c.error('return type mismatch, it should be `${ret_sym.name}`',
114+
stmt.pos)
115+
}
116+
if ret_type.nr_muls() != stmt.typ.nr_muls()
117+
&& stmt.typ.idx() !in [ast.voidptr_type_idx, ast.nil_type_idx] {
118+
type_name := '&'.repeat(ret_type.nr_muls()) + ret_sym.name
119+
c.error('return type mismatch, it should be `${type_name}`',
120+
stmt.pos)
89121
}
90-
stmt.typ = ast.error_type
91-
} else {
92-
c.check_match_branch_last_stmt(stmt, ret_type, expr_type)
93122
}
94123
}
95124
} else if stmt !in [ast.Return, ast.BranchStmt] {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,42 @@
1+
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
2+
25 |
3+
26 | mut res := ''
4+
27 | res = match any {
5+
| ^
6+
28 | string { &any }
7+
29 | else { &variable }
18
vlib/v/checker/tests/match_return_mismatch_type_err.vv:4:10: error: return type mismatch, it should be `string`
29
2 | a := match 1 {
310
3 | 1 { 'aa' }
411
4 | else { 22 }
512
| ~~
613
5 | }
714
6 | println(a)
15+
vlib/v/checker/tests/match_return_mismatch_type_err.vv:18:10: error: return type mismatch, it should be `&string`
16+
16 | _ = match any {
17+
17 | string { &any }
18+
18 | else { variable }
19+
| ~~~~~~~~
20+
19 | }
21+
20 |
22+
vlib/v/checker/tests/match_return_mismatch_type_err.vv:23:10: error: return type mismatch, it should be `string`
23+
21 | _ = match any {
24+
22 | string { any }
25+
23 | else { &variable }
26+
| ^
27+
24 | }
28+
25 |
29+
vlib/v/checker/tests/match_return_mismatch_type_err.vv:43:10: error: return type mismatch, it should be `&string`
30+
41 | _ = match any {
31+
42 | string { &any }
32+
43 | else { variable }
33+
| ~~~~~~~~
34+
44 | }
35+
45 |
36+
vlib/v/checker/tests/match_return_mismatch_type_err.vv:48:10: error: return type mismatch, it should be `string`
37+
46 | _ = match any {
38+
47 | string { any }
39+
48 | else { &variable }
40+
| ^
41+
49 | }
42+
50 | }

vlib/v/checker/tests/match_return_mismatch_type_err.vv

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,46 @@ fn main() {
55
}
66
println(a)
77
}
8+
9+
// for test the returns both interface or non-interface
10+
interface IAny {}
11+
12+
fn returns_both_interface_and_non_interface() {
13+
any := IAny('abc')
14+
variable := ''
15+
16+
_ = match any {
17+
string { &any }
18+
else { variable }
19+
}
20+
21+
_ = match any {
22+
string { any }
23+
else { &variable }
24+
}
25+
26+
mut res := ''
27+
res = match any {
28+
string { &any }
29+
else { &variable }
30+
}
31+
println(res)
32+
}
33+
34+
// for test the returns both sumtype or non-sumtype
35+
type SAny = int | string
36+
37+
fn returns_both_sumtype_and_non_sumtype() {
38+
any := SAny('abc')
39+
variable := ''
40+
41+
_ = match any {
42+
string { &any }
43+
else { variable }
44+
}
45+
46+
_ = match any {
47+
string { any }
48+
else { &variable }
49+
}
50+
}

vlib/v/gen/c/cgen.v

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4349,6 +4349,12 @@ fn (mut g Gen) get_const_name(node ast.Ident) string {
43494349
}
43504350
}
43514351

4352+
[inline]
4353+
fn (mut g Gen) is_interface_var(var ast.ScopeObject) bool {
4354+
return var is ast.Var && var.orig_type != 0 && g.table.sym(var.orig_type).kind == .interface_
4355+
&& g.table.sym(var.smartcasts.last()).kind != .interface_
4356+
}
4357+
43524358
fn (mut g Gen) ident(node ast.Ident) {
43534359
prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once
43544360
g.prevent_sum_type_unwrapping_once = false
@@ -4485,9 +4491,7 @@ fn (mut g Gen) ident(node ast.Ident) {
44854491
g.write('(')
44864492
if obj_sym.kind == .sum_type && !is_auto_heap {
44874493
g.write('*')
4488-
} else if g.inside_casting_to_str && node.obj.orig_type != 0
4489-
&& g.table.sym(node.obj.orig_type).kind == .interface_
4490-
&& g.table.sym(node.obj.smartcasts.last()).kind != .interface_ {
4494+
} else if g.inside_casting_to_str && g.is_interface_var(node.obj) {
44914495
g.write('*')
44924496
}
44934497
}

vlib/v/gen/c/match.v

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,23 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str
230230
if is_expr && tmp_var.len > 0 && g.table.sym(node.return_type).kind == .sum_type {
231231
g.expected_cast_type = node.return_type
232232
}
233+
inside_casting_to_str_old := g.inside_casting_to_str
234+
if is_expr && branch.stmts.len > 0 {
235+
mut stmt := branch.stmts.last()
236+
if mut stmt is ast.ExprStmt {
237+
if mut stmt.expr is ast.Ident && stmt.expr.obj is ast.Var
238+
&& g.is_interface_var(stmt.expr.obj) {
239+
g.inside_casting_to_str = true
240+
} else if mut stmt.expr is ast.PrefixExpr && stmt.expr.right is ast.Ident {
241+
ident := stmt.expr.right as ast.Ident
242+
if ident.obj is ast.Var && g.is_interface_var(ident.obj) {
243+
g.inside_casting_to_str = true
244+
}
245+
}
246+
}
247+
}
233248
g.stmts_with_tmp_var(branch.stmts, tmp_var)
249+
g.inside_casting_to_str = inside_casting_to_str_old
234250
g.expected_cast_type = 0
235251
if g.inside_ternary == 0 {
236252
g.writeln('}')

vlib/v/tests/match_test.v

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,23 @@ fn test_noreturn() {
301301
}
302302
}
303303
}
304+
305+
// for test the returns both interface and non-interface
306+
interface Any {}
307+
308+
fn test_returns_both_interface_and_non_interface() {
309+
any := Any('abc')
310+
311+
mut res := match any {
312+
string { any }
313+
else { 'literal' }
314+
}
315+
assert res == 'abc'
316+
317+
variable := ''
318+
res = match any {
319+
string { any }
320+
else { variable }
321+
}
322+
assert res == 'abc'
323+
}

0 commit comments

Comments
 (0)