vxx / vlib / v / checker / match.v
1194 lines · 1170 sloc · 39.53 KB · 5ad37a5a8656c438c68dabd31c9d36467390e046
Raw
1module checker
2
3import v.ast
4import v.util
5import v.token
6import strings
7
8fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
9 if !node.is_comptime {
10 node.is_expr = c.expected_type != ast.void_type
11 }
12 node.expected_type = c.expected_type
13 if mut node.cond is ast.ParExpr && !c.pref.translated && !c.file.is_translated {
14 c.warn('unnecessary `()` in `match` condition, use `match expr {` instead of `match (expr) {`.',
15 node.cond.pos)
16 }
17 expr_required := c.expected_type != ast.void_type
18 || (node.is_comptime && node.is_expr && c.fn_level > 0)
19 if node.is_expr || expr_required {
20 c.expected_expr_type = c.expected_type
21 defer(fn) {
22 c.expected_expr_type = ast.void_type
23 }
24 }
25 mut cond_type := ast.void_type
26 if node.is_comptime {
27 cond_expr := node.cond
28 if cond_expr is ast.AtExpr {
29 mut checked_cond := node.cond
30 cond_type = c.expr(mut checked_cond)
31 node.cond = checked_cond
32 } else {
33 // for field.name and generic type `T`
34 is_selector_cond := node.cond is ast.SelectorExpr
35 if is_selector_cond {
36 mut selector_cond := node.cond
37 c.expr(mut selector_cond)
38 node.cond = selector_cond
39 }
40 cond_type = c.get_expr_type(node.cond)
41 }
42 } else {
43 cond_type = c.expr(mut node.cond)
44 }
45 // we setting this here rather than at the end of the method
46 // since it is used in c.match_exprs() it saves checking twice
47 node.cond_type = ast.mktyp(cond_type)
48 if (node.cond is ast.Ident && node.cond.is_mut)
49 || (node.cond is ast.SelectorExpr && node.cond.is_mut) {
50 if node.is_comptime {
51 c.error('`\$match` condition `${node.cond}` can not be mutable', node.cond.pos())
52 } else {
53 c.fail_if_immutable(mut node.cond)
54 }
55 }
56 if !c.ensure_type_exists(node.cond_type, node.pos) {
57 return ast.void_type
58 }
59 if node.cond_type == 0 {
60 return ast.void_type
61 }
62 c.check_expr_option_or_result_call(node.cond, node.cond_type)
63 cond_type_sym := c.table.sym(node.cond_type)
64 cond_final_sym := c.table.final_sym(node.cond_type)
65 cond_is_option := node.cond_type.has_flag(.option)
66 node.is_sum_type = cond_final_sym.kind in [.interface, .sum_type]
67 c.match_exprs(mut node, cond_type_sym, cond_final_sym)
68 c.expected_type = node.cond_type
69 mut ret_type_needs_inference := true
70 mut infer_cast_type := ast.void_type
71 mut need_explicit_cast := false
72 mut ret_type := ast.void_type
73 mut nbranches_with_return := 0
74 mut nbranches_without_return := 0
75 mut must_be_option := false
76 comptime_branch_context_str := if node.is_comptime { c.gen_branch_context_string() } else { '' }
77 mut comptime_match_branch_result := false
78 mut comptime_match_found_branch := false
79 mut comptime_match_cond_value := ''
80 if node.is_comptime {
81 if node.cond in [ast.ComptimeType, ast.TypeNode] || (node.cond is ast.Ident
82 && (c.is_generic_ident(node.cond.name)))
83 || (node.cond is ast.SelectorExpr && node.cond.gkind_field in [.typ, .unaliased_typ]) {
84 // must be a type `$match`
85 c.inside_x_matches_type = true
86 } else {
87 // a value `$match`, eval the `node.cond` first
88 c.expr(mut node.cond)
89 if !c.type_resolver.is_generic_param_var(node.cond) {
90 match mut node.cond {
91 ast.StringLiteral, ast.AtExpr {
92 comptime_match_cond_value = node.cond.val
93 }
94 ast.IntegerLiteral {
95 comptime_match_cond_value = node.cond.val.str()
96 }
97 ast.BoolLiteral {
98 comptime_match_cond_value = node.cond.val.str()
99 }
100 ast.Ident {
101 mut cond_expr := c.find_definition(node.cond) or {
102 c.error(err.msg(), node.cond.pos)
103 return ast.void_type
104 }
105 match mut cond_expr {
106 ast.StringLiteral {
107 comptime_match_cond_value = cond_expr.val
108 }
109 ast.IntegerLiteral {
110 comptime_match_cond_value = cond_expr.val.str()
111 }
112 ast.BoolLiteral {
113 comptime_match_cond_value = cond_expr.val.str()
114 }
115 else {
116 c.error('`${node.cond}` is not a string/int/bool literal.',
117 node.cond.pos)
118 return ast.void_type
119 }
120 }
121 }
122 ast.SelectorExpr {
123 if c.comptime.inside_comptime_for && node.cond.field_name in ['name', 'typ'] {
124 // hack: `typ` is just for bypass the error test, because we don't know it is a type match or a value match righ now
125 comptime_match_cond_value = c.comptime.comptime_for_field_value.name
126 } else if mut node.cond.expr is ast.Ident
127 && node.cond.gkind_field in [.typ, .unaliased_typ] {
128 left_type := c.get_expr_type(node.cond.expr)
129 comptime_match_cond_value = c.table.type_to_str(left_type)
130 } else {
131 c.error('`${node.cond}` is not `\$for` field.name.', node.cond.pos)
132 return ast.void_type
133 }
134 }
135 else {
136 c.error('`\$match` cond only support string/int/bool/ident.',
137 node.cond.pos())
138 return ast.void_type
139 }
140 }
141 }
142 }
143 }
144 for i, mut branch in node.branches {
145 if node.is_comptime {
146 // `idx_str` is composed of two parts:
147 // The first part represents the current context of the branch statement, `comptime_branch_context_str`, formatted like `T=int,X=string,method.name=json`
148 // The second part is the branch's id.
149 // This format must match what is in `cgen`.
150 if branch.id == 0 {
151 // this is a new branch, alloc a new id for it
152 c.cur_ct_id++
153 branch.id = c.cur_ct_id
154 }
155 mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
156 if c.comptime.inside_comptime_for && c.comptime.comptime_for_field_var != '' {
157 idx_str += '|field_type=${c.comptime.comptime_for_field_type}|'
158 }
159 mut c_str := ''
160 if !branch.is_else {
161 if c.inside_x_matches_type {
162 // type $match
163 for expr in branch.exprs {
164 if mut node.cond is ast.ComptimeType {
165 // $match $int { a {}
166 branch_type := c.get_expr_type(expr)
167 comptime_match_branch_result = c.type_resolver.is_comptime_type(branch_type,
168 node.cond)
169 c_str = '${expr} == ${c.table.type_to_str(branch_type)}'
170 } else {
171 is_function := c.table.final_sym(node.cond_type).kind == .function
172 if !is_function {
173 // $match a { $int {}
174 comptime_match_branch_result = c.check_compatible_types(node.cond_type,
175 '${node.cond}', expr)
176 c_str = '${c.table.type_to_str(node.cond_type)} == ${expr}'
177 } else {
178 // $match T { FnType {} }
179 branch_type := c.get_expr_type(expr)
180 comptime_match_branch_result = c.table.type_to_str(node.cond_type) == c.table.type_to_str(branch_type)
181 c_str = '${comptime_match_branch_result} == true'
182 }
183 }
184 if comptime_match_branch_result {
185 break
186 }
187 }
188 } else {
189 // value $match
190 for mut expr in branch.exprs {
191 match mut expr {
192 ast.Ident {
193 mut branch_expr := c.find_definition(expr) or {
194 c.error(err.msg(), expr.pos)
195 return ast.void_type
196 }
197 match mut branch_expr {
198 ast.StringLiteral {
199 comptime_match_branch_result = branch_expr.val == comptime_match_cond_value
200 }
201 ast.IntegerLiteral {
202 comptime_match_branch_result = branch_expr.val.str() == comptime_match_cond_value
203 }
204 ast.BoolLiteral {
205 comptime_match_branch_result = branch_expr.val.str() == comptime_match_cond_value
206 }
207 else {
208 c.error('`${expr}` is not a string/int/bool literal.',
209 expr.pos)
210 return ast.void_type
211 }
212 }
213
214 c_str = '${node.cond} == ${expr.name}'
215 }
216 ast.SelectorExpr {}
217 ast.StringLiteral {
218 c_str = '${node.cond} == ${expr}'
219 comptime_match_branch_result = comptime_match_cond_value == expr.val
220 }
221 ast.IntegerLiteral {
222 c_str = '${node.cond} == ${expr.val}'
223 comptime_match_branch_result = comptime_match_cond_value == expr.val.str()
224 }
225 ast.BoolLiteral {
226 c_str = '${node.cond} == ${expr.val}'
227 comptime_match_branch_result = comptime_match_cond_value == expr.val.str()
228 }
229 else {
230 c.error('`\$match` branch only support string/int/bool types',
231 node.cond.pos())
232 return ast.void_type
233 }
234 }
235
236 if comptime_match_branch_result {
237 break
238 }
239 }
240 }
241 if comptime_match_branch_result {
242 comptime_match_found_branch = true
243 }
244 // set `comptime_is_true` which can be used by `cgen`
245 c.table.comptime_is_true[idx_str] = ast.ComptTimeCondResult{
246 val: comptime_match_branch_result
247 c_str: c_str
248 }
249 } else {
250 comptime_match_branch_result = !comptime_match_found_branch
251 c.table.comptime_is_true[idx_str] = ast.ComptTimeCondResult{
252 val: comptime_match_branch_result
253 c_str: ''
254 }
255 }
256 }
257
258 for mut expr in branch.exprs {
259 // (expr) => expr
260 expr = expr.remove_par()
261 }
262
263 if !c.pref.translated && !c.file.is_translated {
264 mut did_warn_self_comparison_branch := false
265 // check for always true/false match branch
266 for mut expr in branch.exprs {
267 mut check_expr := ast.InfixExpr{
268 op: .eq
269 left: node.cond
270 right: expr
271 }
272 t_expr := c.checker_transformer.expr(mut check_expr)
273 if t_expr is ast.BoolLiteral {
274 if t_expr.val {
275 c.note('match is always true', expr.pos())
276 if c.is_always_true_self_comparison(ast.Expr(ast.InfixExpr{
277 op: .eq
278 left: node.cond
279 right: expr
280 left_type: node.cond_type
281 right_type: node.cond_type
282 })) && i < node.branches.len - 1 && !did_warn_self_comparison_branch {
283 c.warn('self-comparison match branch is always true; following branches may be unreachable',
284 expr.pos())
285 did_warn_self_comparison_branch = true
286 }
287 } else {
288 c.note('match is always false', expr.pos())
289 }
290 }
291 }
292 }
293
294 if !node.is_comptime || (node.is_comptime && comptime_match_branch_result) {
295 if node.is_expr {
296 c.stmts_ending_with_expression(mut branch.stmts, c.expected_or_type)
297 } else {
298 c.stmts(mut branch.stmts)
299 }
300 }
301 c.smartcast_mut_pos = token.Pos{}
302 c.smartcast_cond_pos = token.Pos{}
303 if node.is_expr {
304 if branch.stmts.len == 0 && ret_type != ast.void_type {
305 c.error('`match` expression requires an expression as the last statement of every branch',
306 branch.branch_pos)
307 }
308 }
309 if !branch.is_else && cond_is_option && branch.exprs.any(it !is ast.None) {
310 c.error('`match` expression with Option type only checks against `none`, to match its value you must unwrap it first `var?`',
311 branch.pos)
312 }
313 if cond_type_sym.kind == .none {
314 c.error('`none` cannot be a match condition', node.pos)
315 }
316 if branch.stmts.len > 0 && node.is_expr
317 && (!node.is_comptime || (node.is_comptime && comptime_match_branch_result)) {
318 mut stmt := branch.stmts.last()
319 if mut stmt is ast.ExprStmt {
320 // A trailing `spawn`/`go` is the value of this match-expression branch,
321 // so it must produce a usable thread handle (waiter, no detach),
322 // like the assignment form `t := spawn f()`. See issue #27485.
323 if mut stmt.expr is ast.SpawnExpr {
324 stmt.expr.is_expr = true
325 } else if mut stmt.expr is ast.GoExpr {
326 stmt.expr.is_expr = true
327 }
328 c.expected_type = if c.expected_expr_type != ast.void_type {
329 c.expected_expr_type
330 } else {
331 node.expected_type
332 }
333 branch.is_comptime_err = stmt.expr is ast.ComptimeCall
334 && stmt.expr.kind in [.compile_error, .compile_warn]
335 expr_type := if branch.is_comptime_err { c.expected_type } else { c.unwrap_generic(if stmt.expr is ast.CallExpr {
336 stmt.typ
337 } else {
338 c.expr(mut stmt.expr)
339 })
340 }
341 unwrapped_expected_type := c.unwrap_generic(node.expected_type)
342 must_be_option = must_be_option || expr_type == ast.none_type
343 stmt.typ = expr_type
344 if ret_type_needs_inference && !is_noreturn_callexpr(stmt.expr) {
345 if unwrapped_expected_type.has_option_or_result()
346 || c.table.type_kind(unwrapped_expected_type) in [.sum_type, .interface, .multi_return] {
347 c.check_match_branch_last_stmt(mut stmt, unwrapped_expected_type, expr_type)
348 if c.table.type_kind(unwrapped_expected_type) == .multi_return
349 && c.table.type_kind(expr_type) == .multi_return
350 && !c.can_use_expected_multi_return_expr_type(expr_type, unwrapped_expected_type, stmt.expr) {
351 ret_type = expr_type
352 c.expected_expr_type = expr_type
353 } else {
354 ret_type = node.expected_type
355 }
356 } else {
357 ret_type = expr_type
358 if expr_type.is_ptr() {
359 if stmt.expr is ast.Ident && stmt.expr.obj is ast.Var
360 && c.table.is_interface_var(stmt.expr.obj) {
361 ret_type = expr_type.deref()
362 } else if mut stmt.expr is ast.PrefixExpr
363 && stmt.expr.right is ast.Ident {
364 ident := stmt.expr.right as ast.Ident
365 if ident.obj is ast.Var && c.table.is_interface_var(ident.obj) {
366 ret_type = expr_type.deref()
367 }
368 }
369 }
370 c.expected_expr_type = expr_type
371 }
372 infer_cast_type = stmt.typ
373 if mut stmt.expr is ast.CastExpr {
374 need_explicit_cast = true
375 infer_cast_type = stmt.expr.typ
376 }
377 ret_type_needs_inference = false
378 } else if !ret_type_needs_inference {
379 if ret_type.idx() != expr_type.idx() {
380 if unwrapped_expected_type.has_option_or_result()
381 && c.table.sym(stmt.typ).kind == .struct
382 && !c.check_types(expr_type, c.unwrap_generic(ret_type))
383 && c.type_implements(stmt.typ, ast.error_type, node.pos) {
384 stmt.expr = ast.CastExpr{
385 expr: stmt.expr
386 typname: 'IError'
387 typ: ast.error_type
388 expr_type: stmt.typ
389 pos: node.pos
390 }
391 stmt.typ = ast.error_type
392 } else {
393 c.check_match_branch_last_stmt(mut stmt, c.unwrap_generic(ret_type),
394 expr_type)
395 if ret_type.is_number() && expr_type.is_number() && !c.inside_return {
396 ret_type = c.promote_num(ret_type, expr_type)
397 }
398 }
399 }
400 if must_be_option && ret_type == ast.none_type && expr_type != ret_type {
401 ret_type = expr_type.set_flag(.option)
402 }
403 if stmt.typ != ast.error_type && !is_noreturn_callexpr(stmt.expr) {
404 ret_sym := c.table.sym(ret_type)
405 stmt_sym := c.table.sym(stmt.typ)
406 if ret_sym.kind !in [.sum_type, .interface]
407 && stmt_sym.kind in [.sum_type, .interface] {
408 c.error('return type mismatch, it should be `${ret_sym.name}`, but it is instead `${c.table.type_to_str(expr_type)}`',
409 stmt.pos)
410 }
411 if ret_type.nr_muls() != stmt.typ.nr_muls()
412 && stmt.typ.idx() !in [ast.voidptr_type_idx, ast.nil_type_idx] {
413 type_name := '&'.repeat(ret_type.nr_muls()) + ret_sym.name
414 c.error('return type mismatch, it should be `${type_name}`, but it is instead `${c.table.type_to_str(expr_type)}`',
415 stmt.pos)
416 }
417 }
418 if !node.is_sum_type {
419 if mut stmt.expr is ast.CastExpr {
420 expr_typ_sym := c.table.sym(stmt.expr.typ)
421 if need_explicit_cast {
422 if infer_cast_type != stmt.expr.typ
423 && expr_typ_sym.kind !in [.interface, .sum_type] {
424 c.error('the type of the last expression in the first match branch was an explicit `${c.table.type_to_str(infer_cast_type)}`, not `${c.table.type_to_str(stmt.expr.typ)}`',
425 stmt.pos)
426 }
427 } else {
428 if infer_cast_type != stmt.expr.typ
429 && expr_typ_sym.kind !in [.interface, .sum_type]
430 && c.promote_num(stmt.expr.typ, ast.int_type) != ast.int_type {
431 c.error('the type of the last expression of the first match branch was `${c.table.type_to_str(infer_cast_type)}`, which is not compatible with `${c.table.type_to_str(stmt.expr.typ)}`',
432 stmt.pos)
433 }
434 }
435 } else {
436 if mut stmt.expr is ast.IntegerLiteral {
437 cast_type_sym := c.table.sym(infer_cast_type)
438 num := stmt.expr.val.i64()
439 mut needs_explicit_cast := false
440
441 match cast_type_sym.kind {
442 .u8 {
443 if !(num >= min_u8 && num <= max_u8) {
444 needs_explicit_cast = true
445 }
446 }
447 .u16 {
448 if !(num >= min_u16 && num <= max_u16) {
449 needs_explicit_cast = true
450 }
451 }
452 .u32 {
453 if !(num >= min_u32 && num <= max_u32) {
454 needs_explicit_cast = true
455 }
456 }
457 .u64 {
458 if !(num >= min_u64 && num <= max_u64) {
459 needs_explicit_cast = true
460 }
461 }
462 .i8 {
463 if !(num >= min_i32 && num <= max_i32) {
464 needs_explicit_cast = true
465 }
466 }
467 .i16 {
468 if !(num >= min_i16 && num <= max_i16) {
469 needs_explicit_cast = true
470 }
471 }
472 .i32 {
473 if !(num >= min_i32 && num <= max_i32) {
474 needs_explicit_cast = true
475 }
476 }
477 .int {
478 $if new_int ? && x64 {
479 if !(num >= min_i64 && num <= max_i64) {
480 needs_explicit_cast = true
481 }
482 } $else {
483 if !(num >= min_i32 && num <= max_i32) {
484 needs_explicit_cast = true
485 }
486 }
487 }
488 .i64 {
489 if !(num >= min_i64 && num <= max_i64) {
490 needs_explicit_cast = true
491 }
492 }
493 .int_literal {
494 needs_explicit_cast = false
495 }
496 else {}
497 }
498
499 if needs_explicit_cast {
500 c.error('${num} does not fit the range of `${c.table.type_to_str(infer_cast_type)}`',
501 stmt.pos)
502 }
503 }
504 }
505 }
506 }
507 } else if stmt !in [ast.Return, ast.BranchStmt] {
508 if ret_type != ast.void_type {
509 c.error('`match` expression requires an expression as the last statement of every branch',
510 stmt.pos)
511 }
512 } else if c.inside_return && mut stmt is ast.Return && ret_type == ast.void_type {
513 ret_type = if stmt.types.len > 0 { stmt.types[0] } else { c.expected_type }
514 }
515 }
516 if !node.is_comptime || (node.is_comptime && comptime_match_branch_result) {
517 // ret_type_needs_inference is set to false in the ExprStmt branch above
518 // when a non-noreturn expression provides the type
519 }
520 if node.is_comptime {
521 // branches may not have been processed by c.stmts()
522 if c.has_top_return(branch.stmts) {
523 nbranches_with_return++
524 } else {
525 nbranches_without_return++
526 }
527 } else {
528 if has_return := c.has_return(branch.stmts) {
529 if has_return {
530 nbranches_with_return++
531 } else {
532 nbranches_without_return++
533 }
534 }
535 }
536 }
537 if nbranches_with_return > 0 {
538 if nbranches_with_return == node.branches.len {
539 // an exhaustive match, and all branches returned
540 c.returns = true
541 }
542 if nbranches_without_return > 0 {
543 // some of the branches did not return
544 c.returns = false
545 }
546 }
547 if ret_type == ast.none_type {
548 c.error('invalid match expression, must supply at least one value other than `none`',
549 node.pos)
550 }
551 node.return_type = if must_be_option { ret_type.set_flag(.option) } else { ret_type }
552 cond_var := c.get_base_name(&node.cond)
553 if cond_var != '' {
554 mut cond_is_auto_heap := false
555 for branch in node.branches {
556 if v := branch.scope.find_var(cond_var) {
557 if v.is_auto_heap {
558 cond_is_auto_heap = true
559 break
560 }
561 }
562 }
563 if cond_is_auto_heap {
564 for branch in node.branches {
565 mut v := branch.scope.find_var(cond_var) or { continue }
566 v.is_auto_heap = true
567 }
568 }
569 }
570 return node.return_type
571}
572
573fn (mut c Checker) check_match_branch_last_stmt(mut last_stmt ast.ExprStmt, ret_type ast.Type, expr_type ast.Type) {
574 if !c.check_types(ret_type, expr_type) && !c.check_types(expr_type, ret_type) {
575 ret_sym := c.table.sym(ret_type)
576 expr_sym := c.table.sym(expr_type)
577 if ret_sym.kind == .interface {
578 if c.type_implements(expr_type, ret_type, last_stmt.pos) {
579 if !expr_type.is_any_kind_of_pointer() && expr_sym.kind != .interface
580 && !c.inside_unsafe {
581 c.mark_as_referenced(mut &last_stmt.expr, true)
582 }
583 }
584 return
585 }
586 is_noreturn := is_noreturn_callexpr(last_stmt.expr)
587 if !(ret_sym.kind == .sum_type && (ret_type.has_flag(.generic)
588 || c.table.is_sumtype_or_in_variant(ret_type, expr_type))) && !is_noreturn {
589 if expr_sym.kind == .multi_return && ret_sym.kind == .multi_return {
590 if c.can_use_expected_multi_return_expr_type(expr_type, ret_type, last_stmt.expr) {
591 return
592 }
593 }
594 if expr_type != ast.none_type && ret_type != ast.none_type {
595 c.error('return type mismatch, it should be `${ret_sym.name}`, but it is instead `${c.table.type_to_str(expr_type)}`',
596 last_stmt.pos)
597 }
598 }
599 } else if expr_type == ast.void_type && ret_type.idx() == ast.void_type_idx
600 && ret_type.has_option_or_result() {
601 c.error('`${last_stmt.expr}` used as value', last_stmt.pos)
602 }
603}
604
605fn char_literal_number_value(value string) ?i64 {
606 if value.len == 2 && value[0] == `\\` {
607 return match value[1] {
608 `a` { 7 }
609 `b` { 8 }
610 `t` { 9 }
611 `n` { 10 }
612 `v` { 11 }
613 `f` { 12 }
614 `r` { 13 }
615 `e` { 27 }
616 `$` { 36 }
617 `"` { 34 }
618 `'` { 39 }
619 `?` { 63 }
620 `@` { 64 }
621 `\\` { 92 }
622 `\`` { 96 }
623 `{` { 123 }
624 `}` { 125 }
625 else { none }
626 }
627 }
628 runes := value.runes()
629 if runes.len == 1 {
630 return runes[0]
631 }
632 return none
633}
634
635fn (mut c Checker) get_comptime_number_value(mut expr ast.Expr) ?i64 {
636 if mut expr is ast.ParExpr {
637 return c.get_comptime_number_value(mut expr.expr)
638 }
639 if mut expr is ast.PrefixExpr && expr.op == .minus {
640 return -c.get_comptime_number_value(mut expr.right)?
641 }
642 if mut expr is ast.CharLiteral {
643 return char_literal_number_value(expr.val)
644 }
645 if mut expr is ast.IntegerLiteral {
646 return expr.val.i64()
647 }
648 if mut expr is ast.CastExpr {
649 return c.get_comptime_number_value(mut expr.expr)
650 }
651 if mut expr is ast.Ident {
652 if mut obj := c.table.global_scope.find_const(expr.full_name()) {
653 if obj.typ == 0 {
654 obj.typ = c.expr(mut obj.expr)
655 }
656 return c.get_comptime_number_value(mut obj.expr)
657 }
658 }
659 return none
660}
661
662fn (mut c Checker) get_match_case_int_key(mut expr ast.Expr, cond_sym ast.TypeSymbol) ?string {
663 if !cond_sym.is_int() {
664 return none
665 }
666 // Only resolve literal values for dedup, not const idents.
667 // Different const names with the same value should be allowed.
668 if value := c.get_match_case_literal_value(mut expr) {
669 return value.str()
670 }
671 return none
672}
673
674// Like get_comptime_number_value but does NOT resolve const idents.
675// This ensures different named consts with the same value are not flagged as duplicates.
676fn (mut c Checker) get_match_case_literal_value(mut expr ast.Expr) ?i64 {
677 if mut expr is ast.ParExpr {
678 return c.get_match_case_literal_value(mut expr.expr)
679 }
680 if mut expr is ast.PrefixExpr && expr.op == .minus {
681 return -c.get_match_case_literal_value(mut expr.right)?
682 }
683 if mut expr is ast.CharLiteral {
684 return char_literal_number_value(expr.val)
685 }
686 if mut expr is ast.IntegerLiteral {
687 return expr.val.i64()
688 }
689 if mut expr is ast.CastExpr {
690 return c.get_match_case_literal_value(mut expr.expr)
691 }
692 return none
693}
694
695fn (mut c Checker) match_sumtype_has_variant(parent ast.Type, variant ast.Type) bool {
696 if c.table.sumtype_has_variant_recursive(parent, variant, true) {
697 return true
698 }
699 if c.table.sym(parent).kind == .sum_type {
700 return false
701 }
702 for candidate in c.match_sumtype_matchable_variants(parent) {
703 if c.match_sumtype_variant_is_handled(candidate, variant) {
704 return true
705 }
706 }
707 return false
708}
709
710fn (mut c Checker) match_sumtype_matchable_variants(parent ast.Type) []ast.Type {
711 if c.table.sym(parent).kind == .sum_type {
712 return c.table.sumtype_matchable_variants(parent)
713 }
714 mut variants := []ast.Type{}
715 mut seen := map[u32]bool{}
716 c.collect_match_sumtype_matchable_variants(parent, mut seen, mut variants)
717 return variants
718}
719
720fn (mut c Checker) collect_match_sumtype_matchable_variants(parent ast.Type, mut seen map[u32]bool, mut variants []ast.Type) {
721 for variant in c.concrete_sumtype_variants(parent) {
722 key := u32(variant)
723 if key in seen {
724 continue
725 }
726 seen[key] = true
727 variants << variant
728 if c.concrete_sumtype_variants(variant).len > 0 {
729 c.collect_match_sumtype_matchable_variants(variant, mut seen, mut variants)
730 }
731 }
732}
733
734fn (mut c Checker) match_sumtype_missing_variants(parent ast.Type, handled []ast.Type) []ast.Type {
735 if c.table.sym(parent).kind == .sum_type {
736 return c.table.sumtype_missing_variants(parent, handled)
737 }
738 mut missing := []ast.Type{}
739 mut seen := map[u32]bool{}
740 c.collect_match_sumtype_missing_variants(parent, handled, mut seen, mut missing)
741 return missing
742}
743
744fn (mut c Checker) collect_match_sumtype_missing_variants(parent ast.Type, handled []ast.Type, mut seen map[u32]bool, mut missing []ast.Type) {
745 if c.match_sumtype_variant_is_handled_by(parent, handled) {
746 return
747 }
748 for variant in c.concrete_sumtype_variants(parent) {
749 if c.match_sumtype_variant_is_handled_by(variant, handled) {
750 continue
751 }
752 if c.concrete_sumtype_variants(variant).len > 0 {
753 c.collect_match_sumtype_missing_variants(variant, handled, mut seen, mut missing)
754 } else {
755 key := u32(variant)
756 if key !in seen {
757 seen[key] = true
758 missing << variant
759 }
760 }
761 }
762}
763
764fn (mut c Checker) match_sumtype_variant_is_handled_by(variant ast.Type, handled []ast.Type) bool {
765 for handled_variant in handled {
766 if c.match_sumtype_variant_is_handled(variant, handled_variant) {
767 return true
768 }
769 }
770 return false
771}
772
773fn (mut c Checker) match_sumtype_variant_is_handled(variant ast.Type, handled ast.Type) bool {
774 if variant.idx() == handled.idx() && variant.has_flag(.option) == handled.has_flag(.option)
775 && variant.nr_muls() == handled.nr_muls() {
776 return true
777 }
778 handled_sym := c.table.sym(handled)
779 if handled_sym.info is ast.Alias {
780 mut parent_type := handled_sym.info.parent_type.set_nr_muls(handled.nr_muls())
781 if handled.has_flag(.option) {
782 parent_type = parent_type.set_flag(.option)
783 }
784 if handled.has_flag(.result) {
785 parent_type = parent_type.set_flag(.result)
786 }
787 return c.match_sumtype_variant_is_handled(variant, parent_type)
788 }
789 variant_sym := c.table.sym(variant)
790 if variant_sym.info is ast.FnType && handled_sym.info is ast.FnType {
791 return
792 c.table.fn_type_source_signature(variant_sym.info.func) == c.table.fn_type_source_signature(handled_sym.info.func)
793 && variant.has_flag(.option) == handled.has_flag(.option)
794 && variant.nr_muls() == handled.nr_muls()
795 }
796 return false
797}
798
799fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSymbol, cond_final_sym ast.TypeSymbol) {
800 c.expected_type = node.expected_type
801 if node.cond_type.idx() == 0 {
802 return
803 }
804 cond_sym := c.table.sym(node.cond_type)
805 cond_match_type := c.table.final_type(node.cond_type)
806 is_alias_to_matchable_type := cond_type_sym.kind == .alias
807 && cond_final_sym.kind in [.interface, .sum_type]
808 cond_match_sym := if is_alias_to_matchable_type { cond_final_sym } else { cond_type_sym }
809 sumtype_match_variants := c.match_sumtype_matchable_variants(cond_match_type)
810 is_cond_match_sumtype := sumtype_match_variants.len > 0
811 mut enum_ref_checked := false
812 mut is_comptime_value_match := false
813 // branch_exprs is a histogram of how many times
814 // an expr was used in the match
815 mut branch_exprs := map[string]int{}
816 mut branch_expr_types := []ast.Type{}
817 is_multi_allowed_enum_match := cond_type_sym.info is ast.Enum
818 && cond_type_sym.info.is_multi_allowed
819 mut branch_enum_values := map[i64]bool{}
820 for branch_i, _ in node.branches {
821 mut branch := node.branches[branch_i]
822 mut expr_types := []ast.TypeNode{}
823 for k, mut expr in branch.exprs {
824 mut key := ''
825 mut resolved_type_node := ast.no_type
826 // TODO: investigate why enums are different here:
827 if expr !is ast.EnumVal && !(node.is_comptime && expr is ast.ComptimeType) {
828 // ensure that the sub expressions of the branch are actually checked, before anything else:
829 _ := c.expr(mut expr)
830 }
831 if expr is ast.TypeNode && cond_sym.kind == .struct {
832 c.error('struct instances cannot be matched by type name, they can only be matched to other instances of the same struct type',
833 branch.pos)
834 }
835 mut is_comptime := false
836 if c.comptime.inside_comptime_for {
837 // it is a compile-time field.typ checking
838 if mut node.cond is ast.SelectorExpr {
839 is_comptime = node.cond.expr_type == c.field_data_type
840 && node.cond.field_name == 'typ'
841 }
842 }
843 if mut expr is ast.TypeNode && cond_sym.is_primitive() && !is_comptime
844 && !node.is_comptime {
845 c.error('matching by type can only be done for sum types, generics, interfaces, `${node.cond}` is none of those',
846 branch.pos)
847 }
848 if mut expr is ast.RangeExpr {
849 // Allow for `match enum_value { 4..5 { } }`, even though usually int and enum values,
850 // are considered incompatible outside unsafe{}, and are not allowed to be compared directly
851 if cond_sym.kind != .enum && !c.check_types(expr.typ, node.cond_type) {
852 mcstype := c.table.type_to_str(node.cond_type)
853 brstype := c.table.type_to_str(expr.typ)
854 c.add_error_detail('')
855 c.add_error_detail('match condition type: ${mcstype}')
856 c.add_error_detail(' range type: ${brstype}')
857 c.error('the range type and the match condition type should match', expr.pos)
858 }
859 mut low_value_higher_than_high_value := false
860 mut low := i64(0)
861 mut high := i64(0)
862 mut both_low_and_high_are_known := false
863 if low_value := c.get_comptime_number_value(mut expr.low) {
864 low = low_value
865 if high_value := c.get_comptime_number_value(mut expr.high) {
866 high = high_value
867 both_low_and_high_are_known = true
868 if low_value > high_value {
869 low_value_higher_than_high_value = true
870 }
871 } else {
872 if expr.high !is ast.EnumVal {
873 c.error('match branch range expressions need the end value to be known at compile time (only enums, const or literals are supported)',
874 expr.high.pos())
875 }
876 }
877 } else {
878 if expr.low !is ast.EnumVal {
879 c.error('match branch range expressions need the start value to be known at compile time (only enums, const or literals are supported)',
880 expr.low.pos())
881 }
882 }
883 if low_value_higher_than_high_value {
884 c.error('the start value `${low}` should be lower than the end value `${high}`',
885 branch.pos)
886 }
887 if both_low_and_high_are_known {
888 high_low_cutoff := 1000
889 if high - low > high_low_cutoff {
890 c.note('more than ${high_low_cutoff} possibilities (${low} ... ${high}) in match range may affect compile time',
891 branch.pos)
892 }
893 for i in low .. high + 1 {
894 key = i.str()
895 val := if key in branch_exprs { branch_exprs[key] } else { 0 }
896 if val == 1 {
897 c.error('match case `${key}` is handled more than once', branch.pos)
898 }
899 branch_exprs[key] = val + 1
900 if is_multi_allowed_enum_match {
901 branch_enum_values[i] = true
902 }
903 }
904 }
905 continue
906 }
907 is_type_node := expr is ast.TypeNode || expr is ast.ComptimeType
908 match mut expr {
909 ast.TypeNode {
910 resolved_type_node = c.recheck_concrete_type(expr.typ)
911 key = c.table.type_to_str(resolved_type_node)
912 expr_types << ast.TypeNode{
913 ...expr
914 typ: resolved_type_node
915 }
916 }
917 ast.EnumVal {
918 key = expr.val
919 if is_multi_allowed_enum_match {
920 if enum_val := c.table.find_enum_field_val(cond_type_sym.name, expr.val) {
921 branch_enum_values[enum_val] = true
922 }
923 }
924 if !enum_ref_checked {
925 enum_ref_checked = true
926 if node.cond_type.is_ptr() {
927 c.error('missing `*` dereferencing `${node.cond}` in match statement',
928 node.cond.pos())
929 }
930 }
931 }
932 else {
933 key = c.get_match_case_int_key(mut expr, cond_final_sym) or { (*expr).str() }
934 }
935 }
936
937 val := if key in branch_exprs { branch_exprs[key] } else { 0 }
938 if val == 1 {
939 c.error('match case `${key}` is handled more than once', branch.pos)
940 }
941 c.expected_type = node.cond_type
942 if is_type_node {
943 c.inside_x_matches_type = true
944 } else {
945 if node.is_comptime {
946 is_comptime_value_match = true
947 }
948 }
949 if node.is_comptime {
950 expr_pos := expr.pos()
951 if is_type_node {
952 if is_comptime_value_match {
953 // type branch in a value match
954 c.error('can not matching a type in a value `\$match`', expr_pos)
955 return
956 }
957 } else if c.inside_x_matches_type {
958 // value branch in a type match
959 if expr in [ast.IntegerLiteral, ast.BoolLiteral, ast.StringLiteral] {
960 c.error('can not matching a value in a type `\$match`', expr_pos)
961 return
962 }
963 }
964 if !is_type_node {
965 // value check should match cond's type
966 if expr is ast.IntegerLiteral {
967 if mut node.cond is ast.ComptimeType {
968 if node.cond.kind != .int {
969 c.error('can not matching a int value(`${ast.Expr(expr)}`) in a non int type `\$match`, `${node.cond}` type is `${node.cond.kind}`',
970 expr_pos)
971 return
972 }
973 } else if node.cond_type !in ast.integer_type_idxs {
974 c.error('can not matching a int value(`${ast.Expr(expr)}`) in a non int type `\$match`, `${node.cond}` type is `${c.table.type_to_str(node.cond_type)}`',
975 expr_pos)
976 return
977 }
978 } else if expr is ast.BoolLiteral && node.cond_type != ast.bool_type {
979 c.error('can not matching a bool value(`${ast.Expr(expr)}`) in a non bool type `\$match`, `${node.cond}` type is `${c.table.type_to_str(node.cond_type)}`',
980 expr_pos)
981 return
982 } else if expr is ast.StringLiteral {
983 if mut node.cond is ast.ComptimeType {
984 if node.cond.kind != .string {
985 c.error('can not matching a string value(`${ast.Expr(expr)}`) in a non string type `\$match`, `${node.cond}` type is `${node.cond.kind}`',
986 expr_pos)
987 return
988 }
989 } else if node.cond_type != ast.string_type {
990 c.error('can not matching a string value(`${ast.Expr(expr)}`) in a non string type `\$match`, `${node.cond}` type is `${c.table.type_to_str(node.cond_type)}`',
991 expr_pos)
992 return
993 }
994 }
995 }
996 } else {
997 expr_type := if expr is ast.TypeNode {
998 resolved_type_node
999 } else {
1000 c.expr(mut expr)
1001 }
1002 if expr_type.idx() == 0 {
1003 // parser failed, stop checking
1004 return
1005 }
1006 expr_type_sym := c.table.sym(expr_type)
1007 is_exact_alias_type_match := is_alias_to_matchable_type
1008 && expr_type in [node.cond_type, cond_match_type]
1009 if is_exact_alias_type_match {
1010 // Allow matching an alias to its underlying sumtype/interface type.
1011 } else if cond_match_sym.kind == .interface {
1012 // TODO
1013 // This generates a memory issue with TCC
1014 // Needs to be checked later when TCC errors are fixed
1015 // Current solution is to move expr.pos() to its own statement
1016 // c.type_implements(expr_type, c.expected_type, expr.pos())
1017 expr_pos := expr.pos()
1018 if c.type_implements(expr_type, c.expected_type, expr_pos) {
1019 if !expr_type.is_any_kind_of_pointer() && !c.inside_unsafe {
1020 if expr_type_sym.kind != .interface {
1021 c.mark_as_referenced(mut &branch.exprs[k], true)
1022 }
1023 }
1024 }
1025 } else if is_cond_match_sumtype {
1026 if !c.match_sumtype_has_variant(cond_match_type, expr_type) {
1027 expr_str := c.table.type_to_str(expr_type)
1028 expect_str := c.table.type_to_str(node.cond_type)
1029 sumtype_variant_names :=
1030 sumtype_match_variants.map(c.table.type_to_str_using_aliases(it, {}))
1031 suggestion := util.new_suggestion(expr_str, sumtype_variant_names)
1032 c.error(suggestion.say('`${expect_str}` has no variant `${expr_str}`'),
1033 expr.pos())
1034 }
1035 } else if cond_type_sym.info is ast.Alias && expr_type_sym.info is ast.Struct {
1036 expr_str := c.table.type_to_str(expr_type)
1037 expect_str := c.table.type_to_str(node.cond_type)
1038 c.error('cannot match alias type `${expect_str}` with `${expr_str}`',
1039 expr.pos())
1040 } else if !c.check_types(expr_type, node.cond_type) && !is_comptime {
1041 expr_str := c.table.type_to_str(expr_type)
1042 expect_str := c.table.type_to_str(node.cond_type)
1043 c.error('cannot match `${expect_str}` with `${expr_str}`', expr.pos())
1044 }
1045 if is_type_node {
1046 branch_expr_types << if is_alias_to_matchable_type
1047 && expr_type == node.cond_type {
1048 cond_match_type
1049 } else {
1050 expr_type
1051 }
1052 }
1053 }
1054 branch_exprs[key] = val + 1
1055 }
1056 // when match is type matching, then register smart cast for every branch
1057 if expr_types.len > 0 {
1058 if is_cond_match_sumtype || cond_match_sym.kind == .interface {
1059 mut expr_type := ast.no_type
1060 if expr_types.len > 1 {
1061 mut agg_name := strings.new_builder(20)
1062 mut agg_cname := strings.new_builder(20)
1063 agg_name.write_string('(')
1064 for i, expr in expr_types {
1065 if i > 0 {
1066 agg_name.write_string(' | ')
1067 agg_cname.write_string('___')
1068 }
1069 type_str := c.table.type_to_str(expr.typ)
1070 name := if c.is_builtin_mod { type_str } else { '${c.mod}.${type_str}' }
1071 agg_name.write_string(name)
1072 agg_cname.write_string(util.no_dots(name))
1073 }
1074 agg_name.write_string(')')
1075 name := agg_name.str()
1076 existing_idx := c.table.type_idxs[name]
1077 if existing_idx > 0 {
1078 expr_type = existing_idx
1079 } else {
1080 expr_type = c.table.register_sym(ast.TypeSymbol{
1081 name: name
1082 cname: agg_cname.str()
1083 kind: .aggregate
1084 mod: c.mod
1085 info: ast.Aggregate{
1086 sum_type: node.cond_type
1087 types: expr_types.map(it.typ)
1088 }
1089 })
1090 }
1091 } else {
1092 expr_type = expr_types[0].typ
1093 }
1094
1095 allow_mut_selector_smartcast := node.cond is ast.SelectorExpr
1096 c.smartcast(mut node.cond, node.cond_type, expr_type, mut branch.scope, false,
1097 false, allow_mut_selector_smartcast, true)
1098 }
1099 }
1100 }
1101 // check that expressions are exhaustive
1102 // this is achieved either by putting an else
1103 // or, when the match is on a sum type or an enum
1104 // by listing all variants or values
1105 mut is_exhaustive := true
1106 mut unhandled := []string{}
1107 if node.cond_type == ast.bool_type {
1108 variants := ['true', 'false']
1109 for v in variants {
1110 if v !in branch_exprs {
1111 is_exhaustive = false
1112 unhandled << '`${v}`'
1113 }
1114 }
1115 } else {
1116 if is_cond_match_sumtype {
1117 for v in c.match_sumtype_missing_variants(cond_match_type, branch_expr_types) {
1118 is_exhaustive = false
1119 unhandled << '`${c.table.type_to_str(v)}`'
1120 }
1121 } else {
1122 match cond_match_sym.info {
1123 ast.Enum {
1124 for v in cond_match_sym.info.vals {
1125 mut is_handled := v in branch_exprs
1126 if !is_handled && is_multi_allowed_enum_match {
1127 if enum_val := c.table.find_enum_field_val(cond_match_sym.name, v) {
1128 is_handled = enum_val in branch_enum_values
1129 }
1130 }
1131 if !is_handled {
1132 is_exhaustive = false
1133 unhandled << '`.${v}`'
1134 }
1135 }
1136 if cond_match_sym.info.is_flag {
1137 is_exhaustive = false
1138 }
1139 }
1140 else {
1141 is_exhaustive = false
1142 }
1143 }
1144 }
1145 }
1146 if node.branches.len == 0 {
1147 c.error('`match` must have at least two branches including `else`, or an exhaustive set of branches',
1148 node.pos)
1149 return
1150 }
1151 mut else_branch := node.branches.last()
1152 mut has_else, mut has_non_else := else_branch.is_else, !else_branch.is_else
1153 for branch in node.branches[..node.branches.len - 1] {
1154 if branch.is_else {
1155 if has_else {
1156 c.error('`match` can have only one `else` branch', branch.pos)
1157 }
1158 c.error('`else` must be the last branch of `match`', branch.pos)
1159 else_branch = branch
1160 has_else = true
1161 } else {
1162 has_non_else = true
1163 }
1164 }
1165 if !has_non_else {
1166 c.error('`match` must have at least one non `else` branch', else_branch.pos)
1167 }
1168 if is_exhaustive {
1169 if has_else && !c.pref.translated && !c.file.is_translated {
1170 c.error('match expression is exhaustive, `else` is unnecessary', else_branch.pos)
1171 }
1172 return
1173 }
1174 if has_else || node.is_comptime {
1175 return
1176 }
1177 mut err_details := 'match must be exhaustive'
1178 if unhandled.len > 0 {
1179 err_details += ' (add match branches for: '
1180 if unhandled.len < c.match_exhaustive_cutoff_limit {
1181 err_details += unhandled.join(', ')
1182 } else {
1183 remaining := unhandled.len - c.match_exhaustive_cutoff_limit
1184 err_details += unhandled[0..c.match_exhaustive_cutoff_limit].join(', ')
1185 if remaining > 0 {
1186 err_details += ', and ${remaining} others ...'
1187 }
1188 }
1189 err_details += ' or `else {}` at the end)'
1190 } else {
1191 err_details += ' (add `else {}` at the end)'
1192 }
1193 c.error(err_details, node.pos)
1194}
1195