v2 / vlib / v / checker / checker.v
9140 lines · 8869 sloc · 297.23 KB · 9f57c744dac88fb52dfea3775961d6d57e2067fc
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 os
6import strconv
7import v.ast
8import v.vmod
9import v.token
10import v.pref
11import v.util
12import v.util.version
13import v.errors
14import v.pkgconfig
15import v.transformer
16import v.type_resolver
17
18// prevent stack overflows by restricting too deep recursion:
19const expr_level_cutoff_limit = 40
20const stmt_level_cutoff_limit = 40
21const type_level_cutoff_limit = 40 // it is very rarely deeper than 4
22const iface_level_cutoff_limit = 100
23const generic_fn_cutoff_limit_per_fn = 10_000 // how many times post_process_generic_fns, can visit the same function before bailing out
24
25const generic_fn_postprocess_iterations_cutoff_limit = 1_000_000
26
27fn has_ascii_upper(s string) bool {
28 for ch in s {
29 if ch >= `A` && ch <= `Z` {
30 return true
31 }
32 }
33 return false
34}
35
36// array_builtin_methods contains a list of all methods on array, that return other typed arrays.
37// i.e. that act as *pseudogeneric* methods, that need compiler support, so that the types of the results
38// are properly checked.
39// Note that methods that do not return anything, or that return known types, are not listed here, since they are just ordinary non generic methods.
40pub const array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort',
41 'sort_with_compare', 'sorted', 'sorted_with_compare', 'contains', 'index', 'last_index', 'wait',
42 'any', 'all', 'first', 'last', 'get', 'pop_left', 'pop', 'delete', 'insert', 'prepend', 'count']
43pub const array_builtin_methods_chk = token.new_keywords_matcher_from_array_trie(array_builtin_methods)
44pub const fixed_array_builtin_methods = ['contains', 'index', 'last_index', 'any', 'all', 'wait',
45 'map', 'sort', 'sorted', 'sort_with_compare', 'sorted_with_compare', 'reverse',
46 'reverse_in_place', 'count', 'filter']
47pub const fixed_array_builtin_methods_chk = token.new_keywords_matcher_from_array_trie(fixed_array_builtin_methods)
48// TODO: remove `byte` from this list when it is no longer supported
49pub const reserved_type_names = ['bool', 'char', 'i8', 'i16', 'i32', 'int', 'i64', 'u8', 'u16',
50 'u32', 'u64', 'f32', 'f64', 'map', 'string', 'rune', 'usize', 'isize', 'voidptr', 'thread']
51pub const reserved_type_names_chk = token.new_keywords_matcher_from_array_trie(reserved_type_names)
52pub const vroot_is_deprecated_message = '@VROOT is deprecated, use @VMODROOT or @VEXEROOT instead'
53
54struct AssertAutocast {
55 from_type ast.Type
56 to_type ast.Type
57}
58
59@[heap; minify]
60pub struct Checker {
61pub mut:
62 pref &pref.Preferences = unsafe { nil } // Preferences shared from V struct
63
64 table &ast.Table = unsafe { nil }
65 file &ast.File = unsafe { nil }
66
67 nr_errors int
68 nr_warnings int
69 nr_notices int
70 errors []errors.Error
71 warnings []errors.Warning
72 notices []errors.Notice
73 error_lines map[string]bool // dedup errors
74 warning_lines map[string]bool // dedup warns
75 notice_lines map[string]bool // dedup notices
76 error_details []string
77 should_abort bool // when too many errors are accumulated, .should_abort becomes true. It is checked in statement/expression loops, so the checker can return early, instead of wasting time.
78
79 expected_type ast.Type
80 expected_or_type ast.Type // fn() or { 'this type' } eg. string. expected or block type
81 expected_expr_type ast.Type // if/match is_expr: expected_type
82 mod string // current module name
83 has_globals_in_module bool // true if the current module has @[has_globals] attribute
84 strict_map_index_in_module bool // true if the current module has @[strict_map_index] attribute
85 const_var &ast.ConstField = unsafe { nil } // the current constant, when checking const declarations
86 const_deps []string
87 const_names []string
88 global_names []string
89 locked_names []string // vars that are currently locked
90 rlocked_names []string // vars that are currently read-locked
91 in_for_count int // if checker is currently in a for loop
92 returns bool
93 scope_returns bool
94 is_builtin_mod bool // true inside the 'builtin', 'os' or 'strconv' modules; TODO: remove the need for special casing this
95 is_just_builtin_mod bool // true only inside 'builtin'
96 is_generated bool // true for `@[generated] module xyz` .v files
97 unresolved_fixed_sizes []&ast.Stmt // funcs with unresolved array fixed size e.g. fn func() [const1]int
98 inside_recheck bool // true when rechecking rhs assign statement
99 inside_unsafe bool // true inside `unsafe {}` blocks
100 inside_const bool // true inside `const ( ... )` blocks
101 inside_anon_fn bool // true inside `fn() { ... }()`
102 inside_lambda bool // true inside `|...| ...`
103 inside_ref_lit bool // true inside `a := &something`
104 inside_defer bool // true inside `defer {}` blocks
105 inside_return bool // true inside `return ...` blocks
106 inside_fn_arg bool // `a`, `b` in `a.f(b)`
107 inside_ct_attr bool // true inside `[if expr]`
108 inside_x_is_type bool // true inside the Type expression of `if x is Type {`
109 inside_x_matches_type bool // true inside the match branch of `match x.type { Type {} }`
110 anon_struct_should_be_mut bool // true when `mut var := struct { ... }` is used
111 inside_generic_struct_init bool
112 inside_integer_literal_cast bool // true inside `int(123)`
113 cur_struct_generic_types []ast.Type
114 cur_struct_concrete_types []ast.Type
115 anon_fn_generic_names []string
116 anon_fn_concrete_types []ast.Type
117 skip_flags bool // should `#flag` and `#include` be skipped
118 fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
119 smartcast_mut_pos token.Pos // match mut foo, if mut foo is Foo
120 smartcast_cond_pos token.Pos // match cond
121 ct_cond_stack []ast.Expr
122 ct_user_defines map[string]bool
123 ct_system_defines map[string]bool
124 cur_ct_id int // id counter for $if $match branches
125mut:
126 stmt_level int // the nesting level inside each stmts list;
127 // .stmt_level is used to check for `evaluated but not used` ExprStmts like `1 << 1`
128 // 1 for statements directly at each inner scope level;
129 // increases for `x := if cond { statement_list1} else {statement_list2}`;
130 // increases for `x := optfn() or { statement_list3 }`;
131 // files []ast.File
132 expr_level int // to avoid infinite recursion segfaults due to compiler bugs
133 type_level int // to avoid infinite recursion segfaults due to compiler bugs in ensure_type_exists
134 ensure_generic_type_level int // to avoid infinite recursion segfaults in ensure_generic_type_specify_type_names
135 cur_orm_ts ast.TypeSymbol
136 cur_or_expr &ast.OrExpr = unsafe { nil }
137 cur_anon_fn &ast.AnonFn = unsafe { nil }
138 vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path**
139 loop_labels []string // filled, when inside labelled for loops: `a_label: for x in 0..10 {`
140 veb_gen_types []ast.Type // veb route checks
141 timers &util.Timers = util.get_timers()
142 type_resolver type_resolver.TypeResolver
143 comptime &type_resolver.ResolverInfo = unsafe { nil }
144 fn_scope &ast.Scope = unsafe { nil }
145 main_fn_decl_node ast.FnDecl
146 match_exhaustive_cutoff_limit int = 10
147 is_last_stmt bool
148 prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type, stopping unwrapping then
149 need_recheck_generic_fns bool // need recheck generic fns because there are cascaded nested generic fn
150 generic_fns map[string]bool // register generic fns that needs recheck once
151 inside_sql bool // to handle sql table fields pseudo variables
152 inside_selector_expr bool
153 inside_or_block_value bool // true inside or-block where its value is used `f(g() or { true })`
154 inside_interface_deref bool
155 inside_decl_rhs bool
156 inside_if_guard bool // true inside the guard condition of `if x := opt() {}`
157 inside_assign bool
158 assert_autocasts map[string]AssertAutocast
159 is_js_backend bool
160 // doing_line_info int // a quick single file run when called with v -line-info (contains line nr to inspect)
161 // doing_line_path string // same, but stores the path being parsed
162 is_index_assign bool
163 comptime_call_pos int // needed for correctly checking use before decl for templates
164 generic_call_positions map[string]token.Pos // map from generic function key to call position
165 goto_labels map[string]ast.GotoLabel // to check for unused goto labels
166 enum_data_type ast.Type
167 field_data_type ast.Type
168 variant_data_type ast.Type
169 fn_return_type ast.Type
170 orm_table_fields map[string][]ast.StructField // known table structs
171 short_module_names []string // to check for function names colliding with module functions
172 visible_param_mutation_cache map[string]bool
173 visible_param_mutation_in_progress map[string]bool
174 immutable_alias_analysis_in_progress map[string]bool
175 always_error_fn_cache map[string]bool
176 always_error_fn_in_progress map[string]bool
177 generic_parts_cache []i8 // type idx -> 0 unknown, 1 false, 2 true
178
179 v_current_commit_hash string // same as old C.V_CURRENT_COMMIT_HASH
180 assign_stmt_attr string // for `x := [1,2,3] @[freed]`
181
182 js_string ast.Type = ast.void_type // when `js"string literal"` is used, `js_string` will be equal to `JS.String`
183 checker_transformer &transformer.Transformer = unsafe { nil }
184}
185
186pub fn new_checker(table &ast.Table, pref_ &pref.Preferences) &Checker {
187 mut timers_should_print := false
188 $if time_checking ? {
189 timers_should_print = true
190 }
191 v_current_commit_hash := if pref_.building_v {
192 version.githash(pref_.vroot) or { '' }
193 } else {
194 vcurrent_hash()
195 }
196 mut checker := &Checker{
197 table: table
198 pref: pref_
199 timers: util.new_timers(
200 should_print: timers_should_print
201 label: 'checker'
202 )
203 match_exhaustive_cutoff_limit: pref_.checker_match_exhaustive_cutoff_limit
204 v_current_commit_hash: v_current_commit_hash
205 checker_transformer: transformer.new_transformer_with_table(table, pref_)
206 visible_param_mutation_cache: map[string]bool{}
207 visible_param_mutation_in_progress: map[string]bool{}
208 immutable_alias_analysis_in_progress: map[string]bool{}
209 always_error_fn_cache: map[string]bool{}
210 always_error_fn_in_progress: map[string]bool{}
211 generic_parts_cache: []i8{len: table.type_symbols.len}
212 }
213 checker.checker_transformer.skip_array_transform = true
214 checker.type_resolver = type_resolver.TypeResolver.new(table, checker)
215 checker.comptime = &checker.type_resolver.info
216 checker.is_js_backend = checker.pref.backend.is_js()
217 return checker
218}
219
220// build_generic_call_key builds a key for tracking generic function call positions
221fn (c &Checker) build_generic_call_key(fkey string, concrete_types []ast.Type) string {
222 mut types_str := ''
223 for typ in concrete_types {
224 types_str += c.table.type_to_str(typ) + ','
225 }
226 return '${fkey}[${types_str}]'
227}
228
229fn (mut c Checker) has_active_generic_recheck_context() bool {
230 if c.inside_generic_struct_init && c.cur_struct_concrete_types.len > 0 {
231 return true
232 }
233 if c.table.cur_fn == unsafe { nil } {
234 return false
235 }
236 if c.table.cur_concrete_types.len > 0 {
237 return true
238 }
239 if !c.table.cur_fn.is_method {
240 return false
241 }
242 receiver_typ := c.table.cur_fn.receiver.typ
243 if receiver_typ == 0 {
244 return false
245 }
246 if receiver_typ.has_flag(.generic) || c.type_has_unresolved_generic_parts(receiver_typ) {
247 return true
248 }
249 rec_sym := c.table.sym(c.unwrap_generic(receiver_typ))
250 match rec_sym.info {
251 ast.Struct, ast.Interface, ast.SumType {
252 return rec_sym.info.generic_types.len > 0 || rec_sym.info.concrete_types.len > 0
253 }
254 ast.GenericInst {
255 return rec_sym.info.concrete_types.len > 0
256 }
257 else {
258 return false
259 }
260 }
261}
262
263fn (mut c Checker) refresh_generic_scope_var_type_for_use(mut v ast.Var, use_pos int) ast.Type {
264 if v.is_arg || v.expr is ast.EmptyExpr || v.pos.pos <= 0 || v.pos.pos >= use_pos
265 || c.table.cur_fn == unsafe { nil } || !c.has_active_generic_recheck_context() {
266 return v.typ
267 }
268 $if trace_ci_fixes ? {
269 if c.file.path.contains('/datatypes/linked_list.v') {
270 generic_typ_str := if v.generic_typ == 0 {
271 '<none>'
272 } else {
273 c.table.type_to_str(v.generic_typ)
274 }
275 eprintln('refresh_var fn=${c.table.cur_fn.name} var=${v.name} typ=${c.table.type_to_str(v.typ)} gtyp=${generic_typ_str} expr=${v.expr}')
276 }
277 }
278 if v.generic_typ != 0 {
279 refreshed_generic_type := c.unwrap_generic(c.recheck_concrete_type(v.generic_typ))
280 if refreshed_generic_type != 0 && refreshed_generic_type != ast.void_type {
281 v.typ = refreshed_generic_type
282 v.orig_type = ast.no_type
283 v.smartcasts = []
284 v.is_unwrapped = false
285 return v.typ
286 }
287 }
288 if v.expr is ast.Ident && v.expr.name == v.name {
289 return v.typ
290 }
291 saved_expected_type := c.expected_type
292 saved_expected_or_type := c.expected_or_type
293 saved_expected_expr_type := c.expected_expr_type
294 saved_inside_assign := c.inside_assign
295 saved_inside_decl_rhs := c.inside_decl_rhs
296 saved_inside_selector_expr := c.inside_selector_expr
297 saved_inside_fn_arg := c.inside_fn_arg
298 saved_inside_if_guard := c.inside_if_guard
299 saved_prevent_sum_type_unwrapping_once := c.prevent_sum_type_unwrapping_once
300 saved_inside_recheck := c.inside_recheck
301 saved_anon_struct_should_be_mut := c.anon_struct_should_be_mut
302 defer {
303 c.expected_type = saved_expected_type
304 c.expected_or_type = saved_expected_or_type
305 c.expected_expr_type = saved_expected_expr_type
306 c.inside_assign = saved_inside_assign
307 c.inside_decl_rhs = saved_inside_decl_rhs
308 c.inside_selector_expr = saved_inside_selector_expr
309 c.inside_fn_arg = saved_inside_fn_arg
310 c.inside_if_guard = saved_inside_if_guard
311 c.prevent_sum_type_unwrapping_once = saved_prevent_sum_type_unwrapping_once
312 c.inside_recheck = saved_inside_recheck
313 c.anon_struct_should_be_mut = saved_anon_struct_should_be_mut
314 }
315 c.expected_type = ast.void_type
316 c.expected_or_type = ast.void_type
317 c.expected_expr_type = ast.void_type
318 c.inside_assign = false
319 c.inside_decl_rhs = false
320 c.inside_selector_expr = false
321 c.inside_fn_arg = false
322 c.inside_if_guard = false
323 c.prevent_sum_type_unwrapping_once = false
324 c.inside_recheck = true
325 c.anon_struct_should_be_mut = false
326 mut expr := v.expr
327 mut refreshed_type := ast.void_type
328 if mut expr is ast.IfGuardExpr {
329 c.expr(mut expr)
330 if expr.expr_type != 0 && expr.expr_type.clear_option_and_result() != ast.void_type {
331 sym := c.table.sym(expr.expr_type)
332 if sym.kind == .multi_return {
333 mr_info := sym.info as ast.MultiReturn
334 if mr_info.types.len == expr.vars.len {
335 for vi, var in expr.vars {
336 if var.name == v.name {
337 refreshed_type = mr_info.types[vi]
338 break
339 }
340 }
341 }
342 } else {
343 // Rechecks should keep the value type introduced by the guard variable,
344 // not the `bool` condition type returned by `IfGuardExpr`.
345 refreshed_type = expr.expr_type.clear_option_and_result()
346 }
347 }
348 } else {
349 refreshed_type = c.expr(mut expr)
350 }
351 if refreshed_type != 0 && refreshed_type != ast.void_type {
352 mut expr_is_auto_deref_ident := false
353 if expr is ast.Ident {
354 ident := expr as ast.Ident
355 expr_is_auto_deref_ident = ident.obj is ast.Var && ident.obj.is_auto_deref
356 if !expr_is_auto_deref_ident {
357 if source_var := ident.scope.find_var(ident.name) {
358 expr_is_auto_deref_ident = source_var.is_auto_deref
359 }
360 }
361 }
362 // Keep `mut x := param` as a value copy during generic rechecks when the
363 // original parameter type was lowered from a non-pointer source type.
364 // Pointer-typed mut params should keep their declared pointer type.
365 if expr_is_auto_deref_ident && refreshed_type.is_ptr()
366 && !c.auto_deref_source_type_is_pointer(expr) {
367 refreshed_type = refreshed_type.deref()
368 }
369 $if trace_ci_fixes ? {
370 if c.file.path.contains('/datatypes/linked_list.v') {
371 eprintln('refresh_var expr fn=${c.table.cur_fn.name} var=${v.name} refreshed=${c.table.type_to_str(refreshed_type)} expr=${expr}')
372 }
373 }
374 v.typ = c.unwrap_generic(c.recheck_concrete_type(refreshed_type))
375 v.expr = expr
376 v.orig_type = ast.no_type
377 v.smartcasts = []
378 v.is_unwrapped = false
379 }
380 return v.typ
381}
382
383fn (mut c Checker) resolve_selector_field_type(left_type ast.Type, field_name string, field ast.StructField) ast.Type {
384 mut field_type := field.typ
385 sym := c.table.sym(c.unwrap_generic(left_type))
386 match sym.info {
387 ast.Struct, ast.Interface, ast.SumType {
388 mut generic_names := sym.info.generic_types.map(c.table.sym(it).name)
389 mut concrete_types := sym.info.concrete_types.clone()
390 if concrete_types.len == 0 && sym.generic_types.len == generic_names.len
391 && sym.generic_types != sym.info.generic_types {
392 concrete_types = sym.generic_types.clone()
393 }
394 mut source_field_type := field.typ
395 if sym.info.parent_type.has_flag(.generic) {
396 parent_sym := c.table.sym(sym.info.parent_type)
397 if parent_field := c.table.find_field_with_embeds(parent_sym, field_name) {
398 source_field_type = parent_field.typ
399 match parent_sym.info {
400 ast.Struct, ast.Interface, ast.SumType {
401 generic_names = parent_sym.info.generic_types.map(c.table.sym(it).name)
402 }
403 else {}
404 }
405 }
406 }
407 if generic_names.len == concrete_types.len && concrete_types.len > 0 {
408 resolved_field_type := c.table.unwrap_generic_type_ex(source_field_type,
409 generic_names, concrete_types, true)
410 if resolved_field_type != source_field_type {
411 field_type = resolved_field_type
412 } else if converted_field_type := c.table.convert_generic_type(source_field_type,
413 generic_names, concrete_types)
414 {
415 field_type = converted_field_type
416 }
417 }
418 }
419 ast.GenericInst {
420 parent_sym := c.table.sym(ast.new_type(sym.info.parent_idx))
421 mut source_field_type := field.typ
422 if parent_field := c.table.find_field_with_embeds(parent_sym, field_name) {
423 source_field_type = parent_field.typ
424 }
425 match parent_sym.info {
426 ast.Struct, ast.Interface, ast.SumType {
427 generic_names := parent_sym.info.generic_types.map(c.table.sym(it).name)
428 if generic_names.len == sym.info.concrete_types.len
429 && sym.info.concrete_types.len > 0 {
430 resolved_field_type := c.table.unwrap_generic_type_ex(source_field_type,
431 generic_names, sym.info.concrete_types, true)
432 if resolved_field_type != source_field_type {
433 field_type = resolved_field_type
434 } else if converted_field_type := c.table.convert_generic_type(source_field_type,
435 generic_names, sym.info.concrete_types)
436 {
437 field_type = converted_field_type
438 }
439 }
440 }
441 else {}
442 }
443 }
444 else {}
445 }
446
447 $if trace_ci_fixes ? {
448 if c.file.path.contains('/datatypes/linked_list.v') {
449 eprintln('selector_field left=${c.table.type_to_str(left_type)} sym=${sym.name} field=${field_name} raw=${c.table.type_to_str(field.typ)} final=${c.table.type_to_str(field_type)}')
450 }
451 }
452 return c.unwrap_generic(c.recheck_concrete_type(field_type))
453}
454
455fn (mut c Checker) reset_checker_state_at_start_of_new_file() {
456 c.expected_type = ast.void_type
457 c.expected_or_type = ast.void_type
458 c.const_var = unsafe { nil }
459 c.in_for_count = 0
460 c.returns = false
461 c.scope_returns = false
462 c.mod = ''
463 c.is_builtin_mod = false
464 c.is_just_builtin_mod = false
465 c.inside_unsafe = false
466 c.inside_const = false
467 c.inside_anon_fn = false
468 c.inside_ref_lit = false
469 c.inside_defer = false
470 c.inside_fn_arg = false
471 c.inside_ct_attr = false
472 c.inside_x_is_type = false
473 c.inside_x_matches_type = false
474 c.inside_integer_literal_cast = false
475 c.skip_flags = false
476 c.fn_level = 0
477 c.expr_level = 0
478 c.stmt_level = 0
479 c.inside_sql = false
480 c.cur_orm_ts = ast.TypeSymbol{}
481 c.prevent_sum_type_unwrapping_once = false
482 c.loop_labels = []
483 c.inside_selector_expr = false
484 c.inside_interface_deref = false
485 c.inside_decl_rhs = false
486 c.inside_if_guard = false
487 c.has_globals_in_module = false
488 c.error_details.clear()
489}
490
491pub fn (mut c Checker) check(mut ast_file ast.File) {
492 $if trace_check ? {
493 eprintln('> ${@FILE}:${@LINE} | ast_file.path: ${ast_file.path}')
494 }
495 $if trace_checker ? {
496 eprintln('start checking file: ${ast_file.path}')
497 }
498 c.reset_checker_state_at_start_of_new_file()
499 c.change_current_file(ast_file)
500 for i, ast_import in ast_file.imports {
501 // Imports with the same path and name (self-imports and module name conflicts with builtin module imports)
502 if c.mod == ast_import.mod {
503 c.error('cannot import `${ast_import.mod}` into a module with the same name',
504 ast_import.mod_pos)
505 }
506 // Duplicates of regular imports with the default alias (modname) and `as` imports with a custom alias
507 if c.mod == ast_import.alias {
508 if c.mod == ast_import.mod.all_after_last('.') {
509 c.error('cannot import `${ast_import.mod}` into a module with the same name',
510 ast_import.mod_pos)
511 }
512 c.error('cannot import `${ast_import.mod}` as `${ast_import.alias}` into a module with the same name',
513 ast_import.alias_pos)
514 }
515 for sym in ast_import.syms {
516 full_name := ast_import.mod + '.' + sym.name
517 if full_name in c.const_names {
518 c.error('cannot selectively import constant `${sym.name}` from `${ast_import.mod}`, import `${ast_import.mod}` and use `${full_name}` instead',
519 sym.pos)
520 }
521 }
522
523 cmp_mod_name := if ast_import.mod != ast_import.alias && ast_import.alias != '_' {
524 ast_import.alias
525 } else {
526 ast_import.mod
527 }
528 for j in 0 .. i {
529 if cmp_mod_name == if ast_file.imports[j].mod != ast_file.imports[j].alias
530 && ast_file.imports[j].alias != '_' {
531 ast_file.imports[j].alias
532 } else {
533 ast_file.imports[j].mod
534 } {
535 c.error('A module `${cmp_mod_name}` was already imported on line ${
536 ast_file.imports[j].mod_pos.line_nr + 1}`.', ast_import.mod_pos)
537 }
538 }
539 }
540 c.reorder_fns_at_the_end(mut ast_file)
541 c.stmt_level = 0
542 for mut stmt in ast_file.stmts {
543 if stmt in [ast.ConstDecl, ast.ExprStmt] {
544 c.expr_level = 0
545 c.stmt(mut stmt)
546 }
547 if c.should_abort {
548 return
549 }
550 }
551
552 c.stmt_level = 0
553 for mut stmt in ast_file.stmts {
554 is_global_decl := stmt is ast.GlobalDecl
555 if is_global_decl {
556 c.expr_level = 0
557 c.stmt(mut stmt)
558 }
559 if c.should_abort {
560 return
561 }
562 }
563
564 c.stmt_level = 0
565 for mut stmt in ast_file.stmts {
566 if stmt is ast.StructDecl || stmt is ast.InterfaceDecl || stmt is ast.EnumDecl
567 || stmt is ast.TypeDecl {
568 c.expr_level = 0
569 c.stmt(mut stmt)
570 }
571 if c.should_abort {
572 return
573 }
574 }
575
576 c.stmt_level = 0
577 for mut stmt in ast_file.stmts {
578 if mut stmt is ast.FnDecl {
579 return_sym := c.table.sym(stmt.return_type)
580 if return_sym.info is ast.ArrayFixed
581 && c.array_fixed_has_unresolved_size(return_sym.info) {
582 unsafe {
583 c.unresolved_fixed_sizes << &stmt
584 }
585 }
586 }
587 }
588
589 if c.unresolved_fixed_sizes.len > 0 {
590 c.update_unresolved_fixed_sizes()
591 }
592
593 c.stmt_level = 0
594 for mut stmt in ast_file.stmts {
595 if stmt !in [ast.ConstDecl, ast.GlobalDecl, ast.ExprStmt] && stmt !is ast.StructDecl
596 && stmt !is ast.InterfaceDecl && stmt !is ast.EnumDecl && stmt !is ast.TypeDecl {
597 c.expr_level = 0
598 c.stmt(mut stmt)
599 }
600 if c.should_abort {
601 return
602 }
603 }
604
605 c.check_scope_vars(c.file.scope)
606 c.check_unused_labels()
607}
608
609pub fn (mut c Checker) reorder_fns_at_the_end(mut ast_file ast.File) {
610 mut fdeclarations := 0
611 for mut stmt in ast_file.stmts {
612 if stmt is ast.FnDecl {
613 fdeclarations++
614 }
615 }
616 if fdeclarations == 0 {
617 return
618 }
619 // eprintln('>>> ast_file: ${ast_file.path:-60s} | fdeclarations: ${fdeclarations}')
620 mut stmts := []ast.Stmt{cap: ast_file.stmts.len}
621 for stmt in ast_file.stmts {
622 if stmt !is ast.FnDecl {
623 stmts << stmt
624 }
625 }
626 for stmt in ast_file.stmts {
627 if stmt is ast.FnDecl {
628 stmts << stmt
629 }
630 }
631 ast_file.stmts = stmts
632}
633
634pub fn (mut c Checker) check_scope_vars(sc &ast.Scope) {
635 if !c.pref.is_repl && !c.file.is_test {
636 for _, obj in sc.objects {
637 match obj {
638 ast.Var {
639 if !obj.is_special && !obj.is_used && obj.name[0] != `_` {
640 if !c.pref.translated && !c.file.is_translated {
641 if obj.is_arg {
642 if c.pref.show_unused_params {
643 c.note('unused parameter: `${obj.name}`', obj.pos)
644 }
645 } else {
646 c.warn('unused variable: `${obj.name}`', obj.pos)
647 }
648 }
649 }
650 // if obj.is_mut && !obj.is_changed && !c.is_builtin_mod && obj.name != 'it' {
651 // if obj.is_mut && !obj.is_changed && !c.is_builtin { //TODO C error bad field not checked
652 // c.warn('`${obj.name}` is declared as mutable, but it was never changed',
653 // obj.pos)
654 // }
655 }
656 else {}
657 }
658 }
659 }
660 for child in sc.children {
661 c.check_scope_vars(child)
662 }
663}
664
665pub fn (mut c Checker) change_current_file(file &ast.File) {
666 c.file = unsafe { file }
667 c.vmod_file_content = ''
668 c.mod = file.mod.name
669 c.is_just_builtin_mod = c.mod in ['builtin', 'builtin.closure']
670 c.is_builtin_mod = c.is_just_builtin_mod || c.mod in ['os', 'strconv']
671 c.is_generated = file.is_generated
672 c.short_module_names = ['builtin']
673 for import_sym in c.file.imports {
674 c.short_module_names << if import_sym.alias == '' {
675 import_sym.mod.all_after_last('.')
676 } else {
677 import_sym.alias
678 }
679 }
680 // Check if the current module has has_globals attribute
681 c.has_globals_in_module = false
682 c.strict_map_index_in_module = false
683 for attr in file.mod.attrs {
684 match attr.name {
685 'has_globals' {
686 c.has_globals_in_module = true
687 }
688 'strict_map_index' {
689 c.strict_map_index_in_module = true
690 }
691 else {}
692 }
693 }
694}
695
696pub fn (mut c Checker) check_files(ast_files []&ast.File) {
697 // println('check_files')
698 // c.files = ast_files
699 mut has_main_mod_file := false
700 mut has_no_main_mod_file := false
701 mut has_main_fn := false
702 mut invalid_test_file_name := ''
703 mut invalid_test_file_pos := token.Pos{}
704 // Determine the project directory when using -line-info
705 mut project_dir := ''
706 if c.pref.is_vls && c.pref.line_info != '' {
707 project_dir = if os.is_dir(c.pref.path) {
708 os.real_path(c.pref.path)
709 } else {
710 os.real_path(os.dir(c.pref.linfo.path))
711 }
712 }
713 unsafe {
714 mut files_from_main_module := []&ast.File{}
715 for i in 0 .. ast_files.len {
716 mut file := ast_files[i]
717 if c.pref.is_vls && c.pref.line_info == '' && file.path != c.pref.path {
718 continue
719 }
720 if c.pref.is_vls && c.pref.line_info != '' && project_dir != '' {
721 if !os.real_path(file.path).starts_with(project_dir) {
722 continue
723 }
724 }
725 c.timers.start('checker_check ${file.path}')
726 c.check(mut file)
727 if file.mod.name == 'no_main' {
728 has_no_main_mod_file = true
729 }
730 if file.mod.name == 'main' {
731 files_from_main_module << file
732 has_main_mod_file = true
733 if c.file_has_main_fn(file) {
734 has_main_fn = true
735 }
736 }
737 if invalid_test_file_name == '' && is_likely_invalid_test_file_name(file) {
738 invalid_test_file_name = file.path_base
739 invalid_test_file_pos = file.mod.pos
740 }
741 c.timers.show('checker_check ${file.path}')
742 }
743 if has_main_mod_file && !has_main_fn && files_from_main_module.len > 0 {
744 if c.pref.is_script && !c.pref.is_test {
745 // files_from_main_module contain preludes at the start
746 mut the_main_file := files_from_main_module.last()
747 the_main_file.stmts << ast.FnDecl{
748 name: 'main.main'
749 mod: 'main'
750 is_main: true
751 file: the_main_file.path
752 return_type: ast.void_type
753 scope: &ast.Scope{
754 parent: nil
755 }
756 }
757 has_main_fn = true
758 }
759 }
760 }
761 c.timers.start('checker_post_process_generic_fns')
762 mut last_file := c.file
763 // c.file might be nil in vls mode, fall back to first file
764 if c.pref.is_vls && last_file == unsafe { nil } && ast_files.len > 0 {
765 last_file = ast_files[0]
766 }
767 // post process generic functions. must be done after all files have been
768 // checked, to ensure all generic calls are processed, as this information
769 // is needed when the generic type is auto inferred from the call argument.
770 // we may have to loop several times, if there were more concrete types found.
771 mut post_process_generic_fns_iterations := 0
772 post_process_iterations_loop: for post_process_generic_fns_iterations <= generic_fn_postprocess_iterations_cutoff_limit {
773 $if trace_post_process_generic_fns_loop ? {
774 eprintln('>>>>>>>>> recheck_generic_fns loop iteration: ${post_process_generic_fns_iterations}')
775 }
776 for file in ast_files {
777 if file.generic_fns.len > 0 {
778 $if trace_post_process_generic_fns_loop ? {
779 eprintln('>> file.path: ${file.path:-40} | file.generic_fns:' +
780 file.generic_fns.map(it.name).str())
781 }
782 c.change_current_file(file)
783 c.post_process_generic_fns() or { break post_process_iterations_loop }
784 }
785 }
786 // Resolve newly created generic struct/interface instances to concrete types.
787 // This ensures that methods of generic structs instantiated during the current
788 // iteration (e.g. EluLayer[f64] created when checking elu_layer[f64]) get their
789 // concrete types registered for rechecking in the next iteration.
790 mut old_concrete_count := 0
791 for _, v in c.table.fn_generic_types {
792 old_concrete_count += v.len
793 }
794 c.table.generic_insts_to_concrete()
795 mut new_concrete_count := 0
796 for _, v in c.table.fn_generic_types {
797 new_concrete_count += v.len
798 }
799 if new_concrete_count != old_concrete_count {
800 c.need_recheck_generic_fns = true
801 }
802 if !c.need_recheck_generic_fns {
803 break
804 }
805 c.need_recheck_generic_fns = false
806 post_process_generic_fns_iterations++
807 }
808 $if trace_post_process_generic_fns_loop ? {
809 eprintln('>>>>>>>>> recheck_generic_fns loop done, iteration: ${post_process_generic_fns_iterations}')
810 }
811 // restore the original c.file && c.mod after post processing
812 c.change_current_file(last_file)
813 c.timers.show('checker_post_process_generic_fns')
814
815 c.timers.start('checker_verify_all_veb_routes')
816 c.verify_all_veb_routes()
817 c.timers.show('checker_verify_all_veb_routes')
818
819 c.check_unused_declarations(ast_files)
820
821 if c.pref.is_test {
822 mut n_test_fns := 0
823 for _, f in c.table.fns {
824 if f.is_test {
825 n_test_fns++
826 }
827 }
828 if n_test_fns == 0 {
829 c.add_error_detail('The name of a test function in V, should start with `test_`.')
830 c.add_error_detail('The test function should take 0 parameters, and no return type. Example:')
831 c.add_error_detail('fn test_xyz(){ assert 2 + 2 == 4 }')
832 c.error('a _test.v file should have *at least* one `test_` function', token.Pos{})
833 }
834 }
835 // Make sure fn main is defined in non lib builds
836 if c.pref.build_mode == .build_module || c.pref.is_test {
837 return
838 }
839 if c.pref.is_shared {
840 // shared libs do not need to have a main
841 return
842 }
843 if c.pref.is_o {
844 // .o files also do not need main
845 return
846 }
847 if c.pref.no_builtin {
848 // `v -no-builtin module/` do not necessarily need to have a `main` function
849 // This is useful for compiling linux kernel modules for example.
850 return
851 }
852 if has_no_main_mod_file {
853 return
854 }
855 if !has_main_mod_file {
856 if invalid_test_file_name != '' {
857 c.add_error_detail('Test files should have names ending with `_test.v`.')
858 c.error('invalid test file name `${invalid_test_file_name}`', invalid_test_file_pos)
859 } else {
860 c.error('project must include a `main` module or be a shared library (compile with `v -shared`)', token.Pos{})
861 }
862 } else if !has_main_fn && !c.pref.is_o {
863 c.error('function `main` must be declared in the main module', token.Pos{})
864 }
865}
866
867fn is_likely_invalid_test_file_name(file &ast.File) bool {
868 return !file.is_test && (file.path_base.ends_with('.v') || file.path_base.ends_with('.vv'))
869 && file.path_base.starts_with('test_')
870}
871
872fn (mut c Checker) stmts_has_main_fn(stmts []ast.Stmt) bool {
873 mut has_main_fn := false
874 for stmt in stmts {
875 if stmt is ast.ExprStmt {
876 // top level comptime main fn
877 if stmt.expr is ast.IfExpr && stmt.expr.is_comptime {
878 // $if a ? { fn main(){} } $else { fn main() {} }
879 for branch in stmt.expr.branches {
880 if c.stmts_has_main_fn(branch.stmts) {
881 has_main_fn = true
882 }
883 }
884 } else if stmt.expr is ast.MatchExpr && stmt.expr.is_comptime {
885 // $match os { 'windows' { fn main() {} } ...
886 for branch in stmt.expr.branches {
887 if c.stmts_has_main_fn(branch.stmts) {
888 has_main_fn = true
889 }
890 }
891 }
892 }
893 if stmt is ast.FnDecl {
894 if stmt.name == 'main.main' {
895 if has_main_fn {
896 c.error('function `main` is already defined', stmt.pos)
897 }
898 has_main_fn = true
899 if stmt.params.len > 0 {
900 c.error('function `main` cannot have arguments', stmt.pos)
901 }
902 if stmt.return_type != ast.void_type {
903 c.error('function `main` cannot return values', stmt.pos)
904 }
905 if stmt.no_body {
906 c.error('function `main` must declare a body', stmt.pos)
907 }
908 } else if stmt.attrs.contains('console') {
909 c.error('only `main` can have the `@[console]` attribute', stmt.pos)
910 }
911 }
912 }
913 return has_main_fn
914}
915
916// do checks specific to files in main module
917// returns `true` if a main function is in the file
918fn (mut c Checker) file_has_main_fn(file &ast.File) bool {
919 return c.stmts_has_main_fn(file.stmts)
920}
921
922@[direct_array_access]
923fn (mut c Checker) check_valid_snake_case(name string, identifier string, pos token.Pos) {
924 if c.pref.translated || c.file.is_translated {
925 return
926 }
927 if !c.pref.is_template && name.len > 1 && (name[0] == `_` || name.contains('._')) {
928 c.error('${identifier} `${name}` cannot start with `_`', pos)
929 }
930 if util.contains_capital(name) {
931 c.error('${identifier} `${name}` cannot contain uppercase letters, use snake_case instead',
932 pos)
933 }
934}
935
936fn stripped_name(name string) string {
937 idx := name.last_index('.') or { -1 }
938 return name[(idx + 1)..]
939}
940
941fn (mut c Checker) check_valid_pascal_case(name string, identifier string, pos token.Pos) {
942 if c.pref.translated || c.file.is_translated {
943 return
944 }
945 sname := stripped_name(name)
946 if sname.len > 0 && !sname[0].is_capital() {
947 c.error('${identifier} `${name}` must begin with capital letter', pos)
948 }
949}
950
951fn (mut c Checker) type_decl(mut node ast.TypeDecl) {
952 if node.typ == ast.invalid_type && (node is ast.AliasTypeDecl || node is ast.SumTypeDecl) {
953 typ_desc := if node is ast.AliasTypeDecl { 'alias' } else { 'sum type' }
954 c.error('cannot register ${typ_desc} `${node.name}`, another type with this name exists',
955 node.pos)
956 return
957 }
958 match mut node {
959 ast.AliasTypeDecl { c.alias_type_decl(mut node) }
960 ast.FnTypeDecl { c.fn_type_decl(mut node) }
961 ast.SumTypeDecl { c.sum_type_decl(mut node) }
962 }
963}
964
965fn (mut c Checker) alias_type_decl(mut node ast.AliasTypeDecl) {
966 if c.file.mod.name != 'builtin' && !node.name.starts_with('C.') {
967 c.check_valid_pascal_case(node.name, 'type alias', node.pos)
968 }
969 node.parent_type = c.preferred_c_symbol_type(node.parent_type)
970 if c.pref.is_vls && c.pref.linfo.method == .definition {
971 if c.vls_is_the_node(node.type_pos) {
972 typ_str := c.table.type_to_str(node.parent_type)
973 if np := c.name_pos_gotodef(typ_str) {
974 if np.file_idx != -1 {
975 println('${c.table.filelist[np.file_idx]}:${np.line_nr + 1}:${np.col}')
976 exit(0)
977 }
978 }
979 }
980 }
981 if !c.ensure_type_exists(node.parent_type, node.type_pos) {
982 return
983 }
984 mut parent_typ_sym := c.table.sym(node.parent_type)
985 if node.parent_type.has_flag(.result) {
986 c.add_error_detail('Result types cannot be stored and have to be unwrapped immediately')
987 c.error('cannot make an alias of Result type', node.type_pos)
988 }
989 match parent_typ_sym.kind {
990 .placeholder, .int_literal, .float_literal, .any {
991 c.error('unknown aliased type `${parent_typ_sym.name}`', node.type_pos)
992 }
993 .alias {
994 orig_sym := c.table.sym((parent_typ_sym.info as ast.Alias).parent_type)
995 if !node.name.starts_with('C.')
996 && parent_typ_sym.name !in ['strings.Builder', 'StringBuilder', 'builtin.StringBuilder'] {
997 // TODO: remove the whole check, or at least the need for special casing `strings.Builder` and `StringBuilder` here
998 // after more testing and bootstrapping of the strings.Builder -> builtin.StringBuilder change
999 c.error('type `${parent_typ_sym.str()}` is an alias, use the original alias type `${orig_sym.name}` instead',
1000 node.type_pos)
1001 }
1002 }
1003 .chan {
1004 c.error('aliases of `chan` types are not allowed', node.type_pos)
1005 }
1006 .thread {
1007 c.error('aliases of `thread` types are not allowed', node.type_pos)
1008 }
1009 .multi_return {
1010 c.error('aliases of function multi return types are not allowed', node.type_pos)
1011 }
1012 .void {
1013 c.error('aliases of the void type are not allowed', node.type_pos)
1014 }
1015 .function {
1016 orig_sym := c.table.type_to_str(node.parent_type)
1017 c.error('type `${parent_typ_sym.str()}` is an alias, use the original alias type `${orig_sym}` instead',
1018 node.type_pos)
1019 }
1020 .struct {
1021 if mut parent_typ_sym.info is ast.Struct {
1022 // check if the generic param types have been defined
1023 for ct in parent_typ_sym.info.concrete_types {
1024 ct_sym := c.table.sym(ct)
1025 if ct_sym.kind == .placeholder {
1026 c.error('unknown type `${ct_sym.name}`', node.type_pos)
1027 }
1028 }
1029
1030 if parent_typ_sym.info.is_generic && parent_typ_sym.info.concrete_types.len == 0 {
1031 c.error('${parent_typ_sym.name} type is generic struct, must specify the generic type names, e.g. ${parent_typ_sym.name}[int]',
1032 node.type_pos)
1033 }
1034
1035 // check if embed types are supported for struct embedding
1036 for embed_type in parent_typ_sym.info.embeds {
1037 if !c.can_be_embedded_in_struct(embed_type) {
1038 c.error('cannot embed non-struct `${c.table.sym(embed_type).name}`',
1039 node.type_pos)
1040 }
1041 }
1042 if parent_typ_sym.info.is_anon {
1043 for field in parent_typ_sym.info.fields {
1044 mut is_embed := false
1045 field_sym := c.table.sym(field.typ)
1046 if field_sym.info is ast.Alias {
1047 if !c.can_be_embedded_in_struct(field.typ) {
1048 c.error('cannot embed non-struct `${field_sym.name}`',
1049 field.type_pos)
1050 is_embed = true
1051 }
1052 }
1053 if !is_embed {
1054 c.check_valid_snake_case(field.name, 'field name', field.pos)
1055 }
1056 }
1057 }
1058 }
1059 }
1060 .array {
1061 c.check_alias_vs_element_type_of_parent(node,
1062 (parent_typ_sym.info as ast.Array).elem_type, 'array')
1063 }
1064 .array_fixed {
1065 array_fixed_info := parent_typ_sym.info as ast.ArrayFixed
1066 if c.array_fixed_has_unresolved_size(array_fixed_info) {
1067 c.unresolved_fixed_sizes << &ast.TypeDecl(node)
1068 }
1069 c.check_alias_vs_element_type_of_parent(node, array_fixed_info.elem_type, 'fixed array')
1070 }
1071 .map {
1072 info := parent_typ_sym.info as ast.Map
1073 c.check_alias_vs_element_type_of_parent(node, info.key_type, 'map key')
1074 c.check_alias_vs_element_type_of_parent(node, info.value_type, 'map value')
1075 c.markused_used_maps(c.table.used_features.used_maps == 0)
1076 }
1077 .sum_type {
1078 // TODO: decide whether the following should be allowed. Note that it currently works,
1079 // while `type Sum = int | Sum` is explicitly disallowed:
1080 // type Sum = int | Alias
1081 // type Alias = Sum
1082 }
1083 .none {
1084 c.error('cannot create a type alias of `none` as it is a value', node.type_pos)
1085 }
1086 // The rest of the parent symbol kinds are also allowed, since they are either primitive types,
1087 // that in turn do not allow recursion, or are abstract enough so that they can not be checked at comptime:
1088 else {
1089 c.check_any_type(node.parent_type, parent_typ_sym, node.type_pos)
1090 }
1091 /*
1092 .voidptr, .byteptr, .charptr {}
1093 .char, .rune, .bool {}
1094 .string, .enum, .none, .any {}
1095 .i8, .i16, .i32, .int, .i64, .isize {}
1096 .u8, .u16, .u32, .u64, .usize {}
1097 .f32, .f64 {}
1098 .interface {}
1099 .generic_inst {}
1100 .aggregate {}
1101 */
1102 }
1103}
1104
1105fn (c &Checker) preferred_c_symbol_type(typ ast.Type) ast.Type {
1106 sym := c.table.sym(typ)
1107 if sym.language != .c || sym.name == '' {
1108 return typ
1109 }
1110 mut public_typ := ast.invalid_type
1111 for i := c.table.type_symbols.len - 1; i >= 1; i-- {
1112 candidate := c.table.type_symbols[i]
1113 if candidate.language != .c || candidate.name != sym.name || candidate.kind == .placeholder {
1114 continue
1115 }
1116 if candidate.mod == c.mod {
1117 return ast.new_type(i).derive(typ)
1118 }
1119 if candidate.is_pub && public_typ == ast.invalid_type {
1120 public_typ = ast.new_type(i).derive(typ)
1121 }
1122 }
1123 if public_typ != ast.invalid_type {
1124 return public_typ
1125 }
1126 return typ
1127}
1128
1129fn (mut c Checker) check_alias_vs_element_type_of_parent(node ast.AliasTypeDecl, element_type_of_parent ast.Type,
1130 label string) {
1131 if node.typ.idx() != element_type_of_parent.idx() {
1132 return
1133 }
1134 c.error('recursive declarations of aliases are not allowed - the alias `${node.name}` is used in the ${label}',
1135 node.type_pos)
1136}
1137
1138fn (mut c Checker) check_any_type(typ ast.Type, sym &ast.TypeSymbol, pos token.Pos) {
1139 if sym.kind == .any && !typ.has_flag(.generic) && sym.language != .js
1140 && c.file.mod.name != 'builtin' {
1141 c.error('cannot use type `any` here', pos)
1142 }
1143}
1144
1145fn (mut c Checker) fn_type_decl(mut node ast.FnTypeDecl) {
1146 c.check_valid_pascal_case(node.name, 'fn type', node.pos)
1147 typ_sym := c.table.sym(node.typ)
1148 fn_typ_info := typ_sym.info as ast.FnType
1149 fn_info := fn_typ_info.func
1150 c.ensure_type_exists(fn_info.return_type, fn_info.return_type_pos)
1151 ret_sym := c.table.sym(fn_info.return_type)
1152 if ret_sym.kind == .placeholder {
1153 c.error('unknown type `${ret_sym.name}`', fn_info.return_type_pos)
1154 }
1155 for arg in fn_info.params {
1156 if !c.ensure_type_exists(arg.typ, arg.type_pos) {
1157 return
1158 }
1159 arg_sym := c.table.sym(arg.typ)
1160 if arg_sym.kind == .placeholder {
1161 c.error('unknown type `${arg_sym.name}`', arg.type_pos)
1162 }
1163 }
1164}
1165
1166fn (mut c Checker) sum_type_decl(mut node ast.SumTypeDecl) {
1167 c.check_valid_pascal_case(node.name, 'sum type', node.pos)
1168 if c.pref.is_vls && c.pref.linfo.method == .definition {
1169 for variant in node.variants {
1170 if c.vls_is_the_node(variant.pos) {
1171 typ_str := c.table.type_to_str(variant.typ)
1172 if np := c.name_pos_gotodef(typ_str) {
1173 if np.file_idx != -1 {
1174 println('${c.table.filelist[np.file_idx]}:${np.line_nr + 1}:${np.col}')
1175 exit(0)
1176 }
1177 }
1178 }
1179 }
1180 }
1181 mut names_used := []string{}
1182 for variant in node.variants {
1183 c.ensure_type_exists(variant.typ, variant.pos)
1184 sym := c.table.sym(variant.typ)
1185 if variant.typ.is_ptr() || (sym.info is ast.Alias && sym.info.parent_type.is_ptr()) {
1186 variant_name := sym.name.all_after_last('.')
1187 lb, rb := if sym.kind == .struct { '{', '}' } else { '(', ')' }
1188 msg := if sym.info is ast.Alias && sym.info.parent_type.is_ptr() {
1189 'alias as non-reference type'
1190 } else {
1191 'the sum type with non-reference types'
1192 }
1193 c.add_error_detail('declare ${msg}: `${node.name} = ${variant_name} | ...`
1194and use a reference to the sum type instead: `var := &${node.name}(${variant_name}${lb}val${rb})`')
1195 c.error('sum type cannot hold a reference type', variant.pos)
1196 }
1197 variant_name := c.table.type_to_str(variant.typ)
1198 if variant_name in names_used {
1199 c.error('sum type ${node.name} cannot hold the type `${sym.name}` more than once',
1200 variant.pos)
1201 } else if sym.kind in [.placeholder, .int_literal, .float_literal] {
1202 c.error('unknown type `${sym.name}`', variant.pos)
1203 } else if sym.kind == .interface && sym.language != .js {
1204 c.error('sum type cannot hold an interface', variant.pos)
1205 } else if sym.kind == .struct && sym.language == .js {
1206 c.error('sum type cannot hold a JS struct', variant.pos)
1207 } else if sym.info is ast.Struct {
1208 if sym.info.is_generic {
1209 if !variant.typ.has_flag(.generic) {
1210 c.error('generic struct `${sym.name}` must specify generic type names, e.g. ${sym.name}[T]',
1211 variant.pos)
1212 }
1213 if node.generic_types.len == 0 {
1214 c.error('generic sumtype `${node.name}` must specify generic type names, e.g. ${node.name}[T]',
1215 node.name_pos)
1216 } else {
1217 for typ in sym.info.generic_types {
1218 if typ !in node.generic_types {
1219 sumtype_type_names :=
1220 node.generic_types.map(c.table.type_to_str(it)).join(', ')
1221 generic_sumtype_name := '${node.name}[${sumtype_type_names}]'
1222 generic_variant_name := c.table.type_to_str(variant.typ)
1223 c.error('generic type name `${c.table.sym(typ).name}` of generic struct `${generic_variant_name}` is not mentioned in sumtype `${generic_sumtype_name}`',
1224 variant.pos)
1225 }
1226 }
1227 }
1228 }
1229 } else if sym.info is ast.FnType {
1230 if sym.info.func.generic_names.len > 0 {
1231 if !variant.typ.has_flag(.generic) {
1232 c.error('generic fntype `${sym.name}` must specify generic type names, e.g. ${sym.name}[T]',
1233 variant.pos)
1234 }
1235 if node.generic_types.len == 0 {
1236 c.error('generic sumtype `${node.name}` must specify generic type names, e.g. ${node.name}[T]',
1237 node.name_pos)
1238 }
1239 }
1240 if c.table.sym(sym.info.func.return_type).name.ends_with('.${node.name}') {
1241 c.error('sum type `${node.name}` cannot be defined recursively', variant.pos)
1242 }
1243 for param in sym.info.func.params {
1244 if c.table.sym(param.typ).name.ends_with('.${node.name}') {
1245 c.error('sum type `${node.name}` cannot be defined recursively', variant.pos)
1246 }
1247 }
1248 } else if variant.typ.has_flag(.result) {
1249 c.error('sum type cannot hold a Result type', variant.pos)
1250 }
1251 c.check_any_type(variant.typ, sym, variant.pos)
1252
1253 if sym.name.trim_string_left(sym.mod + '.') == node.name {
1254 c.error('sum type cannot hold itself', variant.pos)
1255 } else if sym.kind == .sum_type && sym.info is ast.SumType {
1256 // Check for circular references through other sum types
1257 mut visited := map[int]bool{}
1258 visited[node.typ.idx()] = true
1259 if c.sumtype_has_circular_ref(variant.typ, node.typ, mut visited) {
1260 c.error('sum type `${node.name}` cannot be defined recursively', variant.pos)
1261 }
1262 }
1263 names_used << variant_name
1264 }
1265}
1266
1267// Checks if the sum type `sum_typ` contains `target_typ` in its variants (directly or indirectly through other sum types)
1268fn (mut c Checker) sumtype_has_circular_ref(sum_typ ast.Type, target_typ ast.Type, mut visited map[int]bool) bool {
1269 sum_sym := c.table.sym(sum_typ)
1270 if sum_sym.kind != .sum_type || sum_sym.info !is ast.SumType {
1271 return false
1272 }
1273 sum_info := sum_sym.info as ast.SumType
1274 for variant in sum_info.variants {
1275 if variant.idx() == target_typ.idx() {
1276 return true
1277 }
1278 // Avoid infinite recursion by tracking visited types
1279 if variant.idx() in visited {
1280 continue
1281 }
1282 variant_sym := c.table.sym(variant)
1283 if variant_sym.kind == .sum_type {
1284 visited[variant.idx()] = true
1285 if c.sumtype_has_circular_ref(variant, target_typ, mut visited) {
1286 return true
1287 }
1288 }
1289 }
1290 return false
1291}
1292
1293fn (mut c Checker) expand_iface_embeds(idecl &ast.InterfaceDecl, level int, iface_embeds []ast.InterfaceEmbedding) []ast.InterfaceEmbedding {
1294 // eprintln('> expand_iface_embeds: idecl.name: ${idecl.name} | level: ${level} | iface_embeds.len: ${iface_embeds.len}')
1295 if level > iface_level_cutoff_limit {
1296 c.error('too many interface embedding levels: ${level}, for interface `${idecl.name}`',
1297 idecl.pos)
1298 return []
1299 }
1300 if iface_embeds.len == 0 {
1301 return []
1302 }
1303 mut res := map[int]ast.InterfaceEmbedding{}
1304 mut ares := []ast.InterfaceEmbedding{}
1305 for ie in iface_embeds {
1306 if iface_decl := c.table.interfaces[ie.typ] {
1307 mut list := iface_decl.embeds.clone()
1308 if !iface_decl.are_embeds_expanded {
1309 list = c.expand_iface_embeds(idecl, level + 1, iface_decl.embeds)
1310 unsafe {
1311 c.table.interfaces[ie.typ].embeds = list
1312 }
1313 unsafe {
1314 c.table.interfaces[ie.typ].are_embeds_expanded = true
1315 }
1316 }
1317 for partial in list {
1318 res[partial.typ] = partial
1319 }
1320 }
1321 res[ie.typ] = ie
1322 }
1323 for _, v in res {
1324 ares << v
1325 }
1326 return ares
1327}
1328
1329fn (mut c Checker) expr_is_immutable_source(expr ast.Expr) bool {
1330 match expr {
1331 ast.Ident {
1332 return match expr.obj {
1333 ast.Var { !expr.obj.is_mut }
1334 ast.ConstField { true }
1335 else { false }
1336 }
1337 }
1338 ast.SelectorExpr {
1339 if expr.expr_type == 0 {
1340 return false
1341 }
1342 typ_sym := c.table.final_sym(c.unwrap_generic(expr.expr_type))
1343 match typ_sym.kind {
1344 .struct {
1345 if field_info := c.table.find_field_with_embeds(typ_sym, expr.field_name) {
1346 return !field_info.is_mut || c.expr_is_immutable_source(expr.expr)
1347 }
1348 }
1349 .interface {
1350 interface_info := typ_sym.info as ast.Interface
1351 if field_info := interface_info.find_field(expr.field_name) {
1352 return !field_info.is_mut || c.expr_is_immutable_source(expr.expr)
1353 }
1354 }
1355 .sum_type {
1356 sumtype_info := typ_sym.info as ast.SumType
1357 if field_info := sumtype_info.find_sum_type_field(expr.field_name) {
1358 return !field_info.is_mut || c.expr_is_immutable_source(expr.expr)
1359 }
1360 }
1361 else {}
1362 }
1363
1364 return c.expr_is_immutable_source(expr.expr)
1365 }
1366 ast.IndexExpr {
1367 return c.expr_is_immutable_source(expr.left)
1368 }
1369 ast.ParExpr {
1370 return c.expr_is_immutable_source(expr.expr)
1371 }
1372 else {
1373 return false
1374 }
1375 }
1376}
1377
1378fn (mut c Checker) type_contains_mutable_aliasing(typ ast.Type, mut checked_types []ast.Type) bool {
1379 unwrapped_typ := c.unwrap_generic(typ.clear_option_and_result())
1380 if unwrapped_typ == 0 {
1381 return false
1382 }
1383 if unwrapped_typ.has_flag(.shared_f) || unwrapped_typ.is_any_kind_of_pointer() {
1384 return true
1385 }
1386 if unwrapped_typ in checked_types {
1387 return false
1388 }
1389 checked_types << unwrapped_typ
1390 sym := c.table.sym(unwrapped_typ)
1391 match sym.info {
1392 ast.Alias {
1393 return c.type_contains_mutable_aliasing(sym.info.parent_type, mut checked_types)
1394 }
1395 ast.Array, ast.Map, ast.Interface {
1396 return true
1397 }
1398 ast.ArrayFixed {
1399 return c.type_contains_mutable_aliasing(sym.info.elem_type, mut checked_types)
1400 }
1401 ast.Struct {
1402 if sym.kind == .struct && sym.language == .v {
1403 for field in c.table.struct_fields(sym) {
1404 if !field.is_mut {
1405 continue
1406 }
1407 if c.type_contains_mutable_aliasing(field.typ, mut checked_types) {
1408 return true
1409 }
1410 }
1411 }
1412 }
1413 ast.SumType {
1414 for variant in sym.info.variants {
1415 if c.type_contains_mutable_aliasing(variant, mut checked_types) {
1416 return true
1417 }
1418 }
1419 }
1420 else {}
1421 }
1422
1423 return false
1424}
1425
1426fn (mut c Checker) type_has_mutable_aliasing(typ ast.Type) bool {
1427 mut checked_types := []ast.Type{}
1428 return c.type_contains_mutable_aliasing(typ, mut checked_types)
1429}
1430
1431fn (mut c Checker) expr_is_mutable_alias_of_immutable_source(expr ast.Expr) bool {
1432 match expr {
1433 ast.Ident {
1434 if expr.obj is ast.Var && expr.obj.is_mut && c.type_has_mutable_aliasing(expr.obj.typ) {
1435 match expr.obj.expr {
1436 ast.UnsafeExpr {
1437 return false
1438 }
1439 ast.Ident, ast.CallExpr, ast.CastExpr, ast.AsCast, ast.ParExpr {
1440 return c.expr_is_immutable_source(expr.obj.expr)
1441 || c.expr_is_mutable_alias_of_immutable_source(expr.obj.expr)
1442 }
1443 else {}
1444 }
1445 }
1446 return false
1447 }
1448 ast.CastExpr {
1449 return c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1450 }
1451 ast.CallExpr {
1452 return c.call_expr_immutable_alias_source(expr) !is ast.EmptyExpr
1453 }
1454 ast.IndexExpr {
1455 return c.type_has_mutable_aliasing(expr.typ) && (c.expr_is_immutable_source(expr.left)
1456 || c.expr_is_mutable_alias_of_immutable_source(expr.left))
1457 }
1458 ast.SelectorExpr {
1459 if expr.expr_type == 0 {
1460 return false
1461 }
1462 typ_sym := c.table.final_sym(c.unwrap_generic(expr.expr_type))
1463 match typ_sym.kind {
1464 .struct {
1465 if field_info := c.table.find_field_with_embeds(typ_sym, expr.field_name) {
1466 mut checked_types := []ast.Type{}
1467 return field_info.is_mut
1468 && c.type_contains_mutable_aliasing(field_info.typ, mut checked_types)
1469 && c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1470 }
1471 }
1472 .interface {
1473 interface_info := typ_sym.info as ast.Interface
1474 if field_info := interface_info.find_field(expr.field_name) {
1475 mut checked_types := []ast.Type{}
1476 return field_info.is_mut
1477 && c.type_contains_mutable_aliasing(field_info.typ, mut checked_types)
1478 && c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1479 }
1480 }
1481 .sum_type {
1482 sumtype_info := typ_sym.info as ast.SumType
1483 if field_info := sumtype_info.find_sum_type_field(expr.field_name) {
1484 mut checked_types := []ast.Type{}
1485 return field_info.is_mut
1486 && c.type_contains_mutable_aliasing(field_info.typ, mut checked_types)
1487 && c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1488 }
1489 }
1490 else {}
1491 }
1492
1493 return false
1494 }
1495 ast.AsCast {
1496 return c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1497 }
1498 ast.ParExpr {
1499 return c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1500 }
1501 ast.UnsafeExpr {
1502 return c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1503 }
1504 else {
1505 return false
1506 }
1507 }
1508}
1509
1510fn (mut c Checker) call_arg_expr_for_param(node ast.CallExpr, func ast.Fn, param_name string) ast.Expr {
1511 mut arg_idx := 0
1512 for i, param in func.params {
1513 if func.is_method && i == 0 {
1514 continue
1515 }
1516 if arg_idx >= node.args.len {
1517 break
1518 }
1519 if param.name == param_name {
1520 return node.args[arg_idx].expr
1521 }
1522 arg_idx++
1523 }
1524 return ast.empty_expr
1525}
1526
1527fn (mut c Checker) return_expr_immutable_alias_source(expr ast.Expr, func ast.Fn, call ast.CallExpr, allow_non_ptr bool) ast.Expr {
1528 match expr {
1529 ast.AsCast {
1530 return c.return_expr_immutable_alias_source(expr.expr, func, call, allow_non_ptr)
1531 }
1532 ast.CastExpr {
1533 return c.return_expr_immutable_alias_source(expr.expr, func, call, allow_non_ptr)
1534 }
1535 ast.CallExpr {
1536 return c.call_expr_immutable_alias_source(expr)
1537 }
1538 ast.Ident {
1539 if expr.obj is ast.Var && expr.obj.is_arg {
1540 arg_expr := c.call_arg_expr_for_param(call, func, expr.name)
1541 if arg_expr !is ast.EmptyExpr && (allow_non_ptr || expr.obj.typ.is_ptr())
1542 && c.expr_is_immutable_source(arg_expr) {
1543 return arg_expr
1544 }
1545 }
1546 if expr.obj is ast.Var && (allow_non_ptr || expr.obj.typ.is_ptr()) {
1547 return c.return_expr_immutable_alias_source(expr.obj.expr, func, call,
1548 allow_non_ptr)
1549 }
1550 return ast.empty_expr
1551 }
1552 ast.IndexExpr {
1553 if c.type_has_mutable_aliasing(expr.typ) {
1554 return c.return_expr_immutable_alias_source(expr.left, func, call, true)
1555 }
1556 return ast.empty_expr
1557 }
1558 ast.SelectorExpr {
1559 if c.type_has_mutable_aliasing(expr.typ) {
1560 return c.return_expr_immutable_alias_source(expr.expr, func, call, true)
1561 }
1562 return ast.empty_expr
1563 }
1564 ast.ParExpr {
1565 return c.return_expr_immutable_alias_source(expr.expr, func, call, allow_non_ptr)
1566 }
1567 ast.PrefixExpr {
1568 if expr.op == .amp {
1569 return c.return_expr_immutable_alias_source(expr.right, func, call, true)
1570 }
1571 return ast.empty_expr
1572 }
1573 ast.UnsafeExpr {
1574 return c.return_expr_immutable_alias_source(expr.expr, func, call, allow_non_ptr)
1575 }
1576 else {
1577 return ast.empty_expr
1578 }
1579 }
1580}
1581
1582fn (mut c Checker) node_immutable_alias_source(node ast.Node, func ast.Fn, call ast.CallExpr) ast.Expr {
1583 match node {
1584 ast.Expr {
1585 if node is ast.AnonFn {
1586 return ast.empty_expr
1587 }
1588 }
1589 ast.Stmt {
1590 if node is ast.FnDecl {
1591 return ast.empty_expr
1592 }
1593 if node is ast.Return {
1594 allow_non_ptr := !call.return_type.is_ptr()
1595 && c.type_has_mutable_aliasing(call.return_type)
1596 for expr in node.exprs {
1597 source := c.return_expr_immutable_alias_source(expr, func, call, allow_non_ptr)
1598 if source !is ast.EmptyExpr {
1599 return source
1600 }
1601 }
1602 return ast.empty_expr
1603 }
1604 }
1605 else {}
1606 }
1607
1608 for child in node.children() {
1609 source := c.node_immutable_alias_source(child, func, call)
1610 if source !is ast.EmptyExpr {
1611 return source
1612 }
1613 }
1614 return ast.empty_expr
1615}
1616
1617fn (c &Checker) immutable_alias_call_key(node ast.CallExpr) string {
1618 if node.concrete_types.len > 0 {
1619 return c.build_generic_call_key(node.fkey(), node.concrete_types)
1620 }
1621 return node.fkey()
1622}
1623
1624fn (mut c Checker) call_expr_immutable_alias_source(node ast.CallExpr) ast.Expr {
1625 if !c.type_has_mutable_aliasing(node.return_type) {
1626 return ast.empty_expr
1627 }
1628 func := c.get_fn_from_call_expr(node) or { return ast.empty_expr }
1629 call_key := c.immutable_alias_call_key(node)
1630 if call_key in c.immutable_alias_analysis_in_progress {
1631 return ast.empty_expr
1632 }
1633 c.immutable_alias_analysis_in_progress[call_key] = true
1634 defer {
1635 c.immutable_alias_analysis_in_progress.delete(call_key)
1636 }
1637 if func.source_fn == unsafe { nil } {
1638 return ast.empty_expr
1639 }
1640 fn_decl := unsafe { &ast.FnDecl(func.source_fn) }
1641 for stmt in fn_decl.stmts {
1642 source := c.node_immutable_alias_source(stmt, func, node)
1643 if source !is ast.EmptyExpr {
1644 return source
1645 }
1646 }
1647 return ast.empty_expr
1648}
1649
1650// fail_if_immutable_to_mutable checks if there is a immutable reference on right-side of assignment for mutable var
1651fn (mut c Checker) fail_if_immutable_to_mutable(left_type ast.Type, right_type ast.Type, right ast.Expr) bool {
1652 if c.inside_unsafe || c.pref.translated || c.file.is_translated {
1653 return true
1654 }
1655 match right {
1656 ast.CastExpr {
1657 iface_sym := c.table.final_sym(c.unwrap_generic(right.typ))
1658 if iface_sym.kind == .interface && iface_sym.info is ast.Interface {
1659 // Only fail if the interface has mutable fields; casting an immutable
1660 // reference to a read-only interface is safe because no mutation is
1661 // possible through the interface.
1662 has_mut_fields := iface_sym.info.fields.any(it.is_mut)
1663 if has_mut_fields {
1664 mut expr := right.expr
1665 c.fail_if_immutable(mut expr)
1666 }
1667 }
1668 }
1669 ast.CallExpr {
1670 if right_type.is_ptr() {
1671 source := c.call_expr_immutable_alias_source(right)
1672 if source !is ast.EmptyExpr {
1673 if source is ast.Ident && c.expr_is_immutable_source(source) {
1674 c.note('`${source.name}` is immutable, cannot have a mutable reference to an immutable object',
1675 source.pos)
1676 } else {
1677 c.note('call result aliases mutable data from an immutable value',
1678 right.pos)
1679 }
1680 return false
1681 }
1682 }
1683 }
1684 ast.Ident {
1685 if right.obj is ast.Var {
1686 if left_type.is_ptr() && !right.is_mut() && right_type.is_ptr() {
1687 c.note('`${right.name}` is immutable, cannot have a mutable reference to an immutable object',
1688 right.pos)
1689 return false
1690 }
1691 if !right.obj.is_mut
1692 && c.table.final_sym(right_type).kind in [.array, .array_fixed, .map] {
1693 c.note('left-side of assignment expects a mutable reference, but variable `${right.name}` is immutable, declare it with `mut` to make it mutable or clone it',
1694 right.pos)
1695 return false
1696 }
1697 }
1698 }
1699 ast.IfExpr {
1700 for branch in right.branches {
1701 stmts := branch.stmts.filter(it is ast.ExprStmt)
1702 if stmts.len > 0 {
1703 last_expr := stmts.last() as ast.ExprStmt
1704 c.fail_if_immutable_to_mutable(left_type, right_type, last_expr.expr)
1705 }
1706 }
1707 }
1708 ast.MatchExpr {
1709 for branch in right.branches {
1710 stmts := branch.stmts.filter(it is ast.ExprStmt)
1711 if stmts.len > 0 {
1712 last_expr := stmts.last() as ast.ExprStmt
1713 c.fail_if_immutable_to_mutable(left_type, right_type, last_expr.expr)
1714 }
1715 }
1716 }
1717 ast.StructInit {
1718 typ_sym := c.table.sym(right.typ)
1719 for init_field in right.init_fields {
1720 if field_info := c.table.find_field_with_embeds(typ_sym, init_field.name) {
1721 if field_info.is_mut {
1722 if init_field.expr is ast.Ident && !init_field.expr.is_mut()
1723 && init_field.typ.is_ptr() {
1724 c.error('`${init_field.expr.name}` is immutable, cannot have a mutable reference to an immutable object',
1725 init_field.pos)
1726 } else if init_field.expr is ast.PrefixExpr {
1727 if init_field.expr.op == .amp && init_field.expr.right is ast.Ident
1728 && !init_field.expr.right.is_mut() {
1729 c.error('`${init_field.expr.right.name}` is immutable, cannot have a mutable reference to an immutable object',
1730 init_field.expr.right.pos)
1731 }
1732 }
1733 }
1734 }
1735 }
1736 }
1737 else {
1738 return true
1739 }
1740 }
1741
1742 return true
1743}
1744
1745// returns name and position of variable that needs write lock
1746// also sets `is_changed` to true (TODO update the name to reflect this?)
1747fn (mut c Checker) fail_if_immutable(mut expr ast.Expr) (string, token.Pos) {
1748 mut to_lock := '' // name of variable that needs lock
1749 mut pos := token.Pos{} // and its position
1750 mut explicit_lock_needed := false
1751 match mut expr {
1752 ast.CastExpr {
1753 if c.table.final_sym(c.unwrap_generic(expr.typ)).kind == .interface {
1754 mut inner_expr := expr.expr
1755 return c.fail_if_immutable(mut inner_expr)
1756 }
1757 return '', expr.pos
1758 }
1759 ast.ComptimeSelector {
1760 mut expr_left := expr.left
1761 if mut expr.left is ast.Ident && expr.left.obj is ast.Var {
1762 c.fail_if_immutable(mut expr_left)
1763 }
1764 return '', expr.pos
1765 }
1766 ast.Ident {
1767 if mut expr.obj is ast.Var {
1768 if !expr.obj.is_mut && !c.pref.translated && !c.file.is_translated
1769 && !c.inside_unsafe {
1770 if expr.obj.smartcasts.len > 0 {
1771 c.error('cannot mutate `${expr.name}` in a non-mut smartcast, use `if mut ${expr.name} ...`',
1772 expr.pos)
1773 } else if c.inside_anon_fn {
1774 c.error('the closure copy of `${expr.name}` is immutable, declare it with `mut` to make it mutable',
1775 expr.pos)
1776 } else {
1777 c.error('`${expr.name}` is immutable, declare it with `mut` to make it mutable',
1778 expr.pos)
1779 }
1780 }
1781 expr.obj.is_changed = true
1782 if expr.obj.typ.share() == .shared_t {
1783 if expr.name !in c.locked_names {
1784 if c.locked_names.len > 0 || c.rlocked_names.len > 0 {
1785 if expr.name in c.rlocked_names {
1786 c.error('${expr.name} has an `rlock` but needs a `lock`', expr.pos)
1787 } else {
1788 c.error('${expr.name} must be added to the `lock` list above',
1789 expr.pos)
1790 }
1791 }
1792 to_lock = expr.name
1793 pos = expr.pos
1794 }
1795 }
1796 } else if expr.obj is ast.ConstField && expr.name in c.const_names {
1797 if !c.pref.translated && c.mod != 'veb' {
1798 // TODO: fix this in c2v, do not allow modification of all consts
1799 // in translated code
1800 c.error('cannot modify constant `${expr.name}`', expr.pos)
1801 }
1802 } else if expr.obj is ast.GlobalField && expr.obj.is_const {
1803 if !c.pref.translated && c.mod != 'veb' {
1804 c.error('cannot modify constant `${expr.name}`', expr.pos)
1805 }
1806 }
1807 }
1808 ast.IndexExpr {
1809 if expr.left_type == 0 {
1810 return to_lock, pos
1811 }
1812 left_sym := c.table.sym(expr.left_type)
1813 if !c.inside_unsafe && left_sym.kind == .array
1814 && c.expr_is_mutable_alias_of_immutable_source(expr.left) {
1815 c.error('`${expr.left}` aliases mutable data from an immutable value, clone it first (or use `unsafe`)',
1816 expr.left.pos())
1817 return '', expr.pos
1818 }
1819 mut elem_type := ast.no_type
1820 mut kind := ''
1821 match left_sym.info {
1822 ast.Array {
1823 elem_type, kind = left_sym.info.elem_type, 'array'
1824 }
1825 ast.ArrayFixed {
1826 elem_type, kind = left_sym.info.elem_type, 'fixed array'
1827 }
1828 ast.Map {
1829 elem_type, kind = left_sym.info.value_type, 'map'
1830 }
1831 else {}
1832 }
1833
1834 if elem_type.has_flag(.shared_f) {
1835 expr_name := ast.Expr(expr).str()
1836 if expr_name !in c.locked_names {
1837 if c.locked_names.len > 0 || c.rlocked_names.len > 0 {
1838 if expr_name in c.rlocked_names {
1839 c.error('${expr_name} has an `rlock` but needs a `lock`',
1840 expr.left.pos().extend(expr.pos))
1841 } else {
1842 c.error('${expr_name} must be added to the `lock` list above',
1843 expr.left.pos().extend(expr.pos))
1844 }
1845 } else {
1846 c.error('you have to create a handle and `lock` it to modify `shared` ${kind} element',
1847 expr.left.pos().extend(expr.pos))
1848 }
1849 return '', expr.pos
1850 }
1851 return '', expr.pos
1852 }
1853 to_lock, pos = c.fail_if_immutable(mut expr.left)
1854 }
1855 ast.ParExpr {
1856 to_lock, pos = c.fail_if_immutable(mut expr.expr)
1857 }
1858 ast.PrefixExpr {
1859 if expr.op == .mul && expr.right is ast.Ident {
1860 // Do not fail if dereference is immutable:
1861 // `*x = foo()` doesn't modify `x`
1862 } else {
1863 to_lock, pos = c.fail_if_immutable(mut expr.right)
1864 }
1865 }
1866 ast.PostfixExpr {
1867 to_lock, pos = c.fail_if_immutable(mut expr.expr)
1868 }
1869 ast.SelectorExpr {
1870 if expr.expr_type == 0 {
1871 return '', expr.pos
1872 }
1873 scope_field := expr.scope.find_struct_field(smartcast_selector_expr_str(expr),
1874 expr.expr_type, expr.field_name)
1875 mut selector_smartcast_is_mut := false
1876 if scope_field != unsafe { nil } {
1877 selector_smartcast_is_mut = scope_field.is_mut
1878 }
1879 if !selector_smartcast_is_mut {
1880 expr_sym := c.table.sym(expr.expr_type)
1881 if field := c.table.find_field(expr_sym, expr.field_name) {
1882 if field.is_mut {
1883 if root_ident := expr.root_ident() {
1884 selector_smartcast_is_mut = root_ident.is_mut()
1885 if !selector_smartcast_is_mut {
1886 if v := expr.scope.find_var(root_ident.name) {
1887 selector_smartcast_is_mut = v.is_mut
1888 }
1889 }
1890 }
1891 }
1892 }
1893 }
1894 if scope_field != unsafe { nil } && scope_field.smartcasts.len > 0
1895 && !selector_smartcast_is_mut && !c.pref.translated && !c.file.is_translated
1896 && !c.inside_unsafe {
1897 expr_str := expr.str()
1898 c.error('cannot mutate `${expr_str}` in a non-mut smartcast, use `if mut ${expr_str} ...`',
1899 expr.pos)
1900 }
1901 // retrieve ast.Field
1902 if !c.ensure_type_exists(expr.expr_type, expr.pos) {
1903 return '', expr.pos
1904 }
1905 mut typ_sym := c.table.final_sym(c.unwrap_generic(expr.expr_type))
1906 match typ_sym.kind {
1907 .struct {
1908 mut has_field := true
1909 mut field_info := c.table.find_field_with_embeds(typ_sym, expr.field_name) or {
1910 has_field = false
1911 ast.StructField{}
1912 }
1913 if !has_field {
1914 type_str := c.table.type_to_str(expr.expr_type)
1915 c.error('unknown field `${type_str}.${expr.field_name}`', expr.pos)
1916 return '', expr.pos
1917 }
1918 if field_info.typ.has_flag(.shared_f) {
1919 expr_name := '${expr.expr}.${expr.field_name}'
1920 if expr_name !in c.locked_names {
1921 if c.locked_names.len > 0 || c.rlocked_names.len > 0 {
1922 if expr_name in c.rlocked_names {
1923 c.error('${expr_name} has an `rlock` but needs a `lock`',
1924 expr.pos)
1925 } else {
1926 c.error('${expr_name} must be added to the `lock` list above',
1927 expr.pos)
1928 }
1929 return '', expr.pos
1930 }
1931 to_lock = expr_name
1932 pos = expr.pos
1933 }
1934 } else {
1935 if !field_info.is_mut && !c.pref.translated && !c.file.is_translated {
1936 type_str := c.table.type_to_str(expr.expr_type)
1937 c.error('field `${expr.field_name}` of struct `${type_str}` is immutable',
1938 expr.pos)
1939 }
1940 if field_info.is_mut && expr.expr_type.is_ptr() && !c.inside_unsafe
1941 && c.expr_is_mutable_alias_of_immutable_source(expr.expr) {
1942 expr_str := ast.Expr(expr).str()
1943 c.error('`${expr_str}` aliases mutable data from an immutable value',
1944 expr.pos)
1945 return '', expr.pos
1946 }
1947 to_lock, pos = c.fail_if_immutable(mut expr.expr)
1948 }
1949 if to_lock != '' {
1950 // No automatic lock for struct access
1951 explicit_lock_needed = true
1952 }
1953 }
1954 .interface {
1955 interface_info := typ_sym.info as ast.Interface
1956 mut field_info := interface_info.find_field(expr.field_name) or {
1957 type_str := c.table.type_to_str(expr.expr_type)
1958 c.error('unknown field `${type_str}.${expr.field_name}`', expr.pos)
1959 return '', expr.pos
1960 }
1961 if !field_info.is_mut {
1962 type_str := c.table.type_to_str(expr.expr_type)
1963 c.error('field `${expr.field_name}` of interface `${type_str}` is immutable',
1964 expr.pos)
1965 return '', expr.pos
1966 }
1967 if expr.expr_type.is_ptr() && !c.inside_unsafe
1968 && c.expr_is_mutable_alias_of_immutable_source(expr.expr) {
1969 expr_str := ast.Expr(expr).str()
1970 c.error('`${expr_str}` aliases mutable data from an immutable value',
1971 expr.pos)
1972 return '', expr.pos
1973 }
1974 c.fail_if_immutable(mut expr.expr)
1975 }
1976 .sum_type {
1977 sumtype_info := typ_sym.info as ast.SumType
1978 mut field_info := sumtype_info.find_sum_type_field(expr.field_name) or {
1979 type_str := c.table.type_to_str(expr.expr_type)
1980 c.error('unknown field `${type_str}.${expr.field_name}`', expr.pos)
1981 return '', expr.pos
1982 }
1983 if !field_info.is_mut {
1984 type_str := c.table.type_to_str(expr.expr_type)
1985 c.error('field `${expr.field_name}` of sumtype `${type_str}` is immutable',
1986 expr.pos)
1987 return '', expr.pos
1988 }
1989 if expr.expr_type.is_ptr() && !c.inside_unsafe
1990 && c.expr_is_mutable_alias_of_immutable_source(expr.expr) {
1991 expr_str := ast.Expr(expr).str()
1992 c.error('`${expr_str}` aliases mutable data from an immutable value',
1993 expr.pos)
1994 return '', expr.pos
1995 }
1996 c.fail_if_immutable(mut expr.expr)
1997 }
1998 .array, .string {
1999 // should only happen in `builtin` and unsafe blocks
2000 inside_builtin := c.file.mod.name == 'builtin'
2001 if !inside_builtin && !c.inside_unsafe {
2002 c.error('`${typ_sym.kind}` can not be modified', expr.pos)
2003 return '', expr.pos
2004 }
2005 }
2006 .aggregate, .placeholder, .generic_inst {
2007 c.fail_if_immutable(mut expr.expr)
2008 }
2009 else {
2010 c.error('unexpected symbol `${typ_sym.kind}`', expr.pos)
2011 return '', expr.pos
2012 }
2013 }
2014 }
2015 ast.CallExpr {
2016 // TODO: should only work for builtin method
2017 if expr.name == 'slice' {
2018 to_lock, pos = c.fail_if_immutable(mut expr.left)
2019 if to_lock != '' {
2020 // No automatic lock for array slicing (yet(?))
2021 explicit_lock_needed = true
2022 }
2023 }
2024 if !c.inside_unsafe {
2025 source := c.call_expr_immutable_alias_source(expr)
2026 if source !is ast.EmptyExpr {
2027 if source is ast.Ident && c.expr_is_immutable_source(source) {
2028 c.error('`${source.name}` is immutable, cannot have a mutable reference to an immutable object',
2029 source.pos)
2030 } else {
2031 c.error('`${expr}` aliases mutable data from an immutable value', expr.pos)
2032 }
2033 return '', expr.pos
2034 }
2035 }
2036 }
2037 ast.ArrayInit {
2038 c.error('array literal can not be modified', expr.pos)
2039 return '', expr.pos
2040 }
2041 ast.StructInit {
2042 return '', expr.pos
2043 }
2044 ast.InfixExpr {
2045 return '', expr.pos
2046 }
2047 ast.IfExpr {
2048 for mut branch in expr.branches {
2049 mut last_expr := (branch.stmts.last() as ast.ExprStmt).expr
2050 c.fail_if_immutable(mut last_expr)
2051 }
2052 return '', expr.pos
2053 }
2054 ast.AsCast {
2055 to_lock, pos = c.fail_if_immutable(mut expr.expr)
2056 }
2057 ast.UnsafeExpr {
2058 to_lock, pos = c.fail_if_immutable(mut expr.expr)
2059 }
2060 ast.Nil {
2061 return '', expr.pos
2062 }
2063 else {
2064 if !expr.is_pure_literal() {
2065 c.error('unexpected expression `${expr.type_name()}`', expr.pos())
2066 return '', expr.pos()
2067 }
2068 }
2069 }
2070
2071 if explicit_lock_needed {
2072 c.error('`${to_lock}` is `shared` and needs explicit lock for `${expr.type_name()}`', pos)
2073 to_lock = ''
2074 }
2075 return to_lock, pos
2076}
2077
2078fn (mut c Checker) resolve_method_for_concrete_type(method ast.Fn, typ_sym &ast.TypeSymbol) ast.Fn {
2079 mut resolved_method := method
2080 concrete_types := c.concrete_types_for_type_symbol(typ_sym)
2081 generic_names := c.generic_names_for_type_parent(typ_sym)
2082 if generic_names.len == 0 || generic_names.len != concrete_types.len {
2083 return resolved_method
2084 }
2085 if rt := c.table.convert_generic_type(resolved_method.return_type, generic_names,
2086 concrete_types)
2087 {
2088 resolved_method.return_type = rt
2089 }
2090 resolved_method.params = resolved_method.params.clone()
2091 for mut param in resolved_method.params {
2092 if pt := c.table.convert_generic_type(param.typ, generic_names, concrete_types) {
2093 param.typ = pt
2094 }
2095 }
2096 return resolved_method
2097}
2098
2099fn (c &Checker) concrete_types_for_type_symbol(typ_sym &ast.TypeSymbol) []ast.Type {
2100 match typ_sym.info {
2101 ast.Struct, ast.Interface, ast.SumType {
2102 mut concrete_types := typ_sym.info.concrete_types.clone()
2103 if concrete_types.len == 0
2104 && typ_sym.generic_types.len == typ_sym.info.generic_types.len
2105 && typ_sym.generic_types != typ_sym.info.generic_types {
2106 concrete_types = typ_sym.generic_types.clone()
2107 }
2108 return concrete_types
2109 }
2110 ast.GenericInst {
2111 return typ_sym.info.concrete_types.clone()
2112 }
2113 else {}
2114 }
2115
2116 return []ast.Type{}
2117}
2118
2119fn (c &Checker) generic_names_for_type_parent(typ_sym &ast.TypeSymbol) []string {
2120 match typ_sym.info {
2121 ast.Struct, ast.Interface, ast.SumType {
2122 if !typ_sym.info.parent_type.has_flag(.generic) {
2123 return []string{}
2124 }
2125 parent_sym := c.table.sym(typ_sym.info.parent_type)
2126 match parent_sym.info {
2127 ast.Struct, ast.Interface, ast.SumType {
2128 return parent_sym.info.generic_types.map(c.table.sym(it).name)
2129 }
2130 else {}
2131 }
2132 }
2133 ast.GenericInst {
2134 parent_sym := c.table.sym(ast.new_type(typ_sym.info.parent_idx))
2135 match parent_sym.info {
2136 ast.Struct, ast.Interface, ast.SumType {
2137 return parent_sym.info.generic_types.map(c.table.sym(it).name)
2138 }
2139 else {}
2140 }
2141 }
2142 else {}
2143 }
2144
2145 return []string{}
2146}
2147
2148fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos token.Pos) bool {
2149 return c.type_implements_with_mut_receiver(typ, interface_type, pos, false)
2150}
2151
2152fn (mut c Checker) type_implements_allowing_mut_receiver(typ ast.Type, interface_type ast.Type, pos token.Pos) bool {
2153 return c.type_implements_with_mut_receiver(typ, interface_type, pos, true)
2154}
2155
2156fn (mut c Checker) type_implements_with_mut_receiver(typ ast.Type, interface_type ast.Type, pos token.Pos, allow_mut_receiver bool) bool {
2157 mut resolved_interface_type := c.unwrap_generic(interface_type)
2158 if typ == resolved_interface_type {
2159 return true
2160 }
2161 $if debug_interface_type_implements ? {
2162 eprintln('> type_implements typ: ${typ.debug()} (`${c.table.type_to_str(typ)}`) | inter_typ: ${resolved_interface_type.debug()} (`${c.table.type_to_str(resolved_interface_type)}`)')
2163 }
2164 utyp := c.unwrap_generic(typ)
2165 styp := c.table.type_to_str(utyp)
2166 typ_sym := c.table.sym(utyp)
2167 mut inter_sym := c.table.final_sym(resolved_interface_type)
2168 if !inter_sym.is_pub && inter_sym.mod !in [typ_sym.mod, c.mod] && typ_sym.mod != 'builtin' {
2169 c.error('`${styp}` cannot implement private interface `${inter_sym.name}` of other module',
2170 pos)
2171 return false
2172 }
2173
2174 // small hack for JS.Any type. Since `any` in regular V is getting deprecated we have our own JS.Any type for JS backend.
2175 if typ_sym.name == 'JS.Any' {
2176 return true
2177 }
2178 if inter_sym.kind == .interface && c.table.get_attrs(inter_sym).any(it.name == 'single_impl') {
2179 if pos.file_idx != -1 {
2180 c.error('cannot use `${styp}` as `${inter_sym.name}` without an explicit cast (e.g. `${inter_sym.name}(value)`)',
2181 pos)
2182 }
2183 return false
2184 }
2185 if typ_sym.kind == .function && inter_sym.name != 'JS.Any' {
2186 c.error('cannot implement interface `${inter_sym.name}` using function', pos)
2187 return false
2188 }
2189 if mut inter_sym.info is ast.Interface {
2190 mut generic_type := resolved_interface_type
2191 mut generic_info := inter_sym.info
2192 if inter_sym.info.parent_type.has_flag(.generic) && (inter_sym.info.concrete_types.len == 0
2193 || inter_sym.info.concrete_types.any(it.has_flag(.generic))) {
2194 parent_sym := c.table.sym(inter_sym.info.parent_type)
2195 if parent_sym.info is ast.Interface {
2196 generic_type = inter_sym.info.parent_type
2197 generic_info = parent_sym.info
2198 }
2199 }
2200 mut inferred_type := resolved_interface_type
2201 is_fully_concrete := inter_sym.info.concrete_types.len > 0
2202 && !inter_sym.info.concrete_types.any(it.has_flag(.generic))
2203 if generic_info.is_generic && !is_fully_concrete {
2204 inferred_type = c.unwrap_generic_interface(typ, generic_type, pos)
2205 if inferred_type == 0 {
2206 return false
2207 }
2208 }
2209 if inter_sym.info.is_generic && !is_fully_concrete {
2210 if inferred_type == resolved_interface_type {
2211 // terminate early, since otherwise we get an infinite recursion/segfault:
2212 return false
2213 }
2214 return c.type_implements_with_mut_receiver(typ, inferred_type, pos, allow_mut_receiver)
2215 }
2216 }
2217 if inter_sym.kind == .generic_inst {
2218 generic_inst_info := inter_sym.info as ast.GenericInst
2219 if !generic_inst_info.concrete_types.any(it.has_flag(.generic)) {
2220 c.table.generic_insts_to_concrete()
2221 inter_sym = c.table.final_sym(resolved_interface_type)
2222 }
2223 }
2224 // do not check the same type more than once
2225 if mut inter_sym.info is ast.Interface {
2226 if inter_sym.info.has_implementor(utyp, allow_mut_receiver) {
2227 return true
2228 }
2229 }
2230 if utyp.idx() == resolved_interface_type.idx() {
2231 // same type -> already casted to the interface
2232 return true
2233 }
2234 if resolved_interface_type.idx() == ast.error_type_idx && utyp.idx() == ast.none_type_idx {
2235 // `none` "implements" the Error interface
2236 return true
2237 }
2238 is_interface_upcast := typ_sym.kind == .interface && inter_sym.kind == .interface
2239 && !styp.starts_with('JS.') && !inter_sym.name.starts_with('JS.')
2240 if is_interface_upcast {
2241 if mut inter_sym.info is ast.Interface {
2242 if inter_sym.info.methods.len == 0 && inter_sym.info.fields.len == 0
2243 && inter_sym.info.embeds.len == 0 {
2244 // Preserve the source interface's known variants so the empty interface keeps
2245 // the original dynamic type for later `is`/`as` checks and conversions.
2246 mut source_types := []ast.Type{}
2247 match typ_sym.info {
2248 ast.Interface {
2249 source_types = typ_sym.info.implementor_types(true)
2250 }
2251 else {}
2252 }
2253
2254 mut target_variants := c.table.iface_types[inter_sym.name]
2255 for variant in source_types {
2256 variant_sym := c.table.sym(ast.mktyp(variant))
2257 if variant in [ast.nil_type, ast.none_type] || variant_sym.kind == .interface {
2258 continue
2259 }
2260 if !inter_sym.info.types.contains(variant) {
2261 inter_sym.info.types << variant
2262 }
2263 if variant !in target_variants {
2264 target_variants << variant
2265 }
2266 }
2267 c.table.iface_types[inter_sym.name] = target_variants
2268 if !inter_sym.info.types.contains(ast.voidptr_type) {
2269 inter_sym.info.types << ast.voidptr_type
2270 }
2271 return true
2272 }
2273 }
2274 if !c.table.interface_inherits_interface(utyp, resolved_interface_type) {
2275 c.error('cannot implement interface `${inter_sym.name}` with a different interface `${styp}`',
2276 pos)
2277 return false
2278 }
2279 }
2280 interface_sym := c.table.sym(resolved_interface_type)
2281 mut interface_generic_names := []string{}
2282 mut interface_concrete_types := []ast.Type{}
2283 mut interface_info := ast.Interface{}
2284 mut has_resolved_interface_info := false
2285 if interface_sym.kind == .interface && interface_sym.info is ast.Interface {
2286 // For concrete generic interfaces (has concrete_types, not generic, has parent),
2287 // resolve methods from the root generic parent interface to get correct method
2288 // signatures. The methods stored in the concrete interface's info may have been
2289 // resolved with wrong type parameters for nested generic types.
2290 if interface_sym.info.concrete_types.len > 0
2291 && !interface_sym.info.concrete_types.any(it.has_flag(.generic))
2292 && interface_sym.info.parent_type != 0 {
2293 mut root_type := interface_sym.info.parent_type
2294 for {
2295 rs := c.table.sym(root_type)
2296 if rs.info is ast.Interface {
2297 if rs.info.parent_type != 0 {
2298 root_type = rs.info.parent_type
2299 } else {
2300 break
2301 }
2302 } else {
2303 break
2304 }
2305 }
2306 root_sym := c.table.sym(root_type)
2307 if root_sym.info is ast.Interface {
2308 if root_sym.info.is_generic {
2309 interface_info = root_sym.info
2310 interface_generic_names = interface_info.generic_types.map(c.table.sym(it).name)
2311 interface_concrete_types = interface_sym.info.concrete_types.clone()
2312 has_resolved_interface_info = true
2313 }
2314 }
2315 }
2316 if !has_resolved_interface_info {
2317 interface_info = interface_sym.info
2318 interface_generic_names = interface_info.generic_types.map(c.table.sym(it).name)
2319 interface_concrete_types = interface_info.concrete_types.clone()
2320 if interface_concrete_types.len == 0
2321 && interface_sym.generic_types.len == interface_generic_names.len {
2322 interface_concrete_types = interface_sym.generic_types.clone()
2323 }
2324 has_resolved_interface_info = true
2325 }
2326 } else if interface_sym.kind == .generic_inst {
2327 parent_sym := c.table.sym(ast.new_type((interface_sym.info as ast.GenericInst).parent_idx))
2328 if parent_sym.info is ast.Interface {
2329 interface_info = parent_sym.info
2330 interface_generic_names = interface_info.generic_types.map(c.table.sym(it).name)
2331 interface_concrete_types =
2332 (interface_sym.info as ast.GenericInst).concrete_types.clone()
2333 has_resolved_interface_info = true
2334 }
2335 }
2336 imethods := if has_resolved_interface_info {
2337 mut methods := []ast.Fn{}
2338 for imethod in interface_info.methods {
2339 mut resolved_method := imethod
2340 if interface_generic_names.len == interface_concrete_types.len {
2341 if resolved_return_type := c.table.convert_generic_type(imethod.return_type,
2342 interface_generic_names, interface_concrete_types)
2343 {
2344 resolved_method.return_type = resolved_return_type
2345 }
2346 mut resolved_params := resolved_method.params.clone()
2347 for i in 0 .. resolved_params.len {
2348 if resolved_param_type := c.table.convert_generic_type(resolved_params[i].typ,
2349 interface_generic_names, interface_concrete_types)
2350 {
2351 resolved_params[i].typ = resolved_param_type
2352 }
2353 }
2354 resolved_method.params = resolved_params
2355 }
2356 methods << resolved_method
2357 }
2358 methods
2359 } else {
2360 inter_sym.methods
2361 }
2362 mut requires_mut_receiver := false
2363 // voidptr is an escape hatch, it should be allowed to be passed
2364 if utyp != ast.voidptr_type && utyp != ast.nil_type && !(interface_type.has_flag(.option)
2365 && utyp == ast.none_type) {
2366 mut are_methods_implemented := true
2367
2368 // Verify methods
2369 for imethod in imethods {
2370 if c.table.is_compatible_auto_str_method(imethod) && utyp.nr_muls() == 0
2371 && typ_sym.kind == .char {
2372 c.error("`${styp}` doesn't implement method `${imethod.name}` of interface `${inter_sym.name}`",
2373 pos)
2374 are_methods_implemented = false
2375 continue
2376 }
2377 mut method := typ_sym.find_method_with_generic_parent(imethod.name) or {
2378 c.table.find_method_with_embeds(typ_sym, imethod.name) or {
2379 if c.table.type_has_implicit_str_method(utyp, imethod) {
2380 are_methods_implemented = true
2381 continue
2382 }
2383 c.error("`${styp}` doesn't implement method `${imethod.name}` of interface `${inter_sym.name}`",
2384 pos)
2385 are_methods_implemented = false
2386 continue
2387 }
2388 }
2389 method = c.resolve_method_for_concrete_type(method, typ_sym)
2390
2391 mut checked_method := ast.Fn{
2392 ...imethod
2393 params: imethod.params.clone()
2394 }
2395 mut used_mut_receiver := false
2396 if allow_mut_receiver && checked_method.params.len > 0 && method.params.len > 0
2397 && !checked_method.params[0].is_mut && method.params[0].is_mut {
2398 checked_method.params[0] = ast.Param{
2399 ...checked_method.params[0]
2400 is_mut: true
2401 }
2402 used_mut_receiver = true
2403 }
2404 msg := c.table.is_same_method(checked_method, method)
2405 if msg.len > 0 {
2406 sig := c.table.fn_signature(imethod, skip_receiver: false)
2407 typ_sig := c.table.fn_signature(method, skip_receiver: false)
2408 c.add_error_detail('${inter_sym.name} has `${sig}`')
2409 c.add_error_detail(' ${typ_sym.name} has `${typ_sig}`')
2410 c.error('`${styp}` incorrectly implements method `${imethod.name}` of interface `${inter_sym.name}`: ${msg}',
2411 pos)
2412 return false
2413 }
2414 if used_mut_receiver {
2415 requires_mut_receiver = true
2416 }
2417 }
2418
2419 if !are_methods_implemented {
2420 return false
2421 }
2422 }
2423 // Verify fields
2424 if mut inter_sym.info is ast.Interface {
2425 for ifield in inter_sym.info.fields {
2426 if field := c.table.find_field_with_embeds(typ_sym, ifield.name) {
2427 if ifield.typ != field.typ {
2428 exp := c.table.type_to_str(ifield.typ)
2429 got := c.table.type_to_str(field.typ)
2430 c.error('`${styp}` incorrectly implements field `${ifield.name}` of interface `${inter_sym.name}`, expected `${exp}`, got `${got}`',
2431 pos)
2432 return false
2433 } else if ifield.is_mut && !(field.is_mut || field.is_global) {
2434 c.error('`${styp}` incorrectly implements interface `${inter_sym.name}`, field `${ifield.name}` must be mutable',
2435 pos)
2436 return false
2437 }
2438 continue
2439 }
2440 // voidptr is an escape hatch, it should be allowed to be passed
2441 if utyp != ast.voidptr_type && utyp != ast.nil_type {
2442 c.error("`${styp}` doesn't implement field `${ifield.name}` of interface `${inter_sym.name}`",
2443 pos)
2444 }
2445 }
2446 if !is_interface_upcast && utyp != ast.voidptr_type && utyp != ast.nil_type
2447 && utyp != ast.none_type {
2448 if requires_mut_receiver {
2449 if !inter_sym.info.mut_types.contains(utyp) {
2450 inter_sym.info.mut_types << utyp
2451 }
2452 } else if !inter_sym.info.types.contains(utyp) {
2453 inter_sym.info.types << utyp
2454 }
2455 }
2456 if !inter_sym.info.types.contains(ast.voidptr_type) {
2457 inter_sym.info.types << ast.voidptr_type
2458 }
2459 } else if has_resolved_interface_info {
2460 for ifield in interface_info.fields {
2461 mut resolved_ifield := ifield
2462 if interface_generic_names.len == interface_concrete_types.len {
2463 if ft := c.table.convert_generic_type(ifield.typ, interface_generic_names,
2464 interface_concrete_types)
2465 {
2466 resolved_ifield.typ = ft
2467 }
2468 }
2469 if field := c.table.find_field_with_embeds(typ_sym, resolved_ifield.name) {
2470 if resolved_ifield.typ != field.typ {
2471 exp := c.table.type_to_str(resolved_ifield.typ)
2472 got := c.table.type_to_str(field.typ)
2473 c.error('`${styp}` incorrectly implements field `${resolved_ifield.name}` of interface `${inter_sym.name}`, expected `${exp}`, got `${got}`',
2474 pos)
2475 return false
2476 } else if resolved_ifield.is_mut && !(field.is_mut || field.is_global) {
2477 c.error('`${styp}` incorrectly implements interface `${inter_sym.name}`, field `${resolved_ifield.name}` must be mutable',
2478 pos)
2479 return false
2480 }
2481 continue
2482 }
2483 if utyp != ast.voidptr_type && utyp != ast.nil_type {
2484 c.error("`${styp}` doesn't implement field `${resolved_ifield.name}` of interface `${inter_sym.name}`",
2485 pos)
2486 }
2487 }
2488 }
2489 return true
2490}
2491
2492// helper for expr_or_block_err
2493fn is_field_to_description(expr_name string, is_field bool) string {
2494 return if is_field {
2495 'field `${expr_name}` is not'
2496 } else {
2497 'function `${expr_name}` does not return'
2498 }
2499}
2500
2501fn (mut c Checker) expr_or_block_err(kind ast.OrKind, expr_name string, pos token.Pos, is_field bool) {
2502 match kind {
2503 .absent {
2504 // do nothing, most common case; do not be tempted to move the call to is_field_to_description above it, since that will slow it down
2505 }
2506 .block {
2507 obj_does_not_return_or_is_not := is_field_to_description(expr_name, is_field)
2508 c.error('unexpected `or` block, the ${obj_does_not_return_or_is_not} an Option or a Result',
2509 pos)
2510 }
2511 .propagate_option {
2512 obj_does_not_return_or_is_not := is_field_to_description(expr_name, is_field)
2513 c.error('unexpected `?`, the ${obj_does_not_return_or_is_not} an Option', pos)
2514 }
2515 .propagate_result {
2516 obj_does_not_return_or_is_not := is_field_to_description(expr_name, is_field)
2517 c.error('unexpected `!`, the ${obj_does_not_return_or_is_not} a Result', pos)
2518 }
2519 }
2520}
2521
2522// return the actual type of the expression, once the result or option type is handled
2523fn (mut c Checker) check_expr_option_or_result_call(expr ast.Expr, ret_type ast.Type) ast.Type {
2524 if ret_type.idx() == 0 {
2525 return ret_type
2526 }
2527 match expr {
2528 ast.CallExpr {
2529 mut expr_ret_type := expr.return_type
2530 if expr_ret_type != 0 && c.table.sym(expr_ret_type).kind == .alias {
2531 unaliased_ret_type := c.table.unaliased_type(expr_ret_type)
2532 if unaliased_ret_type.has_option_or_result() {
2533 expr_ret_type = unaliased_ret_type
2534 }
2535 }
2536 // var with option function
2537 if expr.is_fn_var && expr.fn_var_type.has_option_or_result()
2538 && expr.or_block.kind == .absent {
2539 ret_sym := c.table.sym(expr.fn_var_type)
2540 if expr.fn_var_type.has_flag(.option) {
2541 c.error('type `?${ret_sym.name}` is an Option, it must be unwrapped first',
2542 expr.pos)
2543 } else {
2544 c.error('type `?${ret_sym.name}` is an Result, it must be unwrapped first',
2545 expr.pos)
2546 }
2547 }
2548 if expr_ret_type.has_option_or_result() {
2549 if expr.or_block.kind == .absent {
2550 ret_sym := c.table.sym(expr_ret_type)
2551 if expr_ret_type.has_flag(.result)
2552 || (expr_ret_type.has_flag(.option) && ret_sym.kind == .multi_return) {
2553 ret_typ_tok := if expr_ret_type.has_flag(.option) { '?' } else { '!' }
2554 if c.inside_defer {
2555 c.error('${expr.name}() returns `${ret_typ_tok}${ret_sym.name}`, so it should have an `or {}` block at the end',
2556 expr.pos)
2557 } else {
2558 c.error('${expr.name}() returns `${ret_typ_tok}${ret_sym.name}`, so it should have either an `or {}` block, or `${ret_typ_tok}` at the end',
2559 expr.pos)
2560 }
2561 }
2562 } else {
2563 last_cur_or_expr := c.cur_or_expr
2564 c.cur_or_expr = &expr.or_block
2565 or_block_value_type := expr_ret_type.clear_option_and_result()
2566 or_block_unaliased_value_type :=
2567 c.table.fully_unaliased_type(or_block_value_type)
2568 mut or_block_ret_type := or_block_value_type
2569 if ret_type == ast.void_type {
2570 or_block_ret_type = ret_type
2571 } else if ret_type.has_option_or_result() {
2572 ret_value_type :=
2573 c.table.fully_unaliased_type(ret_type).clear_option_and_result()
2574 if ret_value_type == or_block_unaliased_value_type {
2575 or_block_ret_type = ret_type
2576 }
2577 }
2578 c.check_or_expr(expr.or_block, or_block_ret_type, expr_ret_type, expr)
2579 c.cur_or_expr = last_cur_or_expr
2580 }
2581 return if expr.or_block.kind == .absent {
2582 ret_type.clear_flag(.result)
2583 } else {
2584 ret_type.clear_flag(.option).clear_flag(.result)
2585 }
2586 } else {
2587 c.expr_or_block_err(expr.or_block.kind, expr.name, expr.or_block.pos, false)
2588 }
2589 }
2590 ast.SelectorExpr {
2591 if c.table.sym(ret_type).kind != .chan {
2592 if expr.typ.has_option_or_result() {
2593 with_modifier_kind := if expr.typ.has_flag(.option) {
2594 'an Option'
2595 } else {
2596 'a Result'
2597 }
2598 with_modifier := if expr.typ.has_flag(.option) { '?' } else { '!' }
2599 if expr.typ.has_flag(.result) && expr.or_block.kind == .absent {
2600 if c.inside_defer {
2601 c.error('field `${expr.field_name}` is ${with_modifier_kind}, so it should have an `or {}` block at the end',
2602 expr.pos)
2603 } else {
2604 c.error('field `${expr.field_name}` is ${with_modifier_kind}, so it should have either an `or {}` block, or `${with_modifier}` at the end',
2605 expr.pos)
2606 }
2607 } else {
2608 if expr.or_block.kind != .absent {
2609 last_cur_or_expr := c.cur_or_expr
2610 c.cur_or_expr = &expr.or_block
2611 or_block_value_type := expr.typ.clear_option_and_result()
2612 or_block_unaliased_value_type :=
2613 c.table.fully_unaliased_type(or_block_value_type)
2614 mut or_block_ret_type := or_block_value_type
2615 if ret_type.has_option_or_result() {
2616 ret_value_type :=
2617 c.table.fully_unaliased_type(ret_type).clear_option_and_result()
2618 if ret_value_type == or_block_unaliased_value_type {
2619 or_block_ret_type = ret_type
2620 }
2621 }
2622 c.check_or_expr(expr.or_block, or_block_ret_type, expr.typ, expr)
2623 c.cur_or_expr = last_cur_or_expr
2624 }
2625 }
2626 return if expr.or_block.kind == .absent {
2627 ret_type.clear_flag(.result)
2628 } else {
2629 ret_type.clear_flag(.option).clear_flag(.result)
2630 }
2631 } else {
2632 c.expr_or_block_err(expr.or_block.kind, expr.field_name, expr.or_block.pos,
2633 true)
2634 }
2635 }
2636 }
2637 ast.IndexExpr {
2638 if expr.or_expr.kind != .absent {
2639 // When the IndexExpr has already been processed by index_expr()
2640 // (indicated by expr.typ being set), skip re-checking the or block
2641 // since it was already validated. This prevents the duplicate call
2642 // from assign.v from rejecting valid `none` in option map or blocks.
2643 if expr.typ != 0 {
2644 if ret_type.has_flag(.option) && expr.or_expr.stmts.len > 0 {
2645 last := expr.or_expr.stmts.last()
2646 if last is ast.ExprStmt && last.expr is ast.None {
2647 return ret_type
2648 }
2649 }
2650 return ret_type.clear_option_and_result()
2651 }
2652 mut return_none_or_error := false
2653 if expr.or_expr.stmts.len > 0 {
2654 last_stmt := expr.or_expr.stmts.last()
2655 if last_stmt is ast.ExprStmt {
2656 if c.inside_return && last_stmt.typ in [ast.none_type, ast.error_type] {
2657 return_none_or_error = true
2658 }
2659 }
2660 }
2661 if return_none_or_error {
2662 c.check_expr_option_or_result_call(expr.or_expr, c.table.cur_fn.return_type)
2663 } else {
2664 last_cur_or_expr := c.cur_or_expr
2665 c.cur_or_expr = &expr.or_expr
2666 c.check_or_expr(expr.or_expr, ret_type, ret_type.set_flag(.result), expr)
2667 c.cur_or_expr = last_cur_or_expr
2668 }
2669 // When the map value type is itself optional and the or block
2670 // returns none, preserve the option type so the result is ?T.
2671 if ret_type.has_flag(.option) && expr.or_expr.stmts.len > 0 {
2672 last := expr.or_expr.stmts.last()
2673 if last is ast.ExprStmt && last.expr is ast.None {
2674 return ret_type
2675 }
2676 }
2677 return ret_type.clear_option_and_result()
2678 } else if expr.left is ast.SelectorExpr && expr.left_type.has_option_or_result() {
2679 with_modifier_kind := if expr.left_type.has_flag(.option) {
2680 'an Option'
2681 } else {
2682 'a Result'
2683 }
2684 with_modifier := if expr.left_type.has_flag(.option) { '?' } else { '!' }
2685 c.error('field `${expr.left.field_name}` is ${with_modifier_kind}, so it should have either an `or {}` block, or `${with_modifier}` at the end',
2686 expr.left.pos)
2687 }
2688 }
2689 ast.CastExpr {
2690 c.check_expr_option_or_result_call(expr.expr, ret_type)
2691 }
2692 ast.AsCast {
2693 c.check_expr_option_or_result_call(expr.expr, ret_type)
2694 }
2695 ast.ParExpr {
2696 c.check_expr_option_or_result_call(expr.expr, ret_type)
2697 }
2698 ast.InfixExpr {
2699 left_ret_type := if expr.left_type != 0 { expr.left_type } else { ret_type }
2700 right_ret_type := if expr.right_type != 0 { expr.right_type } else { ret_type }
2701 c.check_expr_option_or_result_call(expr.left, left_ret_type)
2702 c.check_expr_option_or_result_call(expr.right, right_ret_type)
2703 }
2704 else {}
2705 }
2706
2707 return ret_type
2708}
2709
2710fn (mut c Checker) expr_unhandled_option_type(expr ast.Expr) ast.Type {
2711 match expr {
2712 ast.Ident {
2713 if expr.or_expr.kind == .absent && expr.info is ast.IdentVar {
2714 return c.type_resolver.get_type_or_default(expr, expr.info.typ)
2715 }
2716 }
2717 ast.CallExpr {
2718 if expr.or_block.kind == .absent {
2719 return expr.return_type
2720 }
2721 }
2722 ast.SelectorExpr {
2723 if expr.or_block.kind == .absent {
2724 return expr.typ
2725 }
2726 }
2727 ast.IndexExpr {
2728 if expr.or_expr.kind == .absent {
2729 return expr.typ
2730 }
2731 }
2732 ast.ParExpr {
2733 return c.expr_unhandled_option_type(expr.expr)
2734 }
2735 else {}
2736 }
2737
2738 return ast.void_type
2739}
2740
2741fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_return_type ast.Type, expr ast.Expr) {
2742 if node.kind == .propagate_option {
2743 if c.table.cur_fn != unsafe { nil } && !c.table.cur_fn.return_type.has_flag(.option)
2744 && !c.table.cur_fn.is_main && !c.table.cur_fn.is_test && !c.inside_const {
2745 c.add_instruction_for_option_type()
2746 if expr is ast.Ident {
2747 c.error('to propagate the Option, `${c.table.cur_fn.name}` must return an Option type',
2748 expr.pos)
2749 } else {
2750 c.error('to propagate the call, `${c.table.cur_fn.name}` must return an Option type',
2751 node.pos)
2752 }
2753 }
2754 if expr !in [ast.Ident, ast.SelectorExpr] && !expr_return_type.has_flag(.option) {
2755 if expr_return_type.has_flag(.result) {
2756 c.error('propagating a Result like an Option is deprecated, use `foo()!` instead of `foo()?`',
2757 node.pos)
2758 } else {
2759 c.error('to propagate an Option, the call must also return an Option type',
2760 node.pos)
2761 }
2762 }
2763 return
2764 }
2765 if node.kind == .propagate_result {
2766 if c.table.cur_fn != unsafe { nil } && !c.table.cur_fn.return_type.has_flag(.result)
2767 && !c.table.cur_fn.is_main && !c.table.cur_fn.is_test && !c.inside_const {
2768 c.add_instruction_for_result_type()
2769 c.error('to propagate the call, `${c.table.cur_fn.name}` must return a Result type',
2770 node.pos)
2771 }
2772 if !expr_return_type.has_flag(.result) {
2773 c.error('to propagate a Result, the call must also return a Result type', node.pos)
2774 }
2775 return
2776 }
2777 if node.stmts.len == 0 {
2778 if expr is ast.CallExpr && expr.is_return_used {
2779 // x := f() or {}, || f() or {} etc
2780 c.error('expression requires a non empty `or {}` block', node.pos)
2781 } else if expr !is ast.CallExpr && ret_type != ast.void_type {
2782 // _ := sql db {... } or { }
2783 c.error('expression requires a non empty `or {}` block', node.pos)
2784 }
2785 // allow `f() or {}`
2786 return
2787 } else if !node.err_used {
2788 if err_var := node.scope.find_var('err') {
2789 c.cur_or_expr.err_used = err_var.is_used
2790 }
2791 }
2792 mut valid_stmts := node.stmts.filter(it !is ast.SemicolonStmt)
2793 mut last_stmt := if valid_stmts.len > 0 { valid_stmts.last() } else { node.stmts.last() }
2794 if !node.err_used && c.cur_or_expr != unsafe { nil } {
2795 if last_stmt is ast.ExprStmt {
2796 if last_stmt.expr is ast.Ident && last_stmt.expr.name == 'err' {
2797 c.cur_or_expr.err_used = true
2798 }
2799 }
2800 }
2801 allow_none_as_option_value := expr is ast.IndexExpr && ret_type.has_flag(.option)
2802 default_or_type := if expr is ast.IndexExpr && ret_type.has_flag(.option) {
2803 ret_type
2804 } else {
2805 expr_return_type.clear_option_and_result()
2806 }
2807 if expr !is ast.CallExpr || (expr is ast.CallExpr && expr.is_return_used) {
2808 // requires a block returning an unwrapped type of expr return type
2809 c.check_or_last_stmt(mut last_stmt, ret_type, default_or_type, allow_none_as_option_value)
2810 } else {
2811 // allow f() or { var = 123 }
2812 c.check_or_last_stmt(mut last_stmt, ast.void_type, default_or_type,
2813 allow_none_as_option_value)
2814 }
2815}
2816
2817fn (mut c Checker) check_or_last_stmt(mut stmt ast.Stmt, ret_type ast.Type, default_or_type ast.Type, allow_none_as_option_value bool) {
2818 if ret_type != ast.void_type {
2819 match mut stmt {
2820 ast.ExprStmt {
2821 c.expected_type = ret_type
2822 c.expected_or_type = default_or_type
2823 if stmt.expr is ast.None && allow_none_as_option_value
2824 && default_or_type.has_flag(.option) {
2825 // `none` is valid when the `or` block itself is expected to return an Option.
2826 return
2827 }
2828 last_stmt_typ := c.expr(mut stmt.expr)
2829 stmt.typ = last_stmt_typ
2830 if last_stmt_typ.has_flag(.option) || last_stmt_typ == ast.none_type {
2831 if stmt.expr in [ast.Ident, ast.SelectorExpr, ast.CallExpr, ast.None, ast.CastExpr]
2832 && !(last_stmt_typ == ast.none_type && allow_none_as_option_value
2833 && default_or_type.has_flag(.option)) && !(last_stmt_typ == ast.none_type
2834 && c.inside_return && ret_type.has_flag(.option)) {
2835 expected_type_name := c.table.type_to_str(default_or_type)
2836 got_type_name := c.table.type_to_str(last_stmt_typ)
2837 c.error('`or` block must provide a value of type `${expected_type_name}`, not `${got_type_name}`',
2838 stmt.expr.pos())
2839 return
2840 }
2841 }
2842
2843 c.expected_or_type = ast.void_type
2844 type_fits := c.check_types(last_stmt_typ, ret_type)
2845 && last_stmt_typ.nr_muls() == ret_type.nr_muls()
2846 is_noreturn := is_noreturn_callexpr(stmt.expr)
2847 if type_fits || is_noreturn {
2848 return
2849 }
2850 if stmt.typ == ast.void_type {
2851 if mut stmt.expr is ast.IfExpr {
2852 for mut branch in stmt.expr.branches {
2853 if branch.stmts.len > 0 {
2854 mut stmt_ := branch.stmts.last()
2855 c.check_or_last_stmt(mut stmt_, ret_type, default_or_type,
2856 allow_none_as_option_value)
2857 }
2858 }
2859 return
2860 } else if mut stmt.expr is ast.MatchExpr {
2861 for mut branch in stmt.expr.branches {
2862 if branch.stmts.len > 0 {
2863 mut stmt_ := branch.stmts.last()
2864 c.check_or_last_stmt(mut stmt_, ret_type, default_or_type,
2865 allow_none_as_option_value)
2866 }
2867 }
2868 return
2869 }
2870 expected_type_name := c.table.type_to_str(default_or_type)
2871 c.error('`or` block must provide a default value of type `${expected_type_name}`, or return/continue/break or call a @[noreturn] function like panic(err) or exit(1)',
2872 stmt.expr.pos())
2873 } else {
2874 if ret_type.is_ptr() && last_stmt_typ.is_pointer()
2875 && c.table.sym(last_stmt_typ).kind == .voidptr {
2876 return
2877 }
2878 if last_stmt_typ == ast.none_type_idx && allow_none_as_option_value
2879 && default_or_type.has_flag(.option) {
2880 return
2881 }
2882 // allow returning a custom error type in `or {}` block of a result function
2883 if ret_type.has_flag(.result) {
2884 last_sym := c.table.sym(last_stmt_typ)
2885 if last_sym.kind == .struct {
2886 if last_sym.info is ast.Struct {
2887 if last_sym.info.embeds.any(c.table.type_to_str(it) == 'Error') {
2888 return
2889 }
2890 }
2891 }
2892 }
2893 type_name := c.table.type_to_str(last_stmt_typ)
2894 expected_type_name := c.table.type_to_str(default_or_type)
2895 if default_or_type.has_flag(.generic) {
2896 return
2897 }
2898 c.error('wrong return type `${type_name}` in the `or {}` block, expected `${expected_type_name}`',
2899 stmt.expr.pos())
2900 }
2901 }
2902 ast.BranchStmt {
2903 if stmt.kind !in [.key_continue, .key_break] {
2904 c.error('only break/continue is allowed as a branch statement in the end of an `or {}` block',
2905 stmt.pos)
2906 return
2907 }
2908 }
2909 ast.Return {}
2910 else {
2911 if stmt !is ast.AssertStmt || c.inside_or_block_value {
2912 expected_type_name := c.table.type_to_str(default_or_type)
2913 c.error('last statement in the `or {}` block should be an expression of type `${expected_type_name}` or exit parent scope',
2914 stmt.pos)
2915 }
2916 }
2917 }
2918 } else if mut stmt is ast.ExprStmt {
2919 match mut stmt.expr {
2920 ast.IfExpr {
2921 for mut branch in stmt.expr.branches {
2922 if branch.stmts.len > 0 {
2923 mut stmt_ := branch.stmts.last()
2924 c.check_or_last_stmt(mut stmt_, ret_type, default_or_type,
2925 allow_none_as_option_value)
2926 }
2927 }
2928 }
2929 ast.MatchExpr {
2930 for mut branch in stmt.expr.branches {
2931 if branch.stmts.len > 0 {
2932 mut stmt_ := branch.stmts.last()
2933 c.check_or_last_stmt(mut stmt_, ret_type, default_or_type,
2934 allow_none_as_option_value)
2935 }
2936 }
2937 }
2938 else {
2939 if stmt.typ == ast.void_type || default_or_type == ast.void_type {
2940 return
2941 }
2942 if is_noreturn_callexpr(stmt.expr) {
2943 return
2944 }
2945 if c.check_types(stmt.typ, default_or_type) {
2946 if stmt.typ.is_ptr() == default_or_type.is_ptr()
2947 || (default_or_type.is_ptr() && stmt.typ.is_pointer()
2948 && c.table.sym(stmt.typ).kind == .voidptr) {
2949 return
2950 }
2951 }
2952 if default_or_type.has_flag(.generic) {
2953 return
2954 }
2955 // opt_returning_string() or { ... 123 }
2956 type_name := c.table.type_to_str(stmt.typ)
2957 expr_return_type_name := c.table.type_to_str(default_or_type)
2958 c.error('the default expression type in the `or` block should be `${expr_return_type_name}`, instead you gave a value of type `${type_name}`',
2959 stmt.expr.pos())
2960 }
2961 }
2962 }
2963}
2964
2965fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
2966 prevent_sum_type_unwrapping_once := c.prevent_sum_type_unwrapping_once
2967 c.prevent_sum_type_unwrapping_once = false
2968
2969 // T.name, typeof(expr).name
2970 mut name_type := 0
2971 mut node_expr := node.expr
2972 match mut node.expr {
2973 ast.Ident {
2974 name := node.expr.name
2975 valid_generic := util.is_generic_type_name(name) && c.table.cur_fn != unsafe { nil }
2976 && name in c.table.cur_fn.generic_names
2977 if valid_generic {
2978 name_type = c.table.find_type(name).set_flag(.generic)
2979 }
2980 }
2981 ast.TypeOf {
2982 // TODO: fix this weird case, since just `typeof(x)` is `string`, but `|typeof(x).| propertyname` should be the actual type,
2983 // so that we can get other metadata properties of the type, depending on `propertyname` (one of `name` or `idx` for now).
2984 // A better alternative would be a new `meta(x).propertyname`, that does not have a `meta(x)` case (an error),
2985 // or if it does, it should be a normal constant struct value, just filled at comptime.
2986 c.expr(mut node_expr)
2987 name_type = node.expr.typ
2988 }
2989 ast.AsCast {
2990 c.add_error_detail('for example `(${node.expr.expr} as ${c.table.type_to_str(node.expr.typ)}).${node.field_name}`')
2991 c.error('indeterminate `as` cast, use parenthesis to clarity', node.expr.pos)
2992 }
2993 else {}
2994 }
2995
2996 if name_type > 0 {
2997 node.name_type = name_type
2998 match node.gkind_field {
2999 .name {
3000 return ast.string_type
3001 }
3002 .unaliased_typ, .typ, .indirections {
3003 return ast.int_type
3004 }
3005 else {
3006 if node.field_name == 'name' {
3007 return ast.string_type
3008 } else if node.field_name in ['idx', 'typ', 'unaliased_typ', 'key_type', 'value_type',
3009 'element_type', 'pointee_type', 'payload_type'] {
3010 return ast.int_type
3011 } else if node.field_name == 'variant_types' {
3012 return ast.new_type(c.table.find_or_register_array(ast.int_type))
3013 } else if node.field_name == 'indirections' {
3014 return ast.int_type
3015 }
3016 c.error('invalid field `.${node.field_name}` for type `${node.expr}`', node.pos)
3017 return ast.string_type
3018 }
3019 }
3020 }
3021 if is_array_init_type_expr_field(node.field_name) && c.is_comptime_type_expr(node.expr) {
3022 mut type_expr := node.expr
3023 base_type := c.comptime_call_type_expr_type(mut type_expr)
3024 node.expr = type_expr
3025 resolved := c.type_resolver.typeof_field_type(base_type, node.field_name)
3026 if resolved != ast.no_type {
3027 node.name_type = base_type
3028 if node.field_name == 'variant_types' {
3029 return ast.new_type(c.table.find_or_register_array(ast.int_type))
3030 }
3031 return ast.int_type
3032 }
3033 }
3034 // evaluates comptime field.<name> (from T.fields)
3035 if c.comptime.check_comptime_is_field_selector(node) {
3036 if c.comptime.check_comptime_is_field_selector_bool(node) {
3037 node.expr_type = ast.bool_type
3038 return node.expr_type
3039 }
3040 }
3041 node.is_field_typ = node.is_field_typ || c.comptime.is_comptime_selector_type(node)
3042 old_selector_expr := c.inside_selector_expr
3043 c.inside_selector_expr = true
3044 mut typ := c.expr(mut node.expr)
3045 expr_is_auto_deref_var := node.expr.is_auto_deref_var()
3046 receiver_uses_wrapped_smartcast := typ.has_option_or_result()
3047 || c.table.sym(c.unwrap_generic(typ)).kind in [.interface, .sum_type, .any]
3048 if mut node.expr is ast.Ident {
3049 if var := node.scope.find_var(node.expr.name) {
3050 if var.smartcasts.len > 0 && (expr_is_auto_deref_var || receiver_uses_wrapped_smartcast) {
3051 typ = c.exposed_smartcast_type(var.orig_type, var.smartcasts.last(), var.is_mut)
3052 } else if expr_is_auto_deref_var {
3053 typ = var.typ
3054 }
3055 }
3056 }
3057 c.inside_selector_expr = old_selector_expr
3058 if typ == ast.void_type_idx {
3059 // This means that the field has an undefined type.
3060 // This error was handled before.
3061 c.error('`${node.expr}` does not return a value', node.pos)
3062 node.expr_type = ast.void_type
3063 return ast.void_type
3064 } else if c.comptime.inside_comptime_for && typ == c.enum_data_type
3065 && node.field_name == 'value' {
3066 // for comp-time enum.values
3067 node.expr_type = c.type_resolver.get_ct_type_or_default('${c.comptime.comptime_for_enum_var}.typ',
3068 node.expr_type)
3069 node.typ = typ
3070 return node.expr_type
3071 }
3072 node.expr_type = typ
3073 typ = c.recheck_concrete_type(typ)
3074 node.expr_type = typ
3075 const_name := c.module_qualified_selector_const_name(node)
3076 if const_name != '' {
3077 c.mark_const_decl_as_referenced(const_name)
3078 if mut const_obj := c.table.global_scope.find_const(node.field_name) {
3079 c.mark_const_decl_as_referenced(const_obj.name)
3080 }
3081 } else {
3082 expr := node.expr
3083 if expr is ast.Ident {
3084 if mut const_obj := c.file.global_scope.find_const('${expr.name}.${node.field_name}') {
3085 c.mark_const_decl_as_referenced(const_obj.name)
3086 }
3087 }
3088 }
3089 if !(node.expr is ast.Ident && node.expr.kind == .constant) {
3090 if node.expr_type.has_flag(.option) {
3091 c.error('cannot access fields of an Option, handle the error with `or {...}` or propagate it with `?`',
3092 node.pos)
3093 } else if node.expr_type.has_flag(.result) {
3094 c.error('cannot access fields of a Result, handle the error with `or {...}` or propagate it with `!`',
3095 node.pos)
3096 }
3097 }
3098 field_name := node.field_name
3099 mut sym := c.table.sym(typ)
3100 mut final_sym := c.table.final_sym(typ)
3101 if (typ.has_flag(.variadic) || final_sym.kind == .array_fixed) && field_name == 'len' {
3102 node.typ = ast.int_type
3103 return ast.int_type
3104 }
3105 if sym.kind == .chan {
3106 if field_name == 'closed' {
3107 node.typ = ast.bool_type
3108 return ast.bool_type
3109 } else if field_name in ['len', 'cap'] {
3110 node.typ = ast.u32_type
3111 return ast.u32_type
3112 }
3113 }
3114 mut unknown_field_msg := ''
3115 mut has_field := false
3116 mut field := ast.StructField{}
3117 if field_name.len > 0 && final_sym.kind == .struct && sym.language == .v
3118 && field_name[0].is_capital() {
3119 // x.Foo.y => access the embedded struct
3120 struct_info := final_sym.info as ast.Struct
3121 for embed in struct_info.embeds {
3122 embed_sym := c.table.sym(embed)
3123 if embed_sym.embed_name() == field_name {
3124 node.typ = embed
3125 return embed
3126 }
3127 }
3128 } else {
3129 if f := c.table.find_field(sym, field_name) {
3130 has_field = true
3131 field = f
3132 } else {
3133 field_err := err
3134 // look for embedded field
3135 if field_, embed_types := c.table.find_field_from_embeds(sym, field_name) {
3136 has_field = true
3137 field = field_
3138 node.from_embed_types = embed_types
3139 } else {
3140 first_err := err
3141 has_field = false
3142 if final_sym.kind in [.aggregate, .sum_type] {
3143 if variant_typ, variant_field, variant_embed_types := c.table.find_single_field_variant(final_sym,
3144 field_name)
3145 {
3146 old_expr := node.expr
3147 node.expr = ast.ParExpr{
3148 expr: ast.AsCast{
3149 expr: old_expr
3150 typ: variant_typ
3151 expr_type: typ
3152 pos: old_expr.pos()
3153 }
3154 }
3155 typ = variant_typ
3156 sym = c.table.sym(typ)
3157 final_sym = c.table.final_sym(typ)
3158 node.expr_type = typ
3159 field = variant_field
3160 node.from_embed_types = variant_embed_types
3161 has_field = true
3162 }
3163 }
3164 if !has_field && final_sym.kind in [.aggregate, .sum_type] {
3165 unknown_field_msg = if field_err.msg() != '' {
3166 field_err.msg()
3167 } else {
3168 first_err.msg()
3169 }
3170 // TODO need a better way to check that we need to display sum type variants info
3171 if final_sym.kind == .sum_type
3172 && unknown_field_msg.contains('does not exist or have the same type in all sumtype')
3173 && !unknown_field_msg.contains('variants: ') {
3174 info := final_sym.info as ast.SumType
3175 missing_variants := c.table.find_missing_variants(info, field_name)
3176 unknown_field_msg += missing_variants
3177 }
3178 }
3179 if !has_field && c.table.cur_fn != unsafe { nil }
3180 && c.table.cur_fn.generic_names.len > 0 && c.table.cur_concrete_types.len == 0
3181 && c.table.sym(c.unwrap_generic(typ)).kind in [.interface, .any] {
3182 node.typ = ast.any_type
3183 return node.typ
3184 }
3185 if !has_field && first_err.msg() != '' {
3186 c.error(first_err.msg(), node.pos)
3187 }
3188 }
3189 }
3190 if !c.inside_unsafe && sym.kind == .struct {
3191 struct_info := sym.info as ast.Struct
3192 if struct_info.is_union && node.next_token !in token.assign_tokens {
3193 if !c.pref.translated && !c.file.is_translated {
3194 c.warn('reading a union field (or its address) requires `unsafe`', node.pos)
3195 }
3196 }
3197 }
3198 if typ.has_flag(.generic) && !has_field {
3199 gs := c.table.sym(c.unwrap_generic(typ))
3200 if f := c.table.find_field(gs, field_name) {
3201 has_field = true
3202 field = f
3203 } else {
3204 // look for embedded field
3205 has_field = true
3206 mut embed_types := []ast.Type{}
3207 field, embed_types = c.table.find_field_from_embeds(gs, field_name) or {
3208 if err.msg() != '' {
3209 c.error(err.msg(), node.pos)
3210 }
3211 has_field = false
3212 ast.StructField{}, []ast.Type{}
3213 }
3214 node.from_embed_types = embed_types
3215 node.generic_from_embed_types << embed_types
3216 }
3217 }
3218 }
3219
3220 if has_field {
3221 is_used_outside := !c.inside_recheck && sym.mod != c.mod
3222 if is_used_outside && sym.language == .c && !sym.is_pub {
3223 c.ensure_type_exists(typ, node.pos)
3224 return field.typ
3225 }
3226 unwrapped_sym := c.table.sym(c.unwrap_generic(typ))
3227 if is_used_outside && !field.is_pub && sym.language != .c {
3228 c.error('field `${unwrapped_sym.name}.${field_name}` is not public', node.pos)
3229 }
3230 field_type := c.resolve_selector_field_type(typ, field_name, field)
3231 field_sym := c.table.sym(field_type)
3232 if field.is_deprecated && is_used_outside {
3233 c.deprecate('field', field_name, field.attrs, node.pos)
3234 }
3235 if field_sym.kind in [.sum_type, .interface] || field_type.has_flag(.option) {
3236 if !prevent_sum_type_unwrapping_once {
3237 scope_field := node.scope.find_struct_field(node.expr.str(), typ, field_name)
3238 if scope_field != unsafe { nil } {
3239 sf_smartcast_type := c.exposed_smartcast_type(scope_field.orig_type,
3240 scope_field.smartcasts.last(), scope_field.is_mut)
3241 if c.inside_sql && node.or_block.kind == .absent {
3242 node.typ = sf_smartcast_type
3243 }
3244 if node.or_block.kind == .absent {
3245 return sf_smartcast_type
3246 }
3247 }
3248 }
3249 }
3250 node.typ = field_type
3251 if node.or_block.kind != .absent {
3252 unwrapped_typ := c.unwrap_generic(node.typ)
3253 c.expected_or_type = unwrapped_typ.clear_option_and_result()
3254 c.stmts_ending_with_expression(mut node.or_block.stmts, c.expected_or_type)
3255 last_cur_or_expr := c.cur_or_expr
3256 c.cur_or_expr = &node.or_block
3257 c.check_or_expr(node.or_block, unwrapped_typ, c.expected_or_type, node)
3258 c.cur_or_expr = last_cur_or_expr
3259 c.expected_or_type = ast.void_type
3260 }
3261 return field_type
3262 }
3263 if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0
3264 && c.table.cur_concrete_types.len == 0
3265 && c.table.sym(c.unwrap_generic(typ)).kind in [.interface, .any] {
3266 node.typ = ast.any_type
3267 return node.typ
3268 }
3269 if mut method := c.table.sym(c.unwrap_generic(typ)).find_method_with_generic_parent(field_name) {
3270 c.mark_fn_decl_as_referenced(method.fkey())
3271 c.markused_comptime_call(typ.has_flag(.generic),
3272 '${int(method.params[0].typ)}.${field_name}')
3273 if c.expected_type != 0 && c.expected_type != ast.none_type {
3274 mut method_copy := method
3275 method_copy.name = ''
3276 fn_type := ast.new_type(c.table.find_or_register_fn_type(method_copy, false, true))
3277 if c.check_types(fn_type, c.expected_type) {
3278 c.table.used_features.anon_fn = true
3279 return fn_type
3280 }
3281 }
3282 receiver := c.unwrap_generic(method.params[0].typ)
3283 if receiver.nr_muls() > 0 {
3284 if !c.inside_unsafe {
3285 rec_sym := c.table.sym(receiver.set_nr_muls(0))
3286 if !rec_sym.is_heap() {
3287 suggestion := if rec_sym.kind == .struct {
3288 'declaring `${rec_sym.name}` as `@[heap]`'
3289 } else {
3290 'wrapping the `${rec_sym.name}` object in a `struct` declared as `@[heap]`'
3291 }
3292 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}.',
3293 node.expr.pos().extend(node.pos))
3294 }
3295 }
3296 }
3297 method.params = method.params[1..]
3298 node.has_hidden_receiver = true
3299 method.name = ''
3300 fn_type := ast.new_type(c.table.find_or_register_fn_type(method, false, true))
3301 node.typ = c.unwrap_generic(fn_type)
3302 if c.type_has_unresolved_generic_parts(fn_type) {
3303 c.error('cannot use `${node.expr}.${node.field_name}` as a generic function value',
3304 node.pos)
3305 c.table.used_features.anon_fn = true
3306 return fn_type
3307 }
3308 c.table.used_features.anon_fn = true
3309 return fn_type
3310 }
3311 if sym.kind !in [.struct, .aggregate, .interface, .sum_type] {
3312 if sym.kind != .placeholder {
3313 unwrapped_sym := c.table.sym(c.unwrap_generic(typ))
3314
3315 if unwrapped_sym.kind == .array_fixed && node.field_name == 'len' {
3316 node.typ = ast.int_type
3317 return ast.int_type
3318 }
3319
3320 c.error('`${unwrapped_sym.name}` has no property `${node.field_name}`', node.pos)
3321 }
3322 } else {
3323 if unknown_field_msg == '' {
3324 if field_name == '' && c.pref.is_vls {
3325 // VLS will often have `foo.`, skip the no field error
3326 return ast.void_type
3327 }
3328 unknown_field_msg = 'type `${sym.name}` has no field named `${field_name}`'
3329 }
3330 if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0
3331 && c.table.cur_concrete_types.len == 0 && sym.kind in [.interface, .any] {
3332 node.typ = ast.any_type
3333 return node.typ
3334 }
3335 if final_sym.kind == .struct {
3336 if c.smartcast_mut_pos != token.Pos{} && !c.implicit_mutability_enabled() {
3337 c.note('smartcasting requires either an immutable value, or an explicit mut keyword before the value',
3338 c.smartcast_mut_pos)
3339 }
3340 struct_info := final_sym.info as ast.Struct
3341 suggestion := util.new_suggestion(field_name, struct_info.fields.map(it.name))
3342 c.error(suggestion.say(unknown_field_msg), node.pos)
3343 return ast.void_type
3344 }
3345 if c.smartcast_mut_pos != token.Pos{} && !c.implicit_mutability_enabled() {
3346 c.note('smartcasting requires either an immutable value, or an explicit mut keyword before the value',
3347 c.smartcast_mut_pos)
3348 }
3349 if c.smartcast_cond_pos != token.Pos{} {
3350 c.note('smartcast can only be used on ident, selector or index expressions, e.g. match foo, match foo.bar, match foo[0]',
3351 c.smartcast_cond_pos)
3352 }
3353 c.error(unknown_field_msg, node.pos)
3354 }
3355 return ast.void_type
3356}
3357
3358fn (mut c Checker) const_decl(mut node ast.ConstDecl) {
3359 if node.fields.len == 0 {
3360 c.warn('const block must have at least 1 declaration', node.pos)
3361 }
3362 if node.is_block {
3363 c.warn('const () groups will be an error after 2025-01-01 (`v fmt -w source.v` will fix that for you)',
3364 node.pos)
3365 }
3366
3367 mut is_export := false
3368 mut export_name := ''
3369 if export_attr := node.attrs.find_first('export') {
3370 is_export = true
3371 export_name = export_attr.arg
3372 }
3373 for mut field in node.fields {
3374 if reserved_type_names_chk.matches(util.no_cur_mod(field.name, c.mod)) {
3375 c.error('invalid use of reserved type `${field.name}` as a const name', field.pos)
3376 }
3377 // TODO: Check const name once the syntax is decided
3378 if field.name in c.const_names {
3379 name_pos := token.Pos{
3380 ...field.pos
3381 len: util.no_cur_mod(field.name, c.mod).len
3382 }
3383 c.error('duplicate const `${field.name}`', name_pos)
3384 }
3385 if is_export {
3386 check_name := if export_name.len == 0 { field.name } else { export_name }
3387 if !check_name.is_identifier() {
3388 c.error('export name `${check_name}` should be a valid identifier', node.pos)
3389 }
3390 if check_name in c.table.export_names.values() {
3391 name_pos := token.Pos{
3392 ...field.pos
3393 len: util.no_cur_mod(field.name, c.mod).len
3394 }
3395 c.error('duplicate export name `${check_name}`', name_pos)
3396 } else {
3397 c.table.export_names[field.name] = check_name
3398 }
3399 }
3400 is_call_expr := field.expr is ast.CallExpr
3401 if is_call_expr {
3402 mut field_expr := field.expr
3403 sym := c.table.sym(c.check_expr_option_or_result_call(field_expr,
3404 c.expr(mut field_expr)))
3405 if sym.kind == .multi_return {
3406 c.error('const declarations do not support multiple return values yet',
3407 field_expr.pos())
3408 }
3409 field.expr = field_expr
3410 }
3411 const_name := field.name.all_after_last('.')
3412 if const_name == c.mod && const_name != 'main' {
3413 name_pos := token.Pos{
3414 ...field.pos
3415 len: util.no_cur_mod(field.name, c.mod).len
3416 }
3417 c.error('duplicate of a module name `${field.name}`', name_pos)
3418 }
3419 for imp in c.file.imports {
3420 imp_alias := if imp.alias.len > 0 { imp.alias } else { imp.mod.all_after_last('.') }
3421 if const_name == imp_alias {
3422 name_pos := token.Pos{
3423 ...field.pos
3424 len: util.no_cur_mod(field.name, c.mod).len
3425 }
3426 c.error('const `${const_name}` conflicts with imported module `${imp.mod}`',
3427 name_pos)
3428 }
3429 }
3430 if const_name == '_' {
3431 name_pos := token.Pos{
3432 ...field.pos
3433 len: util.no_cur_mod(field.name, c.mod).len
3434 }
3435 c.error('cannot use `_` as a const name', name_pos)
3436 return
3437 }
3438 c.const_names << field.name
3439 }
3440 for i, mut field in node.fields {
3441 c.const_deps << field.name
3442 prev_const_var := c.const_var
3443 c.const_var = unsafe { field }
3444 mut typ := c.check_expr_option_or_result_call(field.expr, c.expr(mut field.expr))
3445 typ = c.cast_fixed_array_ret(typ, c.table.sym(typ))
3446 if ct_value := c.eval_comptime_const_expr(field.expr, 0) {
3447 field.comptime_expr_value = ct_value
3448 if ct_value is u64 {
3449 typ = ast.u64_type
3450 }
3451 }
3452 node.fields[i].typ = ast.mktyp(typ)
3453 if mut field.expr is ast.IfExpr {
3454 for branch in field.expr.branches {
3455 if branch.stmts.len > 0 && branch.stmts.last() is ast.ExprStmt
3456 && branch.stmts.last().typ != ast.void_type {
3457 field.expr.is_expr = true
3458 field.expr.typ = (branch.stmts.last() as ast.ExprStmt).typ
3459 field.typ = field.expr.typ
3460 // update ConstField object's type in table
3461 if mut obj := c.file.global_scope.find_const(field.name) {
3462 obj.typ = field.typ
3463 }
3464 break
3465 }
3466 }
3467 }
3468 // Check for int overflow
3469 if field.typ == ast.int_type {
3470 if mut field.expr is ast.IntegerLiteral {
3471 val := field.expr.val.i64()
3472 if overflows_i32(val) {
3473 c.error('overflow in implicit type `int`, use explicit type casting instead',
3474 field.expr.pos)
3475 }
3476 }
3477 }
3478 c.const_deps = []
3479 c.const_var = prev_const_var
3480 }
3481}
3482
3483fn (mut c Checker) enum_decl(mut node ast.EnumDecl) {
3484 c.check_valid_pascal_case(node.name, 'enum name', node.pos)
3485 if node.typ == ast.invalid_type {
3486 c.error('cannot register enum `${node.name}`, another type with this name exists', node.pos)
3487 return
3488 }
3489 mut useen := []u64{}
3490 mut iseen := []i64{}
3491 mut seen_enum_field_names := map[string]int{}
3492 if node.fields.len == 0 {
3493 c.error('enum cannot be empty', node.pos)
3494 }
3495 /*
3496 if node.is_pub && c.mod == 'builtin' {
3497 c.error('`builtin` module cannot have enums', node.pos)
3498 }
3499 */
3500 mut enum_imin := i64(0)
3501 mut enum_imax := i64(0)
3502 mut enum_umin := u64(0)
3503 mut enum_umax := u64(0)
3504 mut signed := true
3505 senum_type := c.table.type_to_str(node.typ)
3506 match node.typ {
3507 ast.i8_type {
3508 signed, enum_imin, enum_imax = true, min_i8, max_i8
3509 }
3510 ast.i16_type {
3511 signed, enum_imin, enum_imax = true, min_i16, max_i16
3512 }
3513 ast.i32_type {
3514 signed, enum_imin, enum_imax = true, min_i32, max_i32
3515 }
3516 ast.int_type {
3517 $if new_int ? && x64 {
3518 signed, enum_imin, enum_imax = true, min_i32, max_i32
3519 } $else {
3520 signed, enum_imin, enum_imax = true, min_i64, max_i64
3521 }
3522 }
3523 ast.i64_type {
3524 signed, enum_imin, enum_imax = true, min_i64, max_i64
3525 }
3526 //
3527 ast.u8_type {
3528 signed, enum_umin, enum_umax = false, min_u8, max_u8
3529 }
3530 ast.u16_type {
3531 signed, enum_umin, enum_umax = false, min_u16, max_u16
3532 }
3533 ast.u32_type {
3534 signed, enum_umin, enum_umax = false, min_u32, max_u32
3535 }
3536 ast.u64_type {
3537 signed, enum_umin, enum_umax = false, min_u64, max_u64
3538 }
3539 else {
3540 if senum_type == 'i32' {
3541 signed, enum_imin, enum_imax = true, min_i32, max_i32
3542 } else {
3543 c.error('`${senum_type}` is not one of `i8`,`i16`,`i32`,`int`,`i64`,`u8`,`u16`,`u32`,`u64`',
3544 node.typ_pos)
3545 }
3546 }
3547 }
3548
3549 if enum_imin > 0 {
3550 // ensure that the minimum value is negative, even with msvc, which has a bug that makes -2147483648 positive ...
3551 enum_imin *= -1
3552 }
3553 for i, mut field in node.fields {
3554 if !c.pref.experimental && util.contains_capital(field.name) {
3555 // TODO: C2V uses hundreds of enums with capitals, remove -experimental check once it's handled
3556 c.error('field name `${field.name}` cannot contain uppercase letters, use snake_case instead',
3557 field.pos)
3558 }
3559 if _ := seen_enum_field_names[field.name] {
3560 c.error('duplicate enum field name `${field.name}`', field.pos)
3561 }
3562 seen_enum_field_names[field.name] = i
3563 if field.has_expr {
3564 mut replacement_expr := ast.empty_expr
3565 mut has_replacement := false
3566 match mut field.expr {
3567 ast.IntegerLiteral {
3568 c.check_enum_field_integer_literal(field.expr, signed, node.is_multi_allowed,
3569 senum_type, field.expr.pos, mut useen, enum_umin, enum_umax, mut iseen,
3570 enum_imin, enum_imax)
3571 }
3572 ast.InfixExpr {
3573 // Handle `enum Foo { x = 1 + 2 }`
3574 c.infix_expr(mut field.expr)
3575 folded_expr := c.checker_transformer.infix_expr(mut field.expr)
3576
3577 if folded_expr is ast.IntegerLiteral {
3578 c.check_enum_field_integer_literal(folded_expr, signed,
3579 node.is_multi_allowed, senum_type, field.expr.pos, mut useen,
3580 enum_umin, enum_umax, mut iseen, enum_imin, enum_imax)
3581 }
3582 }
3583 ast.ParExpr {
3584 c.expr(mut field.expr.expr)
3585 folded_expr := c.checker_transformer.expr(mut field.expr.expr)
3586
3587 if folded_expr is ast.IntegerLiteral {
3588 c.check_enum_field_integer_literal(folded_expr, signed,
3589 node.is_multi_allowed, senum_type, field.expr.pos, mut useen,
3590 enum_umin, enum_umax, mut iseen, enum_imin, enum_imax)
3591 }
3592 }
3593 ast.EnumVal {
3594 // Handle `.a` shorthand syntax to reference another field from same enum
3595 ref_name := if field.expr.enum_name == '' {
3596 node.name
3597 } else {
3598 field.expr.enum_name
3599 }
3600 if ref_name == node.name {
3601 if field.expr.val !in seen_enum_field_names {
3602 c.error('`${node.name}.${field.expr.val}` should be declared before using it',
3603 field.expr.pos)
3604 } else {
3605 // Get the index of the referenced field
3606 ref_idx := seen_enum_field_names[field.expr.val]
3607 // Use the value from the previously seen values and transform to IntegerLiteral
3608 if signed {
3609 mut was_seen := true
3610 ref_val := iseen[ref_idx] or {
3611 was_seen = false
3612 -1
3613 }
3614 if !c.pref.translated && !c.file.is_translated
3615 && !node.is_multi_allowed && ref_val in iseen {
3616 c.add_error_detail('use `@[_allow_multiple_values]` attribute to allow multiple enum values. Use only when needed')
3617 if was_seen {
3618 c.error('enum value `${ref_val}` already exists',
3619 field.expr.pos)
3620 } else {
3621 c.error('enum value `${field.expr.val}` is not allowed to reference itself',
3622 field.expr.pos)
3623 }
3624 }
3625 iseen << ref_val
3626 // Transform to IntegerLiteral for code generation
3627 has_replacement = true
3628 replacement_expr = ast.IntegerLiteral{
3629 val: ref_val.str()
3630 pos: field.expr.pos
3631 }
3632 } else {
3633 mut was_seen := true
3634 ref_val := useen[ref_idx] or {
3635 was_seen = false
3636 -1
3637 }
3638 if !c.pref.translated && !c.file.is_translated
3639 && !node.is_multi_allowed && ref_val in useen {
3640 c.add_error_detail('use `@[_allow_multiple_values]` attribute to allow multiple enum values. Use only when needed')
3641 if was_seen {
3642 c.error('enum value `${ref_val}` already exists',
3643 field.expr.pos)
3644 } else {
3645 c.error('enum value `${field.expr.val}` is not allowed to reference itself',
3646 field.expr.pos)
3647 }
3648 }
3649 useen << ref_val
3650 // Transform to IntegerLiteral for code generation
3651 has_replacement = true
3652 replacement_expr = ast.IntegerLiteral{
3653 val: ref_val.str()
3654 pos: field.expr.pos
3655 }
3656 }
3657 }
3658 } else {
3659 c.error('the default value for an enum has to be an integer',
3660 field.expr.pos)
3661 }
3662 }
3663 ast.CastExpr {
3664 fe_type := c.cast_expr(mut field.expr)
3665 if node.typ != fe_type {
3666 sfe_type := c.table.type_to_str(fe_type)
3667 c.error('the type of the enum value `${sfe_type}` != the enum type itself `${senum_type}`',
3668 field.expr.pos)
3669 }
3670 if !fe_type.is_pure_int() {
3671 c.error('the type of an enum value must be an integer type, like i8, u8, int, u64 etc.',
3672 field.expr.pos)
3673 }
3674 if mut field.expr.expr is ast.EnumVal {
3675 if field.expr.expr.enum_name == node.name {
3676 if field.expr.expr.val !in seen_enum_field_names {
3677 c.error('`${field.expr.expr.enum_name}.${field.expr.expr.val}` should be declared before using it',
3678 field.expr.pos)
3679 continue
3680 }
3681 }
3682 }
3683 cast_expr := ast.Expr(field.expr)
3684 if comptime_value := c.eval_comptime_const_expr(cast_expr, 0) {
3685 comptime_lit := c.comptime_value_to_integer_literal(comptime_value,
3686 field.expr.pos) or {
3687 c.error('the default value for an enum has to be an integer',
3688 field.expr.pos)
3689 continue
3690 }
3691 c.check_enum_field_integer_literal(comptime_lit, signed,
3692 node.is_multi_allowed, senum_type, field.expr.pos, mut useen,
3693 enum_umin, enum_umax, mut iseen, enum_imin, enum_imax)
3694 has_replacement = true
3695 replacement_expr = comptime_lit
3696 } else {
3697 c.error('the default value for an enum has to be an integer',
3698 field.expr.pos)
3699 }
3700 }
3701 ast.CallExpr, ast.IfExpr {
3702 call_ret_type := c.expr(mut field.expr)
3703 call_expr := ast.Expr(field.expr)
3704 c.check_expr_option_or_result_call(call_expr, call_ret_type)
3705 if comptime_value := c.eval_comptime_const_expr(call_expr, 0) {
3706 comptime_lit := c.comptime_value_to_integer_literal(comptime_value,
3707 field.expr.pos) or {
3708 c.error('the default value for an enum has to be an integer',
3709 field.expr.pos)
3710 continue
3711 }
3712 c.check_enum_field_integer_literal(comptime_lit, signed,
3713 node.is_multi_allowed, senum_type, field.expr.pos, mut useen,
3714 enum_umin, enum_umax, mut iseen, enum_imin, enum_imax)
3715 has_replacement = true
3716 replacement_expr = comptime_lit
3717 } else {
3718 c.error('the default value for an enum has to be an integer',
3719 field.expr.pos)
3720 }
3721 }
3722 else {
3723 mut handled_expr := false
3724 if mut field.expr is ast.Ident {
3725 if field.expr.language == .c {
3726 handled_expr = true
3727 } else {
3728 if field.expr.kind == .unresolved {
3729 c.ident(mut field.expr)
3730 }
3731 if field.expr.kind == .constant && field.expr.obj.typ.is_int() {
3732 handled_expr = true
3733 // accepts int constants as enum value
3734 if mut field.expr.obj is ast.ConstField {
3735 if comptime_value := c.eval_comptime_const_expr(field.expr, 0) {
3736 if comptime_lit := c.comptime_value_to_integer_literal(comptime_value,
3737 field.expr.pos)
3738 {
3739 c.check_enum_field_integer_literal(comptime_lit,
3740 signed, node.is_multi_allowed, senum_type,
3741 field.expr.pos, mut useen, enum_umin, enum_umax, mut
3742 iseen, enum_imin, enum_imax)
3743 has_replacement = true
3744 replacement_expr = comptime_lit
3745 }
3746 }
3747 if !has_replacement {
3748 folded_expr :=
3749 c.checker_transformer.expr(mut field.expr.obj.expr)
3750
3751 if folded_expr is ast.IntegerLiteral {
3752 c.check_enum_field_integer_literal(folded_expr, signed,
3753 node.is_multi_allowed, senum_type, field.expr.pos, mut
3754 useen, enum_umin, enum_umax, mut iseen, enum_imin,
3755 enum_imax)
3756 has_replacement = true
3757 replacement_expr = folded_expr
3758 }
3759 }
3760 }
3761 }
3762 }
3763 }
3764 if !handled_expr {
3765 mut pos := field.expr.pos()
3766 if pos.pos == 0 {
3767 pos = field.pos
3768 }
3769 c.error('the default value for an enum has to be an integer', pos)
3770 }
3771 }
3772 }
3773
3774 if has_replacement {
3775 field.expr = replacement_expr
3776 }
3777 } else {
3778 if signed {
3779 if iseen.len > 0 {
3780 ilast := iseen.last()
3781 if ilast == enum_imax {
3782 c.error('enum value overflows type `${senum_type}`, which has a maximum value of ${enum_imax}',
3783 field.pos)
3784 } else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed
3785 && ilast + 1 in iseen {
3786 c.add_error_detail('use `@[_allow_multiple_values]` attribute to allow multiple enum values. Use only when it is needed')
3787 c.error('enum value `${ilast + 1}` already exists', field.pos)
3788 }
3789 iseen << ilast + 1
3790 } else {
3791 iseen << 0
3792 }
3793 } else {
3794 if useen.len > 0 {
3795 ulast := useen.last()
3796 if ulast == enum_umax {
3797 c.error('enum value overflows type `${senum_type}`, which has a maximum value of ${enum_umax}',
3798 field.pos)
3799 } else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed
3800 && ulast + 1 in useen {
3801 c.add_error_detail('use `@[_allow_multiple_values]` attribute to allow multiple enum values. Use only when it is needed')
3802 c.error('enum value `${ulast + 1}` already exists', field.pos)
3803 }
3804 useen << ulast + 1
3805 } else {
3806 useen << 0
3807 }
3808 }
3809 }
3810 }
3811 node.enum_typ = c.table.find_type_idx(node.name)
3812}
3813
3814fn (c &Checker) comptime_value_to_integer_literal(value ast.ComptTimeConstValue, pos token.Pos) ?ast.IntegerLiteral {
3815 if v := value.i64() {
3816 return ast.IntegerLiteral{
3817 val: v.str()
3818 pos: pos
3819 }
3820 }
3821 if v := value.u64() {
3822 return ast.IntegerLiteral{
3823 val: v.str()
3824 pos: pos
3825 }
3826 }
3827 return none
3828}
3829
3830fn (mut c Checker) check_enum_field_integer_literal(expr ast.IntegerLiteral, is_signed bool, is_multi_allowed bool,
3831 styp string, pos token.Pos, mut useen []u64, umin u64, umax u64, mut iseen []i64, imin i64, imax i64) {
3832 mut overflows := false
3833 mut uval := u64(0)
3834 mut ival := i64(0)
3835
3836 if is_signed {
3837 val := expr.val.i64()
3838 ival = val
3839 if val < imin || val >= imax {
3840 c.error('enum value `${expr.val}` overflows the enum type `${styp}`, values of which have to be in [${imin}, ${imax}]',
3841 pos)
3842 overflows = true
3843 }
3844 } else {
3845 val := expr.val.u64()
3846 uval = val
3847 if val >= umax {
3848 overflows = true
3849 if val == umax {
3850 is_bin := expr.val.starts_with('0b')
3851 is_oct := expr.val.starts_with('0o')
3852 is_hex := expr.val.starts_with('0x')
3853
3854 if is_hex {
3855 overflows = val.hex() != umax.hex()
3856 } else if !is_bin && !is_oct && !is_hex {
3857 overflows = expr.val.str() != umax.str()
3858 }
3859 }
3860 if overflows {
3861 c.error('enum value `${expr.val}` overflows the enum type `${styp}`, values of which have to be in [${umin}, ${umax}]',
3862 pos)
3863 }
3864 }
3865 }
3866 if !overflows && !c.pref.translated && !c.file.is_translated && !is_multi_allowed {
3867 if (is_signed && ival in iseen) || (!is_signed && uval in useen) {
3868 c.add_error_detail('use `@[_allow_multiple_values]` attribute to allow multiple enum values. Use only when it is needed')
3869 c.error('enum value `${expr.val}` already exists', pos)
3870 }
3871 }
3872 if is_signed {
3873 iseen << ival
3874 } else {
3875 useen << uval
3876 }
3877}
3878
3879@[inline]
3880fn (mut c Checker) check_loop_labels(label string, pos token.Pos) {
3881 if label == '' {
3882 return
3883 }
3884 if label in c.loop_labels {
3885 c.error('the loop label was already defined before', pos)
3886 return
3887 }
3888 c.loop_labels << label
3889}
3890
3891fn (mut c Checker) stmt(mut node ast.Stmt) {
3892 $if trace_checker ? {
3893 ntype := typeof(*node).replace('v.ast.', '')
3894 eprintln('checking: ${c.file.path:-30} | pos: ${node.pos.line_str():-39} | node: ${ntype} | ${node}')
3895 }
3896 c.expected_type = ast.void_type
3897 match mut node {
3898 ast.ExprStmt {
3899 node.typ = c.expr(mut node.expr)
3900 c.expected_type = ast.void_type
3901 mut or_typ := ast.void_type
3902 match mut node.expr {
3903 ast.IndexExpr {
3904 if node.expr.or_expr.kind != .absent {
3905 node.is_expr = true
3906 or_typ = node.typ
3907 }
3908 }
3909 ast.PrefixExpr {
3910 if node.expr.or_block.kind != .absent {
3911 node.is_expr = true
3912 or_typ = node.typ
3913 }
3914 }
3915 else {}
3916 }
3917
3918 if !c.pref.is_repl && (c.stmt_level == 1 || (c.stmt_level > 1 && !c.is_last_stmt)) {
3919 if mut node.expr is ast.InfixExpr {
3920 if node.expr.op == .left_shift {
3921 left_sym := c.table.final_sym(node.expr.left_type)
3922 if left_sym.kind != .array
3923 && c.table.final_sym(c.unwrap_generic(node.expr.left_type)).kind != .array {
3924 c.error('unused expression', node.pos)
3925 }
3926 }
3927 if node.expr.op == .arrow {
3928 if mut node.expr.right is ast.CallExpr {
3929 if node.expr.right.return_type.has_flag(.result) {
3930 node.expr.or_block.err_used =
3931 node.expr.or_block.scope.known_var('err')
3932 node.expr.right.or_block = node.expr.or_block
3933 }
3934 }
3935 }
3936 }
3937 }
3938 if !c.inside_return {
3939 c.check_expr_option_or_result_call(node.expr, or_typ)
3940 }
3941 // TODO: This should work, even if it's prolly useless .-.
3942 // node.typ = c.check_expr_option_or_result_call(node.expr, ast.void_type)
3943 }
3944 ast.AssignStmt {
3945 c.assign_stmt(mut node)
3946 }
3947 ast.Block {
3948 c.block(mut node)
3949 }
3950 ast.BranchStmt {
3951 c.branch_stmt(node)
3952 }
3953 ast.ComptimeFor {
3954 c.comptime_for(mut node)
3955 }
3956 ast.ConstDecl {
3957 c.inside_const = true
3958 c.const_decl(mut node)
3959 c.inside_const = false
3960 }
3961 ast.DeferStmt {
3962 c.defer_stmt(mut node)
3963 }
3964 ast.EnumDecl {
3965 c.enum_decl(mut node)
3966 }
3967 ast.FnDecl {
3968 c.fn_decl(mut node)
3969 }
3970 ast.ForCStmt {
3971 c.for_c_stmt(mut node)
3972 }
3973 ast.ForInStmt {
3974 c.for_in_stmt(mut node)
3975 }
3976 ast.ForStmt {
3977 c.for_stmt(mut node)
3978 }
3979 ast.GlobalDecl {
3980 c.global_decl(mut node)
3981 }
3982 ast.GotoLabel {
3983 c.goto_label(node)
3984 }
3985 ast.GotoStmt {
3986 c.goto_stmt(node)
3987 }
3988 ast.HashStmt {
3989 c.hash_stmt(mut node)
3990 }
3991 ast.Import {
3992 c.import_stmt(node)
3993 }
3994 ast.InterfaceDecl {
3995 c.interface_decl(mut node)
3996 }
3997 ast.Module {
3998 module_name := node.name.all_after_last('.')
3999 c.check_valid_snake_case(module_name, 'module name', node.pos)
4000 }
4001 ast.Return {
4002 // c.returns = true
4003 c.return_stmt(mut node)
4004 c.scope_returns = true
4005 }
4006 ast.SemicolonStmt {}
4007 ast.SqlStmt {
4008 c.sql_stmt(mut node)
4009 }
4010 ast.StructDecl {
4011 c.struct_decl(mut node)
4012 }
4013 ast.TypeDecl {
4014 c.type_decl(mut node)
4015 }
4016 ast.EmptyStmt {
4017 if c.pref.is_verbose {
4018 eprintln('Checker.stmt() EmptyStmt')
4019 print_backtrace()
4020 }
4021 }
4022 ast.NodeError {}
4023 ast.DebuggerStmt {}
4024 ast.AsmStmt {
4025 c.asm_stmt(mut node)
4026 }
4027 ast.AssertStmt {
4028 c.assert_stmt(mut node)
4029 }
4030 }
4031}
4032
4033fn (mut c Checker) defer_stmt(mut node ast.DeferStmt) {
4034 c.inside_defer = true
4035 if c.table.cur_fn != unsafe { nil } {
4036 if node.idx_in_fn < 0 {
4037 node.idx_in_fn = c.table.cur_fn.defer_stmts.len
4038 }
4039 mut is_registered := false
4040 for defer_stmt in c.table.cur_fn.defer_stmts {
4041 if defer_stmt.idx_in_fn == node.idx_in_fn {
4042 is_registered = true
4043 break
4044 }
4045 }
4046 if !is_registered {
4047 c.table.cur_fn.defer_stmts << node
4048 }
4049 }
4050 if node.mode == .function {
4051 if !isnil(c.fn_scope) && node.scope == c.fn_scope {
4052 c.warn('`defer` is already in function scope; just use `defer {` instead', node.pos)
4053 }
4054 if c.locked_names.len != 0 || c.rlocked_names.len != 0 {
4055 c.error('`defer(fn)`s are not allowed in lock statements', node.pos)
4056 }
4057 for i, ident in node.defer_vars {
4058 mut id := ident
4059 if mut id.info is ast.IdentVar {
4060 if id.comptime
4061 && (id.tok_kind == .question || id.name in ast.valid_comptime_not_user_defined) {
4062 node.defer_vars[i] = ast.Ident{
4063 scope: unsafe { nil }
4064 name: ''
4065 }
4066 continue
4067 }
4068 typ := c.ident(mut id)
4069 if typ == ast.error_type_idx {
4070 continue
4071 }
4072 id.info.typ = typ
4073 node.defer_vars[i] = id
4074 }
4075 }
4076 }
4077 c.stmts(mut node.stmts)
4078 c.inside_defer = false
4079}
4080
4081fn (mut c Checker) assert_stmt(mut node ast.AssertStmt) {
4082 cur_exp_typ := c.expected_type
4083 c.expected_type = ast.bool_type
4084 if !isnil(c.fn_scope) {
4085 scope := c.fn_scope.innermost(node.pos.pos)
4086 c.apply_assert_autocasts(mut node.expr, scope)
4087 }
4088 nr_errors_before := c.nr_errors
4089 assert_type := c.check_expr_option_or_result_call(node.expr, c.expr(mut node.expr))
4090 c.markused_assertstmt_auto_str(mut node)
4091 if assert_type != ast.bool_type_idx {
4092 atype_name := c.table.sym(assert_type).name
4093 c.error('assert can be used only with `bool` expressions, but found `${atype_name}` instead',
4094 node.pos)
4095 }
4096 if node.extra !is ast.EmptyExpr {
4097 extra_type := c.expr(mut node.extra)
4098 if extra_type != ast.string_type {
4099 extra_type_name := c.table.sym(extra_type).name
4100 c.error('assert allows only a single string as its second argument, but found `${extra_type_name}` instead',
4101 node.extra_pos)
4102 }
4103 }
4104 c.fail_if_unreadable(node.expr, ast.bool_type_idx, 'assertion')
4105 // Asserts are stripped in `-prod`, so only persist narrowing when the assert stays in the program.
4106 if node.is_used && !isnil(c.fn_scope) && assert_type == ast.bool_type_idx
4107 && c.nr_errors == nr_errors_before {
4108 scope := c.fn_scope.innermost(node.pos.pos)
4109 c.remember_assert_autocasts(mut node.expr, scope)
4110 }
4111 c.expected_type = cur_exp_typ
4112}
4113
4114fn (mut c Checker) block(mut node ast.Block) {
4115 if node.is_unsafe {
4116 prev_unsafe := c.inside_unsafe
4117 c.inside_unsafe = true
4118 c.stmts(mut node.stmts)
4119 c.inside_unsafe = prev_unsafe
4120 } else {
4121 if node.stmts.len > 0 && node.stmts.last() is ast.ExprStmt {
4122 last_stmt := node.stmts.last() as ast.ExprStmt
4123 if last_stmt.expr !in [ast.CallExpr, ast.IfExpr, ast.MatchExpr, ast.InfixExpr] {
4124 c.warn('expression evaluated but not used', node.stmts.last().pos)
4125 }
4126 }
4127 c.stmts(mut node.stmts)
4128 }
4129}
4130
4131fn (mut c Checker) branch_stmt(node ast.BranchStmt) {
4132 if c.inside_defer {
4133 c.error('`${node.kind.str()}` is not allowed in defer statements', node.pos)
4134 }
4135 if c.in_for_count == 0 {
4136 if c.comptime.inside_comptime_for {
4137 c.error('${node.kind.str()} is not allowed within a compile-time loop', node.pos)
4138 } else {
4139 c.error('${node.kind.str()} statement not within a loop', node.pos)
4140 }
4141 }
4142 if node.label.len > 0 {
4143 if node.label !in c.loop_labels {
4144 c.error('invalid label name `${node.label}`', node.pos)
4145 }
4146 }
4147}
4148
4149fn (mut c Checker) global_decl(mut node ast.GlobalDecl) {
4150 if !c.pref.enable_globals && !c.pref.is_fmt && !c.pref.is_vet && !c.pref.translated
4151 && !c.pref.is_livemain && !c.pref.building_v && !c.is_builtin_mod
4152 && !c.has_globals_in_module && !c.file.is_translated {
4153 c.error('use `v -enable-globals ...` to enable globals', node.pos)
4154 return
4155 }
4156
4157 required_args_attr := ['_linker_section']
4158 for attr_name in required_args_attr {
4159 if attr := node.attrs.find_first(attr_name) {
4160 if attr.arg == '' {
4161 c.error('missing argument for @[${attr_name}] attribute', attr.pos)
4162 }
4163 }
4164 }
4165 mut export_name := ''
4166 if export_attr := node.attrs.find_first('export') {
4167 export_name = export_attr.arg
4168 if export_name.len > 0 && !export_name.is_identifier() {
4169 c.error('export name `${export_name}` should be a valid identifier', node.pos)
4170 }
4171 }
4172 for mut field in node.fields {
4173 if field.language != .c {
4174 c.check_valid_snake_case(field.name, 'global name', field.pos)
4175 }
4176
4177 if field.name in ast.global_reserved_type_names {
4178 c.error('invalid use of reserved type `${field.name}` as a global name', field.pos)
4179 }
4180 if field.name == '_' {
4181 c.error('cannot use `_` as a global name', field.pos)
4182 return
4183 }
4184
4185 if field.name in c.global_names {
4186 c.error('duplicate global `${field.name}`', field.pos)
4187 }
4188 if '${c.mod}.${field.name}' in c.const_names {
4189 c.error('duplicate global and const `${field.name}`', field.pos)
4190 }
4191 check_name := if field.is_exported && export_name.len > 0 { export_name } else { field.name }
4192 if check_name in c.table.export_names.values() {
4193 c.error('duplicate export name `${check_name}`', field.pos)
4194 } else {
4195 // add global to exports for duplicate check
4196 c.table.export_names[field.name] = check_name
4197 }
4198 c.ensure_type_exists(field.typ, field.typ_pos)
4199 if field.is_const && !field.is_extern && !field.has_expr {
4200 c.error('const globals must have an explicit initializer', field.pos)
4201 }
4202 if field.has_expr {
4203 if field.expr is ast.AnonFn && field.name == 'main' {
4204 c.error('the `main` function is the program entry point, cannot redefine it',
4205 field.pos)
4206 }
4207 field.typ = c.expr(mut field.expr)
4208 mut v := c.file.global_scope.find_global(field.name) or {
4209 panic('internal compiler error - could not find global in scope')
4210 }
4211 v.typ = ast.mktyp(field.typ)
4212 } else {
4213 field_sym := c.table.sym(field.typ)
4214 if field_sym.info is ast.ArrayFixed && c.array_fixed_has_unresolved_size(field_sym.info) {
4215 mut size_expr := field_sym.info.size_expr
4216 field.typ = c.eval_array_fixed_sizes(mut size_expr, 0, field_sym.info.elem_type)
4217 mut v := c.file.global_scope.find_global(field.name) or {
4218 panic('internal compiler error - could not find global in scope')
4219 }
4220 v.typ = ast.mktyp(field.typ)
4221 }
4222 }
4223 c.global_names << field.name
4224 }
4225}
4226
4227fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) {
4228 if stmt.is_goto {
4229 c.warn('inline assembly goto is not supported, it will most likely not work', stmt.pos)
4230 }
4231 if c.is_js_backend {
4232 c.error('inline assembly is not supported in the js backend', stmt.pos)
4233 }
4234 mut aliases := c.asm_ios(mut stmt.output, mut stmt.scope, true)
4235 aliases2 := c.asm_ios(mut stmt.input, mut stmt.scope, false)
4236 aliases << aliases2
4237 for mut template in stmt.templates {
4238 if template.is_directive {
4239 /*
4240 align n[,value]
4241 .skip n[,value]
4242 .space n[,value]
4243 .byte value1[,...]
4244 .word value1[,...]
4245 .short value1[,...]
4246 .int value1[,...]
4247 .long value1[,...]
4248 .quad immediate_value1[,...]
4249 .globl symbol
4250 .global symbol
4251 .section section
4252 .text
4253 .data
4254 .bss
4255 .fill repeat[,size[,value]]
4256 .org n
4257 .previous
4258 .string string[,...]
4259 .asciz string[,...]
4260 .ascii string[,...]
4261 */
4262 if template.name !in ['skip', 'space', 'byte', 'word', 'short', 'int', 'long', 'quad',
4263 'globl', 'global', 'section', 'text', 'data', 'bss', 'fill', 'org', 'previous',
4264 'string', 'asciz', 'ascii'] { // all tcc-supported assembler directives
4265 c.error('unknown assembler directive: `${template.name}`', template.pos)
4266 }
4267 } else if expected_operands := asm_expected_operand_count(stmt.arch, template.name) {
4268 if template.args.len != expected_operands {
4269 c.error('asm instruction `${template.name}` expects ${expected_operands} operands, but got ${template.args.len}',
4270 template.pos)
4271 }
4272 }
4273 for mut arg in template.args {
4274 c.asm_arg(arg, stmt, aliases)
4275 }
4276 }
4277 for mut clob in stmt.clobbered {
4278 c.asm_arg(clob.reg, stmt, aliases)
4279 }
4280}
4281
4282fn asm_expected_operand_count(arch pref.Arch, name string) ?int {
4283 if arch !in [.amd64, .i386] {
4284 return none
4285 }
4286 return match name {
4287 'mov' { 2 }
4288 else { none }
4289 }
4290}
4291
4292fn (mut c Checker) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt, aliases []string) {
4293 match arg {
4294 ast.AsmAlias {}
4295 ast.AsmAddressing {
4296 if arg.scale !in [-1, 1, 2, 4, 8] {
4297 c.error('scale must be one of 1, 2, 4, or 8', arg.pos)
4298 }
4299 c.asm_arg(arg.displacement, stmt, aliases)
4300 c.asm_arg(arg.base, stmt, aliases)
4301 c.asm_arg(arg.index, stmt, aliases)
4302 }
4303 ast.BoolLiteral {} // all of these are guaranteed to be correct.
4304 ast.FloatLiteral {}
4305 ast.CharLiteral {}
4306 ast.IntegerLiteral {}
4307 ast.AsmRegister {} // if the register is not found, the parser will register it as an alias
4308 ast.AsmDisp {}
4309 string {}
4310 }
4311}
4312
4313fn (mut c Checker) asm_ios(mut ios []ast.AsmIO, mut scope ast.Scope, output bool) []string {
4314 mut aliases := []string{}
4315 for mut io in ios {
4316 typ := c.expr(mut io.expr)
4317 if output {
4318 c.fail_if_immutable(mut io.expr)
4319 }
4320 if io.alias != '' {
4321 aliases << io.alias
4322 if io.alias in scope.objects {
4323 scope.objects[io.alias] = ast.Var{
4324 name: io.alias
4325 expr: io.expr
4326 is_arg: true
4327 typ: typ
4328 orig_type: typ
4329 pos: io.pos
4330 }
4331 }
4332 }
4333 }
4334
4335 return aliases
4336}
4337
4338fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
4339 if c.skip_flags {
4340 return
4341 }
4342 if c.ct_cond_stack.len > 0 {
4343 node.ct_conds = c.ct_cond_stack.clone()
4344 }
4345 if node.ct_low_level_cond.len > 0
4346 && node.ct_low_level_cond !in ast.valid_comptime_not_user_defined {
4347 c.error('invalid OS/platform condition `${node.ct_low_level_cond}` in #${node.kind}',
4348 node.pos)
4349 }
4350 if c.is_js_backend {
4351 if !c.file.path.ends_with('.js.v') {
4352 c.error('hash statements are only allowed in backend specific files such "x.js.v"',
4353 node.pos)
4354 }
4355 if c.mod == 'main' {
4356 c.error('hash statements are not allowed in the main module. Place them in a separate module.',
4357 node.pos)
4358 }
4359 return
4360 }
4361 match node.kind {
4362 'include', 'insert', 'preinclude', 'postinclude' {
4363 original_flag := node.main
4364 mut flag := node.main
4365 if flag.contains('@DIR') {
4366 vdir := c.dir_path()
4367 val := flag.replace('@DIR', vdir)
4368 node.val = '${node.kind} ${val}'
4369 node.main = val
4370 flag = val
4371 }
4372 if flag.contains('@VROOT') {
4373 // c.note(checker.vroot_is_deprecated_message, node.pos)
4374 vroot := util.resolve_vmodroot(flag.replace('@VROOT', '@VMODROOT'), c.file.path) or {
4375 c.error(err.msg(), node.pos)
4376 return
4377 }
4378 node.val = '${node.kind} ${vroot}'
4379 node.main = vroot
4380 flag = vroot
4381 }
4382 if flag.contains('@VEXEROOT') {
4383 vroot := flag.replace('@VEXEROOT', c.pref.vroot)
4384 node.val = '${node.kind} ${vroot}'
4385 node.main = vroot
4386 flag = vroot
4387 }
4388 if flag.contains('@VMODROOT') {
4389 vroot := util.resolve_vmodroot(flag, c.file.path) or {
4390 c.error(err.msg(), node.pos)
4391 return
4392 }
4393 node.val = '${node.kind} ${vroot}'
4394 node.main = vroot
4395 flag = vroot
4396 }
4397 if flag.contains('\$env(') {
4398 env := util.resolve_env_value(flag, true) or {
4399 c.error(err.msg(), node.pos)
4400 return
4401 }
4402 node.main = env
4403 }
4404 if flag.contains('\$d(') {
4405 d := util.resolve_d_value(c.pref.compile_values, flag) or {
4406 c.error(err.msg(), node.pos)
4407 return
4408 }
4409 node.main = d
4410 }
4411 flag_no_comment := flag.all_before('//').trim_space()
4412 if node.kind in ['include', 'preinclude', 'postinclude'] {
4413 if !((flag_no_comment.starts_with('"') && flag_no_comment.ends_with('"'))
4414 || (flag_no_comment.starts_with('<') && flag_no_comment.ends_with('>'))) {
4415 c.error('including C files should use either `"header_file.h"` or `<header_file.h>` quoting',
4416 node.pos)
4417 }
4418 }
4419 if node.kind == 'insert' {
4420 if !(flag_no_comment.starts_with('"') && flag_no_comment.ends_with('"')) {
4421 c.error('inserting .c or .h files, should use `"header_file.h"` quoting',
4422 node.pos)
4423 }
4424 node.main = node.main.trim('"')
4425 if fcontent := os.read_file(node.main) {
4426 node.val = fcontent
4427 } else {
4428 mut missing_message := 'The file ${original_flag}, needed for insertion by module `${node.mod}`,'
4429 if os.is_file(node.main) {
4430 missing_message += ' is not readable.'
4431 } else {
4432 missing_message += ' does not exist.'
4433 }
4434 if node.msg != '' {
4435 missing_message += ' ${node.msg}.'
4436 }
4437 c.error(missing_message, node.pos)
4438 }
4439 }
4440 }
4441 'pkgconfig' {
4442 if c.pref.output_cross_c || c.pref.os != pref.get_host_os() {
4443 // Do not add host pkg-config flags to cross-target builds.
4444 // They frequently inject include/library paths for the host OS
4445 // (for example Linux OpenSSL headers when targeting Windows).
4446 return
4447 }
4448 args := if node.main.contains('--') {
4449 node.main.split(' ')
4450 } else {
4451 '--cflags --libs ${node.main}'.split(' ')
4452 }
4453 mut m := pkgconfig.main(args) or {
4454 c.error(err.msg(), node.pos)
4455 return
4456 }
4457 cflags := m.run() or {
4458 c.error(err.msg(), node.pos)
4459 return
4460 }
4461 c.table.parse_cflag(cflags, c.mod, c.pref.compile_defines_all) or {
4462 c.error(err.msg(), node.pos)
4463 return
4464 }
4465 }
4466 'flag' {
4467 // #flag linux -lm
4468 mut flag := node.main
4469 if flag == 'flag' { // Checks for empty flag
4470 c.error('no argument(s) provided for #flag', node.pos)
4471 }
4472 flag = c.resolve_pseudo_variables(flag, node.pos) or { return }
4473 c.table.parse_cflag(flag, c.mod, c.pref.compile_defines_all) or {
4474 c.error(err.msg(), node.pos)
4475 }
4476 }
4477 else {
4478 if node.kind == 'define' {
4479 if !c.is_builtin_mod && !c.file.path.ends_with('.c.v')
4480 && !c.file.path.contains('vlib') {
4481 if !c.pref.is_bare {
4482 c.error("#define can only be used in vlib (V's standard library) and *.c.v files",
4483 node.pos)
4484 }
4485 }
4486 } else {
4487 c.error('expected `#define`, `#flag`, `#include`, `#insert` or `#pkgconfig` not ${node.val}',
4488 node.pos)
4489 }
4490 }
4491 }
4492}
4493
4494fn (mut c Checker) resolve_pseudo_variables(oflag string, pos token.Pos) ?string {
4495 mut flag := oflag
4496 if flag.contains('@VEXEROOT') {
4497 // expand `@VEXEROOT` to its absolute path
4498 flag = flag.replace('@VEXEROOT', c.pref.vroot)
4499 }
4500 if flag.contains('@VROOT') {
4501 flag = util.resolve_vmodroot(flag.replace('@VROOT', '@VMODROOT'), c.file.path) or {
4502 c.error(err.msg(), pos)
4503 return none
4504 }
4505 }
4506 if flag.contains('@VMODROOT') {
4507 flag = util.resolve_vmodroot(flag, c.file.path) or {
4508 c.error(err.msg(), pos)
4509 return none
4510 }
4511 }
4512 if flag.contains('@DIR') {
4513 // expand `@DIR` to its absolute path
4514 flag = flag.replace('@DIR', c.dir_path())
4515 }
4516 if flag.contains('\$env(') {
4517 flag = util.resolve_env_value(flag, true) or {
4518 c.error(err.msg(), pos)
4519 return none
4520 }
4521 }
4522 if flag.contains('\$d(') {
4523 flag = util.resolve_d_value(c.pref.compile_values, flag) or {
4524 c.error(err.msg(), pos)
4525 return none
4526 }
4527 }
4528 for deprecated in ['@VMOD', '@VMODULE', '@VPATH', '@VLIB_PATH'] {
4529 if flag.contains(deprecated) {
4530 if !flag.contains('@VMODROOT') {
4531 c.error('${deprecated} had been deprecated, use @VMODROOT instead.', pos)
4532 return none
4533 }
4534 }
4535 }
4536 return flag
4537}
4538
4539fn (mut c Checker) import_stmt(node ast.Import) {
4540 legacy_x_web := 'x.v' + 'web'
4541 legacy_web := 'v' + 'web'
4542 if node.mod == legacy_x_web {
4543 c.error('the legacy x.web module has been removed. Use `import veb` instead.', node.pos)
4544 } else if node.mod == legacy_web {
4545 c.error('the legacy web module has been removed. Use `import veb` instead.', node.pos)
4546 }
4547 c.check_valid_snake_case(node.alias, 'module alias', node.pos)
4548 for sym in node.syms {
4549 name := '${node.mod}.${sym.name}'
4550 if sym.name[0].is_capital() {
4551 if type_sym := c.table.find_sym(name) {
4552 if type_sym.kind != .placeholder {
4553 if !type_sym.is_pub {
4554 c.error('module `${node.mod}` type `${sym.name}` is private', sym.pos)
4555 }
4556 continue
4557 }
4558 }
4559 c.error('module `${node.mod}` has no type `${sym.name}`', sym.pos)
4560 continue
4561 }
4562 if sym.name in ast.builtin_type_names {
4563 c.error('cannot import or override builtin type', sym.pos)
4564 }
4565 if func := c.table.find_fn(name) {
4566 if !func.is_pub {
4567 c.error('module `${node.mod}` function `${sym.name}()` is private', sym.pos)
4568 }
4569 continue
4570 }
4571 if _ := c.file.global_scope.find_const(name) {
4572 continue
4573 }
4574 c.error('module `${node.mod}` has no constant or function `${sym.name}`', sym.pos)
4575 }
4576 if c.table.module_deprecated[node.mod] {
4577 c.deprecate('module', node.mod, c.table.module_attrs[node.mod], node.pos)
4578 }
4579}
4580
4581// stmts should be used for processing normal statement lists (fn bodies, for loop bodies etc).
4582fn (mut c Checker) stmts(mut stmts []ast.Stmt) {
4583 old_stmt_level := c.stmt_level
4584 c.stmt_level = 0
4585 c.stmts_ending_with_expression(mut stmts, c.expected_or_type)
4586 c.stmt_level = old_stmt_level
4587}
4588
4589// stmts_ending_with_expression, should be used for processing list of statements, that can end with an expression.
4590// Examples for such lists are the bodies of `or` blocks, `if` expressions and `match` expressions:
4591// `x := opt() or { stmt1 stmt2 ExprStmt }`,
4592// `x := if cond { stmt1 stmt2 ExprStmt } else { stmt2 stmt3 ExprStmt }`,
4593// `x := match expr { Type1 { stmt1 stmt2 ExprStmt } else { stmt2 stmt3 ExprStmt }`.
4594fn (mut c Checker) stmts_ending_with_expression(mut stmts []ast.Stmt, expected_or_type ast.Type) {
4595 if stmts.len == 0 {
4596 c.scope_returns = false
4597 return
4598 }
4599 if c.stmt_level > stmt_level_cutoff_limit {
4600 c.scope_returns = false
4601 c.error('checker: too many stmt levels: ${c.stmt_level} ', stmts[0].pos)
4602 return
4603 }
4604 mut unreachable := token.Pos{
4605 line_nr: -1
4606 }
4607 c.stmt_level++
4608 for i, mut stmt in stmts {
4609 c.is_last_stmt = i == stmts.len - 1
4610 if c.scope_returns && unreachable.line_nr == -1 && stmt !is ast.SemicolonStmt
4611 && stmt !is ast.EmptyStmt {
4612 unreachable = stmt.pos
4613 }
4614 prev_expected_or_type := c.expected_or_type
4615 c.expected_or_type = if c.is_last_stmt { expected_or_type } else { ast.void_type }
4616 c.stmt(mut stmt)
4617 c.expected_or_type = prev_expected_or_type
4618 if !c.inside_anon_fn && c.in_for_count > 0 && stmt is ast.BranchStmt
4619 && stmt.kind in [.key_continue, .key_break] {
4620 c.scope_returns = true
4621 } else if stmt is ast.GotoLabel {
4622 unreachable = token.Pos{
4623 line_nr: -1
4624 }
4625 c.scope_returns = false
4626 }
4627 if c.should_abort {
4628 return
4629 }
4630 }
4631 c.stmt_level--
4632 if unreachable.line_nr >= 0 && !c.pref.translated && !c.file.is_translated {
4633 c.error('unreachable code', unreachable)
4634 }
4635 c.find_unreachable_statements_after_noreturn_calls(stmts)
4636 c.scope_returns = false
4637}
4638
4639@[inline]
4640fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
4641 if typ == 0 {
4642 return typ
4643 }
4644 has_generic_flag := typ.has_flag(.generic)
4645 if !has_generic_flag {
4646 idx := typ.idx()
4647 if idx <= ast.nil_type_idx
4648 || (idx < c.generic_parts_cache.len && c.generic_parts_cache[idx] == 1) {
4649 return typ
4650 }
4651 }
4652 has_generic_parts := has_generic_flag || c.type_has_unresolved_generic_parts(typ)
4653 if !has_generic_parts {
4654 return typ
4655 }
4656 concrete_typ := c.recheck_concrete_type(typ)
4657 if concrete_typ != typ {
4658 return concrete_typ
4659 }
4660 if c.inside_generic_struct_init {
4661 generic_names := c.cur_struct_generic_types.map(c.table.sym(it).name)
4662 if t_typ := c.table.convert_generic_type(typ, generic_names, c.cur_struct_concrete_types) {
4663 return t_typ
4664 }
4665 }
4666 if c.inside_anon_fn && c.anon_fn_generic_names.len > 0
4667 && c.anon_fn_generic_names.len == c.anon_fn_concrete_types.len {
4668 if t_typ := c.table.convert_generic_type(typ, c.anon_fn_generic_names,
4669 c.anon_fn_concrete_types)
4670 {
4671 return t_typ
4672 }
4673 }
4674 if c.table.cur_fn != unsafe { nil } {
4675 if t_typ := c.table.convert_generic_type(typ, c.table.cur_fn.generic_names,
4676 c.table.cur_concrete_types)
4677 {
4678 return t_typ
4679 }
4680 if c.inside_lambda && c.table.cur_lambda.call_ctx != unsafe { nil }
4681 && c.table.cur_lambda.func != unsafe { nil } {
4682 if t_typ := c.table.convert_generic_type(typ,
4683 c.table.cur_lambda.func.decl.generic_names,
4684 c.table.cur_lambda.call_ctx.concrete_types)
4685 {
4686 return t_typ
4687 }
4688 }
4689 }
4690 if t_typ := c.type_resolver.resolve_bound_generic_type(typ) {
4691 return t_typ
4692 }
4693 return typ
4694}
4695
4696fn (mut c Checker) recheck_concrete_type(typ ast.Type) ast.Type {
4697 if typ == 0 || c.table.cur_fn == unsafe { nil } || c.table.cur_concrete_types.len == 0 {
4698 return typ
4699 }
4700 sym := c.table.sym(typ)
4701 match sym.info {
4702 ast.Struct, ast.Interface, ast.SumType {
4703 if sym.info.concrete_types.len > 0
4704 && !sym.info.concrete_types.any(it.has_flag(.generic)) {
4705 return typ
4706 }
4707 }
4708 ast.GenericInst {
4709 if sym.info.concrete_types.len > 0
4710 && !sym.info.concrete_types.any(it.has_flag(.generic)) {
4711 return typ
4712 }
4713 }
4714 else {}
4715 }
4716
4717 has_generic_flag := typ.has_flag(.generic)
4718 has_unresolved := c.type_has_unresolved_generic_parts(typ)
4719 if !has_generic_flag && !has_unresolved {
4720 return typ
4721 }
4722 generic_names := c.effective_fn_generic_names(c.table.cur_fn)
4723 if generic_names.len == 0 || generic_names.len != c.table.cur_concrete_types.len {
4724 return typ
4725 }
4726 if resolved_typ := c.table.convert_generic_type(typ, generic_names, c.table.cur_concrete_types) {
4727 return resolved_typ
4728 }
4729 unwrapped_typ :=
4730 c.table.unwrap_generic_type_ex(typ, generic_names, c.table.cur_concrete_types, true)
4731 if unwrapped_typ != typ {
4732 return unwrapped_typ
4733 }
4734 return typ
4735}
4736
4737pub fn (mut c Checker) expr(mut node ast.Expr) ast.Type {
4738 c.expr_level++
4739 defer {
4740 c.expr_level--
4741 }
4742
4743 if c.expr_level > expr_level_cutoff_limit {
4744 c.error('checker: too many expr levels: ${c.expr_level} ', node.pos())
4745 return ast.void_type
4746 }
4747 defer {
4748 if c.pref.is_vls {
4749 c.ident_gotodef(node)
4750 c.ident_hover(node)
4751 }
4752 }
4753 match mut node {
4754 ast.IfExpr {
4755 return c.if_expr(mut node)
4756 }
4757 ast.ComptimeType {
4758 c.error('incorrect use of compile-time type', node.pos)
4759 }
4760 ast.EmptyExpr {
4761 if c.pref.is_verbose {
4762 print_backtrace()
4763 }
4764 c.error('checker.expr(): unhandled EmptyExpr', token.Pos{})
4765 return ast.void_type
4766 }
4767 ast.CTempVar {
4768 return node.typ
4769 }
4770 ast.AnonFn {
4771 return c.anon_fn(mut node)
4772 }
4773 ast.ArrayDecompose {
4774 typ := c.expr(mut node.expr)
4775 type_sym := c.table.sym(typ)
4776 if type_sym.kind == .array_fixed {
4777 c.error('direct decomposition of fixed array is not allowed, convert the fixed array to normal array via ${node.expr}[..]',
4778 node.expr.pos())
4779 return ast.void_type
4780 } else if type_sym.kind != .array {
4781 c.error('decomposition can only be used on arrays', node.expr.pos())
4782 return ast.void_type
4783 }
4784 array_info := type_sym.info as ast.Array
4785 elem_type := array_info.elem_type.set_flag(.variadic)
4786 node.expr_type = typ
4787 node.arg_type = elem_type
4788 return elem_type
4789 }
4790 ast.ArrayInit {
4791 return c.array_init(mut node)
4792 }
4793 ast.AsCast {
4794 node.expr_type = c.expr(mut node.expr)
4795 expr_type_sym := c.table.sym(node.expr_type)
4796 type_sym := c.table.sym(c.unwrap_generic(node.typ))
4797 if mut node.expr is ast.Ident {
4798 if mut node.expr.obj is ast.Var {
4799 ident_typ := c.visible_var_type_for_read(node.expr.obj)
4800 if !node.typ.has_flag(.option) && ident_typ.has_flag(.option)
4801 && node.expr.or_expr.kind == .absent {
4802 c.error('variable `${node.expr.name}` is an Option, it must be unwrapped first',
4803 node.expr.pos)
4804 }
4805 }
4806 }
4807 // If the expression is already the target type (e.g. due to smartcasting
4808 // inside an `if x is Type` block), the cast is a no-op.
4809 if node.expr_type.idx() == node.typ.idx() {
4810 return node.typ
4811 }
4812 is_sumtype := expr_type_sym.kind == .sum_type
4813 || (expr_type_sym.kind == .generic_inst && expr_type_sym.info is ast.GenericInst
4814 && c.table.type_symbols[expr_type_sym.info.parent_idx].kind == .sum_type)
4815 if is_sumtype {
4816 c.ensure_type_exists(node.typ, node.pos)
4817 unwrapped_expr_type := c.unwrap_generic(node.expr_type)
4818 unwrapped_node_typ := c.unwrap_generic(node.typ)
4819 if !c.table.sumtype_has_variant(unwrapped_expr_type, unwrapped_node_typ, true) {
4820 // For generic_inst sumtypes, also check using the parent's variant info
4821 mut found := false
4822 ue_sym := c.table.sym(unwrapped_expr_type)
4823 if ue_sym.kind == .generic_inst && ue_sym.info is ast.GenericInst {
4824 parent_sym := c.table.type_symbols[ue_sym.info.parent_idx]
4825 if parent_sym.kind == .sum_type && parent_sym.info is ast.SumType {
4826 // Check if the variant type name matches any resolved variant
4827 variant_sym := c.table.sym(unwrapped_node_typ)
4828 for v in parent_sym.info.variants {
4829 v_sym := c.table.sym(v)
4830 if v_sym.name == variant_sym.name {
4831 found = true
4832 break
4833 }
4834 // Also check resolved generic variants
4835 if v.has_flag(.generic) {
4836 resolved_v := c.unwrap_generic(v)
4837 if resolved_v.idx() == unwrapped_node_typ.idx() {
4838 found = true
4839 break
4840 }
4841 }
4842 }
4843 }
4844 }
4845 if !found {
4846 addr := '&'.repeat(node.typ.nr_muls())
4847 c.error('cannot cast `${expr_type_sym.name}` to `${addr}${type_sym.name}`',
4848 node.pos)
4849 }
4850 }
4851 } else if expr_type_sym.kind == .interface {
4852 c.ensure_type_exists(node.typ, node.pos)
4853 if type_sym.kind != .interface {
4854 c.type_implements(node.typ, node.expr_type, node.pos)
4855 }
4856 } else if node.expr_type.clear_flag(.option) != node.typ.clear_flag(.option) {
4857 // Also compare with unwrapped generic types to avoid false positives
4858 unwrapped_node_typ := c.unwrap_generic(node.typ)
4859 if node.expr_type.clear_flag(.option) != unwrapped_node_typ.clear_flag(.option)
4860 && !node.expr_type.has_flag(.generic) && !node.typ.has_flag(.generic) {
4861 mut s := 'cannot cast non-sum type `${expr_type_sym.name}` using `as`'
4862 if type_sym.kind == .sum_type {
4863 s += ' - use e.g. `${type_sym.name}(some_expr)` instead.'
4864 }
4865 c.error(s, node.pos)
4866 }
4867 }
4868 return node.typ
4869 }
4870 ast.Assoc {
4871 v := node.scope.find_var(node.var_name) or { panic(err) }
4872 for i, _ in node.fields {
4873 mut expr := node.exprs[i]
4874 c.expr(mut expr)
4875 }
4876 node.typ = v.typ
4877 return v.typ
4878 }
4879 ast.BoolLiteral {
4880 return ast.bool_type
4881 }
4882 ast.CastExpr {
4883 return c.cast_expr(mut node)
4884 }
4885 ast.CallExpr {
4886 mut ret_type := c.call_expr(mut node)
4887 if ret_type != 0 && c.table.sym(ret_type).kind == .alias {
4888 unaliased_type := c.table.unaliased_type(ret_type)
4889 if unaliased_type.has_option_or_result() {
4890 ret_type = unaliased_type
4891 }
4892 }
4893 if !ret_type.has_option_or_result() {
4894 c.expr_or_block_err(node.or_block.kind, node.name, node.or_block.pos, false)
4895 }
4896 if node.or_block.kind != .absent {
4897 if ret_type.has_flag(.option) {
4898 ret_type = ret_type.clear_flag(.option)
4899 }
4900 if ret_type.has_flag(.result) {
4901 ret_type = ret_type.clear_flag(.result)
4902 }
4903 }
4904 return ret_type
4905 }
4906 ast.ChanInit {
4907 return c.chan_init(mut node)
4908 }
4909 ast.CharLiteral {
4910 return ast.rune_type
4911 }
4912 ast.Comment {
4913 return ast.void_type
4914 }
4915 ast.AtExpr {
4916 return c.at_expr(mut node)
4917 }
4918 ast.ComptimeCall {
4919 return c.comptime_call(mut node)
4920 }
4921 ast.ComptimeSelector {
4922 return c.comptime_selector(mut node)
4923 }
4924 ast.ConcatExpr {
4925 return c.concat_expr(mut node)
4926 }
4927 ast.DumpExpr {
4928 c.expected_type = ast.string_type
4929 node.expr_type = c.expr(mut node.expr)
4930 c.markused_dumpexpr(mut node)
4931 if c.comptime.inside_comptime_for && mut node.expr is ast.Ident {
4932 if node.expr.ct_expr {
4933 node.expr_type = c.type_resolver.get_type(node.expr as ast.Ident)
4934 } else if (node.expr as ast.Ident).name in c.type_resolver.type_map {
4935 node.expr_type = c.type_resolver.get_ct_type_or_default((node.expr as ast.Ident).name,
4936 node.expr_type)
4937 } else if node.expr.obj is ast.Var {
4938 var_obj := node.expr.obj as ast.Var
4939 if var_obj.smartcasts.len > 0 {
4940 node.expr_type = c.unwrap_generic(c.visible_var_type_for_read(var_obj))
4941 }
4942 }
4943 } else if mut node.expr is ast.Ident {
4944 if node.expr.obj is ast.Var {
4945 var_obj := node.expr.obj as ast.Var
4946 if var_obj.smartcasts.len > 0 {
4947 node.expr_type = c.unwrap_generic(c.visible_var_type_for_read(var_obj))
4948 }
4949 }
4950 }
4951 c.check_expr_option_or_result_call(node.expr, node.expr_type)
4952 etidx := node.expr_type.idx()
4953 if etidx == ast.void_type_idx {
4954 c.error('dump expression can not be void', node.expr.pos())
4955 return ast.void_type
4956 } else if etidx == ast.char_type_idx && node.expr_type.nr_muls() == 0 {
4957 c.error('`char` values cannot be dumped directly, use dump(u8(x)) or dump(int(x)) instead',
4958 node.expr.pos())
4959 return ast.void_type
4960 }
4961 if c.fail_if_private_implicit_str(node.expr_type, node.expr.pos(), 'dump') {
4962 return ast.void_type
4963 }
4964
4965 unwrapped_expr_type := c.unwrap_generic(node.expr_type)
4966 tsym := c.table.sym(unwrapped_expr_type)
4967 tsym_final := c.table.final_sym(unwrapped_expr_type)
4968 if tsym_final.kind == .array_fixed {
4969 info := tsym_final.info as ast.ArrayFixed
4970 if !info.is_fn_ret {
4971 // for dumping fixed array we must register the fixed array struct to return from function
4972 c.table.find_or_register_array_fixed(info.elem_type, info.size, info.size_expr,
4973 true)
4974 }
4975 }
4976 type_cname := if node.expr_type.has_flag(.option) {
4977 '_option_${tsym.cname}'
4978 } else {
4979 tsym.cname
4980 }
4981 c.table.dumps[int(unwrapped_expr_type.clear_flags(.result, .atomic_f))] = type_cname
4982 node.cname = type_cname
4983 return node.expr_type
4984 }
4985 ast.EnumVal {
4986 return c.enum_val(mut node)
4987 }
4988 ast.FloatLiteral {
4989 return ast.float_literal_type
4990 }
4991 ast.GoExpr {
4992 return c.go_expr(mut node)
4993 }
4994 ast.SpawnExpr {
4995 return c.spawn_expr(mut node)
4996 }
4997 ast.Ident {
4998 return c.ident(mut node)
4999 }
5000 ast.IfGuardExpr {
5001 old_inside_if_guard := c.inside_if_guard
5002 c.inside_if_guard = true
5003 node.expr_type = c.expr(mut node.expr)
5004 c.inside_if_guard = old_inside_if_guard
5005 if c.pref.skip_unused && node.expr_type.has_flag(.generic) {
5006 unwrapped_type := c.unwrap_generic(node.expr_type)
5007 c.table.used_features.comptime_syms[unwrapped_type] = true
5008 }
5009 if !node.expr_type.has_flag(.option) && !node.expr_type.has_flag(.result) {
5010 mut no_opt_or_res := true
5011 match mut node.expr {
5012 ast.IndexExpr {
5013 no_opt_or_res = false
5014 node.expr_type = node.expr_type.set_flag(.option)
5015 node.expr.is_option = true
5016 }
5017 ast.PrefixExpr {
5018 if node.expr.op == .arrow {
5019 no_opt_or_res = false
5020 node.expr_type = node.expr_type.set_flag(.option)
5021 node.expr.is_option = true
5022 }
5023 }
5024 else {}
5025 }
5026
5027 if no_opt_or_res {
5028 c.error('expression should either return an Option or a Result',
5029 node.expr.pos())
5030 }
5031 }
5032 return ast.bool_type
5033 }
5034 ast.IndexExpr {
5035 return c.index_expr(mut node)
5036 }
5037 ast.InfixExpr {
5038 return c.infix_expr(mut node)
5039 }
5040 ast.IntegerLiteral {
5041 return c.int_lit(mut node)
5042 }
5043 ast.LambdaExpr {
5044 prev_inside_lambda := c.inside_lambda
5045 prev_cur_lambda := c.table.cur_lambda
5046 c.inside_lambda = true
5047 c.table.cur_lambda = unsafe { &node }
5048 ret_type := c.lambda_expr(mut node, c.expected_type)
5049 c.inside_lambda = prev_inside_lambda
5050 c.table.cur_lambda = prev_cur_lambda
5051 return ret_type
5052 }
5053 ast.LockExpr {
5054 return c.lock_expr(mut node)
5055 }
5056 ast.MapInit {
5057 return c.map_init(mut node)
5058 }
5059 ast.MatchExpr {
5060 return c.match_expr(mut node)
5061 }
5062 ast.Nil {
5063 if !c.inside_unsafe && !c.inside_sql {
5064 c.error('`nil` is only allowed in `unsafe` code', node.pos)
5065 }
5066 return ast.nil_type
5067 }
5068 ast.PostfixExpr {
5069 return c.postfix_expr(mut node)
5070 }
5071 ast.PrefixExpr {
5072 return c.prefix_expr(mut node)
5073 }
5074 ast.None {
5075 return ast.none_type
5076 }
5077 ast.OrExpr {
5078 // never happens
5079 return ast.void_type
5080 }
5081 // ast.OrExpr2 {
5082 // return node.typ
5083 // }
5084 ast.ParExpr {
5085 if node.expr is ast.ParExpr {
5086 c.note('redundant parentheses are used', node.pos)
5087 }
5088 return c.expr(mut node.expr)
5089 }
5090 ast.RangeExpr {
5091 // branch range expression of `match x { a...b {} }`, or: `a#[x..y]`:
5092 ltyp := c.expr(mut node.low)
5093 htyp := c.expr(mut node.high)
5094 if !c.check_types(ltyp, htyp) {
5095 lstype := c.table.type_to_str(ltyp)
5096 hstype := c.table.type_to_str(htyp)
5097 c.add_error_detail('')
5098 c.add_error_detail(' low part type: ${lstype}')
5099 c.add_error_detail('high part type: ${hstype}')
5100 c.error('the low and high parts of a range expression, should have matching types',
5101 node.pos)
5102 }
5103 node.typ = c.promote(ltyp, htyp)
5104 return ltyp
5105 }
5106 ast.SelectExpr {
5107 return c.select_expr(mut node)
5108 }
5109 ast.SelectorExpr {
5110 mut ret_type := c.selector_expr(mut node)
5111 if c.table.sym(ret_type).kind == .chan {
5112 return ret_type
5113 }
5114 if !ret_type.has_flag(.option) && !ret_type.has_flag(.result) {
5115 c.expr_or_block_err(node.or_block.kind, node.field_name, node.or_block.pos, true)
5116 }
5117 if node.or_block.kind != .absent {
5118 if ret_type.has_flag(.option) {
5119 ret_type = ret_type.clear_flag(.option)
5120 }
5121 if ret_type.has_flag(.result) {
5122 ret_type = ret_type.clear_flag(.result)
5123 }
5124 }
5125 return ret_type
5126 }
5127 ast.SizeOf {
5128 if !node.is_type {
5129 node.typ = c.expr(mut node.expr)
5130 }
5131 sym := c.table.final_sym(node.typ)
5132 if sym.kind == .placeholder && sym.language != .c {
5133 // Allow `sizeof(C.MYSQL_TIME)` etc
5134 c.error('unknown type `${sym.name}`', node.pos)
5135 }
5136 // c.deprecate_old_isreftype_and_sizeof_of_a_guessed_type(node.guessed_type,
5137 // node.typ, node.pos, 'sizeof')
5138 return ast.u32_type
5139 }
5140 ast.IsRefType {
5141 if !node.is_type {
5142 node.typ = c.expr(mut node.expr)
5143 }
5144 // c.deprecate_old_isreftype_and_sizeof_of_a_guessed_type(node.guessed_type,
5145 // node.typ, node.pos, 'isreftype')
5146 return ast.bool_type
5147 }
5148 ast.OffsetOf {
5149 return c.offset_of(node)
5150 }
5151 ast.SqlExpr {
5152 return c.sql_expr(mut node)
5153 }
5154 ast.SqlQueryDataExpr {
5155 return c.sql_query_data_expr(mut node)
5156 }
5157 ast.StringLiteral {
5158 if node.language == .c {
5159 // string literal starts with "c": `C.printf(c'hello')`
5160 return ast.u8_type.set_nr_muls(1)
5161 }
5162 if node.language == .js {
5163 // string literal starts with "js": `JS.console.log(js'hello')`
5164 if c.js_string.is_void() {
5165 c.js_string = c.table.find_type('JS.String')
5166 }
5167 return c.js_string
5168 }
5169 if node.is_raw {
5170 // raw strings don't need any sort of checking related to unicode
5171 return ast.string_type
5172 }
5173 return c.string_lit(mut node)
5174 }
5175 ast.StringInterLiteral {
5176 return c.string_inter_lit(mut node)
5177 }
5178 ast.StructInit {
5179 if node.unresolved {
5180 mut expr_ := c.table.resolve_init(node, c.unwrap_generic(node.typ))
5181 c.markused_used_maps(c.table.used_features.used_maps == 0 && expr_ is ast.MapInit)
5182 return c.expr(mut expr_)
5183 }
5184 mut inited_fields := []string{}
5185 return c.struct_init(mut node, false, mut inited_fields)
5186 }
5187 ast.TypeNode {
5188 if !c.inside_x_is_type && node.typ.has_flag(.generic) && unsafe { c.table.cur_fn != 0 }
5189 && c.table.cur_fn.generic_names.len == 0 {
5190 c.error('unexpected generic variable in non-generic function `${c.table.cur_fn.name}`',
5191 node.pos)
5192 } else if node.stmt != ast.empty_stmt && node.typ == ast.void_type {
5193 c.stmt(mut node.stmt)
5194 node.typ = c.table.find_type((node.stmt as ast.StructDecl).name)
5195 }
5196 return node.typ
5197 }
5198 ast.TypeOf {
5199 if !node.is_type {
5200 node.typ = c.expr(mut node.expr)
5201 }
5202 return ast.string_type
5203 }
5204 ast.UnsafeExpr {
5205 return c.unsafe_expr(mut node)
5206 }
5207 ast.Likely {
5208 ltype := c.expr(mut node.expr)
5209 if !c.check_types(ltype, ast.bool_type) {
5210 ltype_sym := c.table.sym(ltype)
5211 lname := if node.is_likely { '_likely_' } else { '_unlikely_' }
5212 c.error('`${lname}()` expects a boolean expression, instead it got `${ltype_sym.name}`',
5213 node.pos)
5214 }
5215 return ast.bool_type
5216 }
5217 ast.NodeError {}
5218 }
5219
5220 return ast.void_type
5221}
5222
5223fn (c &Checker) expr_is_known_zero_integer(expr ast.Expr) bool {
5224 match expr {
5225 ast.IntegerLiteral {
5226 return expr.val.int() == 0
5227 }
5228 ast.ParExpr {
5229 return c.expr_is_known_zero_integer(expr.expr)
5230 }
5231 ast.Ident {
5232 match expr.obj {
5233 ast.GlobalField, ast.ConstField, ast.Var {
5234 if expr.obj.expr is ast.IntegerLiteral {
5235 return (expr.obj.expr as ast.IntegerLiteral).val.int() == 0
5236 }
5237 }
5238 else {}
5239 }
5240 }
5241 else {}
5242 }
5243
5244 return false
5245}
5246
5247// pub fn (mut c Checker) asm_reg(mut node ast.AsmRegister) ast.Type {
5248// name := node.name
5249
5250// for bit_size, array in ast.x86_no_number_register_list {
5251// if name in array {
5252// return c.table.bitsize_to_type(bit_size)
5253// }
5254// }
5255// for bit_size, array in ast.x86_with_number_register_list {
5256// if name in array {
5257// return c.table.bitsize_to_type(bit_size)
5258// }
5259// }
5260// c.error('invalid register name: `${name}`', node.pos)
5261// return ast.void_type
5262// }
5263
5264fn (mut c Checker) rewrite_smartcast_generic_wrapper_cast(mut node ast.CastExpr, to_type ast.Type) bool {
5265 expr := node.expr
5266 if expr is ast.Ident {
5267 if expr.obj is ast.Var {
5268 if expr.obj.smartcasts.len == 0
5269 || c.table.sym(c.unwrap_generic(expr.obj.typ)).kind != .sum_type {
5270 return false
5271 }
5272 field_name := c.smartcast_wrapper_field_name(expr.obj.smartcasts.last(), to_type) or {
5273 return false
5274 }
5275 old_expr := ast.Expr(expr)
5276 node.expr = ast.SelectorExpr{
5277 expr: old_expr
5278 field_name: field_name
5279 pos: node.pos
5280 }
5281 return true
5282 }
5283 }
5284 return false
5285}
5286
5287fn (mut c Checker) smartcast_wrapper_field_name(wrapper_type ast.Type, to_type ast.Type) ?string {
5288 wrapper_sym := c.table.sym(c.unwrap_generic(wrapper_type))
5289 if wrapper_sym.kind != .struct {
5290 return none
5291 }
5292 wrapper_info := wrapper_sym.info as ast.Struct
5293 if wrapper_info.embeds.len > 0 || wrapper_info.fields.len != 1 {
5294 return none
5295 }
5296 field := wrapper_info.fields[0]
5297 field_type := c.unwrap_generic(field.typ)
5298 target_type := c.unwrap_generic(to_type)
5299 target_sym := c.table.sym(target_type)
5300 final_target_type := if target_sym.info is ast.Alias {
5301 target_sym.info.parent_type
5302 } else {
5303 target_type
5304 }
5305 if c.table.final_sym(field_type) == c.table.final_sym(target_type)
5306 && final_target_type.flags() == field_type.flags()
5307 && target_type.flags() == field_type.flags() {
5308 return field.name
5309 }
5310 if c.check_types(field_type, target_type) {
5311 return field.name
5312 }
5313 return none
5314}
5315
5316fn integer_literal_from_pointer_cast_expr(expr ast.Expr) ?ast.IntegerLiteral {
5317 return match expr {
5318 ast.IntegerLiteral {
5319 expr
5320 }
5321 ast.Ident {
5322 match expr.obj {
5323 ast.GlobalField, ast.ConstField, ast.Var {
5324 if expr.obj.expr is ast.IntegerLiteral {
5325 expr.obj.expr
5326 } else {
5327 none
5328 }
5329 }
5330 else {
5331 none
5332 }
5333 }
5334 }
5335 else {
5336 none
5337 }
5338 }
5339}
5340
5341fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
5342 // Given: `Outside( Inside(xyz) )`,
5343 // node.expr_type: `Inside`
5344 // node.typ: `Outside`
5345 mut to_type := c.unwrap_generic(node.typ)
5346 if node.typ.has_flag(.generic) {
5347 c.table.used_features.comptime_syms[to_type] = true
5348 }
5349 base_to_type := to_type.clear_option_and_result()
5350 if mut node.expr is ast.ArrayInit && node.expr.typ == ast.void_type
5351 && c.table.final_sym(base_to_type).kind == .array {
5352 cast_array_type := c.table.unaliased_type(base_to_type).clear_option_and_result()
5353 cast_array_sym := c.table.sym(cast_array_type)
5354 if cast_array_sym.kind == .array {
5355 node.expr.typ = cast_array_type
5356 node.expr.elem_type = cast_array_sym.array_info().elem_type
5357 }
5358 }
5359 if mut node.expr is ast.MapInit && node.expr.typ == ast.void_type
5360 && c.table.final_sym(base_to_type).kind == .map {
5361 cast_map_type := c.table.unaliased_type(base_to_type).clear_option_and_result()
5362 cast_map_sym := c.table.sym(cast_map_type)
5363 if cast_map_sym.kind == .map {
5364 info := cast_map_sym.map_info()
5365 node.expr.typ = cast_map_type
5366 node.expr.key_type = info.key_type
5367 node.expr.value_type = info.value_type
5368 }
5369 }
5370 old_inside_integer_literal_cast := c.inside_integer_literal_cast
5371 c.inside_integer_literal_cast = to_type.is_int() && node.expr is ast.IntegerLiteral
5372 // When casting to a sumtype, reset expected_type so inner array literals
5373 // are typed by their elements, not by the outer context (e.g. []SumType).
5374 old_expected_type := c.expected_type
5375 if c.table.sym(base_to_type).kind == .sum_type && node.expr is ast.ArrayInit {
5376 c.expected_type = ast.void_type
5377 } else if node.expr is ast.Ident && node.expr.language == .c {
5378 c.expected_type = base_to_type
5379 }
5380 expr_is_ident_or_cast := node.expr is ast.Ident || node.expr is ast.CastExpr
5381 node.expr_type = c.expr(mut node.expr) // type to be casted
5382 if c.rewrite_smartcast_generic_wrapper_cast(mut node, to_type) {
5383 node.expr_type = c.expr(mut node.expr)
5384 }
5385 c.inside_integer_literal_cast = old_inside_integer_literal_cast
5386 c.expected_type = old_expected_type
5387
5388 if mut node.expr is ast.ComptimeSelector {
5389 node.expr_type = c.type_resolver.get_comptime_selector_type(node.expr, node.expr_type)
5390 } else if node.expr is ast.Ident && c.comptime.is_comptime_variant_var(node.expr) {
5391 node.expr_type = c.type_resolver.get_ct_type_or_default('${c.comptime.comptime_for_variant_var}.typ',
5392 ast.void_type)
5393 }
5394 mut from_type := c.unwrap_generic(node.expr_type)
5395 from_sym := c.table.sym(from_type)
5396 final_from_sym := c.table.final_sym(from_type)
5397
5398 mut to_sym := c.table.sym(to_type) // type to be used as cast
5399 mut final_to_sym := c.table.final_sym(to_type)
5400 final_to_type := if mut to_sym.info is ast.Alias { to_sym.info.parent_type } else { to_type }
5401 mut to_is_interface := to_sym.kind == .interface
5402 if !to_is_interface && to_sym.kind == .generic_inst {
5403 gi := to_sym.info as ast.GenericInst
5404 to_is_interface = c.table.type_symbols[gi.parent_idx].kind == .interface
5405 }
5406 enforce_safe_pointer_casts := c.file.language == .v && !c.is_builtin_mod
5407 enforce_safe_voidptr_ref_cast := enforce_safe_pointer_casts && expr_is_ident_or_cast
5408
5409 if final_to_sym == final_from_sym && final_to_type.flags() == from_type.flags()
5410 && to_type.flags() == from_type.flags() {
5411 // type alias, and flags are same, e.g. option, result, nr_muls...
5412 return node.typ
5413 }
5414
5415 final_to_is_ptr := to_type.is_ptr() || final_to_type.is_ptr()
5416 c.markused_castexpr(mut node, to_type, mut final_to_sym)
5417 if to_type.has_flag(.result) {
5418 c.error('casting to Result type is forbidden', node.pos)
5419 }
5420 c.check_any_type(to_type, to_sym, node.pos)
5421
5422 if c.is_js_backend {
5423 if (to_sym.is_number() && from_sym.name == 'JS.Number')
5424 || (to_sym.is_number() && from_sym.name == 'JS.BigInt')
5425 || (to_sym.is_string() && from_sym.name == 'JS.String')
5426 || (to_type.is_bool() && from_sym.name == 'JS.Boolean')
5427 || (from_type.is_bool() && to_sym.name == 'JS.Boolean')
5428 || (from_sym.is_number() && to_sym.name == 'JS.Number')
5429 || (from_sym.is_number() && to_sym.name == 'JS.BigInt')
5430 || (from_sym.is_string() && to_sym.name == 'JS.String') {
5431 return to_type
5432 }
5433 }
5434
5435 if !c.expected_type.has_flag(.generic) && to_sym.name.len == 1
5436 && to_sym.name.starts_with_capital() {
5437 c.error('unknown type `${to_sym.name}`', node.pos)
5438 }
5439
5440 mut to_type_exists := true
5441 if to_sym.language != .c {
5442 to_type_exists = c.ensure_type_exists(to_type, node.pos)
5443
5444 if to_sym.info is ast.Alias && to_sym.info.parent_type.has_flag(.option)
5445 && !to_type.has_flag(.option) {
5446 c.error('alias to Option type requires to be used as Option type (?${to_sym.name}(...))',
5447 node.pos)
5448 }
5449 }
5450 if from_sym.kind == .u8 && from_type.is_ptr() && to_sym.kind == .string && !to_type.is_ptr() {
5451 c.error('to convert a C string buffer pointer to a V string, use x.vstring() instead of string(x)',
5452 node.pos)
5453 }
5454 if from_type == ast.void_type {
5455 c.error('expression does not return a value so it cannot be cast', node.expr.pos())
5456 }
5457 inner_to_type := if to_type.has_flag(.option) {
5458 to_type.clear_flag(.option)
5459 } else {
5460 ast.void_type
5461 }
5462 if to_type.has_flag(.option) && from_type == ast.none_type {
5463 // allow conversion from none to every option type
5464 } else if to_type.has_flag(.option) && from_type == inner_to_type {
5465 return to_type
5466 } else if enforce_safe_voidptr_ref_cast && from_type == ast.voidptr_type_idx && to_type.is_ptr()
5467 && !c.inside_unsafe && !c.pref.translated && !c.file.is_translated
5468 && to_sym.kind !in [.sum_type, .struct, .interface] {
5469 tt := c.table.type_to_str(to_type)
5470 c.warn('casting voidptr to `${tt}` is only allowed in `unsafe` code', node.pos)
5471 } else if to_sym.kind == .sum_type {
5472 to_sym_info := to_sym.info as ast.SumType
5473 if c.pref.skip_unused && to_sym_info.concrete_types.len > 0 {
5474 c.table.used_features.comptime_syms[to_type] = true
5475 }
5476 if to_sym_info.generic_types.len > 0 && to_sym_info.concrete_types.len == 0 {
5477 c.error('generic sumtype `${to_sym.name}` must specify type parameter, e.g. ${to_sym.name}[int]',
5478 node.pos)
5479 }
5480 if from_type in [ast.int_literal_type, ast.float_literal_type] {
5481 xx := if from_type == ast.int_literal_type { ast.int_type } else { ast.f64_type }
5482 node.expr_type = c.promote_num(node.expr_type, xx)
5483 from_type = node.expr_type
5484 }
5485 if !c.table.sumtype_has_variant(to_type, from_type, false) {
5486 tt := c.table.type_to_str(to_type)
5487 if from_type == ast.voidptr_type_idx && to_type.is_ptr() {
5488 if !c.inside_unsafe {
5489 c.error('cannot cast voidptr to `${tt}` outside `unsafe`', node.pos)
5490 }
5491 } else {
5492 ft := c.table.type_to_str(from_type)
5493 c.error('cannot cast `${ft}` to `${tt}`', node.pos)
5494 }
5495 }
5496 } else if mut to_sym.info is ast.Alias && !(final_to_sym.kind == .struct && final_to_is_ptr) {
5497 if (!c.check_types(from_type, to_sym.info.parent_type) && !(final_to_sym.is_int()
5498 && final_from_sym.kind in [.enum, .bool, .i8, .u8, .char, .rune])
5499 && !(final_to_sym.is_number() && final_from_sym.is_number())
5500 && !(final_to_sym.kind == .enum && final_from_sym.is_int()))
5501 || (final_to_sym.kind == .struct
5502 && from_type.idx() in [ast.voidptr_type_idx, ast.nil_type_idx]) {
5503 ft := c.table.type_to_str(from_type)
5504 tt := c.table.type_to_str(to_type)
5505 c.error('cannot cast `${ft}` to `${tt}` (alias to `${final_to_sym.name}`)', node.pos)
5506 }
5507 } else if to_sym.kind == .struct && mut to_sym.info is ast.Struct
5508 && (!to_sym.info.is_typedef || from_type.idx() in [ast.voidptr_type_idx, ast.nil_type_idx])
5509 && !final_to_is_ptr {
5510 // For now we ignore C typedef because of `C.Window(C.None)` in vlib/clipboard (except for `from_type` is voidptr/nil)
5511 if from_sym.kind == .struct && from_sym.info is ast.Struct && !from_type.is_ptr() {
5512 if !to_type.has_flag(.option) {
5513 c.warn('casting to struct is deprecated, use e.g. `Struct{...expr}` instead',
5514 node.pos)
5515 }
5516 if from_type.idx() != to_type.idx()
5517 && !c.check_struct_signature(from_sym.info, to_sym.info) {
5518 c.error('cannot convert struct `${from_sym.name}` to struct `${to_sym.name}`',
5519 node.pos)
5520 }
5521 } else {
5522 ft := c.table.type_to_str(from_type)
5523 c.error('cannot cast `${ft}` to struct', node.pos)
5524 }
5525 } else if to_sym.kind == .struct && final_to_is_ptr {
5526 if from_sym.info is ast.Alias {
5527 from_type = from_sym.info.parent_type.derive_add_muls(from_type)
5528 }
5529 if mut node.expr is ast.IntegerLiteral {
5530 if node.expr.val.int() == 0 && !c.pref.translated && !c.file.is_translated {
5531 c.error('cannot null cast a struct pointer, use &${to_sym.name}(unsafe { nil })',
5532 node.pos)
5533 } else if !c.inside_unsafe && !c.pref.translated && !c.file.is_translated {
5534 c.error('cannot cast int to a struct pointer outside `unsafe`', node.pos)
5535 }
5536 } else if mut node.expr is ast.Ident {
5537 match mut node.expr.obj {
5538 ast.GlobalField, ast.ConstField, ast.Var {
5539 if mut node.expr.obj.expr is ast.IntegerLiteral {
5540 if node.expr.obj.expr.val.int() == 0 && !c.pref.translated
5541 && !c.file.is_translated {
5542 c.error('cannot null cast a struct pointer, use &${to_sym.name}(unsafe { nil })',
5543 node.pos)
5544 } else if !c.inside_unsafe && !c.pref.translated && !c.file.is_translated {
5545 c.error('cannot cast int to a struct pointer outside `unsafe`',
5546 node.pos)
5547 }
5548 }
5549 }
5550 else {}
5551 }
5552 }
5553 if enforce_safe_pointer_casts && !c.inside_unsafe && to_type.is_ptr() && from_type.is_ptr()
5554 && to_type != from_type && from_type != ast.voidptr_type_idx
5555 && to_type.deref() != ast.char_type && from_type.deref() != ast.char_type {
5556 ft := c.table.type_to_str(from_type)
5557 tt := c.table.type_to_str(to_type)
5558 c.warn('casting `${ft}` to `${tt}` is only allowed in `unsafe` code', node.pos)
5559 }
5560 if !from_type.is_int() && final_from_sym.kind != .enum
5561 && !from_type.is_any_kind_of_pointer() {
5562 ft := c.table.type_to_str(from_type)
5563 tt := c.table.type_to_str(to_type)
5564 c.error('cannot cast `${ft}` to `${tt}`', node.pos)
5565 }
5566 if enforce_safe_pointer_casts && !c.inside_unsafe && to_type.is_ptr() && from_type.is_ptr()
5567 && to_type != from_type && from_type != ast.voidptr_type_idx
5568 && to_type.deref() != ast.char_type && from_type.deref() != ast.char_type {
5569 ft := c.table.type_to_str(from_type)
5570 tt := c.table.type_to_str(to_type)
5571 c.warn('casting `${ft}` to `${tt}` is only allowed in `unsafe` code', node.pos)
5572 }
5573 } else if !from_type.has_option_or_result() && to_is_interface {
5574 if c.type_implements(from_type, to_type, node.pos) {
5575 if !from_type.is_any_kind_of_pointer() && from_sym.kind != .interface
5576 && !c.inside_unsafe && !from_type.is_number() {
5577 c.mark_as_referenced(mut &node.expr, true)
5578 }
5579 if mut to_sym.info is ast.Interface {
5580 if !to_sym.info.is_generic {
5581 // already concrete
5582 } else {
5583 inferred_type := c.unwrap_generic_interface(from_type, to_type, node.pos)
5584 if inferred_type != 0 {
5585 to_type = inferred_type
5586 to_sym = c.table.sym(to_type)
5587 final_to_sym = c.table.final_sym(to_type)
5588 }
5589 }
5590 } else if to_type.has_flag(.generic) || c.type_has_unresolved_generic_parts(to_type) {
5591 inferred_type := c.unwrap_generic_interface(from_type, to_type, node.pos)
5592 if inferred_type != 0 {
5593 to_type = inferred_type
5594 to_sym = c.table.sym(to_type)
5595 final_to_sym = c.table.final_sym(to_type)
5596 }
5597 }
5598 } else {
5599 if to_type.has_flag(.generic) || c.type_has_unresolved_generic_parts(to_type) {
5600 node.typname = c.table.sym(node.typ).name
5601 return node.typ
5602 }
5603 if from_sym.kind == .interface && to_sym.kind == .interface {
5604 return to_type
5605 }
5606 ft := c.table.type_to_str(from_type)
5607 tt := c.table.type_to_str(to_type)
5608 c.error('`${ft}` does not implement interface `${tt}`, cannot cast `${ft}` to interface `${tt}`',
5609 node.pos)
5610 }
5611 } else if to_type == ast.bool_type && from_type != ast.bool_type && !c.inside_unsafe
5612 && !c.pref.translated && !c.file.is_translated {
5613 c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos)
5614 } else if from_type == ast.none_type && !to_type.has_flag(.option) && !to_type.has_flag(.result) {
5615 type_name := c.table.type_to_str(to_type)
5616 c.error('cannot cast `none` to `${type_name}`', node.pos)
5617 } else if !from_type.has_option_or_result() && from_sym.kind == .struct && !from_type.is_ptr() {
5618 if (final_to_is_ptr || to_sym.kind !in [.sum_type, .interface]) && !c.is_builtin_mod
5619 && !(to_type.is_any_kind_of_pointer() && node.expr.is_auto_deref_var()) {
5620 from_type_name := c.table.type_to_str(from_type)
5621 type_name := c.table.type_to_str(to_type)
5622 c.error('cannot cast struct `${from_type_name}` to `${type_name}`', node.pos)
5623 }
5624 } else if to_sym.kind == .u8 && !final_from_sym.is_number()
5625 && !from_type.is_any_kind_of_pointer() && final_from_sym.kind !in [.char, .enum, .bool] {
5626 ft := c.table.type_to_str(from_type)
5627 tt := c.table.type_to_str(to_type)
5628 c.error('cannot cast type `${ft}` to `${tt}`', node.pos)
5629 } else if (from_type.has_flag(.option) && !to_type.has_flag(.option))
5630 || from_type.has_flag(.result) || from_type.has_flag(.variadic) {
5631 // variadic case can happen when arrays are converted into variadic
5632 msg := if from_type.has_flag(.option) {
5633 'an Option'
5634 } else if from_type.has_flag(.result) {
5635 'a Result'
5636 } else {
5637 'a variadic'
5638 }
5639 c.error('cannot type cast ${msg}', node.pos)
5640 } else if !c.inside_unsafe && !c.is_builtin_mod && to_type.is_ptr() && from_type.is_ptr()
5641 && to_type != from_type && final_to_sym.kind != .char && final_from_sym.kind != .char {
5642 ft := c.table.type_to_str(from_type)
5643 tt := c.table.type_to_str(to_type)
5644 c.warn('casting `${ft}` to `${tt}` is only allowed in `unsafe` code', node.pos)
5645 } else if from_sym.kind == .array_fixed && !from_type.is_ptr() {
5646 if !c.pref.translated && !c.file.is_translated && !c.inside_unsafe {
5647 c.warn('cannot cast a fixed array (use e.g. `&arr[0]` instead)', node.pos)
5648 }
5649 } else if final_from_sym.kind == .string && final_to_sym.is_number()
5650 && final_to_sym.kind != .rune {
5651 snexpr := node.expr.str()
5652 tt := c.table.type_to_str(to_type)
5653 c.error('cannot cast string to `${tt}`, use `${snexpr}.${final_to_sym.name}()` instead.',
5654 node.pos)
5655 } else if final_from_sym.kind == .string && final_to_is_ptr && to_sym.kind != .string {
5656 snexpr := node.expr.str()
5657 tt := c.table.type_to_str(to_type)
5658 c.error('cannot cast string to `${tt}`, use `${snexpr}.str` instead.', node.pos)
5659 } else if final_from_sym.kind == .string && to_sym.kind == .char {
5660 snexpr := node.expr.str()
5661 tt := c.table.type_to_str(to_type)
5662 c.error('cannot cast string to `${tt}`, use `${snexpr}[index]` instead.', node.pos)
5663 } else if final_from_sym.kind == .string && to_type.is_voidptr()
5664 && !node.expr_type.has_flag(.generic) && !from_type.is_ptr() {
5665 c.error('cannot cast string to `voidptr`, use voidptr(s.str) instead', node.pos)
5666 } else if final_from_sym.kind == .string && to_type.is_pointer() && !c.inside_unsafe {
5667 tt := c.table.type_to_str(to_type)
5668 c.error('cannot cast string to `${tt}` outside `unsafe`, use ${tt}(s.str) instead',
5669 node.pos)
5670 } else if final_from_sym.kind == .array && !from_type.is_ptr() && to_type != ast.string_type
5671 && !(to_type.has_flag(.option) && from_type.idx() == to_type.idx()) {
5672 ft := c.table.type_to_str(from_type)
5673 tt := c.table.type_to_str(to_type)
5674 c.error('cannot cast array `${ft}` to `${tt}`', node.pos)
5675 } else if from_type.has_flag(.option) && to_type.has_flag(.option)
5676 && to_sym.kind != final_from_sym.kind {
5677 ft := c.table.type_to_str(from_type)
5678 tt := c.table.type_to_str(to_type)
5679 c.error('cannot cast incompatible option ${final_to_sym.name} `${ft}` to `${tt}`', node.pos)
5680 } else if to_sym.kind == .rune && from_sym.is_string() {
5681 snexpr := node.expr.str()
5682 ft := c.table.type_to_str(from_type)
5683 c.error('cannot cast `${ft}` to rune, use `${snexpr}.runes()` instead.', node.pos)
5684 } else if !from_type.is_ptr() && from_type != ast.string_type
5685 && final_from_sym.info is ast.Struct && !final_from_sym.info.is_empty_struct()
5686 && (final_to_sym.is_int() || final_to_sym.is_float()) {
5687 ft := c.table.type_to_str(from_type)
5688 tt := c.table.type_to_str(to_type)
5689 c.error('cannot cast type `${ft}` to `${tt}`', node.pos)
5690 }
5691
5692 if to_type.is_ptr() && !c.inside_unsafe && !c.pref.translated && !c.file.is_translated
5693 && to_type_exists && final_to_sym.kind != .placeholder {
5694 tt := c.table.type_to_str(to_type)
5695 if from_type.is_number() && from_sym.language != .c {
5696 ne_name := node.expr.str()
5697 if to_sym.kind != .struct && !ne_name.starts_with('C.') {
5698 if c.expr_is_known_zero_integer(node.expr) {
5699 c.error('cannot null cast a pointer, use ${tt}(unsafe { nil })', node.pos)
5700 } else {
5701 c.error('cannot cast a number to `${tt}` outside `unsafe`', node.pos)
5702 }
5703 }
5704 }
5705 }
5706
5707 // T(0) where T is array or map
5708 if node.typ.has_flag(.generic) && to_sym.kind in [.array, .map, .array_fixed]
5709 && node.expr.is_literal() {
5710 c.error('cannot cast literal value to ${to_sym.name} type', node.pos)
5711 }
5712
5713 if to_sym.kind == .enum && !(c.inside_unsafe || c.file.is_translated) && from_sym.is_int() {
5714 c.error('casting numbers to enums, should be done inside `unsafe{}` blocks', node.pos)
5715 }
5716
5717 if final_to_sym.kind == .function && final_from_sym.kind == .function && !(c.inside_unsafe
5718 || c.file.is_translated) && !c.check_matching_function_symbols(final_from_sym, final_to_sym) {
5719 c.error('casting a function value from one function signature, to another function signature, should be done inside `unsafe{}` blocks',
5720 node.pos)
5721 } else if final_to_sym.kind == .function && final_from_sym.kind != .function {
5722 if to_type.has_flag(.option) && node.expr !is ast.None {
5723 c.error('casting number to Option function is not allowed, only compatible function or `none`',
5724 node.pos)
5725 } else if !(c.inside_unsafe || c.file.is_translated) {
5726 if node.expr is ast.IntegerLiteral {
5727 c.warn('casting number to function value should be done inside `unsafe{}` blocks',
5728 node.pos)
5729 } else if node.expr is ast.Nil {
5730 c.warn('casting `nil` to function value should be done inside `unsafe{}` blocks',
5731 node.pos)
5732 } else if node.expr is ast.None {
5733 if from_type.has_flag(.option) {
5734 c.warn('cannot pass `none` to a non Option function type', node.pos)
5735 }
5736 } else if final_from_sym.kind != .voidptr {
5737 c.error('invalid casting value to function', node.pos)
5738 }
5739 }
5740 } else if final_from_sym.kind == .function && final_to_sym.is_number() {
5741 fnexpr := node.expr.str()
5742 tt := c.table.type_to_str(to_type)
5743 c.error('cannot cast function `${fnexpr}` to `${tt}`', node.pos)
5744 }
5745 if to_type.is_ptr() && to_sym.kind == .alias && from_sym.kind == .map {
5746 c.error('cannot cast to alias pointer `${c.table.type_to_str(to_type)}` because `${c.table.type_to_str(from_type)}` is a value',
5747 node.pos)
5748 }
5749
5750 if to_type == ast.string_type {
5751 if from_type in [ast.u8_type, ast.bool_type] {
5752 snexpr := node.expr.str()
5753 ft := c.table.type_to_str(from_type)
5754 c.error('cannot cast type `${ft}` to string, use `${snexpr}.str()` instead.', node.pos)
5755 } else if from_type.is_any_kind_of_pointer() {
5756 snexpr := node.expr.str()
5757 ft := c.table.type_to_str(from_type)
5758 c.error('cannot cast pointer type `${ft}` to string, use `&u8(${snexpr}).vstring()` or `cstring_to_vstring(${snexpr})` instead.',
5759 node.pos)
5760 } else if from_type.is_number() {
5761 snexpr := node.expr.str()
5762 c.error('cannot cast number to string, use `${snexpr}.str()` instead.', node.pos)
5763 } else if from_sym.kind == .alias && final_from_sym.name != 'string' {
5764 ft := c.table.type_to_str(from_type)
5765 c.error('cannot cast type `${ft}` to string, use `x.str()` instead.', node.pos)
5766 } else if final_from_sym.kind == .array {
5767 snexpr := node.expr.str()
5768 if final_from_sym.name == '[]u8' {
5769 c.error('cannot cast []u8 to string, use `${snexpr}.bytestr()` or `${snexpr}.str()` instead.',
5770 node.pos)
5771 } else {
5772 first_elem_idx := '[0]'
5773 c.error('cannot cast array to string, use `${snexpr}${first_elem_idx}.str()` instead.',
5774 node.pos)
5775 }
5776 } else if final_from_sym.kind == .enum {
5777 snexpr := node.expr.str()
5778 c.error('cannot cast enum to string, use ${snexpr}.str() instead.', node.pos)
5779 } else if final_from_sym.kind == .map {
5780 c.error('cannot cast map to string.', node.pos)
5781 } else if final_from_sym.kind == .sum_type {
5782 snexpr := node.expr.str()
5783 ft := c.table.type_to_str(from_type)
5784 c.error('cannot cast sumtype `${ft}` to string, use `${snexpr}.str()` instead.',
5785 node.pos)
5786 } else if final_from_sym.kind == .function {
5787 fnexpr := node.expr.str()
5788 c.error('cannot cast function `${fnexpr}` to string', node.pos)
5789 } else if to_type != ast.string_type && from_type == ast.string_type
5790 && (!(to_sym.kind == .alias && final_to_sym.name == 'string')) {
5791 mut error_msg := 'cannot cast a string to a type `${final_to_sym.name}`, that is not an alias of string'
5792 if mut node.expr is ast.StringLiteral {
5793 if node.expr.val.len == 1 {
5794 error_msg += ", for denoting characters use `${node.expr.val}` instead of '${node.expr.val}'"
5795 }
5796 }
5797 c.error(error_msg, node.pos)
5798 }
5799 } else if to_type.is_int() && mut node.expr is ast.IntegerLiteral {
5800 tt := c.table.type_to_str(to_type)
5801 tsize, _ := c.table.type_size(to_type.idx_type())
5802 bit_size := tsize * 8
5803 signed := node.expr.val[0] == `-`
5804 value_string := match node.expr.val[0] {
5805 `-`, `+` {
5806 node.expr.val[1..]
5807 }
5808 else {
5809 node.expr.val
5810 }
5811 }
5812
5813 mut is_overflowed := false
5814 v, e := strconv.common_parse_uint2(value_string, 0, bit_size)
5815 match e {
5816 0 {}
5817 -3 {
5818 // FIXME: Once integer literal is considered as hard error, remove this warn and migrate to later error
5819 c.error('value `${node.expr.val}` overflows `${tt}`', node.pos)
5820 is_overflowed = true
5821 }
5822 else {
5823 c.error('cannot cast value `${node.expr.val}` to `${tt}`', node.pos)
5824 }
5825 }
5826
5827 // checks if integer literal's most significant bit
5828 // alters sign bit when casting to signed integer
5829 if !is_overflowed && to_type.is_signed() {
5830 signed_one := match to_type.idx() {
5831 ast.char_type_idx, ast.i8_type_idx {
5832 u64(0xff)
5833 }
5834 ast.i16_type_idx {
5835 u64(0xffff)
5836 }
5837 ast.i32_type_idx {
5838 u64(0xffffffff)
5839 }
5840 ast.int_type_idx {
5841 $if new_int ? && x64 {
5842 u64(0xffffffffffffffff)
5843 } $else {
5844 u64(0xffffffff)
5845 }
5846 }
5847 ast.i64_type_idx {
5848 u64(0xffffffffffffffff)
5849 }
5850 ast.isize_type_idx {
5851 $if x64 {
5852 u64(0xffffffffffffffff)
5853 } $else {
5854 u64(0xffffffff)
5855 }
5856 }
5857 else {
5858 c.error('ICE: Not a valid signed type', node.pos)
5859 0
5860 }
5861 }
5862
5863 max_signed := (u64(1) << (bit_size - 1)) - 1
5864
5865 is_overflowed = v == signed_one || (signed && v - 2 == max_signed)
5866 || (!signed && v - 1 == max_signed)
5867 // FIXME: Once integer literal is considered as hard error, remove this warn and migrate to later error
5868 if is_overflowed {
5869 c.warn('value `${node.expr.val}` overflows `${tt}`, this will be considered hard error soon',
5870 node.pos)
5871 }
5872 }
5873
5874 // FIXME: Once integer literal is considered as hard error, uncomment this
5875 // if is_overflowed {
5876 // c.error('value `${node.expr.val}` overflows `${tt}`', node.pos)
5877 // }
5878 } else if to_type.is_float() && mut node.expr is ast.FloatLiteral {
5879 tt := c.table.type_to_str(to_type)
5880 strconv.atof64(node.expr.val) or {
5881 c.error('cannot cast value `${node.expr.val}` to `${tt}`', node.pos)
5882 }
5883 }
5884 if from_sym.language == .v && !from_type.is_ptr()
5885 && final_from_sym.kind in [.sum_type, .interface]
5886 && final_to_sym.kind !in [.sum_type, .interface] {
5887 ft := c.table.type_to_str(from_type)
5888 tt := c.table.type_to_str(to_type)
5889 kind_name := if from_sym.kind == .sum_type { 'sum type' } else { 'interface' }
5890 c.error('cannot cast `${ft}` ${kind_name} value to `${tt}`, use `${node.expr} as ${tt}` instead',
5891 node.pos)
5892 }
5893 if from_sym.language == .v && from_type.is_ptr() && !to_type.is_ptr() && !final_to_type.is_ptr()
5894 && !node.expr.is_auto_deref_var() && final_to_sym.kind == .struct
5895 && final_from_sym.kind == .struct {
5896 if c.check_struct_signature(final_from_sym.info as ast.Struct,
5897 final_to_sym.info as ast.Struct)
5898 {
5899 ft := c.table.type_to_str(from_type)
5900 tt := c.table.type_to_str(to_type)
5901 c.error('cannot cast `${ft}` to `${tt}`, you must dereference it first (e.g. ${tt}(*var))',
5902 node.pos)
5903 }
5904 }
5905
5906 if node.has_arg {
5907 c.expr(mut node.arg)
5908 }
5909
5910 // checks on int literal to enum cast if the value represents a value on the enum
5911 if to_sym.kind == .enum {
5912 if mut node.expr is ast.IntegerLiteral {
5913 enum_typ_name := c.table.get_type_name(to_type)
5914 node_val := node.expr.val.i64()
5915
5916 if enum_decl := c.table.enum_decls[to_sym.name] {
5917 mut in_range := false
5918 if enum_decl.is_flag {
5919 if enum_decl.fields.len == 64 {
5920 // for 64 fields, just use max_u64 and avoid UB:
5921 in_range = node_val >= 0 && u64(node_val) <= max_u64
5922 } else {
5923 // if a flag enum has 4 variants, the maximum possible value would have all 4 flags set (0b1111)
5924 max_val := (u64(1) << enum_decl.fields.len) - 1
5925 in_range = node_val >= 0 && u64(node_val) <= max_val
5926 }
5927 } else {
5928 mut enum_val := i64(0)
5929
5930 for enum_field in enum_decl.fields {
5931 // check if the field of the enum value is an integer literal
5932 if enum_field.expr is ast.IntegerLiteral {
5933 enum_val = enum_field.expr.val.i64()
5934 } else if comptime_value := c.eval_comptime_const_expr(enum_field.expr, 0) {
5935 enum_val = comptime_value.i64() or { -1 }
5936 }
5937 if node_val == enum_val {
5938 in_range = true
5939 break
5940 }
5941 enum_val += 1
5942 }
5943 }
5944
5945 if !in_range {
5946 c.warn('${node_val} does not represent a value of enum ${enum_typ_name}',
5947 node.pos)
5948 }
5949 }
5950 }
5951 if node.expr_type == ast.string_type_idx && !c.skip_flags {
5952 c.add_error_detail('use ${c.table.type_to_str(node.typ)}.from_string(${node.expr}) instead')
5953 c.error('cannot cast `string` to `enum`', node.pos)
5954 }
5955 }
5956
5957 if c.pref.warn_about_allocs && to_sym.info is ast.Interface {
5958 c.warn_alloc('cast to interface', node.pos)
5959 }
5960 node.typname = c.table.sym(node.typ).name
5961 return node.typ
5962}
5963
5964fn (mut c Checker) at_expr(mut node ast.AtExpr) ast.Type {
5965 match node.kind {
5966 .fn_name {
5967 if c.table.cur_fn == unsafe { nil } {
5968 return ast.void_type
5969 }
5970 if c.table.cur_fn.is_static_type_method {
5971 node.val = c.table.cur_fn.name.all_after_last('__static__')
5972 } else {
5973 node.val = c.table.cur_fn.name.all_after_last('.')
5974 }
5975 }
5976 .method_name {
5977 if c.table.cur_fn == unsafe { nil } {
5978 return ast.void_type
5979 }
5980 fname := c.table.cur_fn.name.all_after_last('.')
5981 if c.table.cur_fn.is_method {
5982 node.val = c.table.type_to_str(c.table.cur_fn.receiver.typ).all_after_last('.') +
5983 '.' + fname
5984 } else if c.table.cur_fn.is_static_type_method {
5985 node.val = fname.all_before('__static__') + '.' + fname.all_after('__static__')
5986 } else {
5987 node.val = fname
5988 }
5989 }
5990 .mod_name {
5991 if c.table.cur_fn == unsafe { nil } {
5992 return ast.void_type
5993 }
5994 node.val = c.table.cur_fn.mod
5995 }
5996 .struct_name {
5997 if c.table.cur_fn.is_method || c.table.cur_fn.is_static_type_method {
5998 node.val = c.table.type_to_str(c.table.cur_fn.receiver.typ).all_after_last('.')
5999 } else {
6000 node.val = ''
6001 }
6002 }
6003 .vexe_path {
6004 node.val = pref.vexe_path()
6005 }
6006 .file_path {
6007 node.val = os.real_path(c.file.path)
6008 }
6009 .file_dir {
6010 node.val = os.real_path(os.dir(c.file.path))
6011 }
6012 .line_nr {
6013 node.val = (node.pos.line_nr + 1).str()
6014 }
6015 .file_path_line_nr {
6016 node.val = os.file_name(c.file.path) + ':' + (node.pos.line_nr + 1).str()
6017 }
6018 .column_nr {
6019 node.val = (node.pos.col + 1).str()
6020 }
6021 .location {
6022 mut mname := 'unknown'
6023 if c.table.cur_fn != unsafe { nil } {
6024 if c.table.cur_fn.is_method {
6025 mname = c.table.type_to_str(c.table.cur_fn.receiver.typ) + '{}.' +
6026 c.table.cur_fn.name.all_after_last('.')
6027 } else {
6028 mname = c.table.cur_fn.name
6029 }
6030 if c.table.cur_fn.is_static_type_method {
6031 mname = mname.replace('__static__', '.') + ' (static)'
6032 }
6033 }
6034 node.val = c.file.path + ':' + (node.pos.line_nr + 1).str() + ', ${mname}'
6035 }
6036 .vhash {
6037 node.val = version.vhash()
6038 }
6039 .v_current_hash {
6040 node.val = c.v_current_commit_hash
6041 }
6042 .vmod_file {
6043 // cache the vmod content, do not read it many times
6044 if c.vmod_file_content.len == 0 {
6045 mut mcache := vmod.get_cache()
6046 vmod_file_location := mcache.get_by_file(c.file.path)
6047 if vmod_file_location.vmod_file.len == 0 {
6048 c.error('@VMOD_FILE can only be used in projects that have a v.mod file',
6049 node.pos)
6050 }
6051 vmod_content := os.read_file(vmod_file_location.vmod_file) or { '' }
6052 c.vmod_file_content =
6053 vmod_content.replace('\r\n', '\n') // normalise EOLs just in case
6054 }
6055 node.val = c.vmod_file_content
6056 }
6057 .vroot_path {
6058 node.val = c.pref.vroot
6059 }
6060 .vexeroot_path {
6061 node.val = c.pref.vroot
6062 }
6063 .vmodroot_path {
6064 mut mcache := vmod.get_cache()
6065 vmod_file_location := mcache.get_by_file(c.file.path)
6066 node.val = os.dir(vmod_file_location.vmod_file)
6067 }
6068 .vmod_hash {
6069 mut mcache := vmod.get_cache()
6070 vmod_file_location := mcache.get_by_file(c.file.path)
6071 if vmod_file_location.vmod_file.len == 0 {
6072 c.error('@VMODHASH can only be used in projects that have a v.mod file', node.pos)
6073 }
6074 hash := version.githash(os.dir(vmod_file_location.vmod_file)) or {
6075 c.error(err.msg(), node.pos)
6076 ''
6077 }
6078 node.val = hash
6079 }
6080 .build_date {
6081 node.val = util.stable_build_time.strftime('%Y-%m-%d')
6082 }
6083 .build_time {
6084 node.val = util.stable_build_time.strftime('%H:%M:%S')
6085 }
6086 .build_timestamp {
6087 node.val = util.stable_build_time.unix().str()
6088 }
6089 .os {
6090 node.val = pref.get_host_os().lower()
6091 }
6092 .ccompiler {
6093 node.val = c.pref.ccompiler_type.str()
6094 }
6095 .backend {
6096 node.val = c.pref.backend.str()
6097 }
6098 .platform {
6099 node.val = c.pref.arch.str()
6100 }
6101 .unknown {
6102 c.error('unknown @ identifier: ${node.name}. Available identifiers: ${token.valid_at_tokens}',
6103 node.pos)
6104 }
6105 }
6106
6107 return ast.string_type
6108}
6109
6110fn (mut c Checker) same_inferred_fn_value_type(left ast.Type, right ast.Type) bool {
6111 if left == right {
6112 return true
6113 }
6114 if left.nr_muls() != right.nr_muls() {
6115 return false
6116 }
6117 if left.has_flag(.option) != right.has_flag(.option) {
6118 return false
6119 }
6120 if left.has_flag(.result) != right.has_flag(.result) {
6121 return false
6122 }
6123 if left.has_flag(.variadic) != right.has_flag(.variadic) {
6124 return false
6125 }
6126 if left.share() != right.share() {
6127 return false
6128 }
6129 left_base := c.table.unaliased_type(left.clear_flags(.option, .result, .variadic, .shared_f,
6130 .atomic_f).clear_ref())
6131 right_base := c.table.unaliased_type(right.clear_flags(.option, .result, .variadic, .shared_f,
6132 .atomic_f).clear_ref())
6133 return left_base == right_base
6134}
6135
6136fn (mut c Checker) bind_inferred_fn_value_type(mut inferred map[string]ast.Type, generic_name string, concrete_type ast.Type) bool {
6137 if generic_name in inferred {
6138 return c.same_inferred_fn_value_type(inferred[generic_name], concrete_type)
6139 }
6140 inferred[generic_name] = concrete_type
6141 return true
6142}
6143
6144fn (mut c Checker) infer_fn_value_concrete_type(mut inferred map[string]ast.Type, generic_names []string, generic_type ast.Type, concrete_type ast.Type) bool {
6145 if generic_type.has_flag(.option) != concrete_type.has_flag(.option) {
6146 return false
6147 }
6148 if generic_type.has_flag(.result) != concrete_type.has_flag(.result) {
6149 return false
6150 }
6151 if generic_type.has_flag(.variadic) != concrete_type.has_flag(.variadic) {
6152 return false
6153 }
6154 if generic_type.share() != concrete_type.share() {
6155 return false
6156 }
6157 mut template_type := generic_type.clear_flags(.option, .result, .variadic, .shared_f, .atomic_f)
6158 mut actual_type := concrete_type.clear_flags(.option, .result, .variadic, .shared_f, .atomic_f)
6159 template_sym := c.table.sym(template_type)
6160 if template_sym.name in generic_names {
6161 if template_type.nr_muls() > actual_type.nr_muls() {
6162 return false
6163 }
6164 inferred_type := if template_type.nr_muls() > 0 {
6165 actual_type.set_nr_muls(actual_type.nr_muls() - template_type.nr_muls())
6166 } else {
6167 actual_type
6168 }
6169 return c.bind_inferred_fn_value_type(mut inferred, template_sym.name, inferred_type)
6170 }
6171 if template_type.nr_muls() != actual_type.nr_muls() {
6172 return false
6173 }
6174 template_type = template_type.clear_ref()
6175 actual_type = actual_type.clear_ref()
6176 template_final_sym := c.table.final_sym(template_type)
6177 actual_final_sym := c.table.final_sym(actual_type)
6178 match template_final_sym.info {
6179 ast.Array {
6180 if actual_final_sym.info !is ast.Array {
6181 return false
6182 }
6183 template_array := template_final_sym.info as ast.Array
6184 actual_array := actual_final_sym.info as ast.Array
6185 if template_array.nr_dims != actual_array.nr_dims {
6186 return false
6187 }
6188 return c.infer_fn_value_concrete_type(mut inferred, generic_names,
6189 template_array.elem_type, actual_array.elem_type)
6190 }
6191 ast.ArrayFixed {
6192 if actual_final_sym.info !is ast.ArrayFixed {
6193 return false
6194 }
6195 template_array_fixed := template_final_sym.info as ast.ArrayFixed
6196 actual_array_fixed := actual_final_sym.info as ast.ArrayFixed
6197 if template_array_fixed.size != actual_array_fixed.size {
6198 return false
6199 }
6200 return c.infer_fn_value_concrete_type(mut inferred, generic_names,
6201 template_array_fixed.elem_type, actual_array_fixed.elem_type)
6202 }
6203 ast.Chan {
6204 if actual_final_sym.info !is ast.Chan {
6205 return false
6206 }
6207 template_chan := template_final_sym.info as ast.Chan
6208 actual_chan := actual_final_sym.info as ast.Chan
6209 return c.infer_fn_value_concrete_type(mut inferred, generic_names,
6210 template_chan.elem_type, actual_chan.elem_type)
6211 }
6212 ast.Map {
6213 if actual_final_sym.info !is ast.Map {
6214 return false
6215 }
6216 template_map := template_final_sym.info as ast.Map
6217 actual_map := actual_final_sym.info as ast.Map
6218 return
6219 c.infer_fn_value_concrete_type(mut inferred, generic_names, template_map.key_type, actual_map.key_type)
6220 && c.infer_fn_value_concrete_type(mut inferred, generic_names, template_map.value_type, actual_map.value_type)
6221 }
6222 ast.Thread {
6223 if actual_final_sym.info !is ast.Thread {
6224 return false
6225 }
6226 template_thread := template_final_sym.info as ast.Thread
6227 actual_thread := actual_final_sym.info as ast.Thread
6228 return c.infer_fn_value_concrete_type(mut inferred, generic_names,
6229 template_thread.return_type, actual_thread.return_type)
6230 }
6231 ast.FnType {
6232 if actual_final_sym.info !is ast.FnType {
6233 return false
6234 }
6235 template_fn := (template_final_sym.info as ast.FnType).func
6236 actual_fn := (actual_final_sym.info as ast.FnType).func
6237 if template_fn.params.len != actual_fn.params.len
6238 || template_fn.is_variadic != actual_fn.is_variadic
6239 || template_fn.is_c_variadic != actual_fn.is_c_variadic {
6240 return false
6241 }
6242 for i, template_param in template_fn.params {
6243 actual_param := actual_fn.params[i]
6244 if template_param.is_mut != actual_param.is_mut {
6245 return false
6246 }
6247 if !c.infer_fn_value_concrete_type(mut inferred, generic_names, template_param.typ,
6248 actual_param.typ) {
6249 return false
6250 }
6251 }
6252 return c.infer_fn_value_concrete_type(mut inferred, generic_names,
6253 template_fn.return_type, actual_fn.return_type)
6254 }
6255 else {
6256 return c.table.unaliased_type(template_type) == c.table.unaliased_type(actual_type)
6257 }
6258 }
6259}
6260
6261fn (mut c Checker) infer_fn_value_concrete_types(func &ast.Fn, expected_type ast.Type) ?[]ast.Type {
6262 if expected_type in [0, ast.void_type] {
6263 return none
6264 }
6265 expected_sym := c.table.final_sym(expected_type)
6266 if expected_sym.kind != .function || expected_sym.info !is ast.FnType {
6267 return none
6268 }
6269 expected_fn := (expected_sym.info as ast.FnType).func
6270 if func.params.len != expected_fn.params.len || func.is_variadic != expected_fn.is_variadic
6271 || func.is_c_variadic != expected_fn.is_c_variadic {
6272 return none
6273 }
6274 mut inferred := map[string]ast.Type{}
6275 for i, param in func.params {
6276 expected_param := expected_fn.params[i]
6277 if param.is_mut != expected_param.is_mut {
6278 return none
6279 }
6280 if !c.infer_fn_value_concrete_type(mut inferred, func.generic_names, param.typ,
6281 expected_param.typ) {
6282 return none
6283 }
6284 }
6285 if !c.infer_fn_value_concrete_type(mut inferred, func.generic_names, func.return_type,
6286 expected_fn.return_type) {
6287 return none
6288 }
6289 mut concrete_types := []ast.Type{cap: func.generic_names.len}
6290 for generic_name in func.generic_names {
6291 if generic_name !in inferred {
6292 return none
6293 }
6294 concrete_types << inferred[generic_name]
6295 }
6296 return concrete_types
6297}
6298
6299fn (mut c Checker) infer_ident_fn_value_concrete_types(func &ast.Fn, mut node ast.Ident) {
6300 if func.generic_names.len == 0 || node.concrete_types.len > 0 {
6301 return
6302 }
6303 if concrete_types := c.infer_fn_value_concrete_types(func, c.expected_type) {
6304 node.concrete_types = concrete_types
6305 }
6306}
6307
6308fn (mut c Checker) resolve_var_fn(func &ast.Fn, mut node ast.Ident, name string) ast.Type {
6309 if func.generic_names.len > 0 && node.concrete_types.len == 0 {
6310 c.infer_ident_fn_value_concrete_types(func, mut node)
6311 }
6312 mut fn_type := c.table.find_or_register_fn_type(func, false, true)
6313 if fn_type < 0 {
6314 mut f := ast.Fn{
6315 ...func
6316 }
6317 f.name = ''
6318 fn_type = c.table.find_or_register_fn_type(f, false, true)
6319 }
6320 if func.generic_names.len > 0 {
6321 concrete_types := node.concrete_types.map(c.unwrap_generic(it))
6322 if typ_ := c.table.convert_generic_type(fn_type, func.generic_names, concrete_types) {
6323 fn_type = typ_
6324 if concrete_types.all(!it.has_flag(.generic)) {
6325 c.table.register_fn_concrete_types(func.fkey(), concrete_types)
6326 }
6327 }
6328 }
6329 node.name = name
6330 node.kind = .function
6331 node.info = ast.IdentFn{
6332 typ: fn_type
6333 }
6334 c.mark_fn_decl_as_referenced(func.fkey())
6335 return fn_type
6336}
6337
6338fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
6339 defer {
6340 if c.pref.is_vls {
6341 c.ident_autocomplete(node)
6342 }
6343 }
6344 // TODO: move this
6345 if c.const_deps.len > 0 {
6346 mut name := node.name
6347 if !name.contains('.') && node.mod != 'builtin' {
6348 name = '${node.mod}.${node.name}'
6349 }
6350 // detect cycles, while allowing for references to the same constant,
6351 // used inside its initialisation like: `struct Abc { x &Abc } ... const a = [ Abc{0}, Abc{unsafe{&a[0]}} ]!`
6352 // see vlib/v/tests/const_fixed_array_containing_references_to_itself_test.v
6353 if unsafe { c.const_var != 0 } && name == c.const_var.name {
6354 if mut c.const_var.expr is ast.ArrayInit && c.const_var.expr.is_fixed
6355 && c.expected_type.nr_muls() > 0 {
6356 elem_typ := c.expected_type.deref()
6357 node.kind = .constant
6358 node.name = c.const_var.name
6359 node.info = ast.IdentVar{
6360 typ: elem_typ
6361 }
6362 // c.const_var.typ = elem_typ
6363 node.obj = c.const_var
6364 return c.expected_type
6365 }
6366 c.error('cycle in constant `${c.const_var.name}`', node.pos)
6367 return ast.void_type
6368 }
6369 c.const_deps << name
6370 }
6371 if node.kind == .blank_ident {
6372 if node.tok_kind !in [.assign, .decl_assign] {
6373 c.error('undefined ident: `_` (may only be used in assignments)', node.pos)
6374 }
6375 return ast.void_type
6376 } else if node.kind in [.constant, .global, .variable] {
6377 if node.kind == .constant {
6378 c.mark_const_decl_as_referenced(node.full_name())
6379 }
6380 // second use
6381 info := node.info as ast.IdentVar
6382 mut info_typ := info.typ
6383 if node.kind == .variable {
6384 if mut current_var := node.scope.find_var(node.name) {
6385 c.refresh_generic_scope_var_type_for_use(mut current_var, node.pos.pos)
6386 if current_var.typ != 0 {
6387 info_typ = current_var.typ
6388 }
6389 if current_var.smartcasts.len > 0 && !c.prevent_sum_type_unwrapping_once {
6390 info_typ = c.visible_var_type_for_read(current_var)
6391 }
6392 c.prevent_sum_type_unwrapping_once = false
6393 node.info = ast.IdentVar{
6394