v2 / vlib / v / checker / comptime.v
2334 lines · 2263 sloc · 74.05 KB · e6b78d063953a662a2c9746735674e2f40152508
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 math
6import os
7import v.ast
8import v.pref
9import v.token
10import v.util
11import v.pkgconfig
12import v.type_resolver
13import v.errors
14import strings
15
16@[ignore_overflow]
17fn comptime_power_i64(base i64, exponent i64) i64 {
18 mut exp := exponent
19 mut power := base
20 mut value := i64(1)
21 if exp < 0 {
22 if base == 0 {
23 return -1
24 }
25 return if base * base != 1 {
26 0
27 } else {
28 if exp & 1 > 0 {
29 base
30 } else {
31 1
32 }
33 }
34 }
35 for exp > 0 {
36 if exp & 1 > 0 {
37 value *= power
38 }
39 power *= power
40 exp >>= 1
41 }
42 return value
43}
44
45fn comptime_power_f64(base f64, exponent f64) f64 {
46 return math.pow(base, exponent)
47}
48
49fn comptime_power_value(left ast.ComptTimeConstValue, right ast.ComptTimeConstValue) ?ast.ComptTimeConstValue {
50 if left_i := left.i64() {
51 if right_i := right.i64() {
52 return comptime_power_i64(left_i, right_i)
53 }
54 }
55 if left_f := left.f64() {
56 if right_f := right.f64() {
57 return comptime_power_f64(left_f, right_f)
58 }
59 }
60 return none
61}
62
63fn comptime_compare_i64_values(op token.Kind, left i64, right i64) ?bool {
64 return match op {
65 .eq { left == right }
66 .ne { left != right }
67 .gt { left > right }
68 .lt { left < right }
69 .ge { left >= right }
70 .le { left <= right }
71 else { none }
72 }
73}
74
75fn comptime_compare_f64_values(op token.Kind, left f64, right f64) ?bool {
76 return match op {
77 .eq { left == right }
78 .ne { left != right }
79 .gt { left > right }
80 .lt { left < right }
81 .ge { left >= right }
82 .le { left <= right }
83 else { none }
84 }
85}
86
87fn comptime_compare_string_values(op token.Kind, left string, right string) ?bool {
88 return match op {
89 .eq { left == right }
90 .ne { left != right }
91 else { none }
92 }
93}
94
95struct ComptimeComparisonResult {
96 val bool
97 keep_stmts bool
98}
99
100fn (mut c Checker) eval_comptime_type_meta_value(typ ast.Type, field_name string) ?ast.ComptTimeConstValue {
101 base_type := c.unwrap_generic(typ)
102 match field_name {
103 'name' {
104 return c.table.sym(base_type).name
105 }
106 'idx', 'typ' {
107 return i64(int(base_type))
108 }
109 'unaliased_typ' {
110 return i64(int(c.table.unaliased_type(base_type)))
111 }
112 'indirections' {
113 return i64(base_type.nr_muls())
114 }
115 'key_type', 'value_type', 'element_type', 'pointee_type', 'payload_type' {
116 resolved_type := c.type_resolver.typeof_field_type(base_type, field_name)
117 if resolved_type != ast.no_type {
118 return i64(int(c.unwrap_generic(resolved_type)))
119 }
120 }
121 else {}
122 }
123
124 return none
125}
126
127fn (c &Checker) is_generic_type_expr_ident(name string) bool {
128 return util.is_generic_type_name(name) && c.table.cur_fn != unsafe { nil }
129 && name in c.table.cur_fn.generic_names
130}
131
132fn (c &Checker) is_comptime_type_expr(expr ast.Expr) bool {
133 return match expr {
134 ast.ParExpr {
135 c.is_comptime_type_expr(expr.expr)
136 }
137 ast.TypeNode {
138 true
139 }
140 ast.TypeOf {
141 true
142 }
143 ast.Ident {
144 c.is_generic_type_expr_ident(expr.name)
145 }
146 ast.SelectorExpr {
147 is_array_init_type_expr_field(expr.field_name) && c.is_comptime_type_expr(expr.expr)
148 }
149 else {
150 false
151 }
152 }
153}
154
155fn (mut c Checker) comptime_call_type_expr_type(mut expr ast.Expr) ast.Type {
156 match mut expr {
157 ast.ParExpr {
158 return c.comptime_call_type_expr_type(mut expr.expr)
159 }
160 ast.TypeNode {
161 return c.recheck_concrete_type(expr.typ)
162 }
163 ast.TypeOf {
164 if expr.is_type {
165 return c.recheck_concrete_type(expr.typ)
166 }
167 if expr.typ == 0 || expr.typ == ast.void_type || expr.typ == ast.no_type {
168 expr.typ = c.expr(mut expr.expr)
169 }
170 resolved_type := c.recheck_concrete_type(expr.typ)
171 if resolved_type != 0 && resolved_type != ast.void_type && resolved_type != ast.no_type {
172 return resolved_type
173 }
174 return c.recheck_concrete_type(c.type_resolver.typeof_type(expr.expr, expr.typ))
175 }
176 ast.ArrayInit {
177 if expr.elem_type_expr !is ast.EmptyExpr {
178 c.resolve_array_init_elem_type_expr(mut expr)
179 return expr.typ
180 }
181 return c.expr(mut expr)
182 }
183 ast.SelectorExpr {
184 if is_array_init_type_expr_field(expr.field_name) {
185 base_type := c.comptime_call_type_expr_type(mut expr.expr)
186 resolved := c.type_resolver.typeof_field_type(base_type, expr.field_name)
187 if resolved != ast.no_type {
188 return c.recheck_concrete_type(resolved)
189 }
190 }
191 c.expr(mut expr)
192 return c.recheck_concrete_type(c.get_expr_type(expr))
193 }
194 ast.Ident {
195 if c.is_generic_type_expr_ident(expr.name) {
196 return c.table.find_type(expr.name).set_flag(.generic)
197 }
198 return c.recheck_concrete_type(c.get_expr_type(expr))
199 }
200 else {
201 return c.recheck_concrete_type(c.expr(mut expr))
202 }
203 }
204}
205
206fn (mut c Checker) eval_comptime_type_selector_value(expr ast.SelectorExpr) ?ast.ComptTimeConstValue {
207 if expr.expr is ast.Ident && c.table.cur_fn != unsafe { nil } {
208 idx := c.table.cur_fn.generic_names.index(expr.expr.name)
209 if idx >= 0 && idx < c.table.cur_concrete_types.len {
210 return c.eval_comptime_type_meta_value(c.table.cur_concrete_types[idx], expr.field_name)
211 }
212 }
213 if expr.expr is ast.TypeOf {
214 mut resolved_type := c.recheck_concrete_type(expr.expr.typ)
215 if resolved_type == ast.void_type || resolved_type == ast.no_type {
216 resolved_type = c.recheck_concrete_type(expr.name_type)
217 }
218 if resolved_type == ast.void_type || resolved_type == ast.no_type {
219 resolved_type = c.type_resolver.typeof_type(expr.expr.expr, expr.name_type)
220 }
221 if resolved_type != ast.void_type && resolved_type != ast.no_type {
222 return c.eval_comptime_type_meta_value(resolved_type, expr.field_name)
223 }
224 }
225 return none
226}
227
228fn (c &Checker) comptime_expr_needs_multi_pass(expr ast.Expr) bool {
229 return match expr {
230 ast.TypeOf {
231 true
232 }
233 ast.Ident {
234 (c.table.cur_fn != unsafe { nil } && expr.name in c.table.cur_fn.generic_names)
235 || (expr.obj is ast.Var && expr.obj.typ.has_flag(.generic))
236 || expr.ct_expr
237 }
238 ast.SelectorExpr {
239 expr.expr is ast.TypeOf || (expr.expr is ast.Ident && c.table.cur_fn != unsafe { nil }
240 && expr.expr.name in c.table.cur_fn.generic_names)
241 || c.comptime_expr_needs_multi_pass(expr.expr)
242 }
243 ast.InfixExpr {
244 c.comptime_expr_needs_multi_pass(expr.left)
245 || c.comptime_expr_needs_multi_pass(expr.right)
246 }
247 ast.CastExpr {
248 c.comptime_expr_needs_multi_pass(expr.expr)
249 }
250 ast.IndexExpr {
251 c.comptime_expr_needs_multi_pass(expr.left)
252 || c.comptime_expr_needs_multi_pass(expr.index)
253 }
254 ast.ParExpr {
255 c.comptime_expr_needs_multi_pass(expr.expr)
256 }
257 ast.PostfixExpr {
258 c.comptime_expr_needs_multi_pass(expr.expr)
259 }
260 ast.PrefixExpr {
261 c.comptime_expr_needs_multi_pass(expr.right)
262 }
263 else {
264 false
265 }
266 }
267}
268
269fn (mut c Checker) try_eval_comptime_comparison(mut left ast.Expr, mut right ast.Expr, op token.Kind) ?ComptimeComparisonResult {
270 start_errors := c.nr_errors
271 left_type := c.unwrap_generic(c.expr(mut left))
272 right_type := c.unwrap_generic(c.expr(mut right))
273 if c.nr_errors > start_errors {
274 return none
275 }
276 left_value := c.eval_comptime_const_expr(left, 0)?
277 right_value := c.eval_comptime_const_expr(right, 0)?
278 keep_stmts := c.comptime_expr_needs_multi_pass(left) || c.comptime_expr_needs_multi_pass(right)
279 if left_type == ast.string_type || right_type == ast.string_type {
280 return ComptimeComparisonResult{
281 val: comptime_compare_string_values(op, left_value.string()?,
282 right_value.string()?)?
283 keep_stmts: keep_stmts
284 }
285 }
286 if left_type.is_float() || right_type.is_float() {
287 return ComptimeComparisonResult{
288 val: comptime_compare_f64_values(op, left_value.f64()?, right_value.f64()?)?
289 keep_stmts: keep_stmts
290 }
291 }
292 return ComptimeComparisonResult{
293 val: comptime_compare_i64_values(op, left_value.i64()?, right_value.i64()?)?
294 keep_stmts: keep_stmts
295 }
296}
297
298fn (mut c Checker) is_string_array_type(typ ast.Type) bool {
299 final_typ := c.table.unaliased_type(c.unwrap_generic(typ))
300 sym := c.table.final_sym(final_typ)
301 if sym.info is ast.Array {
302 return c.table.unaliased_type(sym.info.elem_type) == ast.string_type
303 }
304 return false
305}
306
307fn (mut c Checker) check_comptime_method_string_auto_expand(mut node ast.ComptimeCall) bool {
308 if c.comptime.comptime_for_method == unsafe { nil } || node.args.len == 0
309 || node.args.any(it.expr is ast.ArrayDecompose) {
310 return false
311 }
312 method := c.comptime.comptime_for_method
313 if method.params.len - 1 < node.args.len {
314 return false
315 }
316 last_arg := node.args.last()
317 if !c.is_string_array_type(last_arg.typ) {
318 return false
319 }
320 next_param := method.params[node.args.len].typ
321 if c.is_string_array_type(next_param) {
322 return false
323 }
324 c.error('to auto-expand `[]string` arguments in comptime method calls, use `...${last_arg.expr}`',
325 last_arg.pos)
326 return true
327}
328
329fn (mut c Checker) check_comptime_method_call_args(mut node ast.ComptimeCall) {
330 if c.comptime.comptime_for_method == unsafe { nil } {
331 return
332 }
333 if node.args.any(it.expr is ast.ArrayDecompose) {
334 return
335 }
336 method := c.comptime.comptime_for_method
337 if method.params.len == 0 {
338 return
339 }
340 nr_method_args := method.params.len - 1
341 if !method.is_variadic && node.args.len > nr_method_args {
342 return
343 }
344 receiver_name := c.table.type_to_str(c.unwrap_generic(method.receiver_type).set_nr_muls(0))
345 for i in 0 .. node.args.len {
346 mut arg := node.args[i]
347 param := if method.is_variadic && i >= nr_method_args - 1 {
348 method.params.last()
349 } else if i + 1 < method.params.len {
350 method.params[i + 1]
351 } else {
352 break
353 }
354 arg = c.implicit_mut_call_arg(param, arg)
355 node.args[i] = arg
356 param_share := param.typ.share()
357 if arg.is_mut {
358 to_lock, pos := c.fail_if_immutable(mut arg.expr)
359 if !param.is_mut {
360 tok := arg.share.str()
361 c.error('`${method.name}` parameter `${param.name}` is not `${tok}`, `${tok}` is not needed`',
362 arg.expr.pos())
363 continue
364 }
365 if param_share != arg.share {
366 c.error('wrong shared type `${arg.share.str()}`, expected: `${param_share.str()}`',
367 arg.expr.pos())
368 }
369 if to_lock != '' && param_share != .shared_t {
370 c.error('${to_lock} is `shared` and must be `lock`ed to be passed as `mut`', pos)
371 }
372 } else if param.is_mut {
373 tok := param.specifier()
374 c.error('method `${method.name}` parameter `${param.name}` is `${tok}`, so use `${tok} ${arg.expr}` instead',
375 arg.expr.pos())
376 continue
377 } else {
378 c.fail_if_unreadable(arg.expr, arg.typ, 'argument')
379 }
380 c.check_expected_call_arg(arg.typ, c.unwrap_generic(param.typ), method.language, arg) or {
381 if arg.typ != ast.void_type {
382 c.error('${err.msg()} in argument ${i + 1} to `${receiver_name}.${method.name}`',
383 arg.pos)
384 }
385 }
386 }
387}
388
389fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
390 if node.left !is ast.EmptyExpr {
391 node.left_type = c.expr(mut node.left)
392 }
393 if node.kind == .compile_error {
394 mut err_pos := node.pos
395 // During generic rechecks, report the instantiation site instead of the
396 // `$compile_error()` directive inside the generic body.
397 if c.fn_level > 0 && c.table.cur_fn != unsafe { nil } {
398 call_key := c.build_generic_call_key(c.table.cur_fn.fkey(), c.table.cur_concrete_types)
399 if pos := c.generic_call_positions[call_key] {
400 err_pos = pos
401 }
402 }
403 c.error(c.comptime_call_msg(node), err_pos)
404 return ast.void_type
405 } else if node.kind == .compile_warn {
406 c.warn(c.comptime_call_msg(node), node.pos)
407 return ast.void_type
408 }
409 if node.kind == .env {
410 env_value := util.resolve_env_value("\$env('${node.args_var}')", false) or {
411 c.error(err.msg(), node.env_pos)
412 return ast.string_type
413 }
414 node.env_value = env_value
415 return ast.string_type
416 }
417 if node.kind == .d {
418 node.resolve_compile_value(c.pref.compile_values) or {
419 c.error(err.msg(), node.pos)
420 return ast.void_type
421 }
422 return node.result_type
423 }
424 if node.kind in [.zero, .new] {
425 if node.args.len != 1 {
426 c.error('`\$${node.method_name}()` expects 1 type argument', node.pos)
427 return ast.void_type
428 }
429 mut type_expr := node.args[0].expr
430 resolved_type := c.comptime_call_type_expr_type(mut type_expr)
431 node.args[0].expr = type_expr
432 if resolved_type == ast.void_type || resolved_type == ast.no_type {
433 c.error('`\$${node.method_name}()` expects a valid type expression', node.args[0].pos)
434 return ast.void_type
435 }
436 node.args[0].typ = resolved_type
437 node.result_type = if node.kind == .new { resolved_type.ref() } else { resolved_type }
438 return node.result_type
439 }
440 if node.kind == .embed_file {
441 if node.args.len == 1 {
442 embed_arg := node.args[0]
443 mut raw_path := ''
444 if embed_arg.expr is ast.AtExpr {
445 mut expr := embed_arg.expr
446 c.at_expr(mut expr)
447 raw_path = expr.val
448 }
449 if embed_arg.expr is ast.StringLiteral {
450 raw_path = embed_arg.expr.val
451 } else if embed_arg.expr is ast.Ident {
452 if var := c.fn_scope.find_var(embed_arg.expr.name) {
453 if var.expr is ast.StringLiteral {
454 raw_path = var.expr.val
455 }
456 }
457 }
458 mut escaped_path := raw_path.replace('/', os.path_separator)
459 // Validate that the epath exists, and that it is actually a file.
460 if escaped_path == '' {
461 c.error('supply a valid relative or absolute file path to the file to embed, that is known at compile time',
462 node.pos)
463 return ast.string_type
464 }
465 escaped_path = c.resolve_pseudo_variables(escaped_path, node.pos) or {
466 return ast.string_type
467 }
468 abs_path := os.real_path(escaped_path)
469 // check absolute path first
470 if !os.exists(abs_path) {
471 // ... look relative to the source file:
472 escaped_path = os.real_path(os.join_path_single(os.dir(c.file.path), escaped_path))
473 if !os.exists(escaped_path) {
474 c.error('"${escaped_path}" does not exist so it cannot be embedded', node.pos)
475 return ast.string_type
476 }
477 if !os.is_file(escaped_path) {
478 c.error('"${escaped_path}" is not a file so it cannot be embedded', node.pos)
479 return ast.string_type
480 }
481 } else {
482 escaped_path = abs_path
483 }
484 node.embed_file.rpath = raw_path
485 node.embed_file.apath = escaped_path
486 }
487 // c.file.embedded_files << node.embed_file
488 if node.embed_file.compression_type !in ast.valid_comptime_compression_types {
489 supported := ast.valid_comptime_compression_types.map('.${it}').join(', ')
490 c.error('not supported compression type: .${node.embed_file.compression_type}. supported: ${supported}',
491 node.pos)
492 }
493 return c.table.find_type('v.embed_file.EmbedFileData')
494 }
495 if node.is_template {
496 // TODO: assoc parser bug
497 save_cur_fn := c.table.cur_fn
498 pref_ := *c.pref
499 pref2 := &pref.Preferences{
500 ...pref_
501 is_template: true
502 }
503 mut c2 := new_checker(c.table, pref2)
504 c2.comptime_call_pos = node.pos.pos
505 template_parser_errors := node.veb_tmpl.errors.clone()
506 template_parser_warnings := node.veb_tmpl.warnings.clone()
507 template_parser_notices := node.veb_tmpl.notices.clone()
508 c2.check(mut node.veb_tmpl)
509
510 // Cache template file content for error display using the relative path
511 // The template_paths contains full paths, but errors use relative paths from node.veb_tmpl.path
512 if node.veb_tmpl.template_paths.len > 0 {
513 // Cache the main template file
514 main_template_path := node.veb_tmpl.template_paths[0]
515 if content := os.read_file(main_template_path) {
516 util.set_source_for_path(node.veb_tmpl.path, content)
517 }
518 }
519
520 // Fix error line numbers using template line mapping
521 line_map := node.veb_tmpl.template_line_map
522 if line_map.len > 0 {
523 for i, err in c2.errors {
524 if err.pos.line_nr >= 0 && err.pos.line_nr < line_map.len {
525 line_info := line_map[err.pos.line_nr]
526 c2.errors[i] = errors.Error{
527 message: err.message
528 details: err.details
529 file_path: line_info.tmpl_path
530 pos: token.Pos{
531 ...err.pos
532 line_nr: line_info.tmpl_line
533 }
534 reporter: err.reporter
535 call_stack: err.call_stack
536 }
537 }
538 }
539 for i, warn in c2.warnings {
540 if warn.pos.line_nr >= 0 && warn.pos.line_nr < line_map.len {
541 line_info := line_map[warn.pos.line_nr]
542 c2.warnings[i] = errors.Warning{
543 message: warn.message
544 details: warn.details
545 file_path: line_info.tmpl_path
546 pos: token.Pos{
547 ...warn.pos
548 line_nr: line_info.tmpl_line
549 }
550 reporter: warn.reporter
551 call_stack: warn.call_stack
552 }
553 }
554 }
555 for i, notice in c2.notices {
556 if notice.pos.line_nr >= 0 && notice.pos.line_nr < line_map.len {
557 line_info := line_map[notice.pos.line_nr]
558 c2.notices[i] = errors.Notice{
559 message: notice.message
560 details: notice.details
561 file_path: line_info.tmpl_path
562 pos: token.Pos{
563 ...notice.pos
564 line_nr: line_info.tmpl_line
565 }
566 reporter: notice.reporter
567 call_stack: notice.call_stack
568 }
569 }
570 }
571 }
572
573 c.warnings << template_parser_warnings
574 c.errors << template_parser_errors
575 c.notices << template_parser_notices
576 c.nr_warnings += template_parser_warnings.len
577 c.nr_errors += template_parser_errors.len
578 c.nr_notices += template_parser_notices.len
579
580 c.warnings << c2.warnings
581 c.errors << c2.errors
582 c.notices << c2.notices
583 c.nr_warnings += c2.nr_warnings
584 c.nr_errors += c2.nr_errors
585 c.nr_notices += c2.nr_notices
586
587 c.table.cur_fn = save_cur_fn
588 }
589 if node.kind == .html {
590 ret_sym := c.table.sym(c.table.cur_fn.return_type)
591 if ret_sym.cname != 'veb__Result' {
592 c.error("`\$veb.html()` must be called inside a web method, e.g. `fn (mut app App) foo(mut ctx Context) veb.Result { return \$veb.html('index.html') }`",
593 node.pos)
594 }
595 rtyp := c.table.find_type('veb.Result')
596 node.result_type = rtyp
597 return rtyp
598 }
599 if node.method_name == 'method' {
600 if c.inside_anon_fn && 'method' !in c.cur_anon_fn.inherited_vars.map(it.name) {
601 c.error('undefined ident `method` in the anonymous function', node.pos)
602 }
603 for i, mut arg in node.args {
604 // check each arg expression
605 node.args[i].typ = c.expr(mut arg.expr)
606 }
607 if c.check_comptime_method_string_auto_expand(mut node) {
608 return ast.void_type
609 }
610 c.check_comptime_method_call_args(mut node)
611 c.markused_comptimecall(mut node)
612 c.stmts_ending_with_expression(mut node.or_block.stmts, c.expected_or_type)
613 return c.type_resolver.get_type(node)
614 }
615 if node.kind == .res {
616 if !c.inside_defer {
617 c.error('`res` can only be used in defer blocks', node.pos)
618 return ast.void_type
619 }
620
621 if c.fn_return_type == ast.void_type {
622 c.error('`res` can only be used in functions that returns something', node.pos)
623 return ast.void_type
624 }
625
626 sym := c.table.sym(c.fn_return_type)
627
628 if c.fn_return_type.has_flag(.result) {
629 c.error('`res` cannot be used in functions that returns a Result', node.pos)
630 return ast.void_type
631 }
632
633 if sym.info is ast.MultiReturn {
634 if node.args_var == '' {
635 c.error('`res` requires an index of the returned value', node.pos)
636 return ast.void_type
637 }
638 idx := node.args_var.int()
639 if idx < 0 || idx >= sym.info.types.len {
640 c.error('index ${idx} out of range of ${sym.info.types.len} return types', node.pos)
641 return ast.void_type
642 }
643 return sym.info.types[idx]
644 }
645
646 return c.fn_return_type
647 }
648 if node.is_template {
649 return ast.string_type
650 }
651 // s.$my_str()
652 v := node.scope.find_var(node.method_name) or {
653 c.error('unknown identifier `${node.method_name}`', node.method_pos)
654 return ast.void_type
655 }
656 if v.typ != ast.string_type {
657 s := c.expected_msg(v.typ, ast.string_type)
658 c.error('invalid string method call: ${s}', node.method_pos)
659 return ast.void_type
660 }
661 // note: we should use a compile-time evaluation function rather than handle here
662 // mut variables will not work after init
663 mut method_name := ''
664 if v.expr is ast.StringLiteral {
665 method_name = v.expr.val
666 } else {
667 c.error('todo: not a string literal', node.method_pos)
668 }
669 left_type := c.unwrap_generic(node.left_type)
670 left_sym := c.table.sym(left_type)
671 f := left_sym.find_method(method_name) or {
672 c.error('could not find method `${method_name}`', node.method_pos)
673 return ast.void_type
674 }
675 c.mark_fn_decl_as_referenced(f.fkey())
676 c.markused_comptime_call(true, '${int(left_type)}.${method_name}')
677 node.result_type = f.return_type
678 return f.return_type
679}
680
681fn (mut c Checker) comptime_call_msg(node ast.ComptimeCall) string {
682 return if node.args_var.len > 0 {
683 node.args_var
684 } else if value := c.eval_comptime_const_expr(node.args[0].expr, -1) {
685 value.string() or { '' }
686 } else {
687 ''
688 }
689}
690
691fn (mut c Checker) comptime_selector_method_value(mut node ast.ComptimeSelector) ast.Type {
692 if c.comptime.comptime_for_method == unsafe { nil } {
693 c.error('compile time method access can only be used when iterating over `T.methods`',
694 node.field_expr.pos())
695 return ast.void_type
696 }
697 method := c.comptime.comptime_for_method
698 node.is_method = true
699 fn_type := c.type_resolver.get_comptime_selector_type(node, ast.void_type)
700 node.typ = c.unwrap_generic(fn_type)
701 c.mark_fn_decl_as_referenced(method.fkey())
702 c.markused_comptime_call(true, '${int(method.params[0].typ)}.${method.name}')
703 receiver := c.unwrap_generic(method.params[0].typ)
704 if receiver.nr_muls() > 0 && !c.inside_unsafe {
705 rec_sym := c.table.sym(receiver.set_nr_muls(0))
706 if !rec_sym.is_heap() {
707 suggestion := if rec_sym.kind == .struct {
708 'declaring `${rec_sym.name}` as `@[heap]`'
709 } else {
710 'wrapping the `${rec_sym.name}` object in a `struct` declared as `@[heap]`'
711 }
712 c.error('method `${c.table.type_to_str(receiver.idx_type())}.${method.name}` cannot be used as a variable outside `unsafe` blocks as its receiver might refer to an object stored on stack. Consider ${suggestion}.',
713 node.left.pos().extend(node.pos))
714 }
715 }
716 c.table.used_features.anon_fn = true
717 return fn_type
718}
719
720fn (mut c Checker) comptime_selector(mut node ast.ComptimeSelector) ast.Type {
721 node.left_type = c.expr(mut node.left)
722 mut expr_type := c.unwrap_generic(c.expr(mut node.field_expr))
723 if c.type_resolver.info.is_comptime_method_selector(node.field_expr) {
724 return c.comptime_selector_method_value(mut node)
725 }
726 expr_sym := c.table.sym(expr_type)
727 if expr_type != ast.string_type {
728 c.error('expected `string` instead of `${expr_sym.name}` (e.g. `field.name`)',
729 node.field_expr.pos())
730 }
731 if mut node.field_expr is ast.SelectorExpr {
732 left_pos := node.field_expr.expr.pos()
733 if c.type_resolver.type_map.len == 0 {
734 c.error('compile time field access can only be used when iterating over `T.fields`',
735 left_pos)
736 }
737 node.is_name = node.field_expr.field_name == 'name'
738 if mut node.field_expr.expr is ast.Ident {
739 node.typ_key = if c.comptime.comptime_for_field_value.name != '' {
740 '${node.field_expr.expr.name}.typ|${c.comptime.comptime_for_field_value.name}'
741 } else {
742 '${node.field_expr.expr.name}.typ'
743 }
744 }
745 expr_type = c.type_resolver.get_comptime_selector_type(node, ast.void_type)
746 if expr_type != ast.void_type {
747 if node.or_block.kind == .propagate_option {
748 return expr_type.clear_flag(.option)
749 }
750 return expr_type
751 }
752 expr_name := node.field_expr.expr.str()
753 if expr_name in c.type_resolver.type_map {
754 return c.type_resolver.get_ct_type_or_default(expr_name, ast.void_type)
755 }
756 c.error('unknown `\$for` variable `${expr_name}`', left_pos)
757 } else {
758 c.error('expected selector expression e.g. `$(field.name)`', node.field_expr.pos())
759 }
760 return ast.void_type
761}
762
763fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) {
764 mut typ := if node.expr !is ast.EmptyExpr {
765 node.typ = c.expr(mut node.expr)
766 c.unwrap_generic(node.typ)
767 } else if node.typ != ast.void_type {
768 c.unwrap_generic(node.typ)
769 } else {
770 node.typ = c.expr(mut node.expr)
771 c.unwrap_generic(node.typ)
772 }
773 if node.expr !is ast.EmptyExpr {
774 resolved_typ := c.type_resolver.get_type(node.expr)
775 if resolved_typ != ast.void_type {
776 node.typ = resolved_typ
777 typ = c.unwrap_generic(node.typ)
778 }
779 }
780 sym := if node.typ != c.field_data_type {
781 c.table.final_sym(typ)
782 } else {
783 c.table.final_sym(c.comptime.comptime_for_field_type)
784 }
785 if sym.kind == .placeholder || typ.has_flag(.generic) {
786 c.error('\$for expects a type name or variable name to be used here, but ${sym.name} is not a type or variable name',
787 node.typ_pos)
788 return
789 } else if sym.kind == .void {
790 c.error('only known compile-time variables can be used', node.typ_pos)
791 return
792 }
793 if node.kind == .fields {
794 if sym.kind in [.struct, .interface] {
795 mut fields := []ast.StructField{}
796 match sym.info {
797 ast.Struct {
798 fields = sym.info.fields.clone()
799 }
800 ast.Interface {
801 fields = sym.info.fields.clone()
802 }
803 else {
804 c.error('iterating over .fields is supported only for structs and interfaces, and ${sym.name} is neither',
805 node.typ_pos)
806 return
807 }
808 }
809
810 has_different_types := fields.len > 1
811 && !fields.all(c.check_basic(it.typ, fields[0].typ))
812 if fields.len == 0 {
813 // force eval `node.stmts` to set their types
814 fields << ast.StructField{
815 typ: ast.error_type
816 }
817 }
818 for field in fields {
819 c.push_new_comptime_info()
820 prev_inside_x_matches_type := c.inside_x_matches_type
821 c.comptime.inside_comptime_for = true
822 if c.field_data_type == 0 {
823 c.field_data_type = c.table.find_type('FieldData')
824 }
825 c.comptime.comptime_for_field_value = field
826 c.comptime.comptime_for_field_var = node.val_var
827 resolved_field_typ := c.unwrap_generic(field.typ)
828 c.type_resolver.update_ct_type(node.val_var, c.field_data_type)
829 c.type_resolver.update_ct_type('${node.val_var}.typ', resolved_field_typ)
830 c.comptime.comptime_for_field_type = resolved_field_typ
831 c.comptime.has_different_types = has_different_types
832 c.stmts(mut node.stmts)
833
834 if resolved_field_typ != ast.no_type {
835 unwrapped_expr_type := c.unwrap_generic(resolved_field_typ)
836 tsym := c.table.sym(unwrapped_expr_type)
837 c.markused_comptimefor(mut node, unwrapped_expr_type)
838 if tsym.kind == .array_fixed {
839 info := tsym.info as ast.ArrayFixed
840 if !info.is_fn_ret {
841 // for dumping fixed array we must register the fixed array struct to return from function
842 c.table.find_or_register_array_fixed(info.elem_type, info.size,
843 info.size_expr, true)
844 }
845 }
846 }
847 c.inside_x_matches_type = prev_inside_x_matches_type
848 c.pop_comptime_info()
849 }
850 } else if node.typ != ast.void_type && c.table.generic_type_names(node.typ).len == 0
851 && sym.kind != .placeholder {
852 c.error('iterating over .fields is supported only for structs and interfaces, and ${sym.name} is neither',
853 node.typ_pos)
854 return
855 }
856 } else if node.kind == .values {
857 if sym.kind == .enum {
858 if sym.info is ast.Enum {
859 for _ in sym.info.vals {
860 c.push_new_comptime_info()
861 c.comptime.inside_comptime_for = true
862 if c.enum_data_type == 0 {
863 c.enum_data_type = c.table.find_type('EnumData')
864 }
865 c.comptime.comptime_for_enum_var = node.val_var
866 c.type_resolver.update_ct_type(node.val_var, c.enum_data_type)
867 c.type_resolver.update_ct_type('${node.val_var}.typ', node.typ)
868 c.stmts(mut node.stmts)
869 c.pop_comptime_info()
870 }
871 }
872 } else {
873 c.error('iterating over .values is supported only for enums, and ${sym.name} is not an enum',
874 node.typ_pos)
875 return
876 }
877 } else if node.kind == .methods {
878 mut methods := c.table.get_type_methods(typ)
879 if methods.len == 0 {
880 // force eval `node.stmts` to set their types
881 methods << ast.Fn{}
882 }
883 for method in methods {
884 c.push_new_comptime_info()
885 c.comptime.inside_comptime_for = true
886 c.comptime.comptime_for_method = unsafe { &method }
887 c.comptime.comptime_for_method_var = node.val_var
888 c.comptime.comptime_for_method_ret_type = method.return_type
889 c.type_resolver.update_ct_type('${node.val_var}.return_type', method.return_type)
890 if method.params.len > 0 {
891 for j, arg in method.params[1..] {
892 c.type_resolver.update_ct_type('${node.val_var}.args[${j}].typ', arg.typ.idx())
893 }
894 }
895 c.stmts(mut node.stmts)
896 c.pop_comptime_info()
897 }
898 } else if node.kind == .params {
899 if !(sym.kind == .function || sym.name == 'FunctionData') {
900 c.error('iterating over `.params` is supported only for functions, and `${sym.name}` is not a function',
901 node.typ_pos)
902 return
903 }
904
905 func := if sym.info is ast.FnType { &sym.info.func } else { c.comptime.comptime_for_method }
906 mut params := if func.is_method { func.params[1..] } else { func.params }
907 if params.len == 0 {
908 // force eval `node.stmts` to set their types
909 params << ast.Param{}
910 }
911 // example: fn (mut d MyStruct) add(x int, y int) string
912 // `d` is params[0], `x` is params[1], `y` is params[2]
913 // so we at least has one param (`d`) for method
914 for param in params {
915 c.push_new_comptime_info()
916 c.comptime.inside_comptime_for = true
917 c.comptime.comptime_for_method_param_var = node.val_var
918 c.type_resolver.update_ct_type('${node.val_var}.typ', param.typ)
919 c.stmts(mut node.stmts)
920 c.pop_comptime_info()
921 }
922 } else if node.kind == .attributes {
923 mut attrs := c.table.get_attrs(sym)
924 if attrs.len == 0 {
925 // force eval `node.stmts` to set their types
926 attrs << ast.Attr{}
927 }
928 for attr in attrs {
929 c.push_new_comptime_info()
930 c.comptime.inside_comptime_for = true
931 c.comptime.comptime_for_attr_var = node.val_var
932 c.comptime.comptime_for_attr_value = attr
933 c.stmts(mut node.stmts)
934 c.pop_comptime_info()
935 }
936 } else if node.kind == .variants {
937 if c.variant_data_type == 0 {
938 c.variant_data_type = c.table.find_type('VariantData')
939 }
940 mut variants := []ast.Type{}
941 if c.comptime.comptime_for_field_var != '' && typ == c.field_data_type {
942 sumtype_sym := c.table.sym(c.comptime.comptime_for_field_type)
943 if sumtype_sym.kind == .sum_type {
944 variants = (sumtype_sym.info as ast.SumType).variants.clone()
945 }
946 } else if sym.kind != .sum_type {
947 c.error('${sym.name} is not Sum type to use with .variants', node.typ_pos)
948 } else {
949 variants = (sym.info as ast.SumType).variants.clone()
950 }
951 for variant in variants {
952 c.push_new_comptime_info()
953 c.comptime.inside_comptime_for = true
954 c.comptime.comptime_for_variant_var = node.val_var
955 c.type_resolver.update_ct_type(node.val_var, c.variant_data_type)
956 c.type_resolver.update_ct_type('${node.val_var}.typ', variant)
957 c.stmts(mut node.stmts)
958 c.pop_comptime_info()
959 }
960 } else {
961 c.stmts(mut node.stmts)
962 }
963}
964
965fn (mut c Checker) find_comptime_eval_fn(node ast.CallExpr) ?ast.Fn {
966 if f := c.table.find_fn(node.name) {
967 return f
968 }
969 if node.mod != '' && !node.name.contains('.') {
970 if f := c.table.find_fn('${node.mod}.${node.name}') {
971 return f
972 }
973 }
974 if !node.name.contains('.') {
975 if f := c.table.find_fn('${c.mod}.${node.name}') {
976 return f
977 }
978 if f := c.table.find_fn('builtin.${node.name}') {
979 return f
980 }
981 }
982 return none
983}
984
985fn (c &Checker) find_comptime_eval_fn_decl(func ast.Fn) ?ast.FnDecl {
986 if func.source_fn != unsafe { nil } {
987 fn_decl := unsafe { &ast.FnDecl(func.source_fn) }
988 if fn_decl != unsafe { nil } {
989 return *fn_decl
990 }
991 }
992 if c.file != unsafe { nil } {
993 for stmt in c.file.stmts {
994 if stmt is ast.FnDecl && stmt.name == func.name {
995 return stmt
996 }
997 }
998 }
999 return none
1000}
1001
1002fn (mut c Checker) eval_comptime_fn_decl_value_with_locals(fn_decl ast.FnDecl, nlevel int, local_values map[string]ast.ComptTimeConstValue) ?ast.ComptTimeConstValue {
1003 mut stmts := fn_decl.stmts.clone()
1004 if stmts.len == 1 && stmts[0] is ast.Block {
1005 unsafe_block := stmts[0] as ast.Block
1006 if unsafe_block.is_unsafe {
1007 stmts = unsafe_block.stmts.clone()
1008 }
1009 }
1010 if stmts.len != 1 {
1011 return none
1012 }
1013 stmt := stmts[0]
1014 match stmt {
1015 ast.Return {
1016 if stmt.exprs.len != 1 {
1017 return none
1018 }
1019 return c.eval_comptime_const_expr_with_locals(stmt.exprs[0], nlevel + 1, local_values)
1020 }
1021 ast.ExprStmt {
1022 return c.eval_comptime_const_expr_with_locals(stmt.expr, nlevel + 1, local_values)
1023 }
1024 else {
1025 return none
1026 }
1027 }
1028}
1029
1030fn (mut c Checker) eval_comptime_fn_call_expr_with_locals(node ast.CallExpr, nlevel int, local_values map[string]ast.ComptTimeConstValue) ?ast.ComptTimeConstValue {
1031 if node.is_method || node.is_fn_var || node.is_fn_a_const {
1032 return none
1033 }
1034 func := c.find_comptime_eval_fn(node) or { return none }
1035 if !func.attrs.contains('comptime') {
1036 return none
1037 }
1038 if func.is_method || func.is_variadic || func.is_c_variadic || func.no_body
1039 || func.generic_names.len > 0 || func.params.len != node.args.len {
1040 return none
1041 }
1042 fn_decl := c.find_comptime_eval_fn_decl(func) or { return none }
1043 mut local_args := map[string]ast.ComptTimeConstValue{}
1044 for idx, param in func.params {
1045 arg_value := c.eval_comptime_const_expr_with_locals(node.args[idx].expr, nlevel + 1,
1046 local_values) or { return none }
1047 local_args[param.name] = arg_value
1048 }
1049 return c.eval_comptime_fn_decl_value_with_locals(fn_decl, nlevel + 1, local_args)
1050}
1051
1052// comptime const eval
1053fn (mut c Checker) eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.ComptTimeConstValue {
1054 return c.eval_comptime_const_expr_with_locals(expr, nlevel,
1055 map[string]ast.ComptTimeConstValue{})
1056}
1057
1058fn (mut c Checker) eval_comptime_const_expr_with_locals(expr ast.Expr, nlevel int, local_values map[string]ast.ComptTimeConstValue) ?ast.ComptTimeConstValue {
1059 if nlevel > 100 {
1060 // protect against a too deep comptime eval recursion
1061 return none
1062 }
1063 match expr {
1064 ast.ParExpr {
1065 return c.eval_comptime_const_expr_with_locals(expr.expr, nlevel + 1, local_values)
1066 }
1067 ast.EnumVal {
1068 enum_name := if expr.enum_name == '' {
1069 c.table.type_to_str(c.expected_type)
1070 } else {
1071 expr.enum_name
1072 }
1073 if val := c.table.find_enum_field_val(enum_name, expr.val) {
1074 return val
1075 }
1076 }
1077 ast.SizeOf {
1078 s, _ := c.table.type_size(c.unwrap_generic(expr.typ))
1079 return i64(s)
1080 }
1081 ast.FloatLiteral {
1082 x := expr.val.f64()
1083 return x
1084 }
1085 ast.IntegerLiteral {
1086 x := expr.val.u64()
1087 if x > 9223372036854775807 {
1088 return x
1089 }
1090 return expr.val.i64()
1091 }
1092 ast.StringLiteral {
1093 return util.smart_quote(expr.val, expr.is_raw)
1094 }
1095 ast.StringInterLiteral {
1096 if nlevel < 0 {
1097 mut sb := strings.new_builder(20)
1098 for i, val in expr.vals {
1099 sb.write_string(val)
1100 if e := expr.exprs[i] {
1101 if value := c.eval_comptime_const_expr_with_locals(e, nlevel + 1,
1102 local_values)
1103 {
1104 sb.write_string(value.string() or { '' })
1105 } else {
1106 c.error('unsupport expr `${e.str()}`', e.pos())
1107 }
1108 }
1109 }
1110 return sb.str()
1111 }
1112 }
1113 ast.CharLiteral {
1114 runes := expr.val.runes()
1115 if runes.len > 0 {
1116 return runes[0]
1117 }
1118 return none
1119 }
1120 ast.Ident {
1121 if value := local_values[expr.name] {
1122 return value
1123 }
1124 if expr.obj is ast.ConstField {
1125 // an existing constant?
1126 return c.eval_comptime_const_expr_with_locals(expr.obj.expr, nlevel + 1,
1127 local_values)
1128 }
1129 if c.table.cur_fn != unsafe { nil } {
1130 idx := c.table.cur_fn.generic_names.index(expr.name)
1131 if typ := c.table.cur_concrete_types[idx] {
1132 sym := c.table.sym(typ)
1133 return sym.str()
1134 }
1135 }
1136 }
1137 ast.SelectorExpr {
1138 if value := c.eval_comptime_type_selector_value(expr) {
1139 return value
1140 }
1141 }
1142 ast.CastExpr {
1143 cast_expr_value := c.eval_comptime_const_expr_with_locals(expr.expr, nlevel + 1,
1144 local_values) or { return none }
1145 if expr.typ == ast.i8_type {
1146 return cast_expr_value.i8() or { return none }
1147 }
1148 if expr.typ == ast.i16_type {
1149 return cast_expr_value.i16() or { return none }
1150 }
1151 if expr.typ == ast.i32_type {
1152 return cast_expr_value.i32() or { return none }
1153 }
1154 if expr.typ == ast.i64_type {
1155 return cast_expr_value.i64() or { return none }
1156 }
1157 if expr.typ == ast.int_type {
1158 return cast_expr_value.i64() or { return none }
1159 }
1160 //
1161 if expr.typ == ast.u8_type {
1162 return cast_expr_value.u8() or { return none }
1163 }
1164 if expr.typ == ast.u16_type {
1165 return cast_expr_value.u16() or { return none }
1166 }
1167 if expr.typ == ast.u32_type {
1168 return cast_expr_value.u32() or { return none }
1169 }
1170 if expr.typ == ast.u64_type {
1171 return cast_expr_value.u64() or { return none }
1172 }
1173 //
1174 if expr.typ == ast.f32_type {
1175 return cast_expr_value.f32() or { return none }
1176 }
1177 if expr.typ == ast.f64_type {
1178 return cast_expr_value.f64() or { return none }
1179 }
1180 if expr.typ == ast.voidptr_type || expr.typ == ast.nil_type {
1181 ptrvalue := cast_expr_value.voidptr() or { return none }
1182 return ast.ComptTimeConstValue(ptrvalue)
1183 }
1184 }
1185 ast.CallExpr {
1186 return c.eval_comptime_fn_call_expr_with_locals(expr, nlevel, local_values)
1187 }
1188 ast.InfixExpr {
1189 left := c.eval_comptime_const_expr_with_locals(expr.left, nlevel + 1, local_values)?
1190 saved_expected_type := c.expected_type
1191 if expr.left is ast.EnumVal {
1192 c.expected_type = expr.left.typ
1193 } else if expr.left is ast.InfixExpr {
1194 mut infixexpr := expr
1195 for {
1196 if infixexpr.left is ast.InfixExpr {
1197 infixexpr = infixexpr.left as ast.InfixExpr
1198 } else {
1199 break
1200 }
1201 }
1202 if mut infixexpr.left is ast.EnumVal {
1203 c.expected_type = infixexpr.left.typ
1204 }
1205 }
1206 right := c.eval_comptime_const_expr_with_locals(expr.right, nlevel + 1, local_values)?
1207 c.expected_type = saved_expected_type
1208 if expr.op == .power {
1209 return comptime_power_value(left, right)
1210 }
1211 if left is string && right is string {
1212 match expr.op {
1213 .plus {
1214 return left + right
1215 }
1216 else {
1217 return none
1218 }
1219 }
1220 } else if left is u32 && right is i64 {
1221 match expr.op {
1222 .plus { return i64(left) + right }
1223 .minus { return i64(left) - right }
1224 .mul { return i64(left) * right }
1225 .div { return i64(left) / right }
1226 .mod { return i64(left) % right }
1227 .xor { return i64(left) ^ right }
1228 .pipe { return i64(left) | right }
1229 .amp { return i64(left) & right }
1230 .left_shift { return i64(u64(left) << right) }
1231 .right_shift { return i64(u64(left) >> right) }
1232 .unsigned_right_shift { return i64(u64(left) >>> right) }
1233 else { return none }
1234 }
1235 } else if left is i64 && right is u32 {
1236 match expr.op {
1237 .plus { return left + i64(right) }
1238 .minus { return left - i64(right) }
1239 .mul { return left * i64(right) }
1240 .div { return left / i64(right) }
1241 .mod { return left % i64(right) }
1242 .xor { return left ^ i64(right) }
1243 .pipe { return left | i64(right) }
1244 .amp { return left & i64(right) }
1245 .left_shift { return i64(u64(left) << i64(right)) }
1246 .right_shift { return i64(u64(left) >> i64(right)) }
1247 .unsigned_right_shift { return i64(u64(left) >>> i64(right)) }
1248 else { return none }
1249 }
1250 } else if left is u32 && right is u32 {
1251 match expr.op {
1252 .plus { return i64(left) + i64(right) }
1253 .minus { return i64(left) - i64(right) }
1254 .mul { return i64(left) * i64(right) }
1255 .div { return i64(left) / i64(right) }
1256 .mod { return i64(left) % i64(right) }
1257 .xor { return i64(left) ^ i64(right) }
1258 .pipe { return i64(left) | i64(right) }
1259 .amp { return i64(left) & i64(right) }
1260 .left_shift { return i64(u64(left) << right) }
1261 .right_shift { return i64(u64(left) >> right) }
1262 .unsigned_right_shift { return i64(u64(left) >>> right) }
1263 else { return none }
1264 }
1265 } else if left is u64 && right is i64 {
1266 match expr.op {
1267 .plus { return i64(left) + i64(right) }
1268 .minus { return i64(left) - i64(right) }
1269 .mul { return i64(left) * i64(right) }
1270 .div { return i64(left) / i64(right) }
1271 .mod { return i64(left) % i64(right) }
1272 .xor { return i64(left) ^ i64(right) }
1273 .pipe { return i64(left) | i64(right) }
1274 .amp { return i64(left) & i64(right) }
1275 .left_shift { return i64(u64(left) << i64(right)) }
1276 .right_shift { return i64(u64(left) >> i64(right)) }
1277 .unsigned_right_shift { return i64(u64(left) >>> i64(right)) }
1278 else { return none }
1279 }
1280 } else if left is i64 && right is u64 {
1281 match expr.op {
1282 .plus { return i64(left) + i64(right) }
1283 .minus { return i64(left) - i64(right) }
1284 .mul { return i64(left) * i64(right) }
1285 .div { return i64(left) / i64(right) }
1286 .mod { return i64(left) % i64(right) }
1287 .xor { return i64(left) ^ i64(right) }
1288 .pipe { return i64(left) | i64(right) }
1289 .amp { return i64(left) & i64(right) }
1290 .left_shift { return i64(u64(left) << i64(right)) }
1291 .right_shift { return i64(u64(left) >> i64(right)) }
1292 .unsigned_right_shift { return i64(u64(left) >>> i64(right)) }
1293 else { return none }
1294 }
1295 } else if left is u64 && right is u64 {
1296 match expr.op {
1297 .plus { return left + right }
1298 .minus { return left - right }
1299 .mul { return left * right }
1300 .div { return left / right }
1301 .mod { return left % right }
1302 .xor { return left ^ right }
1303 .pipe { return left | right }
1304 .amp { return left & right }
1305 .left_shift { return left << right }
1306 .right_shift { return left >> right }
1307 .unsigned_right_shift { return left >>> right }
1308 else { return none }
1309 }
1310 } else if left is i64 && right is i64 {
1311 match expr.op {
1312 .plus { return left + right }
1313 .minus { return left - right }
1314 .mul { return left * right }
1315 .div { return left / right }
1316 .mod { return left % right }
1317 .xor { return left ^ right }
1318 .pipe { return left | right }
1319 .amp { return left & right }
1320 .left_shift { return i64(u64(left) << right) }
1321 .right_shift { return i64(u64(left) >> right) }
1322 .unsigned_right_shift { return i64(u64(left) >>> right) }
1323 else { return none }
1324 }
1325 } else if left is u8 && right is u8 {
1326 match expr.op {
1327 .plus { return left + right }
1328 .minus { return left - right }
1329 .mul { return left * right }
1330 .div { return left / right }
1331 .mod { return left % right }
1332 .xor { return left ^ right }
1333 .pipe { return left | right }
1334 .amp { return left & right }
1335 .left_shift { return left << right }
1336 .right_shift { return left >> right }
1337 .unsigned_right_shift { return left >>> right }
1338 else { return none }
1339 }
1340 }
1341 }
1342 ast.IfExpr {
1343 if !expr.is_comptime {
1344 return none
1345 }
1346 for i in 0 .. expr.branches.len {
1347 mut branch := expr.branches[i]
1348 if !expr.has_else || i < expr.branches.len - 1 {
1349 mut sb := strings.new_builder(256)
1350 is_true, _ := c.comptime_if_cond(mut branch.cond, mut sb)
1351 if is_true {
1352 last_stmt := branch.stmts.last()
1353 if last_stmt is ast.ExprStmt {
1354 return c.eval_comptime_const_expr_with_locals(last_stmt.expr,
1355 nlevel + 1, local_values)
1356 }
1357 }
1358 } else {
1359 last_stmt := branch.stmts.last()
1360 if last_stmt is ast.ExprStmt {
1361 return c.eval_comptime_const_expr_with_locals(last_stmt.expr, nlevel + 1,
1362 local_values)
1363 }
1364 }
1365 }
1366 }
1367 // ast.ArrayInit {}
1368 // ast.PrefixExpr {
1369 // c.note('prefixexpr: ${expr}', expr.pos)
1370 // }
1371 else {
1372 // eprintln('>>> nlevel: ${nlevel} | another ${expr.type_name()} | ${expr} ')
1373 return none
1374 }
1375 }
1376
1377 return none
1378}
1379
1380fn (mut c Checker) verify_veb_params_for_method(node &ast.Fn) (bool, int, int) {
1381 margs := node.params.len - 1 // first arg is the receiver/this
1382 // if node.attrs.len == 0 || (node.attrs.len == 1 && node.attrs[0].name == 'post') {
1383 if node.attrs.len == 0 {
1384 // allow non custom routed methods, with 1:1 mapping
1385 return true, -1, margs
1386 }
1387 mut context_params := 0
1388 if node.params.len > 1 {
1389 for param in node.params[1..] {
1390 if c.has_veb_context(param.typ) {
1391 context_params++
1392 continue
1393 }
1394 param_sym := c.table.final_sym(param.typ)
1395 if !(param_sym.is_string() || param_sym.is_number() || param_sym.is_float()
1396 || param_sym.kind == .bool) {
1397 c.error('invalid type `${param_sym.name}` for parameter `${param.name}` in veb app method `${node.name}` (only strings, numbers, and bools are allowed)',
1398 param.pos)
1399 }
1400 }
1401 }
1402 mut route_attributes := 0
1403 for a in node.attrs {
1404 if a.name.starts_with('/') {
1405 route_attributes += a.name.count(':')
1406 }
1407 }
1408 return route_attributes == margs - context_params, route_attributes, margs - context_params
1409}
1410
1411fn (mut c Checker) verify_all_veb_routes() {
1412 if c.veb_gen_types.len == 0 {
1413 return
1414 }
1415 c.table.used_features.used_veb_types = c.veb_gen_types
1416 typ_veb_result := c.table.find_type('veb.Result')
1417 old_file := c.file
1418 for vgt in c.veb_gen_types {
1419 sym_app := c.table.sym(vgt)
1420 for m in sym_app.methods {
1421 if m.return_type == typ_veb_result {
1422 is_ok, nroute_attributes, nargs := c.verify_veb_params_for_method(m)
1423 if !is_ok {
1424 f := unsafe { &ast.FnDecl(m.source_fn) }
1425 if f == unsafe { nil } {
1426 continue
1427 }
1428 if f.return_type == typ_veb_result && f.receiver.typ == m.params[0].typ
1429 && f.name == m.name && !f.attrs.contains('post') {
1430 c.change_current_file(f.source_file) // setup of file path for the warning
1431 c.warn('mismatched parameters count between veb method `${sym_app.name}.${m.name}` (${nargs}) and route attribute ${m.attrs} (${nroute_attributes})',
1432 f.pos)
1433 }
1434 }
1435 }
1436 }
1437 }
1438 c.change_current_file(old_file)
1439}
1440
1441fn (mut c Checker) evaluate_once_comptime_if_attribute(mut node ast.Attr) bool {
1442 if node.ct_evaled {
1443 return node.ct_skip
1444 }
1445 if mut node.ct_expr is ast.Ident {
1446 if node.ct_opt {
1447 if node.ct_expr.name in ast.valid_comptime_not_user_defined {
1448 c.error('option `@[if expression ?]` tags, can be used only for user defined identifiers',
1449 node.pos)
1450 node.ct_skip = true
1451 } else {
1452 node.ct_skip = node.ct_expr.name !in c.pref.compile_defines
1453 }
1454 node.ct_evaled = true
1455 return node.ct_skip
1456 } else {
1457 if node.ct_expr.name !in ast.valid_comptime_not_user_defined {
1458 c.note('`@[if ${node.ct_expr.name}]` is deprecated. Use `@[if ${node.ct_expr.name} ?]` instead',
1459 node.pos)
1460 node.ct_skip = node.ct_expr.name !in c.pref.compile_defines
1461 node.ct_evaled = true
1462 return node.ct_skip
1463 } else {
1464 if node.ct_expr.name in c.pref.compile_defines {
1465 // explicitly allow custom user overrides with `-d linux` for example, for easier testing:
1466 node.ct_skip = false
1467 node.ct_evaled = true
1468 return node.ct_skip
1469 }
1470 }
1471 }
1472 }
1473 c.inside_ct_attr = true
1474 mut sb := strings.new_builder(256)
1475 is_true, _ := c.comptime_if_cond(mut node.ct_expr, mut sb)
1476 node.ct_skip = !is_true
1477 c.inside_ct_attr = false
1478 node.ct_evaled = true
1479 return node.ct_skip
1480}
1481
1482// check if `ident` is a function generic, such as `T`
1483fn (mut c Checker) is_generic_ident(ident string) bool {
1484 if c.table.cur_fn != unsafe { nil } && ident in c.table.cur_fn.generic_names
1485 && c.table.cur_fn.generic_names.len == c.table.cur_concrete_types.len {
1486 return true
1487 }
1488 return false
1489}
1490
1491fn (mut c Checker) get_expr_type(cond ast.Expr) ast.Type {
1492 match cond {
1493 ast.Ident {
1494 mut checked_type := ast.void_type
1495 if c.comptime.inside_comptime_for && (cond.name == c.comptime.comptime_for_variant_var
1496 || cond.name == c.comptime.comptime_for_method_param_var
1497 || cond.name == c.comptime.comptime_for_field_var) {
1498 // struct field
1499 return c.type_resolver.get_type_from_comptime_var(cond)
1500 } else if c.is_generic_ident(cond.name) {
1501 // generic type `T`
1502 if c.table.cur_fn != unsafe { nil } {
1503 idx := c.table.cur_fn.generic_names.index(cond.name)
1504 if idx >= 0 && idx < c.table.cur_concrete_types.len {
1505 concrete_type := c.table.cur_concrete_types[idx]
1506 if concrete_type != 0 {
1507 return concrete_type
1508 }
1509 }
1510 }
1511 type_idx := c.table.find_type_idx(cond.name)
1512 return if type_idx == 0 {
1513 ast.void_type
1514 } else {
1515 ast.new_type(type_idx).set_flag(.generic)
1516 }
1517 } else if cond.name in c.type_resolver.type_map {
1518 return c.type_resolver.get_ct_type_or_default(cond.name, ast.void_type)
1519 } else if var := cond.scope.find_var(cond.name) {
1520 // var
1521 checked_type = c.unwrap_generic(var.typ)
1522 if var.smartcasts.len > 0 {
1523 checked_type = c.unwrap_generic(c.visible_var_type_for_read(var))
1524 }
1525 }
1526 return checked_type
1527 }
1528 ast.TypeNode {
1529 return c.unwrap_generic(cond.typ)
1530 }
1531 ast.SelectorExpr {
1532 if c.comptime.inside_comptime_for
1533 && cond.field_name in ['typ', 'unaliased_typ', 'indirections', 'pointee_type', 'payload_type', 'variant_types']
1534 && cond.expr is ast.Ident && (cond.expr.name == c.comptime.comptime_for_variant_var
1535 || cond.expr.name == c.comptime.comptime_for_method_param_var
1536 || cond.expr.name == c.comptime.comptime_for_field_var) {
1537 typ := c.type_resolver.get_type_from_comptime_var(cond.expr as ast.Ident)
1538 if cond.field_name == 'unaliased_typ' {
1539 return c.table.unaliased_type(typ)
1540 } else if cond.field_name in ['pointee_type', 'payload_type', 'variant_types'] {
1541 return c.type_resolver.typeof_field_type(typ, cond.field_name)
1542 }
1543 // for `indirections` we also return the `typ`
1544 return typ
1545 }
1546 if cond.name_type != 0
1547 && cond.field_name in ['key_type', 'value_type', 'element_type', 'pointee_type', 'payload_type', 'variant_types'] {
1548 return c.type_resolver.typeof_field_type(cond.name_type, cond.field_name)
1549 }
1550 if cond.gkind_field in [.typ, .indirections, .unaliased_typ] {
1551 if cond.expr is ast.Ident {
1552 generic_name := cond.expr.name
1553 if c.table.cur_fn != unsafe { nil }
1554 && generic_name in c.table.cur_fn.generic_names {
1555 idx := c.table.cur_fn.generic_names.index(generic_name)
1556 if idx >= 0 && idx < c.table.cur_concrete_types.len {
1557 concrete_type := c.table.cur_concrete_types[idx]
1558 if cond.gkind_field == .unaliased_typ {
1559 return c.table.unaliased_type(concrete_type)
1560 }
1561 return concrete_type
1562 }
1563 }
1564 }
1565 unwrapped := c.unwrap_generic(cond.name_type)
1566 if cond.gkind_field == .unaliased_typ {
1567 if unwrapped.idx() == 0 || unwrapped.has_flag(.generic) {
1568 return unwrapped
1569 }
1570 return c.table.unaliased_type(unwrapped)
1571 }
1572 return unwrapped
1573 } else {
1574 if cond.expr is ast.TypeOf {
1575 return c.type_resolver.typeof_field_type(c.type_resolver.typeof_type(cond.expr.expr,
1576 cond.name_type), cond.field_name)
1577 }
1578 name := '${cond.expr}.${cond.field_name}'
1579 if name in c.type_resolver.type_map {
1580 return c.type_resolver.get_ct_type_or_default(name, ast.void_type)
1581 } else {
1582 return c.unwrap_generic(cond.typ)
1583 }
1584 }
1585 }
1586 ast.IntegerLiteral {
1587 return ast.int_type
1588 }
1589 ast.BoolLiteral {
1590 return ast.bool_type
1591 }
1592 ast.StringLiteral {
1593 return ast.string_type
1594 }
1595 ast.CharLiteral {
1596 return ast.char_type
1597 }
1598 ast.FloatLiteral {
1599 return ast.f64_type
1600 }
1601 else {
1602 return ast.void_type
1603 }
1604 }
1605}
1606
1607fn (mut c Checker) check_compatible_types(left_type ast.Type, left_name string, expr ast.Expr) bool {
1608 mut resolved_left_type := c.unwrap_generic(left_type)
1609 if expr is ast.ComptimeType {
1610 return c.type_resolver.is_comptime_type(resolved_left_type, expr as ast.ComptimeType)
1611 } else if expr is ast.TypeNode {
1612 typ := c.get_expr_type(expr)
1613 right_type := c.unwrap_generic(typ)
1614 mut right_sym := c.table.sym(right_type)
1615 if right_sym.kind == .generic_inst {
1616 gi := right_sym.info as ast.GenericInst
1617 if gi.parent_idx > 0 && gi.parent_idx < c.table.type_symbols.len
1618 && c.table.type_symbols[gi.parent_idx].kind == .interface
1619 && !gi.concrete_types.any(c.type_has_unresolved_generic_parts(it)) {
1620 c.table.generic_insts_to_concrete()
1621 right_sym = c.table.sym(right_type)
1622 }
1623 }
1624 if c.type_resolver.bind_matching_generic_type(resolved_left_type, right_type) {
1625 return true
1626 }
1627 if right_sym.kind == .placeholder || right_type.has_flag(.generic) {
1628 c.error('unknown type `${right_sym.name}`', expr.pos)
1629 }
1630 if right_sym.kind == .interface && right_sym.info is ast.Interface {
1631 return resolved_left_type.has_flag(.option) == right_type.has_flag(.option)
1632 && c.table.does_type_implement_interface(resolved_left_type, right_type)
1633 }
1634 if right_sym.info is ast.FnType && c.comptime.comptime_for_method_var != ''
1635 && c.comptime.comptime_for_method != unsafe { nil }
1636 && c.comptime.comptime_for_method_var == left_name {
1637 right_fn_type := right_sym.info as ast.FnType
1638 return c.table.fn_signature(right_fn_type.func,
1639 skip_receiver: true
1640 type_only: true
1641 ) == c.table.fn_signature(c.comptime.comptime_for_method,
1642 skip_receiver: true
1643 type_only: true
1644 )
1645 }
1646 left_unaliased_type := c.table.fully_unaliased_type(resolved_left_type)
1647 right_unaliased_type := c.table.fully_unaliased_type(right_type)
1648 left_unaliased_sym := c.table.sym(left_unaliased_type)
1649 right_unaliased_sym := c.table.sym(right_unaliased_type)
1650 if left_unaliased_sym.info is ast.FnType && right_unaliased_sym.info is ast.FnType {
1651 same_flags := left_unaliased_type.nr_muls() == right_unaliased_type.nr_muls()
1652 && left_unaliased_type.has_flag(.option) == right_unaliased_type.has_flag(.option)
1653 && left_unaliased_type.has_flag(.result) == right_unaliased_type.has_flag(.result)
1654 && left_unaliased_type.has_flag(.shared_f) == right_unaliased_type.has_flag(.shared_f)
1655 && left_unaliased_type.has_flag(.atomic_f) == right_unaliased_type.has_flag(.atomic_f)
1656 return same_flags && c.table.fn_signature(left_unaliased_sym.info.func,
1657 skip_receiver: true
1658 type_only: true
1659 ) == c.table.fn_signature(right_unaliased_sym.info.func,
1660 skip_receiver: true
1661 type_only: true
1662 )
1663 } else {
1664 return resolved_left_type == right_type
1665 }
1666 }
1667 return false
1668}
1669
1670// comptime_if_cond evaluate the `cond` and return (`is_true`, `keep_stmts`)
1671// `is_true` is the evaluate result of `cond`;
1672// `keep_stmts` meaning the branch is a `multi pass branch`, we should keep the branch stmts even `is_true` is false, such as `$if T is int {`
1673fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, mut sb strings.Builder) (bool, bool) {
1674 mut should_record_ident := false
1675 mut is_user_ident := false
1676 mut ident_name := ''
1677 defer {
1678 if should_record_ident {
1679 // record current cond result for debugging
1680 if is_user_ident {
1681 c.ct_user_defines[ident_name] = $res(0)
1682 } else {
1683 c.ct_system_defines[ident_name] = $res(0)
1684 }
1685 }
1686 }
1687 mut is_true := false
1688
1689 match mut cond {
1690 ast.BoolLiteral {
1691 c.expr(mut cond)
1692 is_true = cond.val
1693 sb.write_string('${is_true}')
1694 return is_true, false
1695 }
1696 ast.ParExpr {
1697 sb.write_string('(')
1698 is_true_result, multi_pass_stmts := c.comptime_if_cond(mut cond.expr, mut sb)
1699 sb.write_string(')')
1700 return is_true_result, multi_pass_stmts
1701 }
1702 ast.PrefixExpr {
1703 if cond.op != .not {
1704 c.error('invalid \$if prefix operator, only allow `!`.', cond.pos)
1705 return false, false
1706 }
1707 sb.write_string(cond.op.str())
1708 is_true_result, multi_pass_stmts := c.comptime_if_cond(mut cond.right, mut sb)
1709 return !is_true_result, multi_pass_stmts
1710 }
1711 ast.PostfixExpr {
1712 if cond.op != .question {
1713 c.error('invalid \$if postfix operator, only allow `?`.', cond.pos)
1714 return false, false
1715 }
1716 if cond.expr !is ast.Ident {
1717 c.error('invalid \$if postfix condition, only allow `Indent`.', cond.expr.pos())
1718 return false, false
1719 }
1720 cname := (cond.expr as ast.Ident).name
1721 // record current cond result for debugging
1722 should_record_ident = true
1723 is_user_ident = true
1724 ident_name = cname
1725 sb.write_string('defined(CUSTOM_DEFINE_${cname})')
1726 is_true = cname in c.pref.compile_defines
1727 return is_true, false
1728 }
1729 ast.InfixExpr {
1730 match cond.op {
1731 .and, .logical_or {
1732 l, d1 := c.comptime_if_cond(mut cond.left, mut sb)
1733 sb.write_string(' ${cond.op} ')
1734 r, d2 := c.comptime_if_cond(mut cond.right, mut sb)
1735 // if at least one of the cond has `keep_stmts`, we should keep stmts
1736 return if cond.op == .and { l && r } else { l || r }, d1 || d2
1737 }
1738 .key_is, .not_is, .key_in, .not_in {
1739 // $if T is Type
1740 // $if T in [Type1, Type2]
1741 if cond.left in [ast.TypeNode, ast.Ident, ast.SelectorExpr]
1742 && ((cond.right in [ast.ComptimeType, ast.TypeNode]
1743 && cond.op in [.key_is, .not_is])
1744 || (cond.right is ast.ArrayInit && cond.op in [.key_in, .not_in])) {
1745 c.expr(mut cond.left)
1746
1747 // resolve left type
1748 mut left_name := ''
1749 if mut cond.left is ast.Ident {
1750 left_name = cond.left.name
1751 } else if mut cond.left is ast.SelectorExpr {
1752 left_name = '${cond.left.expr}'.all_before('.')
1753 }
1754 left_type := c.get_expr_type(cond.left)
1755 type_array := if cond.op in [.key_is, .not_is] {
1756 // construct a type array for a single `is`, `!is`
1757 [cond.right]
1758 } else {
1759 (cond.right as ast.ArrayInit).exprs
1760 }
1761 // iter the `type_array`, for `is` and `!is`, it has only one element
1762 for expr in type_array {
1763 is_true = c.check_compatible_types(left_type, left_name, expr)
1764 if is_true {
1765 break
1766 }
1767 }
1768 is_true = if cond.op in [.key_in, .key_is] { is_true } else { !is_true }
1769 sb.write_string('${is_true}')
1770 return is_true, true
1771 }
1772
1773 if cond.left !in [ast.TypeNode, ast.Ident, ast.SelectorExpr] {
1774 c.error('invalid \$if left expr: expected a Type/Ident/SelectorExpr',
1775 cond.left.pos())
1776 return false, false
1777 }
1778 if cond.right !in [ast.ComptimeType, ast.TypeNode] {
1779 c.error('invalid \$if right expr: expected a type', cond.right.pos())
1780 return false, false
1781 }
1782 c.error('invalid \$if condition: is/!is/in/!in', cond.pos)
1783 return false, false
1784 }
1785 .eq, .ne, .gt, .lt, .ge, .le {
1786 match mut cond.left {
1787 ast.AtExpr {
1788 // @OS == 'linux'
1789 left_type := c.expr(mut cond.left)
1790 right_type := c.expr(mut cond.right)
1791 if !c.check_types(right_type, left_type) {
1792 left_name := c.table.type_to_str(left_type)
1793 right_name := c.table.type_to_str(right_type)
1794 c.error('mismatched types `${left_name}` and `${right_name}`',
1795 cond.pos)
1796 }
1797 left_str := cond.left.val
1798 right_str := (cond.right as ast.StringLiteral).val
1799 if cond.op == .eq {
1800 is_true = left_str == right_str
1801 } else if cond.op == .ne {
1802 is_true = left_str != right_str
1803 } else {
1804 c.error('string type only support `==` and `!=` operator', cond.pos)
1805 return false, false
1806 }
1807 sb.write_string('${is_true}')
1808 return is_true, false
1809 }
1810 ast.Ident {
1811 // $if version == 2
1812 left_type := c.expr(mut cond.left)
1813 right_type := c.expr(mut cond.right)
1814 expr := c.find_definition(cond.left) or {
1815 c.error(err.msg(), cond.left.pos)
1816 return false, false
1817 }
1818 if !c.check_types(right_type, left_type) {
1819 left_name := c.table.type_to_str(left_type)
1820 right_name := c.table.type_to_str(right_type)
1821 c.error('mismatched types `${left_name}` and `${right_name}`',
1822 cond.pos)
1823 }
1824 match mut cond.right {
1825 ast.StringLiteral {
1826 match cond.op {
1827 .eq {
1828 is_true = expr.str() == cond.right.str()
1829 }
1830 .ne {
1831 is_true = expr.str() != cond.right.str()
1832 }
1833 else {
1834 c.error('string type only support `==` and `!=` operator',
1835 cond.pos)
1836 return false, false
1837 }
1838 }
1839 }
1840 ast.IntegerLiteral {
1841 match cond.op {
1842 .eq {
1843 is_true = expr.str().i64() == cond.right.val.i64()
1844 }
1845 .ne {
1846 is_true = expr.str().i64() != cond.right.val.i64()
1847 }
1848 .gt {
1849 is_true = expr.str().i64() > cond.right.val.i64()
1850 }
1851 .lt {
1852 is_true = expr.str().i64() < cond.right.val.i64()
1853 }
1854 .ge {
1855 is_true = expr.str().i64() >= cond.right.val.i64()
1856 }
1857 .le {
1858 is_true = expr.str().i64() <= cond.right.val.i64()
1859 }
1860 else {
1861 c.error('int type only support `==` `!=` `>` `<` `>=` and `<=` operator',
1862 cond.pos)
1863 return false, false
1864 }
1865 }
1866 }
1867 ast.BoolLiteral {
1868 match cond.op {
1869 .eq {
1870 is_true = expr.str().bool() == cond.right.val
1871 }
1872 .ne {
1873 is_true = expr.str().bool() != cond.right.val
1874 }
1875 else {
1876 c.error('bool type only support `==` and `!=` operator',
1877 cond.pos)
1878 return false, false
1879 }
1880 }
1881 }
1882 else {
1883 c.error('compare only support string int and bool type',
1884 cond.pos)
1885 return false, false
1886 }
1887 }
1888
1889 sb.write_string('${is_true}')
1890 return is_true, false
1891 }
1892 ast.SelectorExpr {
1893 // $if field.name == 'abc'
1894 c.expr(mut cond.left)
1895 match mut cond.right {
1896 ast.StringLiteral {
1897 if cond.left.field_name == 'name'
1898 && c.comptime.inside_comptime_for {
1899 left_name := (cond.left.expr as ast.Ident).name
1900 match left_name {
1901 c.comptime.comptime_for_method_var {
1902 is_true = c.comptime.comptime_for_method.name == cond.right.val
1903 }
1904 c.comptime.comptime_for_field_var {
1905 is_true = c.comptime.comptime_for_field_value.name == cond.right.val
1906 }
1907 c.comptime.comptime_for_attr_var {
1908 is_true = c.comptime.comptime_for_attr_value.name == cond.right.val
1909 }
1910 else {
1911 c.error('.name compare only support for \$for vars',
1912 cond.pos)
1913 return false, false
1914 }
1915 }
1916
1917 match cond.op {
1918 .eq {
1919 sb.write_string('${is_true}')
1920 return is_true, true
1921 }
1922 .ne {
1923 sb.write_string('${!is_true}')
1924 return !is_true, true
1925 }
1926 else {
1927 c.error('.name compare only support for `==` and `!=`',
1928 cond.pos)
1929 return false, false
1930 }
1931 }
1932 } else {
1933 if comparison := c.try_eval_comptime_comparison(mut cond.left, mut
1934 cond.right, cond.op)
1935 {
1936 sb.write_string(if comparison.val { '1' } else { '0' })
1937 return comparison.val, comparison.keep_stmts
1938 }
1939 c.error('only support .name compare for \$for vars',
1940 cond.pos)
1941 return false, false
1942 }
1943 }
1944 ast.IntegerLiteral {
1945 if cond.left.field_name == 'indirections' {
1946 // field.indirections, T.indirections
1947 left_type := c.get_expr_type(ast.Expr(cond.left))
1948 left_muls := left_type.nr_muls()
1949 match cond.op {
1950 .eq {
1951 is_true = left_muls == cond.right.val.i64()
1952 }
1953 .ne {
1954 is_true = left_muls != cond.right.val.i64()
1955 }
1956 .gt {
1957 is_true = left_muls > cond.right.val.i64()
1958 }
1959 .lt {
1960 is_true = left_muls < cond.right.val.i64()
1961 }
1962 .ge {
1963 is_true = left_muls >= cond.right.val.i64()
1964 }
1965 .le {
1966 is_true = left_muls <= cond.right.val.i64()
1967 }
1968 else {
1969 c.error('.indirections only support `==` `!=` `>` `<` `>=` and `<=` operator',
1970 cond.pos)
1971 return false, false
1972 }
1973 }
1974
1975 sb.write_string('${is_true}')
1976 return is_true, true
1977 } else if cond.left.field_name == 'return_type' {
1978 // method.return_type
1979 left_name := (cond.left.expr as ast.Ident).name
1980 if c.comptime.inside_comptime_for
1981 && left_name == c.comptime.comptime_for_method_var {
1982 left_type_idx :=
1983 c.comptime.comptime_for_method_ret_type.idx()
1984 match cond.op {
1985 .eq {
1986 is_true = left_type_idx == cond.right.val.i64()
1987 }
1988 .gt {
1989 is_true = left_type_idx > cond.right.val.i64()
1990 }
1991 .lt {
1992 is_true = left_type_idx < cond.right.val.i64()
1993 }
1994 .ge {
1995 is_true = left_type_idx >= cond.right.val.i64()
1996 }
1997 .le {
1998 is_true = left_type_idx <= cond.right.val.i64()
1999 }
2000 else {
2001 c.error('.return_type only support `==` `!=` `>` `<` `>=` and `<=` operator',
2002 cond.pos)
2003 return false, false
2004 }
2005 }
2006
2007 sb.write_string('${is_true}')
2008 return is_true, false
2009 } else {
2010 c.error('only support .return_type compare for \$for method',
2011 cond.pos)
2012 return false, false
2013 }
2014 } else {
2015 if comparison := c.try_eval_comptime_comparison(mut cond.left, mut
2016 cond.right, cond.op)
2017 {
2018 sb.write_string(if comparison.val { '1' } else { '0' })
2019 return comparison.val, comparison.keep_stmts
2020 }
2021 c.error('only support .indirections/.return_type compare for \$for vars and generic',
2022 cond.pos)
2023 return false, false
2024 }
2025 }
2026 ast.BoolLiteral {
2027 // field.is_pub == true
2028 mut left := ast.Expr(cond.left)
2029 l, _ := c.comptime_if_cond(mut left, mut sb)
2030 sb.write_string(' ${cond.op} ')
2031 r := (cond.right as ast.BoolLiteral).val
2032 sb.write_string('${r}')
2033 is_true = if cond.op == .eq { l == r } else { l != r }
2034 return is_true, true
2035 }
2036 else {
2037 if comparison := c.try_eval_comptime_comparison(mut cond.left, mut
2038 cond.right, cond.op)
2039 {
2040 sb.write_string(if comparison.val { '1' } else { '0' })
2041 return comparison.val, comparison.keep_stmts
2042 }
2043 c.error('definition of `${cond.left}` is unknown at compile time',
2044 cond.pos)
2045 return false, false
2046 }
2047 }
2048
2049 if comparison := c.try_eval_comptime_comparison(mut cond.left, mut
2050 cond.right, cond.op)
2051 {
2052 sb.write_string(if comparison.val { '1' } else { '0' })
2053 return comparison.val, comparison.keep_stmts
2054 }
2055 c.error('invalid \$if condition: SelectorExpr', cond.pos)
2056 return false, false
2057 }
2058 ast.SizeOf {
2059 match mut cond.right {
2060 ast.IntegerLiteral {
2061 s, _ := c.table.type_size(c.unwrap_generic(cond.left.typ))
2062 match cond.op {
2063 .eq {
2064 is_true = s == cond.right.val.i64()
2065 }
2066 .ne {
2067 is_true = s != cond.right.val.i64()
2068 }
2069 .gt {
2070 is_true = s > cond.right.val.i64()
2071 }
2072 .lt {
2073 is_true = s < cond.right.val.i64()
2074 }
2075 .ge {
2076 is_true = s >= cond.right.val.i64()
2077 }
2078 .le {
2079 is_true = s <= cond.right.val.i64()
2080 }
2081 else {
2082 c.error('sizeof() only support `==` `!=` `>` `<` `>=` and `<=` operator',
2083 cond.pos)
2084 return false, false
2085 }
2086 }
2087
2088 sb.write_string('${is_true}')
2089 return is_true, true
2090 }
2091 else {
2092 c.error('sizeof() can only compare with int type', cond.pos)
2093 return false, false
2094 }
2095 }
2096 }
2097 else {
2098 if comparison := c.try_eval_comptime_comparison(mut cond.left, mut
2099 cond.right, cond.op)
2100 {
2101 sb.write_string(if comparison.val { '1' } else { '0' })
2102 return comparison.val, comparison.keep_stmts
2103 }
2104 c.error('invalid \$if condition', cond.pos)
2105 return false, false
2106 }
2107 }
2108
2109 c.error('invalid \$if condition', cond.pos)
2110 return false, false
2111 }
2112 else {
2113 c.error('invalid \$if operator: ${cond.op}', cond.pos)
2114 return false, false
2115 }
2116 }
2117 }
2118 ast.Ident {
2119 cname := cond.name
2120 // record current cond result for debugging
2121 should_record_ident = true
2122 is_user_ident = false
2123 ident_name = cname
2124 if cname in ast.valid_comptime_not_user_defined {
2125 if cname == 'threads' {
2126 is_true = c.table.gostmts > 0
2127 } else {
2128 is_true = ast.eval_comptime_not_user_defined_ident(cname, c.pref) or {
2129 c.error(err.msg(), cond.pos)
2130 return false, false
2131 }
2132 }
2133 } else if cname !in c.pref.compile_defines_all {
2134 if cname == 'linux_or_macos' {
2135 c.error('linux_or_macos is deprecated, use `\$if linux || macos {` instead',
2136 cond.pos)
2137 return false, false
2138 }
2139 // `$if some_var {}`, or `[if user_defined_tag] fn abc(){}`
2140 mut ident_expr := ast.Expr(cond)
2141 typ := c.unwrap_generic(c.expr(mut ident_expr))
2142 resolved_obj := if mut ident_expr is ast.Ident {
2143 ident_expr.obj
2144 } else {
2145 cond.obj
2146 }
2147 if resolved_obj !in [ast.Var, ast.ConstField, ast.GlobalField] {
2148 if !c.inside_ct_attr {
2149 c.error('unknown var: `${cname}`', cond.pos)
2150 return false, false
2151 }
2152 c.error('invalid \$if condition: unknown indent `${cname}`', cond.pos)
2153 return false, false
2154 }
2155 expr := c.find_obj_definition(resolved_obj) or {
2156 c.error(err.msg(), cond.pos)
2157 return false, false
2158 }
2159 if !c.check_types(typ, ast.bool_type) {
2160 type_name := c.table.type_to_str(typ)
2161 c.error('non-bool type `${type_name}` used as \$if condition', cond.pos)
2162 return false, false
2163 }
2164 is_true = (expr as ast.BoolLiteral).val
2165 } else if cname in c.pref.compile_defines {
2166 is_true = true
2167 } else {
2168 c.error('invalid \$if condition: unknown indent `${cname}`', cond.pos)
2169 return false, false
2170 }
2171 if ifdef := ast.comptime_if_to_ifdef(cname, c.pref) {
2172 sb.write_string('defined(${ifdef})')
2173 } else {
2174 sb.write_string('${is_true}')
2175 }
2176 return is_true, false
2177 }
2178 ast.ComptimeCall {
2179 if cond.kind == .pkgconfig {
2180 if mut m := pkgconfig.main([cond.args_var]) {
2181 if _ := m.run() {
2182 is_true = true
2183 } else {
2184 // pkgconfig not found, do not issue error, just set false
2185 is_true = false
2186 }
2187 } else {
2188 c.error(err.msg(), cond.pos)
2189 is_true = false
2190 }
2191 sb.write_string('${is_true}')
2192 return is_true, true
2193 }
2194 if cond.kind == .d {
2195 cond.resolve_compile_value(c.pref.compile_values) or {
2196 c.error(err.msg(), cond.pos)
2197 return false, false
2198 }
2199 if cond.result_type != ast.bool_type {
2200 c.error('inside \$if, only \$d() expressions that return bool are allowed',
2201 cond.pos)
2202 return false, false
2203 }
2204 is_true = cond.compile_value.bool()
2205 sb.write_string('${is_true}')
2206 return is_true, false
2207 }
2208 c.error('invalid \$if condition: unknown ComptimeCall', cond.pos)
2209 return false, false
2210 }
2211 ast.SelectorExpr {
2212 if c.comptime.comptime_for_field_var != '' && cond.expr is ast.Ident {
2213 if (cond.expr as ast.Ident).name == c.comptime.comptime_for_field_var && cond.field_name in ['is_mut', 'is_pub', 'is_embed', 'is_shared', 'is_atomic', 'is_option', 'is_array', 'is_map', 'is_chan', 'is_struct', 'is_alias', 'is_enum'] {
2214 is_true = c.type_resolver.get_comptime_selector_bool_field(cond.field_name)
2215 sb.write_string('${is_true}')
2216 return is_true, true
2217 }
2218 c.error('unknown field `${cond.field_name}` from ${c.comptime.comptime_for_field_var}',
2219 cond.pos)
2220 }
2221 if c.comptime.comptime_for_attr_var != '' && cond.expr is ast.Ident {
2222 if (cond.expr as ast.Ident).name == c.comptime.comptime_for_attr_var && cond.field_name == 'has_arg' {
2223 is_true = c.comptime.comptime_for_attr_value.has_arg
2224 sb.write_string('${is_true}')
2225 return is_true, true
2226 }
2227 c.error('unknown field `${cond.field_name}` from ${c.comptime.comptime_for_attr_var}',
2228 cond.pos)
2229 }
2230 if c.comptime.comptime_for_method_var != '' && cond.expr is ast.Ident {
2231 if (cond.expr as ast.Ident).name == c.comptime.comptime_for_method_var && cond.field_name in ['is_variadic', 'is_c_variadic', 'is_pub', 'is_ctor_new', 'is_deprecated', 'is_noreturn', 'is_unsafe', 'is_must_use', 'is_placeholder', 'is_main', 'is_test', 'is_keep_alive', 'is_method', 'is_static_type_method', 'no_body', 'is_file_translated', 'is_conditional', 'is_expand_simple_interpolation'] {
2232 method := c.comptime.comptime_for_method
2233 is_true = match cond.field_name {
2234 'is_variadic' { method.is_variadic }
2235 'is_c_variadic' { method.is_c_variadic }
2236 'is_pub' { method.is_pub }
2237 'is_ctor_new' { method.is_ctor_new }
2238 'is_deprecated' { method.is_deprecated }
2239 'is_noreturn' { method.is_noreturn }
2240 'is_unsafe' { method.is_unsafe }
2241 'is_must_use' { method.is_must_use }
2242 'is_placeholder' { method.is_placeholder }
2243 'is_main' { method.is_main }
2244 'is_test' { method.is_test }
2245 'is_keep_alive' { method.is_keep_alive }
2246 'is_method' { method.is_method }
2247 'is_static_type_method' { method.is_static_type_method }
2248 'no_body' { method.no_body }
2249 'is_file_translated' { method.is_file_translated }
2250 'is_conditional' { method.is_conditional }
2251 'is_expand_simple_interpolation' { method.is_expand_simple_interpolation }
2252 else { false }
2253 }
2254
2255 sb.write_string('${is_true}')
2256 return is_true, true
2257 }
2258 c.error('unknown field `${cond.field_name}` from ${c.comptime.comptime_for_method_var}',
2259 cond.pos)
2260 }
2261 return false, false
2262 }
2263 else {
2264 c.error('invalid \$if condition ${cond}', cond.pos())
2265 return false, false
2266 }
2267 }
2268
2269 c.error('invalid \$if condition ${cond}', cond.pos())
2270 return false, false
2271}
2272
2273// push_new_comptime_info saves the current comptime information
2274fn (mut c Checker) push_new_comptime_info() {
2275 c.type_resolver.info_stack << type_resolver.ResolverInfo{
2276 saved_type_map: c.type_resolver.type_map.clone()
2277 inside_comptime_for: c.comptime.inside_comptime_for
2278 inside_comptime_if: c.comptime.inside_comptime_if
2279 has_different_types: c.comptime.has_different_types
2280 comptime_for_variant_var: c.comptime.comptime_for_variant_var
2281 comptime_for_field_var: c.comptime.comptime_for_field_var
2282 comptime_for_field_type: c.comptime.comptime_for_field_type
2283 comptime_for_field_value: c.comptime.comptime_for_field_value
2284 comptime_for_enum_var: c.comptime.comptime_for_enum_var
2285 comptime_for_attr_var: c.comptime.comptime_for_attr_var
2286 comptime_for_attr_value: c.comptime.comptime_for_attr_value
2287 comptime_for_method_var: c.comptime.comptime_for_method_var
2288 comptime_for_method: c.comptime.comptime_for_method
2289 comptime_for_method_ret_type: c.comptime.comptime_for_method_ret_type
2290 }
2291}
2292
2293// pop_comptime_info pops the current comptime information frame
2294fn (mut c Checker) pop_comptime_info() {
2295 old := c.type_resolver.info_stack.pop()
2296 c.type_resolver.type_map = old.saved_type_map.clone()
2297 c.comptime.inside_comptime_for = old.inside_comptime_for
2298 c.comptime.inside_comptime_if = old.inside_comptime_if
2299 c.comptime.has_different_types = old.has_different_types
2300 c.comptime.comptime_for_variant_var = old.comptime_for_variant_var
2301 c.comptime.comptime_for_field_var = old.comptime_for_field_var
2302 c.comptime.comptime_for_field_type = old.comptime_for_field_type
2303 c.comptime.comptime_for_field_value = old.comptime_for_field_value
2304 c.comptime.comptime_for_enum_var = old.comptime_for_enum_var
2305 c.comptime.comptime_for_attr_var = old.comptime_for_attr_var
2306 c.comptime.comptime_for_attr_value = old.comptime_for_attr_value
2307 c.comptime.comptime_for_method_var = old.comptime_for_method_var
2308 c.comptime.comptime_for_method = old.comptime_for_method
2309 c.comptime.comptime_for_method_ret_type = old.comptime_for_method_ret_type
2310}
2311
2312fn overflows_i8(val i64) bool {
2313 return val > max_i8 || val < min_i8
2314}
2315
2316fn overflows_i16(val i64) bool {
2317 return val > max_i16 || val < min_i16
2318}
2319
2320fn overflows_i32(val i64) bool {
2321 return val > max_i32 || val < min_i32
2322}
2323
2324fn overflows_u8(val i64) bool {
2325 return val > max_u8 || val < min_u8
2326}
2327
2328fn overflows_u16(val i64) bool {
2329 return val > max_u16 || val < min_u16
2330}
2331
2332fn overflows_u32(val i64) bool {
2333 return val > max_u32 || val < min_u32
2334}
2335