vxx2 / vlib / v / parser / fn.v
1849 lines · 1811 sloc · 49.46 KB · a8cc0bb64eb2e3d48acfa0409c1e95b2226c8e5c
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 mut is_structured_receiver_pattern := false
844 if decl_generic_names.len != generic_names.len {
845 structured_generic_names :=
846 p.table.structured_receiver_generic_pattern_names(rec.typ)
847 if structured_generic_names.len > 0 {
848 generic_names = structured_generic_names.clone()
849 is_structured_receiver_pattern = true
850 }
851 }
852 if decl_generic_names.len != generic_names.len && !is_structured_receiver_pattern {
853 plural := if decl_generic_names.len == 1 { '' } else { 's' }
854 p.error_with_pos('expected ${decl_generic_names.len} generic parameter${plural}, got ${generic_names.len}',
855 rec.type_pos)
856 }
857 for gname in fn_generic_names {
858 if gname !in generic_names {
859 generic_names << gname
860 }
861 }
862 }
863 }
864 if generic_names.len > 0 {
865 for fna in fn_attrs {
866 if fna.name == 'export' {
867 p.error_with_pos('generic functions cannot be exported', fna.pos)
868 break
869 }
870 }
871 }
872 // Params
873 params_t, are_params_type_only, mut is_variadic, mut is_c_variadic := p.fn_params()
874 if is_c2v_variadic {
875 is_variadic = true
876 is_c_variadic = true
877 }
878 params << params_t
879 // Return type
880 mut return_type_pos := p.tok.pos()
881 mut return_type := ast.void_type
882 // don't confuse token on the next line: fn decl, [attribute]
883 same_line := p.tok.line_nr == p.prev_tok.line_nr
884 // `fn foo()[` is usually a mistaken body opener; report it as such
885 // instead of trying to parse `[` as a fixed array return type.
886 invalid_body_opener := same_line && p.tok.kind == .lsbr && p.peek_tok.line_nr > p.tok.line_nr
887 if invalid_body_opener {
888 p.unexpected(got: '${p.tok} after function signature', expecting: '`{`')
889 } else if (p.tok.kind.is_start_of_type() && (same_line || p.tok.kind != .lsbr))
890 || (same_line && p.tok.kind == .key_fn) {
891 // Disallow [T] as return type
892 if p.tok.kind == .lsbr && p.peek_tok.kind == .name && p.peek_tok.lit.len == 1
893 && p.peek_tok.lit[0].is_capital() {
894 return_type_pos = return_type_pos.extend(p.peek_tok.pos()).extend(p.peek_token(2).pos())
895 p.error_with_pos('invalid generic return, use `${p.peek_tok.lit}` instead',
896 return_type_pos)
897 }
898 p.inside_fn_return = true
899 return_type = p.parse_type()
900 p.inside_fn_return = false
901 return_type_pos = return_type_pos.extend(p.prev_tok.pos())
902
903 if p.tok.kind in [.question, .not] {
904 ret_type_sym := p.table.sym(return_type)
905 p.error_with_pos('wrong syntax, it must be ${p.tok.kind}${ret_type_sym.name}, not ${ret_type_sym.name}${p.tok.kind}',
906 return_type_pos)
907 }
908 }
909
910 if p.tok.kind == .comma {
911 mr_pos := return_type_pos.extend(p.peek_tok.pos())
912 p.error_with_pos('multiple return types in function declaration must use parentheses, e.g. (int, string)',
913 mr_pos)
914 }
915 mut type_sym_method_idx := 0
916 no_body := p.tok.kind != .lcbr
917 end_pos := p.prev_tok.pos()
918 short_fn_name := name
919 is_main := short_fn_name == 'main' && p.mod == 'main'
920 if is_main {
921 p.main_already_defined = true
922 }
923 is_test := (!is_method && params.len == 0) && p.inside_test_file
924 && (short_fn_name.starts_with('test_') || short_fn_name.starts_with('testsuite_')
925 || short_fn_name in ['before_each', 'after_each'])
926 file_mode := p.file_backend_mode
927 if is_main {
928 if 'main.main' in p.table.fns {
929 if '.' in os.args {
930 p.error_with_pos('multiple `main` functions detected, and you ran `v .`
931perhaps there are multiple V programs in this directory, and you need to
932run them via `v file.v` instead',
933 name_pos)
934 }
935 }
936 }
937 if is_method && is_static_type_method {
938 p.error_with_pos('cannot declare a static function as a receiver method', name_pos)
939 }
940 // Register
941 if !are_params_type_only {
942 for k, param in params {
943 if p.scope.known_var(param.name) {
944 p.error_with_pos('redefinition of parameter `${param.name}`', param.pos)
945 return ast.FnDecl{
946 scope: unsafe { nil }
947 }
948 }
949 effective_is_mut := if is_method && k == 0 {
950 param.is_mut
951 } else {
952 p.scope_var_is_mut(param.is_mut)
953 }
954 is_stack_obj := !param.typ.has_flag(.shared_f) && (param.is_mut || param.typ.is_ptr())
955 p.scope.register(ast.Var{
956 name: param.name
957 typ: param.typ
958 generic_typ: if param.typ.has_flag(.generic) { param.typ } else { ast.Type(0) }
959 is_mut: effective_is_mut
960 is_auto_deref: param.is_mut
961 is_stack_obj: is_stack_obj
962 pos: param.pos
963 is_used: is_pub || no_body || (is_method && k == 0) || p.builtin_mod
964 is_arg: true
965 ct_type_var: if (!is_method || k >= 0) && param.typ.has_flag(.generic)
966 && !param.typ.has_flag(.variadic) {
967 .generic_param
968 } else {
969 .no_comptime
970 }
971 })
972 }
973 }
974 if is_method {
975 // Do not allow to modify / add methods to types from other modules
976 // arrays/maps dont belong to a module only their element types do
977 // we could also check if kind is .array, .array_fixed, .map instead of mod.len
978 mut is_non_local := type_sym.mod.len > 0 && type_sym.mod != p.mod && type_sym.language == .v
979 // check maps & arrays, must be defined in same module as the elem type
980 if !is_non_local && !(p.builtin_mod && p.pref.is_fmt) && type_sym.kind in [.array, .map] {
981 elem_type_sym := p.table.sym(p.table.value_type(rec.typ))
982 is_non_local = elem_type_sym.mod.len > 0 && elem_type_sym.mod != p.mod
983 && elem_type_sym.language == .v
984 }
985 if is_non_local && !(p.file_backend_mode == .js && type_sym.mod.starts_with('Promise')) {
986 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}` ',
987 rec.type_pos)
988 return ast.FnDecl{
989 scope: unsafe { nil }
990 }
991 }
992 method := ast.Fn{
993 name: name
994 file_mode: file_mode
995 params: params
996 return_type: return_type
997 is_variadic: is_variadic
998 generic_names: generic_names
999 is_pub: is_pub
1000 is_deprecated: is_deprecated
1001 is_noreturn: is_noreturn
1002 is_unsafe: is_unsafe
1003 is_must_use: is_must_use
1004 is_main: is_main
1005 is_test: is_test
1006 is_keep_alive: is_keep_alive
1007 is_method: true
1008 receiver_type: rec.typ
1009 //
1010 attrs: fn_attrs
1011 is_conditional: conditional_ctdefine_idx != ast.invalid_type_idx
1012 ctdefine_idx: conditional_ctdefine_idx
1013 //
1014 no_body: no_body
1015 mod: p.mod
1016 file: p.file_path
1017 pos: start_pos
1018 name_pos: name_pos
1019 language: language
1020 //
1021 is_expand_simple_interpolation: is_expand_simple_interpolation
1022 }
1023 type_sym_method_idx = type_sym.register_method(method)
1024 p.table.register_structured_receiver_method(method)
1025 } else {
1026 name = match language {
1027 .c { 'C.${name}' }
1028 .js { 'JS.${name}' }
1029 .wasm { 'WASM.${name}' }
1030 else { p.prepend_mod(name) }
1031 }
1032
1033 if language == .v {
1034 existing, has_existing := table_fn_lookup(p.table, name)
1035 if has_existing {
1036 if existing.name != '' {
1037 if file_mode == .v && existing.file_mode != .v {
1038 // a definition made in a .c.v file, should have a priority over a .v file definition of the same function
1039 if !p.pref.is_fmt {
1040 name =
1041 p.prepend_mod('pure_v_but_overridden_by_${existing.file_mode}_${short_fn_name}')
1042 }
1043 } else if !p.pref.translated {
1044 p.table.redefined_fns << name
1045 }
1046 }
1047 }
1048 }
1049 p.table.register_fn(ast.Fn{
1050 name: name
1051 file_mode: file_mode
1052 params: params
1053 return_type: return_type
1054 is_variadic: is_variadic
1055 is_c_variadic: is_c_variadic
1056 generic_names: generic_names
1057 is_pub: is_pub
1058 is_deprecated: is_deprecated
1059 is_noreturn: is_noreturn
1060 is_ctor_new: is_ctor_new
1061 is_unsafe: is_unsafe
1062 is_must_use: is_must_use
1063 is_main: is_main
1064 is_test: is_test
1065 is_keep_alive: is_keep_alive
1066 is_method: false
1067 is_static_type_method: is_static_type_method
1068 receiver_type: if is_static_type_method { rec.typ } else { 0 } // used only if is static type method
1069 is_file_translated: p.is_translated
1070 //
1071 attrs: fn_attrs
1072 is_conditional: conditional_ctdefine_idx != ast.invalid_type_idx
1073 ctdefine_idx: conditional_ctdefine_idx
1074 //
1075 no_body: no_body
1076 mod: p.mod
1077 file: p.file_path
1078 pos: start_pos
1079 name_pos: name_pos
1080 language: language
1081 //
1082 is_expand_simple_interpolation: is_expand_simple_interpolation
1083 })
1084 }
1085 /*
1086 // Register implicit context var
1087 p.scope.register(ast.Var{
1088 name: 'ctx'
1089 typ: ast.error_type
1090 pos: p.tok.pos()
1091 is_used: true
1092 is_stack_obj: true
1093 })
1094 */
1095 // Body
1096 keep_fn_name := p.cur_fn_name
1097 p.cur_fn_name = name
1098 mut stmts := []ast.Stmt{}
1099 body_start_pos := p.tok.pos()
1100 if p.tok.kind == .lcbr {
1101 if language != .v && !(language == .js && type_sym.info is ast.Interface) {
1102 p.error_with_pos('interop functions cannot have a body', body_start_pos)
1103 }
1104 last_fn_scope := p.scope
1105 p.inside_fn = true
1106 p.inside_unsafe_fn = is_unsafe
1107 p.cur_fn_scope = p.scope
1108 if p.is_vls_skip_file {
1109 p.skip_scope()
1110 } else {
1111 stmts = p.parse_block_no_scope(true)
1112 }
1113 p.cur_fn_scope = last_fn_scope
1114 p.inside_unsafe_fn = false
1115 p.inside_fn = false
1116 }
1117 p.cur_fn_name = keep_fn_name
1118 if !no_body && are_params_type_only {
1119 p.error_with_pos('functions with type only params can not have bodies', body_start_pos)
1120 return ast.FnDecl{
1121 scope: unsafe { nil }
1122 }
1123 }
1124 // if no_body && !name.starts_with('C.') {
1125 // p.error_with_pos('did you mean C.${name} instead of ${name}', start_pos)
1126 // }
1127 fn_decl := ast.FnDecl{
1128 name: name
1129 short_name: short_fn_name
1130 mod: p.mod
1131 kind: p.call_kind(name)
1132 stmts: stmts
1133 return_type: return_type
1134 return_type_pos: return_type_pos
1135 params: params
1136 is_noreturn: is_noreturn
1137 is_manualfree: is_manualfree
1138 is_deprecated: is_deprecated
1139 is_exported: is_exported
1140 is_direct_arr: is_direct_arr
1141 is_pub: is_pub
1142 is_variadic: is_variadic
1143 is_c_variadic: is_c_variadic
1144 is_c_extern: is_c_extern
1145 is_main: is_main
1146 is_test: is_test
1147 is_keep_alive: is_keep_alive
1148 is_unsafe: is_unsafe
1149 is_must_use: is_must_use
1150 is_markused: is_markused
1151 is_ignore_overflow: is_ignore_overflow
1152 is_weak: is_weak
1153 is_file_translated: p.is_translated
1154 //
1155 attrs: fn_attrs
1156 is_conditional: conditional_ctdefine_idx != ast.invalid_type_idx
1157 ctdefine_idx: conditional_ctdefine_idx
1158 //
1159 receiver: ast.StructField{
1160 name: rec.name
1161 typ: rec.typ
1162 type_pos: rec.type_pos
1163 pos: rec.pos
1164 }
1165 generic_names: generic_names
1166 receiver_pos: rec.pos
1167 is_method: is_method
1168 is_static_type_method: is_static_type_method
1169 static_type_pos: static_type_pos
1170 method_type_pos: rec.type_pos
1171 method_idx: type_sym_method_idx
1172 rec_mut: rec.is_mut
1173 language: language
1174 no_body: no_body
1175 pos: start_pos.extend_with_last_line(end_pos, p.prev_tok.line_nr)
1176 end_pos: p.tok.pos()
1177 name_pos: name_pos
1178 body_pos: body_start_pos
1179 file: p.file_path
1180 is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts
1181 scope: p.scope
1182 label_names: p.label_names
1183 end_comments: p.eat_comments(same_line: true)
1184 comments: comments
1185 //
1186 is_expand_simple_interpolation: is_expand_simple_interpolation
1187 }
1188 if generic_names.len > 0 {
1189 p.table.register_fn_generic_types(fn_decl.fkey())
1190 }
1191 p.label_names = []
1192 if p.pref.is_vls {
1193 type_str := if (is_method || is_static_type_method) && rec.typ != ast.no_type {
1194 p.table.sym(rec.typ.idx_type()).name.all_after_last('.')
1195 } else {
1196 ''
1197 }
1198 key := 'fn_${p.mod}[${type_str}]${short_fn_name}'
1199 val := ast.VlsInfo{
1200 pos: fn_decl.pos
1201 doc: p.keyword_comments_to_string(short_fn_name, comments_before_key_fn) +
1202 p.comments_to_string(fn_decl.end_comments)
1203 }
1204 p.table.register_vls_info(key, val)
1205 }
1206 return fn_decl
1207}
1208
1209fn (mut p Parser) fn_receiver(mut params []ast.Param, mut rec ReceiverParsingInfo) ! {
1210 p.inside_receiver_param = true
1211 defer {
1212 p.inside_receiver_param = false
1213 }
1214 lpar_pos := p.tok.pos()
1215 p.next() // (
1216 is_shared := p.tok.kind == .key_shared
1217 is_atomic := p.tok.kind == .key_atomic
1218 rec.is_mut = p.tok.kind == .key_mut || is_shared || is_atomic
1219 if rec.is_mut {
1220 p.next() // `mut`
1221 }
1222 if is_shared {
1223 p.register_auto_import('sync')
1224 }
1225 rec_start_pos := p.tok.pos()
1226 rec.name = p.check_name()
1227 if !rec.is_mut {
1228 rec.is_mut = p.tok.kind == .key_mut
1229 if rec.is_mut {
1230 ptoken2 := p.peek_token(2) // needed to prevent codegen bug, where .pos() expects &Token
1231 p.warn_with_pos('use `(mut f Foo)` instead of `(f mut Foo)`',
1232 lpar_pos.extend(ptoken2.pos()))
1233 }
1234 }
1235 if p.tok.kind == .key_shared {
1236 ptoken2 := p.peek_token(2) // needed to prevent codegen bug, where .pos() expects &Token
1237 p.error_with_pos('use `(shared f Foo)` instead of `(f shared Foo)`',
1238 lpar_pos.extend(ptoken2.pos()))
1239 }
1240 rec.pos = rec_start_pos.extend(p.tok.pos())
1241 is_amp := p.tok.kind == .amp
1242 if p.tok.kind == .name && p.tok.lit == 'JS' {
1243 rec.language = ast.Language.js
1244 }
1245 // if rec.is_mut {
1246 // p.check(.key_mut)
1247 // }
1248 // TODO: talk to alex, should mut be parsed with the type like this?
1249 // or should it be a property of the arg, like this ptr/mut becomes indistinguishable
1250 rec.type_pos = p.tok.pos()
1251 rec.typ = p.parse_type_with_mut(rec.is_mut)
1252 if rec.typ.idx() == 0 {
1253 // error is set in parse_type
1254 return error('void receiver type')
1255 }
1256 rec.type_pos = rec.type_pos.extend(p.prev_tok.pos())
1257 if is_amp && rec.is_mut {
1258 p.error_with_pos('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`',
1259 lpar_pos.extend(p.tok.pos()))
1260 return error('invalid `mut f &Foo`')
1261 }
1262 if is_shared {
1263 rec.typ = rec.typ.set_flag(.shared_f)
1264 }
1265 if is_atomic {
1266 rec.typ = rec.typ.set_flag(.atomic_f)
1267 }
1268
1269 if rec.language != .v {
1270 p.check_for_impure_v(rec.language, rec.type_pos)
1271 }
1272
1273 p.check(.rpar)
1274
1275 params << ast.Param{
1276 pos: rec_start_pos
1277 name: rec.name
1278 is_mut: rec.is_mut
1279 is_atomic: is_atomic
1280 is_shared: is_shared
1281 typ: rec.typ
1282 type_pos: rec.type_pos
1283 }
1284}
1285
1286fn (mut p Parser) anon_fn() ast.AnonFn {
1287 pos := p.tok.pos()
1288 p.check(.key_fn)
1289 if p.tok.kind == .name {
1290 if p.disallow_declarations_in_script_mode() {
1291 return ast.AnonFn{}
1292 }
1293 }
1294 old_inside_defer := p.inside_defer
1295 p.inside_defer = false
1296 p.open_scope()
1297 defer {
1298 p.close_scope()
1299 }
1300 p.scope.detached_from_parent = true
1301 inherited_vars := if p.tok.kind == .lsbr && !(p.peek_tok.kind == .name
1302 && p.peek_tok.lit.len == 1 && p.peek_tok.lit[0].is_capital()) {
1303 p.closure_vars()
1304 } else {
1305 []ast.Param{}
1306 }
1307 inherited_vars_name := inherited_vars.map(it.name)
1308 _, generic_names := p.parse_generic_types()
1309 params, _, is_variadic, _ := p.fn_params()
1310 for param in params {
1311 if param.name == '' && p.table.sym(param.typ).kind != .placeholder {
1312 p.error_with_pos('use `_` to name an unused parameter', param.pos)
1313 }
1314 if param.name in inherited_vars_name {
1315 p.error_with_pos('the parameter name `${param.name}` conflicts with the captured value name',
1316 param.pos)
1317 } else if p.scope.known_var(param.name) {
1318 p.error_with_pos('redefinition of parameter `${param.name}`', param.pos)
1319 }
1320 is_stack_obj := !param.typ.has_flag(.shared_f) && (param.is_mut || param.typ.is_ptr())
1321 p.scope.register(ast.Var{
1322 name: param.name
1323 typ: param.typ
1324 generic_typ: if param.typ.has_flag(.generic) { param.typ } else { ast.Type(0) }
1325 is_mut: p.scope_var_is_mut(param.is_mut)
1326 is_auto_deref: param.is_mut
1327 pos: param.pos
1328 is_used: true
1329 is_arg: true
1330 is_stack_obj: is_stack_obj
1331 })
1332 }
1333 mut same_line := p.tok.line_nr == p.prev_tok.line_nr
1334 mut return_type := ast.void_type
1335 mut return_type_pos := p.tok.pos()
1336 // lpar: multiple return types
1337 if same_line {
1338 if (p.tok.kind.is_start_of_type() && (same_line || p.tok.kind != .lsbr))
1339 || (same_line && p.tok.kind == .key_fn) {
1340 p.inside_fn_return = true
1341 return_type = p.parse_type()
1342 p.inside_fn_return = false
1343 return_type_pos = return_type_pos.extend(p.tok.pos())
1344 } else if p.tok.kind != .lcbr {
1345 p.error_with_pos('expected return type, not ${p.tok} for anonymous function',
1346 p.tok.pos())
1347 }
1348 }
1349 mut stmts := []ast.Stmt{}
1350 no_body := p.tok.kind != .lcbr
1351 same_line = p.tok.line_nr == p.prev_tok.line_nr
1352 if no_body && same_line {
1353 p.unexpected(got: '${p.tok} after anonymous function signature', expecting: '`{`')
1354 }
1355 mut label_names := []string{}
1356 mut func := ast.Fn{
1357 params: params
1358 is_variadic: is_variadic
1359 return_type: return_type
1360 is_method: false
1361 }
1362 name := p.table.get_anon_fn_name(p.unique_prefix, func, p.tok.pos())
1363 keep_fn_name := p.cur_fn_name
1364 p.cur_fn_name = name
1365 if p.tok.kind == .lcbr {
1366 tmp := p.label_names
1367 p.label_names = []
1368 old_assign_rhs := p.inside_assign_rhs
1369 p.inside_assign_rhs = false
1370 stmts = p.parse_block_no_scope(false)
1371 p.inside_assign_rhs = old_assign_rhs
1372 label_names = p.label_names.clone()
1373 p.label_names = tmp
1374 }
1375 p.cur_fn_name = keep_fn_name
1376 func.name = name
1377 idx := p.table.find_or_register_fn_type(func, true, false)
1378 typ := if generic_names.len > 0 {
1379 ast.new_type(idx).set_flag(.generic)
1380 } else {
1381 ast.new_type(idx)
1382 }
1383 p.inside_defer = old_inside_defer
1384 // name := p.table.get_type_name(typ)
1385 return ast.AnonFn{
1386 decl: ast.FnDecl{
1387 name: name
1388 short_name: ''
1389 mod: p.mod
1390 stmts: stmts
1391 return_type: return_type
1392 return_type_pos: return_type_pos
1393 params: params
1394 is_variadic: is_variadic
1395 is_closure: inherited_vars.len > 0
1396 is_method: false
1397 generic_names: generic_names
1398 is_anon: true
1399 no_body: no_body
1400 pos: pos.extend(p.prev_tok.pos())
1401 file: p.file_path
1402 scope: p.scope
1403 label_names: label_names
1404 }
1405 inherited_vars: inherited_vars
1406 typ: typ
1407 }
1408}
1409
1410// part of fn declaration
1411// returns: params, are_params_type_only, mut is_variadic, mut is_c_variadic
1412fn (mut p Parser) fn_params() ([]ast.Param, bool, bool, bool) {
1413 p.check(.lpar)
1414 mut params := []ast.Param{}
1415 mut is_variadic := false
1416 mut is_c_variadic := false
1417 // `int, int, string` (no names, just types)
1418 param_name := if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() {
1419 p.prepend_mod(p.tok.lit)
1420 } else {
1421 p.tok.lit
1422 }
1423 is_generic_type := p.tok.kind == .name && p.tok.lit.len == 1 && p.tok.lit[0].is_capital()
1424
1425 types_only := p.tok.kind in [.question, .not, .amp, .ellipsis, .key_fn, .lsbr]
1426 || (p.peek_tok.kind == .comma && (p.table.known_type(param_name) || is_generic_type))
1427 || p.peek_tok.kind == .dot || p.peek_tok.kind == .rpar || p.fn_language == .c
1428 || (p.tok.kind == .key_mut && (p.peek_tok.kind in [.amp, .ellipsis, .key_fn, .lsbr]
1429 || (p.peek_token(2).kind == .comma && (p.tok.kind != .key_mut
1430 || p.peek_tok.lit[0].is_capital())) || p.peek_token(2).kind == .rpar
1431 || (p.peek_tok.kind == .name && p.peek_token(2).kind == .dot)))
1432 mut prev_param_newline := p.tok.pos().line_nr
1433 // TODO: copy paste, merge 2 branches
1434 if types_only {
1435 mut param_no := 1
1436 for p.tok.kind != .rpar {
1437 if p.tok.kind == .eof {
1438 p.error_with_pos('expecting `)`', p.tok.pos())
1439 return []ast.Param{}, false, false, false
1440 }
1441 is_shared := p.tok.kind == .key_shared
1442 is_atomic := p.tok.kind == .key_atomic
1443 is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
1444 mut name := ''
1445 if is_mut {
1446 p.next()
1447 }
1448 if p.fn_language == .c && (is_ident_name(p.tok.lit) || (p.tok.lit.len > 1
1449 && p.tok.lit[0] == `@` && is_ident_name(p.tok.lit[1..])))
1450 && p.tok.kind !in [.key_fn, .key_struct]
1451 && p.peek_tok.kind !in [.comma, .rpar, .dot] {
1452 name = p.tok.lit
1453 p.next()
1454 }
1455 if p.tok.kind == .ellipsis {
1456 p.next()
1457 is_variadic = true
1458 is_c_variadic = p.tok.kind == .rpar
1459 if is_c_variadic {
1460 p.check(.rpar)
1461 return params, types_only, is_variadic, is_c_variadic
1462 }
1463 }
1464 pos := p.tok.pos()
1465 prev_inside_fn_param := p.inside_fn_param
1466 p.inside_fn_param = true
1467 mut param_type := p.parse_type()
1468 p.inside_fn_param = prev_inside_fn_param
1469 orig_param_type := param_type
1470 type_pos := pos.extend(p.prev_tok.pos())
1471 if param_type == 0 {
1472 // error is added in parse_type
1473 return []ast.Param{}, false, false, false
1474 }
1475 if param_type == ast.chan_type {
1476 p.chan_type_error()
1477 return []ast.Param{}, false, false, false
1478 }
1479 if is_mut {
1480 if !param_type.has_flag(.generic) {
1481 if is_variadic {
1482 p.error_with_pos('variadic arguments cannot be `mut`, `shared` or `atomic`',
1483 pos)
1484 }
1485 if is_shared {
1486 p.check_fn_shared_arguments(param_type, pos)
1487 } else if is_atomic {
1488 p.check_fn_atomic_arguments(param_type, pos)
1489 } else {
1490 p.check_fn_mutable_arguments(param_type, pos)
1491 }
1492 } else if is_shared || is_atomic {
1493 p.error_with_pos('generic object cannot be `atomic`or `shared`', pos)
1494 return []ast.Param{}, false, false, false
1495 }
1496 if param_type.is_ptr() && p.table.sym(param_type).kind == .struct {
1497 param_type = param_type.ref()
1498 } else {
1499 param_type = param_type.set_nr_muls(1)
1500 }
1501 if param_type.has_flag(.option) {
1502 param_type = param_type.set_flag(.option_mut_param_t)
1503 }
1504 if is_shared {
1505 param_type = param_type.set_flag(.shared_f)
1506 }
1507 if is_atomic {
1508 param_type = param_type.set_flag(.atomic_f)
1509 }
1510 }
1511 if is_variadic {
1512 param_type =
1513 ast.new_type(p.table.find_or_register_array(param_type)).set_flag(.variadic)
1514 }
1515 if p.tok.kind == .eof {
1516 p.error_with_pos('expecting `)`', p.prev_tok.pos())
1517 return []ast.Param{}, false, false, false
1518 }
1519
1520 if p.tok.kind == .comma {
1521 if is_variadic {
1522 p.error_with_pos('cannot use ...(variadic) with non-final parameter no ${param_no}',
1523 pos)
1524 return []ast.Param{}, false, false, false
1525 }
1526 p.next()
1527 }
1528 alanguage := p.table.sym(param_type).language
1529 if alanguage != .v {
1530 p.check_for_impure_v(alanguage, pos)
1531 }
1532 params << ast.Param{
1533 pos: pos
1534 name: name
1535 is_mut: is_mut
1536 orig_typ: orig_param_type
1537 typ: param_type
1538 type_pos: type_pos
1539 on_newline: prev_param_newline != pos.line_nr
1540 }
1541 prev_param_newline = pos.line_nr
1542 param_no++
1543 if param_no > 1024 {
1544 p.error_with_pos('too many parameters', pos)
1545 return []ast.Param{}, false, false, false
1546 }
1547 }
1548 } else {
1549 for p.tok.kind != .rpar {
1550 if p.tok.kind == .eof {
1551 p.error_with_pos('expecting `)`', p.tok.pos())
1552 return []ast.Param{}, false, false, false
1553 }
1554 is_shared := p.tok.kind == .key_shared
1555 is_atomic := p.tok.kind == .key_atomic
1556 mut is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
1557 if is_mut {
1558 p.next()
1559 }
1560 if p.tok.kind == .ellipsis && p.peek_tok.kind == .rpar {
1561 p.check(.ellipsis)
1562 p.check(.rpar)
1563 return params, types_only, true, true
1564 }
1565
1566 mut param_pos := [p.tok.pos()]
1567 name := p.check_name()
1568 mut param_names := [name]
1569 if name != '' && p.fn_language == .v && name[0].is_capital() {
1570 p.error_with_pos('parameter name must not begin with upper case letter (`${param_names[0]}`)',
1571 p.prev_tok.pos())
1572 }
1573 mut type_pos := [p.tok.pos()]
1574 // `a, b, c int`
1575 for p.tok.kind == .comma {
1576 if !p.pref.is_fmt {
1577 p.error('`fn f(x, y Type)` syntax has been deprecated. ' +
1578 'Use `fn f(x Type, y Type)` instead. You can run `v fmt -w "${p.scanner.file_path}"` to automatically fix your code.')
1579 }
1580 p.next()
1581 param_pos << p.tok.pos()
1582 param_names << p.check_name()
1583 type_pos << p.tok.pos()
1584 }
1585 if p.tok.kind == .key_mut {
1586 // TODO: remove old syntax
1587 if !p.pref.is_fmt {
1588 p.warn_with_pos('use `mut f Foo` instead of `f mut Foo`', p.tok.pos())
1589 }
1590 is_mut = true
1591 }
1592 if p.tok.kind == .key_shared {
1593 p.error_with_pos('use `shared f Foo` instead of `f shared Foo`', p.tok.pos())
1594 }
1595 if p.tok.kind == .ellipsis {
1596 p.next()
1597 is_variadic = true
1598 is_c_variadic = p.tok.kind == .rpar
1599 if is_c_variadic {
1600 p.check(.rpar)
1601 return params, types_only, is_variadic, is_c_variadic
1602 }
1603 }
1604 pos := p.tok.pos()
1605 prev_inside_fn_param := p.inside_fn_param
1606 p.inside_fn_param = true
1607 mut typ := p.parse_type()
1608 p.inside_fn_param = prev_inside_fn_param
1609 orig_typ := typ
1610 type_pos[0] = pos.extend(p.prev_tok.pos())
1611 if typ == 0 {
1612 // error is added in parse_type
1613 return []ast.Param{}, false, false, false
1614 }
1615 if typ == ast.chan_type {
1616 p.chan_type_error()
1617 return []ast.Param{}, false, false, false
1618 }
1619 if is_mut {
1620 if !typ.has_flag(.generic) {
1621 if is_variadic {
1622 p.error_with_pos('variadic arguments cannot be `mut`, `shared` or `atomic`',
1623 pos)
1624 }
1625 if is_shared {
1626 p.check_fn_shared_arguments(typ, pos)
1627 } else if is_atomic {
1628 p.check_fn_atomic_arguments(typ, pos)
1629 } else {
1630 p.check_fn_mutable_arguments(typ, pos)
1631 }
1632 } else if is_shared || is_atomic {
1633 p.error_with_pos('generic object cannot be `atomic` or `shared`', pos)
1634 return []ast.Param{}, false, false, false
1635 }
1636 if typ.is_ptr() && p.table.sym(typ).kind == .struct {
1637 typ = typ.ref()
1638 } else {
1639 typ = typ.set_nr_muls(1)
1640 }
1641 if typ.has_flag(.option) {
1642 typ = typ.set_flag(.option_mut_param_t)
1643 }
1644 if is_shared {
1645 typ = typ.set_flag(.shared_f)
1646 }
1647 if is_atomic {
1648 typ = typ.set_flag(.atomic_f)
1649 }
1650 }
1651 if is_variadic {
1652 // derive flags, however nr_muls only needs to be set on the array elem type, so clear it on the arg type
1653 typ =
1654 ast.new_type(p.table.find_or_register_array(typ)).derive(typ).set_nr_muls(0).set_flag(.variadic)
1655 }
1656 for i, para_name in param_names {
1657 alanguage := p.table.sym(typ).language
1658 if alanguage != .v {
1659 p.check_for_impure_v(alanguage, type_pos[i])
1660 }
1661 can_omit_comma := p.can_omit_comma_between_fn_params()
1662 params << ast.Param{
1663 pos: param_pos[i]
1664 name: para_name
1665 is_mut: is_mut
1666 is_atomic: is_atomic
1667 is_shared: is_shared
1668 orig_typ: orig_typ
1669 typ: typ
1670 type_pos: type_pos[i]
1671 on_newline: prev_param_newline != param_pos[i].line_nr
1672 }
1673 prev_param_newline = param_pos[i].line_nr
1674 if is_variadic && ((p.tok.kind == .comma && p.peek_tok.kind != .rpar)
1675 || can_omit_comma) {
1676 p.error_with_pos('cannot use ...(variadic) with non-final parameter ${para_name}',
1677 param_pos[i])
1678 return []ast.Param{}, false, false, false
1679 }
1680 }
1681 if p.tok.kind == .eof {
1682 p.error_with_pos('expecting `)`', p.prev_tok.pos())
1683 return []ast.Param{}, false, false, false
1684 }
1685 if p.tok.kind == .comma {
1686 p.next()
1687 } else if p.tok.kind != .rpar && !p.can_omit_comma_between_fn_params() {
1688 p.check(.comma)
1689 }
1690 }
1691 }
1692 p.check(.rpar)
1693 return params, types_only, is_variadic, is_c_variadic
1694}
1695
1696fn (mut p Parser) spawn_expr() ast.SpawnExpr {
1697 p.next()
1698 spos := p.tok.pos()
1699 old_inside_assign_rhs := p.inside_assign_rhs
1700 p.inside_assign_rhs = false
1701 expr := p.expr(0)
1702 p.inside_assign_rhs = old_inside_assign_rhs
1703 mut call_expr := if expr is ast.CallExpr {
1704 expr
1705 } else {
1706 p.error_with_pos('expression in `spawn` must be a function call', expr.pos())
1707 ast.CallExpr{
1708 scope: p.scope
1709 }
1710 }
1711 call_expr.is_return_used = true
1712 pos := spos.extend(p.prev_tok.pos())
1713 p.register_auto_import('sync.threads')
1714 p.table.gostmts++
1715 return ast.SpawnExpr{
1716 call_expr: call_expr
1717 pos: pos
1718 }
1719}
1720
1721fn (mut p Parser) go_expr() ast.GoExpr {
1722 p.next()
1723 spos := p.tok.pos()
1724 old_inside_assign_rhs := p.inside_assign_rhs
1725 p.inside_assign_rhs = false
1726 expr := p.expr(0)
1727 p.inside_assign_rhs = old_inside_assign_rhs
1728 mut call_expr := if expr is ast.CallExpr {
1729 expr
1730 } else {
1731 p.error_with_pos('expression in `go` must be a function call', expr.pos())
1732 ast.CallExpr{
1733 scope: p.scope
1734 }
1735 }
1736 call_expr.is_return_used = true
1737 pos := spos.extend(p.prev_tok.pos())
1738 // p.register_auto_import('coroutines')
1739 p.table.gostmts++
1740 return ast.GoExpr{
1741 call_expr: call_expr
1742 pos: pos
1743 }
1744}
1745
1746fn (mut p Parser) closure_vars() []ast.Param {
1747 p.check(.lsbr)
1748 mut vars := []ast.Param{cap: 5}
1749 for {
1750 is_shared := p.tok.kind == .key_shared
1751 is_atomic := p.tok.kind == .key_atomic
1752 is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
1753 // FIXME: is_shared & is_atomic aren't used further
1754 if is_mut {
1755 p.next()
1756 }
1757 var_pos := p.tok.pos()
1758 p.check(.name)
1759 var_name := p.prev_tok.lit
1760 mut var := p.scope.parent.find_var(var_name) or {
1761 if p.table.global_scope.known_global(var_name) {
1762 p.error_with_pos('no need to capture global variable `${var_name}` in closure',
1763 p.prev_tok.pos())
1764 return []
1765 }
1766 p.error_with_pos('undefined ident: `${var_name}`', p.prev_tok.pos())
1767 return []
1768 }
1769 var.is_used = true
1770 if is_mut {
1771 var.is_changed = true
1772 }
1773 p.scope.register(ast.Var{
1774 ...(*var)
1775 pos: var_pos
1776 is_inherited: true
1777 has_inherited: var.is_inherited
1778 is_used: false
1779 is_changed: false
1780 is_mut: p.scope_var_is_mut(is_mut)
1781 })
1782 vars << ast.Param{
1783 pos: var_pos
1784 name: var_name
1785 is_mut: p.scope_var_is_mut(is_mut)
1786 is_atomic: is_atomic
1787 is_shared: is_shared
1788 }
1789 if p.tok.kind != .comma {
1790 break
1791 }
1792 p.next()
1793 }
1794 p.check(.rsbr)
1795 return vars
1796}
1797
1798fn (mut p Parser) check_fn_mutable_arguments(typ ast.Type, pos token.Pos) {
1799 sym := p.table.sym(typ)
1800 if sym.kind in [.array, .array_fixed, .interface, .map, .placeholder, .struct, .generic_inst,
1801 .sum_type] {
1802 return
1803 }
1804 if typ.is_any_kind_of_pointer() {
1805 return
1806 }
1807 if sym.kind == .alias {
1808 atyp := (sym.info as ast.Alias).parent_type
1809 p.check_fn_mutable_arguments(atyp, pos)
1810 return
1811 }
1812 if p.fn_language == .c {
1813 return
1814 }
1815 p.error_with_pos(
1816 'mutable arguments are only allowed for arrays, interfaces, maps, pointers, structs or their aliases\n' +
1817 'return values instead: `fn foo(mut n ${sym.name}) {` => `fn foo(n ${sym.name}) ${sym.name} {`',
1818 pos)
1819}
1820
1821fn (mut p Parser) check_fn_shared_arguments(typ ast.Type, pos token.Pos) {
1822 mut sym := p.table.sym(typ)
1823 if sym.kind == .generic_inst {
1824 sym = p.table.type_symbols[(sym.info as ast.GenericInst).parent_idx]
1825 }
1826 if sym.kind == .function {
1827 p.error_with_pos('shared arguments are not allowed for function types', pos)
1828 }
1829}
1830
1831fn (mut p Parser) check_fn_atomic_arguments(typ ast.Type, pos token.Pos) {
1832 sym := p.table.sym(typ)
1833 if sym.kind !in [.u32, .int, .u64] {
1834 p.error_with_pos('atomic arguments are only allowed for 32/64 bit integers\n' +
1835 'use shared arguments instead: `fn foo(atomic n ${sym.name}) {` => `fn foo(shared n ${sym.name}) {`',
1836 pos)
1837 }
1838}
1839
1840fn have_fn_main(stmts []ast.Stmt) bool {
1841 for stmt in stmts {
1842 if stmt is ast.FnDecl {
1843 if stmt.name == 'main.main' {
1844 return true
1845 }
1846 }
1847 }
1848 return false
1849}
1850