v2 / vlib / v / parser / fn.v
1838 lines · 1800 sloc · 49.01 KB · c05cd760858e64e5ffd66fb28bbd1318357f9f75
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module parser
5
6import v.ast
7import v.token
8import v.util
9import os
10
11fn table_fn_lookup(table &ast.Table, name string) (ast.Fn, bool) {
12 if name !in table.fns {
13 return ast.Fn{}, false
14 }
15 return unsafe { table.fns[name] }, true
16}
17
18fn comptime_define_idx(attrs []ast.Attr) int {
19 for idx in 0 .. attrs.len {
20 if attrs[idx].kind == .comptime_define {
21 return idx
22 }
23 }
24 return ast.invalid_type_idx
25}
26
27fn type_method_name_pos(sym &ast.TypeSymbol, name string, fallback token.Pos) token.Pos {
28 for method in sym.methods {
29 if method.name == name {
30 return method.name_pos
31 }
32 }
33 return fallback
34}
35
36fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr {
37 first_pos := p.tok.pos()
38 mut name := if language == .js { p.check_js_name() } else { p.check_name() }
39 mut is_static_type_method := language == .v && name != '' && name[0].is_capital()
40 && p.tok.kind == .dot
41 if is_static_type_method {
42 p.check(.dot)
43 name = name + '__static__' + p.check_name()
44 }
45 mut fn_name := if language == .c {
46 'C.${name}'
47 } else if language == .js {
48 'JS.${name}'
49 } else if language == .wasm {
50 'WASM.${name}'
51 } else if mod != '' {
52 '${mod}.${name}'
53 } else {
54 name
55 }
56 if language != .v {
57 p.check_for_impure_v(language, first_pos)
58 }
59 mut or_kind := ast.OrKind.absent
60 if fn_name == 'json.decode' || fn_name == 'C.va_arg' {
61 p.expecting_type = true // Makes name_expr() parse the type `User` in `json.decode(User, txt)`
62 }
63
64 old_expr_mod := p.expr_mod
65 defer {
66 p.expr_mod = old_expr_mod
67 }
68 p.expr_mod = ''
69
70 mut concrete_types := []ast.Type{}
71 mut concrete_list_pos := p.tok.pos()
72 if p.tok.kind == .lsbr {
73 // `foo[int](10)`
74 p.expr_mod = ''
75 concrete_types = p.parse_concrete_types()
76 concrete_list_pos = concrete_list_pos.extend(p.prev_tok.pos())
77 }
78 p.check(.lpar)
79 args := p.call_args()
80 if p.tok.kind != .rpar && !p.pref.is_vls {
81 mut params := []ast.Param{}
82 fn_info, has_fn_info := table_fn_lookup(p.table, fn_name)
83 if has_fn_info {
84 params = fn_info.params.clone()
85 } else {
86 mod_fn_info, has_mod_fn_info := table_fn_lookup(p.table, '${p.mod}.${fn_name}')
87 if has_mod_fn_info {
88 params = mod_fn_info.params.clone()
89 }
90 }
91 min_required_params := min_required_call_args(params)
92 if args.len < min_required_params && p.prev_tok.kind != .comma {
93 pos := if p.tok.kind == .eof { p.prev_tok.pos() } else { p.tok.pos() }
94 p.unexpected_with_pos(pos, expecting: '`,`')
95 } else if args.len > params.len {
96 ok_arg_pos := if params.len > 0 { args[params.len - 1].pos } else { args[0].pos }
97 pos := token.Pos{
98 ...ok_arg_pos
99 col: u16(ok_arg_pos.col + ok_arg_pos.len)
100 }
101 p.unexpected_with_pos(pos.extend(p.tok.pos()), expecting: '`)`')
102 } else {
103 pos := if p.tok.kind == .eof { p.prev_tok.pos() } else { p.tok.pos() }
104 p.unexpected_with_pos(pos, expecting: '`)`')
105 }
106 }
107 last_pos := p.tok.pos()
108 if p.tok.kind == .rpar {
109 p.next()
110 }
111 mut pos := first_pos.extend(last_pos)
112 mut or_stmts := []ast.Stmt{} // TODO: remove unnecessary allocations by just using .absent
113 mut or_pos := p.tok.pos()
114 mut or_scope := ast.empty_scope
115 if p.tok.kind == .key_orelse {
116 // `foo() or {}``
117 or_kind = .block
118 or_stmts, or_pos, or_scope = p.or_block(.with_err_var)
119 }
120 if p.tok.kind in [.question, .not] {
121 is_not := p.tok.kind == .not
122 // `foo()?`
123 p.next()
124 if p.inside_defer {
125 p.error_with_pos('error propagation not allowed inside `defer` blocks',
126 p.prev_tok.pos())
127 }
128 or_kind = if is_not { .propagate_result } else { .propagate_option }
129 or_scope = p.scope
130 }
131 if p.is_imported_symbol(fn_name) {
132 check := !p.imported_symbols_used[fn_name]
133 fn_name = p.imported_symbols[fn_name]
134 if check {
135 p.register_used_import_for_symbol_name(fn_name)
136 }
137 }
138 comments := p.eat_comments(same_line: true)
139 pos.update_last_line(p.prev_tok.line_nr)
140 return ast.CallExpr{
141 name: fn_name
142 name_pos: first_pos
143 args: args
144 mod: p.mod
145 kind: p.call_kind(fn_name)
146 pos: pos
147 language: language
148 concrete_types: concrete_types
149 concrete_list_pos: concrete_list_pos
150 raw_concrete_types: concrete_types
151 or_block: ast.OrExpr{
152 stmts: or_stmts
153 kind: or_kind
154 pos: or_pos
155 scope: or_scope
156 }
157 scope: p.scope
158 comments: comments
159 is_return_used: p.expecting_value
160 is_static_method: is_static_type_method
161 }
162}
163
164fn min_required_call_args(params []ast.Param) int {
165 if params.len == 0 {
166 return 0
167 }
168 mut required := params.len
169 mut idx := params.len - 1
170 for idx >= 0 {
171 if params[idx].typ.has_flag(.option) {
172 required--
173 idx--
174 continue
175 }
176 break
177 }
178 return required
179}
180
181fn (mut p Parser) call_kind(fn_name string) ast.CallKind {
182 if fn_name.len < 3 || fn_name.len > 20 {
183 return .unknown
184 }
185 return match fn_name.len {
186 3 {
187 match fn_name {
188 'str' {
189 .str
190 }
191 'map' {
192 .map
193 }
194 'any' {
195 .any
196 }
197 'all' {
198 .all
199 }
200 'pop' {
201 .pop
202 }
203 else {
204 .unknown
205 }
206 }
207 }
208 4 {
209 match fn_name {
210 'wait' {
211 .wait
212 }
213 'free' {
214 .free
215 }
216 'keys' {
217 .keys
218 }
219 'sort' {
220 .sort
221 }
222 'trim' {
223 .trim
224 }
225 'last' {
226 .last
227 }
228 'drop' {
229 .drop
230 }
231 'main' {
232 .main
233 }
234 'move' {
235 .move
236 }
237 else {
238 .unknown
239 }
240 }
241 }
242 5 {
243 return match fn_name {
244 'count' {
245 .count
246 }
247 'print' {
248 .print
249 }
250 'close' {
251 .close
252 }
253 'slice' {
254 .slice
255 }
256 'clone' {
257 .clone
258 }
259 'index' {
260 .index
261 }
262 'first' {
263 .first
264 }
265 'panic' {
266 .panic
267 }
268 'clear' {
269 .clear
270 }
271 'error' {
272 .error
273 }
274 else {
275 .unknown
276 }
277 }
278 }
279 6 {
280 return match fn_name {
281 'values' {
282 .values
283 }
284 'eprint' {
285 .eprint
286 }
287 'sorted' {
288 .sorted
289 }
290 'filter' {
291 .filter
292 }
293 'insert' {
294 .insert
295 }
296 'delete' {
297 .delete
298 }
299 'repeat' {
300 .repeat
301 }
302 '__addr' {
303 .addr
304 }
305 'malloc' {
306 .malloc
307 }
308 else {
309 .unknown
310 }
311 }
312 }
313 7 {
314 return match fn_name {
315 'prepend' {
316 .prepend
317 }
318 'writeln' {
319 .writeln
320 }
321 'println' {
322 .println
323 }
324 'try_pop' {
325 .try_pop
326 }
327 'reverse' {
328 .reverse
329 }
330 'reserve' {
331 .reserve
332 }
333 else {
334 .unknown
335 }
336 }
337 }
338 8 {
339 return match fn_name {
340 'try_push' {
341 .try_push
342 }
343 'eprintln' {
344 .eprintln
345 }
346 'pointers' {
347 .pointers
348 }
349 'contains' {
350 .contains
351 }
352 'pop_left' {
353 .pop_left
354 }
355 'type_idx' {
356 .type_idx
357 }
358 'C.va_arg' {
359 .va_arg
360 }
361 'JS.await' {
362 .jsawait
363 }
364 'grow_len' {
365 .grow_len
366 }
367 'grow_cap' {
368 .grow_cap
369 }
370 else {
371 .unknown
372 }
373 }
374 }
375 9 {
376 return match fn_name {
377 'type_name' {
378 .type_name
379 }
380 'main.main' {
381 .main_main
382 }
383 'push_many' {
384 .push_many
385 }
386 else {
387 .unknown
388 }
389 }
390 }
391 10 {
392 return match fn_name {
393 'last_index' {
394 .last_index
395 }
396 else {
397 .unknown
398 }
399 }
400 }
401 11 {
402 return match fn_name {
403 'delete_many' {
404 .delete_many
405 }
406 'delete_last' {
407 .delete_last
408 }
409 'json.decode' {
410 .json_decode
411 }
412 'json.encode' {
413 .json_encode
414 }
415 else {
416 .unknown
417 }
418 }
419 }
420 else {
421 return match fn_name {
422 'sort_with_compare' {
423 .sort_with_compare
424 }
425 'sorted_with_compare' {
426 .sorted_with_compare
427 }
428 'reverse_in_place' {
429 .reverse_in_place
430 }
431 'json.encode_pretty' {
432 .json_encode_pretty
433 }
434 'clone_to_depth' {
435 .clone_to_depth
436 }
437 else {
438 .unknown
439 }
440 }
441 }
442 }
443}
444
445@[inline]
446fn (p &Parser) is_start_of_call_arg_expr() bool {
447 if p.tok.kind == .name && p.peek_tok.kind == .string && p.tok.lit !in ['c', 'r', 'js'] {
448 return false
449 }
450 return match p.tok.kind {
451 .name, .number, .string, .chartoken, .dot, .at, .dollar, .amp, .mul, .not, .bit_not,
452 .arrow, .minus, .lpar, .lsbr, .lcbr, .pipe, .logical_or, .question, .key_fn, .key_type,
453 .key_struct, .key_none, .key_nil, .key_true, .key_false, .key_if, .key_match, .key_select,
454 .key_lock, .key_rlock, .key_go, .key_spawn, .key_unsafe, .key_likely, .key_unlikely,
455 .key_typeof, .key_sizeof, .key_isreftype, .key_offsetof, .key_dump, .key_mut, .key_shared,
456 .key_atomic, .key_static, .key_volatile {
457 true
458 }
459 else {
460 false
461 }
462 }
463}
464
465@[inline]
466fn (p &Parser) can_omit_comma_between_fn_params() bool {
467 return p.tok.line_nr > p.prev_tok.line_nr
468 && p.tok.kind in [.name, .key_mut, .key_shared, .key_atomic, .ellipsis]
469}
470
471fn (mut p Parser) call_args() []ast.CallArg {
472 prev_inside_call_args := p.inside_call_args
473 p.inside_call_args = true
474 defer {
475 p.inside_call_args = prev_inside_call_args
476 }
477 mut args := []ast.CallArg{}
478 for p.tok.kind != .rpar {
479 if p.tok.kind == .eof {
480 return args
481 }
482 is_shared := p.tok.kind == .key_shared
483 is_atomic := p.tok.kind == .key_atomic
484 is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
485 if is_mut {
486 p.next()
487 }
488 mut comments := p.eat_comments()
489 arg_start_pos := p.tok.pos()
490 mut array_decompose := false
491 if p.tok.kind == .ellipsis {
492 p.next()
493 array_decompose = true
494 }
495 mut expr := ast.empty_expr
496 if p.peek_tok.kind == .colon {
497 // `foo(key:val, key2:val2)`
498 expr = p.struct_init('void_type', .short_syntax, false)
499 } else {
500 expr = p.expr(0)
501 if mut expr is ast.Ident {
502 if p.is_imported_symbol(expr.name) && !p.imported_symbols_used[expr.name] {
503 // func call arg is another function call
504 // import term { bright_cyan, colorize } ... colorize(bright_cyan, 'hello')
505 p.register_used_import_for_symbol_name(p.imported_symbols[expr.name])
506 }
507 }
508 }
509 if array_decompose {
510 expr = ast.ArrayDecompose{
511 expr: expr
512 pos: p.tok.pos()
513 }
514 }
515 if mut expr is ast.StructInit {
516 expr.pre_comments << comments
517 comments = []ast.Comment{}
518 }
519 pos := arg_start_pos.extend(p.prev_tok.pos())
520 comments << p.eat_comments()
521 args << ast.CallArg{
522 is_mut: is_mut
523 share: ast.sharetype_from_flags(is_shared, is_atomic)
524 expr: expr
525 comments: comments
526 pos: pos
527 }
528 if p.tok.kind != .comma {
529 if !p.is_start_of_call_arg_expr() {
530 break
531 }
532 continue
533 }
534 p.next()
535 }
536 return args
537}
538
539struct ReceiverParsingInfo {
540mut:
541 name string
542 pos token.Pos
543 typ ast.Type
544 type_pos token.Pos
545 is_mut bool
546 language ast.Language
547}
548
549fn (mut p Parser) fn_decl() ast.FnDecl {
550 p.top_level_statement_start()
551 start_pos := p.tok.pos()
552
553 mut is_manualfree := p.is_manualfree
554 mut is_deprecated := false
555 mut is_direct_arr := false
556 mut is_keep_alive := false
557 mut is_exported := false
558 mut is_unsafe := false
559 mut is_must_use := false
560 mut is_trusted := false
561 mut is_noreturn := false
562 mut is_ctor_new := false
563 mut is_c2v_variadic := false
564 mut is_c_extern := false
565 mut is_markused := false
566 mut is_ignore_overflow := false
567 mut is_weak := false
568 mut is_expand_simple_interpolation := false
569 mut comments := []ast.Comment{}
570 fn_attrs := p.attrs
571 p.attrs = []
572 for fna in fn_attrs {
573 match fna.name {
574 'noreturn' {
575 is_noreturn = true
576 }
577 'manualfree' {
578 is_manualfree = true
579 }
580 'deprecated' {
581 is_deprecated = true
582 }
583 'direct_array_access' {
584 if !p.pref.force_bounds_checking {
585 is_direct_arr = true
586 }
587 }
588 'keep_args_alive' {
589 is_keep_alive = true
590 }
591 'export' {
592 is_exported = true
593 }
594 'wasm_export' {
595 is_exported = true
596 }
597 'unsafe' {
598 is_unsafe = true
599 }
600 'must_use' {
601 is_must_use = true
602 }
603 'trusted' {
604 is_trusted = true
605 }
606 'c2v_variadic' {
607 is_c2v_variadic = true
608 }
609 'weak' {
610 is_weak = true
611 }
612 'use_new' {
613 is_ctor_new = true
614 }
615 'markused' {
616 is_markused = true
617 }
618 'ignore_overflow' {
619 is_ignore_overflow = true
620 }
621 'c_extern' {
622 is_c_extern = true
623 }
624 'windows_stdcall' {
625 p.note_with_pos('the tag [windows_stdcall] has been deprecated, it will be an error after 2022-06-01, use `[callconv: stdcall]` instead',
626 p.tok.pos())
627 }
628 '_fastcall' {
629 p.note_with_pos('the tag [_fastcall] has been deprecated, it will be an error after 2022-06-01, use `[callconv: fastcall]` instead',
630 p.tok.pos())
631 }
632 'callconv' {
633 if !fna.has_arg {
634 p.error_with_pos('callconv attribute is present but its value is missing',
635 p.prev_tok.pos())
636 }
637 if fna.arg !in ['stdcall', 'fastcall', 'cdecl'] {
638 p.error_with_pos('unsupported calling convention, supported are stdcall, fastcall and cdecl',
639 p.prev_tok.pos())
640 }
641 }
642 'expand_simple_interpolation' {
643 is_expand_simple_interpolation = true
644 }
645 else {}
646 }
647 }
648 conditional_ctdefine_idx := comptime_define_idx(fn_attrs)
649 is_pub := p.tok.kind == .key_pub
650 if is_pub {
651 p.next()
652 }
653 p.check(.key_fn)
654 mut comments_before_key_fn := if p.pref.is_vls { p.cur_comments.clone() } else { [] }
655 comments << p.eat_comments()
656 p.open_scope()
657 defer {
658 p.close_scope()
659 }
660 language_tok_pos := p.tok.pos()
661 mut language := p.parse_language()
662 p.fn_language = language
663 if language != .v {
664 for fna in fn_attrs {
665 if fna.name == 'export' {
666 p.error_with_pos('interop function cannot be exported', fna.pos)
667 break
668 }
669 }
670 }
671 if is_keep_alive && language != .c {
672 p.error_with_pos('attribute [keep_args_alive] is only supported for C functions',
673 language_tok_pos)
674 }
675 if language != .v {
676 p.check_for_impure_v(language, language_tok_pos)
677 if language == .c {
678 is_unsafe = !is_trusted
679 }
680 }
681 // Receiver?
682 mut rec := ReceiverParsingInfo{
683 typ: ast.void_type
684 language: language
685 }
686 mut is_method := false
687 mut is_static_type_method := false
688 mut params := []ast.Param{}
689 if p.tok.kind == .lpar {
690 is_method = true
691 p.fn_receiver(mut params, mut rec) or { return ast.FnDecl{
692 scope: unsafe { nil }
693 } }
694
695 // rec.language was initialized with language variable.
696 // So language is changed only if rec.language has been changed.
697 language = rec.language
698 p.fn_language = language
699 }
700 mut name := ''
701 mut type_sym := p.table.sym(rec.typ)
702 mut name_pos := p.tok.pos()
703 mut static_type_pos := p.tok.pos()
704 if p.tok.kind == .name || is_ident_name(p.tok.lit) {
705 mut check_name := ''
706 // TODO: high order fn
707 is_static_type_method = p.tok.lit.len > 0 && p.tok.lit[0].is_capital()
708 && p.peek_tok.kind == .dot && language == .v // `fn Foo.bar() {}`
709 if is_static_type_method {
710 type_name := p.tok.lit // "Foo"
711 static_type_pos = p.tok.pos()
712 rec.typ = p.parse_type()
713 p.check(.dot)
714 check_name = p.check_name()
715 name = type_name + '__static__' + check_name // "foo__bar"
716 name_pos = name_pos.extend(p.prev_tok.pos())
717 } else {
718 check_name = if language == .js { p.check_js_name() } else { p.check_name() }
719 name = check_name
720 }
721
722 if language == .v && !p.pref.translated && !p.is_translated && !p.builtin_mod
723 && util.contains_capital(check_name) {
724 p.error_with_pos('function names cannot contain uppercase letters, use snake_case instead',
725 name_pos)
726 return ast.FnDecl{
727 scope: unsafe { nil }
728 }
729 }
730 if is_method {
731 mut is_duplicate := type_sym.has_method(name)
732 // make sure this is a normal method and not an interface method
733 if type_sym.kind == .interface && is_duplicate {
734 if mut type_sym.info is ast.Interface {
735 // if the method is in info then its an interface method
736 is_duplicate = !type_sym.info.has_method(name)
737 }
738 }
739 // when formatting, methods in mutually exclusive $if/$else branches
740 // may appear as duplicates since all branches are parsed
741 if is_duplicate && !p.pref.is_fmt {
742 if type_sym.kind == .enum
743 && name in ['is_empty', 'has', 'all', 'set', 'set_all', 'clear', 'clear_all', 'toggle', 'zero', 'from'] {
744 name_pos = type_method_name_pos(type_sym, name, name_pos)
745 p.error_with_pos('duplicate method `${name}`, `${name}` is an enum type built-in method',
746 name_pos)
747 } else {
748 p.error_with_pos('duplicate method `${name}`', name_pos)
749 }
750 return ast.FnDecl{
751 scope: unsafe { nil }
752 }
753 }
754 }
755 if !p.pref.is_fmt {
756 if p.is_imported_symbol(name) {
757 p.error_with_pos('cannot redefine imported function `${name}`', name_pos)
758 return ast.FnDecl{
759 scope: unsafe { nil }
760 }
761 }
762 }
763 } else if p.tok.kind in [.plus, .minus, .mul, .power, .div, .mod, .lt, .eq]
764 && p.peek_tok.kind == .lpar {
765 name = p.tok.kind.str() // op_to_fn_name()
766 if rec.typ == ast.void_type {
767 p.error_with_pos('cannot use operator overloading with normal functions', p.tok.pos())
768 }
769 if type_sym.has_method(name) {
770 p.error_with_pos('cannot duplicate operator overload `${name}`', p.tok.pos())
771 }
772 p.next()
773 } else if p.tok.kind == .lsbr && p.peek_tok.kind == .rsbr && p.peek_token(2).kind == .lpar {
774 name = '[]'
775 if rec.typ == ast.void_type {
776 p.error_with_pos('cannot use operator overloading with normal functions', p.tok.pos())
777 }
778 if type_sym.has_method(name) {
779 p.error_with_pos('cannot duplicate operator overload `${name}`', p.tok.pos())
780 }
781 p.next()
782 p.next()
783 } else if p.tok.kind == .lsbr && p.peek_tok.kind == .rsbr && p.peek_token(2).kind == .assign
784 && p.peek_token(3).kind == .lpar {
785 name = '[]='
786 if rec.typ == ast.void_type {
787 p.error_with_pos('cannot use operator overloading with normal functions', p.tok.pos())
788 }
789 if type_sym.has_method(name) {
790 p.error_with_pos('cannot duplicate operator overload `${name}`', p.tok.pos())
791 }
792 p.next()
793 p.next()
794 p.next()
795 } else if p.tok.kind in [.ne, .gt, .ge, .le] && p.peek_tok.kind == .lpar {
796 p.error_with_pos('cannot overload `!=`, `>`, `<=` and `>=` as they are auto generated from `==` and`<`',
797 p.tok.pos())
798 } else if p.tok.kind in [.plus_assign, .minus_assign, .div_assign, .mult_assign, .power_assign,
799 .mod_assign] {
800 extracted_op := match p.tok.kind {
801 .plus_assign { '+' }
802 .minus_assign { '-' }
803 .div_assign { '/' }
804 .mod_assign { '%' }
805 .mult_assign { '*' }
806 .power_assign { '**' }
807 else { 'unknown op' }
808 }
809
810 if type_sym.has_method(extracted_op) {
811 p.error('cannot overload `${p.tok.kind}`, operator is implicitly overloaded because the `${extracted_op}` operator is overloaded')
812 }
813 p.error('cannot overload `${p.tok.kind}`, overload `${extracted_op}` and `${p.tok.kind}` will be automatically generated')
814 } else {
815 p.error_with_pos('expecting method name', p.tok.pos())
816 return ast.FnDecl{
817 scope: unsafe { nil }
818 }
819 }
820 // [T]
821 _, mut generic_names := p.parse_generic_types()
822 // generic names can be infer with receiver's generic names
823 if is_method && rec.typ.has_flag(.generic) {
824 sym := p.table.sym(rec.typ)
825 mut rec_generic_types := []ast.Type{}
826 match sym.info {
827 ast.Struct {
828 rec_generic_types = sym.info.generic_types.clone()
829 }
830 ast.Interface {
831 rec_generic_types = sym.info.generic_types.clone()
832 }
833 else {}
834 }
835
836 if rec_generic_types.len > 0 {
837 decl_generic_names := p.types_to_names(rec_generic_types, p.tok.pos(),
838 'rec_generic_types') or { return ast.FnDecl{
839 scope: unsafe { nil }
840 } }
841 fn_generic_names := generic_names.clone()
842 generic_names = p.table.generic_type_names(rec.typ)
843 if decl_generic_names.len != generic_names.len {
844 plural := if decl_generic_names.len == 1 { '' } else { 's' }
845 p.error_with_pos('expected ${decl_generic_names.len} generic parameter${plural}, got ${generic_names.len}',
846 rec.type_pos)
847 }
848 for gname in fn_generic_names {
849 if gname !in generic_names {
850 generic_names << gname
851 }
852 }
853 }
854 }
855 if generic_names.len > 0 {
856 for fna in fn_attrs {
857 if fna.name == 'export' {
858 p.error_with_pos('generic functions cannot be exported', fna.pos)
859 break
860 }
861 }
862 }
863 // Params
864 params_t, are_params_type_only, mut is_variadic, mut is_c_variadic := p.fn_params()
865 if is_c2v_variadic {
866 is_variadic = true
867 is_c_variadic = true
868 }
869 params << params_t
870 // Return type
871 mut return_type_pos := p.tok.pos()
872 mut return_type := ast.void_type
873 // don't confuse token on the next line: fn decl, [attribute]
874 same_line := p.tok.line_nr == p.prev_tok.line_nr
875 // `fn foo()[` is usually a mistaken body opener; report it as such
876 // instead of trying to parse `[` as a fixed array return type.
877 invalid_body_opener := same_line && p.tok.kind == .lsbr && p.peek_tok.line_nr > p.tok.line_nr
878 if invalid_body_opener {
879 p.unexpected(got: '${p.tok} after function signature', expecting: '`{`')
880 } else if (p.tok.kind.is_start_of_type() && (same_line || p.tok.kind != .lsbr))
881 || (same_line && p.tok.kind == .key_fn) {
882 // Disallow [T] as return type
883 if p.tok.kind == .lsbr && p.peek_tok.kind == .name && p.peek_tok.lit.len == 1
884 && p.peek_tok.lit[0].is_capital() {
885 return_type_pos = return_type_pos.extend(p.peek_tok.pos()).extend(p.peek_token(2).pos())
886 p.error_with_pos('invalid generic return, use `${p.peek_tok.lit}` instead',
887 return_type_pos)
888 }
889 p.inside_fn_return = true
890 return_type = p.parse_type()
891 p.inside_fn_return = false
892 return_type_pos = return_type_pos.extend(p.prev_tok.pos())
893
894 if p.tok.kind in [.question, .not] {
895 ret_type_sym := p.table.sym(return_type)
896 p.error_with_pos('wrong syntax, it must be ${p.tok.kind}${ret_type_sym.name}, not ${ret_type_sym.name}${p.tok.kind}',
897 return_type_pos)
898 }
899 }
900
901 if p.tok.kind == .comma {
902 mr_pos := return_type_pos.extend(p.peek_tok.pos())
903 p.error_with_pos('multiple return types in function declaration must use parentheses, e.g. (int, string)',
904 mr_pos)
905 }
906 mut type_sym_method_idx := 0
907 no_body := p.tok.kind != .lcbr
908 end_pos := p.prev_tok.pos()
909 short_fn_name := name
910 is_main := short_fn_name == 'main' && p.mod == 'main'
911 if is_main {
912 p.main_already_defined = true
913 }
914 is_test := (!is_method && params.len == 0) && p.inside_test_file
915 && (short_fn_name.starts_with('test_') || short_fn_name.starts_with('testsuite_')
916 || short_fn_name in ['before_each', 'after_each'])
917 file_mode := p.file_backend_mode
918 if is_main {
919 if 'main.main' in p.table.fns {
920 if '.' in os.args {
921 p.error_with_pos('multiple `main` functions detected, and you ran `v .`
922perhaps there are multiple V programs in this directory, and you need to
923run them via `v file.v` instead',
924 name_pos)
925 }
926 }
927 }
928 if is_method && is_static_type_method {
929 p.error_with_pos('cannot declare a static function as a receiver method', name_pos)
930 }
931 // Register
932 if !are_params_type_only {
933 for k, param in params {
934 if p.scope.known_var(param.name) {
935 p.error_with_pos('redefinition of parameter `${param.name}`', param.pos)
936 return ast.FnDecl{
937 scope: unsafe { nil }
938 }
939 }
940 effective_is_mut := if is_method && k == 0 {
941 param.is_mut
942 } else {
943 p.scope_var_is_mut(param.is_mut)
944 }
945 is_stack_obj := !param.typ.has_flag(.shared_f) && (param.is_mut || param.typ.is_ptr())
946 p.scope.register(ast.Var{
947 name: param.name
948 typ: param.typ
949 generic_typ: if param.typ.has_flag(.generic) { param.typ } else { ast.Type(0) }
950 is_mut: effective_is_mut
951 is_auto_deref: param.is_mut
952 is_stack_obj: is_stack_obj
953 pos: param.pos
954 is_used: is_pub || no_body || (is_method && k == 0) || p.builtin_mod
955 is_arg: true
956 ct_type_var: if (!is_method || k >= 0) && param.typ.has_flag(.generic)
957 && !param.typ.has_flag(.variadic) {
958 .generic_param
959 } else {
960 .no_comptime
961 }
962 })
963 }
964 }
965 if is_method {
966 // Do not allow to modify / add methods to types from other modules
967 // arrays/maps dont belong to a module only their element types do
968 // we could also check if kind is .array, .array_fixed, .map instead of mod.len
969 mut is_non_local := type_sym.mod.len > 0 && type_sym.mod != p.mod && type_sym.language == .v
970 // check maps & arrays, must be defined in same module as the elem type
971 if !is_non_local && !(p.builtin_mod && p.pref.is_fmt) && type_sym.kind in [.array, .map] {
972 elem_type_sym := p.table.sym(p.table.value_type(rec.typ))
973 is_non_local = elem_type_sym.mod.len > 0 && elem_type_sym.mod != p.mod
974 && elem_type_sym.language == .v
975 }
976 if is_non_local && !(p.file_backend_mode == .js && type_sym.mod.starts_with('Promise')) {
977 p.error_with_pos('cannot define new methods on non-local type ${type_sym.name}. Define an alias and use that instead like `type AliasName = ${type_sym.name}` ',
978 rec.type_pos)
979 return ast.FnDecl{
980 scope: unsafe { nil }
981 }
982 }
983 type_sym_method_idx = type_sym.register_method(ast.Fn{
984 name: name
985 file_mode: file_mode
986 params: params
987 return_type: return_type
988 is_variadic: is_variadic
989 generic_names: generic_names
990 is_pub: is_pub
991 is_deprecated: is_deprecated
992 is_noreturn: is_noreturn
993 is_unsafe: is_unsafe
994 is_must_use: is_must_use
995 is_main: is_main
996 is_test: is_test
997 is_keep_alive: is_keep_alive
998 is_method: true
999 receiver_type: rec.typ
1000 //
1001 attrs: fn_attrs
1002 is_conditional: conditional_ctdefine_idx != ast.invalid_type_idx
1003 ctdefine_idx: conditional_ctdefine_idx
1004 //
1005 no_body: no_body
1006 mod: p.mod
1007 file: p.file_path
1008 pos: start_pos
1009 name_pos: name_pos
1010 language: language
1011 //
1012 is_expand_simple_interpolation: is_expand_simple_interpolation
1013 })
1014 } else {
1015 name = match language {
1016 .c { 'C.${name}' }
1017 .js { 'JS.${name}' }
1018 .wasm { 'WASM.${name}' }
1019 else { p.prepend_mod(name) }
1020 }
1021
1022 if language == .v {
1023 existing, has_existing := table_fn_lookup(p.table, name)
1024 if has_existing {
1025 if existing.name != '' {
1026 if file_mode == .v && existing.file_mode != .v {
1027 // a definition made in a .c.v file, should have a priority over a .v file definition of the same function
1028 if !p.pref.is_fmt {
1029 name =
1030 p.prepend_mod('pure_v_but_overridden_by_${existing.file_mode}_${short_fn_name}')
1031 }
1032 } else if !p.pref.translated {
1033 p.table.redefined_fns << name
1034 }
1035 }
1036 }
1037 }
1038 p.table.register_fn(ast.Fn{
1039 name: name
1040 file_mode: file_mode
1041 params: params
1042 return_type: return_type
1043 is_variadic: is_variadic
1044 is_c_variadic: is_c_variadic
1045 generic_names: generic_names
1046 is_pub: is_pub
1047 is_deprecated: is_deprecated
1048 is_noreturn: is_noreturn
1049 is_ctor_new: is_ctor_new
1050 is_unsafe: is_unsafe
1051 is_must_use: is_must_use
1052 is_main: is_main
1053 is_test: is_test
1054 is_keep_alive: is_keep_alive
1055 is_method: false
1056 is_static_type_method: is_static_type_method
1057 receiver_type: if is_static_type_method { rec.typ } else { 0 } // used only if is static type method
1058 is_file_translated: p.is_translated
1059 //
1060 attrs: fn_attrs
1061 is_conditional: conditional_ctdefine_idx != ast.invalid_type_idx
1062 ctdefine_idx: conditional_ctdefine_idx
1063 //
1064 no_body: no_body
1065 mod: p.mod
1066 file: p.file_path
1067 pos: start_pos
1068 name_pos: name_pos
1069 language: language
1070 //
1071 is_expand_simple_interpolation: is_expand_simple_interpolation
1072 })
1073 }
1074 /*
1075 // Register implicit context var
1076 p.scope.register(ast.Var{
1077 name: 'ctx'
1078 typ: ast.error_type
1079 pos: p.tok.pos()
1080 is_used: true
1081 is_stack_obj: true
1082 })
1083 */
1084 // Body
1085 keep_fn_name := p.cur_fn_name
1086 p.cur_fn_name = name
1087 mut stmts := []ast.Stmt{}
1088 body_start_pos := p.tok.pos()
1089 if p.tok.kind == .lcbr {
1090 if language != .v && !(language == .js && type_sym.info is ast.Interface) {
1091 p.error_with_pos('interop functions cannot have a body', body_start_pos)
1092 }
1093 last_fn_scope := p.scope
1094 p.inside_fn = true
1095 p.inside_unsafe_fn = is_unsafe
1096 p.cur_fn_scope = p.scope
1097 if p.is_vls_skip_file {
1098 p.skip_scope()
1099 } else {
1100 stmts = p.parse_block_no_scope(true)
1101 }
1102 p.cur_fn_scope = last_fn_scope
1103 p.inside_unsafe_fn = false
1104 p.inside_fn = false
1105 }
1106 p.cur_fn_name = keep_fn_name
1107 if !no_body && are_params_type_only {
1108 p.error_with_pos('functions with type only params can not have bodies', body_start_pos)
1109 return ast.FnDecl{
1110 scope: unsafe { nil }
1111 }
1112 }
1113 // if no_body && !name.starts_with('C.') {
1114 // p.error_with_pos('did you mean C.${name} instead of ${name}', start_pos)
1115 // }
1116 fn_decl := ast.FnDecl{
1117 name: name
1118 short_name: short_fn_name
1119 mod: p.mod
1120 kind: p.call_kind(name)
1121 stmts: stmts
1122 return_type: return_type
1123 return_type_pos: return_type_pos
1124 params: params
1125 is_noreturn: is_noreturn
1126 is_manualfree: is_manualfree
1127 is_deprecated: is_deprecated
1128 is_exported: is_exported
1129 is_direct_arr: is_direct_arr
1130 is_pub: is_pub
1131 is_variadic: is_variadic
1132 is_c_variadic: is_c_variadic
1133 is_c_extern: is_c_extern
1134 is_main: is_main
1135 is_test: is_test
1136 is_keep_alive: is_keep_alive
1137 is_unsafe: is_unsafe
1138 is_must_use: is_must_use
1139 is_markused: is_markused
1140 is_ignore_overflow: is_ignore_overflow
1141 is_weak: is_weak
1142 is_file_translated: p.is_translated
1143 //
1144 attrs: fn_attrs
1145 is_conditional: conditional_ctdefine_idx != ast.invalid_type_idx
1146 ctdefine_idx: conditional_ctdefine_idx
1147 //
1148 receiver: ast.StructField{
1149 name: rec.name
1150 typ: rec.typ
1151 type_pos: rec.type_pos
1152 pos: rec.pos
1153 }
1154 generic_names: generic_names
1155 receiver_pos: rec.pos
1156 is_method: is_method
1157 is_static_type_method: is_static_type_method
1158 static_type_pos: static_type_pos
1159 method_type_pos: rec.type_pos
1160 method_idx: type_sym_method_idx
1161 rec_mut: rec.is_mut
1162 language: language
1163 no_body: no_body
1164 pos: start_pos.extend_with_last_line(end_pos, p.prev_tok.line_nr)
1165 end_pos: p.tok.pos()
1166 name_pos: name_pos
1167 body_pos: body_start_pos
1168 file: p.file_path
1169 is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts
1170 scope: p.scope
1171 label_names: p.label_names
1172 end_comments: p.eat_comments(same_line: true)
1173 comments: comments
1174 //
1175 is_expand_simple_interpolation: is_expand_simple_interpolation
1176 }
1177 if generic_names.len > 0 {
1178 p.table.register_fn_generic_types(fn_decl.fkey())
1179 }
1180 p.label_names = []
1181 if p.pref.is_vls {
1182 type_str := if (is_method || is_static_type_method) && rec.typ != ast.no_type {
1183 p.table.sym(rec.typ.idx_type()).name.all_after_last('.')
1184 } else {
1185 ''
1186 }
1187 key := 'fn_${p.mod}[${type_str}]${short_fn_name}'
1188 val := ast.VlsInfo{
1189 pos: fn_decl.pos
1190 doc: p.keyword_comments_to_string(short_fn_name, comments_before_key_fn) +
1191 p.comments_to_string(fn_decl.end_comments)
1192 }
1193 p.table.register_vls_info(key, val)
1194 }
1195 return fn_decl
1196}
1197
1198fn (mut p Parser) fn_receiver(mut params []ast.Param, mut rec ReceiverParsingInfo) ! {
1199 p.inside_receiver_param = true
1200 defer {
1201 p.inside_receiver_param = false
1202 }
1203 lpar_pos := p.tok.pos()
1204 p.next() // (
1205 is_shared := p.tok.kind == .key_shared
1206 is_atomic := p.tok.kind == .key_atomic
1207 rec.is_mut = p.tok.kind == .key_mut || is_shared || is_atomic
1208 if rec.is_mut {
1209 p.next() // `mut`
1210 }
1211 if is_shared {
1212 p.register_auto_import('sync')
1213 }
1214 rec_start_pos := p.tok.pos()
1215 rec.name = p.check_name()
1216 if !rec.is_mut {
1217 rec.is_mut = p.tok.kind == .key_mut
1218 if rec.is_mut {
1219 ptoken2 := p.peek_token(2) // needed to prevent codegen bug, where .pos() expects &Token
1220 p.warn_with_pos('use `(mut f Foo)` instead of `(f mut Foo)`',
1221 lpar_pos.extend(ptoken2.pos()))
1222 }
1223 }
1224 if p.tok.kind == .key_shared {
1225 ptoken2 := p.peek_token(2) // needed to prevent codegen bug, where .pos() expects &Token
1226 p.error_with_pos('use `(shared f Foo)` instead of `(f shared Foo)`',
1227 lpar_pos.extend(ptoken2.pos()))
1228 }
1229 rec.pos = rec_start_pos.extend(p.tok.pos())
1230 is_amp := p.tok.kind == .amp
1231 if p.tok.kind == .name && p.tok.lit == 'JS' {
1232 rec.language = ast.Language.js
1233 }
1234 // if rec.is_mut {
1235 // p.check(.key_mut)
1236 // }
1237 // TODO: talk to alex, should mut be parsed with the type like this?
1238 // or should it be a property of the arg, like this ptr/mut becomes indistinguishable
1239 rec.type_pos = p.tok.pos()
1240 rec.typ = p.parse_type_with_mut(rec.is_mut)
1241 if rec.typ.idx() == 0 {
1242 // error is set in parse_type
1243 return error('void receiver type')
1244 }
1245 rec.type_pos = rec.type_pos.extend(p.prev_tok.pos())
1246 if is_amp && rec.is_mut {
1247 p.error_with_pos('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`',
1248 lpar_pos.extend(p.tok.pos()))
1249 return error('invalid `mut f &Foo`')
1250 }
1251 if is_shared {
1252 rec.typ = rec.typ.set_flag(.shared_f)
1253 }
1254 if is_atomic {
1255 rec.typ = rec.typ.set_flag(.atomic_f)
1256 }
1257
1258 if rec.language != .v {
1259 p.check_for_impure_v(rec.language, rec.type_pos)
1260 }
1261
1262 p.check(.rpar)
1263
1264 params << ast.Param{
1265 pos: rec_start_pos
1266 name: rec.name
1267 is_mut: rec.is_mut
1268 is_atomic: is_atomic
1269 is_shared: is_shared
1270 typ: rec.typ
1271 type_pos: rec.type_pos
1272 }
1273}
1274
1275fn (mut p Parser) anon_fn() ast.AnonFn {
1276 pos := p.tok.pos()
1277 p.check(.key_fn)
1278 if p.tok.kind == .name {
1279 if p.disallow_declarations_in_script_mode() {
1280 return ast.AnonFn{}
1281 }
1282 }
1283 old_inside_defer := p.inside_defer
1284 p.inside_defer = false
1285 p.open_scope()
1286 defer {
1287 p.close_scope()
1288 }
1289 p.scope.detached_from_parent = true
1290 inherited_vars := if p.tok.kind == .lsbr && !(p.peek_tok.kind == .name
1291 && p.peek_tok.lit.len == 1 && p.peek_tok.lit[0].is_capital()) {
1292 p.closure_vars()
1293 } else {
1294 []ast.Param{}
1295 }
1296 inherited_vars_name := inherited_vars.map(it.name)
1297 _, generic_names := p.parse_generic_types()
1298 params, _, is_variadic, _ := p.fn_params()
1299 for param in params {
1300 if param.name == '' && p.table.sym(param.typ).kind != .placeholder {
1301 p.error_with_pos('use `_` to name an unused parameter', param.pos)
1302 }
1303 if param.name in inherited_vars_name {
1304 p.error_with_pos('the parameter name `${param.name}` conflicts with the captured value name',
1305 param.pos)
1306 } else if p.scope.known_var(param.name) {
1307 p.error_with_pos('redefinition of parameter `${param.name}`', param.pos)
1308 }
1309 is_stack_obj := !param.typ.has_flag(.shared_f) && (param.is_mut || param.typ.is_ptr())
1310 p.scope.register(ast.Var{
1311 name: param.name
1312 typ: param.typ
1313 generic_typ: if param.typ.has_flag(.generic) { param.typ } else { ast.Type(0) }
1314 is_mut: p.scope_var_is_mut(param.is_mut)
1315 is_auto_deref: param.is_mut
1316 pos: param.pos
1317 is_used: true
1318 is_arg: true
1319 is_stack_obj: is_stack_obj
1320 })
1321 }
1322 mut same_line := p.tok.line_nr == p.prev_tok.line_nr
1323 mut return_type := ast.void_type
1324 mut return_type_pos := p.tok.pos()
1325 // lpar: multiple return types
1326 if same_line {
1327 if (p.tok.kind.is_start_of_type() && (same_line || p.tok.kind != .lsbr))
1328 || (same_line && p.tok.kind == .key_fn) {
1329 p.inside_fn_return = true
1330 return_type = p.parse_type()
1331 p.inside_fn_return = false
1332 return_type_pos = return_type_pos.extend(p.tok.pos())
1333 } else if p.tok.kind != .lcbr {
1334 p.error_with_pos('expected return type, not ${p.tok} for anonymous function',
1335 p.tok.pos())
1336 }
1337 }
1338 mut stmts := []ast.Stmt{}
1339 no_body := p.tok.kind != .lcbr
1340 same_line = p.tok.line_nr == p.prev_tok.line_nr
1341 if no_body && same_line {
1342 p.unexpected(got: '${p.tok} after anonymous function signature', expecting: '`{`')
1343 }
1344 mut label_names := []string{}
1345 mut func := ast.Fn{
1346 params: params
1347 is_variadic: is_variadic
1348 return_type: return_type
1349 is_method: false
1350 }
1351 name := p.table.get_anon_fn_name(p.unique_prefix, func, p.tok.pos())
1352 keep_fn_name := p.cur_fn_name
1353 p.cur_fn_name = name
1354 if p.tok.kind == .lcbr {
1355 tmp := p.label_names
1356 p.label_names = []
1357 old_assign_rhs := p.inside_assign_rhs
1358 p.inside_assign_rhs = false
1359 stmts = p.parse_block_no_scope(false)
1360 p.inside_assign_rhs = old_assign_rhs
1361 label_names = p.label_names.clone()
1362 p.label_names = tmp
1363 }
1364 p.cur_fn_name = keep_fn_name
1365 func.name = name
1366 idx := p.table.find_or_register_fn_type(func, true, false)
1367 typ := if generic_names.len > 0 {
1368 ast.new_type(idx).set_flag(.generic)
1369 } else {
1370 ast.new_type(idx)
1371 }
1372 p.inside_defer = old_inside_defer
1373 // name := p.table.get_type_name(typ)
1374 return ast.AnonFn{
1375 decl: ast.FnDecl{
1376 name: name
1377 short_name: ''
1378 mod: p.mod
1379 stmts: stmts
1380 return_type: return_type
1381 return_type_pos: return_type_pos
1382 params: params
1383 is_variadic: is_variadic
1384 is_closure: inherited_vars.len > 0
1385 is_method: false
1386 generic_names: generic_names
1387 is_anon: true
1388 no_body: no_body
1389 pos: pos.extend(p.prev_tok.pos())
1390 file: p.file_path
1391 scope: p.scope
1392 label_names: label_names
1393 }
1394 inherited_vars: inherited_vars
1395 typ: typ
1396 }
1397}
1398
1399// part of fn declaration
1400// returns: params, are_params_type_only, mut is_variadic, mut is_c_variadic
1401fn (mut p Parser) fn_params() ([]ast.Param, bool, bool, bool) {
1402 p.check(.lpar)
1403 mut params := []ast.Param{}
1404 mut is_variadic := false
1405 mut is_c_variadic := false
1406 // `int, int, string` (no names, just types)
1407 param_name := if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() {
1408 p.prepend_mod(p.tok.lit)
1409 } else {
1410 p.tok.lit
1411 }
1412 is_generic_type := p.tok.kind == .name && p.tok.lit.len == 1 && p.tok.lit[0].is_capital()
1413
1414 types_only := p.tok.kind in [.question, .not, .amp, .ellipsis, .key_fn, .lsbr]
1415 || (p.peek_tok.kind == .comma && (p.table.known_type(param_name) || is_generic_type))
1416 || p.peek_tok.kind == .dot || p.peek_tok.kind == .rpar || p.fn_language == .c
1417 || (p.tok.kind == .key_mut && (p.peek_tok.kind in [.amp, .ellipsis, .key_fn, .lsbr]
1418 || (p.peek_token(2).kind == .comma && (p.tok.kind != .key_mut
1419 || p.peek_tok.lit[0].is_capital())) || p.peek_token(2).kind == .rpar
1420 || (p.peek_tok.kind == .name && p.peek_token(2).kind == .dot)))
1421 mut prev_param_newline := p.tok.pos().line_nr
1422 // TODO: copy paste, merge 2 branches
1423 if types_only {
1424 mut param_no := 1
1425 for p.tok.kind != .rpar {
1426 if p.tok.kind == .eof {
1427 p.error_with_pos('expecting `)`', p.tok.pos())
1428 return []ast.Param{}, false, false, false
1429 }
1430 is_shared := p.tok.kind == .key_shared
1431 is_atomic := p.tok.kind == .key_atomic
1432 is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
1433 mut name := ''
1434 if is_mut {
1435 p.next()
1436 }
1437 if p.fn_language == .c && (is_ident_name(p.tok.lit) || (p.tok.lit.len > 1
1438 && p.tok.lit[0] == `@` && is_ident_name(p.tok.lit[1..])))
1439 && p.tok.kind !in [.key_fn, .key_struct]
1440 && p.peek_tok.kind !in [.comma, .rpar, .dot] {
1441 name = p.tok.lit
1442 p.next()
1443 }
1444 if p.tok.kind == .ellipsis {
1445 p.next()
1446 is_variadic = true
1447 is_c_variadic = p.tok.kind == .rpar
1448 if is_c_variadic {
1449 p.check(.rpar)
1450 return params, types_only, is_variadic, is_c_variadic
1451 }
1452 }
1453 pos := p.tok.pos()
1454 prev_inside_fn_param := p.inside_fn_param
1455 p.inside_fn_param = true
1456 mut param_type := p.parse_type()
1457 p.inside_fn_param = prev_inside_fn_param
1458 orig_param_type := param_type
1459 type_pos := pos.extend(p.prev_tok.pos())
1460 if param_type == 0 {
1461 // error is added in parse_type
1462 return []ast.Param{}, false, false, false
1463 }
1464 if param_type == ast.chan_type {
1465 p.chan_type_error()
1466 return []ast.Param{}, false, false, false
1467 }
1468 if is_mut {
1469 if !param_type.has_flag(.generic) {
1470 if is_variadic {
1471 p.error_with_pos('variadic arguments cannot be `mut`, `shared` or `atomic`',
1472 pos)
1473 }
1474 if is_shared {
1475 p.check_fn_shared_arguments(param_type, pos)
1476 } else if is_atomic {
1477 p.check_fn_atomic_arguments(param_type, pos)
1478 } else {
1479 p.check_fn_mutable_arguments(param_type, pos)
1480 }
1481 } else if is_shared || is_atomic {
1482 p.error_with_pos('generic object cannot be `atomic`or `shared`', pos)
1483 return []ast.Param{}, false, false, false
1484 }
1485 if param_type.is_ptr() && p.table.sym(param_type).kind == .struct {
1486 param_type = param_type.ref()
1487 } else {
1488 param_type = param_type.set_nr_muls(1)
1489 }
1490 if param_type.has_flag(.option) {
1491 param_type = param_type.set_flag(.option_mut_param_t)
1492 }
1493 if is_shared {
1494 param_type = param_type.set_flag(.shared_f)
1495 }
1496 if is_atomic {
1497 param_type = param_type.set_flag(.atomic_f)
1498 }
1499 }
1500 if is_variadic {
1501 param_type =
1502 ast.new_type(p.table.find_or_register_array(param_type)).set_flag(.variadic)
1503 }
1504 if p.tok.kind == .eof {
1505 p.error_with_pos('expecting `)`', p.prev_tok.pos())
1506 return []ast.Param{}, false, false, false
1507 }
1508
1509 if p.tok.kind == .comma {
1510 if is_variadic {
1511 p.error_with_pos('cannot use ...(variadic) with non-final parameter no ${param_no}',
1512 pos)
1513 return []ast.Param{}, false, false, false
1514 }
1515 p.next()
1516 }
1517 alanguage := p.table.sym(param_type).language
1518 if alanguage != .v {
1519 p.check_for_impure_v(alanguage, pos)
1520 }
1521 params << ast.Param{
1522 pos: pos
1523 name: name
1524 is_mut: is_mut
1525 orig_typ: orig_param_type
1526 typ: param_type
1527 type_pos: type_pos
1528 on_newline: prev_param_newline != pos.line_nr
1529 }
1530 prev_param_newline = pos.line_nr
1531 param_no++
1532 if param_no > 1024 {
1533 p.error_with_pos('too many parameters', pos)
1534 return []ast.Param{}, false, false, false
1535 }
1536 }
1537 } else {
1538 for p.tok.kind != .rpar {
1539 if p.tok.kind == .eof {
1540 p.error_with_pos('expecting `)`', p.tok.pos())
1541 return []ast.Param{}, false, false, false
1542 }
1543 is_shared := p.tok.kind == .key_shared
1544 is_atomic := p.tok.kind == .key_atomic
1545 mut is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
1546 if is_mut {
1547 p.next()
1548 }
1549 if p.tok.kind == .ellipsis && p.peek_tok.kind == .rpar {
1550 p.check(.ellipsis)
1551 p.check(.rpar)
1552 return params, types_only, true, true
1553 }
1554
1555 mut param_pos := [p.tok.pos()]
1556 name := p.check_name()
1557 mut param_names := [name]
1558 if name != '' && p.fn_language == .v && name[0].is_capital() {
1559 p.error_with_pos('parameter name must not begin with upper case letter (`${param_names[0]}`)',
1560 p.prev_tok.pos())
1561 }
1562 mut type_pos := [p.tok.pos()]
1563 // `a, b, c int`
1564 for p.tok.kind == .comma {
1565 if !p.pref.is_fmt {
1566 p.error('`fn f(x, y Type)` syntax has been deprecated. ' +
1567 'Use `fn f(x Type, y Type)` instead. You can run `v fmt -w "${p.scanner.file_path}"` to automatically fix your code.')
1568 }
1569 p.next()
1570 param_pos << p.tok.pos()
1571 param_names << p.check_name()
1572 type_pos << p.tok.pos()
1573 }
1574 if p.tok.kind == .key_mut {
1575 // TODO: remove old syntax
1576 if !p.pref.is_fmt {
1577 p.warn_with_pos('use `mut f Foo` instead of `f mut Foo`', p.tok.pos())
1578 }
1579 is_mut = true
1580 }
1581 if p.tok.kind == .key_shared {
1582 p.error_with_pos('use `shared f Foo` instead of `f shared Foo`', p.tok.pos())
1583 }
1584 if p.tok.kind == .ellipsis {
1585 p.next()
1586 is_variadic = true
1587 is_c_variadic = p.tok.kind == .rpar
1588 if is_c_variadic {
1589 p.check(.rpar)
1590 return params, types_only, is_variadic, is_c_variadic
1591 }
1592 }
1593 pos := p.tok.pos()
1594 prev_inside_fn_param := p.inside_fn_param
1595 p.inside_fn_param = true
1596 mut typ := p.parse_type()
1597 p.inside_fn_param = prev_inside_fn_param
1598 orig_typ := typ
1599 type_pos[0] = pos.extend(p.prev_tok.pos())
1600 if typ == 0 {
1601 // error is added in parse_type
1602 return []ast.Param{}, false, false, false
1603 }
1604 if typ == ast.chan_type {
1605 p.chan_type_error()
1606 return []ast.Param{}, false, false, false
1607 }
1608 if is_mut {
1609 if !typ.has_flag(.generic) {
1610 if is_variadic {
1611 p.error_with_pos('variadic arguments cannot be `mut`, `shared` or `atomic`',
1612 pos)
1613 }
1614 if is_shared {
1615 p.check_fn_shared_arguments(typ, pos)
1616 } else if is_atomic {
1617 p.check_fn_atomic_arguments(typ, pos)
1618 } else {
1619 p.check_fn_mutable_arguments(typ, pos)
1620 }
1621 } else if is_shared || is_atomic {
1622 p.error_with_pos('generic object cannot be `atomic` or `shared`', pos)
1623 return []ast.Param{}, false, false, false
1624 }
1625 if typ.is_ptr() && p.table.sym(typ).kind == .struct {
1626 typ = typ.ref()
1627 } else {
1628 typ = typ.set_nr_muls(1)
1629 }
1630 if typ.has_flag(.option) {
1631 typ = typ.set_flag(.option_mut_param_t)
1632 }
1633 if is_shared {
1634 typ = typ.set_flag(.shared_f)
1635 }
1636 if is_atomic {
1637 typ = typ.set_flag(.atomic_f)
1638 }
1639 }
1640 if is_variadic {
1641 // derive flags, however nr_muls only needs to be set on the array elem type, so clear it on the arg type
1642 typ =
1643 ast.new_type(p.table.find_or_register_array(typ)).derive(typ).set_nr_muls(0).set_flag(.variadic)
1644 }
1645 for i, para_name in param_names {
1646 alanguage := p.table.sym(typ).language
1647 if alanguage != .v {
1648 p.check_for_impure_v(alanguage, type_pos[i])
1649 }
1650 can_omit_comma := p.can_omit_comma_between_fn_params()
1651 params << ast.Param{
1652 pos: param_pos[i]
1653 name: para_name
1654 is_mut: is_mut
1655 is_atomic: is_atomic
1656 is_shared: is_shared
1657 orig_typ: orig_typ
1658 typ: typ
1659 type_pos: type_pos[i]
1660 on_newline: prev_param_newline != param_pos[i].line_nr
1661 }
1662 prev_param_newline = param_pos[i].line_nr
1663 if is_variadic && ((p.tok.kind == .comma && p.peek_tok.kind != .rpar)
1664 || can_omit_comma) {
1665 p.error_with_pos('cannot use ...(variadic) with non-final parameter ${para_name}',
1666 param_pos[i])
1667 return []ast.Param{}, false, false, false
1668 }
1669 }
1670 if p.tok.kind == .eof {
1671 p.error_with_pos('expecting `)`', p.prev_tok.pos())
1672 return []ast.Param{}, false, false, false
1673 }
1674 if p.tok.kind == .comma {
1675 p.next()
1676 } else if p.tok.kind != .rpar && !p.can_omit_comma_between_fn_params() {
1677 p.check(.comma)
1678 }
1679 }
1680 }
1681 p.check(.rpar)
1682 return params, types_only, is_variadic, is_c_variadic
1683}
1684
1685fn (mut p Parser) spawn_expr() ast.SpawnExpr {
1686 p.next()
1687 spos := p.tok.pos()
1688 old_inside_assign_rhs := p.inside_assign_rhs
1689 p.inside_assign_rhs = false
1690 expr := p.expr(0)
1691 p.inside_assign_rhs = old_inside_assign_rhs
1692 mut call_expr := if expr is ast.CallExpr {
1693 expr
1694 } else {
1695 p.error_with_pos('expression in `spawn` must be a function call', expr.pos())
1696 ast.CallExpr{
1697 scope: p.scope
1698 }
1699 }
1700 call_expr.is_return_used = true
1701 pos := spos.extend(p.prev_tok.pos())
1702 p.register_auto_import('sync.threads')
1703 p.table.gostmts++
1704 return ast.SpawnExpr{
1705 call_expr: call_expr
1706 pos: pos
1707 }
1708}
1709
1710fn (mut p Parser) go_expr() ast.GoExpr {
1711 p.next()
1712 spos := p.tok.pos()
1713 old_inside_assign_rhs := p.inside_assign_rhs
1714 p.inside_assign_rhs = false
1715 expr := p.expr(0)
1716 p.inside_assign_rhs = old_inside_assign_rhs
1717 mut call_expr := if expr is ast.CallExpr {
1718 expr
1719 } else {
1720 p.error_with_pos('expression in `go` must be a function call', expr.pos())
1721 ast.CallExpr{
1722 scope: p.scope
1723 }
1724 }
1725 call_expr.is_return_used = true
1726 pos := spos.extend(p.prev_tok.pos())
1727 // p.register_auto_import('coroutines')
1728 p.table.gostmts++
1729 return ast.GoExpr{
1730 call_expr: call_expr
1731 pos: pos
1732 }
1733}
1734
1735fn (mut p Parser) closure_vars() []ast.Param {
1736 p.check(.lsbr)
1737 mut vars := []ast.Param{cap: 5}
1738 for {
1739 is_shared := p.tok.kind == .key_shared
1740 is_atomic := p.tok.kind == .key_atomic
1741 is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
1742 // FIXME: is_shared & is_atomic aren't used further
1743 if is_mut {
1744 p.next()
1745 }
1746 var_pos := p.tok.pos()
1747 p.check(.name)
1748 var_name := p.prev_tok.lit
1749 mut var := p.scope.parent.find_var(var_name) or {
1750 if p.table.global_scope.known_global(var_name) {
1751 p.error_with_pos('no need to capture global variable `${var_name}` in closure',
1752 p.prev_tok.pos())
1753 return []
1754 }
1755 p.error_with_pos('undefined ident: `${var_name}`', p.prev_tok.pos())
1756 return []
1757 }
1758 var.is_used = true
1759 if is_mut {
1760 var.is_changed = true
1761 }
1762 p.scope.register(ast.Var{
1763 ...(*var)
1764 pos: var_pos
1765 is_inherited: true
1766 has_inherited: var.is_inherited
1767 is_used: false
1768 is_changed: false
1769 is_mut: p.scope_var_is_mut(is_mut)
1770 })
1771 vars << ast.Param{
1772 pos: var_pos
1773 name: var_name
1774 is_mut: p.scope_var_is_mut(is_mut)
1775 is_atomic: is_atomic
1776 is_shared: is_shared
1777 }
1778 if p.tok.kind != .comma {
1779 break
1780 }
1781 p.next()
1782 }
1783 p.check(.rsbr)
1784 return vars
1785}
1786
1787fn (mut p Parser) check_fn_mutable_arguments(typ ast.Type, pos token.Pos) {
1788 sym := p.table.sym(typ)
1789 if sym.kind in [.array, .array_fixed, .interface, .map, .placeholder, .struct, .generic_inst,
1790 .sum_type] {
1791 return
1792 }
1793 if typ.is_any_kind_of_pointer() {
1794 return
1795 }
1796 if sym.kind == .alias {
1797 atyp := (sym.info as ast.Alias).parent_type
1798 p.check_fn_mutable_arguments(atyp, pos)
1799 return
1800 }
1801 if p.fn_language == .c {
1802 return
1803 }
1804 p.error_with_pos(
1805 'mutable arguments are only allowed for arrays, interfaces, maps, pointers, structs or their aliases\n' +
1806 'return values instead: `fn foo(mut n ${sym.name}) {` => `fn foo(n ${sym.name}) ${sym.name} {`',
1807 pos)
1808}
1809
1810fn (mut p Parser) check_fn_shared_arguments(typ ast.Type, pos token.Pos) {
1811 mut sym := p.table.sym(typ)
1812 if sym.kind == .generic_inst {
1813 sym = p.table.type_symbols[(sym.info as ast.GenericInst).parent_idx]
1814 }
1815 if sym.kind == .function {
1816 p.error_with_pos('shared arguments are not allowed for function types', pos)
1817 }
1818}
1819
1820fn (mut p Parser) check_fn_atomic_arguments(typ ast.Type, pos token.Pos) {
1821 sym := p.table.sym(typ)
1822 if sym.kind !in [.u32, .int, .u64] {
1823 p.error_with_pos('atomic arguments are only allowed for 32/64 bit integers\n' +
1824 'use shared arguments instead: `fn foo(atomic n ${sym.name}) {` => `fn foo(shared n ${sym.name}) {`',
1825 pos)
1826 }
1827}
1828
1829fn have_fn_main(stmts []ast.Stmt) bool {
1830 for stmt in stmts {
1831 if stmt is ast.FnDecl {
1832 if stmt.name == 'main.main' {
1833 return true
1834 }
1835 }
1836 }
1837 return false
1838}
1839