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