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