Skip to content

Commit ddc1a1f

Browse files
authored
checker,cgen: support const y = term.yellow, then println(y('abc')) (#16436)
1 parent dc81d75 commit ddc1a1f

File tree

6 files changed

+115
-9
lines changed

6 files changed

+115
-9
lines changed

vlib/v/ast/ast.v

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,8 @@ pub mut:
580580
name string // left.name()
581581
is_method bool
582582
is_field bool // temp hack, remove ASAP when re-impl CallExpr / Selector (joe)
583-
is_fn_var bool // fn variable
583+
is_fn_var bool // fn variable, `a := fn() {}`, then: `a()`
584+
is_fn_a_const bool // fn const, `const c = abc`, where `abc` is a function, then: `c()`
584585
is_keep_alive bool // GC must not free arguments before fn returns
585586
is_noreturn bool // whether the function/method is marked as [noreturn]
586587
is_ctor_new bool // if JS ctor calls requires `new` before call, marked as `[use_new]` in V
@@ -592,7 +593,8 @@ pub mut:
592593
left_type Type // type of `user`
593594
receiver_type Type // User
594595
return_type Type
595-
fn_var_type Type // fn variable type
596+
fn_var_type Type // the fn type, when `is_fn_a_const` or `is_fn_var` is true
597+
const_name string // the fully qualified name of the const, i.e. `main.c`, given `const c = abc`, and callexpr: `c()`
596598
should_be_skipped bool // true for calls to `[if someflag?]` functions, when there is no `-d someflag`
597599
concrete_types []Type // concrete types, e.g. <int, string>
598600
concrete_list_pos token.Pos

vlib/v/ast/types.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,8 +1434,8 @@ pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string
14341434
return sb.str()
14351435
}
14361436

1437-
// Get the name of the complete quanlified name of the type
1438-
// without the generic parts.
1437+
// symbol_name_except_generic return the name of the complete qualified name of the type,
1438+
// but without the generic parts. For example, `main.Abc<int>` -> `main.Abc`
14391439
pub fn (t &TypeSymbol) symbol_name_except_generic() string {
14401440
// main.Abc<int>
14411441
mut embed_name := t.name

vlib/v/checker/fn.v

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,12 +832,37 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
832832
if obj.typ != 0 {
833833
sym := c.table.sym(obj.typ)
834834
if sym.kind == .function {
835+
func = (sym.info as ast.FnType).func
836+
found = true
837+
}
838+
}
839+
}
840+
}
841+
// a same module constant?
842+
if !found {
843+
// allow for `const abc = myfunc`, then calling `abc()`
844+
qualified_const_name := if fn_name.contains('.') { fn_name } else { '${c.mod}.${fn_name}' }
845+
if mut obj := c.table.global_scope.find_const(qualified_const_name) {
846+
if obj.typ == 0 {
847+
obj.typ = c.expr(obj.expr)
848+
}
849+
if obj.typ != 0 {
850+
sym := c.table.sym(obj.typ)
851+
if sym.kind == .function {
852+
// at this point, the const metadata should be already known,
853+
// and we are sure that it is just a function
854+
c.table.fns[qualified_const_name].usages++
855+
c.table.fns[func.name].usages++
835856
found = true
836857
func = (sym.info as ast.FnType).func
858+
node.is_fn_a_const = true
859+
node.fn_var_type = obj.typ
860+
node.const_name = qualified_const_name
837861
}
838862
}
839863
}
840864
}
865+
841866
if !found {
842867
continue_check = false
843868
if dot_index := fn_name.index('.') {

vlib/v/gen/c/cgen.v

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4890,11 +4890,15 @@ fn (mut g Gen) const_decl_simple_define(mod string, name string, val string) {
48904890
}
48914891
}
48924892

4893+
fn (mut g Gen) c_const_name(name string) string {
4894+
return if g.pref.translated && !g.is_builtin_mod { name } else { '_const_${name}' }
4895+
}
4896+
48934897
fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ ast.Type, unwrap_option bool) {
48944898
// Initialize more complex consts in `void _vinit/2{}`
48954899
// (C doesn't allow init expressions that can't be resolved at compile time).
48964900
mut styp := g.typ(typ)
4897-
cname := if g.pref.translated && !g.is_builtin_mod { name } else { '_const_${name}' }
4901+
cname := g.c_const_name(name)
48984902
mut init := strings.new_builder(100)
48994903
if cname == '_const_os__args' {
49004904
if g.pref.os == .windows {
@@ -4911,9 +4915,16 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ
49114915
init.writeln(g.expr_string_surround('\t${cname} = ', expr, ';'))
49124916
}
49134917
}
4918+
mut def := '${styp} ${cname}'
4919+
expr_sym := g.table.sym(typ)
4920+
if expr_sym.kind == .function {
4921+
// allow for: `const xyz = abc`, where `abc` is `fn abc() {}`
4922+
func := (expr_sym.info as ast.FnType).func
4923+
def = g.fn_var_signature(func.return_type, func.params.map(it.typ), cname)
4924+
}
49144925
g.global_const_defs[util.no_dots(name)] = GlobalConstDef{
49154926
mod: mod
4916-
def: '${styp} ${cname}; // inited later'
4927+
def: '${def}; // inited later'
49174928
init: init.str().trim_right('\n')
49184929
dep_names: g.table.dependent_names_in_expr(expr)
49194930
}

vlib/v/gen/c/fn.v

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,9 @@ fn (mut g Gen) get_anon_fn_type_name(mut node ast.AnonFn, var_name string) strin
638638
}
639639

640640
fn (mut g Gen) call_expr(node ast.CallExpr) {
641+
if node.should_be_skipped {
642+
return
643+
}
641644
// NOTE: everything could be done this way
642645
// see my comment in parser near anon_fn
643646
if node.left is ast.AnonFn {
@@ -659,9 +662,6 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
659662
} else if node.left is ast.CallExpr && node.name == '' {
660663
g.expr(node.left)
661664
}
662-
if node.should_be_skipped {
663-
return
664-
}
665665
old_inside_call := g.inside_call
666666
g.inside_call = true
667667
defer {
@@ -1358,6 +1358,9 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
13581358
}
13591359
}
13601360
}
1361+
if node.is_fn_a_const {
1362+
name = g.c_const_name(node.const_name.replace('.', '__'))
1363+
}
13611364
// TODO2
13621365
// cgen shouldn't modify ast nodes, this should be moved
13631366
// g.generate_tmp_autofree_arg_vars(node, name)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
module main
2+
3+
import term
4+
5+
fn abc() {
6+
println('xyz')
7+
}
8+
9+
fn def() string {
10+
return 'xyz'
11+
}
12+
13+
const a_const_that_is_fn = abc
14+
15+
const a_const_that_is_fn_returning_value = def
16+
17+
const a_const_same_as_fn_in_another_module = term.yellow
18+
19+
fn test_simple_fn_assigned_to_const_can_be_called() {
20+
a_const_that_is_fn()
21+
assert true
22+
}
23+
24+
fn test_simple_fn_assigned_to_const_can_be_called_and_returns_value() {
25+
assert def == a_const_that_is_fn_returning_value
26+
assert def() == 'xyz'
27+
assert a_const_that_is_fn_returning_value() == 'xyz'
28+
assert def() == a_const_that_is_fn_returning_value()
29+
assert a_const_that_is_fn_returning_value() == def()
30+
}
31+
32+
//
33+
34+
fn test_a_const_that_is_alias_to_fn_from_module() {
35+
assert a_const_same_as_fn_in_another_module == term.yellow
36+
assert term.yellow('x') == a_const_same_as_fn_in_another_module('x')
37+
assert ptr_str(term.yellow) == ptr_str(a_const_same_as_fn_in_another_module)
38+
}
39+
40+
//
41+
42+
const pg = fn_generator()
43+
44+
const pg2 = main.fn_generator()()
45+
46+
fn fn_generator() fn () string {
47+
return fn () string {
48+
println('hello')
49+
return 'ok'
50+
}
51+
}
52+
53+
fn test_a_const_can_be_assigned_a_fn_produced_by_a_fn_generator_and_the_const_can_be_used() {
54+
assert main.fn_generator()() == 'ok'
55+
56+
x := fn_generator()
57+
assert x() == 'ok'
58+
59+
y := pg
60+
assert y() == 'ok'
61+
62+
assert pg() == 'ok'
63+
64+
assert pg2 == 'ok'
65+
}

0 commit comments

Comments
 (0)