v4 / vlib / v / checker / if.v
854 lines · 834 sloc · 30.07 KB · 5ad37a5a8656c438c68dabd31c9d36467390e046
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
3module checker
4
5import v.ast
6import v.token
7import v.util
8import strings
9
10fn (c &Checker) simple_expr_name_for_dead_branch_check(expr ast.Expr) string {
11 mut current_expr := expr
12 current_expr = current_expr.remove_par()
13 if current_expr !in [ast.Ident, ast.SelectorExpr] {
14 return ''
15 }
16 return current_expr.str()
17}
18
19fn (mut c Checker) is_always_true_self_comparison(cond ast.Expr) bool {
20 mut cond_expr := cond
21 cond_expr = cond_expr.remove_par()
22 if mut cond_expr is ast.InfixExpr {
23 if cond_expr.op != .eq {
24 return false
25 }
26 left_name := c.simple_expr_name_for_dead_branch_check(cond_expr.left)
27 right_name := c.simple_expr_name_for_dead_branch_check(cond_expr.right)
28 if left_name == '' || left_name != right_name {
29 return false
30 }
31 mut left_type := if cond_expr.left_type == ast.no_type {
32 c.get_expr_type(cond_expr.left)
33 } else {
34 cond_expr.left_type
35 }
36 mut right_type := if cond_expr.right_type == ast.no_type {
37 c.get_expr_type(cond_expr.right)
38 } else {
39 cond_expr.right_type
40 }
41 left_type = c.table.unaliased_type(left_type)
42 right_type = c.table.unaliased_type(right_type)
43 if left_type.is_float() || right_type.is_float() {
44 // `NaN == NaN` is always false.
45 return false
46 }
47 return true
48 }
49 return false
50}
51
52fn (c &Checker) is_interface_smartcast_sumtype_variant(expr ast.Expr, expected_type ast.Type, got_type ast.Type) bool {
53 sum_type := expected_type.clear_option_and_result()
54 if c.table.type_kind(sum_type) != .sum_type || !got_type.is_ptr() {
55 return false
56 }
57 if expr is ast.Ident && expr.obj is ast.Var {
58 return c.table.is_interface_var(expr.obj)
59 && c.table.is_sumtype_or_in_variant(sum_type, got_type.deref())
60 }
61 return false
62}
63
64// gen_branch_context_string generate current branches context string.
65// context include generic types, `$for`.
66fn (mut c Checker) gen_branch_context_string() string {
67 mut arr := []string{}
68
69 // gen `T=int,X=string`
70 if !isnil(c.table.cur_fn) && c.table.cur_fn.generic_names.len == c.table.cur_concrete_types.len {
71 for i in 0 .. c.table.cur_fn.generic_names.len {
72 arr << c.table.cur_fn.generic_names[i] + '=' +
73 util.strip_main_name(c.table.type_to_str(c.table.cur_concrete_types[i]))
74 }
75 }
76
77 // gen comptime `$for`
78 if c.comptime.inside_comptime_for {
79 // variants
80 if c.comptime.comptime_for_variant_var.len > 0 {
81 variant := c.table.type_to_str(c.type_resolver.get_ct_type_or_default('${c.comptime.comptime_for_variant_var}.typ',
82 ast.no_type))
83 arr << c.comptime.comptime_for_variant_var + '.typ=' + variant
84 }
85 // fields
86 if c.comptime.comptime_for_field_var.len > 0 {
87 arr << c.comptime.comptime_for_field_var + '.name=' +
88 c.comptime.comptime_for_field_value.name
89 }
90 // values
91 if c.comptime.comptime_for_enum_var.len > 0 {
92 enum_var := c.table.type_to_str(c.type_resolver.get_ct_type_or_default('${c.comptime.comptime_for_enum_var}.typ',
93 ast.void_type))
94 arr << c.comptime.comptime_for_enum_var + '.typ=' + enum_var
95 }
96 // attributes
97 if c.comptime.comptime_for_attr_var.len > 0 {
98 arr << c.comptime.comptime_for_attr_var + '.name=' +
99 c.comptime.comptime_for_attr_value.name
100 }
101 // methods
102 if c.comptime.comptime_for_method_var.len > 0 {
103 arr << c.comptime.comptime_for_method_var + '.name=' +
104 c.comptime.comptime_for_method.name
105 }
106 // args
107 if c.comptime.comptime_for_method_param_var.len > 0 {
108 arg_var := c.table.type_to_str(c.type_resolver.get_ct_type_or_default('${c.comptime.comptime_for_method_param_var}.typ',
109 ast.void_type))
110 arr << c.comptime.comptime_for_method_param_var + '.typ=' + arg_var
111 }
112 }
113 return arr.join(',')
114}
115
116fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
117 if_kind := if node.is_comptime { '\$if' } else { 'if' }
118 mut node_is_expr := false
119 if node.branches.len > 0 && node.has_else {
120 stmts := node.branches[0].stmts
121 if stmts.len > 0 && stmts.last() is ast.ExprStmt && stmts.last().typ != ast.void_type {
122 node_is_expr = true
123 } else if node.is_expr {
124 node_is_expr = true
125 }
126 }
127 if c.expected_type == ast.void_type && node_is_expr {
128 c.expected_type = c.expected_or_type
129 }
130 expr_required := node.force_expr || c.expected_type != ast.void_type
131 || (node.is_comptime && node.is_expr && node.has_else && c.fn_level > 0)
132 former_expected_type := c.expected_type
133 if node_is_expr {
134 c.expected_expr_type = c.expected_type
135 defer(fn) {
136 c.expected_expr_type = ast.void_type
137 }
138 }
139 node.typ = ast.void_type
140 mut nbranches_with_return := 0
141 mut nbranches_without_return := 0
142 mut comptime_if_result := false
143 mut comptime_if_multi_pass_branch := false
144 mut comptime_if_found_branch := false
145 mut comptime_if_has_multi_pass_branch := false
146
147 last_in_comptime_if := c.comptime.inside_comptime_if
148 defer {
149 c.comptime.inside_comptime_if = last_in_comptime_if
150 }
151
152 comptime_branch_context_str := if node.is_comptime { c.gen_branch_context_string() } else { '' }
153
154 for i, mut branch in node.branches {
155 if node.is_comptime {
156 c.push_new_comptime_info()
157 }
158 orig_branch_cond := branch.cond
159 mut comptime_remove_curr_branch_stmts := false
160 if branch.cond is ast.ParExpr && !c.pref.translated && !c.file.is_translated {
161 c.warn('unnecessary `()` in `${if_kind}` condition, use `${if_kind} expr {` instead of `${if_kind} (expr) {`.',
162 branch.pos)
163 }
164 if !node.has_else || i < node.branches.len - 1 {
165 // if branch
166 if node.is_comptime {
167 // `idx_str` is composed of two parts:
168 // The first part represents the current context of the branch statement, `comptime_branch_context_str`, formatted like `T=int,X=string,method.name=json`
169 // The second part is the branch's id.
170 // This format must match what is in `cgen`.
171 if branch.id == 0 {
172 // this is a new branch, alloc a new id for it
173 c.cur_ct_id++
174 branch.id = c.cur_ct_id
175 }
176 mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
177 if c.comptime.inside_comptime_for && c.comptime.comptime_for_field_var != '' {
178 idx_str += '|field_type=${c.comptime.comptime_for_field_type}|'
179 }
180 c.comptime.inside_comptime_if = true
181 mut sb := strings.new_builder(256)
182 comptime_if_result, comptime_if_multi_pass_branch =
183 c.comptime_if_cond(mut branch.cond, mut sb)
184 if comptime_if_multi_pass_branch {
185 comptime_if_has_multi_pass_branch = true
186 }
187
188 if comptime_if_found_branch {
189 comptime_if_result = false
190 }
191
192 if !comptime_if_has_multi_pass_branch
193 && (comptime_if_found_branch || !comptime_if_result) {
194 // when all prev branchs are single pass branchs,
195 // 1. already has a true branch or
196 // 2. `comptime_if_result is` false
197 // remove current branchs' stmts
198 comptime_remove_curr_branch_stmts = true
199 }
200 if old_val := c.table.comptime_is_true[idx_str] {
201 if old_val.val != comptime_if_result {
202 c.error('checker error : branch eval wrong', branch.cond.pos())
203 }
204 }
205
206 // set `comptime_is_true` which can be used by `cgen`
207 c.table.comptime_is_true[idx_str] = ast.ComptTimeCondResult{
208 val: comptime_if_result
209 c_str: sb.str()
210 }
211 } else {
212 // check condition type is boolean
213 c.expected_type = ast.bool_type
214 cond_typ := c.table.unaliased_type(c.unwrap_generic(c.expr(mut branch.cond)))
215 if (cond_typ.idx() != ast.bool_type_idx || cond_typ.has_flag(.option)
216 || cond_typ.has_flag(.result)) && !c.pref.translated && !c.file.is_translated {
217 //&& cond_typ.idx() != ast.void_type_idx { TODO bring back after the void split
218 c.error('non-bool type `${c.table.type_to_str(cond_typ)}` used as if condition',
219 branch.cond.pos())
220 }
221 if !c.pref.translated && !c.file.is_translated && !c.inside_unsafe {
222 mut check_expr := branch.cond
223 t_expr := c.checker_transformer.expr(mut check_expr)
224 if t_expr is ast.BoolLiteral {
225 if t_expr.val {
226 c.note('condition is always true', branch.cond.pos())
227 } else {
228 c.note('condition is always false', branch.cond.pos())
229 }
230 }
231 }
232 if !c.pref.translated && !c.file.is_translated && !c.inside_unsafe
233 && c.is_always_true_self_comparison(orig_branch_cond) {
234 c.warn('self-comparison in `if` condition is always true; following branches may be unreachable',
235 branch.cond.pos())
236 }
237 }
238 } else {
239 // else branch
240 if node.is_comptime {
241 c.comptime.inside_comptime_if = true
242 comptime_if_result = !comptime_if_found_branch
243 // if all other branchs has at least one multi pass branch, we should keep this else branch
244 comptime_if_multi_pass_branch = comptime_if_has_multi_pass_branch
245 if !comptime_if_has_multi_pass_branch && comptime_if_found_branch {
246 // when all prev branchs are single pass branchs, already has a true branch
247 // remove current branchs' stmts
248 comptime_remove_curr_branch_stmts = true
249 }
250 // hack: as a `else` has no `cond`, so we use `branch.pos` here
251 if branch.id == 0 {
252 // this is a new branch, alloc a new id for it
253 c.cur_ct_id++
254 branch.id = c.cur_ct_id
255 }
256 mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
257 if c.comptime.inside_comptime_for && c.comptime.comptime_for_field_var != '' {
258 idx_str += '|field_type=${c.comptime.comptime_for_field_type}|'
259 }
260 c.table.comptime_is_true[idx_str] = ast.ComptTimeCondResult{
261 val: comptime_if_result
262 c_str: ''
263 }
264 }
265 }
266 if mut branch.cond is ast.IfGuardExpr {
267 if branch.cond.expr_type.clear_option_and_result() == ast.void_type
268 && !(branch.cond.vars.len == 1 && branch.cond.vars[0].name == '_') {
269 c.error('if guard expects non-propagate option or result', branch.pos)
270 continue
271 }
272 sym := c.table.sym(branch.cond.expr_type)
273 if sym.kind == .multi_return {
274 mr_info := sym.info as ast.MultiReturn
275 if branch.cond.vars.len != mr_info.types.len {
276 c.error('if guard expects ${mr_info.types.len} variables, but got ${branch.cond.vars.len}',
277 branch.pos)
278 continue
279 } else {
280 for vi, var in branch.cond.vars {
281 branch.scope.update_var_type(var.name, mr_info.types[vi])
282 }
283 }
284 } else {
285 if branch.cond.vars.len != 1 {
286 c.error('if guard expects a single variable, but got ${branch.cond.vars.len}',
287 branch.pos)
288 continue
289 }
290 for _, var in branch.cond.vars {
291 if var.name == '_' {
292 continue
293 }
294 unwrapped_guard_typ := branch.cond.expr_type.clear_option_and_result()
295 if w := branch.scope.find_var(var.name) {
296 $if trace_veb_guard ? {
297 if var.name == 'params' {
298 expr_type_str := if branch.cond.expr_type == 0 {
299 '<none>'
300 } else {
301 c.table.type_to_str(branch.cond.expr_type)
302 }
303 clear_type := branch.cond.expr_type.clear_option_and_result()
304 clear_type_str := if clear_type == 0 {
305 '<none>'
306 } else {
307 c.table.type_to_str(clear_type)
308 }
309 old_type_str := if w.typ == 0 {
310 '<none>'
311 } else {
312 c.table.type_to_str(w.typ)
313 }
314 eprintln('if_guard scope name=${var.name} expr_type=${expr_type_str} clear=${clear_type_str} old=${old_type_str} file=${c.file.path}')
315 }
316 }
317 if var.name !in branch.scope.objects {
318 branch.scope.objects[var.name] = w
319 }
320 branch.scope.update_var_type(var.name, unwrapped_guard_typ)
321 resolved_guard_typ := c.unwrap_generic(unwrapped_guard_typ)
322 if !resolved_guard_typ.is_ptr() && c.table.sym(resolved_guard_typ).is_heap() {
323 if mut guard_var := branch.scope.find_var(var.name) {
324 guard_var.is_auto_heap = true
325 }
326 }
327 }
328 }
329 }
330 }
331 if node.is_comptime { // Skip checking if needed
332 cur_skip_flags := c.skip_flags
333 // if current cond result is false, or we already found a branch, we should skip current branch
334 c.skip_flags = !comptime_if_result || comptime_if_found_branch
335 if c.fn_level == 0 && c.pref.output_cross_c {
336 // do not skip any of the branches for top level `$if OS {`
337 // statements, in `-cross` mode
338 comptime_remove_curr_branch_stmts = false
339 c.skip_flags = false
340 // hack: because `else` branch has no `cond`, so create an Ident, set the `pos`, for `hash_stmt()` work.
341 if branch.cond is ast.NodeError {
342 c.ct_cond_stack << ast.Ident{
343 name: '__else_branch__'
344 pos: branch.pos
345 }
346 } else {
347 c.ct_cond_stack << branch.cond
348 }
349 }
350 if !c.skip_flags {
351 if node_is_expr {
352 c.stmts_ending_with_expression(mut branch.stmts, c.expected_or_type)
353 } else {
354 c.stmts(mut branch.stmts)
355 c.check_non_expr_branch_last_stmt(branch.stmts)
356 }
357 } else if c.pref.output_cross_c {
358 mut is_freestanding_block := false
359 if mut branch.cond is ast.Ident {
360 if branch.cond.name == 'freestanding' {
361 is_freestanding_block = true
362 }
363 }
364 if is_freestanding_block {
365 branch.stmts = []
366 node.branches[i].stmts = []
367 }
368 if node_is_expr {
369 c.stmts_ending_with_expression(mut branch.stmts, c.expected_or_type)
370 } else {
371 c.stmts(mut branch.stmts)
372 c.check_non_expr_branch_last_stmt(branch.stmts)
373 }
374 }
375 c.skip_flags = cur_skip_flags
376 if c.fn_level == 0 && c.pref.output_cross_c && c.ct_cond_stack.len > 0 {
377 c.ct_cond_stack.delete_last()
378 }
379 if comptime_if_result {
380 comptime_if_found_branch = true
381 }
382 } else {
383 // smartcast sumtypes and interfaces when using `is`
384 c.smartcast_if_conds(mut branch.cond, mut branch.scope, node)
385 if node_is_expr {
386 c.stmts_ending_with_expression(mut branch.stmts, c.expected_or_type)
387 } else {
388 c.stmts(mut branch.stmts)
389 c.check_non_expr_branch_last_stmt(branch.stmts)
390 }
391 c.smartcast_mut_pos = token.Pos{}
392 c.smartcast_cond_pos = token.Pos{}
393 }
394 if expr_required {
395 if branch.stmts.len > 0 {
396 mut stmt := branch.stmts.last()
397 if mut stmt is ast.ExprStmt {
398 // A trailing `spawn`/`go` is the value of this if-expression branch,
399 // so it must produce a usable thread handle (waiter, no detach),
400 // like the assignment form `t := spawn f()`. See issue #27485.
401 if mut stmt.expr is ast.SpawnExpr {
402 stmt.expr.is_expr = true
403 } else if mut stmt.expr is ast.GoExpr {
404 stmt.expr.is_expr = true
405 }
406 if mut stmt.expr is ast.ConcatExpr {
407 for mut val in stmt.expr.vals {
408 c.check_expr_option_or_result_call(val, c.expr(mut val))
409 }
410 }
411 c.expected_type = former_expected_type
412 if c.table.type_kind(c.expected_type) == .sum_type
413 && c.table.is_sumtype_or_in_variant(c.expected_type, node.typ) {
414 node.is_expr = true
415 node.typ = c.expected_type
416 }
417 if c.expected_type.has_option_or_result() {
418 if node.typ == ast.void_type {
419 node.is_expr = true
420 node.typ = c.expected_type
421 }
422 }
423 if c.expected_type.has_flag(.generic) {
424 if node.typ == ast.void_type {
425 node.is_expr = true
426 node.typ = c.unwrap_generic(c.expected_type)
427 }
428 unsafe {
429 goto end_if
430 }
431 }
432 if c.expected_expr_type != ast.void_type {
433 c.expected_type = c.expected_expr_type
434 }
435 stmt.typ = c.expr(mut stmt.expr)
436 if c.table.type_kind(c.expected_type) == .multi_return
437 && c.table.type_kind(stmt.typ) == .multi_return {
438 if node.typ == ast.void_type
439 && c.can_use_expected_multi_return_expr_type(stmt.typ, c.expected_type, stmt.expr) {
440 node.is_expr = true
441 node.typ = c.expected_type
442 }
443 }
444 if stmt.typ == ast.void_type && !is_noreturn_callexpr(stmt.expr)
445 && !c.skip_flags {
446 // cannot return void type and use it as expr in any circumstances
447 // (e.g. argument expression, variable declaration / assignment)
448 c.error('the final expression in `if` or `match`, must have a value of a non-void type',
449 stmt.pos)
450 unsafe {
451 goto end_if
452 }
453 }
454 if !c.check_types(stmt.typ, node.typ)
455 && !c.check_types(c.unwrap_generic(stmt.typ), c.unwrap_generic(node.typ)) {
456 if node.typ == ast.void_type {
457 // first branch of if expression
458 node.is_expr = true
459 if stmt.expr.is_auto_deref_var() && stmt.typ.is_ptr() {
460 node.typ = stmt.typ.deref()
461 } else {
462 node.typ = stmt.typ
463 }
464 c.expected_expr_type = node.typ
465 unsafe {
466 goto end_if
467 }
468 } else if node.typ in [ast.float_literal_type, ast.int_literal_type] {
469 if node.typ == ast.int_literal_type {
470 if stmt.typ.is_int() || stmt.typ.is_float() {
471 node.typ = stmt.typ
472 unsafe {
473 goto end_if
474 }
475 }
476 } else { // node.typ == float_literal
477 if stmt.typ.is_float() {
478 node.typ = stmt.typ
479 unsafe {
480 goto end_if
481 }
482 }
483 }
484 }
485 if stmt.typ in [ast.float_literal_type, ast.int_literal_type] {
486 if stmt.typ == ast.int_literal_type {
487 if node.typ.is_int() || node.typ.is_float() {
488 unsafe {
489 goto end_if
490 }
491 }
492 } else { // expr_type == float_literal
493 if node.typ.is_float() {
494 unsafe {
495 goto end_if
496 }
497 }
498 }
499 }
500 if c.is_interface_smartcast_sumtype_variant(stmt.expr,
501 former_expected_type, stmt.typ)
502 {
503 node.is_expr = true
504 node.typ = former_expected_type.clear_option_and_result()
505 unsafe {
506 goto end_if
507 }
508 }
509 if node.is_expr && c.table.sym(former_expected_type).kind == .sum_type {
510 node.typ = former_expected_type
511 unsafe {
512 goto end_if
513 }
514 }
515 if is_noreturn_callexpr(stmt.expr) {
516 unsafe {
517 goto end_if
518 }
519 }
520 if (node.typ.has_option_or_result())
521 && c.table.sym(stmt.typ).kind == .struct
522 && c.type_implements(stmt.typ, ast.error_type, node.pos) {
523 stmt.expr = ast.CastExpr{
524 expr: stmt.expr
525 typname: 'IError'
526 typ: ast.error_type
527 expr_type: stmt.typ
528 pos: node.pos
529 }
530 stmt.typ = ast.error_type
531 unsafe {
532 goto end_if
533 }
534 }
535 if (node.typ == ast.none_type && stmt.typ != ast.none_type)
536 || (stmt.typ == ast.none_type && node.typ != ast.none_type) {
537 node.typ = if stmt.typ != ast.none_type {
538 stmt.typ.set_flag(.option)
539 } else {
540 node.typ.set_flag(.option)
541 }
542 unsafe {
543 goto end_if
544 }
545 }
546 c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(stmt.typ)}`',
547 node.pos)
548 } else {
549 if !node.typ.has_option_or_result() && !node.typ.has_flag(.shared_f)
550 && stmt.typ != ast.voidptr_type
551 && stmt.typ.nr_muls() != node.typ.nr_muls()
552 && !c.is_interface_smartcast_sumtype_variant(stmt.expr, node.typ, stmt.typ) {
553 c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(stmt.typ)}`',
554 node.pos)
555 }
556 if node.is_expr == false && c.type_resolver.is_generic_param_var(stmt.expr) {
557 // generic variable no yet type bounded
558 node.is_expr = true
559 }
560 if c.inside_assign && node.is_expr && !node.typ.has_flag(.shared_f)
561 && stmt.typ != ast.voidptr_type
562 && !c.is_interface_smartcast_sumtype_variant(stmt.expr, node.typ, stmt.typ) {
563 if stmt.typ.is_ptr() != node.typ.is_ptr() {
564 c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(stmt.typ)}`',
565 node.pos)
566 } else if stmt.typ != ast.none_type {
567 if !node.typ.has_flag(.option) && stmt.typ.has_flag(.option) {
568 c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(stmt.typ)}`',
569 node.pos)
570 } else if !node.typ.has_flag(.result) && stmt.typ.has_flag(.result) {
571 c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(stmt.typ)}`',
572 node.pos)
573 }
574 }
575 }
576 }
577 } else if !node.is_comptime && stmt !in [ast.Return, ast.BranchStmt] {
578 pos := if node_is_expr { stmt.pos } else { branch.pos }
579 c.error('`${if_kind}` expression requires an expression as the last statement of every branch',
580 pos)
581 }
582 } else if !node.is_comptime {
583 c.error('`${if_kind}` expression requires an expression as the last statement of every branch',
584 branch.pos)
585 }
586 }
587 end_if:
588 // Also check for returns inside a comp.if's statements, even if its contents aren't parsed
589 if has_return := c.has_return(branch.stmts) {
590 if has_return {
591 nbranches_with_return++
592 } else {
593 nbranches_without_return++
594 }
595 }
596 if comptime_remove_curr_branch_stmts && !c.pref.output_cross_c {
597 // remove the branch statements since they may contain OS-specific code.
598 branch.stmts = []
599 }
600 if node.is_comptime {
601 c.pop_comptime_info()
602 }
603 }
604 if nbranches_with_return > 0 {
605 if nbranches_with_return == node.branches.len {
606 // if/else... where all branches returned
607 c.returns = true
608 }
609 if !node.has_else {
610 // `if cond { return ... }` means that when cond is false, execution continues
611 c.returns = false
612 }
613 if nbranches_without_return > 0 {
614 // some of the branches did not return
615 c.returns = false
616 }
617 }
618 if !node.is_comptime && node.branches.len > 0 && c.has_top_return(node.branches[0].stmts)
619 && node.branches[0].scope != unsafe { nil }
620 && node.branches[0].scope.parent != unsafe { nil } {
621 mut continuation_scope := node.branches[0].scope.parent
622 c.smartcast_none_guard_fallthrough(node.branches[0].cond, mut continuation_scope)
623 }
624 if node.typ == ast.none_type {
625 c.error('invalid if expression, must supply at least one value other than `none`', node.pos)
626 }
627 // if only untyped literals were given default to int/f64
628 node.typ = ast.mktyp(node.typ)
629 if expr_required && !node.has_else {
630 d := if node.is_comptime { '$' } else { '' }
631 c.error('`${if_kind}` expression needs `${d}else` clause', node.pos)
632 }
633 return node.typ
634}
635
636fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, control_expr ast.Expr) {
637 if mut node is ast.InfixExpr {
638 if node.op == .and {
639 c.smartcast_if_conds(mut node.left, mut scope, control_expr)
640 c.smartcast_if_conds(mut node.right, mut scope, control_expr)
641 } else if node.left in [ast.Ident, ast.SelectorExpr] && node.op == .ne
642 && node.right is ast.None {
643 if (node.left is ast.Ident && node.left.is_mut)
644 || (node.left is ast.SelectorExpr && node.left.is_mut) {
645 c.fail_if_immutable(mut node.left)
646 }
647 if c.comptime.inside_comptime_for && c.comptime.comptime_for_field_var != ''
648 && node.left is ast.Ident {
649 mut left_expr := node.left as ast.Ident
650 if mut left_expr.obj is ast.Var {
651 if left_expr.obj.ct_type_var == .field_var {
652 scope.register(ast.Var{
653 name: left_expr.name
654 typ: node.left_type
655 pos: left_expr.pos
656 is_used: true
657 is_mut: left_expr.is_mut
658 is_inherited: left_expr.obj.is_inherited
659 is_unwrapped: true
660 orig_type: node.left_type
661 ct_type_var: .field_var
662 ct_type_unwrapped: true
663 })
664 }
665 }
666 } else {
667 allow_mut_selector_smartcast := node.left is ast.SelectorExpr
668 is_smartcast_ident := node.left is ast.Ident
669 && c.comptime.get_ct_type_var(node.left) == .smartcast
670 if is_smartcast_ident {
671 node.left_type = c.type_resolver.get_type(node.left)
672 c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut
673 scope, true, true, allow_mut_selector_smartcast, true)
674 } else {
675 c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut
676 scope, false, true, allow_mut_selector_smartcast, true)
677 }
678 }
679 } else if node.op == .key_is {
680 if node.left is ast.Ident && node.left.ct_expr {
681 node.left_type = c.type_resolver.get_type(node.left)
682 } else {
683 node.left_type = c.expr(mut node.left)
684 }
685 mut is_comptime := false
686 right_expr := node.right
687 right_type := match right_expr {
688 ast.TypeNode {
689 right_expr.typ
690 }
691 ast.None {
692 ast.none_type_idx
693 }
694 ast.Ident {
695 if right_expr.name == c.comptime.comptime_for_variant_var {
696 is_comptime = true
697 c.type_resolver.get_ct_type_or_default('${c.comptime.comptime_for_variant_var}.typ',
698 ast.no_type)
699 } else {
700 c.error('invalid type `${right_expr}`', right_expr.pos)
701 ast.no_type
702 }
703 }
704 else {
705 c.error('invalid type `${right_expr}`', right_expr.pos())
706 ast.no_type
707 }
708 }
709
710 if right_type != ast.no_type {
711 resolved_right_type := c.unwrap_generic(right_type)
712 right_sym := c.table.final_sym(resolved_right_type)
713 mut expr_type := c.unwrap_generic(node.left_type)
714 left_sym := c.table.sym(expr_type)
715 left_final_sym := c.table.final_sym(expr_type)
716 if left_sym.kind == .aggregate {
717 expr_type = (left_sym.info as ast.Aggregate).sum_type
718 }
719 if left_sym.kind == .interface {
720 if right_sym.kind != .interface {
721 c.type_implements(right_type, expr_type, node.pos)
722 }
723 } else if !c.check_types(right_type, expr_type) && left_final_sym.kind != .sum_type {
724 expect_str := c.table.type_to_str(right_type)
725 expr_str := c.table.type_to_str(expr_type)
726 c.error('cannot use type `${expect_str}` as type `${expr_str}`', node.pos)
727 }
728 if node.left in [ast.Ident, ast.SelectorExpr, ast.IndexExpr]
729 && node.right in [ast.ComptimeType, ast.TypeNode, ast.Ident] {
730 is_variable := if mut node.left is ast.Ident {
731 node.left.kind == .variable
732 } else {
733 true
734 }
735 if is_variable {
736 if (node.left is ast.Ident && node.left.is_mut)
737 || (node.left is ast.SelectorExpr && node.left.is_mut) {
738 c.fail_if_immutable(mut node.left)
739 }
740 // TODO: Add check for sum types in a way that it doesn't break a lot of compiler code
741 if mut node.left is ast.Ident
742 && (left_sym.kind == .interface && right_sym.kind != .interface) {
743 v := scope.find_var(node.left.name) or { &ast.Var{} }
744 if v.is_mut && !node.left.is_mut && !c.implicit_mutability_enabled() {
745 c.error('smart casting a mutable interface value requires `if mut ${node.left.name} is ...`',
746 node.left.pos)
747 }
748 }
749 is_option_unwrap := node.left_type.has_flag(.option)
750 && !right_type.has_flag(.option)
751 skip_smartcast := c.comptime.inside_comptime_for
752 && c.comptime.comptime_for_field_var != '' && node.left is ast.Ident
753 && (node.left as ast.Ident).name == c.comptime.comptime_for_field_var
754 if !skip_smartcast
755 && (left_final_sym.kind in [.interface, .sum_type] || is_option_unwrap) {
756 allow_mut_selector_smartcast := node.left is ast.SelectorExpr
757 c.smartcast(mut node.left, node.left_type, right_type, mut scope,
758 is_comptime, is_option_unwrap, allow_mut_selector_smartcast,
759 allow_mut_selector_smartcast)
760 }
761 }
762 }
763 }
764 }
765 } else if mut node is ast.Likely {
766 c.smartcast_if_conds(mut node.expr, mut scope, control_expr)
767 } else if control_expr is ast.IfExpr && mut node is ast.NodeError { // IfExpr else branch
768 if control_expr.branches.len != 2 {
769 return
770 }
771 first_cond := control_expr.branches[0].cond
772 // `if x !is Type { ... } else { /* smartcast x to Type here */ }`
773 if first_cond is ast.InfixExpr && first_cond.op == .not_is
774 && first_cond.left in [ast.Ident, ast.SelectorExpr, ast.IndexExpr] {
775 right_type := first_cond.right_type
776 left_type := first_cond.left_type
777 left_sym := c.table.final_sym(left_type)
778 is_option_unwrap := left_type.has_flag(.option) && !right_type.has_flag(.option)
779 if left_sym.kind in [.interface, .sum_type] || is_option_unwrap {
780 mut left_expr := first_cond.left
781 allow_mut_selector_smartcast := left_expr is ast.SelectorExpr
782 c.smartcast(mut left_expr, left_type, right_type, mut scope, false,
783 is_option_unwrap, allow_mut_selector_smartcast, allow_mut_selector_smartcast)
784 }
785 }
786 c.smartcast_none_guard_unwrap(control_expr.branches[0].cond, mut scope)
787 }
788}
789
790fn (mut c Checker) smartcast_none_guard_unwrap(cond ast.Expr, mut scope ast.Scope) {
791 mut cond_expr := cond
792 cond_expr = cond_expr.remove_par()
793 // Handles unwrapping on `if var == none { return }` fallthroughs and on
794 // `if var == none { ... } else { /* unwrapped var */ }` branches.
795 if mut cond_expr is ast.InfixExpr {
796 if cond_expr.left !in [ast.Ident, ast.SelectorExpr] || cond_expr.op != .eq
797 || cond_expr.right !is ast.None {
798 return
799 }
800 to_type := cond_expr.left_type.clear_flag(.option)
801 allow_mut_selector_smartcast := match cond_expr.left {
802 ast.SelectorExpr { true }
803 else { false }
804 }
805
806 if c.comptime.get_ct_type_var(cond_expr.left) == .smartcast {
807 cond_expr.left_type = c.type_resolver.get_type(cond_expr.left)
808 c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, true, true,
809 allow_mut_selector_smartcast, true)
810 } else {
811 c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, false, true,
812 allow_mut_selector_smartcast, true)
813 }
814 }
815}
816
817fn (mut c Checker) smartcast_none_guard_fallthrough(cond ast.Expr, mut scope ast.Scope) {
818 mut cond_expr := cond
819 cond_expr = cond_expr.remove_par()
820 if mut cond_expr is ast.InfixExpr {
821 if mut cond_expr.left is ast.Ident && cond_expr.op == .eq && cond_expr.right is ast.None {
822 to_type := cond_expr.left_type.clear_flag(.option)
823 if mut cond_expr.left.obj is ast.Var && cond_expr.left.name in scope.objects {
824 if scope_var := scope.find_var(cond_expr.left.name) {
825 if scope_var.pos.pos == cond_expr.left.obj.pos.pos {
826 cond_expr.left.obj.smartcasts = [to_type]
827 cond_expr.left.obj.is_unwrapped = true
828 scope.update_smartcasts(cond_expr.left.name, to_type, true)
829 return
830 }
831 }
832 }
833 if c.comptime.get_ct_type_var(cond_expr.left) == .smartcast {
834 cond_expr.left_type = c.type_resolver.get_type(cond_expr.left)
835 c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, true,
836 true, false, true)
837 } else {
838 c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, false,
839 true, false, true)
840 }
841 }
842 }
843}
844
845fn (mut c Checker) check_non_expr_branch_last_stmt(stmts []ast.Stmt) {
846 if stmts.len == 0 {
847 return
848 }
849 last_stmt := stmts.last()
850 if last_stmt is ast.ExprStmt && (last_stmt.expr is ast.InfixExpr
851 && last_stmt.expr.op !in [.left_shift, .right_shift, .unsigned_right_shift, .arrow]) {
852 c.error('expression evaluated but not used', last_stmt.pos)
853 }
854}
855