gen: add callconv attribute for fn and type (#14027)

pull/13758/merge
fleur 2022-04-14 10:29:52 +02:00 committed by GitHub
parent 5905590e78
commit 68401d9dc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 153 additions and 42 deletions

View File

@ -40,7 +40,7 @@ NB: You can also pass one of `-gcc`, `-msvc`, `-clang` to `make.bat` instead,
if you do prefer to use a different C compiler, but -tcc is small, fast, and
easy to install (V will download a prebuilt binary automatically).
For C compiler downloads and more info, see
For C compiler downloads and more info, see
[here](https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows).
It is recommended to add this folder to the PATH of your environment variables.
@ -843,7 +843,7 @@ println(nums.cap) // "3" or greater
nums = [] // The array is now empty
println(nums.len) // "0"
```
`data` is a field (of type `voidptr`) with the address of the first
`data` is a field (of type `voidptr`) with the address of the first
element. This is for low-level [`unsafe`](#memory-unsafe-code) code.
Note that the fields are read-only and can't be modified by the user.
@ -892,7 +892,7 @@ for i in 0 .. 1000 {
```
Note: The above code uses a [range `for`](#range-for) statement.
You can initialize the array by accessing the `it` variable which gives
You can initialize the array by accessing the `it` variable which gives
the index as shown here:
```v
@ -1022,7 +1022,7 @@ upper_fn := words.map(fn (w string) string {
println(upper_fn) // ['HELLO', 'WORLD']
```
`it` is a builtin variable which refers to the element currently being
`it` is a builtin variable which refers to the element currently being
processed in filter/map methods.
Additionally, `.any()` and `.all()` can be used to conveniently test
@ -1035,8 +1035,8 @@ println(nums.all(it >= 2)) // false
```
There are further built-in methods for arrays:
* `a.repeat(n)` concatenates the array elements `n` times
* `a.insert(i, val)` inserts a new element `val` at index `i` and
* `a.repeat(n)` concatenates the array elements `n` times
* `a.insert(i, val)` inserts a new element `val` at index `i` and
shifts all following elements to the right
* `a.insert(i, [3, 4, 5])` inserts several elements
* `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:
* `a.pop()` removes the last element and returns it
* `a.reverse()` makes a new array with the elements of `a` in reverse order
* `a.reverse_in_place()` reverses the order of elements in `a`
* `a.join(joiner)` concatenates an array of strings into one string
* `a.join(joiner)` concatenates an array of strings into one string
using `joiner` string as a separator
See also [vlib/arrays](https://modules.vlang.io/arrays.html).
@ -1185,7 +1185,7 @@ println(b) // [7, 3]
V supports array and string slices with negative indexes.
Negative indexing starts from the end of the array towards the start,
for example `-3` is equal to `array.len - 3`.
for example `-3` is equal to `array.len - 3`.
Negative slices have a different syntax from normal slices, i.e. you need
to add a `gate` between the array name and the square bracket: `a#[..-3]`.
The `gate` specifies that this is a different type of slice and remember that
@ -2223,7 +2223,7 @@ button.Size = Size{
If multiple embedded structs have methods or fields with the same name, or if methods or fields
with the same name are defined in the struct, you can call methods or assign to variables in
the embedded struct like `button.Size.area()`.
When you do not specify the embedded struct name, the method of the outermost struct will be
When you do not specify the embedded struct name, the method of the outermost struct will be
targeted.
## Unions
@ -2951,7 +2951,7 @@ a convenience for writing `s.xyz()` instead of `xyz(s)`.
N.B. This feature is NOT a "default implementation" like in C#.
For example, if a struct `cat` is wrapped in an interface `a`, that has
implemented a method with the same name `speak`, as a method implemented by
implemented a method with the same name `speak`, as a method implemented by
the struct, and you do `a.speak()`, *only* the interface method is called:
```v
@ -3491,13 +3491,13 @@ Above, `http.get` returns a `?http.Response`. `resp` is only in scope for the fi
## Custom error types
V gives you the ability to define custom error types through the `IError` interface.
The interface requires two methods: `msg() string` and `code() int`. Every type that
implements these methods can be used as an error.
V gives you the ability to define custom error types through the `IError` interface.
The interface requires two methods: `msg() string` and `code() int`. Every type that
implements these methods can be used as an error.
When defining a custom error type it is recommended to embed the builtin `Error` default
implementation. This provides an empty default implementation for both required methods,
so you only have to implement what you really need, and may provide additional utility
When defining a custom error type it is recommended to embed the builtin `Error` default
implementation. This provides an empty default implementation for both required methods,
so you only have to implement what you really need, and may provide additional utility
functions in the future.
```v
@ -4112,8 +4112,8 @@ memory manually. (See [attributes](#attributes)).
_Note: right now autofree is hidden behind the -autofree flag. It will be enabled by
default in V 0.3. If autofree is not used, V programs will leak memory._
Note 2: Autofree is still WIP. Until it stabilises and becomes the default, please
compile your long running processes with `-gc boehm`, which will use the
Note 2: Autofree is still WIP. Until it stabilises and becomes the default, please
compile your long running processes with `-gc boehm`, which will use the
Boehm-Demers-Weiser conservative garbage collector, to free the memory, that your
programs leak, at runtime.
@ -4702,7 +4702,7 @@ Modules are up to date.
at the top of all files in your module. For `mymodule.v`:
```v
module mymodule
pub fn hello_world() {
println('Hello World!')
}
@ -5745,11 +5745,11 @@ fn main() {
```
Build this example with `v -live message.v`.
You can also run this example with `v -live run message.v`.
Make sure that in command you use a path to a V's file,
**not** a path to a folder (like `v -live run .`) -
in that case you need to modify content of a folder (add new file, for example),
You can also run this example with `v -live run message.v`.
Make sure that in command you use a path to a V's file,
**not** a path to a folder (like `v -live run .`) -
in that case you need to modify content of a folder (add new file, for example),
because changes in *message.v* will have no effect.
Functions that you want to be reloaded must have `[live]` attribute
@ -5978,10 +5978,15 @@ fn custom_allocations() {
struct C.Foo {
}
// Used in Win32 API code when you need to pass callback function
[windows_stdcall]
// Used to add a custom calling convention to a function, available calling convention: stdcall, fastcall and cdecl.
// This list aslo apply for type aliases (see below).
[callconv: "stdcall"]
fn C.DefWindowProc(hwnd int, msg int, lparam int, wparam int)
// Used to add a custom calling convention to a function type aliases.
[callconv: "fastcall"]
type FastFn = fn (int) bool
// Windows only:
// If a default graphics library is imported (ex. gg, ui), then the graphical window takes
// priority and no console window is created, effectively disabling println() statements.

View File

@ -1152,6 +1152,7 @@ pub:
pos token.Pos
type_pos token.Pos
comments []Comment
attrs []Attr // attributes of type declaration
}
// TODO: handle this differently

View File

@ -1290,6 +1290,7 @@ pub fn (mut f Fmt) alias_type_decl(node ast.AliasTypeDecl) {
}
pub fn (mut f Fmt) fn_type_decl(node ast.FnTypeDecl) {
f.attrs(node.attrs)
if node.is_pub {
f.write('pub ')
}

View File

@ -0,0 +1,10 @@
[callconv: 'stdcall']
fn C.DefWindowProc(hwnd int, msg int, lparam int, wparam int)
[callconv: 'stdcall']
type FastFn = fn (int) bool
[callconv: 'fastcall']
fn zzz(x int, y int) int {
return x + y * 2
}

View File

@ -96,6 +96,7 @@ mut:
is_json_fn bool // inside json.encode()
is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)`
is_fn_index_call bool
is_cc_msvc bool // g.pref.ccompiler == 'msvc'
vlines_path string // set to the proper path for generating #line directives
optionals map[string]string // to avoid duplicates
done_optionals shared []string // to avoid duplicates
@ -258,6 +259,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
inner_loop: &ast.EmptyStmt{}
field_data_type: ast.Type(table.find_type_idx('FieldData'))
init: strings.new_builder(100)
is_cc_msvc: pref.ccompiler == 'msvc'
}
// anon fn may include assert and thus this needs
// 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 {
done_optionals: global_g.done_optionals
is_autofree: global_g.pref.autofree
referenced_fns: global_g.referenced_fns
is_cc_msvc: global_g.is_cc_msvc
}
g.gen_file()
return g
@ -1343,14 +1346,35 @@ pub fn (mut g Gen) write_fn_typesymbol_declaration(sym ast.TypeSymbol) {
if !info.has_decl && (not_anon || is_fn_sig) && !func.return_type.has_flag(.generic)
&& !has_generic_arg {
fn_name := sym.cname
g.type_definitions.write_string('typedef ${g.typ(func.return_type)} (*$fn_name)(')
mut call_conv := ''
mut msvc_call_conv := ''
for attr in func.attrs {
match attr.name {
'callconv' {
if g.is_cc_msvc {
msvc_call_conv = '__$attr.arg '
} else {
call_conv = '$attr.arg'
}
}
else {}
}
}
call_conv_attribute_suffix := if call_conv.len != 0 {
'__attribute__(($call_conv))'
} else {
''
}
g.type_definitions.write_string('typedef ${g.typ(func.return_type)} ($msvc_call_conv*$fn_name)(')
for i, param in func.params {
g.type_definitions.write_string(g.typ(param.typ))
if i < func.params.len - 1 {
g.type_definitions.write_string(',')
}
}
g.type_definitions.writeln(');')
g.type_definitions.writeln(')$call_conv_attribute_suffix;')
}
}

View File

@ -2148,10 +2148,13 @@ fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string {
'windows_stdcall' {
// windows attributes (msvc/mingw)
// prefixed by windows to indicate they're for advanced users only and not really supported by V.
fn_attrs += '__stdcall '
fn_attrs += call_convention_attribute('stdcall', g.is_cc_msvc)
}
'_fastcall' {
fn_attrs += '__fastcall '
fn_attrs += call_convention_attribute('fastcall', g.is_cc_msvc)
}
'callconv' {
fn_attrs += call_convention_attribute(attr.arg, g.is_cc_msvc)
}
'console' {
g.force_main_console = true
@ -2163,3 +2166,7 @@ fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string {
}
return fn_attrs
}
fn call_convention_attribute(cconvention string, is_cc_msvc bool) string {
return if is_cc_msvc { '__$cconvention ' } else { '__attribute__(($cconvention)) ' }
}

View File

@ -185,18 +185,60 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
mut comments := []ast.Comment{}
for fna in p.attrs {
match fna.name {
'noreturn' { is_noreturn = true }
'manualfree' { is_manualfree = true }
'deprecated' { is_deprecated = true }
'direct_array_access' { is_direct_arr = true }
'keep_args_alive' { is_keep_alive = true }
'export' { is_exported = true }
'wasm_export' { is_exported = true }
'unsafe' { is_unsafe = true }
'trusted' { is_trusted = true }
'c2v_variadic' { is_c2v_variadic = true }
'use_new' { is_ctor_new = true }
'markused' { is_markused = true }
'noreturn' {
is_noreturn = true
}
'manualfree' {
is_manualfree = true
}
'deprecated' {
is_deprecated = true
}
'direct_array_access' {
is_direct_arr = true
}
'keep_args_alive' {
is_keep_alive = true
}
'export' {
is_exported = true
}
'wasm_export' {
is_exported = true
}
'unsafe' {
is_unsafe = true
}
'trusted' {
is_trusted = true
}
'c2v_variadic' {
is_c2v_variadic = true
}
'use_new' {
is_ctor_new = true
}
'markused' {
is_markused = true
}
'windows_stdcall' {
// TODO: uncomment after bootstrapping and replacing usages in builtin
// p.note_with_pos('windows_stdcall has been deprecated, it will be an error soon', p.tok.pos())
}
'_fastcall' {
// TODO: uncomment after bootstrapping and replacing usages in builtin
// p.note_with_pos('_fastcall has been deprecated, it will be an error soon', p.tok.pos())
}
'callconv' {
if !fna.has_arg {
p.error_with_pos('callconv attribute is present but its value is missing',
p.prev_tok.pos())
}
if fna.arg !in ['stdcall', 'fastcall', 'cdecl'] {
p.error_with_pos('unsupported calling convention, supported are stdcall, fastcall and cdecl',
p.prev_tok.pos())
}
}
else {}
}
}

View File

@ -229,6 +229,23 @@ pub fn (mut p Parser) parse_multi_return_type() ast.Type {
pub fn (mut p Parser) parse_fn_type(name string) ast.Type {
// p.warn('parse fn')
p.check(.key_fn)
for attr in p.attrs {
match attr.name {
'callconv' {
if !attr.has_arg {
p.error_with_pos('callconv attribute is present but its value is missing',
p.prev_tok.pos())
}
if attr.arg !in ['stdcall', 'fastcall', 'cdecl'] {
p.error_with_pos('unsupported calling convention, supported are stdcall, fastcall and cdecl',
p.prev_tok.pos())
}
}
else {}
}
}
mut has_generic := false
line_nr := p.tok.line_nr
args, _, is_variadic := p.fn_args()
@ -255,6 +272,7 @@ pub fn (mut p Parser) parse_fn_type(name string) ast.Type {
return_type: return_type
return_type_pos: return_type_pos
is_method: false
attrs: p.attrs
}
// MapFooFn typedefs are manually added in cheaders.v
// because typedefs get generated after the map struct is generated

View File

@ -3579,6 +3579,8 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
p.table.sym(fn_type).is_pub = is_pub
type_pos = type_pos.extend(p.tok.pos())
comments = p.eat_comments(same_line: true)
attrs := p.attrs
p.attrs = []
return ast.FnTypeDecl{
name: fn_name
is_pub: is_pub
@ -3586,6 +3588,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
pos: decl_pos
type_pos: type_pos
comments: comments
attrs: attrs
}
}
sum_variants << p.parse_sum_type_variants()