mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
checker,cgen: support const y = term.yellow
, then println(y('abc'))
(#16436)
This commit is contained in:
parent
dc81d755e8
commit
ddc1a1fc08
@ -580,7 +580,8 @@ pub mut:
|
||||
name string // left.name()
|
||||
is_method bool
|
||||
is_field bool // temp hack, remove ASAP when re-impl CallExpr / Selector (joe)
|
||||
is_fn_var bool // fn variable
|
||||
is_fn_var bool // fn variable, `a := fn() {}`, then: `a()`
|
||||
is_fn_a_const bool // fn const, `const c = abc`, where `abc` is a function, then: `c()`
|
||||
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
|
||||
@ -592,7 +593,8 @@ pub mut:
|
||||
left_type Type // type of `user`
|
||||
receiver_type Type // User
|
||||
return_type Type
|
||||
fn_var_type Type // fn variable type
|
||||
fn_var_type Type // the fn type, when `is_fn_a_const` or `is_fn_var` is true
|
||||
const_name string // the fully qualified name of the const, i.e. `main.c`, given `const c = abc`, and callexpr: `c()`
|
||||
should_be_skipped bool // true for calls to `[if someflag?]` functions, when there is no `-d someflag`
|
||||
concrete_types []Type // concrete types, e.g. <int, string>
|
||||
concrete_list_pos token.Pos
|
||||
|
@ -1434,8 +1434,8 @@ pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string
|
||||
return sb.str()
|
||||
}
|
||||
|
||||
// Get the name of the complete quanlified name of the type
|
||||
// without the generic parts.
|
||||
// symbol_name_except_generic return the name of the complete qualified name of the type,
|
||||
// but without the generic parts. For example, `main.Abc<int>` -> `main.Abc`
|
||||
pub fn (t &TypeSymbol) symbol_name_except_generic() string {
|
||||
// main.Abc<int>
|
||||
mut embed_name := t.name
|
||||
|
@ -832,12 +832,37 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
||||
if obj.typ != 0 {
|
||||
sym := c.table.sym(obj.typ)
|
||||
if sym.kind == .function {
|
||||
found = true
|
||||
func = (sym.info as ast.FnType).func
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// a same module constant?
|
||||
if !found {
|
||||
// allow for `const abc = myfunc`, then calling `abc()`
|
||||
qualified_const_name := if fn_name.contains('.') { fn_name } else { '${c.mod}.${fn_name}' }
|
||||
if mut obj := c.table.global_scope.find_const(qualified_const_name) {
|
||||
if obj.typ == 0 {
|
||||
obj.typ = c.expr(obj.expr)
|
||||
}
|
||||
if obj.typ != 0 {
|
||||
sym := c.table.sym(obj.typ)
|
||||
if sym.kind == .function {
|
||||
// at this point, the const metadata should be already known,
|
||||
// and we are sure that it is just a function
|
||||
c.table.fns[qualified_const_name].usages++
|
||||
c.table.fns[func.name].usages++
|
||||
found = true
|
||||
func = (sym.info as ast.FnType).func
|
||||
node.is_fn_a_const = true
|
||||
node.fn_var_type = obj.typ
|
||||
node.const_name = qualified_const_name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
continue_check = false
|
||||
if dot_index := fn_name.index('.') {
|
||||
|
@ -4890,11 +4890,15 @@ fn (mut g Gen) const_decl_simple_define(mod string, name string, val string) {
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) c_const_name(name string) string {
|
||||
return if g.pref.translated && !g.is_builtin_mod { name } else { '_const_${name}' }
|
||||
}
|
||||
|
||||
fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ ast.Type, unwrap_option bool) {
|
||||
// Initialize more complex consts in `void _vinit/2{}`
|
||||
// (C doesn't allow init expressions that can't be resolved at compile time).
|
||||
mut styp := g.typ(typ)
|
||||
cname := if g.pref.translated && !g.is_builtin_mod { name } else { '_const_${name}' }
|
||||
cname := g.c_const_name(name)
|
||||
mut init := strings.new_builder(100)
|
||||
if cname == '_const_os__args' {
|
||||
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
|
||||
init.writeln(g.expr_string_surround('\t${cname} = ', expr, ';'))
|
||||
}
|
||||
}
|
||||
mut def := '${styp} ${cname}'
|
||||
expr_sym := g.table.sym(typ)
|
||||
if expr_sym.kind == .function {
|
||||
// allow for: `const xyz = abc`, where `abc` is `fn abc() {}`
|
||||
func := (expr_sym.info as ast.FnType).func
|
||||
def = g.fn_var_signature(func.return_type, func.params.map(it.typ), cname)
|
||||
}
|
||||
g.global_const_defs[util.no_dots(name)] = GlobalConstDef{
|
||||
mod: mod
|
||||
def: '${styp} ${cname}; // inited later'
|
||||
def: '${def}; // inited later'
|
||||
init: init.str().trim_right('\n')
|
||||
dep_names: g.table.dependent_names_in_expr(expr)
|
||||
}
|
||||
|
@ -638,6 +638,9 @@ fn (mut g Gen) get_anon_fn_type_name(mut node ast.AnonFn, var_name string) strin
|
||||
}
|
||||
|
||||
fn (mut g Gen) call_expr(node ast.CallExpr) {
|
||||
if node.should_be_skipped {
|
||||
return
|
||||
}
|
||||
// NOTE: everything could be done this way
|
||||
// see my comment in parser near anon_fn
|
||||
if node.left is ast.AnonFn {
|
||||
@ -659,9 +662,6 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
||||
} else if node.left is ast.CallExpr && node.name == '' {
|
||||
g.expr(node.left)
|
||||
}
|
||||
if node.should_be_skipped {
|
||||
return
|
||||
}
|
||||
old_inside_call := g.inside_call
|
||||
g.inside_call = true
|
||||
defer {
|
||||
@ -1358,6 +1358,9 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.is_fn_a_const {
|
||||
name = g.c_const_name(node.const_name.replace('.', '__'))
|
||||
}
|
||||
// TODO2
|
||||
// cgen shouldn't modify ast nodes, this should be moved
|
||||
// g.generate_tmp_autofree_arg_vars(node, name)
|
||||
|
65
vlib/v/tests/const_name_equals_fn_name_test.v
Normal file
65
vlib/v/tests/const_name_equals_fn_name_test.v
Normal file
@ -0,0 +1,65 @@
|
||||
module main
|
||||
|
||||
import term
|
||||
|
||||
fn abc() {
|
||||
println('xyz')
|
||||
}
|
||||
|
||||
fn def() string {
|
||||
return 'xyz'
|
||||
}
|
||||
|
||||
const a_const_that_is_fn = abc
|
||||
|
||||
const a_const_that_is_fn_returning_value = def
|
||||
|
||||
const a_const_same_as_fn_in_another_module = term.yellow
|
||||
|
||||
fn test_simple_fn_assigned_to_const_can_be_called() {
|
||||
a_const_that_is_fn()
|
||||
assert true
|
||||
}
|
||||
|
||||
fn test_simple_fn_assigned_to_const_can_be_called_and_returns_value() {
|
||||
assert def == a_const_that_is_fn_returning_value
|
||||
assert def() == 'xyz'
|
||||
assert a_const_that_is_fn_returning_value() == 'xyz'
|
||||
assert def() == a_const_that_is_fn_returning_value()
|
||||
assert a_const_that_is_fn_returning_value() == def()
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
fn test_a_const_that_is_alias_to_fn_from_module() {
|
||||
assert a_const_same_as_fn_in_another_module == term.yellow
|
||||
assert term.yellow('x') == a_const_same_as_fn_in_another_module('x')
|
||||
assert ptr_str(term.yellow) == ptr_str(a_const_same_as_fn_in_another_module)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const pg = fn_generator()
|
||||
|
||||
const pg2 = main.fn_generator()()
|
||||
|
||||
fn fn_generator() fn () string {
|
||||
return fn () string {
|
||||
println('hello')
|
||||
return 'ok'
|
||||
}
|
||||
}
|
||||
|
||||
fn test_a_const_can_be_assigned_a_fn_produced_by_a_fn_generator_and_the_const_can_be_used() {
|
||||
assert main.fn_generator()() == 'ok'
|
||||
|
||||
x := fn_generator()
|
||||
assert x() == 'ok'
|
||||
|
||||
y := pg
|
||||
assert y() == 'ok'
|
||||
|
||||
assert pg() == 'ok'
|
||||
|
||||
assert pg2 == 'ok'
|
||||
}
|
Loading…
Reference in New Issue
Block a user