v2 / vlib / v / checker / checker.v
9140 lines · 8869 sloc · 297.23 KB · 9f57c744dac88fb52dfea3775961d6d57e2067fc
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
3module checker
4
5import os
6import strconv
7import v.ast
8import v.vmod
9import v.token
10import v.pref
11import v.util
12import v.util.version
13import v.errors
14import v.pkgconfig
15import v.transformer
16import v.type_resolver
17
18// prevent stack overflows by restricting too deep recursion:
19const expr_level_cutoff_limit = 40
20const stmt_level_cutoff_limit = 40
21const type_level_cutoff_limit = 40 // it is very rarely deeper than 4
22const iface_level_cutoff_limit = 100
23const generic_fn_cutoff_limit_per_fn = 10_000 // how many times post_process_generic_fns, can visit the same function before bailing out
24
25const generic_fn_postprocess_iterations_cutoff_limit = 1_000_000
26
27fn has_ascii_upper(s string) bool {
28 for ch in s {
29 if ch >= `A` && ch <= `Z` {
30 return true
31 }
32 }
33 return false
34}
35
36// array_builtin_methods contains a list of all methods on array, that return other typed arrays.
37// i.e. that act as *pseudogeneric* methods, that need compiler support, so that the types of the results
38// are properly checked.
39// Note that methods that do not return anything, or that return known types, are not listed here, since they are just ordinary non generic methods.
40pub const array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort',
41 'sort_with_compare', 'sorted', 'sorted_with_compare', 'contains', 'index', 'last_index', 'wait',
42 'any', 'all', 'first', 'last', 'get', 'pop_left', 'pop', 'delete', 'insert', 'prepend', 'count']
43pub const array_builtin_methods_chk = token.new_keywords_matcher_from_array_trie(array_builtin_methods)
44pub const fixed_array_builtin_methods = ['contains', 'index', 'last_index', 'any', 'all', 'wait',
45 'map', 'sort', 'sorted', 'sort_with_compare', 'sorted_with_compare', 'reverse',
46 'reverse_in_place', 'count', 'filter']
47pub const fixed_array_builtin_methods_chk = token.new_keywords_matcher_from_array_trie(fixed_array_builtin_methods)
48// TODO: remove `byte` from this list when it is no longer supported
49pub const reserved_type_names = ['bool', 'char', 'i8', 'i16', 'i32', 'int', 'i64', 'u8', 'u16',
50 'u32', 'u64', 'f32', 'f64', 'map', 'string', 'rune', 'usize', 'isize', 'voidptr', 'thread']
51pub const reserved_type_names_chk = token.new_keywords_matcher_from_array_trie(reserved_type_names)
52pub const vroot_is_deprecated_message = '@VROOT is deprecated, use @VMODROOT or @VEXEROOT instead'
53
54struct AssertAutocast {
55 from_type ast.Type
56 to_type ast.Type
57}
58
59@[heap; minify]
60pub struct Checker {
61pub mut:
62 pref &pref.Preferences = unsafe { nil } // Preferences shared from V struct
63
64 table &ast.Table = unsafe { nil }
65 file &ast.File = unsafe { nil }
66
67 nr_errors int
68 nr_warnings int
69 nr_notices int
70 errors []errors.Error
71 warnings []errors.Warning
72 notices []errors.Notice
73 error_lines map[string]bool // dedup errors
74 warning_lines map[string]bool // dedup warns
75 notice_lines map[string]bool // dedup notices
76 error_details []string
77 should_abort bool // when too many errors are accumulated, .should_abort becomes true. It is checked in statement/expression loops, so the checker can return early, instead of wasting time.
78
79 expected_type ast.Type
80 expected_or_type ast.Type // fn() or { 'this type' } eg. string. expected or block type
81 expected_expr_type ast.Type // if/match is_expr: expected_type
82 mod string // current module name
83 has_globals_in_module bool // true if the current module has @[has_globals] attribute
84 strict_map_index_in_module bool // true if the current module has @[strict_map_index] attribute
85 const_var &ast.ConstField = unsafe { nil } // the current constant, when checking const declarations
86 const_deps []string
87 const_names []string
88 global_names []string
89 locked_names []string // vars that are currently locked
90 rlocked_names []string // vars that are currently read-locked
91 in_for_count int // if checker is currently in a for loop
92 returns bool
93 scope_returns bool
94 is_builtin_mod bool // true inside the 'builtin', 'os' or 'strconv' modules; TODO: remove the need for special casing this
95 is_just_builtin_mod bool // true only inside 'builtin'
96 is_generated bool // true for `@[generated] module xyz` .v files
97 unresolved_fixed_sizes []&ast.Stmt // funcs with unresolved array fixed size e.g. fn func() [const1]int
98 inside_recheck bool // true when rechecking rhs assign statement
99 inside_unsafe bool // true inside `unsafe {}` blocks
100 inside_const bool // true inside `const ( ... )` blocks
101 inside_anon_fn bool // true inside `fn() { ... }()`
102 inside_lambda bool // true inside `|...| ...`
103 inside_ref_lit bool // true inside `a := &something`
104 inside_defer bool // true inside `defer {}` blocks
105 inside_return bool // true inside `return ...` blocks
106 inside_fn_arg bool // `a`, `b` in `a.f(b)`
107 inside_ct_attr bool // true inside `[if expr]`
108 inside_x_is_type bool // true inside the Type expression of `if x is Type {`
109 inside_x_matches_type bool // true inside the match branch of `match x.type { Type {} }`
110 anon_struct_should_be_mut bool // true when `mut var := struct { ... }` is used
111 inside_generic_struct_init bool
112 inside_integer_literal_cast bool // true inside `int(123)`
113 cur_struct_generic_types []ast.Type
114 cur_struct_concrete_types []ast.Type
115 anon_fn_generic_names []string
116 anon_fn_concrete_types []ast.Type
117 skip_flags bool // should `#flag` and `#include` be skipped
118 fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
119 smartcast_mut_pos token.Pos // match mut foo, if mut foo is Foo
120 smartcast_cond_pos token.Pos // match cond
121 ct_cond_stack []ast.Expr
122 ct_user_defines map[string]bool
123 ct_system_defines map[string]bool
124 cur_ct_id int // id counter for $if $match branches
125mut:
126 stmt_level int // the nesting level inside each stmts list;
127 // .stmt_level is used to check for `evaluated but not used` ExprStmts like `1 << 1`
128 // 1 for statements directly at each inner scope level;
129 // increases for `x := if cond { statement_list1} else {statement_list2}`;
130 // increases for `x := optfn() or { statement_list3 }`;
131 // files []ast.File
132 expr_level int // to avoid infinite recursion segfaults due to compiler bugs
133 type_level int // to avoid infinite recursion segfaults due to compiler bugs in ensure_type_exists
134 ensure_generic_type_level int // to avoid infinite recursion segfaults in ensure_generic_type_specify_type_names
135 cur_orm_ts ast.TypeSymbol
136 cur_or_expr &ast.OrExpr = unsafe { nil }
137 cur_anon_fn &ast.AnonFn = unsafe { nil }
138 vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path**
139 loop_labels []string // filled, when inside labelled for loops: `a_label: for x in 0..10 {`
140 veb_gen_types []ast.Type // veb route checks
141 timers &util.Timers = util.get_timers()
142 type_resolver type_resolver.TypeResolver
143 comptime &type_resolver.ResolverInfo = unsafe { nil }
144 fn_scope &ast.Scope = unsafe { nil }
145 main_fn_decl_node ast.FnDecl
146 match_exhaustive_cutoff_limit int = 10
147 is_last_stmt bool
148 prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type, stopping unwrapping then
149 need_recheck_generic_fns bool // need recheck generic fns because there are cascaded nested generic fn
150 generic_fns map[string]bool // register generic fns that needs recheck once
151 inside_sql bool // to handle sql table fields pseudo variables
152 inside_selector_expr bool
153 inside_or_block_value bool // true inside or-block where its value is used `f(g() or { true })`
154 inside_interface_deref bool
155 inside_decl_rhs bool
156 inside_if_guard bool // true inside the guard condition of `if x := opt() {}`
157 inside_assign bool
158 assert_autocasts map[string]AssertAutocast
159 is_js_backend bool
160 // doing_line_info int // a quick single file run when called with v -line-info (contains line nr to inspect)
161 // doing_line_path string // same, but stores the path being parsed
162 is_index_assign bool
163 comptime_call_pos int // needed for correctly checking use before decl for templates
164 generic_call_positions map[string]token.Pos // map from generic function key to call position
165 goto_labels map[string]ast.GotoLabel // to check for unused goto labels
166 enum_data_type ast.Type
167 field_data_type ast.Type
168 variant_data_type ast.Type
169 fn_return_type ast.Type
170 orm_table_fields map[string][]ast.StructField // known table structs
171 short_module_names []string // to check for function names colliding with module functions
172 visible_param_mutation_cache map[string]bool
173 visible_param_mutation_in_progress map[string]bool
174 immutable_alias_analysis_in_progress map[string]bool
175 always_error_fn_cache map[string]bool
176 always_error_fn_in_progress map[string]bool
177 generic_parts_cache []i8 // type idx -> 0 unknown, 1 false, 2 true
178
179 v_current_commit_hash string // same as old C.V_CURRENT_COMMIT_HASH
180 assign_stmt_attr string // for `x := [1,2,3] @[freed]`
181
182 js_string ast.Type = ast.void_type // when `js"string literal"` is used, `js_string` will be equal to `JS.String`
183 checker_transformer &transformer.Transformer = unsafe { nil }
184}
185
186pub fn new_checker(table &ast.Table, pref_ &pref.Preferences) &Checker {
187 mut timers_should_print := false
188 $if time_checking ? {
189 timers_should_print = true
190 }
191 v_current_commit_hash := if pref_.building_v {
192 version.githash(pref_.vroot) or { '' }
193 } else {
194 vcurrent_hash()
195 }
196 mut checker := &Checker{
197 table: table
198 pref: pref_
199 timers: util.new_timers(
200 should_print: timers_should_print
201 label: 'checker'
202 )
203 match_exhaustive_cutoff_limit: pref_.checker_match_exhaustive_cutoff_limit
204 v_current_commit_hash: v_current_commit_hash
205 checker_transformer: transformer.new_transformer_with_table(table, pref_)
206 visible_param_mutation_cache: map[string]bool{}
207 visible_param_mutation_in_progress: map[string]bool{}
208 immutable_alias_analysis_in_progress: map[string]bool{}
209 always_error_fn_cache: map[string]bool{}
210 always_error_fn_in_progress: map[string]bool{}
211 generic_parts_cache: []i8{len: table.type_symbols.len}
212 }
213 checker.checker_transformer.skip_array_transform = true
214 checker.type_resolver = type_resolver.TypeResolver.new(table, checker)
215 checker.comptime = &checker.type_resolver.info
216 checker.is_js_backend = checker.pref.backend.is_js()
217 return checker
218}
219
220// build_generic_call_key builds a key for tracking generic function call positions
221fn (c &Checker) build_generic_call_key(fkey string, concrete_types []ast.Type) string {
222 mut types_str := ''
223 for typ in concrete_types {
224 types_str += c.table.type_to_str(typ) + ','
225 }
226 return '${fkey}[${types_str}]'
227}
228
229fn (mut c Checker) has_active_generic_recheck_context() bool {
230 if c.inside_generic_struct_init && c.cur_struct_concrete_types.len > 0 {
231 return true
232 }
233 if c.table.cur_fn == unsafe { nil } {
234 return false
235 }
236 if c.table.cur_concrete_types.len > 0 {
237 return true
238 }
239 if !c.table.cur_fn.is_method {
240 return false
241 }
242 receiver_typ := c.table.cur_fn.receiver.typ
243 if receiver_typ == 0 {
244 return false
245 }
246 if receiver_typ.has_flag(.generic) || c.type_has_unresolved_generic_parts(receiver_typ) {
247 return true
248 }
249 rec_sym := c.table.sym(c.unwrap_generic(receiver_typ))
250 match rec_sym.info {
251 ast.Struct, ast.Interface, ast.SumType {
252 return rec_sym.info.generic_types.len > 0 || rec_sym.info.concrete_types.len > 0
253 }
254 ast.GenericInst {
255 return rec_sym.info.concrete_types.len > 0
256 }
257 else {
258 return false
259 }
260 }
261}
262
263fn (mut c Checker) refresh_generic_scope_var_type_for_use(mut v ast.Var, use_pos int) ast.Type {
264 if v.is_arg || v.expr is ast.EmptyExpr || v.pos.pos <= 0 || v.pos.pos >= use_pos
265 || c.table.cur_fn == unsafe { nil } || !c.has_active_generic_recheck_context() {
266 return v.typ
267 }
268 $if trace_ci_fixes ? {
269 if c.file.path.contains('/datatypes/linked_list.v') {
270 generic_typ_str := if v.generic_typ == 0 {
271 '<none>'
272 } else {
273 c.table.type_to_str(v.generic_typ)
274 }
275 eprintln('refresh_var fn=${c.table.cur_fn.name} var=${v.name} typ=${c.table.type_to_str(v.typ)} gtyp=${generic_typ_str} expr=${v.expr}')
276 }
277 }
278 if v.generic_typ != 0 {
279 refreshed_generic_type := c.unwrap_generic(c.recheck_concrete_type(v.generic_typ))
280 if refreshed_generic_type != 0 && refreshed_generic_type != ast.void_type {
281 v.typ = refreshed_generic_type
282 v.orig_type = ast.no_type
283 v.smartcasts = []
284 v.is_unwrapped = false
285 return v.typ
286 }
287 }
288 if v.expr is ast.Ident && v.expr.name == v.name {
289 return v.typ
290 }
291 saved_expected_type := c.expected_type
292 saved_expected_or_type := c.expected_or_type
293 saved_expected_expr_type := c.expected_expr_type
294 saved_inside_assign := c.inside_assign
295 saved_inside_decl_rhs := c.inside_decl_rhs
296 saved_inside_selector_expr := c.inside_selector_expr
297 saved_inside_fn_arg := c.inside_fn_arg
298 saved_inside_if_guard := c.inside_if_guard
299 saved_prevent_sum_type_unwrapping_once := c.prevent_sum_type_unwrapping_once
300 saved_inside_recheck := c.inside_recheck
301 saved_anon_struct_should_be_mut := c.anon_struct_should_be_mut
302 defer {
303 c.expected_type = saved_expected_type
304 c.expected_or_type = saved_expected_or_type
305 c.expected_expr_type = saved_expected_expr_type
306 c.inside_assign = saved_inside_assign
307 c.inside_decl_rhs = saved_inside_decl_rhs
308 c.inside_selector_expr = saved_inside_selector_expr
309 c.inside_fn_arg = saved_inside_fn_arg
310 c.inside_if_guard = saved_inside_if_guard
311 c.prevent_sum_type_unwrapping_once = saved_prevent_sum_type_unwrapping_once
312 c.inside_recheck = saved_inside_recheck
313 c.anon_struct_should_be_mut = saved_anon_struct_should_be_mut
314 }
315 c.expected_type = ast.void_type
316 c.expected_or_type = ast.void_type
317 c.expected_expr_type = ast.void_type
318 c.inside_assign = false
319 c.inside_decl_rhs = false
320 c.inside_selector_expr = false
321 c.inside_fn_arg = false
322 c.inside_if_guard = false
323 c.prevent_sum_type_unwrapping_once = false
324 c.inside_recheck = true
325 c.anon_struct_should_be_mut = false
326 mut expr := v.expr
327 mut refreshed_type := ast.void_type
328 if mut expr is ast.IfGuardExpr {
329 c.expr(mut expr)
330 if expr.expr_type != 0 && expr.expr_type.clear_option_and_result() != ast.void_type {
331 sym := c.table.sym(expr.expr_type)
332 if sym.kind == .multi_return {
333 mr_info := sym.info as ast.MultiReturn
334 if mr_info.types.len == expr.vars.len {
335 for vi, var in expr.vars {
336 if var.name == v.name {
337 refreshed_type = mr_info.types[vi]
338 break
339 }
340 }
341 }
342 } else {
343 // Rechecks should keep the value type introduced by the guard variable,
344 // not the `bool` condition type returned by `IfGuardExpr`.
345 refreshed_type = expr.expr_type.clear_option_and_result()
346 }
347 }
348 } else {
349 refreshed_type = c.expr(mut expr)
350 }
351 if refreshed_type != 0 && refreshed_type != ast.void_type {
352 mut expr_is_auto_deref_ident := false
353 if expr is ast.Ident {
354 ident := expr as ast.Ident
355 expr_is_auto_deref_ident = ident.obj is ast.Var && ident.obj.is_auto_deref
356 if !expr_is_auto_deref_ident {
357 if source_var := ident.scope.find_var(ident.name) {
358 expr_is_auto_deref_ident = source_var.is_auto_deref
359 }
360 }
361 }
362 // Keep `mut x := param` as a value copy during generic rechecks when the
363 // original parameter type was lowered from a non-pointer source type.
364 // Pointer-typed mut params should keep their declared pointer type.
365 if expr_is_auto_deref_ident && refreshed_type.is_ptr()
366 && !c.auto_deref_source_type_is_pointer(expr) {
367 refreshed_type = refreshed_type.deref()
368 }
369 $if trace_ci_fixes ? {
370 if c.file.path.contains('/datatypes/linked_list.v') {
371 eprintln('refresh_var expr fn=${c.table.cur_fn.name} var=${v.name} refreshed=${c.table.type_to_str(refreshed_type)} expr=${expr}')
372 }
373 }
374 v.typ = c.unwrap_generic(c.recheck_concrete_type(refreshed_type))
375 v.expr = expr
376 v.orig_type = ast.no_type
377 v.smartcasts = []
378 v.is_unwrapped = false
379 }
380 return v.typ
381}
382
383fn (mut c Checker) resolve_selector_field_type(left_type ast.Type, field_name string, field ast.StructField) ast.Type {
384 mut field_type := field.typ
385 sym := c.table.sym(c.unwrap_generic(left_type))
386 match sym.info {
387 ast.Struct, ast.Interface, ast.SumType {
388 mut generic_names := sym.info.generic_types.map(c.table.sym(it).name)
389 mut concrete_types := sym.info.concrete_types.clone()
390 if concrete_types.len == 0 && sym.generic_types.len == generic_names.len
391 && sym.generic_types != sym.info.generic_types {
392 concrete_types = sym.generic_types.clone()
393 }
394 mut source_field_type := field.typ
395 if sym.info.parent_type.has_flag(.generic) {
396 parent_sym := c.table.sym(sym.info.parent_type)
397 if parent_field := c.table.find_field_with_embeds(parent_sym, field_name) {
398 source_field_type = parent_field.typ
399 match parent_sym.info {
400 ast.Struct, ast.Interface, ast.SumType {
401 generic_names = parent_sym.info.generic_types.map(c.table.sym(it).name)
402 }
403 else {}
404 }
405 }
406 }
407 if generic_names.len == concrete_types.len && concrete_types.len > 0 {
408 resolved_field_type := c.table.unwrap_generic_type_ex(source_field_type,
409 generic_names, concrete_types, true)
410 if resolved_field_type != source_field_type {
411 field_type = resolved_field_type
412 } else if converted_field_type := c.table.convert_generic_type(source_field_type,
413 generic_names, concrete_types)
414 {
415 field_type = converted_field_type
416 }
417 }
418 }
419 ast.GenericInst {
420 parent_sym := c.table.sym(ast.new_type(sym.info.parent_idx))
421 mut source_field_type := field.typ
422 if parent_field := c.table.find_field_with_embeds(parent_sym, field_name) {
423 source_field_type = parent_field.typ
424 }
425 match parent_sym.info {
426 ast.Struct, ast.Interface, ast.SumType {
427 generic_names := parent_sym.info.generic_types.map(c.table.sym(it).name)
428 if generic_names.len == sym.info.concrete_types.len
429 && sym.info.concrete_types.len > 0 {
430 resolved_field_type := c.table.unwrap_generic_type_ex(source_field_type,
431 generic_names, sym.info.concrete_types, true)
432 if resolved_field_type != source_field_type {
433 field_type = resolved_field_type
434 } else if converted_field_type := c.table.convert_generic_type(source_field_type,
435 generic_names, sym.info.concrete_types)
436 {
437 field_type = converted_field_type
438 }
439 }
440 }
441 else {}
442 }
443 }
444 else {}
445 }
446
447 $if trace_ci_fixes ? {
448 if c.file.path.contains('/datatypes/linked_list.v') {
449 eprintln('selector_field left=${c.table.type_to_str(left_type)} sym=${sym.name} field=${field_name} raw=${c.table.type_to_str(field.typ)} final=${c.table.type_to_str(field_type)}')
450 }
451 }
452 return c.unwrap_generic(c.recheck_concrete_type(field_type))
453}
454
455fn (mut c Checker) reset_checker_state_at_start_of_new_file() {
456 c.expected_type = ast.void_type
457 c.expected_or_type = ast.void_type
458 c.const_var = unsafe { nil }
459 c.in_for_count = 0
460 c.returns = false
461 c.scope_returns = false
462 c.mod = ''
463 c.is_builtin_mod = false
464 c.is_just_builtin_mod = false
465 c.inside_unsafe = false
466 c.inside_const = false
467 c.inside_anon_fn = false
468 c.inside_ref_lit = false
469 c.inside_defer = false
470 c.inside_fn_arg = false
471 c.inside_ct_attr = false
472 c.inside_x_is_type = false
473 c.inside_x_matches_type = false
474 c.inside_integer_literal_cast = false
475 c.skip_flags = false
476 c.fn_level = 0
477 c.expr_level = 0
478 c.stmt_level = 0
479 c.inside_sql = false
480 c.cur_orm_ts = ast.TypeSymbol{}
481 c.prevent_sum_type_unwrapping_once = false
482 c.loop_labels = []
483 c.inside_selector_expr = false
484 c.inside_interface_deref = false
485 c.inside_decl_rhs = false
486 c.inside_if_guard = false
487 c.has_globals_in_module = false
488 c.error_details.clear()
489}
490
491pub fn (mut c Checker) check(mut ast_file ast.File) {
492 $if trace_check ? {
493 eprintln('> ${@FILE}:${@LINE} | ast_file.path: ${ast_file.path}')
494 }
495 $if trace_checker ? {
496 eprintln('start checking file: ${ast_file.path}')
497 }
498 c.reset_checker_state_at_start_of_new_file()
499 c.change_current_file(ast_file)
500 for i, ast_import in ast_file.imports {
501 // Imports with the same path and name (self-imports and module name conflicts with builtin module imports)
502 if c.mod == ast_import.mod {
503 c.error('cannot import `${ast_import.mod}` into a module with the same name',
504 ast_import.mod_pos)
505 }
506 // Duplicates of regular imports with the default alias (modname) and `as` imports with a custom alias
507 if c.mod == ast_import.alias {
508 if c.mod == ast_import.mod.all_after_last('.') {
509 c.error('cannot import `${ast_import.mod}` into a module with the same name',
510 ast_import.mod_pos)
511 }
512 c.error('cannot import `${ast_import.mod}` as `${ast_import.alias}` into a module with the same name',
513 ast_import.alias_pos)
514 }
515 for sym in ast_import.syms {
516 full_name := ast_import.mod + '.' + sym.name
517 if full_name in c.const_names {
518 c.error('cannot selectively import constant `${sym.name}` from `${ast_import.mod}`, import `${ast_import.mod}` and use `${full_name}` instead',
519 sym.pos)
520 }
521 }
522
523 cmp_mod_name := if ast_import.mod != ast_import.alias && ast_import.alias != '_' {
524 ast_import.alias
525 } else {
526 ast_import.mod
527 }
528 for j in 0 .. i {
529 if cmp_mod_name == if ast_file.imports[j].mod != ast_file.imports[j].alias
530 && ast_file.imports[j].alias != '_' {
531 ast_file.imports[j].alias
532 } else {
533 ast_file.imports[j].mod
534 } {
535 c.error('A module `${cmp_mod_name}` was already imported on line ${
536 ast_file.imports[j].mod_pos.line_nr + 1}`.', ast_import.mod_pos)
537 }
538 }
539 }
540 c.reorder_fns_at_the_end(mut ast_file)
541 c.stmt_level = 0
542 for mut stmt in ast_file.stmts {
543 if stmt in [ast.ConstDecl, ast.ExprStmt] {
544 c.expr_level = 0
545 c.stmt(mut stmt)
546 }
547 if c.should_abort {
548 return
549 }
550 }
551
552 c.stmt_level = 0
553 for mut stmt in ast_file.stmts {
554 is_global_decl := stmt is ast.GlobalDecl
555 if is_global_decl {
556 c.expr_level = 0
557 c.stmt(mut stmt)
558 }
559 if c.should_abort {
560 return
561 }
562 }
563
564 c.stmt_level = 0
565 for mut stmt in ast_file.stmts {
566 if stmt is ast.StructDecl || stmt is ast.InterfaceDecl || stmt is ast.EnumDecl
567 || stmt is ast.TypeDecl {
568 c.expr_level = 0
569 c.stmt(mut stmt)
570 }
571 if c.should_abort {
572 return
573 }
574 }
575
576 c.stmt_level = 0
577 for mut stmt in ast_file.stmts {
578 if mut stmt is ast.FnDecl {
579 return_sym := c.table.sym(stmt.return_type)
580 if return_sym.info is ast.ArrayFixed
581 && c.array_fixed_has_unresolved_size(return_sym.info) {
582 unsafe {
583 c.unresolved_fixed_sizes << &stmt
584 }
585 }
586 }
587 }
588
589 if c.unresolved_fixed_sizes.len > 0 {
590 c.update_unresolved_fixed_sizes()
591 }
592
593 c.stmt_level = 0
594 for mut stmt in ast_file.stmts {
595 if stmt !in [ast.ConstDecl, ast.GlobalDecl, ast.ExprStmt] && stmt !is ast.StructDecl
596 && stmt !is ast.InterfaceDecl && stmt !is ast.EnumDecl && stmt !is ast.TypeDecl {
597 c.expr_level = 0
598 c.stmt(mut stmt)
599 }
600 if c.should_abort {
601 return
602 }
603 }
604
605 c.check_scope_vars(c.file.scope)
606 c.check_unused_labels()
607}
608
609pub fn (mut c Checker) reorder_fns_at_the_end(mut ast_file ast.File) {
610 mut fdeclarations := 0
611 for mut stmt in ast_file.stmts {
612 if stmt is ast.FnDecl {
613 fdeclarations++
614 }
615 }
616 if fdeclarations == 0 {
617 return
618 }
619 // eprintln('>>> ast_file: ${ast_file.path:-60s} | fdeclarations: ${fdeclarations}')
620 mut stmts := []ast.Stmt{cap: ast_file.stmts.len}
621 for stmt in ast_file.stmts {
622 if stmt !is ast.FnDecl {
623 stmts << stmt
624 }
625 }
626 for stmt in ast_file.stmts {
627 if stmt is ast.FnDecl {
628 stmts << stmt
629 }
630 }
631 ast_file.stmts = stmts
632}
633
634pub fn (mut c Checker) check_scope_vars(sc &ast.Scope) {
635 if !c.pref.is_repl && !c.file.is_test {
636 for _, obj in sc.objects {
637 match obj {
638 ast.Var {
639 if !obj.is_special && !obj.is_used && obj.name[0] != `_` {
640 if !c.pref.translated && !c.file.is_translated {
641 if obj.is_arg {
642 if c.pref.show_unused_params {
643 c.note('unused parameter: `${obj.name}`', obj.pos)
644 }
645 } else {
646 c.warn('unused variable: `${obj.name}`', obj.pos)
647 }
648 }
649 }
650 // if obj.is_mut && !obj.is_changed && !c.is_builtin_mod && obj.name != 'it' {
651 // if obj.is_mut && !obj.is_changed && !c.is_builtin { //TODO C error bad field not checked
652 // c.warn('`${obj.name}` is declared as mutable, but it was never changed',
653 // obj.pos)
654 // }
655 }
656 else {}
657 }
658 }
659 }
660 for child in sc.children {
661 c.check_scope_vars(child)
662 }
663}
664
665pub fn (mut c Checker) change_current_file(file &ast.File) {
666 c.file = unsafe { file }
667 c.vmod_file_content = ''
668 c.mod = file.mod.name
669 c.is_just_builtin_mod = c.mod in ['builtin', 'builtin.closure']
670 c.is_builtin_mod = c.is_just_builtin_mod || c.mod in ['os', 'strconv']
671 c.is_generated = file.is_generated
672 c.short_module_names = ['builtin']
673 for import_sym in c.file.imports {
674 c.short_module_names << if import_sym.alias == '' {
675 import_sym.mod.all_after_last('.')
676 } else {
677 import_sym.alias
678 }
679 }
680 // Check if the current module has has_globals attribute
681 c.has_globals_in_module = false
682 c.strict_map_index_in_module = false
683 for attr in file.mod.attrs {
684 match attr.name {
685 'has_globals' {
686 c.has_globals_in_module = true
687 }
688 'strict_map_index' {
689 c.strict_map_index_in_module = true
690 }
691 else {}
692 }
693 }
694}
695
696pub fn (mut c Checker) check_files(ast_files []&ast.File) {
697 // println('check_files')
698 // c.files = ast_files
699 mut has_main_mod_file := false
700 mut has_no_main_mod_file := false
701 mut has_main_fn := false
702 mut invalid_test_file_name := ''
703 mut invalid_test_file_pos := token.Pos{}
704 // Determine the project directory when using -line-info
705 mut project_dir := ''
706 if c.pref.is_vls && c.pref.line_info != '' {
707 project_dir = if os.is_dir(c.pref.path) {
708 os.real_path(c.pref.path)
709 } else {
710 os.real_path(os.dir(c.pref.linfo.path))
711 }
712 }
713 unsafe {
714 mut files_from_main_module := []&ast.File{}
715 for i in 0 .. ast_files.len {
716 mut file := ast_files[i]
717 if c.pref.is_vls && c.pref.line_info == '' && file.path != c.pref.path {
718 continue
719 }
720 if c.pref.is_vls && c.pref.line_info != '' && project_dir != '' {
721 if !os.real_path(file.path).starts_with(project_dir) {
722 continue
723 }
724 }
725 c.timers.start('checker_check ${file.path}')
726 c.check(mut file)
727 if file.mod.name == 'no_main' {
728 has_no_main_mod_file = true
729 }
730 if file.mod.name == 'main' {
731 files_from_main_module << file
732 has_main_mod_file = true
733 if c.file_has_main_fn(file) {
734 has_main_fn = true
735 }
736 }
737 if invalid_test_file_name == '' && is_likely_invalid_test_file_name(file) {
738 invalid_test_file_name = file.path_base
739 invalid_test_file_pos = file.mod.pos
740 }
741 c.timers.show('checker_check ${file.path}')
742 }
743 if has_main_mod_file && !has_main_fn && files_from_main_module.len > 0 {
744 if c.pref.is_script && !c.pref.is_test {
745 // files_from_main_module contain preludes at the start
746 mut the_main_file := files_from_main_module.last()
747 the_main_file.stmts << ast.FnDecl{
748 name: 'main.main'
749 mod: 'main'
750 is_main: true
751 file: the_main_file.path
752 return_type: ast.void_type
753 scope: &ast.Scope{
754 parent: nil
755 }
756 }
757 has_main_fn = true
758 }
759 }
760 }
761 c.timers.start('checker_post_process_generic_fns')
762 mut last_file := c.file
763 // c.file might be nil in vls mode, fall back to first file
764 if c.pref.is_vls && last_file == unsafe { nil } && ast_files.len > 0 {
765 last_file = ast_files[0]
766 }
767 // post process generic functions. must be done after all files have been
768 // checked, to ensure all generic calls are processed, as this information
769 // is needed when the generic type is auto inferred from the call argument.
770 // we may have to loop several times, if there were more concrete types found.
771 mut post_process_generic_fns_iterations := 0
772 post_process_iterations_loop: for post_process_generic_fns_iterations <= generic_fn_postprocess_iterations_cutoff_limit {
773 $if trace_post_process_generic_fns_loop ? {
774 eprintln('>>>>>>>>> recheck_generic_fns loop iteration: ${post_process_generic_fns_iterations}')
775 }
776 for file in ast_files {
777 if file.generic_fns.len > 0 {
778 $if trace_post_process_generic_fns_loop ? {
779 eprintln('>> file.path: ${file.path:-40} | file.generic_fns:' +
780 file.generic_fns.map(it.name).str())
781 }
782 c.change_current_file(file)
783 c.post_process_generic_fns() or { break post_process_iterations_loop }
784 }
785 }
786 // Resolve newly created generic struct/interface instances to concrete types.
787 // This ensures that methods of generic structs instantiated during the current
788 // iteration (e.g. EluLayer[f64] created when checking elu_layer[f64]) get their
789 // concrete types registered for rechecking in the next iteration.
790 mut old_concrete_count := 0
791 for _, v in c.table.fn_generic_types {
792 old_concrete_count += v.len
793 }
794 c.table.generic_insts_to_concrete()
795 mut new_concrete_count := 0
796 for _, v in c.table.fn_generic_types {
797 new_concrete_count += v.len
798 }
799 if new_concrete_count != old_concrete_count {
800 c.need_recheck_generic_fns = true
801 }
802 if !c.need_recheck_generic_fns {
803 break
804 }
805 c.need_recheck_generic_fns = false
806 post_process_generic_fns_iterations++
807 }
808 $if trace_post_process_generic_fns_loop ? {
809 eprintln('>>>>>>>>> recheck_generic_fns loop done, iteration: ${post_process_generic_fns_iterations}')
810 }
811 // restore the original c.file && c.mod after post processing
812 c.change_current_file(last_file)
813 c.timers.show('checker_post_process_generic_fns')
814
815 c.timers.start('checker_verify_all_veb_routes')
816 c.verify_all_veb_routes()
817 c.timers.show('checker_verify_all_veb_routes')
818
819 c.check_unused_declarations(ast_files)
820
821 if c.pref.is_test {
822 mut n_test_fns := 0
823 for _, f in c.table.fns {
824 if f.is_test {
825 n_test_fns++
826 }
827 }
828 if n_test_fns == 0 {
829 c.add_error_detail('The name of a test function in V, should start with `test_`.')
830 c.add_error_detail('The test function should take 0 parameters, and no return type. Example:')
831 c.add_error_detail('fn test_xyz(){ assert 2 + 2 == 4 }')
832 c.error('a _test.v file should have *at least* one `test_` function', token.Pos{})
833 }
834 }
835 // Make sure fn main is defined in non lib builds
836 if c.pref.build_mode == .build_module || c.pref.is_test {
837 return
838 }
839 if c.pref.is_shared {
840 // shared libs do not need to have a main
841 return
842 }
843 if c.pref.is_o {
844 // .o files also do not need main
845 return
846 }
847 if c.pref.no_builtin {
848 // `v -no-builtin module/` do not necessarily need to have a `main` function
849 // This is useful for compiling linux kernel modules for example.
850 return
851 }
852 if has_no_main_mod_file {
853 return
854 }
855 if !has_main_mod_file {
856 if invalid_test_file_name != '' {
857 c.add_error_detail('Test files should have names ending with `_test.v`.')
858 c.error('invalid test file name `${invalid_test_file_name}`', invalid_test_file_pos)
859 } else {
860 c.error('project must include a `main` module or be a shared library (compile with `v -shared`)', token.Pos{})
861 }
862 } else if !has_main_fn && !c.pref.is_o {
863 c.error('function `main` must be declared in the main module', token.Pos{})
864 }
865}
866
867fn is_likely_invalid_test_file_name(file &ast.File) bool {
868 return !file.is_test && (file.path_base.ends_with('.v') || file.path_base.ends_with('.vv'))
869 && file.path_base.starts_with('test_')
870}
871
872fn (mut c Checker) stmts_has_main_fn(stmts []ast.Stmt) bool {
873 mut has_main_fn := false
874 for stmt in stmts {
875 if stmt is ast.ExprStmt {
876 // top level comptime main fn
877 if stmt.expr is ast.IfExpr && stmt.expr.is_comptime {
878 // $if a ? { fn main(){} } $else { fn main() {} }
879 for branch in stmt.expr.branches {
880 if c.stmts_has_main_fn(branch.stmts) {
881 has_main_fn = true
882 }
883 }
884 } else if stmt.expr is ast.MatchExpr && stmt.expr.is_comptime {
885 // $match os { 'windows' { fn main() {} } ...
886 for branch in stmt.expr.branches {
887 if c.stmts_has_main_fn(branch.stmts) {
888 has_main_fn = true
889 }
890 }
891 }
892 }
893 if stmt is ast.FnDecl {
894 if stmt.name == 'main.main' {
895 if has_main_fn {
896 c.error('function `main` is already defined', stmt.pos)
897 }
898 has_main_fn = true
899 if stmt.params.len > 0 {
900 c.error('function `main` cannot have arguments', stmt.pos)
901 }
902 if stmt.return_type != ast.void_type {
903 c.error('function `main` cannot return values', stmt.pos)
904 }
905 if stmt.no_body {
906 c.error('function `main` must declare a body', stmt.pos)
907 }
908 } else if stmt.attrs.contains('console') {
909 c.error('only `main` can have the `@[console]` attribute', stmt.pos)
910 }
911 }
912 }
913 return has_main_fn
914}
915
916// do checks specific to files in main module
917// returns `true` if a main function is in the file
918fn (mut c Checker) file_has_main_fn(file &ast.File) bool {
919 return c.stmts_has_main_fn(file.stmts)
920}
921
922@[direct_array_access]
923fn (mut c Checker) check_valid_snake_case(name string, identifier string, pos token.Pos) {
924 if c.pref.translated || c.file.is_translated {
925 return
926 }
927 if !c.pref.is_template && name.len > 1 && (name[0] == `_` || name.contains('._')) {
928 c.error('${identifier} `${name}` cannot start with `_`', pos)
929 }
930 if util.contains_capital(name) {
931 c.error('${identifier} `${name}` cannot contain uppercase letters, use snake_case instead',
932 pos)
933 }
934}
935
936fn stripped_name(name string) string {
937 idx := name.last_index('.') or { -1 }
938 return name[(idx + 1)..]
939}
940
941fn (mut c Checker) check_valid_pascal_case(name string, identifier string, pos token.Pos) {
942 if c.pref.translated || c.file.is_translated {
943 return
944 }
945 sname := stripped_name(name)
946 if sname.len > 0 && !sname[0].is_capital() {
947 c.error('${identifier} `${name}` must begin with capital letter', pos)
948 }
949}
950
951fn (mut c Checker) type_decl(mut node ast.TypeDecl) {
952 if node.typ == ast.invalid_type && (node is ast.AliasTypeDecl || node is ast.SumTypeDecl) {
953 typ_desc := if node is ast.AliasTypeDecl { 'alias' } else { 'sum type' }
954 c.error('cannot register ${typ_desc} `${node.name}`, another type with this name exists',
955 node.pos)
956 return
957 }
958 match mut node {
959 ast.AliasTypeDecl { c.alias_type_decl(mut node) }
960 ast.FnTypeDecl { c.fn_type_decl(mut node) }
961 ast.SumTypeDecl { c.sum_type_decl(mut node) }
962 }
963}
964
965fn (mut c Checker) alias_type_decl(mut node ast.AliasTypeDecl) {
966 if c.file.mod.name != 'builtin' && !node.name.starts_with('C.') {
967 c.check_valid_pascal_case(node.name, 'type alias', node.pos)
968 }
969 node.parent_type = c.preferred_c_symbol_type(node.parent_type)
970 if c.pref.is_vls && c.pref.linfo.method == .definition {
971 if c.vls_is_the_node(node.type_pos) {
972 typ_str := c.table.type_to_str(node.parent_type)
973 if np := c.name_pos_gotodef(typ_str) {
974 if np.file_idx != -1 {
975 println('${c.table.filelist[np.file_idx]}:${np.line_nr + 1}:${np.col}')
976 exit(0)
977 }
978 }
979 }
980 }
981 if !c.ensure_type_exists(node.parent_type, node.type_pos) {
982 return
983 }
984 mut parent_typ_sym := c.table.sym(node.parent_type)
985 if node.parent_type.has_flag(.result) {
986 c.add_error_detail('Result types cannot be stored and have to be unwrapped immediately')
987 c.error('cannot make an alias of Result type', node.type_pos)
988 }
989 match parent_typ_sym.kind {
990 .placeholder, .int_literal, .float_literal, .any {
991 c.error('unknown aliased type `${parent_typ_sym.name}`', node.type_pos)
992 }
993 .alias {
994 orig_sym := c.table.sym((parent_typ_sym.info as ast.Alias).parent_type)
995 if !node.name.starts_with('C.')
996 && parent_typ_sym.name !in ['strings.Builder', 'StringBuilder', 'builtin.StringBuilder'] {
997 // TODO: remove the whole check, or at least the need for special casing `strings.Builder` and `StringBuilder` here
998 // after more testing and bootstrapping of the strings.Builder -> builtin.StringBuilder change
999 c.error('type `${parent_typ_sym.str()}` is an alias, use the original alias type `${orig_sym.name}` instead',
1000 node.type_pos)
1001 }
1002 }
1003 .chan {
1004 c.error('aliases of `chan` types are not allowed', node.type_pos)
1005 }
1006 .thread {
1007 c.error('aliases of `thread` types are not allowed', node.type_pos)
1008 }
1009 .multi_return {
1010 c.error('aliases of function multi return types are not allowed', node.type_pos)
1011 }
1012 .void {
1013 c.error('aliases of the void type are not allowed', node.type_pos)
1014 }
1015 .function {
1016 orig_sym := c.table.type_to_str(node.parent_type)
1017 c.error('type `${parent_typ_sym.str()}` is an alias, use the original alias type `${orig_sym}` instead',
1018 node.type_pos)
1019 }
1020 .struct {
1021 if mut parent_typ_sym.info is ast.Struct {
1022 // check if the generic param types have been defined
1023 for ct in parent_typ_sym.info.concrete_types {
1024 ct_sym := c.table.sym(ct)
1025 if ct_sym.kind == .placeholder {
1026 c.error('unknown type `${ct_sym.name}`', node.type_pos)
1027 }
1028 }
1029
1030 if parent_typ_sym.info.is_generic && parent_typ_sym.info.concrete_types.len == 0 {
1031 c.error('${parent_typ_sym.name} type is generic struct, must specify the generic type names, e.g. ${parent_typ_sym.name}[int]',
1032 node.type_pos)
1033 }
1034
1035 // check if embed types are supported for struct embedding
1036 for embed_type in parent_typ_sym.info.embeds {
1037 if !c.can_be_embedded_in_struct(embed_type) {
1038 c.error('cannot embed non-struct `${c.table.sym(embed_type).name}`',
1039 node.type_pos)
1040 }
1041 }
1042 if parent_typ_sym.info.is_anon {
1043 for field in parent_typ_sym.info.fields {
1044 mut is_embed := false
1045 field_sym := c.table.sym(field.typ)
1046 if field_sym.info is ast.Alias {
1047 if !c.can_be_embedded_in_struct(field.typ) {
1048 c.error('cannot embed non-struct `${field_sym.name}`',
1049 field.type_pos)
1050 is_embed = true
1051 }
1052 }
1053 if !is_embed {
1054 c.check_valid_snake_case(field.name, 'field name', field.pos)
1055 }
1056 }
1057 }
1058 }
1059 }
1060 .array {
1061 c.check_alias_vs_element_type_of_parent(node,
1062 (parent_typ_sym.info as ast.Array).elem_type, 'array')
1063 }
1064 .array_fixed {
1065 array_fixed_info := parent_typ_sym.info as ast.ArrayFixed
1066 if c.array_fixed_has_unresolved_size(array_fixed_info) {
1067 c.unresolved_fixed_sizes << &ast.TypeDecl(node)
1068 }
1069 c.check_alias_vs_element_type_of_parent(node, array_fixed_info.elem_type, 'fixed array')
1070 }
1071 .map {
1072 info := parent_typ_sym.info as ast.Map
1073 c.check_alias_vs_element_type_of_parent(node, info.key_type, 'map key')
1074 c.check_alias_vs_element_type_of_parent(node, info.value_type, 'map value')
1075 c.markused_used_maps(c.table.used_features.used_maps == 0)
1076 }
1077 .sum_type {
1078 // TODO: decide whether the following should be allowed. Note that it currently works,
1079 // while `type Sum = int | Sum` is explicitly disallowed:
1080 // type Sum = int | Alias
1081 // type Alias = Sum
1082 }
1083 .none {
1084 c.error('cannot create a type alias of `none` as it is a value', node.type_pos)
1085 }
1086 // The rest of the parent symbol kinds are also allowed, since they are either primitive types,
1087 // that in turn do not allow recursion, or are abstract enough so that they can not be checked at comptime:
1088 else {
1089 c.check_any_type(node.parent_type, parent_typ_sym, node.type_pos)
1090 }
1091 /*
1092 .voidptr, .byteptr, .charptr {}
1093 .char, .rune, .bool {}
1094 .string, .enum, .none, .any {}
1095 .i8, .i16, .i32, .int, .i64, .isize {}
1096 .u8, .u16, .u32, .u64, .usize {}
1097 .f32, .f64 {}
1098 .interface {}
1099 .generic_inst {}
1100 .aggregate {}
1101 */
1102 }
1103}
1104
1105fn (c &Checker) preferred_c_symbol_type(typ ast.Type) ast.Type {
1106 sym := c.table.sym(typ)
1107 if sym.language != .c || sym.name == '' {
1108 return typ
1109 }
1110 mut public_typ := ast.invalid_type
1111 for i := c.table.type_symbols.len - 1; i >= 1; i-- {
1112 candidate := c.table.type_symbols[i]
1113 if candidate.language != .c || candidate.name != sym.name || candidate.kind == .placeholder {
1114 continue
1115 }
1116 if candidate.mod == c.mod {
1117 return ast.new_type(i).derive(typ)
1118 }
1119 if candidate.is_pub && public_typ == ast.invalid_type {
1120 public_typ = ast.new_type(i).derive(typ)
1121 }
1122 }
1123 if public_typ != ast.invalid_type {
1124 return public_typ
1125 }
1126 return typ
1127}
1128
1129fn (mut c Checker) check_alias_vs_element_type_of_parent(node ast.AliasTypeDecl, element_type_of_parent ast.Type,
1130 label string) {
1131 if node.typ.idx() != element_type_of_parent.idx() {
1132 return
1133 }
1134 c.error('recursive declarations of aliases are not allowed - the alias `${node.name}` is used in the ${label}',
1135 node.type_pos)
1136}
1137
1138fn (mut c Checker) check_any_type(typ ast.Type, sym &ast.TypeSymbol, pos token.Pos) {
1139 if sym.kind == .any && !typ.has_flag(.generic) && sym.language != .js
1140 && c.file.mod.name != 'builtin' {
1141 c.error('cannot use type `any` here', pos)
1142 }
1143}
1144
1145fn (mut c Checker) fn_type_decl(mut node ast.FnTypeDecl) {
1146 c.check_valid_pascal_case(node.name, 'fn type', node.pos)
1147 typ_sym := c.table.sym(node.typ)
1148 fn_typ_info := typ_sym.info as ast.FnType
1149 fn_info := fn_typ_info.func
1150 c.ensure_type_exists(fn_info.return_type, fn_info.return_type_pos)
1151 ret_sym := c.table.sym(fn_info.return_type)
1152 if ret_sym.kind == .placeholder {
1153 c.error('unknown type `${ret_sym.name}`', fn_info.return_type_pos)
1154 }
1155 for arg in fn_info.params {
1156 if !c.ensure_type_exists(arg.typ, arg.type_pos) {
1157 return
1158 }
1159 arg_sym := c.table.sym(arg.typ)
1160 if arg_sym.kind == .placeholder {
1161 c.error('unknown type `${arg_sym.name}`', arg.type_pos)
1162 }
1163 }
1164}
1165
1166fn (mut c Checker) sum_type_decl(mut node ast.SumTypeDecl) {
1167 c.check_valid_pascal_case(node.name, 'sum type', node.pos)
1168 if c.pref.is_vls && c.pref.linfo.method == .definition {
1169 for variant in node.variants {
1170 if c.vls_is_the_node(variant.pos) {
1171 typ_str := c.table.type_to_str(variant.typ)
1172 if np := c.name_pos_gotodef(typ_str) {
1173 if np.file_idx != -1 {
1174 println('${c.table.filelist[np.file_idx]}:${np.line_nr + 1}:${np.col}')
1175 exit(0)
1176 }
1177 }
1178 }
1179 }
1180 }
1181 mut names_used := []string{}
1182 for variant in node.variants {
1183 c.ensure_type_exists(variant.typ, variant.pos)
1184 sym := c.table.sym(variant.typ)
1185 if variant.typ.is_ptr() || (sym.info is ast.Alias && sym.info.parent_type.is_ptr()) {
1186 variant_name := sym.name.all_after_last('.')
1187 lb, rb := if sym.kind == .struct { '{', '}' } else { '(', ')' }
1188 msg := if sym.info is ast.Alias && sym.info.parent_type.is_ptr() {
1189 'alias as non-reference type'
1190 } else {
1191 'the sum type with non-reference types'
1192 }
1193 c.add_error_detail('declare ${msg}: `${node.name} = ${variant_name} | ...`
1194and use a reference to the sum type instead: `var := &${node.name}(${variant_name}${lb}val${rb})`')
1195 c.error('sum type cannot hold a reference type', variant.pos)
1196 }
1197 variant_name := c.table.type_to_str(variant.typ)
1198 if variant_name in names_used {
1199 c.error('sum type ${node.name} cannot hold the type `${sym.name}` more than once',
1200 variant.pos)
1201 } else if sym.kind in [.placeholder, .int_literal, .float_literal] {
1202 c.error('unknown type `${sym.name}`', variant.pos)
1203 } else if sym.kind == .interface && sym.language != .js {
1204 c.error('sum type cannot hold an interface', variant.pos)
1205 } else if sym.kind == .struct && sym.language == .js {
1206 c.error('sum type cannot hold a JS struct', variant.pos)
1207 } else if sym.info is ast.Struct {
1208 if sym.info.is_generic {
1209 if !variant.typ.has_flag(.generic) {
1210 c.error('generic struct `${sym.name}` must specify generic type names, e.g. ${sym.name}[T]',
1211 variant.pos)
1212 }
1213 if node.generic_types.len == 0 {
1214 c.error('generic sumtype `${node.name}` must specify generic type names, e.g. ${node.name}[T]',
1215 node.name_pos)
1216 } else {
1217 for typ in sym.info.generic_types {
1218 if typ !in node.generic_types {
1219 sumtype_type_names :=
1220 node.generic_types.map(c.table.type_to_str(it)).join(', ')
1221 generic_sumtype_name := '${node.name}[${sumtype_type_names}]'
1222 generic_variant_name := c.table.type_to_str(variant.typ)
1223 c.error('generic type name `${c.table.sym(typ).name}` of generic struct `${generic_variant_name}` is not mentioned in sumtype `${generic_sumtype_name}`',
1224 variant.pos)
1225 }
1226 }
1227 }
1228 }
1229 } else if sym.info is ast.FnType {
1230 if sym.info.func.generic_names.len > 0 {
1231 if !variant.typ.has_flag(.generic) {
1232 c.error('generic fntype `${sym.name}` must specify generic type names, e.g. ${sym.name}[T]',
1233 variant.pos)
1234 }
1235 if node.generic_types.len == 0 {
1236 c.error('generic sumtype `${node.name}` must specify generic type names, e.g. ${node.name}[T]',
1237 node.name_pos)
1238 }
1239 }
1240 if c.table.sym(sym.info.func.return_type).name.ends_with('.${node.name}') {
1241 c.error('sum type `${node.name}` cannot be defined recursively', variant.pos)
1242 }
1243 for param in sym.info.func.params {
1244 if c.table.sym(param.typ).name.ends_with('.${node.name}') {
1245 c.error('sum type `${node.name}` cannot be defined recursively', variant.pos)
1246 }
1247 }
1248 } else if variant.typ.has_flag(.result) {
1249 c.error('sum type cannot hold a Result type', variant.pos)
1250 }
1251 c.check_any_type(variant.typ, sym, variant.pos)
1252
1253 if sym.name.trim_string_left(sym.mod + '.') == node.name {
1254 c.error('sum type cannot hold itself', variant.pos)
1255 } else if sym.kind == .sum_type && sym.info is ast.SumType {
1256 // Check for circular references through other sum types
1257 mut visited := map[int]bool{}
1258 visited[node.typ.idx()] = true
1259 if c.sumtype_has_circular_ref(variant.typ, node.typ, mut visited) {
1260 c.error('sum type `${node.name}` cannot be defined recursively', variant.pos)
1261 }
1262 }
1263 names_used << variant_name
1264 }
1265}
1266
1267// Checks if the sum type `sum_typ` contains `target_typ` in its variants (directly or indirectly through other sum types)
1268fn (mut c Checker) sumtype_has_circular_ref(sum_typ ast.Type, target_typ ast.Type, mut visited map[int]bool) bool {
1269 sum_sym := c.table.sym(sum_typ)
1270 if sum_sym.kind != .sum_type || sum_sym.info !is ast.SumType {
1271 return false
1272 }
1273 sum_info := sum_sym.info as ast.SumType
1274 for variant in sum_info.variants {
1275 if variant.idx() == target_typ.idx() {
1276 return true
1277 }
1278 // Avoid infinite recursion by tracking visited types
1279 if variant.idx() in visited {
1280 continue
1281 }
1282 variant_sym := c.table.sym(variant)
1283 if variant_sym.kind == .sum_type {
1284 visited[variant.idx()] = true
1285 if c.sumtype_has_circular_ref(variant, target_typ, mut visited) {
1286 return true
1287 }
1288 }
1289 }
1290 return false
1291}
1292
1293fn (mut c Checker) expand_iface_embeds(idecl &ast.InterfaceDecl, level int, iface_embeds []ast.InterfaceEmbedding) []ast.InterfaceEmbedding {
1294 // eprintln('> expand_iface_embeds: idecl.name: ${idecl.name} | level: ${level} | iface_embeds.len: ${iface_embeds.len}')
1295 if level > iface_level_cutoff_limit {
1296 c.error('too many interface embedding levels: ${level}, for interface `${idecl.name}`',
1297 idecl.pos)
1298 return []
1299 }
1300 if iface_embeds.len == 0 {
1301 return []
1302 }
1303 mut res := map[int]ast.InterfaceEmbedding{}
1304 mut ares := []ast.InterfaceEmbedding{}
1305 for ie in iface_embeds {
1306 if iface_decl := c.table.interfaces[ie.typ] {
1307 mut list := iface_decl.embeds.clone()
1308 if !iface_decl.are_embeds_expanded {
1309 list = c.expand_iface_embeds(idecl, level + 1, iface_decl.embeds)
1310 unsafe {
1311 c.table.interfaces[ie.typ].embeds = list
1312 }
1313 unsafe {
1314 c.table.interfaces[ie.typ].are_embeds_expanded = true
1315 }
1316 }
1317 for partial in list {
1318 res[partial.typ] = partial
1319 }
1320 }
1321 res[ie.typ] = ie
1322 }
1323 for _, v in res {
1324 ares << v
1325 }
1326 return ares
1327}
1328
1329fn (mut c Checker) expr_is_immutable_source(expr ast.Expr) bool {
1330 match expr {
1331 ast.Ident {
1332 return match expr.obj {
1333 ast.Var { !expr.obj.is_mut }
1334 ast.ConstField { true }
1335 else { false }
1336 }
1337 }
1338 ast.SelectorExpr {
1339 if expr.expr_type == 0 {
1340 return false
1341 }
1342 typ_sym := c.table.final_sym(c.unwrap_generic(expr.expr_type))
1343 match typ_sym.kind {
1344 .struct {
1345 if field_info := c.table.find_field_with_embeds(typ_sym, expr.field_name) {
1346 return !field_info.is_mut || c.expr_is_immutable_source(expr.expr)
1347 }
1348 }
1349 .interface {
1350 interface_info := typ_sym.info as ast.Interface
1351 if field_info := interface_info.find_field(expr.field_name) {
1352 return !field_info.is_mut || c.expr_is_immutable_source(expr.expr)
1353 }
1354 }
1355 .sum_type {
1356 sumtype_info := typ_sym.info as ast.SumType
1357 if field_info := sumtype_info.find_sum_type_field(expr.field_name) {
1358 return !field_info.is_mut || c.expr_is_immutable_source(expr.expr)
1359 }
1360 }
1361 else {}
1362 }
1363
1364 return c.expr_is_immutable_source(expr.expr)
1365 }
1366 ast.IndexExpr {
1367 return c.expr_is_immutable_source(expr.left)
1368 }
1369 ast.ParExpr {
1370 return c.expr_is_immutable_source(expr.expr)
1371 }
1372 else {
1373 return false
1374 }
1375 }
1376}
1377
1378fn (mut c Checker) type_contains_mutable_aliasing(typ ast.Type, mut checked_types []ast.Type) bool {
1379 unwrapped_typ := c.unwrap_generic(typ.clear_option_and_result())
1380 if unwrapped_typ == 0 {
1381 return false
1382 }
1383 if unwrapped_typ.has_flag(.shared_f) || unwrapped_typ.is_any_kind_of_pointer() {
1384 return true
1385 }
1386 if unwrapped_typ in checked_types {
1387 return false
1388 }
1389 checked_types << unwrapped_typ
1390 sym := c.table.sym(unwrapped_typ)
1391 match sym.info {
1392 ast.Alias {
1393 return c.type_contains_mutable_aliasing(sym.info.parent_type, mut checked_types)
1394 }
1395 ast.Array, ast.Map, ast.Interface {
1396 return true
1397 }
1398 ast.ArrayFixed {
1399 return c.type_contains_mutable_aliasing(sym.info.elem_type, mut checked_types)
1400 }
1401 ast.Struct {
1402 if sym.kind == .struct && sym.language == .v {
1403 for field in c.table.struct_fields(sym) {
1404 if !field.is_mut {
1405 continue
1406 }
1407 if c.type_contains_mutable_aliasing(field.typ, mut checked_types) {
1408 return true
1409 }
1410 }
1411 }
1412 }
1413 ast.SumType {
1414 for variant in sym.info.variants {
1415 if c.type_contains_mutable_aliasing(variant, mut checked_types) {
1416 return true
1417 }
1418 }
1419 }
1420 else {}
1421 }
1422
1423 return false
1424}
1425
1426fn (mut c Checker) type_has_mutable_aliasing(typ ast.Type) bool {
1427 mut checked_types := []ast.Type{}
1428 return c.type_contains_mutable_aliasing(typ, mut checked_types)
1429}
1430
1431fn (mut c Checker) expr_is_mutable_alias_of_immutable_source(expr ast.Expr) bool {
1432 match expr {
1433 ast.Ident {
1434 if expr.obj is ast.Var && expr.obj.is_mut && c.type_has_mutable_aliasing(expr.obj.typ) {
1435 match expr.obj.expr {
1436 ast.UnsafeExpr {
1437 return false
1438 }
1439 ast.Ident, ast.CallExpr, ast.CastExpr, ast.AsCast, ast.ParExpr {
1440 return c.expr_is_immutable_source(expr.obj.expr)
1441 || c.expr_is_mutable_alias_of_immutable_source(expr.obj.expr)
1442 }
1443 else {}
1444 }
1445 }
1446 return false
1447 }
1448 ast.CastExpr {
1449 return c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1450 }
1451 ast.CallExpr {
1452 return c.call_expr_immutable_alias_source(expr) !is ast.EmptyExpr
1453 }
1454 ast.IndexExpr {
1455 return c.type_has_mutable_aliasing(expr.typ) && (c.expr_is_immutable_source(expr.left)
1456 || c.expr_is_mutable_alias_of_immutable_source(expr.left))
1457 }
1458 ast.SelectorExpr {
1459 if expr.expr_type == 0 {
1460 return false
1461 }
1462 typ_sym := c.table.final_sym(c.unwrap_generic(expr.expr_type))
1463 match typ_sym.kind {
1464 .struct {
1465 if field_info := c.table.find_field_with_embeds(typ_sym, expr.field_name) {
1466 mut checked_types := []ast.Type{}
1467 return field_info.is_mut
1468 && c.type_contains_mutable_aliasing(field_info.typ, mut checked_types)
1469 && c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1470 }
1471 }
1472 .interface {
1473 interface_info := typ_sym.info as ast.Interface
1474 if field_info := interface_info.find_field(expr.field_name) {
1475 mut checked_types := []ast.Type{}
1476 return field_info.is_mut
1477 && c.type_contains_mutable_aliasing(field_info.typ, mut checked_types)
1478 && c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1479 }
1480 }
1481 .sum_type {
1482 sumtype_info := typ_sym.info as ast.SumType
1483 if field_info := sumtype_info.find_sum_type_field(expr.field_name) {
1484 mut checked_types := []ast.Type{}
1485 return field_info.is_mut
1486 && c.type_contains_mutable_aliasing(field_info.typ, mut checked_types)
1487 && c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1488 }
1489 }
1490 else {}
1491 }
1492
1493 return false
1494 }
1495 ast.AsCast {
1496 return c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1497 }
1498 ast.ParExpr {
1499 return c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1500 }
1501 ast.UnsafeExpr {
1502 return c.expr_is_mutable_alias_of_immutable_source(expr.expr)
1503 }
1504 else {
1505 return false
1506 }
1507 }
1508}
1509
1510fn (mut c Checker) call_arg_expr_for_param(node ast.CallExpr, func ast.Fn, param_name string) ast.Expr {
1511 mut arg_idx := 0
1512 for i, param in func.params {
1513 if func.is_method && i == 0 {
1514 continue
1515 }
1516 if arg_idx >= node.args.len {
1517 break
1518 }
1519 if param.name == param_name {
1520 return node.args[arg_idx].expr
1521 }
1522 arg_idx++
1523 }
1524 return ast.empty_expr
1525}
1526
1527fn (mut c Checker) return_expr_immutable_alias_source(expr ast.Expr, func ast.Fn, call ast.CallExpr, allow_non_ptr bool) ast.Expr {
1528 match expr {
1529 ast.AsCast {
1530 return c.return_expr_immutable_alias_source(expr.expr, func, call, allow_non_ptr)
1531 }
1532 ast.CastExpr {
1533 return c.return_expr_immutable_alias_source(expr.expr, func, call, allow_non_ptr)
1534 }
1535 ast.CallExpr {
1536 return c.call_expr_immutable_alias_source(expr)
1537 }
1538 ast.Ident {
1539 if expr.obj is ast.Var && expr.obj.is_arg {
1540 arg_expr := c.call_arg_expr_for_param(call, func, expr.name)
1541 if arg_expr !is ast.EmptyExpr && (allow_non_ptr || expr.obj.typ.is_ptr())
1542 && c.expr_is_immutable_source(arg_expr) {
1543 return arg_expr
1544 }
1545 }
1546 if expr.obj is ast.Var && (allow_non_ptr || expr.obj.typ.is_ptr()) {
1547 return c.return_expr_immutable_alias_source(expr.obj.expr, func, call,
1548 allow_non_ptr)
1549 }
1550 return ast.empty_expr
1551 }
1552 ast.IndexExpr {
1553 if c.type_has_mutable_aliasing(expr.typ) {
1554 return c.return_expr_immutable_alias_source(expr.left, func, call, true)
1555 }
1556 return ast.empty_expr
1557 }
1558 ast.SelectorExpr {
1559 if c.type_has_mutable_aliasing(expr.typ) {
1560 return c.return_expr_immutable_alias_source(expr.expr, func, call, true)
1561 }
1562 return ast.empty_expr
1563 }
1564 ast.ParExpr {
1565 return c.return_expr_immutable_alias_source(expr.expr, func, call, allow_non_ptr)
1566 }
1567 ast.PrefixExpr {
1568 if expr.op == .amp {
1569 return c.return_expr_immutable_alias_source(expr.right, func, call, true)
1570 }
1571 return ast.empty_expr
1572 }
1573 ast.UnsafeExpr {
1574 return c.return_expr_immutable_alias_source(expr.expr, func, call, allow_non_ptr)
1575 }
1576 else {
1577 return ast.empty_expr
1578 }
1579 }
1580}
1581
1582fn (mut c Checker) node_immutable_alias_source(node ast.Node, func ast.Fn, call ast.CallExpr) ast.Expr {
1583 match node {
1584 ast.Expr {
1585 if node is ast.AnonFn {
1586 return ast.empty_expr
1587 }
1588 }
1589 ast.Stmt {
1590 if node is ast.FnDecl {
1591 return ast.empty_expr
1592 }
1593 if node is ast.Return {
1594 allow_non_ptr := !call.return_type.is_ptr()
1595 && c.type_has_mutable_aliasing(call.return_type)
1596 for expr in node.exprs {
1597 source := c.return_expr_immutable_alias_source(expr, func, call, allow_non_ptr)
1598 if source !is ast.EmptyExpr {
1599 return source
1600 }
1601 }
1602 return ast.empty_expr
1603 }
1604 }
1605 else {}
1606 }
1607
1608 for child in node.children() {
1609 source := c.node_immutable_alias_source(child, func, call)
1610 if source !is ast.EmptyExpr {
1611 return source
1612 }
1613 }
1614 return ast.empty_expr
1615}
1616
1617fn (c &Checker) immutable_alias_call_key(node ast.CallExpr) string {
1618 if node.concrete_types.len > 0 {
1619 return c.build_generic_call_key(node.fkey(), node.concrete_types)
1620 }
1621 return node.fkey()
1622}
1623
1624fn (mut c Checker) call_expr_immutable_alias_source(node ast.CallExpr) ast.Expr {
1625 if !c.type_has_mutable_aliasing(node.return_type) {
1626 return ast.empty_expr
1627 }
1628 func := c.get_fn_from_call_expr(node) or { return ast.empty_expr }
1629 call_key := c.immutable_alias_call_key(node)
1630 if call_key in c.immutable_alias_analysis_in_progress {
1631 return ast.empty_expr
1632 }
1633 c.immutable_alias_analysis_in_progress[call_key] = true
1634 defer {
1635 c.immutable_alias_analysis_in_progress.delete(call_key)
1636 }
1637 if func.source_fn == unsafe { nil } {
1638 return ast.empty_expr
1639 }
1640 fn_decl := unsafe { &ast.FnDecl(func.source_fn) }
1641 for stmt in fn_decl.stmts {
1642 source := c.node_immutable_alias_source(stmt, func, node)
1643 if source !is ast.EmptyExpr {
1644 return source
1645 }
1646 }
1647 return ast.empty_expr
1648}
1649
1650// fail_if_immutable_to_mutable checks if there is a immutable reference on right-side of assignment for mutable var
1651fn (mut c Checker) fail_if_immutable_to_mutable(left_type ast.Type, right_type ast.Type, right ast.Expr) bool {
1652 if c.inside_unsafe || c.pref.translated || c.file.is_translated {
1653 return true
1654 }
1655 match right {
1656 ast.CastExpr {
1657 iface_sym := c.table.final_sym(c.unwrap_generic(right.typ))
1658 if iface_sym.kind == .interface && iface_sym.info is ast.Interface {
1659 // Only fail if the interface has mutable fields; casting an immutable
1660 // reference to a read-only interface is safe because no mutation is
1661 // possible through the interface.
1662 has_mut_fields := iface_sym.info.fields.any(it.is_mut)
1663 if has_mut_fields {
1664 mut expr := right.expr
1665 c.fail_if_immutable(mut expr)
1666 }
1667 }
1668 }
1669 ast.CallExpr {
1670 if right_type.is_ptr() {
1671 source := c.call_expr_immutable_alias_source(right)
1672 if source !is ast.EmptyExpr {
1673 if source is ast.Ident && c.expr_is_immutable_source(source) {
1674 c.note('`${source.name}` is immutable, cannot have a mutable reference to an immutable object',
1675 source.pos)
1676 } else {
1677 c.note('call result aliases mutable data from an immutable value',
1678 right.pos)
1679 }
1680 return false
1681 }
1682 }
1683 }
1684 ast.Ident {
1685 if right.obj is ast.Var {
1686 if left_type.is_ptr() && !right.is_mut() && right_type.is_ptr() {
1687 c.note('`${right.name}` is immutable, cannot have a mutable reference to an immutable object',
1688 right.pos)
1689 return false
1690 }
1691 if !right.obj.is_mut
1692 && c.table.final_sym(right_type).kind in [.array, .array_fixed, .map] {
1693 c.note('left-side of assignment expects a mutable reference, but variable `${right.name}` is immutable, declare it with `mut` to make it mutable or clone it',
1694 right.pos)
1695 return false
1696 }
1697 }
1698 }
1699 ast.IfExpr {
1700 for branch in right.branches {
1701 stmts := branch.stmts.filter(it is ast.ExprStmt)
1702 if stmts.len > 0 {
1703 last_expr := stmts.last() as ast.ExprStmt
1704 c.fail_if_immutable_to_mutable(left_type, right_type, last_expr.expr)
1705 }
1706 }
1707 }
1708 ast.MatchExpr {
1709 for branch in right.branches {
1710 stmts := branch.stmts.filter(it is ast.ExprStmt)
1711 if stmts.len > 0 {
1712 last_expr := stmts.last() as ast.ExprStmt
1713 c.fail_if_immutable_to_mutable(left_type, right_type, last_expr.expr)
1714 }
1715 }
1716 }
1717 ast.StructInit {
1718 typ_sym := c.table.sym(right.typ)
1719 for init_field in right.init_fields {
1720 if field_info := c.table.find_field_with_embeds(typ_sym, init_field.name) {
1721 if field_info.is_mut {
1722 if init_field.expr is ast.Ident && !init_field.expr.is_mut()
1723 && init_field.typ.is_ptr() {
1724 c.error('`${init_field.expr.name}` is immutable, cannot have a mutable reference to an immutable object',
1725 init_field.pos)
1726 } else if init_field.expr is ast.PrefixExpr {
1727 if init_field.expr.op == .amp && init_field.expr.right is ast.Ident
1728 && !init_field.expr.right.is_mut() {
1729 c.error('`${init_field.expr.right.name}` is immutable, cannot have a mutable reference to an immutable object',
1730 init_field.expr.right.pos)
1731 }
1732 }
1733 }
1734 }
1735 }
1736 }
1737 else {
1738 return true
1739 }
1740 }
1741
1742 return true
1743}
1744
1745// returns name and position of variable that needs write lock
1746// also sets `is_changed` to true (TODO update the name to reflect this?)
1747fn (mut c Checker) fail_if_immutable(mut expr ast.Expr) (string, token.Pos) {
1748 mut to_lock := '' // name of variable that needs lock
1749 mut pos := token.Pos{} // and its position
1750 mut explicit_lock_needed := false
1751 match mut expr {
1752 ast.CastExpr {
1753 if c.table.final_sym(c.unwrap_generic(expr.typ)).kind == .interface {
1754 mut inner_expr := expr.expr
1755 return c.fail_if_immutable(mut inner_expr)
1756 }
1757 return '', expr.pos
1758 }
1759 ast.ComptimeSelector {
1760 mut expr_left := expr.left
1761 if mut expr.left is ast.Ident && expr.left.obj is ast.Var {
1762 c.fail_if_immutable(mut expr_left)
1763 }
1764 return '', expr.pos
1765 }
1766 ast.Ident {
1767 if mut expr.obj is ast.Var {
1768 if !expr.obj.is_mut && !c.pref.translated && !c.file.is_translated
1769 && !c.inside_unsafe {
1770 if expr.obj.smartcasts.len > 0 {
1771 c.error('cannot mutate `${expr.name}` in a non-mut smartcast, use `if mut ${expr.name} ...`',
1772 expr.pos)
1773 } else if c.inside_anon_fn {
1774 c.error('the closure copy of `${expr.name}` is immutable, declare it with `mut` to make it mutable',
1775 expr.pos)
1776 } else {
1777 c.error('`${expr.name}` is immutable, declare it with `mut` to make it mutable',
1778 expr.pos)
1779 }
1780 }
1781 expr.obj.is_changed = true
1782 if expr.obj.typ.share() == .shared_t {
1783 if expr.name !in c.locked_names {
1784 if c.locked_names.len > 0 || c.rlocked_names.len > 0 {
1785 if expr.name in c.rlocked_names {
1786 c.error('${expr.name} has an `rlock` but needs a `lock`', expr.pos)
1787 } else {
1788 c.error('${expr.name} must be added to the `lock` list above',
1789 expr.pos)
1790 }
1791 }
1792 to_lock = expr.name
1793 pos = expr.pos
1794 }
1795 }
1796 } else if expr.obj is ast.ConstField && expr.name in c.const_names {
1797 if !c.pref.translated && c.mod != 'veb' {
1798 // TODO: fix this in c2v, do not allow modification of all consts
1799 // in translated code
1800 c.error('cannot modify constant `${expr.name}`', expr.pos)
1801 }
1802 } else if expr.obj is ast.GlobalField && expr.obj.is_const {
1803 if !c.pref.translated && c.mod != 'veb' {
1804 c.error('cannot modify constant `${expr.name}`', expr.pos)
1805 }
1806 }
1807 }
1808 ast.IndexExpr {
1809 if expr.left_type == 0 {
1810 return to_lock, pos
1811 }
1812 left_sym := c.table.sym(expr.left_type)
1813 if !c.inside_unsafe && left_sym.kind == .array
1814 && c.expr_is_mutable_alias_of_immutable_source(expr.left) {
1815 c.error('`${expr.left}` aliases mutable data from an immutable value, clone it first (or use `unsafe`)',
1816 expr.left.pos())
1817 return '', expr.pos
1818 }
1819 mut elem_type := ast.no_type
1820 mut kind := ''
1821 match left_sym.info {
1822 ast.Array {
1823 elem_type, kind = left_sym.info.elem_type, 'array'
1824 }
1825 ast.ArrayFixed {
1826 elem_type, kind = left_sym.info.elem_type, 'fixed array'
1827 }
1828 ast.Map {
1829 elem_type, kind = left_sym.info.value_type, 'map'
1830 }
1831 else {}
1832 }
1833
1834 if elem_type.has_flag(.shared_f) {
1835 expr_name := ast.Expr(expr).str()
1836 if expr_name !in c.locked_names {
1837 if c.locked_names.len > 0 || c.rlocked_names.len > 0 {
1838 if expr_name in c.rlocked_names {
1839 c.error('${expr_name} has an `rlock` but needs a `lock`',
1840 expr.left.pos().extend(expr.pos))
1841 } else {
1842 c.error('${expr_name} must be added to the `lock` list above',
1843 expr.left.pos().extend(expr.pos))
1844 }
1845 } else {
1846 c.error('you have to create a handle and `lock` it to modify `shared` ${kind} element',
1847 expr.left.pos().extend(expr.pos))
1848 }
1849 return '', expr.pos
1850 }
1851 return '', expr.pos
1852 }
1853 to_lock, pos = c.fail_if_immutable(mut expr.left)
1854 }
1855 ast.ParExpr {
1856 to_lock, pos = c.fail_if_immutable(mut expr.expr)
1857 }
1858 ast.PrefixExpr {
1859 if expr.op == .mul && expr.right is ast.Ident {
1860 // Do not fail if dereference is immutable:
1861 // `*x = foo()` doesn't modify `x`
1862 } else {
1863 to_lock, pos = c.fail_if_immutable(mut expr.right)
1864 }
1865 }
1866 ast.PostfixExpr {
1867 to_lock, pos = c.fail_if_immutable(mut expr.expr)
1868 }
1869 ast.SelectorExpr {
1870 if expr.expr_type == 0 {
1871 return '', expr.pos
1872 }
1873 scope_field := expr.scope.find_struct_field(smartcast_selector_expr_str(expr),
1874 expr.expr_type, expr.field_name)
1875 mut selector_smartcast_is_mut := false
1876 if scope_field != unsafe { nil } {
1877 selector_smartcast_is_mut = scope_field.is_mut
1878 }
1879 if !selector_smartcast_is_mut {
1880 expr_sym := c.table.sym(expr.expr_type)
1881 if field := c.table.find_field(expr_sym, expr.field_name) {
1882 if field.is_mut {
1883 if root_ident := expr.root_ident() {
1884 selector_smartcast_is_mut = root_ident.is_mut()
1885 if !selector_smartcast_is_mut {
1886 if v := expr.scope.find_var(root_ident.name) {
1887 selector_smartcast_is_mut = v.is_mut
1888 }
1889 }
1890 }
1891 }
1892 }
1893 }
1894 if scope_field != unsafe { nil } && scope_field.smartcasts.len > 0
1895 && !selector_smartcast_is_mut && !c.pref.translated && !c.file.is_translated
1896 && !c.inside_unsafe {
1897 expr_str := expr.str()
1898 c.error('cannot mutate `${expr_str}` in a non-mut smartcast, use `if mut ${expr_str} ...`',
1899 expr.pos)
1900 }
1901 // retrieve ast.Field
1902 if !c.ensure_type_exists(expr.expr_type, expr.pos) {
1903 return '', expr.pos
1904 }
1905 mut typ_sym := c.table.final_sym(c.unwrap_generic(expr.expr_type))
1906 match typ_sym.kind {
1907 .struct {
1908 mut has_field := true
1909 mut field_info := c.table.find_field_with_embeds(typ_sym, expr.field_name) or {
1910 has_field = false
1911 ast.StructField{}
1912 }
1913 if !has_field {
1914 type_str := c.table.type_to_str(expr.expr_type)
1915 c.error('unknown field `${type_str}.${expr.field_name}`', expr.pos)
1916 return '', expr.pos
1917 }
1918 if field_info.typ.has_flag(.shared_f) {
1919 expr_name := '${expr.expr}.${expr.field_name}'
1920 if expr_name !in c.locked_names {
1921 if c.locked_names.len > 0 || c.rlocked_names.len > 0 {
1922 if expr_name in c.rlocked_names {
1923 c.error('${expr_name} has an `rlock` but needs a `lock`',
1924 expr.pos)
1925 } else {
1926 c.error('${expr_name} must be added to the `lock` list above',
1927 expr.pos)
1928 }
1929 return '', expr.pos
1930 }
1931 to_lock = expr_name
1932 pos = expr.pos
1933 }
1934 } else {
1935 if !field_info.is_mut && !c.pref.translated && !c.file.is_translated {
1936 type_str := c.table.type_to_str(expr.expr_type)
1937 c.error('field `${expr.field_name}` of struct `${type_str}` is immutable',
1938 expr.pos)
1939 }
1940 if field_info.is_mut && expr.expr_type.is_ptr() && !c.inside_unsafe
1941 && c.expr_is_mutable_alias_of_immutable_source(expr.expr) {
1942 expr_str := ast.Expr(expr).str()
1943 c.error('`${expr_str}` aliases mutable data from an immutable value',
1944 expr.pos)
1945 return '', expr.pos
1946 }
1947 to_lock, pos = c.fail_if_immutable(mut expr.expr)
1948 }
1949 if to_lock != '' {
1950 // No automatic lock for struct access
1951 explicit_lock_needed = true
1952 }
1953 }
1954 .interface {
1955 interface_info := typ_sym.info as ast.Interface
1956 mut field_info := interface_info.find_field(expr.field_name) or {
1957 type_str := c.table.type_to_str(expr.expr_type)
1958 c.error('unknown field `${type_str}.${expr.field_name}`', expr.pos)
1959 return '', expr.pos
1960 }
1961 if !field_info.is_mut {
1962 type_str := c.table.type_to_str(expr.expr_type)
1963 c.error('field `${expr.field_name}` of interface `${type_str}` is immutable',
1964 expr.pos)
1965 return '', expr.pos
1966 }
1967 if expr.expr_type.is_ptr() && !c.inside_unsafe
1968 && c.expr_is_mutable_alias_of_immutable_source(expr.expr) {
1969 expr_str := ast.Expr(expr).str()
1970 c.error('`${expr_str}` aliases mutable data from an immutable value',
1971 expr.pos)
1972 return '', expr.pos
1973 }
1974 c.fail_if_immutable(mut expr.expr)
1975 }
1976 .sum_type {
1977 sumtype_info := typ_sym.info as ast.SumType
1978 mut field_info := sumtype_info.find_sum_type_field(expr.field_name) or {
1979 type_str := c.table.type_to_str(expr.expr_type)
1980 c.error('unknown field `${type_str}.${expr.field_name}`', expr.pos)
1981 return '', expr.pos
1982 }
1983 if !field_info.is_mut {
1984 type_str := c.table.type_to_str(expr.expr_type)
1985 c.error('field `${expr.field_name}` of sumtype `${type_str}` is immutable',
1986 expr.pos)
1987 return '', expr.pos
1988 }
1989 if expr.expr_type.is_ptr() && !c.inside_unsafe
1990 && c.expr_is_mutable_alias_of_immutable_source(expr.expr) {
1991 expr_str := ast.Expr(expr).str()
1992 c.error('`${expr_str}` aliases mutable data from an immutable value',
1993 expr.pos)
1994 return '', expr.pos
1995 }
1996 c.fail_if_immutable(mut expr.expr)
1997 }
1998 .array, .string {
1999 // should only happen in `builtin` and unsafe blocks
2000 inside_builtin := c.file.mod.name == 'builtin'
2001 if !inside_builtin && !c.inside_unsafe {
2002 c.error('`${typ_sym.kind}` can not be modified', expr.pos)
2003 return '', expr.pos
2004 }
2005 }
2006 .aggregate, .placeholder, .generic_inst {
2007 c.fail_if_immutable(mut expr.expr)
2008 }
2009 else {
2010 c.error('unexpected symbol `${typ_sym.kind}`', expr.pos)
2011 return '', expr.pos
2012 }
2013 }
2014 }
2015 ast.CallExpr {
2016 // TODO: should only work for builtin method
2017 if expr.name == 'slice' {
2018 to_lock, pos = c.fail_if_immutable(mut expr.left)
2019 if to_lock != '' {
2020 // No automatic lock for array slicing (yet(?))
2021 explicit_lock_needed = true
2022 }
2023 }
2024 if !c.inside_unsafe {
2025 source := c.call_expr_immutable_alias_source(expr)
2026 if source !is ast.EmptyExpr {
2027 if source is ast.Ident && c.expr_is_immutable_source(source) {
2028 c.error('`${source.name}` is immutable, cannot have a mutable reference to an immutable object',
2029 source.pos)
2030 } else {
2031 c.error('`${expr}` aliases mutable data from an immutable value', expr.pos)
2032 }
2033 return '', expr.pos
2034 }
2035 }
2036 }
2037 ast.ArrayInit {
2038 c.error('array literal can not be modified', expr.pos)
2039 return '', expr.pos
2040 }
2041 ast.StructInit {
2042 return '', expr.pos
2043 }
2044 ast.InfixExpr {
2045 return '', expr.pos
2046 }
2047 ast.IfExpr {
2048 for mut branch in expr.branches {
2049 mut last_expr := (branch.stmts.last() as ast.ExprStmt).expr
2050 c.fail_if_immutable(mut last_expr)
2051 }
2052 return '', expr.pos
2053 }
2054 ast.AsCast {
2055 to_lock, pos = c.fail_if_immutable(mut expr.expr)
2056 }
2057 ast.UnsafeExpr {
2058 to_lock, pos = c.fail_if_immutable(mut expr.expr)
2059 }
2060 ast.Nil {
2061 return '', expr.pos
2062 }
2063 else {
2064 if !expr.is_pure_literal() {
2065 c.error('unexpected expression `${expr.type_name()}`', expr.pos())
2066 return '', expr.pos()
2067 }
2068 }
2069 }
2070
2071 if explicit_lock_needed {
2072 c.error('`${to_lock}` is `shared` and needs explicit lock for `${expr.type_name()}`', pos)
2073 to_lock = ''
2074 }
2075 return to_lock, pos
2076}
2077
2078fn (mut c Checker) resolve_method_for_concrete_type(method ast.Fn, typ_sym &ast.TypeSymbol) ast.Fn {
2079 mut resolved_method := method
2080 concrete_types := c.concrete_types_for_type_symbol(typ_sym)
2081 generic_names := c.generic_names_for_type_parent(typ_sym)
2082 if generic_names.len == 0 || generic_names.len != concrete_types.len {
2083 return resolved_method
2084 }
2085 if rt := c.table.convert_generic_type(resolved_method.return_type, generic_names,
2086 concrete_types)
2087 {
2088 resolved_method.return_type = rt
2089 }
2090 resolved_method.params = resolved_method.params.clone()
2091 for mut param in resolved_method.params {
2092 if pt := c.table.convert_generic_type(param.typ, generic_names, concrete_types) {
2093 param.typ = pt
2094 }
2095 }
2096 return resolved_method
2097}
2098
2099fn (c &Checker) concrete_types_for_type_symbol(typ_sym &ast.TypeSymbol) []ast.Type {
2100 match typ_sym.info {
2101 ast.Struct, ast.Interface, ast.SumType {
2102 mut concrete_types := typ_sym.info.concrete_types.clone()
2103 if concrete_types.len == 0
2104 && typ_sym.generic_types.len == typ_sym.info.generic_types.len
2105 && typ_sym.generic_types != typ_sym.info.generic_types {
2106 concrete_types = typ_sym.generic_types.clone()
2107 }
2108 return concrete_types
2109 }
2110 ast.GenericInst {
2111 return typ_sym.info.concrete_types.clone()
2112 }
2113 else {}
2114 }
2115
2116 return []ast.Type{}
2117}
2118
2119fn (c &Checker) generic_names_for_type_parent(typ_sym &ast.TypeSymbol) []string {
2120 match typ_sym.info {
2121 ast.Struct, ast.Interface, ast.SumType {
2122 if !typ_sym.info.parent_type.has_flag(.generic) {
2123 return []string{}
2124 }
2125 parent_sym := c.table.sym(typ_sym.info.parent_type)
2126 match parent_sym.info {
2127 ast.Struct, ast.Interface, ast.SumType {
2128 return parent_sym.info.generic_types.map(c.table.sym(it).name)
2129 }
2130 else {}
2131 }
2132 }
2133 ast.GenericInst {
2134 parent_sym := c.table.sym(ast.new_type(typ_sym.info.parent_idx))
2135 match parent_sym.info {
2136 ast.Struct, ast.Interface, ast.SumType {
2137 return parent_sym.info.generic_types.map(c.table.sym(it).name)
2138 }
2139 else {}
2140 }
2141 }
2142 else {}
2143 }
2144
2145 return []string{}
2146}
2147
2148fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos token.Pos) bool {
2149 return c.type_implements_with_mut_receiver(typ, interface_type, pos, false)
2150}
2151
2152fn (mut c Checker) type_implements_allowing_mut_receiver(typ ast.Type, interface_type ast.Type, pos token.Pos) bool {
2153 return c.type_implements_with_mut_receiver(typ, interface_type, pos, true)
2154}
2155
2156fn (mut c Checker) type_implements_with_mut_receiver(typ ast.Type, interface_type ast.Type, pos token.Pos, allow_mut_receiver bool) bool {
2157 mut resolved_interface_type := c.unwrap_generic(interface_type)
2158 if typ == resolved_interface_type {
2159 return true
2160 }
2161 $if debug_interface_type_implements ? {
2162 eprintln('> type_implements typ: ${typ.debug()} (`${c.table.type_to_str(typ)}`) | inter_typ: ${resolved_interface_type.debug()} (`${c.table.type_to_str(resolved_interface_type)}`)')
2163 }
2164 utyp := c.unwrap_generic(typ)
2165 styp := c.table.type_to_str(utyp)
2166 typ_sym := c.table.sym(utyp)
2167 mut inter_sym := c.table.final_sym(resolved_interface_type)
2168 if !inter_sym.is_pub && inter_sym.mod !in [typ_sym.mod, c.mod] && typ_sym.mod != 'builtin' {
2169 c.error('`${styp}` cannot implement private interface `${inter_sym.name}` of other module',
2170 pos)
2171 return false
2172 }
2173
2174 // small hack for JS.Any type. Since `any` in regular V is getting deprecated we have our own JS.Any type for JS backend.
2175 if typ_sym.name == 'JS.Any' {
2176 return true
2177 }
2178 if inter_sym.kind == .interface && c.table.get_attrs(inter_sym).any(it.name == 'single_impl') {
2179 if pos.file_idx != -1 {
2180 c.error('cannot use `${styp}` as `${inter_sym.name}` without an explicit cast (e.g. `${inter_sym.name}(value)`)',
2181 pos)
2182 }
2183 return false
2184 }
2185 if typ_sym.kind == .function && inter_sym.name != 'JS.Any' {
2186 c.error('cannot implement interface `${inter_sym.name}` using function', pos)
2187 return false
2188 }
2189 if mut inter_sym.info is ast.Interface {
2190 mut generic_type := resolved_interface_type
2191 mut generic_info := inter_sym.info
2192 if inter_sym.info.parent_type.has_flag(.generic) && (inter_sym.info.concrete_types.len == 0
2193 || inter_sym.info.concrete_types.any(it.has_flag(.generic))) {
2194 parent_sym := c.table.sym(inter_sym.info.parent_type)
2195 if parent_sym.info is ast.Interface {
2196 generic_type = inter_sym.info.parent_type
2197 generic_info = parent_sym.info
2198 }
2199 }
2200 mut inferred_type := resolved_interface_type
2201 is_fully_concrete := inter_sym.info.concrete_types.len > 0
2202 && !inter_sym.info.concrete_types.any(it.has_flag(.generic))
2203 if generic_info.is_generic && !is_fully_concrete {
2204 inferred_type = c.unwrap_generic_interface(typ, generic_type, pos)
2205 if inferred_type == 0 {
2206 return false
2207 }
2208 }
2209 if inter_sym.info.is_generic && !is_fully_concrete {
2210 if inferred_type == resolved_interface_type {
2211 // terminate early, since otherwise we get an infinite recursion/segfault:
2212 return false
2213 }
2214 return c.type_implements_with_mut_receiver(typ, inferred_type, pos, allow_mut_receiver)
2215 }
2216 }
2217 if inter_sym.kind == .generic_inst {
2218 generic_inst_info := inter_sym.info as ast.GenericInst
2219 if !generic_inst_info.concrete_types.any(it.has_flag(.generic)) {
2220 c.table.generic_insts_to_concrete()
2221 inter_sym = c.table.final_sym(resolved_interface_type)
2222 }
2223 }
2224 // do not check the same type more than once
2225 if mut inter_sym.info is ast.Interface {
2226 if inter_sym.info.has_implementor(utyp, allow_mut_receiver) {
2227 return true
2228 }
2229 }
2230 if utyp.idx() == resolved_interface_type.idx() {
2231 // same type -> already casted to the interface
2232 return true
2233 }
2234 if resolved_interface_type.idx() == ast.error_type_idx && utyp.idx() == ast.none_type_idx {
2235 // `none` "implements" the Error interface
2236 return true
2237 }
2238 is_interface_upcast := typ_sym.kind == .interface && inter_sym.kind == .interface
2239 && !styp.starts_with('JS.') && !inter_sym.name.starts_with('JS.')
2240 if is_interface_upcast {
2241 if mut inter_sym.info is ast.Interface {
2242 if inter_sym.info.methods.len == 0 && inter_sym.info.fields.len == 0
2243 && inter_sym.info.embeds.len == 0 {
2244 // Preserve the source interface's known variants so the empty interface keeps
2245 // the original dynamic type for later `is`/`as` checks and conversions.
2246 mut source_types := []ast.Type{}
2247 match typ_sym.info {
2248 ast.Interface {
2249 source_types = typ_sym.info.implementor_types(true)
2250 }
2251 else {}
2252 }
2253
2254 mut target_variants := c.table.iface_types[inter_sym.name]
2255 for variant in source_types {
2256 variant_sym := c.table.sym(ast.mktyp(variant))
2257 if variant in [ast.nil_type, ast.none_type] || variant_sym.kind == .interface {
2258 continue
2259 }
2260 if !inter_sym.info.types.contains(variant) {
2261 inter_sym.info.types << variant
2262 }
2263 if variant !in target_variants {
2264 target_variants << variant
2265 }
2266 }
2267 c.table.iface_types[inter_sym.name] = target_variants
2268 if !inter_sym.info.types.contains(ast.voidptr_type) {
2269 inter_sym.info.types << ast.voidptr_type
2270 }
2271 return true
2272 }
2273 }
2274 if !c.table.interface_inherits_interface(utyp, resolved_interface_type) {
2275 c.error('cannot implement interface `${inter_sym.name}` with a different interface `${styp}`',
2276 pos)
2277 return false
2278 }
2279 }
2280 interface_sym := c.table.sym(resolved_interface_type)
2281 mut interface_generic_names := []string{}
2282 mut interface_concrete_types := []ast.Type{}
2283 mut interface_info := ast.Interface{}
2284 mut has_resolved_interface_info := false
2285 if interface_sym.kind == .interface && interface_sym.info is ast.Interface {
2286 // For concrete generic interfaces (has concrete_types, not generic, has parent),
2287 // resolve methods from the root generic parent interface to get correct method
2288 // signatures. The methods stored in the concrete interface's info may have been
2289 // resolved with wrong type parameters for nested generic types.
2290 if interface_sym.info.concrete_types.len > 0
2291 && !interface_sym.info.concrete_types.any(it.has_flag(.generic))
2292 && interface_sym.info.parent_type != 0 {
2293 mut root_type := interface_sym.info.parent_type
2294 for {
2295 rs := c.table.sym(root_type)
2296 if rs.info is ast.Interface {
2297 if rs.info.parent_type != 0 {
2298 root_type = rs.info.parent_type
2299 } else {
2300 break
2301 }
2302 } else {
2303 break
2304 }
2305 }
2306 root_sym := c.table.sym(root_type)
2307 if root_sym.info is ast.Interface {
2308 if root_sym.info.is_generic {
2309 interface_info = root_sym.info
2310 interface_generic_names = interface_info.generic_types.map(c.table.sym(it).name)
2311 interface_concrete_types = interface_sym.info.concrete_types.clone()
2312 has_resolved_interface_info = true
2313 }
2314 }
2315 }
2316 if !has_resolved_interface_info {
2317 interface_info = interface_sym.info
2318 interface_generic_names = interface_info.generic_types.map(c.table.sym(it).name)
2319 interface_concrete_types = interface_info.concrete_types.clone()
2320 if interface_concrete_types.len == 0
2321 && interface_sym.generic_types.len == interface_generic_names.len {
2322 interface_concrete_types = interface_sym.generic_types.clone()
2323 }
2324 has_resolved_interface_info = true
2325 }
2326 } else if interface_sym.kind == .generic_inst {
2327 parent_sym := c.table.sym(ast.new_type((interface_sym.info as ast.GenericInst).parent_idx))
2328 if parent_sym.info is ast.Interface {
2329 interface_info = parent_sym.info
2330 interface_generic_names = interface_info.generic_types.map(c.table.sym(it).name)
2331 interface_concrete_types =
2332 (interface_sym.info as ast.GenericInst).concrete_types.clone()
2333 has_resolved_interface_info = true
2334 }
2335 }
2336 imethods := if has_resolved_interface_info {
2337 mut methods := []ast.Fn{}
2338 for imethod in interface_info.methods {
2339 mut resolved_method := imethod
2340 if interface_generic_names.len == interface_concrete_types.len {
2341 if resolved_return_type := c.table.convert_generic_type(imethod.return_type,
2342 interface_generic_names, interface_concrete_types)
2343 {
2344 resolved_method.return_type = resolved_return_type
2345 }
2346 mut resolved_params := resolved_method.params.clone()
2347 for i in 0 .. resolved_params.len {
2348 if resolved_param_type := c.table.convert_generic_type(resolved_params[i].typ,
2349 interface_generic_names, interface_concrete_types)
2350 {
2351 resolved_params[i].typ = resolved_param_type
2352 }
2353 }
2354 resolved_method.params = resolved_params
2355 }
2356 methods << resolved_method
2357 }
2358 methods
2359 } else {
2360 inter_sym.methods
2361 }
2362 mut requires_mut_receiver := false
2363 // voidptr is an escape hatch, it should be allowed to be passed
2364 if utyp != ast.voidptr_type && utyp != ast.nil_type && !(interface_type.has_flag(.option)
2365 && utyp == ast.none_type) {
2366 mut are_methods_implemented := true
2367
2368 // Verify methods
2369 for imethod in imethods {
2370 if c.table.is_compatible_auto_str_method(imethod) && utyp.nr_muls() == 0
2371 && typ_sym.kind == .char {
2372 c.error("`${styp}` doesn't implement method `${imethod.name}` of interface `${inter_sym.name}`",
2373 pos)
2374 are_methods_implemented = false
2375 continue
2376 }
2377 mut method := typ_sym.find_method_with_generic_parent(imethod.name) or {
2378 c.table.find_method_with_embeds(typ_sym, imethod.name) or {
2379 if c.table.type_has_implicit_str_method(utyp, imethod) {
2380 are_methods_implemented = true
2381 continue
2382 }
2383 c.error("`${styp}` doesn't implement method `${imethod.name}` of interface `${inter_sym.name}`",
2384 pos)
2385 are_methods_implemented = false
2386 continue
2387 }
2388 }
2389 method = c.resolve_method_for_concrete_type(method, typ_sym)
2390
2391 mut checked_method := ast.Fn{
2392 ...imethod
2393 params: imethod.params.clone()
2394 }
2395 mut used_mut_receiver := false
2396 if allow_mut_receiver && checked_method.params.len > 0 && method.params.len > 0
2397 && !checked_method.params[0].is_mut && method.params[0].is_mut {
2398 checked_method.params[0] = ast.Param{
2399 ...checked_method.params[0]
2400 is_mut: true
2401 }
2402 used_mut_receiver = true
2403 }
2404 msg := c.table.is_same_method(checked_method, method)
2405 if msg.len > 0 {
2406 sig := c.table.fn_signature(imethod, skip_receiver: false)
2407 typ_sig := c.table.fn_signature(method, skip_receiver: false)
2408 c.add_error_detail('${inter_sym.name} has `${sig}`')
2409 c.add_error_detail(' ${typ_sym.name} has `${typ_sig}`')
2410 c.error('`${styp}` incorrectly implements method `${imethod.name}` of interface `${inter_sym.name}`: ${msg}',
2411 pos)
2412 return false
2413 }
2414 if used_mut_receiver {
2415 requires_mut_receiver = true
2416 }
2417 }
2418
2419 if !are_methods_implemented {
2420 return false
2421 }
2422 }
2423 // Verify fields
2424 if mut inter_sym.info is ast.Interface {
2425 for ifield in inter_sym.info.fields {
2426 if field := c.table.find_field_with_embeds(typ_sym, ifield.name) {
2427 if ifield.typ != field.typ {
2428 exp := c.table.type_to_str(ifield.typ)
2429 got := c.table.type_to_str(field.typ)
2430 c.error('`${styp}` incorrectly implements field `${ifield.name}` of interface `${inter_sym.name}`, expected `${exp}`, got `${got}`',
2431 pos)
2432 return false
2433 } else if ifield.is_mut && !(field.is_mut || field.is_global) {
2434 c.error('`${styp}` incorrectly implements interface `${inter_sym.name}`, field `${ifield.name}` must be mutable',
2435 pos)
2436 return false
2437 }
2438 continue
2439 }
2440 // voidptr is an escape hatch, it should be allowed to be passed
2441 if utyp != ast.voidptr_type && utyp != ast.nil_type {
2442 c.error("`${styp}` doesn't implement field `${ifield.name}` of interface `${inter_sym.name}`",
2443 pos)
2444 }
2445 }
2446 if !is_interface_upcast && utyp != ast.voidptr_type && utyp != ast.nil_type
2447 && utyp != ast.none_type {
2448 if requires_mut_receiver {
2449 if !inter_sym.info.mut_types.contains(utyp) {
2450 inter_sym.info.mut_types << utyp
2451 }
2452 } else if !inter_sym.info.types.contains(utyp) {
2453 inter_sym.info.types << utyp
2454 }
2455 }
2456 if !inter_sym.info.types.contains(ast.voidptr_type) {
2457 inter_sym.info.types << ast.voidptr_type
2458 }
2459 } else if has_resolved_interface_info {
2460 for ifield in interface_info.fields {
2461 mut resolved_ifield := ifield
2462 if interface_generic_names.len == interface_concrete_types.len {
2463 if ft := c.table.convert_generic_type(ifield.typ, interface_generic_names,
2464 interface_concrete_types)
2465 {
2466 resolved_ifield.typ = ft
2467 }
2468 }
2469 if field := c.table.find_field_with_embeds(typ_sym, resolved_ifield.name) {
2470 if resolved_ifield.typ != field.typ {
2471 exp := c.table.type_to_str(resolved_ifield.typ)
2472 got := c.table.type_to_str(field.typ)
2473 c.error('`${styp}` incorrectly implements field `${resolved_ifield.name}` of interface `${inter_sym.name}`, expected `${exp}`, got `${got}`',
2474 pos)
2475 return false
2476 } else if resolved_ifield.is_mut && !(field.is_mut || field.is_global) {
2477 c.error('`${styp}` incorrectly implements interface `${inter_sym.name}`, field `${resolved_ifield.name}` must be mutable',
2478 pos)
2479 return false
2480 }
2481 continue
2482 }
2483 if utyp != ast.voidptr_type && utyp != ast.nil_type {
2484 c.error("`${styp}` doesn't implement field `${resolved_ifield.name}` of interface `${inter_sym.name}`",
2485 pos)
2486 }
2487 }
2488 }
2489 return true
2490}
2491
2492// helper for expr_or_block_err
2493fn is_field_to_description(expr_name string, is_field bool) string {
2494 return if is_field {
2495 'field `${expr_name}` is not'
2496 } else {
2497 'function `${expr_name}` does not return'
2498 }
2499}
2500
2501fn (mut c Checker) expr_or_block_err(kind ast.OrKind, expr_name string, pos token.Pos, is_field bool) {
2502 match kind {
2503 .absent {
2504 // do nothing, most common case; do not be tempted to move the call to is_field_to_description above it, since that will slow it down
2505 }
2506 .block {
2507 obj_does_not_return_or_is_not := is_field_to_description(expr_name, is_field)
2508 c.error('unexpected `or` block, the ${obj_does_not_return_or_is_not} an Option or a Result',
2509 pos)
2510 }
2511 .propagate_option {
2512 obj_does_not_return_or_is_not := is_field_to_description(expr_name, is_field)
2513 c.error('unexpected `?`, the ${obj_does_not_return_or_is_not} an Option', pos)
2514 }
2515 .propagate_result {
2516 obj_does_not_return_or_is_not := is_field_to_description(expr_name, is_field)
2517 c.error('unexpected `!`, the ${obj_does_not_return_or_is_not} a Result', pos)
2518 }
2519 }
2520}
2521
2522// return the actual type of the expression, once the result or option type is handled
2523fn (mut c Checker) check_expr_option_or_result_call(expr ast.Expr, ret_type ast.Type) ast.Type {
2524 if ret_type.idx() == 0 {
2525 return ret_type
2526 }
2527 match expr {
2528 ast.CallExpr {
2529 mut expr_ret_type := expr.return_type
2530 if expr_ret_type != 0 && c.table.sym(expr_ret_type).kind == .alias {
2531 unaliased_ret_type := c.table.unaliased_type(expr_ret_type)
2532 if unaliased_ret_type.has_option_or_result() {
2533 expr_ret_type = unaliased_ret_type
2534 }
2535 }
2536 // var with option function
2537 if expr.is_fn_var && expr.fn_var_type.has_option_or_result()
2538 && expr.or_block.kind == .absent {
2539 ret_sym := c.table.sym(expr.fn_var_type)
2540 if expr.fn_var_type.has_flag(.option) {
2541 c.error('type `?${ret_sym.name}` is an Option, it must be unwrapped first',
2542 expr.pos)
2543 } else {
2544 c.error('type `?${ret_sym.name}` is an Result, it must be unwrapped first',
2545 expr.pos)
2546 }
2547 }
2548 if expr_ret_type.has_option_or_result() {
2549 if expr.or_block.kind == .absent {
2550 ret_sym := c.table.sym(expr_ret_type)
2551 if expr_ret_type.has_flag(.result)
2552 || (expr_ret_type.has_flag(.option) && ret_sym.kind == .multi_return) {
2553 ret_typ_tok := if expr_ret_type.has_flag(.option) { '?' } else { '!' }
2554 if c.inside_defer {
2555 c.error('${expr.name}() returns `${ret_typ_tok}${ret_sym.name}`, so it should have an `or {}` block at the end',
2556 expr.pos)
2557 } else {
2558 c.error('${expr.name}() returns `${ret_typ_tok}${ret_sym.name}`, so it should have either an `or {}` block, or `${ret_typ_tok}` at the end',
2559 expr.pos)
2560 }
2561 }
2562 } else {
2563 last_cur_or_expr := c.cur_or_expr
2564 c.cur_or_expr = &expr.or_block
2565 or_block_value_type := expr_ret_type.clear_option_and_result()
2566 or_block_unaliased_value_type :=
2567 c.table.fully_unaliased_type(or_block_value_type)
2568 mut or_block_ret_type := or_block_value_type
2569 if ret_type == ast.void_type {
2570 or_block_ret_type = ret_type
2571 } else if ret_type.has_option_or_result() {
2572 ret_value_type :=
2573 c.table.fully_unaliased_type(ret_type).clear_option_and_result()
2574 if ret_value_type == or_block_unaliased_value_type {
2575 or_block_ret_type = ret_type
2576 }
2577 }
2578 c.check_or_expr(expr.or_block, or_block_ret_type, expr_ret_type, expr)
2579 c.cur_or_expr = last_cur_or_expr
2580 }
2581 return if expr.or_block.kind == .absent {
2582 ret_type.clear_flag(.result)
2583 } else {
2584 ret_type.clear_flag(.option).clear_flag(.result)
2585 }
2586 } else {
2587 c.expr_or_block_err(expr.or_block.kind, expr.name, expr.or_block.pos, false)
2588 }
2589 }
2590 ast.SelectorExpr {
2591 if c.table.sym(ret_type).kind != .chan {
2592 if expr.typ.has_option_or_result() {
2593 with_modifier_kind := if expr.typ.has_flag(.option) {
2594 'an Option'
2595 } else {
2596 'a Result'
2597 }
2598 with_modifier := if expr.typ.has_flag(.option) { '?' } else { '!' }
2599 if expr.typ.has_flag(.result) && expr.or_block.kind == .absent {
2600 if c.inside_defer {
2601 c.error('field `${expr.field_name}` is ${with_modifier_kind}, so it should have an `or {}` block at the end',
2602 expr.pos)
2603 } else {
2604 c.error('field `${expr.field_name}` is ${with_modifier_kind}, so it should have either an `or {}` block, or `${with_modifier}` at the end',
2605 expr.pos)
2606 }
2607 } else {
2608 if expr.or_block.kind != .absent {
2609 last_cur_or_expr := c.cur_or_expr
2610 c.cur_or_expr = &expr.or_block
2611 or_block_value_type := expr.typ.clear_option_and_result()
2612 or_block_unaliased_value_type :=
2613 c.table.fully_unaliased_type(or_block_value_type)
2614 mut or_block_ret_type := or_block_value_type
2615 if ret_type.has_option_or_result() {
2616 ret_value_type :=
2617 c.table.fully_unaliased_type(ret_type).clear_option_and_result()
2618 if ret_value_type == or_block_unaliased_value_type {
2619 or_block_ret_type = ret_type
2620 }
2621 }
2622 c.check_or_expr(expr.or_block, or_block_ret_type, expr.typ, expr)
2623 c.cur_or_expr = last_cur_or_expr
2624 }
2625 }
2626 return if expr.or_block.kind == .absent {
2627 ret_type.clear_flag(.result)
2628 } else {
2629 ret_type.clear_flag(.option).clear_flag(.result)
2630 }
2631 } else {
2632 c.expr_or_block_err(expr.or_block.kind, expr.field_name, expr.or_block.pos,
2633 true)
2634 }
2635 }
2636 }
2637 ast.IndexExpr {
2638 if expr.or_expr.kind != .absent {
2639 // When the IndexExpr has already been processed by index_expr()
2640 // (indicated by expr.typ being set), skip re-checking the or block
2641 // since it was already validated. This prevents the duplicate call
2642 // from assign.v from rejecting valid `none` in option map or blocks.
2643 if expr.typ != 0 {
2644 if ret_type.has_flag(.option) && expr.or_expr.stmts.len > 0 {
2645 last := expr.or_expr.stmts.last()
2646 if last is ast.ExprStmt && last.expr is ast.None {
2647 return ret_type
2648 }
2649 }
2650 return ret_type.clear_option_and_result()
2651 }
2652 mut return_none_or_error := false
2653 if expr.or_expr.stmts.len > 0 {
2654 last_stmt := expr.or_expr.stmts.last()
2655 if last_stmt is ast.ExprStmt {
2656 if c.inside_return && last_stmt.typ in [ast.none_type, ast.error_type] {
2657 return_none_or_error = true
2658 }
2659 }
2660 }
2661 if return_none_or_error {
2662 c.check_expr_option_or_result_call(expr.or_expr, c.table.cur_fn.return_type)
2663 } else {
2664 last_cur_or_expr := c.cur_or_expr
2665 c.cur_or_expr = &expr.or_expr
2666 c.check_or_expr(expr.or_expr, ret_type, ret_type.set_flag(.result), expr)
2667 c.cur_or_expr = last_cur_or_expr
2668 }
2669 // When the map value type is itself optional and the or block
2670 // returns none, preserve the option type so the result is ?T.
2671 if ret_type.has_flag(.option) && expr.or_expr.stmts.len > 0 {
2672 last := expr.or_expr.stmts.last()
2673 if last is ast.ExprStmt && last.expr is ast.None {
2674 return ret_type
2675 }
2676 }
2677 return ret_type.clear_option_and_result()
2678 } else if expr.left is ast.SelectorExpr && expr.left_type.has_option_or_result() {
2679 with_modifier_kind := if expr.left_type.has_flag(.option) {
2680 'an Option'
2681 } else {
2682 'a Result'
2683 }
2684 with_modifier := if expr.left_type.has_flag(.option) { '?' } else { '!' }
2685 c.error('field `${expr.left.field_name}` is ${with_modifier_kind}, so it should have either an `or {}` block, or `${with_modifier}` at the end',
2686 expr.left.pos)
2687 }
2688 }
2689 ast.CastExpr {
2690 c.check_expr_option_or_result_call(expr.expr, ret_type)
2691 }
2692 ast.AsCast {
2693 c.check_expr_option_or_result_call(expr.expr, ret_type)
2694 }
2695 ast.ParExpr {
2696 c.check_expr_option_or_result_call(expr.expr, ret_type)
2697 }
2698 ast.InfixExpr {
2699 left_ret_type := if expr.left_type != 0 { expr.left_type } else { ret_type }
2700 right_ret_type := if expr.right_type != 0 { expr.right_type } else { ret_type }
2701 c.check_expr_option_or_result_call(expr.left, left_ret_type)
2702 c.check_expr_option_or_result_call(expr.right, right_ret_type)
2703 }
2704 else {}
2705 }
2706
2707 return ret_type
2708}
2709
2710fn (mut c Checker) expr_unhandled_option_type(expr ast.Expr) ast.Type {
2711 match expr {
2712 ast.Ident {
2713 if expr.or_expr.kind == .absent && expr.info is ast.IdentVar {
2714 return c.type_resolver.get_type_or_default(expr, expr.info.typ)
2715 }
2716 }
2717 ast.CallExpr {
2718 if expr.or_block.kind == .absent {
2719 return expr.return_type
2720 }
2721 }
2722 ast.SelectorExpr {
2723 if expr.or_block.kind == .absent {
2724 return expr.typ
2725 }
2726 }
2727 ast.IndexExpr {
2728 if expr.or_expr.kind == .absent {
2729 return expr.typ
2730 }
2731 }
2732 ast.ParExpr {
2733 return c.expr_unhandled_option_type(expr.expr)
2734 }
2735 else {}
2736 }
2737
2738 return ast.void_type
2739}
2740
2741fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_return_type ast.Type, expr ast.Expr) {
2742 if node.kind == .propagate_option {
2743 if c.table.cur_fn != unsafe { nil } && !c.table.cur_fn.return_type.has_flag(.option)
2744 && !c.table.cur_fn.is_main && !c.table.cur_fn.is_test && !c.inside_const {
2745 c.add_instruction_for_option_type()
2746 if expr is ast.Ident {
2747 c.error('to propagate the Option, `${c.table.cur_fn.name}` must return an Option type',
2748 expr.pos)
2749 } else {
2750 c.error('to propagate the call, `${c.table.cur_fn.name}` must return an Option type',
2751 node.pos)
2752 }
2753 }
2754 if expr !in [ast.Ident, ast.SelectorExpr] && !expr_return_type.has_flag(.option) {
2755 if expr_return_type.has_flag(.result) {
2756 c.error('propagating a Result like an Option is deprecated, use `foo()!` instead of `foo()?`',
2757 node.pos)
2758 } else {
2759 c.error('to propagate an Option, the call must also return an Option type',
2760 node.pos)
2761 }
2762 }
2763 return
2764 }
2765 if node.kind == .propagate_result {
2766 if c.table.cur_fn != unsafe { nil } && !c.table.cur_fn.return_type.has_flag(.result)
2767 && !c.table.cur_fn.is_main && !c.table.cur_fn.is_test && !c.inside_const {
2768 c.add_instruction_for_result_type()
2769 c.error('to propagate the call, `${c.table.cur_fn.name}` must return a Result type',
2770 node.pos)
2771 }
2772 if !expr_return_type.has_flag(.result) {
2773 c.error('to propagate a Result, the call must also return a Result type', node.pos)
2774 }
2775 return
2776 }
2777 if node.stmts.len == 0 {
2778 if expr is ast.CallExpr && expr.is_return_used {
2779 // x := f() or {}, || f() or {} etc
2780 c.error('expression requires a non empty `or {}` block', node.pos)
2781 } else if expr !is ast.CallExpr && ret_type != ast.void_type {
2782 // _ := sql db {... } or { }
2783 c.error('expression requires a non empty `or {}` block', node.pos)
2784 }
2785 // allow `f() or {}`
2786 return
2787 } else if !node.err_used {
2788 if err_var := node.scope.find_var('err') {
2789 c.cur_or_expr.err_used = err_var.is_used
2790 }
2791 }
2792 mut valid_stmts := node.stmts.filter(it !is ast.SemicolonStmt)
2793 mut last_stmt := if valid_stmts.len > 0 { valid_stmts.last() } else { node.stmts.last() }
2794 if !node.err_used && c.cur_or_expr != unsafe { nil } {
2795 if last_stmt is ast.ExprStmt {
2796 if last_stmt.expr is ast.Ident && last_stmt.expr.name == 'err' {
2797 c.cur_or_expr.err_used = true
2798 }
2799 }
2800 }
2801 allow_none_as_option_value := expr is ast.IndexExpr && ret_type.has_flag(.option)
2802 default_or_type := if expr is ast.IndexExpr && ret_type.has_flag(.option) {
2803 ret_type
2804 } else {
2805 expr_return_type.clear_option_and_result()
2806 }
2807 if expr !is ast.CallExpr || (expr is ast.CallExpr && expr.is_return_used) {
2808 // requires a block returning an unwrapped type of expr return type
2809 c.check_or_last_stmt(mut last_stmt, ret_type, default_or_type, allow_none_as_option_value)
2810 } else {
2811 // allow f() or { var = 123 }
2812 c.check_or_last_stmt(mut last_stmt, ast.void_type, default_or_type,
2813 allow_none_as_option_value)
2814 }
2815}
2816
2817fn (mut c Checker) check_or_last_stmt(mut stmt ast.Stmt, ret_type ast.Type, default_or_type ast.Type, allow_none_as_option_value bool) {
2818 if ret_type != ast.void_type {
2819 match mut stmt {
2820 ast.ExprStmt {
2821 c.expected_type = ret_type
2822 c.expected_or_type = default_or_type
2823 if stmt.expr is ast.None && allow_none_as_option_value
2824 && default_or_type.has_flag(.option) {
2825 // `none` is valid when the `or` block itself is expected to return an Option.
2826 return
2827 }
2828 last_stmt_typ := c.expr(mut stmt.expr)
2829 stmt.typ = last_stmt_typ
2830 if last_stmt_typ.has_flag(.option) || last_stmt_typ == ast.none_type {
2831 if stmt.expr in [ast.Ident, ast.SelectorExpr, ast.CallExpr, ast.None, ast.CastExpr]
2832 && !(last_stmt_typ == ast.none_type && allow_none_as_option_value
2833 && default_or_type.has_flag(.option)) && !(last_stmt_typ == ast.none_type
2834 && c.inside_return && ret_type.has_flag(.option)) {
2835 expected_type_name := c.table.type_to_str(default_or_type)
2836 got_type_name := c.table.type_to_str(last_stmt_typ)
2837 c.error('`or` block must provide a value of type `${expected_type_name}`, not `${got_type_name}`',
2838 stmt.expr.pos())
2839 return
2840 }
2841 }
2842
2843 c.expected_or_type = ast.void_type
2844 type_fits := c.check_types(last_stmt_typ, ret_type)
2845 && last_stmt_typ.nr_muls() == ret_type.nr_muls()
2846 is_noreturn := is_noreturn_callexpr(stmt.expr)
2847 if type_fits || is_noreturn {
2848 return
2849 }
2850 if stmt.typ == ast.void_type {
2851 if mut stmt.expr is ast.IfExpr {
2852 for mut branch in stmt.expr.branches {
2853 if branch.stmts.len > 0 {
2854 mut stmt_ := branch.stmts.last()
2855 c.check_or_last_stmt(mut stmt_, ret_type, default_or_type,
2856 allow_none_as_option_value)
2857 }
2858 }
2859 return
2860 } else if mut stmt.expr is ast.MatchExpr {
2861 for mut branch in stmt.expr.branches {
2862 if branch.stmts.len > 0 {
2863 mut stmt_ := branch.stmts.last()
2864 c.check_or_last_stmt(mut stmt_, ret_type, default_or_type,
2865 allow_none_as_option_value)
2866 }
2867 }
2868 return
2869 }
2870 expected_type_name := c.table.type_to_str(default_or_type)
2871 c.error('`or` block must provide a default value of type `${expected_type_name}`, or return/continue/break or call a @[noreturn] function like panic(err) or exit(1)',
2872 stmt.expr.pos())
2873 } else {
2874 if ret_type.is_ptr() && last_stmt_typ.is_pointer()
2875 && c.table.sym(last_stmt_typ).kind == .voidptr {
2876 return
2877 }
2878 if last_stmt_typ == ast.none_type_idx && allow_none_as_option_value
2879 && default_or_type.has_flag(.option) {
2880 return
2881 }
2882 // allow returning a custom error type in `or {}` block of a result function
2883 if ret_type.has_flag(.result) {
2884 last_sym := c.table.sym(last_stmt_typ)
2885 if last_sym.kind == .struct {
2886 if last_sym.info is ast.Struct {
2887 if last_sym.info.embeds.any(c.table.type_to_str(it) == 'Error') {
2888 return
2889 }
2890 }
2891 }
2892 }
2893 type_name := c.table.type_to_str(last_stmt_typ)
2894 expected_type_name := c.table.type_to_str(default_or_type)
2895 if default_or_type.has_flag(.generic) {
2896 return
2897 }
2898 c.error('wrong return type `${type_name}` in the `or {}` block, expected `${expected_type_name}`',
2899 stmt.expr.pos())
2900 }
2901 }
2902 ast.BranchStmt {
2903 if stmt.kind !in [.key_continue, .key_break] {
2904 c.error('only break/continue is allowed as a branch statement in the end of an `or {}` block',
2905 stmt.pos)
2906 return
2907 }
2908 }
2909 ast.Return {}
2910 else {
2911 if stmt !is ast.AssertStmt || c.inside_or_block_value {
2912 expected_type_name := c.table.type_to_str(default_or_type)
2913 c.error('last statement in the `or {}` block should be an expression of type `${expected_type_name}` or exit parent scope',
2914 stmt.pos)
2915 }
2916 }
2917 }
2918 } else if mut stmt is ast.ExprStmt {
2919 match mut stmt.expr {
2920 ast.IfExpr {
2921 for mut branch in stmt.expr.branches {
2922 if branch.stmts.len > 0 {
2923 mut stmt_ := branch.stmts.last()
2924 c.check_or_last_stmt(mut stmt_, ret_type, default_or_type,
2925 allow_none_as_option_value)
2926 }
2927 }
2928 }
2929 ast.MatchExpr {
2930 for mut branch in stmt.expr.branches {
2931 if branch.stmts.len > 0 {
2932 mut stmt_ := branch.stmts.last()
2933 c.check_or_last_stmt(mut stmt_, ret_type, default_or_type,
2934 allow_none_as_option_value)
2935 }
2936 }
2937 }
2938 else {
2939 if stmt.typ == ast.void_type || default_or_type == ast.void_type {
2940 return
2941 }
2942 if is_noreturn_callexpr(stmt.expr) {
2943 return
2944 }
2945 if c.check_types(stmt.typ, default_or_type) {
2946 if stmt.typ.is_ptr() == default_or_type.is_ptr()
2947 || (default_or_type.is_ptr() && stmt.typ.is_pointer()
2948 && c.table.sym(stmt.typ).kind == .voidptr) {
2949 return
2950 }
2951 }
2952 if default_or_type.has_flag(.generic) {
2953 return
2954 }
2955 // opt_returning_string() or { ... 123 }
2956 type_name := c.table.type_to_str(stmt.typ)
2957 expr_return_type_name := c.table.type_to_str(default_or_type)
2958 c.error('the default expression type in the `or` block should be `${expr_return_type_name}`, instead you gave a value of type `${type_name}`',
2959 stmt.expr.pos())
2960 }
2961 }
2962 }
2963}
2964
2965fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
2966 prevent_sum_type_unwrapping_once := c.prevent_sum_type_unwrapping_once
2967 c.prevent_sum_type_unwrapping_once = false
2968
2969 // T.name, typeof(expr).name
2970 mut name_type := 0
2971 mut node_expr := node.expr
2972 match mut node.expr {
2973 ast.Ident {
2974 name := node.expr.name
2975 valid_generic := util.is_generic_type_name(name) && c.table.cur_fn != unsafe { nil }
2976 && name in c.table.cur_fn.generic_names
2977 if valid_generic {
2978 name_type = c.table.find_type(name).set_flag(.generic)
2979 }
2980 }
2981 ast.TypeOf {
2982 // TODO: fix this weird case, since just `typeof(x)` is `string`, but `|typeof(x).| propertyname` should be the actual type,
2983 // so that we can get other metadata properties of the type, depending on `propertyname` (one of `name` or `idx` for now).
2984 // A better alternative would be a new `meta(x).propertyname`, that does not have a `meta(x)` case (an error),
2985 // or if it does, it should be a normal constant struct value, just filled at comptime.
2986 c.expr(mut node_expr)
2987 name_type = node.expr.typ
2988 }
2989 ast.AsCast {
2990 c.add_error_detail('for example `(${node.expr.expr} as ${c.table.type_to_str(node.expr.typ)}).${node.field_name}`')
2991 c.error('indeterminate `as` cast, use parenthesis to clarity', node.expr.pos)
2992 }
2993 else {}
2994 }
2995
2996 if name_type > 0 {
2997 node.name_type = name_type
2998 match node.gkind_field {
2999 .name {
3000 return ast.string_type
3001 }
3002 .unaliased_typ, .typ, .indirections {
3003 return ast.int_type
3004 }
3005 else {
3006 if node.field_name == 'name' {
3007 return ast.string_type
3008 } else if node.field_name in ['idx', 'typ', 'unaliased_typ', 'key_type', 'value_type',
3009 'element_type', 'pointee_type', 'payload_type'] {
3010 return ast.int_type
3011 } else if node.field_name == 'variant_types' {
3012 return ast.new_type(c.table.find_or_register_array(ast.int_type))
3013 } else if node.field_name == 'indirections' {
3014 return ast.int_type
3015 }
3016 c.error('invalid field `.${node.field_name}` for type `${node.expr}`', node.pos)
3017 return ast.string_type
3018 }
3019 }
3020 }
3021 if is_array_init_type_expr_field(node.field_name) && c.is_comptime_type_expr(node.expr) {
3022 mut type_expr := node.expr
3023 base_type := c.comptime_call_type_expr_type(mut type_expr)
3024 node.expr = type_expr
3025 resolved := c.type_resolver.typeof_field_type(base_type, node.field_name)
3026 if resolved != ast.no_type {
3027 node.name_type = base_type
3028 if node.field_name == 'variant_types' {
3029 return ast.new_type(c.table.find_or_register_array(ast.int_type))
3030 }
3031 return ast.int_type
3032 }
3033 }
3034 // evaluates comptime field.<name> (from T.fields)
3035 if c.comptime.check_comptime_is_field_selector(node) {
3036 if c.comptime.check_comptime_is_field_selector_bool(node) {
3037 node.expr_type = ast.bool_type
3038 return node.expr_type
3039 }
3040 }
3041 node.is_field_typ = node.is_field_typ || c.comptime.is_comptime_selector_type(node)
3042 old_selector_expr := c.inside_selector_expr
3043 c.inside_selector_expr = true
3044 mut typ := c.expr(mut node.expr)
3045 expr_is_auto_deref_var := node.expr.is_auto_deref_var()
3046 receiver_uses_wrapped_smartcast := typ.has_option_or_result()
3047 || c.table.sym(c.unwrap_generic(typ)).kind in [.interface, .sum_type, .any]
3048 if mut node.expr is ast.Ident {
3049 if var := node.scope.find_var(node.expr.name) {
3050 if var.smartcasts.len > 0 && (expr_is_auto_deref_var || receiver_uses_wrapped_smartcast) {
3051 typ = c.exposed_smartcast_type(var.orig_type, var.smartcasts.last(), var.is_mut)
3052 } else if expr_is_auto_deref_var {
3053 typ = var.typ
3054 }
3055 }
3056 }
3057 c.inside_selector_expr = old_selector_expr
3058 if typ == ast.void_type_idx {
3059 // This means that the field has an undefined type.
3060 // This error was handled before.
3061 c.error('`${node.expr}` does not return a value', node.pos)
3062 node.expr_type = ast.void_type
3063 return ast.void_type
3064 } else if c.comptime.inside_comptime_for && typ == c.enum_data_type
3065 && node.field_name == 'value' {
3066 // for comp-time enum.values
3067 node.expr_type = c.type_resolver.get_ct_type_or_default('${c.comptime.comptime_for_enum_var}.typ',
3068 node.expr_type)
3069 node.typ = typ
3070 return node.expr_type
3071 }
3072 node.expr_type = typ
3073 typ = c.recheck_concrete_type(typ)
3074 node.expr_type = typ
3075 const_name := c.module_qualified_selector_const_name(node)
3076 if const_name != '' {
3077 c.mark_const_decl_as_referenced(const_name)
3078 if mut const_obj := c.table.global_scope.find_const(node.field_name) {
3079 c.mark_const_decl_as_referenced(const_obj.name)
3080 }
3081 } else {
3082 expr := node.expr
3083 if expr is ast.Ident {
3084 if mut const_obj := c.file.global_scope.find_const('${expr.name}.${node.field_name}') {
3085 c.mark_const_decl_as_referenced(const_obj.name)
3086 }
3087 }
3088 }
3089 if !(node.expr is ast.Ident && node.expr.kind == .constant) {
3090 if node.expr_type.has_flag(.option) {
3091 c.error('cannot access fields of an Option, handle the error with `or {...}` or propagate it with `?`',
3092 node.pos)
3093 } else if node.expr_type.has_flag(.result) {
3094 c.error('cannot access fields of a Result, handle the error with `or {...}` or propagate it with `!`',
3095 node.pos)
3096 }
3097 }
3098 field_name := node.field_name
3099 mut sym := c.table.sym(typ)
3100 mut final_sym := c.table.final_sym(typ)
3101 if (typ.has_flag(.variadic) || final_sym.kind == .array_fixed) && field_name == 'len' {
3102 node.typ = ast.int_type
3103 return ast.int_type
3104 }
3105 if sym.kind == .chan {
3106 if field_name == 'closed' {
3107 node.typ = ast.bool_type
3108 return ast.bool_type
3109 } else if field_name in ['len', 'cap'] {
3110 node.typ = ast.u32_type
3111 return ast.u32_type
3112 }
3113 }
3114 mut unknown_field_msg := ''
3115 mut has_field := false
3116 mut field := ast.StructField{}
3117 if field_name.len > 0 && final_sym.kind == .struct && sym.language == .v
3118 && field_name[0].is_capital() {
3119 // x.Foo.y => access the embedded struct
3120 struct_info := final_sym.info as ast.Struct
3121 for embed in struct_info.embeds {
3122 embed_sym := c.table.sym(embed)
3123 if embed_sym.embed_name() == field_name {
3124 node.typ = embed
3125 return embed
3126 }
3127 }
3128 } else {
3129 if f := c.table.find_field(sym, field_name) {
3130 has_field = true
3131 field = f
3132 } else {
3133 field_err := err
3134 // look for embedded field
3135 if field_, embed_types := c.table.find_field_from_embeds(sym, field_name) {
3136 has_field = true
3137 field = field_
3138 node.from_embed_types = embed_types
3139 } else {
3140 first_err := err
3141 has_field = false
3142 if final_sym.kind in [.aggregate, .sum_type] {
3143 if variant_typ, variant_field, variant_embed_types := c.table.find_single_field_variant(final_sym,
3144 field_name)
3145 {
3146 old_expr := node.expr
3147 node.expr = ast.ParExpr{
3148 expr: ast.AsCast{
3149 expr: old_expr
3150 typ: variant_typ
3151 expr_type: typ
3152 pos: old_expr.pos()
3153 }
3154 }
3155 typ = variant_typ
3156 sym = c.table.sym(typ)
3157 final_sym = c.table.final_sym(typ)
3158 node.expr_type = typ
3159 field = variant_field
3160 node.from_embed_types = variant_embed_types
3161 has_field = true
3162 }
3163 }
3164 if !has_field && final_sym.kind in [.aggregate, .sum_type] {
3165 unknown_field_msg = if field_err.msg() != '' {
3166 field_err.msg()
3167 } else {
3168 first_err.msg()
3169 }
3170 // TODO need a better way to check that we need to display sum type variants info
3171 if final_sym.kind == .sum_type
3172 && unknown_field_msg.contains('does not exist or have the same type in all sumtype')
3173 && !unknown_field_msg.contains('variants: ') {
3174 info := final_sym.info as ast.SumType
3175 missing_variants := c.table.find_missing_variants(info, field_name)
3176 unknown_field_msg += missing_variants
3177 }
3178 }
3179 if !has_field && c.table.cur_fn != unsafe { nil }
3180 && c.table.cur_fn.generic_names.len > 0 && c.table.cur_concrete_types.len == 0
3181 && c.table.sym(c.unwrap_generic(typ)).kind in [.interface, .any] {
3182 node.typ = ast.any_type
3183 return node.typ
3184 }
3185 if !has_field && first_err.msg() != '' {
3186 c.error(first_err.msg(), node.pos)
3187 }
3188 }
3189 }
3190 if !c.inside_unsafe && sym.kind == .struct {
3191 struct_info := sym.info as ast.Struct
3192 if struct_info.is_union && node.next_token !in token.assign_tokens {
3193 if !c.pref.translated && !c.file.is_translated {
3194 c.warn('reading a union field (or its address) requires `unsafe`', node.pos)
3195 }
3196 }
3197 }
3198 if typ.has_flag(.generic) && !has_field {
3199 gs := c.table.sym(c.unwrap_generic(typ))
3200 if f := c.table.find_field(gs, field_name) {
3201 has_field = true
3202 field = f
3203 } else {
3204 // look for embedded field
3205 has_field = true
3206 mut embed_types := []ast.Type{}
3207 field, embed_types = c.table.find_field_from_embeds(gs, field_name) or {
3208 if err.msg() != '' {
3209 c.error(err.msg(), node.pos)
3210 }
3211 has_field = false
3212 ast.StructField{}, []ast.Type{}
3213 }
3214 node.from_embed_types = embed_types
3215 node.generic_from_embed_types << embed_types
3216 }
3217 }
3218 }
3219
3220 if has_field {
3221 is_used_outside := !c.inside_recheck && sym.mod != c.mod
3222 if is_used_outside && sym.language == .c && !sym.is_pub {
3223 c.ensure_type_exists(typ, node.pos)
3224 return field.typ
3225 }
3226 unwrapped_sym := c.table.sym(c.unwrap_generic(typ))
3227 if is_used_outside && !field.is_pub && sym.language != .c {
3228 c.error('field `${unwrapped_sym.name}.${field_name}` is not public', node.pos)
3229 }
3230 field_type := c.resolve_selector_field_type(typ, field_name, field)
3231 field_sym := c.table.sym(field_type)
3232 if field.is_deprecated && is_used_outside {
3233 c.deprecate('field', field_name, field.attrs, node.pos)
3234 }
3235 if field_sym.kind in [.sum_type, .interface] || field_type.has_flag(.option) {
3236 if !prevent_sum_type_unwrapping_once {
3237 scope_field := node.scope.find_struct_field(node.expr.str(), typ, field_name)
3238 if scope_field != unsafe { nil } {
3239 sf_smartcast_type := c.exposed_smartcast_type(scope_field.orig_type,
3240 scope_field.smartcasts.last(), scope_field.is_mut)
3241 if c.inside_sql && node.or_block.kind == .absent {
3242 node.typ = sf_smartcast_type
3243 }
3244 if node.or_block.kind == .absent {
3245 return sf_smartcast_type
3246 }
3247 }
3248 }
3249 }
3250 node.typ = field_type
3251 if node.or_block.kind != .absent {
3252 unwrapped_typ := c.unwrap_generic(node.typ)
3253 c.expected_or_type = unwrapped_typ.clear_option_and_result()
3254 c.stmts_ending_with_expression(mut node.or_block.stmts, c.expected_or_type)
3255 last_cur_or_expr := c.cur_or_expr
3256 c.cur_or_expr = &node.or_block
3257 c.check_or_expr(node.or_block, unwrapped_typ, c.expected_or_type, node)
3258 c.cur_or_expr = last_cur_or_expr
3259 c.expected_or_type = ast.void_type
3260 }
3261 return field_type
3262 }
3263 if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0
3264 && c.table.cur_concrete_types.len == 0
3265 && c.table.sym(c.unwrap_generic(typ)).kind in [.interface, .any] {
3266 node.typ = ast.any_type
3267 return node.typ
3268 }
3269 if mut method := c.table.sym(c.unwrap_generic(typ)).find_method_with_generic_parent(field_name) {
3270 c.mark_fn_decl_as_referenced(method.fkey())
3271 c.markused_comptime_call(typ.has_flag(.generic),
3272 '${int(method.params[0].typ)}.${field_name}')
3273 if c.expected_type != 0 && c.expected_type != ast.none_type {
3274 mut method_copy := method
3275 method_copy.name = ''
3276 fn_type := ast.new_type(c.table.find_or_register_fn_type(method_copy, false, true))
3277 if c.check_types(fn_type, c.expected_type) {
3278 c.table.used_features.anon_fn = true
3279 return fn_type
3280 }
3281 }
3282 receiver := c.unwrap_generic(method.params[0].typ)
3283 if receiver.nr_muls() > 0 {
3284 if !c.inside_unsafe {
3285 rec_sym := c.table.sym(receiver.set_nr_muls(0))
3286 if !rec_sym.is_heap() {
3287 suggestion := if rec_sym.kind == .struct {
3288 'declaring `${rec_sym.name}` as `@[heap]`'
3289 } else {
3290 'wrapping the `${rec_sym.name}` object in a `struct` declared as `@[heap]`'
3291 }
3292 c.error('method `${c.table.type_to_str(receiver.idx_type())}.${method.name}` cannot be used as a variable outside `unsafe` blocks as its receiver might refer to an object stored on stack. Consider ${suggestion}.',
3293 node.expr.pos().extend(node.pos))
3294 }
3295 }
3296 }
3297 method.params = method.params[1..]
3298 node.has_hidden_receiver = true
3299 method.name = ''
3300 fn_type := ast.new_type(c.table.find_or_register_fn_type(method, false, true))
3301 node.typ = c.unwrap_generic(fn_type)
3302 if c.type_has_unresolved_generic_parts(fn_type) {
3303 c.error('cannot use `${node.expr}.${node.field_name}` as a generic function value',
3304 node.pos)
3305 c.table.used_features.anon_fn = true
3306 return fn_type
3307 }
3308 c.table.used_features.anon_fn = true
3309 return fn_type
3310 }
3311 if sym.kind !in [.struct, .aggregate, .interface, .sum_type] {
3312 if sym.kind != .placeholder {
3313 unwrapped_sym := c.table.sym(c.unwrap_generic(typ))
3314
3315 if unwrapped_sym.kind == .array_fixed && node.field_name == 'len' {
3316 node.typ = ast.int_type
3317 return ast.int_type
3318 }
3319
3320 c.error('`${unwrapped_sym.name}` has no property `${node.field_name}`', node.pos)
3321 }
3322 } else {
3323 if unknown_field_msg == '' {
3324 if field_name == '' && c.pref.is_vls {
3325 // VLS will often have `foo.`, skip the no field error
3326 return ast.void_type
3327 }
3328 unknown_field_msg = 'type `${sym.name}` has no field named `${field_name}`'
3329 }
3330 if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0
3331 && c.table.cur_concrete_types.len == 0 && sym.kind in [.interface, .any] {
3332 node.typ = ast.any_type
3333 return node.typ
3334 }
3335 if final_sym.kind == .struct {
3336 if c.smartcast_mut_pos != token.Pos{} && !c.implicit_mutability_enabled() {
3337 c.note('smartcasting requires either an immutable value, or an explicit mut keyword before the value',
3338 c.smartcast_mut_pos)
3339 }
3340 struct_info := final_sym.info as ast.Struct
3341 suggestion := util.new_suggestion(field_name, struct_info.fields.map(it.name))
3342 c.error(suggestion.say(unknown_field_msg), node.pos)
3343 return ast.void_type
3344 }
3345 if c.smartcast_mut_pos != token.Pos{} && !c.implicit_mutability_enabled() {
3346 c.note('smartcasting requires either an immutable value, or an explicit mut keyword before the value',
3347 c.smartcast_mut_pos)
3348 }
3349 if c.smartcast_cond_pos != token.Pos{} {
3350 c.note('smartcast can only be used on ident, selector or index expressions, e.g. match foo, match foo.bar, match foo[0]',
3351 c.smartcast_cond_pos)
3352 }
3353 c.error(unknown_field_msg, node.pos)
3354 }
3355 return ast.void_type
3356}
3357
3358fn (mut c Checker) const_decl(mut node ast.ConstDecl) {
3359 if node.fields.len == 0 {
3360 c.warn('const block must have at least 1 declaration', node.pos)
3361 }
3362 if node.is_block {
3363 c.warn('const () groups will be an error after 2025-01-01 (`v fmt -w source.v` will fix that for you)',
3364 node.pos)
3365 }
3366
3367 mut is_export := false
3368 mut export_name := ''
3369 if export_attr := node.attrs.find_first('export') {
3370 is_export = true
3371 export_name = export_attr.arg
3372 }
3373 for mut field in node.fields {
3374 if reserved_type_names_chk.matches(util.no_cur_mod(field.name, c.mod)) {
3375 c.error('invalid use of reserved type `${field.name}` as a const name', field.pos)
3376 }
3377 // TODO: Check const name once the syntax is decided
3378 if field.name in c.const_names {
3379 name_pos := token.Pos{
3380 ...field.pos
3381 len: util.no_cur_mod(field.name, c.mod).len
3382 }
3383 c.error('duplicate const `${field.name}`', name_pos)
3384 }
3385 if is_export {
3386 check_name := if export_name.len == 0 { field.name } else { export_name }
3387 if !check_name.is_identifier() {
3388 c.error('export name `${check_name}` should be a valid identifier', node.pos)
3389 }
3390 if check_name in c.table.export_names.values() {
3391 name_pos := token.Pos{
3392 ...field.pos
3393 len: util.no_cur_mod(field.name, c.mod).len
3394 }
3395 c.error('duplicate export name `${check_name}`', name_pos)
3396 } else {
3397 c.table.export_names[field.name] = check_name
3398 }
3399 }
3400 is_call_expr := field.expr is ast.CallExpr
3401 if is_call_expr {
3402 mut field_expr := field.expr
3403 sym := c.table.sym(c.check_expr_option_or_result_call(field_expr,
3404 c.expr(mut field_expr)))
3405 if sym.kind == .multi_return {
3406 c.error('const declarations do not support multiple return values yet',
3407 field_expr.pos())
3408 }
3409 field.expr = field_expr
3410 }
3411 const_name := field.name.all_after_last('.')
3412 if const_name == c.mod && const_name != 'main' {
3413 name_pos := token.Pos{
3414 ...field.pos
3415 len: util.no_cur_mod(field.name, c.mod).len
3416 }
3417 c.error('duplicate of a module name `${field.name}`', name_pos)
3418 }
3419 for imp in c.file.imports {
3420 imp_alias := if imp.alias.len > 0 { imp.alias } else { imp.mod.all_after_last('.') }
3421 if const_name == imp_alias {
3422 name_pos := token.Pos{
3423 ...field.pos
3424 len: util.no_cur_mod(field.name, c.mod).len
3425 }
3426 c.error('const `${const_name}` conflicts with imported module `${imp.mod}`',
3427 name_pos)
3428 }
3429 }
3430 if const_name == '_' {
3431 name_pos := token.Pos{
3432 ...field.pos
3433 len: util.no_cur_mod(field.name, c.mod).len
3434 }
3435 c.error('cannot use `_` as a const name', name_pos)
3436 return
3437 }
3438 c.const_names << field.name
3439 }
3440 for i, mut field in node.fields {
3441 c.const_deps << field.name
3442 prev_const_var := c.const_var