v2 / vlib / v / gen / c / consts_and_globals.v
696 lines · 680 sloc · 23.17 KB · d398402707ab2298a12fbeabefe274eba5b1c2cd
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module c
5
6import v.ast
7import v.util
8import strings
9import v.depgraph
10
11// global or const variable definition string
12struct GlobalConstDef {
13 mod string // module name
14 def string // definition
15 init string // init later (in _vinit)
16 dep_names []string // the names of all the consts, that this const depends on
17 order int // -1 for simple defines, string literals, anonymous function names, extern declarations etc
18 is_precomputed bool // can be declared as a const in C: primitive, and a simple definition
19}
20
21fn (mut g Gen) const_decl(node ast.ConstDecl) {
22 g.inside_const = true
23 defer {
24 g.inside_const = false
25 }
26 for field in node.fields {
27 if g.pref.skip_unused {
28 if field.name !in g.table.used_features.used_consts {
29 $if trace_skip_unused_consts ? {
30 eprintln('>> skipping unused const name: ${field.name}')
31 }
32 continue
33 }
34 }
35 name := c_name(field.name)
36 const_name := g.c_const_name(field.name)
37 field_expr := field.expr
38 if field.attrs.contains('cinit') || node.attrs.contains('cinit') {
39 styp := g.styp(field.typ)
40 val := g.expr_string(field.expr)
41 g.global_const_defs[name] = GlobalConstDef{
42 mod: field.mod
43 def: '${g.static_non_parallel}${styp} ${const_name} = ${val};'
44 dep_names: g.table.dependent_names_in_expr(field.expr)
45 }
46 continue
47 }
48 match field.expr {
49 ast.ArrayInit {
50 elems_are_const := field.expr.exprs.all(g.check_expr_is_const(it))
51 if field.expr.is_fixed && !field.expr.has_index
52 && g.pref.build_mode != .build_module
53 && (!g.is_cc_msvc || field.expr.elem_type != ast.string_type) && elems_are_const {
54 styp := g.styp(field.expr.typ)
55 val := g.expr_string(ast.Expr(field.expr))
56 // eprintln('> const_name: ${const_name} | name: ${name} | styp: ${styp} | val: ${val}')
57 g.global_const_defs[name] = GlobalConstDef{
58 mod: field.mod
59 def: '${g.static_non_parallel}${styp} ${const_name} = ${val}; // fixed array const'
60 dep_names: g.table.dependent_names_in_expr(field_expr)
61 }
62 } else if field.expr.is_fixed && !field.expr.has_index
63 && ((g.is_cc_msvc && field.expr.elem_type == ast.string_type)
64 || !elems_are_const) {
65 g.const_decl_init_later_msvc_string_fixed_array(field.mod, name, const_name,
66 field.expr, field.typ)
67 } else {
68 g.const_decl_init_later(field.mod, name, const_name, ast.Expr(field.expr),
69 field.typ, false)
70 }
71 }
72 ast.StringLiteral {
73 val := g.expr_string(ast.Expr(field.expr))
74 typ := if field.expr.language == .c { 'char*' } else { 'string' }
75 g.global_const_defs[name] = GlobalConstDef{
76 mod: field.mod
77 def: '${g.static_non_parallel}${typ} ${const_name}; // a string literal, inited later'
78 init: '\t${const_name} = ${val};'
79 order: -1
80 }
81 }
82 ast.CallExpr {
83 if field.expr.return_type.has_flag(.option)
84 || field.expr.return_type.has_flag(.result) {
85 old_inside_const_opt_or_res := g.inside_const_opt_or_res
86 g.inside_const_opt_or_res = true
87 unwrap_opt_res := field.expr.or_block.kind != .absent
88 g.const_decl_init_later(field.mod, name, const_name, ast.Expr(field.expr),
89 field.typ, unwrap_opt_res)
90 g.inside_const_opt_or_res = old_inside_const_opt_or_res
91 } else {
92 g.const_decl_init_later(field.mod, name, const_name, ast.Expr(field.expr),
93 field.typ, false)
94 }
95 }
96 else {
97 // Note: -usecache uses prebuilt modules, each compiled with:
98 // `v build-module vlib/module`
99 // combined with a top level program, that is compiled with:
100 // `v -usecache toplevel`
101 // For it to work, the consts optimisations should be identical, because
102 // only the top level program will have the const initialisation code for
103 // all the modules.
104 // TODO: encapsulate const initialisation for each module in a separate function,
105 // that is just called by the top level program in _vinit, instead of generating
106 // all the code inside _vinit for each module.
107 use_cache_mode := g.pref.build_mode == .build_module || g.pref.use_cache
108 if !use_cache_mode {
109 if ct_value := field.comptime_expr_value() {
110 if g.const_decl_precomputed(field.mod, name, const_name, field.name,
111 ct_value, field.typ)
112 {
113 continue
114 }
115 }
116 }
117 if field.is_simple_define_const() {
118 // "Simple" expressions are not going to need multiple statements,
119 // only the ones which are inited later, so it's safe to use expr_string
120 g.const_decl_simple_define(field.mod, field.name, const_name,
121 g.expr_string(field_expr))
122 } else if field.expr is ast.CastExpr {
123 if field.expr.expr is ast.ArrayInit {
124 if field.expr.expr.is_fixed && g.pref.build_mode != .build_module {
125 styp := g.styp(field.expr.typ)
126 val := g.expr_string(field.expr.expr)
127 g.global_const_defs[name] = GlobalConstDef{
128 mod: field.mod
129 def: '${g.static_non_parallel}${styp} ${const_name} = ${val}; // fixed array const'
130 dep_names: g.table.dependent_names_in_expr(field_expr)
131 }
132 continue
133 }
134 }
135 should_surround := field.expr.expr is ast.CallExpr
136 && field.expr.expr.or_block.kind != .absent
137 g.const_decl_init_later(field.mod, name, const_name, ast.Expr(field.expr),
138 field.typ, should_surround)
139 } else if field.expr is ast.InfixExpr {
140 mut has_unwrap_opt_res := false
141 if field.expr.left is ast.CallExpr {
142 has_unwrap_opt_res = field.expr.left.or_block.kind != .absent
143 } else if field.expr.right is ast.CallExpr {
144 has_unwrap_opt_res = field.expr.right.or_block.kind != .absent
145 }
146 g.const_decl_init_later(field.mod, name, const_name, ast.Expr(field.expr),
147 field.typ, has_unwrap_opt_res)
148 } else {
149 g.const_decl_init_later(field.mod, name, const_name, field.expr, field.typ,
150 true)
151 }
152 }
153 }
154 }
155}
156
157fn (mut g Gen) const_decl_precomputed(mod string, name string, cname string, field_name string, ct_value ast.ComptTimeConstValue,
158 typ ast.Type) bool {
159 mut styp := g.styp(typ)
160 $if trace_const_precomputed ? {
161 eprintln('> styp: ${styp} | cname: ${cname} | ct_value: ${ct_value} | ${ct_value.type_name()}')
162 }
163 match ct_value {
164 i8 {
165 g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
166 }
167 i16 {
168 g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
169 }
170 i32 {
171 g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
172 }
173 // int {
174 // XTODO int64
175 // g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
176 //}
177 i64 {
178 if typ == ast.i64_type {
179 return false
180 }
181 if typ == ast.int_type {
182 // TODO: use g.const_decl_write_precomputed here too.
183 // For now, use #define macros, so existing code compiles
184 // with -cstrict. Add checker errors for overflows instead,
185 // so V can catch them earlier, instead of relying on the
186 // C compiler for that.
187 g.const_decl_simple_define(mod, name, cname, ct_value.str())
188 return true
189 }
190 if typ == ast.u64_type {
191 g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str() + 'U')
192 } else {
193 g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
194 }
195 }
196 u8 {
197 g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
198 }
199 u16 {
200 g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
201 }
202 u32 {
203 g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
204 }
205 u64 {
206 g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str() + 'U')
207 }
208 f32 {
209 g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
210 }
211 f64 {
212 g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str())
213 }
214 rune {
215 rune_code := u32(ct_value)
216 if rune_code <= 127 {
217 if rune_code in [`"`, `\\`, `'`] {
218 return false
219 }
220 escval := util.smart_quote(u8(rune_code).ascii_str(), false)
221
222 g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{
223 mod: mod
224 def: "#define ${cname} '${escval}'"
225 order: -1
226 }
227 } else {
228 g.const_decl_write_precomputed(mod, styp, cname, field_name, u32(ct_value).str())
229 }
230 }
231 string {
232 escaped_val := util.smart_quote(ct_value, false)
233 // g.const_decl_write_precomputed(line_nr, styp, cname, '_S("${escaped_val}")')
234 // TODO: ^ the above for strings, cause:
235 // `error C2099: initializer is not a constant` errors in MSVC,
236 // so fall back to the delayed initialisation scheme:
237 init := if typ == ast.string_type {
238 '_S(${cescaped_string_literal(escaped_val)})'
239 } else {
240 '(${styp})${cescaped_string_literal(escaped_val)}'
241 }
242 g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{
243 mod: mod
244 def: '${g.static_non_parallel}${styp} ${cname}; // str inited later'
245 init: '\t${cname} = ${init};'
246 order: -1
247 }
248 if g.is_autofree {
249 g.cleanups[mod].writeln('\tbuiltin__string_free(&${cname});')
250 }
251 }
252 voidptr {
253 g.const_decl_write_precomputed(mod, styp, cname, field_name, '(voidptr)(0x${ct_value})')
254 }
255 ast.EmptyExpr {
256 return false
257 }
258 }
259
260 return true
261}
262
263fn (mut g Gen) const_decl_write_precomputed(mod string, styp string, cname string, field_name string, ct_value string) {
264 if g.pref.is_livemain || g.pref.is_liveshared {
265 // Note: tcc has problems reloading .so files with consts in them, when the consts are then used inside the reloaded
266 // live functions. As a workaround, just use simple #define macros in this case.
267 //
268 // If you change it, please also test with `v -live run examples/hot_reload/graph.v` which uses `math.pi` .
269 g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{
270 mod: mod
271 def: '#define ${cname} ${ct_value} // precomputed3, -live mode'
272 order: -1
273 }
274 return
275 }
276 g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{
277 mod: mod
278 def: '${g.static_non_parallel}const ${styp} ${cname} = ${ct_value}; // precomputed2'
279 // is_precomputed: true
280 }
281}
282
283fn (mut g Gen) const_decl_simple_define(mod string, name string, cname string, val string) {
284 // Simple expressions should use a #define
285 // so that we don't pollute the binary with unnecessary global vars
286 // Do not do this when building a module, otherwise the consts
287 // will not be accessible.
288 if g.pref.translated {
289 g.global_const_defs[util.no_dots(name)] = GlobalConstDef{
290 mod: mod
291 def: '${g.static_non_parallel}const ${ast.int_type_name} ${cname} = ${val};'
292 order: -1
293 }
294 } else {
295 g.global_const_defs[util.no_dots(name)] = GlobalConstDef{
296 mod: mod
297 def: '#define ${cname} ${val}'
298 order: -1
299 }
300 }
301}
302
303fn (mut g Gen) c_const_name(name string) string {
304 if name in g.table.export_names {
305 // `@[export] name
306 return g.table.export_names[name]
307 }
308 mut const_name := util.no_dots(name)
309 if g.pref.translated && !g.is_builtin_mod && !util.module_is_builtin(name.all_before_last('.')) {
310 if name.starts_with('main.') {
311 const_name = util.no_dots(name.all_after_first('main.'))
312 }
313 }
314
315 return if g.pref.translated && !g.is_builtin_mod {
316 const_name
317 } else {
318 '_const_' + g.get_ternary_name(c_name(name))
319 }
320}
321
322fn (mut g Gen) const_decl_init_later(mod string, name string, cname string, expr ast.Expr, typ ast.Type, surround_cbr bool) {
323 if name.starts_with('C__') {
324 return
325 }
326 // Initialize more complex consts in `void _vinit/2{}`
327 // (C doesn't allow init expressions that can't be resolved at compile time).
328 mut styp := g.styp(typ)
329 mut init := strings.new_builder(100)
330
331 if surround_cbr {
332 init.writeln('{')
333 }
334 if (expr is ast.ArrayInit && expr.has_index)
335 || (expr is ast.IfExpr && g.table.type_kind(expr.typ) == .array_fixed) {
336 init.writeln(g.expr_string_surround('\tmemcpy(&${cname}, &', expr, ', sizeof(${styp}));'))
337 } else if expr is ast.CallExpr
338 && g.table.final_sym(g.unwrap_generic((expr as ast.CallExpr).return_type)).kind == .array_fixed {
339 init.writeln(g.expr_string_surround('\tmemcpy(&${cname}, ', expr, ', sizeof(${styp}));'))
340 } else {
341 init.writeln(g.expr_string_surround('\t${cname} = ', expr, ';'))
342 }
343 if surround_cbr {
344 init.writeln('}')
345 }
346 mut def := '${g.static_non_parallel}${styp} ${cname}'
347 if g.pref.parallel_cc {
348 // So that the const is usable in other files
349 // def = 'extern ${def}'
350 }
351 expr_sym := g.table.sym(typ)
352 if expr_sym.kind == .function {
353 // allow for: `const xyz = abc`, where `abc` is `fn abc() {}`
354 func := (expr_sym.info as ast.FnType).func
355 def = g.fn_var_signature(ast.void_type, func.return_type, func.params.map(it.typ), cname)
356 }
357 init_str := init.str().trim_right('\n')
358 g.global_const_defs[util.no_dots(name)] = GlobalConstDef{
359 mod: mod
360 def: '${def}; // inited later'
361 init: if init_str.count('\n') > 1 {
362 '{\n${init_str}\n}'
363 } else {
364 init_str
365 }
366 dep_names: g.table.dependent_names_in_expr(expr)
367 }
368 if g.is_autofree {
369 sym := g.table.sym(typ)
370 if sym.kind == .array {
371 if sym.has_method_with_generic_parent('free') {
372 g.cleanup.writeln('\t${styp}_free(&${cname});')
373 } else {
374 g.cleanup.writeln('\tbuiltin__array_free(&${cname});')
375 }
376 } else if styp == 'string' {
377 g.cleanup.writeln('\tbuiltin__string_free(&${cname});')
378 } else if sym.kind == .map {
379 g.cleanup.writeln('\tbuiltin__map_free(&${cname});')
380 } else if styp == 'IError' {
381 g.cleanup.writeln('\tbuiltin__IError_free(&${cname});')
382 }
383 }
384}
385
386fn (mut g Gen) const_decl_init_later_msvc_string_fixed_array(mod string, name string, cname string, expr ast.ArrayInit,
387 typ ast.Type) {
388 mut styp := g.styp(typ)
389 mut init := strings.new_builder(100)
390 for i, elem_expr in expr.exprs {
391 if elem_expr is ast.ArrayInit && elem_expr.is_fixed {
392 elem_typ := g.styp(elem_expr.typ)
393 init.writeln(g.expr_string_surround('\tmemcpy(${cname}[${i}], (${elem_typ})',
394 elem_expr, ', sizeof(${elem_typ}));'))
395 } else if elem_expr is ast.Ident {
396 elem_typ := elem_expr.obj.typ
397 if g.table.final_sym(elem_typ).kind == .array_fixed {
398 elem_styp := g.styp(elem_expr.obj.typ)
399 init.writeln(g.expr_string_surround('\tmemcpy(${cname}[${i}], ', elem_expr,
400 ', sizeof(${elem_styp}));'))
401 } else {
402 init.writeln(g.expr_string_surround('\t${cname}[${i}] = ', elem_expr, ';'))
403 }
404 } else {
405 init.writeln(g.expr_string_surround('\t${cname}[${i}] = ', elem_expr, ';'))
406 }
407 }
408 mut def := '${g.static_non_parallel}${styp} ${cname}'
409 g.global_const_defs[util.no_dots(name)] = GlobalConstDef{
410 mod: mod
411 def: '${def}; // inited later'
412 init: init.str().trim_right('\n')
413 dep_names: g.table.dependent_names_in_expr(expr)
414 }
415 if g.is_autofree {
416 sym := g.table.sym(typ)
417 if sym.has_method_with_generic_parent('free') {
418 if sym.is_builtin() {
419 g.cleanup.writeln('\tbuiltin__${styp}_free(&${cname});')
420 } else {
421 g.cleanup.writeln('\t${styp}_free(&${cname});')
422 }
423 } else {
424 g.cleanup.writeln('\tbuiltin__array_free(&${cname});')
425 }
426 }
427}
428
429fn (mut g Gen) global_const_expr_is_c_const(expr ast.Expr) bool {
430 match expr {
431 ast.BoolLiteral, ast.CharLiteral, ast.EnumVal, ast.FloatLiteral, ast.IntegerLiteral,
432 ast.Nil, ast.SizeOf, ast.StringLiteral {
433 return true
434 }
435 ast.ArrayInit {
436 if !expr.is_fixed || expr.has_len || expr.has_cap || expr.has_init || expr.has_index {
437 return false
438 }
439 for item in expr.exprs {
440 if !g.global_const_expr_is_c_const(item) {
441 return false
442 }
443 }
444 return true
445 }
446 ast.CastExpr {
447 return !expr.has_arg && g.global_const_expr_is_c_const(expr.expr)
448 }
449 ast.Ident {
450 return expr.kind == .function
451 || (expr.obj is ast.ConstField && expr.obj.is_simple_define_const())
452 }
453 ast.InfixExpr {
454 if expr.left_type == ast.string_type || expr.right_type == ast.string_type
455 || expr.promoted_type == ast.string_type {
456 return false
457 }
458 return g.global_const_expr_is_c_const(expr.left)
459 && g.global_const_expr_is_c_const(expr.right)
460 }
461 ast.ParExpr {
462 return g.global_const_expr_is_c_const(expr.expr)
463 }
464 ast.PrefixExpr {
465 if expr.op == .amp {
466 return expr.right is ast.Ident || expr.right is ast.SelectorExpr
467 }
468 return g.global_const_expr_is_c_const(expr.right)
469 }
470 ast.SelectorExpr {
471 return expr.expr is ast.Ident && expr.expr.name == 'C'
472 }
473 ast.StructInit {
474 if expr.has_update_expr {
475 return false
476 }
477 for field in expr.init_fields {
478 if !g.global_const_expr_is_c_const(field.expr) {
479 return false
480 }
481 }
482 return true
483 }
484 ast.UnsafeExpr {
485 return g.global_const_expr_is_c_const(expr.expr)
486 }
487 else {
488 return false
489 }
490 }
491}
492
493fn (mut g Gen) global_decl(node ast.GlobalDecl) {
494 // was static used here to to make code optimizable? it was removed when
495 // 'extern' was used to fix the duplicate symbols with usecache && clang
496 // visibility_kw := if g.pref.build_mode == .build_module && g.is_builtin_mod { 'static ' }
497 visibility_kw := if g.should_use_object_local_linkage(node.mod) {
498 'static '
499 } else if
500 (g.pref.use_cache || (g.pref.build_mode == .build_module && g.module_built != node.mod))
501 && !util.should_bundle_module(node.mod) {
502 'extern '
503 } else {
504 ''
505 // g.static_modifier // TODO: used to be '' before parallel_cc, may cause issues
506 }
507 // should the global be initialized now, not later in `vinit()`
508 cinit := node.attrs.contains('cinit')
509 g.inside_cinit = cinit
510 g.inside_global_decl = true
511 defer {
512 g.inside_cinit = false
513 g.inside_global_decl = false
514 }
515 should_init := (!g.pref.use_cache && g.pref.build_mode != .build_module)
516 || (g.pref.build_mode == .build_module && g.module_built == node.mod)
517 mut attributes := ''
518 first_field := node.fields[0]
519 if first_field.is_weak {
520 attributes += 'VWEAK '
521 }
522 if first_field.is_hidden {
523 attributes += 'VHIDDEN '
524 }
525 if first_field.is_exported {
526 attributes += 'VV_EXP '
527 }
528 if attr := node.attrs.find_first('_linker_section') {
529 attributes += '__attribute__ ((section ("${attr.arg}"))) '
530 }
531 for field in node.fields {
532 name := c_name(field.name)
533 if g.pref.skip_unused {
534 if field.name !in g.table.used_features.used_globals {
535 $if trace_skip_unused_globals ? {
536 eprintln('>> skipping unused global name: ${field.name}')
537 }
538 continue
539 }
540 }
541 styp := g.styp(field.typ)
542 mut def_builder := strings.new_builder(100)
543 mut init := ''
544 extern := if field.is_extern { 'extern ' } else { '' }
545 field_visibility_kw := if field.is_extern { '' } else { visibility_kw }
546 mut qualifiers := ''
547 if field.is_const {
548 qualifiers += 'const '
549 }
550 if field.is_volatile {
551 qualifiers += 'volatile '
552 }
553 final_c_name := field.name.all_after('C.')
554 if field.is_const {
555 if field.is_extern || field_visibility_kw == 'extern ' {
556 def_builder.writeln('${extern}${field_visibility_kw}${qualifiers}${styp} ${attributes}${final_c_name}; // global 2')
557 g.global_const_defs[name] = GlobalConstDef{
558 mod: node.mod
559 def: def_builder.str()
560 order: -1
561 }
562 continue
563 }
564 if !field.has_expr {
565 g.error('const globals must have an explicit initializer', field.pos)
566 continue
567 }
568 if !g.global_const_expr_is_c_const(field.expr) {
569 g.error('const global `${field.name}` must be initialized with a C constant expression',
570 field.pos)
571 continue
572 }
573 def_builder.write_string('${extern}${field_visibility_kw}${qualifiers}${styp} ${attributes}${final_c_name}')
574 def_builder.write_string(' = ${g.expr_string(field.expr)}')
575 def_builder.writeln('; // global 2')
576 g.global_const_defs[name] = GlobalConstDef{
577 mod: node.mod
578 def: def_builder.str()
579 order: -1
580 }
581 continue
582 }
583 mut anon_fn_expr := unsafe { field.expr }
584 if field.has_expr && mut anon_fn_expr is ast.AnonFn {
585 g.gen_anon_fn_decl(mut anon_fn_expr)
586 fn_type_name := g.get_anon_fn_type_name(mut anon_fn_expr, field.name)
587 g.global_const_defs[util.no_dots(fn_type_name)] = GlobalConstDef{
588 mod: node.mod
589 def: '${fn_type_name} = ${g.table.sym(field.typ).name}; // global 1'
590 order: -1
591 }
592 continue
593 }
594 if field.is_extern {
595 tls_kw := if field.name == 'g_memory_block' && g.pref.prealloc {
596 '_Thread_local '
597 } else {
598 ''
599 }
600 def_builder.writeln('${extern}${tls_kw}${field_visibility_kw}${qualifiers}${styp} ${attributes}${final_c_name}; // global 2')
601 g.global_const_defs[name] = GlobalConstDef{
602 mod: node.mod
603 def: def_builder.str()
604 order: -1
605 }
606 continue
607 }
608 mut needs_ending_semicolon := false
609 if field.language != .c || field.has_expr {
610 tls_kw := if field.name == 'g_memory_block' && g.pref.prealloc {
611 '_Thread_local '
612 } else {
613 ''
614 }
615 def_builder.write_string('${extern}${tls_kw}${field_visibility_kw}${qualifiers}${styp} ${attributes}${final_c_name}')
616 needs_ending_semicolon = true
617 }
618 if field.has_expr || cinit {
619 // `__global x = unsafe { nil }` should still use the simple direct initialisation, `g_main_argv` needs it.
620 mut is_simple_unsafe_expr := false
621 if field.expr is ast.UnsafeExpr {
622 if field.expr.expr is ast.Nil {
623 is_simple_unsafe_expr = true
624 }
625 if field.expr.expr.is_literal() {
626 is_simple_unsafe_expr = true
627 }
628 }
629 if g.pref.translated {
630 def_builder.write_string(' = ${g.expr_string(field.expr)}')
631 } else if (field.expr.is_literal() && should_init) || cinit
632 || (field.expr is ast.ArrayInit && field.expr.is_fixed)
633 || (is_simple_unsafe_expr && should_init) {
634 // Simple literals can be initialized right away in global scope in C.
635 // e.g. `int myglobal = 10;`
636 def_builder.write_string(' = ${g.expr_string(field.expr)}')
637 } else {
638 // More complex expressions need to be moved to `_vinit()`
639 // e.g. `__global ( mygblobal = 'hello ' + world' )`
640 if field.name in ['g_main_argc', 'g_main_argv'] {
641 init = '\t// skipping ${final_c_name}, it was initialised in main'
642 } else {
643 init = '\t${final_c_name} = ${g.expr_string(field.expr)}; // global 3'
644 }
645 }
646 } else if !g.pref.translated && field.language == .v {
647 // don't zero globals from C code
648 g.type_default_vars.clear()
649 default_initializer := g.type_default(field.typ)
650 if default_initializer == '{0}' && should_init {
651 def_builder.write_string(' = {0}')
652 } else if default_initializer == '{E_STRUCT}' && should_init {
653 init = '\tmemcpy(${final_c_name}, (${styp}){${default_initializer}}, sizeof(${styp})); // global 4'
654 } else {
655 if field.name !in ['as_cast_type_indexes', 'g_memory_block', 'global_allocator'] {
656 decls := g.type_default_vars.str()
657 if decls != '' {
658 init = '\t${decls}'
659 }
660 init += '\t${final_c_name} = *(${styp}*)&((${styp}[]){${default_initializer}}[0]); // global 5'
661 }
662 }
663 } else {
664 def_builder.writeln('/* skip C global: ${final_c_name} */')
665 }
666 if needs_ending_semicolon {
667 def_builder.writeln('; // global 6')
668 }
669 g.global_const_defs[name] = GlobalConstDef{
670 mod: node.mod
671 def: def_builder.str()
672 init: init
673 dep_names: g.table.dependent_names_in_expr(field.expr)
674 }
675 }
676}
677
678fn (mut g Gen) sort_globals_consts() {
679 util.timing_start(@METHOD)
680 defer {
681 util.timing_measure(@METHOD)
682 }
683 g.sorted_global_const_names.clear()
684 mut dep_graph := depgraph.new_dep_graph()
685 for var_name, var_info in g.global_const_defs {
686 dep_graph.add_with_value(var_name, var_info.dep_names, var_info.order)
687 }
688 dep_graph_sorted := dep_graph.resolve()
689 for order in [-1, 0] {
690 for node in dep_graph_sorted.nodes {
691 if node.value == order {
692 g.sorted_global_const_names << node.name
693 }
694 }
695 }
696}
697