v2 / vlib / v / checker / match.v
1075 lines · 1058 sloc · 35.51 KB · 267475d919454faf39dc54db1f7b802fe86698fa
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 c.expected_type = if c.expected_expr_type != ast.void_type {
321 c.expected_expr_type
322 } else {
323 node.expected_type
324 }
325 branch.is_comptime_err = stmt.expr is ast.ComptimeCall
326 && stmt.expr.kind in [.compile_error, .compile_warn]
327 expr_type := if branch.is_comptime_err { c.expected_type } else { c.unwrap_generic(if stmt.expr is ast.CallExpr {
328 stmt.typ
329 } else {
330 c.expr(mut stmt.expr)
331 })
332 }
333 unwrapped_expected_type := c.unwrap_generic(node.expected_type)
334 must_be_option = must_be_option || expr_type == ast.none_type
335 stmt.typ = expr_type
336 if ret_type_needs_inference && !is_noreturn_callexpr(stmt.expr) {
337 if unwrapped_expected_type.has_option_or_result()
338 || c.table.type_kind(unwrapped_expected_type) in [.sum_type, .interface, .multi_return] {
339 c.check_match_branch_last_stmt(mut stmt, unwrapped_expected_type, expr_type)
340 ret_type = node.expected_type
341 } else {
342 ret_type = expr_type
343 if expr_type.is_ptr() {
344 if stmt.expr is ast.Ident && stmt.expr.obj is ast.Var
345 && c.table.is_interface_var(stmt.expr.obj) {
346 ret_type = expr_type.deref()
347 } else if mut stmt.expr is ast.PrefixExpr
348 && stmt.expr.right is ast.Ident {
349 ident := stmt.expr.right as ast.Ident
350 if ident.obj is ast.Var && c.table.is_interface_var(ident.obj) {
351 ret_type = expr_type.deref()
352 }
353 }
354 }
355 c.expected_expr_type = expr_type
356 }
357 infer_cast_type = stmt.typ
358 if mut stmt.expr is ast.CastExpr {
359 need_explicit_cast = true
360 infer_cast_type = stmt.expr.typ
361 }
362 ret_type_needs_inference = false
363 } else if !ret_type_needs_inference {
364 if ret_type.idx() != expr_type.idx() {
365 if unwrapped_expected_type.has_option_or_result()
366 && c.table.sym(stmt.typ).kind == .struct
367 && !c.check_types(expr_type, c.unwrap_generic(ret_type))
368 && c.type_implements(stmt.typ, ast.error_type, node.pos) {
369 stmt.expr = ast.CastExpr{
370 expr: stmt.expr
371 typname: 'IError'
372 typ: ast.error_type
373 expr_type: stmt.typ
374 pos: node.pos
375 }
376 stmt.typ = ast.error_type
377 } else {
378 c.check_match_branch_last_stmt(mut stmt, c.unwrap_generic(ret_type),
379 expr_type)
380 if ret_type.is_number() && expr_type.is_number() && !c.inside_return {
381 ret_type = c.promote_num(ret_type, expr_type)
382 }
383 }
384 }
385 if must_be_option && ret_type == ast.none_type && expr_type != ret_type {
386 ret_type = expr_type.set_flag(.option)
387 }
388 if stmt.typ != ast.error_type && !is_noreturn_callexpr(stmt.expr) {
389 ret_sym := c.table.sym(ret_type)
390 stmt_sym := c.table.sym(stmt.typ)
391 if ret_sym.kind !in [.sum_type, .interface]
392 && stmt_sym.kind in [.sum_type, .interface] {
393 c.error('return type mismatch, it should be `${ret_sym.name}`, but it is instead `${c.table.type_to_str(expr_type)}`',
394 stmt.pos)
395 }
396 if ret_type.nr_muls() != stmt.typ.nr_muls()
397 && stmt.typ.idx() !in [ast.voidptr_type_idx, ast.nil_type_idx] {
398 type_name := '&'.repeat(ret_type.nr_muls()) + ret_sym.name
399 c.error('return type mismatch, it should be `${type_name}`, but it is instead `${c.table.type_to_str(expr_type)}`',
400 stmt.pos)
401 }
402 }
403 if !node.is_sum_type {
404 if mut stmt.expr is ast.CastExpr {
405 expr_typ_sym := c.table.sym(stmt.expr.typ)
406 if need_explicit_cast {
407 if infer_cast_type != stmt.expr.typ
408 && expr_typ_sym.kind !in [.interface, .sum_type] {
409 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)}`',
410 stmt.pos)
411 }
412 } else {
413 if infer_cast_type != stmt.expr.typ
414 && expr_typ_sym.kind !in [.interface, .sum_type]
415 && c.promote_num(stmt.expr.typ, ast.int_type) != ast.int_type {
416 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)}`',
417 stmt.pos)
418 }
419 }
420 } else {
421 if mut stmt.expr is ast.IntegerLiteral {
422 cast_type_sym := c.table.sym(infer_cast_type)
423 num := stmt.expr.val.i64()
424 mut needs_explicit_cast := false
425
426 match cast_type_sym.kind {
427 .u8 {
428 if !(num >= min_u8 && num <= max_u8) {
429 needs_explicit_cast = true
430 }
431 }
432 .u16 {
433 if !(num >= min_u16 && num <= max_u16) {
434 needs_explicit_cast = true
435 }
436 }
437 .u32 {
438 if !(num >= min_u32 && num <= max_u32) {
439 needs_explicit_cast = true
440 }
441 }
442 .u64 {
443 if !(num >= min_u64 && num <= max_u64) {
444 needs_explicit_cast = true
445 }
446 }
447 .i8 {
448 if !(num >= min_i32 && num <= max_i32) {
449 needs_explicit_cast = true
450 }
451 }
452 .i16 {
453 if !(num >= min_i16 && num <= max_i16) {
454 needs_explicit_cast = true
455 }
456 }
457 .i32 {
458 if !(num >= min_i32 && num <= max_i32) {
459 needs_explicit_cast = true
460 }
461 }
462 .int {
463 $if new_int ? && x64 {
464 if !(num >= min_i64 && num <= max_i64) {
465 needs_explicit_cast = true
466 }
467 } $else {
468 if !(num >= min_i32 && num <= max_i32) {
469 needs_explicit_cast = true
470 }
471 }
472 }
473 .i64 {
474 if !(num >= min_i64 && num <= max_i64) {
475 needs_explicit_cast = true
476 }
477 }
478 .int_literal {
479 needs_explicit_cast = false
480 }
481 else {}
482 }
483
484 if needs_explicit_cast {
485 c.error('${num} does not fit the range of `${c.table.type_to_str(infer_cast_type)}`',
486 stmt.pos)
487 }
488 }
489 }
490 }
491 }
492 } else if stmt !in [ast.Return, ast.BranchStmt] {
493 if ret_type != ast.void_type {
494 c.error('`match` expression requires an expression as the last statement of every branch',
495 stmt.pos)
496 }
497 } else if c.inside_return && mut stmt is ast.Return && ret_type == ast.void_type {
498 ret_type = if stmt.types.len > 0 { stmt.types[0] } else { c.expected_type }
499 }
500 }
501 if !node.is_comptime || (node.is_comptime && comptime_match_branch_result) {
502 // ret_type_needs_inference is set to false in the ExprStmt branch above
503 // when a non-noreturn expression provides the type
504 }
505 if node.is_comptime {
506 // branches may not have been processed by c.stmts()
507 if c.has_top_return(branch.stmts) {
508 nbranches_with_return++
509 } else {
510 nbranches_without_return++
511 }
512 } else {
513 if has_return := c.has_return(branch.stmts) {
514 if has_return {
515 nbranches_with_return++
516 } else {
517 nbranches_without_return++
518 }
519 }
520 }
521 }
522 if nbranches_with_return > 0 {
523 if nbranches_with_return == node.branches.len {
524 // an exhaustive match, and all branches returned
525 c.returns = true
526 }
527 if nbranches_without_return > 0 {
528 // some of the branches did not return
529 c.returns = false
530 }
531 }
532 if ret_type == ast.none_type {
533 c.error('invalid match expression, must supply at least one value other than `none`',
534 node.pos)
535 }
536 node.return_type = if must_be_option { ret_type.set_flag(.option) } else { ret_type }
537 cond_var := c.get_base_name(&node.cond)
538 if cond_var != '' {
539 mut cond_is_auto_heap := false
540 for branch in node.branches {
541 if v := branch.scope.find_var(cond_var) {
542 if v.is_auto_heap {
543 cond_is_auto_heap = true
544 break
545 }
546 }
547 }
548 if cond_is_auto_heap {
549 for branch in node.branches {
550 mut v := branch.scope.find_var(cond_var) or { continue }
551 v.is_auto_heap = true
552 }
553 }
554 }
555 return node.return_type
556}
557
558fn (mut c Checker) check_match_branch_last_stmt(mut last_stmt ast.ExprStmt, ret_type ast.Type, expr_type ast.Type) {
559 if !c.check_types(ret_type, expr_type) && !c.check_types(expr_type, ret_type) {
560 ret_sym := c.table.sym(ret_type)
561 expr_sym := c.table.sym(expr_type)
562 if ret_sym.kind == .interface {
563 if c.type_implements(expr_type, ret_type, last_stmt.pos) {
564 if !expr_type.is_any_kind_of_pointer() && expr_sym.kind != .interface
565 && !c.inside_unsafe {
566 c.mark_as_referenced(mut &last_stmt.expr, true)
567 }
568 }
569 return
570 }
571 is_noreturn := is_noreturn_callexpr(last_stmt.expr)
572 if !(ret_sym.kind == .sum_type && (ret_type.has_flag(.generic)
573 || c.table.is_sumtype_or_in_variant(ret_type, expr_type))) && !is_noreturn {
574 if expr_sym.kind == .multi_return && ret_sym.kind == .multi_return {
575 ret_types := ret_sym.mr_info().types
576 expr_types := expr_sym.mr_info().types.map(ast.mktyp(it))
577 if expr_types == ret_types {
578 return
579 }
580 }
581 if expr_type != ast.none_type && ret_type != ast.none_type {
582 c.error('return type mismatch, it should be `${ret_sym.name}`, but it is instead `${c.table.type_to_str(expr_type)}`',
583 last_stmt.pos)
584 }
585 }
586 } else if expr_type == ast.void_type && ret_type.idx() == ast.void_type_idx
587 && ret_type.has_option_or_result() {
588 c.error('`${last_stmt.expr}` used as value', last_stmt.pos)
589 }
590}
591
592fn char_literal_number_value(value string) ?i64 {
593 if value.len == 2 && value[0] == `\\` {
594 return match value[1] {
595 `a` { 7 }
596 `b` { 8 }
597 `t` { 9 }
598 `n` { 10 }
599 `v` { 11 }
600 `f` { 12 }
601 `r` { 13 }
602 `e` { 27 }
603 `$` { 36 }
604 `"` { 34 }
605 `'` { 39 }
606 `?` { 63 }
607 `@` { 64 }
608 `\\` { 92 }
609 `\`` { 96 }
610 `{` { 123 }
611 `}` { 125 }
612 else { none }
613 }
614 }
615 runes := value.runes()
616 if runes.len == 1 {
617 return runes[0]
618 }
619 return none
620}
621
622fn (mut c Checker) get_comptime_number_value(mut expr ast.Expr) ?i64 {
623 if mut expr is ast.ParExpr {
624 return c.get_comptime_number_value(mut expr.expr)
625 }
626 if mut expr is ast.PrefixExpr && expr.op == .minus {
627 return -c.get_comptime_number_value(mut expr.right)?
628 }
629 if mut expr is ast.CharLiteral {
630 return char_literal_number_value(expr.val)
631 }
632 if mut expr is ast.IntegerLiteral {
633 return expr.val.i64()
634 }
635 if mut expr is ast.CastExpr {
636 return c.get_comptime_number_value(mut expr.expr)
637 }
638 if mut expr is ast.Ident {
639 if mut obj := c.table.global_scope.find_const(expr.full_name()) {
640 if obj.typ == 0 {
641 obj.typ = c.expr(mut obj.expr)
642 }
643 return c.get_comptime_number_value(mut obj.expr)
644 }
645 }
646 return none
647}
648
649fn (mut c Checker) get_match_case_int_key(mut expr ast.Expr, cond_sym ast.TypeSymbol) ?string {
650 if !cond_sym.is_int() {
651 return none
652 }
653 // Only resolve literal values for dedup, not const idents.
654 // Different const names with the same value should be allowed.
655 if value := c.get_match_case_literal_value(mut expr) {
656 return value.str()
657 }
658 return none
659}
660
661// Like get_comptime_number_value but does NOT resolve const idents.
662// This ensures different named consts with the same value are not flagged as duplicates.
663fn (mut c Checker) get_match_case_literal_value(mut expr ast.Expr) ?i64 {
664 if mut expr is ast.ParExpr {
665 return c.get_match_case_literal_value(mut expr.expr)
666 }
667 if mut expr is ast.PrefixExpr && expr.op == .minus {
668 return -c.get_match_case_literal_value(mut expr.right)?
669 }
670 if mut expr is ast.CharLiteral {
671 return char_literal_number_value(expr.val)
672 }
673 if mut expr is ast.IntegerLiteral {
674 return expr.val.i64()
675 }
676 if mut expr is ast.CastExpr {
677 return c.get_match_case_literal_value(mut expr.expr)
678 }
679 return none
680}
681
682fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSymbol, cond_final_sym ast.TypeSymbol) {
683 c.expected_type = node.expected_type
684 if node.cond_type.idx() == 0 {
685 return
686 }
687 cond_sym := c.table.sym(node.cond_type)
688 cond_match_type := c.table.final_type(node.cond_type)
689 is_alias_to_matchable_type := cond_type_sym.kind == .alias
690 && cond_final_sym.kind in [.interface, .sum_type]
691 cond_match_sym := if is_alias_to_matchable_type { cond_final_sym } else { cond_type_sym }
692 mut enum_ref_checked := false
693 mut is_comptime_value_match := false
694 // branch_exprs is a histogram of how many times
695 // an expr was used in the match
696 mut branch_exprs := map[string]int{}
697 mut branch_expr_types := []ast.Type{}
698 is_multi_allowed_enum_match := cond_type_sym.info is ast.Enum
699 && cond_type_sym.info.is_multi_allowed
700 mut branch_enum_values := map[i64]bool{}
701 for branch_i, _ in node.branches {
702 mut branch := node.branches[branch_i]
703 mut expr_types := []ast.TypeNode{}
704 for k, mut expr in branch.exprs {
705 mut key := ''
706 mut resolved_type_node := ast.no_type
707 // TODO: investigate why enums are different here:
708 if expr !is ast.EnumVal && !(node.is_comptime && expr is ast.ComptimeType) {
709 // ensure that the sub expressions of the branch are actually checked, before anything else:
710 _ := c.expr(mut expr)
711 }
712 if expr is ast.TypeNode && cond_sym.kind == .struct {
713 c.error('struct instances cannot be matched by type name, they can only be matched to other instances of the same struct type',
714 branch.pos)
715 }
716 mut is_comptime := false
717 if c.comptime.inside_comptime_for {
718 // it is a compile-time field.typ checking
719 if mut node.cond is ast.SelectorExpr {
720 is_comptime = node.cond.expr_type == c.field_data_type
721 && node.cond.field_name == 'typ'
722 }
723 }
724 if mut expr is ast.TypeNode && cond_sym.is_primitive() && !is_comptime
725 && !node.is_comptime {
726 c.error('matching by type can only be done for sum types, generics, interfaces, `${node.cond}` is none of those',
727 branch.pos)
728 }
729 if mut expr is ast.RangeExpr {
730 // Allow for `match enum_value { 4..5 { } }`, even though usually int and enum values,
731 // are considered incompatible outside unsafe{}, and are not allowed to be compared directly
732 if cond_sym.kind != .enum && !c.check_types(expr.typ, node.cond_type) {
733 mcstype := c.table.type_to_str(node.cond_type)
734 brstype := c.table.type_to_str(expr.typ)
735 c.add_error_detail('')
736 c.add_error_detail('match condition type: ${mcstype}')
737 c.add_error_detail(' range type: ${brstype}')
738 c.error('the range type and the match condition type should match', expr.pos)
739 }
740 mut low_value_higher_than_high_value := false
741 mut low := i64(0)
742 mut high := i64(0)
743 mut both_low_and_high_are_known := false
744 if low_value := c.get_comptime_number_value(mut expr.low) {
745 low = low_value
746 if high_value := c.get_comptime_number_value(mut expr.high) {
747 high = high_value
748 both_low_and_high_are_known = true
749 if low_value > high_value {
750 low_value_higher_than_high_value = true
751 }
752 } else {
753 if expr.high !is ast.EnumVal {
754 c.error('match branch range expressions need the end value to be known at compile time (only enums, const or literals are supported)',
755 expr.high.pos())
756 }
757 }
758 } else {
759 if expr.low !is ast.EnumVal {
760 c.error('match branch range expressions need the start value to be known at compile time (only enums, const or literals are supported)',
761 expr.low.pos())
762 }
763 }
764 if low_value_higher_than_high_value {
765 c.error('the start value `${low}` should be lower than the end value `${high}`',
766 branch.pos)
767 }
768 if both_low_and_high_are_known {
769 high_low_cutoff := 1000
770 if high - low > high_low_cutoff {
771 c.note('more than ${high_low_cutoff} possibilities (${low} ... ${high}) in match range may affect compile time',
772 branch.pos)
773 }
774 for i in low .. high + 1 {
775 key = i.str()
776 val := if key in branch_exprs { branch_exprs[key] } else { 0 }
777 if val == 1 {
778 c.error('match case `${key}` is handled more than once', branch.pos)
779 }
780 branch_exprs[key] = val + 1
781 if is_multi_allowed_enum_match {
782 branch_enum_values[i] = true
783 }
784 }
785 }
786 continue
787 }
788 is_type_node := expr is ast.TypeNode || expr is ast.ComptimeType
789 match mut expr {
790 ast.TypeNode {
791 resolved_type_node = c.recheck_concrete_type(expr.typ)
792 key = c.table.type_to_str(resolved_type_node)
793 expr_types << ast.TypeNode{
794 ...expr
795 typ: resolved_type_node
796 }
797 }
798 ast.EnumVal {
799 key = expr.val
800 if is_multi_allowed_enum_match {
801 if enum_val := c.table.find_enum_field_val(cond_type_sym.name, expr.val) {
802 branch_enum_values[enum_val] = true
803 }
804 }
805 if !enum_ref_checked {
806 enum_ref_checked = true
807 if node.cond_type.is_ptr() {
808 c.error('missing `*` dereferencing `${node.cond}` in match statement',
809 node.cond.pos())
810 }
811 }
812 }
813 else {
814 key = c.get_match_case_int_key(mut expr, cond_final_sym) or { (*expr).str() }
815 }
816 }
817
818 val := if key in branch_exprs { branch_exprs[key] } else { 0 }
819 if val == 1 {
820 c.error('match case `${key}` is handled more than once', branch.pos)
821 }
822 c.expected_type = node.cond_type
823 if is_type_node {
824 c.inside_x_matches_type = true
825 } else {
826 if node.is_comptime {
827 is_comptime_value_match = true
828 }
829 }
830 if node.is_comptime {
831 expr_pos := expr.pos()
832 if is_type_node {
833 if is_comptime_value_match {
834 // type branch in a value match
835 c.error('can not matching a type in a value `\$match`', expr_pos)
836 return
837 }
838 } else if c.inside_x_matches_type {
839 // value branch in a type match
840 if expr in [ast.IntegerLiteral, ast.BoolLiteral, ast.StringLiteral] {
841 c.error('can not matching a value in a type `\$match`', expr_pos)
842 return
843 }
844 }
845 if !is_type_node {
846 // value check should match cond's type
847 if expr is ast.IntegerLiteral {
848 if mut node.cond is ast.ComptimeType {
849 if node.cond.kind != .int {
850 c.error('can not matching a int value(`${ast.Expr(expr)}`) in a non int type `\$match`, `${node.cond}` type is `${node.cond.kind}`',
851 expr_pos)
852 return
853 }
854 } else if node.cond_type !in ast.integer_type_idxs {
855 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)}`',
856 expr_pos)
857 return
858 }
859 } else if expr is ast.BoolLiteral && node.cond_type != ast.bool_type {
860 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)}`',
861 expr_pos)
862 return
863 } else if expr is ast.StringLiteral {
864 if mut node.cond is ast.ComptimeType {
865 if node.cond.kind != .string {
866 c.error('can not matching a string value(`${ast.Expr(expr)}`) in a non string type `\$match`, `${node.cond}` type is `${node.cond.kind}`',
867 expr_pos)
868 return
869 }
870 } else if node.cond_type != ast.string_type {
871 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)}`',
872 expr_pos)
873 return
874 }
875 }
876 }
877 } else {
878 expr_type := if expr is ast.TypeNode {
879 resolved_type_node
880 } else {
881 c.expr(mut expr)
882 }
883 if expr_type.idx() == 0 {
884 // parser failed, stop checking
885 return
886 }
887 expr_type_sym := c.table.sym(expr_type)
888 is_exact_alias_type_match := is_alias_to_matchable_type
889 && expr_type in [node.cond_type, cond_match_type]
890 if is_exact_alias_type_match {
891 // Allow matching an alias to its underlying sumtype/interface type.
892 } else if cond_match_sym.kind == .interface {
893 // TODO
894 // This generates a memory issue with TCC
895 // Needs to be checked later when TCC errors are fixed
896 // Current solution is to move expr.pos() to its own statement
897 // c.type_implements(expr_type, c.expected_type, expr.pos())
898 expr_pos := expr.pos()
899 if c.type_implements(expr_type, c.expected_type, expr_pos) {
900 if !expr_type.is_any_kind_of_pointer() && !c.inside_unsafe {
901 if expr_type_sym.kind != .interface {
902 c.mark_as_referenced(mut &branch.exprs[k], true)
903 }
904 }
905 }
906 } else if cond_match_sym.info is ast.SumType {
907 if !c.table.sumtype_has_variant_recursive(cond_match_type, expr_type, true) {
908 expr_str := c.table.type_to_str(expr_type)
909 expect_str := c.table.type_to_str(node.cond_type)
910 sumtype_variant_names :=
911 c.table.sumtype_matchable_variants(cond_match_type).map(c.table.type_to_str_using_aliases(it, {}))
912 suggestion := util.new_suggestion(expr_str, sumtype_variant_names)
913 c.error(suggestion.say('`${expect_str}` has no variant `${expr_str}`'),
914 expr.pos())
915 }
916 } else if cond_type_sym.info is ast.Alias && expr_type_sym.info is ast.Struct {
917 expr_str := c.table.type_to_str(expr_type)
918 expect_str := c.table.type_to_str(node.cond_type)
919 c.error('cannot match alias type `${expect_str}` with `${expr_str}`',
920 expr.pos())
921 } else if !c.check_types(expr_type, node.cond_type) && !is_comptime {
922 expr_str := c.table.type_to_str(expr_type)
923 expect_str := c.table.type_to_str(node.cond_type)
924 c.error('cannot match `${expect_str}` with `${expr_str}`', expr.pos())
925 }
926 if is_type_node {
927 branch_expr_types << if is_alias_to_matchable_type
928 && expr_type == node.cond_type {
929 cond_match_type
930 } else {
931 expr_type
932 }
933 }
934 }
935 branch_exprs[key] = val + 1
936 }
937 // when match is type matching, then register smart cast for every branch
938 if expr_types.len > 0 {
939 if cond_match_sym.kind in [.sum_type, .interface] {
940 mut expr_type := ast.no_type
941 if expr_types.len > 1 {
942 mut agg_name := strings.new_builder(20)
943 mut agg_cname := strings.new_builder(20)
944 agg_name.write_string('(')
945 for i, expr in expr_types {
946 if i > 0 {
947 agg_name.write_string(' | ')
948 agg_cname.write_string('___')
949 }
950 type_str := c.table.type_to_str(expr.typ)
951 name := if c.is_builtin_mod { type_str } else { '${c.mod}.${type_str}' }
952 agg_name.write_string(name)
953 agg_cname.write_string(util.no_dots(name))
954 }
955 agg_name.write_string(')')
956 name := agg_name.str()
957 existing_idx := c.table.type_idxs[name]
958 if existing_idx > 0 {
959 expr_type = existing_idx
960 } else {
961 expr_type = c.table.register_sym(ast.TypeSymbol{
962 name: name
963 cname: agg_cname.str()
964 kind: .aggregate
965 mod: c.mod
966 info: ast.Aggregate{
967 sum_type: node.cond_type
968 types: expr_types.map(it.typ)
969 }
970 })
971 }
972 } else {
973 expr_type = expr_types[0].typ
974 }
975
976 allow_mut_selector_smartcast := node.cond is ast.SelectorExpr
977 c.smartcast(mut node.cond, node.cond_type, expr_type, mut branch.scope, false,
978 false, allow_mut_selector_smartcast, true)
979 }
980 }
981 }
982 // check that expressions are exhaustive
983 // this is achieved either by putting an else
984 // or, when the match is on a sum type or an enum
985 // by listing all variants or values
986 mut is_exhaustive := true
987 mut unhandled := []string{}
988 if node.cond_type == ast.bool_type {
989 variants := ['true', 'false']
990 for v in variants {
991 if v !in branch_exprs {
992 is_exhaustive = false
993 unhandled << '`${v}`'
994 }
995 }
996 } else {
997 match cond_match_sym.info {
998 ast.SumType {
999 for v in c.table.sumtype_missing_variants(cond_match_type, branch_expr_types) {
1000 is_exhaustive = false
1001 unhandled << '`${c.table.type_to_str(v)}`'
1002 }
1003 }
1004 //
1005 ast.Enum {
1006 for v in cond_match_sym.info.vals {
1007 mut is_handled := v in branch_exprs
1008 if !is_handled && is_multi_allowed_enum_match {
1009 if enum_val := c.table.find_enum_field_val(cond_match_sym.name, v) {
1010 is_handled = enum_val in branch_enum_values
1011 }
1012 }
1013 if !is_handled {
1014 is_exhaustive = false
1015 unhandled << '`.${v}`'
1016 }
1017 }
1018 if cond_match_sym.info.is_flag {
1019 is_exhaustive = false
1020 }
1021 }
1022 else {
1023 is_exhaustive = false
1024 }
1025 }
1026 }
1027 if node.branches.len == 0 {
1028 c.error('`match` must have at least two branches including `else`, or an exhaustive set of branches',
1029 node.pos)
1030 return
1031 }
1032 mut else_branch := node.branches.last()
1033 mut has_else, mut has_non_else := else_branch.is_else, !else_branch.is_else
1034 for branch in node.branches[..node.branches.len - 1] {
1035 if branch.is_else {
1036 if has_else {
1037 c.error('`match` can have only one `else` branch', branch.pos)
1038 }
1039 c.error('`else` must be the last branch of `match`', branch.pos)
1040 else_branch = branch
1041 has_else = true
1042 } else {
1043 has_non_else = true
1044 }
1045 }
1046 if !has_non_else {
1047 c.error('`match` must have at least one non `else` branch', else_branch.pos)
1048 }
1049 if is_exhaustive {
1050 if has_else && !c.pref.translated && !c.file.is_translated {
1051 c.error('match expression is exhaustive, `else` is unnecessary', else_branch.pos)
1052 }
1053 return
1054 }
1055 if has_else || node.is_comptime {
1056 return
1057 }
1058 mut err_details := 'match must be exhaustive'
1059 if unhandled.len > 0 {
1060 err_details += ' (add match branches for: '
1061 if unhandled.len < c.match_exhaustive_cutoff_limit {
1062 err_details += unhandled.join(', ')
1063 } else {
1064 remaining := unhandled.len - c.match_exhaustive_cutoff_limit
1065 err_details += unhandled[0..c.match_exhaustive_cutoff_limit].join(', ')
1066 if remaining > 0 {
1067 err_details += ', and ${remaining} others ...'
1068 }
1069 }
1070 err_details += ' or `else {}` at the end)'
1071 } else {
1072 err_details += ' (add `else {}` at the end)'
1073 }
1074 c.error(err_details, node.pos)
1075}
1076