mirror of
https://github.com/vlang/v.git
synced 2025-09-13 14:32:26 +03:00
562 lines
19 KiB
V
562 lines
19 KiB
V
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
|
// Use of this source code is governed by an MIT license
|
|
// that can be found in the LICENSE file.
|
|
module c
|
|
|
|
import v.ast
|
|
import v.util
|
|
import strings
|
|
import v.depgraph
|
|
|
|
// global or const variable definition string
|
|
struct GlobalConstDef {
|
|
mod string // module name
|
|
def string // definition
|
|
init string // init later (in _vinit)
|
|
dep_names []string // the names of all the consts, that this const depends on
|
|
order int // -1 for simple defines, string literals, anonymous function names, extern declarations etc
|
|
is_precomputed bool // can be declared as a const in C: primitive, and a simple definition
|
|
}
|
|
|
|
fn (mut g Gen) const_decl(node ast.ConstDecl) {
|
|
g.inside_const = true
|
|
defer {
|
|
g.inside_const = false
|
|
}
|
|
for field in node.fields {
|
|
if g.pref.skip_unused {
|
|
if field.name !in g.table.used_features.used_consts {
|
|
$if trace_skip_unused_consts ? {
|
|
eprintln('>> skipping unused const name: ${field.name}')
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
name := c_name(field.name)
|
|
mut const_name := '_const_' + name
|
|
if g.pref.translated && !g.is_builtin_mod
|
|
&& !util.module_is_builtin(field.name.all_before_last('.')) {
|
|
if name.starts_with('main__') {
|
|
const_name = name.all_after_first('main__')
|
|
}
|
|
}
|
|
if !g.is_builtin_mod {
|
|
if cattr := node.attrs.find_first('export') {
|
|
const_name = cattr.arg
|
|
}
|
|
}
|
|
field_expr := field.expr
|
|
match field.expr {
|
|
ast.ArrayInit {
|
|
elems_are_const := field.expr.exprs.all(g.check_expr_is_const(it))
|
|
if field.expr.is_fixed && !field.expr.has_index
|
|
&& g.pref.build_mode != .build_module
|
|
&& (!g.is_cc_msvc || field.expr.elem_type != ast.string_type) && elems_are_const {
|
|
styp := g.styp(field.expr.typ)
|
|
val := g.expr_string(field.expr)
|
|
// eprintln('> const_name: ${const_name} | name: ${name} | styp: ${styp} | val: ${val}')
|
|
g.global_const_defs[name] = GlobalConstDef{
|
|
mod: field.mod
|
|
def: '${g.static_non_parallel}${styp} ${const_name} = ${val}; // fixed array const'
|
|
dep_names: g.table.dependent_names_in_expr(field_expr)
|
|
}
|
|
} else if field.expr.is_fixed && !field.expr.has_index
|
|
&& ((g.is_cc_msvc && field.expr.elem_type == ast.string_type)
|
|
|| !elems_are_const) {
|
|
g.const_decl_init_later_msvc_string_fixed_array(field.mod, name, field.expr,
|
|
field.typ)
|
|
} else {
|
|
g.const_decl_init_later(field.mod, name, field.expr, field.typ, false)
|
|
}
|
|
}
|
|
ast.StringLiteral {
|
|
val := g.expr_string(field.expr)
|
|
typ := if field.expr.language == .c { 'char*' } else { 'string' }
|
|
g.global_const_defs[name] = GlobalConstDef{
|
|
mod: field.mod
|
|
def: '${typ} ${const_name}; // a string literal, inited later'
|
|
init: '\t${const_name} = ${val};'
|
|
order: -1
|
|
}
|
|
}
|
|
ast.CallExpr {
|
|
if field.expr.return_type.has_flag(.option)
|
|
|| field.expr.return_type.has_flag(.result) {
|
|
old_inside_const_opt_or_res := g.inside_const_opt_or_res
|
|
g.inside_const_opt_or_res = true
|
|
unwrap_opt_res := field.expr.or_block.kind != .absent
|
|
g.const_decl_init_later(field.mod, name, field.expr, field.typ, unwrap_opt_res)
|
|
g.inside_const_opt_or_res = old_inside_const_opt_or_res
|
|
} else {
|
|
g.const_decl_init_later(field.mod, name, field.expr, field.typ, false)
|
|
}
|
|
}
|
|
else {
|
|
// Note: -usecache uses prebuilt modules, each compiled with:
|
|
// `v build-module vlib/module`
|
|
// combined with a top level program, that is compiled with:
|
|
// `v -usecache toplevel`
|
|
// For it to work, the consts optimisations should be identical, because
|
|
// only the top level program will have the const initialisation code for
|
|
// all the modules.
|
|
// TODO: encapsulate const initialisation for each module in a separate function,
|
|
// that is just called by the top level program in _vinit, instead of generating
|
|
// all the code inside _vinit for each module.
|
|
use_cache_mode := g.pref.build_mode == .build_module || g.pref.use_cache
|
|
if !use_cache_mode {
|
|
if ct_value := field.comptime_expr_value() {
|
|
if g.const_decl_precomputed(field.mod, name, field.name, ct_value,
|
|
field.typ)
|
|
{
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
if field.is_simple_define_const() {
|
|
// "Simple" expressions are not going to need multiple statements,
|
|
// only the ones which are inited later, so it's safe to use expr_string
|
|
g.const_decl_simple_define(field.mod, field.name, g.expr_string(field_expr))
|
|
} else if field.expr is ast.CastExpr {
|
|
if field.expr.expr is ast.ArrayInit {
|
|
if field.expr.expr.is_fixed && g.pref.build_mode != .build_module {
|
|
styp := g.styp(field.expr.typ)
|
|
val := g.expr_string(field.expr.expr)
|
|
g.global_const_defs[name] = GlobalConstDef{
|
|
mod: field.mod
|
|
def: '${g.static_non_parallel}${styp} ${const_name} = ${val}; // fixed array const'
|
|
dep_names: g.table.dependent_names_in_expr(field_expr)
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
should_surround := field.expr.expr is ast.CallExpr
|
|
&& field.expr.expr.or_block.kind != .absent
|
|
g.const_decl_init_later(field.mod, name, field.expr, field.typ, should_surround)
|
|
} else if field.expr is ast.InfixExpr {
|
|
mut has_unwrap_opt_res := false
|
|
if field.expr.left is ast.CallExpr {
|
|
has_unwrap_opt_res = field.expr.left.or_block.kind != .absent
|
|
} else if field.expr.right is ast.CallExpr {
|
|
has_unwrap_opt_res = field.expr.right.or_block.kind != .absent
|
|
}
|
|
g.const_decl_init_later(field.mod, name, field.expr, field.typ, has_unwrap_opt_res)
|
|
} else {
|
|
g.const_decl_init_later(field.mod, name, field.expr, field.typ, true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut g Gen) const_decl_precomputed(mod string, name string, field_name string, ct_value ast.ComptTimeConstValue,
|
|
typ ast.Type) bool {
|
|
mut styp := g.styp(typ)
|
|
cname := if g.pref.translated && !g.is_builtin_mod { name } else { '_const_${name}' }
|
|
$if trace_const_precomputed ? {
|
|
eprintln('> styp: ${styp} | cname: ${cname} | ct_value: ${ct_value} | ${ct_value.type_name()}')
|
|
}
|
|
match ct_value {
|
|
i8 {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
|
|
}
|
|
i16 {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
|
|
}
|
|
i32 {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
|
|
}
|
|
// int {
|
|
// XTODO int64
|
|
// g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
|
|
//}
|
|
i64 {
|
|
if typ == ast.i64_type {
|
|
return false
|
|
}
|
|
if typ == ast.int_type {
|
|
// TODO: use g.const_decl_write_precomputed here too.
|
|
// For now, use #define macros, so existing code compiles
|
|
// with -cstrict. Add checker errors for overflows instead,
|
|
// so V can catch them earlier, instead of relying on the
|
|
// C compiler for that.
|
|
g.const_decl_simple_define(mod, name, ct_value.str())
|
|
return true
|
|
}
|
|
if typ == ast.u64_type {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str() + 'U')
|
|
} else {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
|
|
}
|
|
}
|
|
u8 {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
|
|
}
|
|
u16 {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
|
|
}
|
|
u32 {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
|
|
}
|
|
u64 {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str() + 'U')
|
|
}
|
|
f32 {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
|
|
}
|
|
f64 {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
|
|
}
|
|
rune {
|
|
rune_code := u32(ct_value)
|
|
if rune_code <= 127 {
|
|
if rune_code in [`"`, `\\`, `'`] {
|
|
return false
|
|
}
|
|
escval := util.smart_quote(u8(rune_code).ascii_str(), false)
|
|
|
|
g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{
|
|
mod: mod
|
|
def: "#define ${cname} '${escval}'"
|
|
order: -1
|
|
}
|
|
} else {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, u32(ct_value).str())
|
|
}
|
|
}
|
|
string {
|
|
escaped_val := util.smart_quote(ct_value, false)
|
|
// g.const_decl_write_precomputed(line_nr, styp, cname, '_S("$escaped_val")')
|
|
// TODO: ^ the above for strings, cause:
|
|
// `error C2099: initializer is not a constant` errors in MSVC,
|
|
// so fall back to the delayed initialisation scheme:
|
|
init := if typ == ast.string_type {
|
|
'_S("${escaped_val}")'
|
|
} else {
|
|
'(${styp})"${escaped_val}"'
|
|
}
|
|
g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{
|
|
mod: mod
|
|
def: '${styp} ${cname}; // str inited later'
|
|
init: '\t${cname} = ${init};'
|
|
order: -1
|
|
}
|
|
if g.is_autofree {
|
|
g.cleanups[mod].writeln('\tstring_free(&${cname});')
|
|
}
|
|
}
|
|
voidptr {
|
|
g.const_decl_write_precomputed(mod, styp, cname, field_name, '(voidptr)(0x${ct_value})')
|
|
}
|
|
ast.EmptyExpr {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
fn (mut g Gen) const_decl_write_precomputed(mod string, styp string, cname string, field_name string, ct_value string) {
|
|
if g.pref.is_livemain || g.pref.is_liveshared {
|
|
// Note: tcc has problems reloading .so files with consts in them, when the consts are then used inside the reloaded
|
|
// live functions. As a workaround, just use simple #define macros in this case.
|
|
//
|
|
// If you change it, please also test with `v -live run examples/hot_reload/graph.v` which uses `math.pi` .
|
|
g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{
|
|
mod: mod
|
|
def: '#define ${cname} ${ct_value} // precomputed3, -live mode'
|
|
order: -1
|
|
}
|
|
return
|
|
}
|
|
g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{
|
|
mod: mod
|
|
def: '${g.static_non_parallel}const ${styp} ${cname} = ${ct_value}; // precomputed2'
|
|
// is_precomputed: true
|
|
}
|
|
}
|
|
|
|
fn (mut g Gen) const_decl_simple_define(mod string, name string, val string) {
|
|
// Simple expressions should use a #define
|
|
// so that we don't pollute the binary with unnecessary global vars
|
|
// Do not do this when building a module, otherwise the consts
|
|
// will not be accessible.
|
|
mut x := util.no_dots(name)
|
|
if g.pref.translated && !g.is_builtin_mod && !util.module_is_builtin(name.all_before_last('.')) {
|
|
// Don't prepend "_const" to translated C consts,
|
|
// but only in user code, continue prepending "_const" to builtin consts.
|
|
if x.starts_with('main__') {
|
|
x = x['main__'.len..]
|
|
}
|
|
} else {
|
|
x = '_const_${x}'
|
|
}
|
|
if g.pref.translated {
|
|
g.global_const_defs[util.no_dots(name)] = GlobalConstDef{
|
|
mod: mod
|
|
def: 'const int ${x} = ${val};'
|
|
order: -1
|
|
}
|
|
} else {
|
|
g.global_const_defs[util.no_dots(name)] = GlobalConstDef{
|
|
mod: mod
|
|
def: '#define ${x} ${val}'
|
|
order: -1
|
|
}
|
|
}
|
|
}
|
|
|
|
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, surround_cbr bool) {
|
|
if name.starts_with('C__') {
|
|
return
|
|
}
|
|
// 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.styp(typ)
|
|
cname := g.c_const_name(name)
|
|
mut init := strings.new_builder(100)
|
|
|
|
if surround_cbr {
|
|
init.writeln('{')
|
|
}
|
|
if expr is ast.ArrayInit && (expr as ast.ArrayInit).has_index {
|
|
init.writeln(g.expr_string_surround('\tmemcpy(&${cname}, &', expr, ', sizeof(${styp}));'))
|
|
} else if expr is ast.CallExpr
|
|
&& g.table.final_sym(g.unwrap_generic((expr as ast.CallExpr).return_type)).kind == .array_fixed {
|
|
init.writeln(g.expr_string_surround('\tmemcpy(&${cname}, ', expr, ', sizeof(${styp}));'))
|
|
} else {
|
|
init.writeln(g.expr_string_surround('\t${cname} = ', expr, ';'))
|
|
}
|
|
if surround_cbr {
|
|
init.writeln('}')
|
|
}
|
|
mut def := '${styp} ${cname}'
|
|
if g.pref.parallel_cc {
|
|
// So that the const is usable in other files
|
|
// def = 'extern ${def}'
|
|
}
|
|
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)
|
|
}
|
|
init_str := init.str().trim_right('\n')
|
|
g.global_const_defs[util.no_dots(name)] = GlobalConstDef{
|
|
mod: mod
|
|
def: '${def}; // inited later'
|
|
init: if init_str.count('\n') > 1 {
|
|
'{\n${init_str}\n}'
|
|
} else {
|
|
init_str
|
|
}
|
|
dep_names: g.table.dependent_names_in_expr(expr)
|
|
}
|
|
if g.is_autofree {
|
|
sym := g.table.sym(typ)
|
|
if styp.starts_with('Array_') {
|
|
if sym.has_method_with_generic_parent('free') {
|
|
g.cleanup.writeln('\t${styp}_free(&${cname});')
|
|
} else {
|
|
g.cleanup.writeln('\tarray_free(&${cname});')
|
|
}
|
|
} else if styp == 'string' {
|
|
g.cleanup.writeln('\tstring_free(&${cname});')
|
|
} else if sym.kind == .map {
|
|
g.cleanup.writeln('\tmap_free(&${cname});')
|
|
} else if styp == 'IError' {
|
|
g.cleanup.writeln('\tIError_free(&${cname});')
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut g Gen) const_decl_init_later_msvc_string_fixed_array(mod string, name string, expr ast.ArrayInit,
|
|
typ ast.Type) {
|
|
mut styp := g.styp(typ)
|
|
cname := g.c_const_name(name)
|
|
mut init := strings.new_builder(100)
|
|
for i, elem_expr in expr.exprs {
|
|
if elem_expr is ast.ArrayInit && elem_expr.is_fixed {
|
|
elem_typ := g.styp(elem_expr.typ)
|
|
init.writeln(g.expr_string_surround('\tmemcpy(${cname}[${i}], (${elem_typ})',
|
|
elem_expr, ', sizeof(${elem_typ}));'))
|
|
} else if elem_expr is ast.Ident {
|
|
elem_typ := elem_expr.obj.typ
|
|
if g.table.final_sym(elem_typ).kind == .array_fixed {
|
|
elem_styp := g.styp(elem_expr.obj.typ)
|
|
init.writeln(g.expr_string_surround('\tmemcpy(${cname}[${i}], ', elem_expr,
|
|
', sizeof(${elem_styp}));'))
|
|
} else {
|
|
init.writeln(g.expr_string_surround('\t${cname}[${i}] = ', elem_expr,
|
|
';'))
|
|
}
|
|
} else {
|
|
init.writeln(g.expr_string_surround('\t${cname}[${i}] = ', elem_expr, ';'))
|
|
}
|
|
}
|
|
mut def := '${styp} ${cname}'
|
|
g.global_const_defs[util.no_dots(name)] = GlobalConstDef{
|
|
mod: mod
|
|
def: '${def}; // inited later'
|
|
init: init.str().trim_right('\n')
|
|
dep_names: g.table.dependent_names_in_expr(expr)
|
|
}
|
|
if g.is_autofree {
|
|
sym := g.table.sym(typ)
|
|
if sym.has_method_with_generic_parent('free') {
|
|
g.cleanup.writeln('\t${styp}_free(&${cname});')
|
|
} else {
|
|
g.cleanup.writeln('\tarray_free(&${cname});')
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut g Gen) global_decl(node ast.GlobalDecl) {
|
|
// was static used here to to make code optimizable? it was removed when
|
|
// 'extern' was used to fix the duplicate symbols with usecache && clang
|
|
// visibility_kw := if g.pref.build_mode == .build_module && g.is_builtin_mod { 'static ' }
|
|
visibility_kw := if
|
|
(g.pref.use_cache || (g.pref.build_mode == .build_module && g.module_built != node.mod))
|
|
&& !util.should_bundle_module(node.mod) {
|
|
'extern '
|
|
} else {
|
|
''
|
|
// g.static_modifier // TODO: used to be '' before parallel_cc, may cause issues
|
|
}
|
|
// should the global be initialized now, not later in `vinit()`
|
|
cinit := node.attrs.contains('cinit')
|
|
g.inside_cinit = cinit
|
|
g.inside_global_decl = true
|
|
defer {
|
|
g.inside_cinit = false
|
|
g.inside_global_decl = false
|
|
}
|
|
cextern := node.attrs.contains('c_extern')
|
|
should_init := (!g.pref.use_cache && g.pref.build_mode != .build_module)
|
|
|| (g.pref.build_mode == .build_module && g.module_built == node.mod)
|
|
mut attributes := ''
|
|
first_field := node.fields[0]
|
|
if first_field.is_weak {
|
|
attributes += 'VWEAK '
|
|
}
|
|
if first_field.is_hidden {
|
|
attributes += 'VHIDDEN '
|
|
}
|
|
if first_field.is_exported {
|
|
attributes += 'VV_EXP '
|
|
}
|
|
if attr := node.attrs.find_first('_linker_section') {
|
|
attributes += '__attribute__ ((section ("${attr.arg}"))) '
|
|
}
|
|
for field in node.fields {
|
|
name := c_name(field.name)
|
|
if g.pref.skip_unused {
|
|
if field.name !in g.table.used_features.used_globals {
|
|
$if trace_skip_unused_globals ? {
|
|
eprintln('>> skipping unused global name: ${field.name}')
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
styp := g.styp(field.typ)
|
|
mut anon_fn_expr := unsafe { field.expr }
|
|
if field.has_expr && mut anon_fn_expr is ast.AnonFn {
|
|
g.gen_anon_fn_decl(mut anon_fn_expr)
|
|
fn_type_name := g.get_anon_fn_type_name(mut anon_fn_expr, field.name)
|
|
g.global_const_defs[util.no_dots(fn_type_name)] = GlobalConstDef{
|
|
mod: node.mod
|
|
def: '${fn_type_name} = ${g.table.sym(field.typ).name}; // global 1'
|
|
order: -1
|
|
}
|
|
continue
|
|
}
|
|
mut def_builder := strings.new_builder(100)
|
|
mut init := ''
|
|
extern := if cextern { 'extern ' } else { '' }
|
|
modifier := if field.is_volatile { ' volatile ' } else { '' }
|
|
def_builder.write_string('${extern}${visibility_kw}${modifier}${styp} ${attributes}${field.name}')
|
|
if cextern {
|
|
def_builder.writeln('; // global 2')
|
|
g.global_const_defs[name] = GlobalConstDef{
|
|
mod: node.mod
|
|
def: def_builder.str()
|
|
order: -1
|
|
}
|
|
continue
|
|
}
|
|
if field.has_expr || cinit {
|
|
// `__global x = unsafe { nil }` should still use the simple direct initialisation, `g_main_argv` needs it.
|
|
mut is_simple_unsafe_expr := false
|
|
if field.expr is ast.UnsafeExpr {
|
|
if field.expr.expr is ast.Nil {
|
|
is_simple_unsafe_expr = true
|
|
}
|
|
if field.expr.expr.is_literal() {
|
|
is_simple_unsafe_expr = true
|
|
}
|
|
}
|
|
if g.pref.translated {
|
|
def_builder.write_string(' = ${g.expr_string(field.expr)}')
|
|
} else if (field.expr.is_literal() && should_init) || cinit
|
|
|| (field.expr is ast.ArrayInit && field.expr.is_fixed)
|
|
|| (is_simple_unsafe_expr && should_init) {
|
|
// Simple literals can be initialized right away in global scope in C.
|
|
// e.g. `int myglobal = 10;`
|
|
def_builder.write_string(' = ${g.expr_string(field.expr)}')
|
|
} else {
|
|
// More complex expressions need to be moved to `_vinit()`
|
|
// e.g. `__global ( mygblobal = 'hello ' + world' )`
|
|
if field.name in ['g_main_argc', 'g_main_argv'] {
|
|
init = '\t// skipping ${field.name}, it was initialised in main'
|
|
} else {
|
|
init = '\t${field.name} = ${g.expr_string(field.expr)}; // global 3'
|
|
}
|
|
}
|
|
} else if !g.pref.translated { // don't zero globals from C code
|
|
g.type_default_vars.clear()
|
|
default_initializer := g.type_default(field.typ)
|
|
if default_initializer == '{0}' && should_init {
|
|
def_builder.write_string(' = {0}')
|
|
} else if default_initializer == '{E_STRUCT}' && should_init {
|
|
init = '\tmemcpy(${field.name}, (${styp}){${default_initializer}}, sizeof(${styp})); // global 4'
|
|
} else {
|
|
if field.name !in ['as_cast_type_indexes', 'g_memory_block', 'global_allocator'] {
|
|
decls := g.type_default_vars.str()
|
|
if decls != '' {
|
|
init = '\t${decls}'
|
|
}
|
|
init += '\t${field.name} = *(${styp}*)&((${styp}[]){${default_initializer}}[0]); // global 5'
|
|
}
|
|
}
|
|
}
|
|
def_builder.writeln('; // global 6')
|
|
g.global_const_defs[name] = GlobalConstDef{
|
|
mod: node.mod
|
|
def: def_builder.str()
|
|
init: init
|
|
dep_names: g.table.dependent_names_in_expr(field.expr)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut g Gen) sort_globals_consts() {
|
|
util.timing_start(@METHOD)
|
|
defer {
|
|
util.timing_measure(@METHOD)
|
|
}
|
|
g.sorted_global_const_names.clear()
|
|
mut dep_graph := depgraph.new_dep_graph()
|
|
for var_name, var_info in g.global_const_defs {
|
|
dep_graph.add_with_value(var_name, var_info.dep_names, var_info.order)
|
|
}
|
|
dep_graph_sorted := dep_graph.resolve()
|
|
for order in [-1, 0] {
|
|
for node in dep_graph_sorted.nodes {
|
|
if node.value == order {
|
|
g.sorted_global_const_names << node.name
|
|
}
|
|
}
|
|
}
|
|
}
|