1
0
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:
Delyan Angelov 2022-11-15 19:51:57 +02:00 committed by GitHub
parent dc81d755e8
commit ddc1a1fc08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 116 additions and 10 deletions

View File

@ -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

View File

@ -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

View File

@ -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('.') {

View File

@ -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)
}

View File

@ -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)

View 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'
}