Skip to content

Commit 68401d9

Browse files
author
fleur
authored
gen: add callconv attribute for fn and type (#14027)
1 parent 5905590 commit 68401d9

File tree

9 files changed

+153
-42
lines changed

9 files changed

+153
-42
lines changed

doc/docs.md

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ NB: You can also pass one of `-gcc`, `-msvc`, `-clang` to `make.bat` instead,
4040
if you do prefer to use a different C compiler, but -tcc is small, fast, and
4141
easy to install (V will download a prebuilt binary automatically).
4242

43-
For C compiler downloads and more info, see
43+
For C compiler downloads and more info, see
4444
[here](https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows).
4545

4646
It is recommended to add this folder to the PATH of your environment variables.
@@ -843,7 +843,7 @@ println(nums.cap) // "3" or greater
843843
nums = [] // The array is now empty
844844
println(nums.len) // "0"
845845
```
846-
`data` is a field (of type `voidptr`) with the address of the first
846+
`data` is a field (of type `voidptr`) with the address of the first
847847
element. This is for low-level [`unsafe`](#memory-unsafe-code) code.
848848

849849
Note that the fields are read-only and can't be modified by the user.
@@ -892,7 +892,7 @@ for i in 0 .. 1000 {
892892
```
893893
Note: The above code uses a [range `for`](#range-for) statement.
894894

895-
You can initialize the array by accessing the `it` variable which gives
895+
You can initialize the array by accessing the `it` variable which gives
896896
the index as shown here:
897897

898898
```v
@@ -1022,7 +1022,7 @@ upper_fn := words.map(fn (w string) string {
10221022
println(upper_fn) // ['HELLO', 'WORLD']
10231023
```
10241024

1025-
`it` is a builtin variable which refers to the element currently being
1025+
`it` is a builtin variable which refers to the element currently being
10261026
processed in filter/map methods.
10271027

10281028
Additionally, `.any()` and `.all()` can be used to conveniently test
@@ -1035,8 +1035,8 @@ println(nums.all(it >= 2)) // false
10351035
```
10361036

10371037
There are further built-in methods for arrays:
1038-
* `a.repeat(n)` concatenates the array elements `n` times
1039-
* `a.insert(i, val)` inserts a new element `val` at index `i` and
1038+
* `a.repeat(n)` concatenates the array elements `n` times
1039+
* `a.insert(i, val)` inserts a new element `val` at index `i` and
10401040
shifts all following elements to the right
10411041
* `a.insert(i, [3, 4, 5])` inserts several elements
10421042
* `a.prepend(val)` inserts a value at the beginning, equivalent to `a.insert(0, val)`
@@ -1052,7 +1052,7 @@ There are further built-in methods for arrays:
10521052
* `a.pop()` removes the last element and returns it
10531053
* `a.reverse()` makes a new array with the elements of `a` in reverse order
10541054
* `a.reverse_in_place()` reverses the order of elements in `a`
1055-
* `a.join(joiner)` concatenates an array of strings into one string
1055+
* `a.join(joiner)` concatenates an array of strings into one string
10561056
using `joiner` string as a separator
10571057

10581058
See also [vlib/arrays](https://modules.vlang.io/arrays.html).
@@ -1185,7 +1185,7 @@ println(b) // [7, 3]
11851185

11861186
V supports array and string slices with negative indexes.
11871187
Negative indexing starts from the end of the array towards the start,
1188-
for example `-3` is equal to `array.len - 3`.
1188+
for example `-3` is equal to `array.len - 3`.
11891189
Negative slices have a different syntax from normal slices, i.e. you need
11901190
to add a `gate` between the array name and the square bracket: `a#[..-3]`.
11911191
The `gate` specifies that this is a different type of slice and remember that
@@ -2223,7 +2223,7 @@ button.Size = Size{
22232223
If multiple embedded structs have methods or fields with the same name, or if methods or fields
22242224
with the same name are defined in the struct, you can call methods or assign to variables in
22252225
the embedded struct like `button.Size.area()`.
2226-
When you do not specify the embedded struct name, the method of the outermost struct will be
2226+
When you do not specify the embedded struct name, the method of the outermost struct will be
22272227
targeted.
22282228

22292229
## Unions
@@ -2951,7 +2951,7 @@ a convenience for writing `s.xyz()` instead of `xyz(s)`.
29512951
N.B. This feature is NOT a "default implementation" like in C#.
29522952

29532953
For example, if a struct `cat` is wrapped in an interface `a`, that has
2954-
implemented a method with the same name `speak`, as a method implemented by
2954+
implemented a method with the same name `speak`, as a method implemented by
29552955
the struct, and you do `a.speak()`, *only* the interface method is called:
29562956

29572957
```v
@@ -3491,13 +3491,13 @@ Above, `http.get` returns a `?http.Response`. `resp` is only in scope for the fi
34913491

34923492
## Custom error types
34933493

3494-
V gives you the ability to define custom error types through the `IError` interface.
3495-
The interface requires two methods: `msg() string` and `code() int`. Every type that
3496-
implements these methods can be used as an error.
3494+
V gives you the ability to define custom error types through the `IError` interface.
3495+
The interface requires two methods: `msg() string` and `code() int`. Every type that
3496+
implements these methods can be used as an error.
34973497

3498-
When defining a custom error type it is recommended to embed the builtin `Error` default
3499-
implementation. This provides an empty default implementation for both required methods,
3500-
so you only have to implement what you really need, and may provide additional utility
3498+
When defining a custom error type it is recommended to embed the builtin `Error` default
3499+
implementation. This provides an empty default implementation for both required methods,
3500+
so you only have to implement what you really need, and may provide additional utility
35013501
functions in the future.
35023502

35033503
```v
@@ -4112,8 +4112,8 @@ memory manually. (See [attributes](#attributes)).
41124112
_Note: right now autofree is hidden behind the -autofree flag. It will be enabled by
41134113
default in V 0.3. If autofree is not used, V programs will leak memory._
41144114

4115-
Note 2: Autofree is still WIP. Until it stabilises and becomes the default, please
4116-
compile your long running processes with `-gc boehm`, which will use the
4115+
Note 2: Autofree is still WIP. Until it stabilises and becomes the default, please
4116+
compile your long running processes with `-gc boehm`, which will use the
41174117
Boehm-Demers-Weiser conservative garbage collector, to free the memory, that your
41184118
programs leak, at runtime.
41194119

@@ -4702,7 +4702,7 @@ Modules are up to date.
47024702
at the top of all files in your module. For `mymodule.v`:
47034703
```v
47044704
module mymodule
4705-
4705+
47064706
pub fn hello_world() {
47074707
println('Hello World!')
47084708
}
@@ -5745,11 +5745,11 @@ fn main() {
57455745
```
57465746

57475747
Build this example with `v -live message.v`.
5748-
5749-
You can also run this example with `v -live run message.v`.
5750-
Make sure that in command you use a path to a V's file,
5751-
**not** a path to a folder (like `v -live run .`) -
5752-
in that case you need to modify content of a folder (add new file, for example),
5748+
5749+
You can also run this example with `v -live run message.v`.
5750+
Make sure that in command you use a path to a V's file,
5751+
**not** a path to a folder (like `v -live run .`) -
5752+
in that case you need to modify content of a folder (add new file, for example),
57535753
because changes in *message.v* will have no effect.
57545754

57555755
Functions that you want to be reloaded must have `[live]` attribute
@@ -5978,10 +5978,15 @@ fn custom_allocations() {
59785978
struct C.Foo {
59795979
}
59805980
5981-
// Used in Win32 API code when you need to pass callback function
5982-
[windows_stdcall]
5981+
// Used to add a custom calling convention to a function, available calling convention: stdcall, fastcall and cdecl.
5982+
// This list aslo apply for type aliases (see below).
5983+
[callconv: "stdcall"]
59835984
fn C.DefWindowProc(hwnd int, msg int, lparam int, wparam int)
59845985
5986+
// Used to add a custom calling convention to a function type aliases.
5987+
[callconv: "fastcall"]
5988+
type FastFn = fn (int) bool
5989+
59855990
// Windows only:
59865991
// If a default graphics library is imported (ex. gg, ui), then the graphical window takes
59875992
// priority and no console window is created, effectively disabling println() statements.

vlib/v/ast/ast.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,7 @@ pub:
11521152
pos token.Pos
11531153
type_pos token.Pos
11541154
comments []Comment
1155+
attrs []Attr // attributes of type declaration
11551156
}
11561157

11571158
// TODO: handle this differently

vlib/v/fmt/fmt.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,7 @@ pub fn (mut f Fmt) alias_type_decl(node ast.AliasTypeDecl) {
12901290
}
12911291

12921292
pub fn (mut f Fmt) fn_type_decl(node ast.FnTypeDecl) {
1293+
f.attrs(node.attrs)
12931294
if node.is_pub {
12941295
f.write('pub ')
12951296
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[callconv: 'stdcall']
2+
fn C.DefWindowProc(hwnd int, msg int, lparam int, wparam int)
3+
4+
[callconv: 'stdcall']
5+
type FastFn = fn (int) bool
6+
7+
[callconv: 'fastcall']
8+
fn zzz(x int, y int) int {
9+
return x + y * 2
10+
}

vlib/v/gen/c/cgen.v

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ mut:
9696
is_json_fn bool // inside json.encode()
9797
is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)`
9898
is_fn_index_call bool
99+
is_cc_msvc bool // g.pref.ccompiler == 'msvc'
99100
vlines_path string // set to the proper path for generating #line directives
100101
optionals map[string]string // to avoid duplicates
101102
done_optionals shared []string // to avoid duplicates
@@ -258,6 +259,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
258259
inner_loop: &ast.EmptyStmt{}
259260
field_data_type: ast.Type(table.find_type_idx('FieldData'))
260261
init: strings.new_builder(100)
262+
is_cc_msvc: pref.ccompiler == 'msvc'
261263
}
262264
// anon fn may include assert and thus this needs
263265
// to be included before any test contents are written
@@ -557,6 +559,7 @@ fn cgen_process_one_file_cb(p &pool.PoolProcessor, idx int, wid int) &Gen {
557559
done_optionals: global_g.done_optionals
558560
is_autofree: global_g.pref.autofree
559561
referenced_fns: global_g.referenced_fns
562+
is_cc_msvc: global_g.is_cc_msvc
560563
}
561564
g.gen_file()
562565
return g
@@ -1343,14 +1346,35 @@ pub fn (mut g Gen) write_fn_typesymbol_declaration(sym ast.TypeSymbol) {
13431346
if !info.has_decl && (not_anon || is_fn_sig) && !func.return_type.has_flag(.generic)
13441347
&& !has_generic_arg {
13451348
fn_name := sym.cname
1346-
g.type_definitions.write_string('typedef ${g.typ(func.return_type)} (*$fn_name)(')
1349+
1350+
mut call_conv := ''
1351+
mut msvc_call_conv := ''
1352+
for attr in func.attrs {
1353+
match attr.name {
1354+
'callconv' {
1355+
if g.is_cc_msvc {
1356+
msvc_call_conv = '__$attr.arg '
1357+
} else {
1358+
call_conv = '$attr.arg'
1359+
}
1360+
}
1361+
else {}
1362+
}
1363+
}
1364+
call_conv_attribute_suffix := if call_conv.len != 0 {
1365+
'__attribute__(($call_conv))'
1366+
} else {
1367+
''
1368+
}
1369+
1370+
g.type_definitions.write_string('typedef ${g.typ(func.return_type)} ($msvc_call_conv*$fn_name)(')
13471371
for i, param in func.params {
13481372
g.type_definitions.write_string(g.typ(param.typ))
13491373
if i < func.params.len - 1 {
13501374
g.type_definitions.write_string(',')
13511375
}
13521376
}
1353-
g.type_definitions.writeln(');')
1377+
g.type_definitions.writeln(')$call_conv_attribute_suffix;')
13541378
}
13551379
}
13561380

vlib/v/gen/c/fn.v

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,10 +2148,13 @@ fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string {
21482148
'windows_stdcall' {
21492149
// windows attributes (msvc/mingw)
21502150
// prefixed by windows to indicate they're for advanced users only and not really supported by V.
2151-
fn_attrs += '__stdcall '
2151+
fn_attrs += call_convention_attribute('stdcall', g.is_cc_msvc)
21522152
}
21532153
'_fastcall' {
2154-
fn_attrs += '__fastcall '
2154+
fn_attrs += call_convention_attribute('fastcall', g.is_cc_msvc)
2155+
}
2156+
'callconv' {
2157+
fn_attrs += call_convention_attribute(attr.arg, g.is_cc_msvc)
21552158
}
21562159
'console' {
21572160
g.force_main_console = true
@@ -2163,3 +2166,7 @@ fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string {
21632166
}
21642167
return fn_attrs
21652168
}
2169+
2170+
fn call_convention_attribute(cconvention string, is_cc_msvc bool) string {
2171+
return if is_cc_msvc { '__$cconvention ' } else { '__attribute__(($cconvention)) ' }
2172+
}

vlib/v/parser/fn.v

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -185,18 +185,60 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
185185
mut comments := []ast.Comment{}
186186
for fna in p.attrs {
187187
match fna.name {
188-
'noreturn' { is_noreturn = true }
189-
'manualfree' { is_manualfree = true }
190-
'deprecated' { is_deprecated = true }
191-
'direct_array_access' { is_direct_arr = true }
192-
'keep_args_alive' { is_keep_alive = true }
193-
'export' { is_exported = true }
194-
'wasm_export' { is_exported = true }
195-
'unsafe' { is_unsafe = true }
196-
'trusted' { is_trusted = true }
197-
'c2v_variadic' { is_c2v_variadic = true }
198-
'use_new' { is_ctor_new = true }
199-
'markused' { is_markused = true }
188+
'noreturn' {
189+
is_noreturn = true
190+
}
191+
'manualfree' {
192+
is_manualfree = true
193+
}
194+
'deprecated' {
195+
is_deprecated = true
196+
}
197+
'direct_array_access' {
198+
is_direct_arr = true
199+
}
200+
'keep_args_alive' {
201+
is_keep_alive = true
202+
}
203+
'export' {
204+
is_exported = true
205+
}
206+
'wasm_export' {
207+
is_exported = true
208+
}
209+
'unsafe' {
210+
is_unsafe = true
211+
}
212+
'trusted' {
213+
is_trusted = true
214+
}
215+
'c2v_variadic' {
216+
is_c2v_variadic = true
217+
}
218+
'use_new' {
219+
is_ctor_new = true
220+
}
221+
'markused' {
222+
is_markused = true
223+
}
224+
'windows_stdcall' {
225+
// TODO: uncomment after bootstrapping and replacing usages in builtin
226+
// p.note_with_pos('windows_stdcall has been deprecated, it will be an error soon', p.tok.pos())
227+
}
228+
'_fastcall' {
229+
// TODO: uncomment after bootstrapping and replacing usages in builtin
230+
// p.note_with_pos('_fastcall has been deprecated, it will be an error soon', p.tok.pos())
231+
}
232+
'callconv' {
233+
if !fna.has_arg {
234+
p.error_with_pos('callconv attribute is present but its value is missing',
235+
p.prev_tok.pos())
236+
}
237+
if fna.arg !in ['stdcall', 'fastcall', 'cdecl'] {
238+
p.error_with_pos('unsupported calling convention, supported are stdcall, fastcall and cdecl',
239+
p.prev_tok.pos())
240+
}
241+
}
200242
else {}
201243
}
202244
}

vlib/v/parser/parse_type.v

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,23 @@ pub fn (mut p Parser) parse_multi_return_type() ast.Type {
229229
pub fn (mut p Parser) parse_fn_type(name string) ast.Type {
230230
// p.warn('parse fn')
231231
p.check(.key_fn)
232+
233+
for attr in p.attrs {
234+
match attr.name {
235+
'callconv' {
236+
if !attr.has_arg {
237+
p.error_with_pos('callconv attribute is present but its value is missing',
238+
p.prev_tok.pos())
239+
}
240+
if attr.arg !in ['stdcall', 'fastcall', 'cdecl'] {
241+
p.error_with_pos('unsupported calling convention, supported are stdcall, fastcall and cdecl',
242+
p.prev_tok.pos())
243+
}
244+
}
245+
else {}
246+
}
247+
}
248+
232249
mut has_generic := false
233250
line_nr := p.tok.line_nr
234251
args, _, is_variadic := p.fn_args()
@@ -255,6 +272,7 @@ pub fn (mut p Parser) parse_fn_type(name string) ast.Type {
255272
return_type: return_type
256273
return_type_pos: return_type_pos
257274
is_method: false
275+
attrs: p.attrs
258276
}
259277
// MapFooFn typedefs are manually added in cheaders.v
260278
// because typedefs get generated after the map struct is generated

vlib/v/parser/parser.v

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3579,13 +3579,16 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
35793579
p.table.sym(fn_type).is_pub = is_pub
35803580
type_pos = type_pos.extend(p.tok.pos())
35813581
comments = p.eat_comments(same_line: true)
3582+
attrs := p.attrs
3583+
p.attrs = []
35823584
return ast.FnTypeDecl{
35833585
name: fn_name
35843586
is_pub: is_pub
35853587
typ: fn_type
35863588
pos: decl_pos
35873589
type_pos: type_pos
35883590
comments: comments
3591+
attrs: attrs
35893592
}
35903593
}
35913594
sum_variants << p.parse_sum_type_variants()

0 commit comments

Comments
 (0)