From c14d15bd3d6385d47afe16e87f35c080bee6057c Mon Sep 17 00:00:00 2001 From: l-m Date: Thu, 26 Jan 2023 15:47:38 +0000 Subject: [PATCH] ast,checker,parser,cgen: `[c:'sym']` rework; allow compiling code from .v files tagged with `[translated]` without needing -translated too (#17125) --- vlib/v/ast/ast.v | 68 ++++++++++--------- vlib/v/ast/table.v | 37 +++++----- vlib/v/checker/fn.v | 3 +- vlib/v/gen/c/cgen.v | 9 +-- vlib/v/gen/c/fn.v | 4 +- vlib/v/gen/c/testdata/translated/sym.c | 3 + .../translated/translated_module_actual.v | 7 ++ vlib/v/gen/c/testdata/translated/v.mod | 0 .../c/testdata/translated_module.c.must_have | 2 + vlib/v/gen/c/testdata/translated_module.out | 1 + vlib/v/gen/c/testdata/translated_module.vv | 9 +++ vlib/v/parser/fn.v | 2 + 12 files changed, 87 insertions(+), 58 deletions(-) create mode 100644 vlib/v/gen/c/testdata/translated/sym.c create mode 100644 vlib/v/gen/c/testdata/translated/translated_module_actual.v create mode 100644 vlib/v/gen/c/testdata/translated/v.mod create mode 100644 vlib/v/gen/c/testdata/translated_module.c.must_have create mode 100644 vlib/v/gen/c/testdata/translated_module.out create mode 100644 vlib/v/gen/c/testdata/translated_module.vv diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 4af16c82c0..d3885bb668 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -513,39 +513,40 @@ pub mut: [minify] pub struct FnDecl { pub: - name string // 'math.bits.normalize' - short_name string // 'normalize' - mod string // 'math.bits' - is_deprecated bool - is_pub bool - is_variadic bool - is_anon bool - is_noreturn bool // true, when [noreturn] is used on a fn - is_manualfree bool // true, when [manualfree] is used on a fn - is_main bool // true for `fn main()` - is_test bool // true for `fn test_abcde() {}`, false for `fn test_abc(x int) {}`, or for fns that do not start with test_ - is_conditional bool // true for `[if abc] fn abc(){}` - is_exported bool // true for `[export: 'exact_C_name']` - is_keep_alive bool // passed memory must not be freed (by GC) before function returns - is_unsafe bool // true, when [unsafe] is used on a fn - is_markused bool // true, when an explict `[markused]` tag was put on a fn; `-skip-unused` will not remove that fn - receiver StructField // TODO this is not a struct field - receiver_pos token.Pos // `(u User)` in `fn (u User) name()` position - is_method bool - method_type_pos token.Pos // `User` in ` fn (u User)` position - method_idx int - rec_mut bool // is receiver mutable - rec_share ShareType - language Language // V, C, JS - file_mode Language // whether *the file*, where a function was a '.c.v', '.js.v' etc. - no_body bool // just a definition `fn C.malloc()` - is_builtin bool // this function is defined in builtin/strconv - body_pos token.Pos // function bodys position - file string - generic_names []string - is_direct_arr bool // direct array access - attrs []Attr - ctdefine_idx int = -1 // the index in fn.attrs of `[if xyz]`, when such attribute exists + name string // 'math.bits.normalize' + short_name string // 'normalize' + mod string // 'math.bits' + is_deprecated bool + is_pub bool + is_variadic bool + is_anon bool + is_noreturn bool // true, when [noreturn] is used on a fn + is_manualfree bool // true, when [manualfree] is used on a fn + is_main bool // true for `fn main()` + is_test bool // true for `fn test_abcde() {}`, false for `fn test_abc(x int) {}`, or for fns that do not start with test_ + is_conditional bool // true for `[if abc] fn abc(){}` + is_exported bool // true for `[export: 'exact_C_name']` + is_keep_alive bool // passed memory must not be freed (by GC) before function returns + is_unsafe bool // true, when [unsafe] is used on a fn + is_markused bool // true, when an explict `[markused]` tag was put on a fn; `-skip-unused` will not remove that fn + is_file_translated bool // true, when the file it resides in is `[translated]` + receiver StructField // TODO this is not a struct field + receiver_pos token.Pos // `(u User)` in `fn (u User) name()` position + is_method bool + method_type_pos token.Pos // `User` in ` fn (u User)` position + method_idx int + rec_mut bool // is receiver mutable + rec_share ShareType + language Language // V, C, JS + file_mode Language // whether *the file*, where a function was a '.c.v', '.js.v' etc. + no_body bool // just a definition `fn C.malloc()` + is_builtin bool // this function is defined in builtin/strconv + body_pos token.Pos // function bodys position + file string + generic_names []string + is_direct_arr bool // direct array access + attrs []Attr + ctdefine_idx int = -1 // the index in fn.attrs of `[if xyz]`, when such attribute exists pub mut: idx int // index in an external container; can be used to refer to the function in a more efficient way, just by its integer index params []Param @@ -593,6 +594,7 @@ pub mut: is_keep_alive bool // GC must not free arguments before fn returns is_noreturn bool // whether the function/method is marked as [noreturn] is_ctor_new bool // if JS ctor calls requires `new` before call, marked as `[use_new]` in V + is_file_translated bool // true, when the file it resides in is `[translated]` args []CallArg expected_arg_types []Type comptime_ret_val bool diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 5988861713..8fc72b9589 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -93,24 +93,25 @@ pub fn (t &Table) panic(message string) { [minify] pub struct Fn { pub: - is_variadic bool - language Language - is_pub bool - is_ctor_new bool // `[use_new] fn JS.Array.prototype.constructor()` - is_deprecated bool // `[deprecated] fn abc(){}` - is_noreturn bool // `[noreturn] fn abc(){}` - is_unsafe bool // `[unsafe] fn abc(){}` - is_placeholder bool - is_main bool // `fn main(){}` - is_test bool // `fn test_abc(){}` - is_keep_alive bool // passed memory must not be freed (by GC) before function returns - is_method bool // true for `fn (x T) name()`, and for interface declarations (which are also for methods) - no_body bool // a pure declaration like `fn abc(x int)`; used in .vh files, C./JS. fns. - mod string - file string - file_mode Language - pos token.Pos - return_type_pos token.Pos + is_variadic bool + language Language + is_pub bool + is_ctor_new bool // `[use_new] fn JS.Array.prototype.constructor()` + is_deprecated bool // `[deprecated] fn abc(){}` + is_noreturn bool // `[noreturn] fn abc(){}` + is_unsafe bool // `[unsafe] fn abc(){}` + is_placeholder bool + is_main bool // `fn main(){}` + is_test bool // `fn test_abc(){}` + is_keep_alive bool // passed memory must not be freed (by GC) before function returns + is_method bool // true for `fn (x T) name()`, and for interface declarations (which are also for methods) + no_body bool // a pure declaration like `fn abc(x int)`; used in .vh files, C./JS. fns. + is_file_translated bool // true, when the file it resides in is `[translated]` + mod string + file string + file_mode Language + pos token.Pos + return_type_pos token.Pos pub mut: return_type Type receiver_type Type // != 0, when .is_method == true diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index fc5c5440cf..dec7467e66 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -887,6 +887,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. return ast.void_type } + node.is_file_translated = func.is_file_translated node.is_noreturn = func.is_noreturn node.is_ctor_new = func.is_ctor_new if !found_in_args { @@ -909,7 +910,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } node.is_keep_alive = func.is_keep_alive if func.language == .v && func.no_body && !c.pref.translated && !c.file.is_translated - && !func.is_unsafe && func.mod != 'builtin' { + && !func.is_unsafe && !func.is_file_translated && func.mod != 'builtin' { c.error('cannot call a function that does not have a body', node.pos) } if node.concrete_types.len > 0 && func.generic_names.len > 0 diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index ae527cae15..5783af6f66 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4357,15 +4357,16 @@ fn (mut g Gen) ident(node ast.Ident) { } } } else if node.info is ast.IdentFn { - if g.pref.translated || g.file.is_translated { - // `p_mobjthinker` => `P_MobjThinker` - if func := g.table.find_fn(node.name) { - // TODO PERF fn lookup for each fn call in translated mode + // TODO PERF fn lookup for each fn call in translated mode + if func := g.table.find_fn(node.name) { + if g.pref.translated || g.file.is_translated || func.is_file_translated { + // `p_mobjthinker` => `P_MobjThinker` if cattr := func.attrs.find_first('c') { name = cattr.arg } } } + if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') { key := node.name g.write('/* obf identfn: ${key} */') diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 44cd1ee0e3..e87fcc4306 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -446,7 +446,7 @@ fn (mut g Gen) c_fn_name(node &ast.FnDecl) !string { name = g.generic_fn_name(g.cur_concrete_types, name) } - if g.pref.translated || g.file.is_translated { + if g.pref.translated || g.file.is_translated || node.is_file_translated { if cattr := node.attrs.find_first('c') { // This fixes unknown symbols errors when building separate .c => .v files into .o files // example: @@ -1362,7 +1362,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { } else { name = c_name(name) } - if g.pref.translated || g.file.is_translated { + if g.pref.translated || g.file.is_translated || node.is_file_translated { // For `[c: 'P_TryMove'] fn p_trymove( ... ` // every time `p_trymove` is called, `P_TryMove` must be generated instead. if f := g.table.find_fn(node.name) { diff --git a/vlib/v/gen/c/testdata/translated/sym.c b/vlib/v/gen/c/testdata/translated/sym.c new file mode 100644 index 0000000000..fdf78acfc7 --- /dev/null +++ b/vlib/v/gen/c/testdata/translated/sym.c @@ -0,0 +1,3 @@ +int ExternalSymbol(char *hello) { + return *hello; +} \ No newline at end of file diff --git a/vlib/v/gen/c/testdata/translated/translated_module_actual.v b/vlib/v/gen/c/testdata/translated/translated_module_actual.v new file mode 100644 index 0000000000..445f3b1e54 --- /dev/null +++ b/vlib/v/gen/c/testdata/translated/translated_module_actual.v @@ -0,0 +1,7 @@ +[translated] +module translated + +#include "@VMODROOT/sym.c" + +[c: 'ExternalSymbol'] +pub fn external_symbol(&char) int diff --git a/vlib/v/gen/c/testdata/translated/v.mod b/vlib/v/gen/c/testdata/translated/v.mod new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vlib/v/gen/c/testdata/translated_module.c.must_have b/vlib/v/gen/c/testdata/translated_module.c.must_have new file mode 100644 index 0000000000..50396d4f50 --- /dev/null +++ b/vlib/v/gen/c/testdata/translated_module.c.must_have @@ -0,0 +1,2 @@ +int ExternalSymbol(char* ); +int a = ExternalSymbol("hello"); diff --git a/vlib/v/gen/c/testdata/translated_module.out b/vlib/v/gen/c/testdata/translated_module.out new file mode 100644 index 0000000000..cf5a24f2b8 --- /dev/null +++ b/vlib/v/gen/c/testdata/translated_module.out @@ -0,0 +1 @@ +result: 104 diff --git a/vlib/v/gen/c/testdata/translated_module.vv b/vlib/v/gen/c/testdata/translated_module.vv new file mode 100644 index 0000000000..ab234e2474 --- /dev/null +++ b/vlib/v/gen/c/testdata/translated_module.vv @@ -0,0 +1,9 @@ +module main + +import v.gen.c.testdata.translated { external_symbol } + +fn main() { + a := external_symbol(c'hello') + println('result: ${a}') + assert a == 104 +} diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index d7feceb18b..aa149b486e 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -495,6 +495,7 @@ run them via `v file.v` instead', is_test: is_test is_keep_alive: is_keep_alive is_method: false + is_file_translated: p.is_translated // attrs: p.attrs is_conditional: conditional_ctdefine_idx != ast.invalid_type_idx @@ -550,6 +551,7 @@ run them via `v file.v` instead', is_keep_alive: is_keep_alive is_unsafe: is_unsafe is_markused: is_markused + is_file_translated: p.is_translated // attrs: p.attrs is_conditional: conditional_ctdefine_idx != ast.invalid_type_idx