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