v2 / vlib / v / checker / if.v
845 lines · 825 sloc · 29.63 KB · 288ce180560f3a715e74c3fdf14fcfbbbe1c953a
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 if mut stmt.expr is ast.ConcatExpr {
399 for mut val in stmt.expr.vals {
400 c.check_expr_option_or_result_call(val, c.expr(mut val))
401 }
402 }
403 c.expected_type = former_expected_type
404 if c.table.type_kind(c.expected_type) == .sum_type
405 && c.table.is_sumtype_or_in_variant(c.expected_type, node.typ) {
406 node.is_expr = true
407 node.typ = c.expected_type
408 }
409 if c.expected_type.has_option_or_result() {
410 if node.typ == ast.void_type {
411 node.is_expr = true
412 node.typ = c.expected_type
413 }
414 }
415 if c.expected_type.has_flag(.generic) {
416 if node.typ == ast.void_type {
417 node.is_expr = true
418 node.typ = c.unwrap_generic(c.expected_type)
419 }
420 unsafe {
421 goto end_if
422 }
423 }
424 if c.expected_expr_type != ast.void_type {
425 c.expected_type = c.expected_expr_type
426 }
427 stmt.typ = c.expr(mut stmt.expr)
428 if c.table.type_kind(c.expected_type) == .multi_return
429 && c.table.type_kind(stmt.typ) == .multi_return {
430 if node.typ == ast.void_type {
431 node.is_expr = true
432 node.typ = c.expected_type
433 }
434 }
435 if stmt.typ == ast.void_type && !is_noreturn_callexpr(stmt.expr)
436 && !c.skip_flags {
437 // cannot return void type and use it as expr in any circumstances
438 // (e.g. argument expression, variable declaration / assignment)
439 c.error('the final expression in `if` or `match`, must have a value of a non-void type',
440 stmt.pos)
441 unsafe {
442 goto end_if
443 }
444 }
445 if !c.check_types(stmt.typ, node.typ)
446 && !c.check_types(c.unwrap_generic(stmt.typ), c.unwrap_generic(node.typ)) {
447 if node.typ == ast.void_type {
448 // first branch of if expression
449 node.is_expr = true
450 if stmt.expr.is_auto_deref_var() && stmt.typ.is_ptr() {
451 node.typ = stmt.typ.deref()
452 } else {
453 node.typ = stmt.typ
454 }
455 c.expected_expr_type = node.typ
456 unsafe {
457 goto end_if
458 }
459 } else if node.typ in [ast.float_literal_type, ast.int_literal_type] {
460 if node.typ == ast.int_literal_type {
461 if stmt.typ.is_int() || stmt.typ.is_float() {
462 node.typ = stmt.typ
463 unsafe {
464 goto end_if
465 }
466 }
467 } else { // node.typ == float_literal
468 if stmt.typ.is_float() {
469 node.typ = stmt.typ
470 unsafe {
471 goto end_if
472 }
473 }
474 }
475 }
476 if stmt.typ in [ast.float_literal_type, ast.int_literal_type] {
477 if stmt.typ == ast.int_literal_type {
478 if node.typ.is_int() || node.typ.is_float() {
479 unsafe {
480 goto end_if
481 }
482 }
483 } else { // expr_type == float_literal
484 if node.typ.is_float() {
485 unsafe {
486 goto end_if
487 }
488 }
489 }
490 }
491 if c.is_interface_smartcast_sumtype_variant(stmt.expr,
492 former_expected_type, stmt.typ)
493 {
494 node.is_expr = true
495 node.typ = former_expected_type.clear_option_and_result()
496 unsafe {
497 goto end_if
498 }
499 }
500 if node.is_expr && c.table.sym(former_expected_type).kind == .sum_type {
501 node.typ = former_expected_type
502 unsafe {
503 goto end_if
504 }
505 }
506 if is_noreturn_callexpr(stmt.expr) {
507 unsafe {
508 goto end_if
509 }
510 }
511 if (node.typ.has_option_or_result())
512 && c.table.sym(stmt.typ).kind == .struct
513 && c.type_implements(stmt.typ, ast.error_type, node.pos) {
514 stmt.expr = ast.CastExpr{
515 expr: stmt.expr
516 typname: 'IError'
517 typ: ast.error_type
518 expr_type: stmt.typ
519 pos: node.pos
520 }
521 stmt.typ = ast.error_type
522 unsafe {
523 goto end_if
524 }
525 }
526 if (node.typ == ast.none_type && stmt.typ != ast.none_type)
527 || (stmt.typ == ast.none_type && node.typ != ast.none_type) {
528 node.typ = if stmt.typ != ast.none_type {
529 stmt.typ.set_flag(.option)
530 } else {
531 node.typ.set_flag(.option)
532 }
533 unsafe {
534 goto end_if
535 }
536 }
537 c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(stmt.typ)}`',
538 node.pos)
539 } else {
540 if !node.typ.has_option_or_result() && !node.typ.has_flag(.shared_f)
541 && stmt.typ != ast.voidptr_type
542 && stmt.typ.nr_muls() != node.typ.nr_muls()
543 && !c.is_interface_smartcast_sumtype_variant(stmt.expr, node.typ, stmt.typ) {
544 c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(stmt.typ)}`',
545 node.pos)
546 }
547 if node.is_expr == false && c.type_resolver.is_generic_param_var(stmt.expr) {
548 // generic variable no yet type bounded
549 node.is_expr = true
550 }
551 if c.inside_assign && node.is_expr && !node.typ.has_flag(.shared_f)
552 && stmt.typ != ast.voidptr_type
553 && !c.is_interface_smartcast_sumtype_variant(stmt.expr, node.typ, stmt.typ) {
554 if stmt.typ.is_ptr() != node.typ.is_ptr() {
555 c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(stmt.typ)}`',
556 node.pos)
557 } else if stmt.typ != ast.none_type {
558 if !node.typ.has_flag(.option) && stmt.typ.has_flag(.option) {
559 c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(stmt.typ)}`',
560 node.pos)
561 } else if !node.typ.has_flag(.result) && stmt.typ.has_flag(.result) {
562 c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(stmt.typ)}`',
563 node.pos)
564 }
565 }
566 }
567 }
568 } else if !node.is_comptime && stmt !in [ast.Return, ast.BranchStmt] {
569 pos := if node_is_expr { stmt.pos } else { branch.pos }
570 c.error('`${if_kind}` expression requires an expression as the last statement of every branch',
571 pos)
572 }
573 } else if !node.is_comptime {
574 c.error('`${if_kind}` expression requires an expression as the last statement of every branch',
575 branch.pos)
576 }
577 }
578 end_if:
579 // Also check for returns inside a comp.if's statements, even if its contents aren't parsed
580 if has_return := c.has_return(branch.stmts) {
581 if has_return {
582 nbranches_with_return++
583 } else {
584 nbranches_without_return++
585 }
586 }
587 if comptime_remove_curr_branch_stmts && !c.pref.output_cross_c {
588 // remove the branch statements since they may contain OS-specific code.
589 branch.stmts = []
590 }
591 if node.is_comptime {
592 c.pop_comptime_info()
593 }
594 }
595 if nbranches_with_return > 0 {
596 if nbranches_with_return == node.branches.len {
597 // if/else... where all branches returned
598 c.returns = true
599 }
600 if !node.has_else {
601 // `if cond { return ... }` means that when cond is false, execution continues
602 c.returns = false
603 }
604 if nbranches_without_return > 0 {
605 // some of the branches did not return
606 c.returns = false
607 }
608 }
609 if !node.is_comptime && node.branches.len > 0 && c.has_top_return(node.branches[0].stmts)
610 && node.branches[0].scope != unsafe { nil }
611 && node.branches[0].scope.parent != unsafe { nil } {
612 mut continuation_scope := node.branches[0].scope.parent
613 c.smartcast_none_guard_fallthrough(node.branches[0].cond, mut continuation_scope)
614 }
615 if node.typ == ast.none_type {
616 c.error('invalid if expression, must supply at least one value other than `none`', node.pos)
617 }
618 // if only untyped literals were given default to int/f64
619 node.typ = ast.mktyp(node.typ)
620 if expr_required && !node.has_else {
621 d := if node.is_comptime { '$' } else { '' }
622 c.error('`${if_kind}` expression needs `${d}else` clause', node.pos)
623 }
624 return node.typ
625}
626
627fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, control_expr ast.Expr) {
628 if mut node is ast.InfixExpr {
629 if node.op == .and {
630 c.smartcast_if_conds(mut node.left, mut scope, control_expr)
631 c.smartcast_if_conds(mut node.right, mut scope, control_expr)
632 } else if node.left in [ast.Ident, ast.SelectorExpr] && node.op == .ne
633 && node.right is ast.None {
634 if (node.left is ast.Ident && node.left.is_mut)
635 || (node.left is ast.SelectorExpr && node.left.is_mut) {
636 c.fail_if_immutable(mut node.left)
637 }
638 if c.comptime.inside_comptime_for && c.comptime.comptime_for_field_var != ''
639 && node.left is ast.Ident {
640 mut left_expr := node.left as ast.Ident
641 if mut left_expr.obj is ast.Var {
642 if left_expr.obj.ct_type_var == .field_var {
643 scope.register(ast.Var{
644 name: left_expr.name
645 typ: node.left_type
646 pos: left_expr.pos
647 is_used: true
648 is_mut: left_expr.is_mut
649 is_inherited: left_expr.obj.is_inherited
650 is_unwrapped: true
651 orig_type: node.left_type
652 ct_type_var: .field_var
653 ct_type_unwrapped: true
654 })
655 }
656 }
657 } else {
658 allow_mut_selector_smartcast := node.left is ast.SelectorExpr
659 is_smartcast_ident := node.left is ast.Ident
660 && c.comptime.get_ct_type_var(node.left) == .smartcast
661 if is_smartcast_ident {
662 node.left_type = c.type_resolver.get_type(node.left)
663 c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut
664 scope, true, true, allow_mut_selector_smartcast, true)
665 } else {
666 c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut
667 scope, false, true, allow_mut_selector_smartcast, true)
668 }
669 }
670 } else if node.op == .key_is {
671 if node.left is ast.Ident && node.left.ct_expr {
672 node.left_type = c.type_resolver.get_type(node.left)
673 } else {
674 node.left_type = c.expr(mut node.left)
675 }
676 mut is_comptime := false
677 right_expr := node.right
678 right_type := match right_expr {
679 ast.TypeNode {
680 right_expr.typ
681 }
682 ast.None {
683 ast.none_type_idx
684 }
685 ast.Ident {
686 if right_expr.name == c.comptime.comptime_for_variant_var {
687 is_comptime = true
688 c.type_resolver.get_ct_type_or_default('${c.comptime.comptime_for_variant_var}.typ',
689 ast.no_type)
690 } else {
691 c.error('invalid type `${right_expr}`', right_expr.pos)
692 ast.no_type
693 }
694 }
695 else {
696 c.error('invalid type `${right_expr}`', right_expr.pos())
697 ast.no_type
698 }
699 }
700
701 if right_type != ast.no_type {
702 resolved_right_type := c.unwrap_generic(right_type)
703 right_sym := c.table.final_sym(resolved_right_type)
704 mut expr_type := c.unwrap_generic(node.left_type)
705 left_sym := c.table.sym(expr_type)
706 left_final_sym := c.table.final_sym(expr_type)
707 if left_sym.kind == .aggregate {
708 expr_type = (left_sym.info as ast.Aggregate).sum_type
709 }
710 if left_sym.kind == .interface {
711 if right_sym.kind != .interface {
712 c.type_implements(right_type, expr_type, node.pos)
713 }
714 } else if !c.check_types(right_type, expr_type) && left_final_sym.kind != .sum_type {
715 expect_str := c.table.type_to_str(right_type)
716 expr_str := c.table.type_to_str(expr_type)
717 c.error('cannot use type `${expect_str}` as type `${expr_str}`', node.pos)
718 }
719 if node.left in [ast.Ident, ast.SelectorExpr, ast.IndexExpr]
720 && node.right in [ast.ComptimeType, ast.TypeNode, ast.Ident] {
721 is_variable := if mut node.left is ast.Ident {
722 node.left.kind == .variable
723 } else {
724 true
725 }
726 if is_variable {
727 if (node.left is ast.Ident && node.left.is_mut)
728 || (node.left is ast.SelectorExpr && node.left.is_mut) {
729 c.fail_if_immutable(mut node.left)
730 }
731 // TODO: Add check for sum types in a way that it doesn't break a lot of compiler code
732 if mut node.left is ast.Ident
733 && (left_sym.kind == .interface && right_sym.kind != .interface) {
734 v := scope.find_var(node.left.name) or { &ast.Var{} }
735 if v.is_mut && !node.left.is_mut && !c.implicit_mutability_enabled() {
736 c.error('smart casting a mutable interface value requires `if mut ${node.left.name} is ...`',
737 node.left.pos)
738 }
739 }
740 is_option_unwrap := node.left_type.has_flag(.option)
741 && !right_type.has_flag(.option)
742 skip_smartcast := c.comptime.inside_comptime_for
743 && c.comptime.comptime_for_field_var != '' && node.left is ast.Ident
744 && (node.left as ast.Ident).name == c.comptime.comptime_for_field_var
745 if !skip_smartcast
746 && (left_final_sym.kind in [.interface, .sum_type] || is_option_unwrap) {
747 allow_mut_selector_smartcast := node.left is ast.SelectorExpr
748 c.smartcast(mut node.left, node.left_type, right_type, mut scope,
749 is_comptime, is_option_unwrap, allow_mut_selector_smartcast,
750 allow_mut_selector_smartcast)
751 }
752 }
753 }
754 }
755 }
756 } else if mut node is ast.Likely {
757 c.smartcast_if_conds(mut node.expr, mut scope, control_expr)
758 } else if control_expr is ast.IfExpr && mut node is ast.NodeError { // IfExpr else branch
759 if control_expr.branches.len != 2 {
760 return
761 }
762 first_cond := control_expr.branches[0].cond
763 // `if x !is Type { ... } else { /* smartcast x to Type here */ }`
764 if first_cond is ast.InfixExpr && first_cond.op == .not_is
765 && first_cond.left in [ast.Ident, ast.SelectorExpr, ast.IndexExpr] {
766 right_type := first_cond.right_type
767 left_type := first_cond.left_type
768 left_sym := c.table.final_sym(left_type)
769 is_option_unwrap := left_type.has_flag(.option) && !right_type.has_flag(.option)
770 if left_sym.kind in [.interface, .sum_type] || is_option_unwrap {
771 mut left_expr := first_cond.left
772 allow_mut_selector_smartcast := left_expr is ast.SelectorExpr
773 c.smartcast(mut left_expr, left_type, right_type, mut scope, false,
774 is_option_unwrap, allow_mut_selector_smartcast, allow_mut_selector_smartcast)
775 }
776 }
777 c.smartcast_none_guard_unwrap(control_expr.branches[0].cond, mut scope)
778 }
779}
780
781fn (mut c Checker) smartcast_none_guard_unwrap(cond ast.Expr, mut scope ast.Scope) {
782 mut cond_expr := cond
783 cond_expr = cond_expr.remove_par()
784 // Handles unwrapping on `if var == none { return }` fallthroughs and on
785 // `if var == none { ... } else { /* unwrapped var */ }` branches.
786 if mut cond_expr is ast.InfixExpr {
787 if cond_expr.left !in [ast.Ident, ast.SelectorExpr] || cond_expr.op != .eq
788 || cond_expr.right !is ast.None {
789 return
790 }
791 to_type := cond_expr.left_type.clear_flag(.option)
792 allow_mut_selector_smartcast := match cond_expr.left {
793 ast.SelectorExpr { true }
794 else { false }
795 }
796
797 if c.comptime.get_ct_type_var(cond_expr.left) == .smartcast {
798 cond_expr.left_type = c.type_resolver.get_type(cond_expr.left)
799 c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, true, true,
800 allow_mut_selector_smartcast, true)
801 } else {
802 c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, false, true,
803 allow_mut_selector_smartcast, true)
804 }
805 }
806}
807
808fn (mut c Checker) smartcast_none_guard_fallthrough(cond ast.Expr, mut scope ast.Scope) {
809 mut cond_expr := cond
810 cond_expr = cond_expr.remove_par()
811 if mut cond_expr is ast.InfixExpr {
812 if mut cond_expr.left is ast.Ident && cond_expr.op == .eq && cond_expr.right is ast.None {
813 to_type := cond_expr.left_type.clear_flag(.option)
814 if mut cond_expr.left.obj is ast.Var && cond_expr.left.name in scope.objects {
815 if scope_var := scope.find_var(cond_expr.left.name) {
816 if scope_var.pos.pos == cond_expr.left.obj.pos.pos {
817 cond_expr.left.obj.smartcasts = [to_type]
818 cond_expr.left.obj.is_unwrapped = true
819 scope.update_smartcasts(cond_expr.left.name, to_type, true)
820 return
821 }
822 }
823 }
824 if c.comptime.get_ct_type_var(cond_expr.left) == .smartcast {
825 cond_expr.left_type = c.type_resolver.get_type(cond_expr.left)
826 c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, true,
827 true, false, true)
828 } else {
829 c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, false,
830 true, false, true)
831 }
832 }
833 }
834}
835
836fn (mut c Checker) check_non_expr_branch_last_stmt(stmts []ast.Stmt) {
837 if stmts.len == 0 {
838 return
839 }
840 last_stmt := stmts.last()
841 if last_stmt is ast.ExprStmt && (last_stmt.expr is ast.InfixExpr
842 && last_stmt.expr.op !in [.left_shift, .right_shift, .unsigned_right_shift, .arrow]) {
843 c.error('expression evaluated but not used', last_stmt.pos)
844 }
845}
846