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

@ -5978,10 +5978,15 @@ fn custom_allocations() {
struct C.Foo { struct C.Foo {
} }
// Used in Win32 API code when you need to pass callback function // Used to add a custom calling convention to a function, available calling convention: stdcall, fastcall and cdecl.
[windows_stdcall] // This list aslo apply for type aliases (see below).
[callconv: "stdcall"]
fn C.DefWindowProc(hwnd int, msg int, lparam int, wparam int) 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: // Windows only:
// If a default graphics library is imported (ex. gg, ui), then the graphical window takes // 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. // priority and no console window is created, effectively disabling println() statements.

View File

@ -1152,6 +1152,7 @@ pub:
pos token.Pos pos token.Pos
type_pos token.Pos type_pos token.Pos
comments []Comment comments []Comment
attrs []Attr // attributes of type declaration
} }
// TODO: handle this differently // 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) { pub fn (mut f Fmt) fn_type_decl(node ast.FnTypeDecl) {
f.attrs(node.attrs)
if node.is_pub { if node.is_pub {
f.write('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_json_fn bool // inside json.encode()
is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)` is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)`
is_fn_index_call bool 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 vlines_path string // set to the proper path for generating #line directives
optionals map[string]string // to avoid duplicates optionals map[string]string // to avoid duplicates
done_optionals shared []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{} inner_loop: &ast.EmptyStmt{}
field_data_type: ast.Type(table.find_type_idx('FieldData')) field_data_type: ast.Type(table.find_type_idx('FieldData'))
init: strings.new_builder(100) init: strings.new_builder(100)
is_cc_msvc: pref.ccompiler == 'msvc'
} }
// anon fn may include assert and thus this needs // anon fn may include assert and thus this needs
// to be included before any test contents are written // 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 done_optionals: global_g.done_optionals
is_autofree: global_g.pref.autofree is_autofree: global_g.pref.autofree
referenced_fns: global_g.referenced_fns referenced_fns: global_g.referenced_fns
is_cc_msvc: global_g.is_cc_msvc
} }
g.gen_file() g.gen_file()
return g 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) if !info.has_decl && (not_anon || is_fn_sig) && !func.return_type.has_flag(.generic)
&& !has_generic_arg { && !has_generic_arg {
fn_name := sym.cname 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 { for i, param in func.params {
g.type_definitions.write_string(g.typ(param.typ)) g.type_definitions.write_string(g.typ(param.typ))
if i < func.params.len - 1 { if i < func.params.len - 1 {
g.type_definitions.write_string(',') 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_stdcall' {
// windows attributes (msvc/mingw) // windows attributes (msvc/mingw)
// prefixed by windows to indicate they're for advanced users only and not really supported by V. // 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' { '_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' { 'console' {
g.force_main_console = true g.force_main_console = true
@ -2163,3 +2166,7 @@ fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string {
} }
return fn_attrs 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{} mut comments := []ast.Comment{}
for fna in p.attrs { for fna in p.attrs {
match fna.name { match fna.name {
'noreturn' { is_noreturn = true } 'noreturn' {
'manualfree' { is_manualfree = true } is_noreturn = true
'deprecated' { is_deprecated = true } }
'direct_array_access' { is_direct_arr = true } 'manualfree' {
'keep_args_alive' { is_keep_alive = true } is_manualfree = true
'export' { is_exported = true } }
'wasm_export' { is_exported = true } 'deprecated' {
'unsafe' { is_unsafe = true } is_deprecated = true
'trusted' { is_trusted = true } }
'c2v_variadic' { is_c2v_variadic = true } 'direct_array_access' {
'use_new' { is_ctor_new = true } is_direct_arr = true
'markused' { is_markused = 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 {} 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 { pub fn (mut p Parser) parse_fn_type(name string) ast.Type {
// p.warn('parse fn') // p.warn('parse fn')
p.check(.key_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 mut has_generic := false
line_nr := p.tok.line_nr line_nr := p.tok.line_nr
args, _, is_variadic := p.fn_args() 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: return_type
return_type_pos: return_type_pos return_type_pos: return_type_pos
is_method: false is_method: false
attrs: p.attrs
} }
// MapFooFn typedefs are manually added in cheaders.v // MapFooFn typedefs are manually added in cheaders.v
// because typedefs get generated after the map struct is generated // 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 p.table.sym(fn_type).is_pub = is_pub
type_pos = type_pos.extend(p.tok.pos()) type_pos = type_pos.extend(p.tok.pos())
comments = p.eat_comments(same_line: true) comments = p.eat_comments(same_line: true)
attrs := p.attrs
p.attrs = []
return ast.FnTypeDecl{ return ast.FnTypeDecl{
name: fn_name name: fn_name
is_pub: is_pub is_pub: is_pub
@ -3586,6 +3588,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
pos: decl_pos pos: decl_pos
type_pos: type_pos type_pos: type_pos
comments: comments comments: comments
attrs: attrs
} }
} }
sum_variants << p.parse_sum_type_variants() sum_variants << p.parse_sum_type_variants()