Skip to content

Commit 8fa8354

Browse files
authored
v: support __global C.Name = i16(123) and @[c_extern] __global C.stdout &C.FILE declarations (in addition to @[c_extern] fn C.name()), to allow for more precise C API error checking (fix #25319) (#25331)
1 parent 93a136f commit 8fa8354

File tree

8 files changed

+125
-13
lines changed

8 files changed

+125
-13
lines changed

vlib/v/ast/ast.v

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,12 @@ pub:
967967
is_exported bool // an explicit `@[export]` tag; the global will NOT be removed by `-skip-unused`
968968
is_weak bool
969969
is_hidden bool
970+
// The following fields, are relevant for non V globals, for example `__global C.stdout &C.FILE`:
971+
language Language // for C.stdout, it will be .c .
972+
is_extern bool // true, if an explicit `@[c_extern]` tag was used. It is suitable for globals, that are not initialised by V,
973+
// but come from the external linked objects/libs, like C.stdout etc, and that *are not* declared in included .h files .
974+
// Without an explicit `@[c_extern]` tag, V will avoid emiting `extern CType CName;` lines.
975+
// V will still know, that the type of C.stdout, is not the default `int`, but &C.FILE, and thus will do more checks on it.
970976
pub mut:
971977
expr Expr
972978
typ Type

vlib/v/checker/checker.v

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2529,7 +2529,9 @@ fn (mut c Checker) global_decl(mut node ast.GlobalDecl) {
25292529
}
25302530
}
25312531
for mut field in node.fields {
2532-
c.check_valid_snake_case(field.name, 'global name', field.pos)
2532+
if field.language != .c {
2533+
c.check_valid_snake_case(field.name, 'global name', field.pos)
2534+
}
25332535

25342536
if field.name in ast.global_reserved_type_names {
25352537
c.error('invalid use of reserved type `${field.name}` as a global name', field.pos)

vlib/v/gen/c/consts_and_globals.v

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,6 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
434434
g.inside_cinit = false
435435
g.inside_global_decl = false
436436
}
437-
cextern := node.attrs.contains('c_extern')
438437
should_init := (!g.pref.use_cache && g.pref.build_mode != .build_module)
439438
|| (g.pref.build_mode == .build_module && g.module_built == node.mod)
440439
mut attributes := ''
@@ -475,18 +474,23 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
475474
}
476475
mut def_builder := strings.new_builder(100)
477476
mut init := ''
478-
extern := if cextern { 'extern ' } else { '' }
477+
extern := if field.is_extern { 'extern ' } else { '' }
479478
modifier := if field.is_volatile { ' volatile ' } else { '' }
480-
def_builder.write_string('${extern}${visibility_kw}${modifier}${styp} ${attributes}${field.name}')
481-
if cextern {
482-
def_builder.writeln('; // global 2')
479+
final_c_name := field.name.all_after('C.')
480+
if field.is_extern {
481+
def_builder.writeln('${extern}${visibility_kw}${modifier}${styp} ${attributes}${final_c_name}; // global 2')
483482
g.global_const_defs[name] = GlobalConstDef{
484483
mod: node.mod
485484
def: def_builder.str()
486485
order: -1
487486
}
488487
continue
489488
}
489+
mut needs_ending_semicolon := false
490+
if field.language != .c || field.has_expr {
491+
def_builder.write_string('${extern}${visibility_kw}${modifier}${styp} ${attributes}${final_c_name}')
492+
needs_ending_semicolon = true
493+
}
490494
if field.has_expr || cinit {
491495
// `__global x = unsafe { nil }` should still use the simple direct initialisation, `g_main_argv` needs it.
492496
mut is_simple_unsafe_expr := false
@@ -510,29 +514,34 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
510514
// More complex expressions need to be moved to `_vinit()`
511515
// e.g. `__global ( mygblobal = 'hello ' + world' )`
512516
if field.name in ['g_main_argc', 'g_main_argv'] {
513-
init = '\t// skipping ${field.name}, it was initialised in main'
517+
init = '\t// skipping ${final_c_name}, it was initialised in main'
514518
} else {
515-
init = '\t${field.name} = ${g.expr_string(field.expr)}; // global 3'
519+
init = '\t${final_c_name} = ${g.expr_string(field.expr)}; // global 3'
516520
}
517521
}
518-
} else if !g.pref.translated { // don't zero globals from C code
522+
} else if !g.pref.translated && field.language == .v {
523+
// don't zero globals from C code
519524
g.type_default_vars.clear()
520525
default_initializer := g.type_default(field.typ)
521526
if default_initializer == '{0}' && should_init {
522527
def_builder.write_string(' = {0}')
523528
} else if default_initializer == '{E_STRUCT}' && should_init {
524-
init = '\tmemcpy(${field.name}, (${styp}){${default_initializer}}, sizeof(${styp})); // global 4'
529+
init = '\tmemcpy(${final_c_name}, (${styp}){${default_initializer}}, sizeof(${styp})); // global 4'
525530
} else {
526531
if field.name !in ['as_cast_type_indexes', 'g_memory_block', 'global_allocator'] {
527532
decls := g.type_default_vars.str()
528533
if decls != '' {
529534
init = '\t${decls}'
530535
}
531-
init += '\t${field.name} = *(${styp}*)&((${styp}[]){${default_initializer}}[0]); // global 5'
536+
init += '\t${final_c_name} = *(${styp}*)&((${styp}[]){${default_initializer}}[0]); // global 5'
532537
}
533538
}
539+
} else {
540+
def_builder.writeln('/* skip C global: ${final_c_name} */')
541+
}
542+
if needs_ending_semicolon {
543+
def_builder.writeln('; // global 6')
534544
}
535-
def_builder.writeln('; // global 6')
536545
g.global_const_defs[name] = GlobalConstDef{
537546
mod: node.mod
538547
def: def_builder.str()
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* skip C global: stdout */
2+
/* skip C global: stderr */
3+
builtin__println(_S("&C.FILE"));
4+
builtin__FILE_str(stdout)
5+
builtin__FILE_str(stderr)
6+
fprintf(stdout, "Hello world on stdout.\n");
7+
fprintf(stderr, "This should be on stderr.\n");
8+
9+
extern u64 My_C_u64_global;
10+
extern i32 My_C_i32_global;
11+
fprintf(stdout, "My_C_u64_global: %ld\n", My_C_u64_global);
12+
fprintf(stdout, "My_C_i32_global: %d\n", My_C_i32_global);
13+
builtin__u64_str(My_C_u64_global)
14+
builtin__i32_str(My_C_i32_global)
15+
16+
i16 My_non_extern_C_global = ((i16)(-9876));
17+
fprintf(stdout, "C.My_non_extern_C_global: %d\n", My_non_extern_C_global);
18+
builtin__i16_str(My_non_extern_C_global)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
types:
2+
&C.FILE
3+
&C.FILE
4+
values:
5+
&C.FILE{}
6+
&C.FILE{}
7+
This should be on stderr.
8+
Hello world on stdout.
9+
My_C_u64_global: 456
10+
My_C_i32_global: -123
11+
C.My_non_extern_C_global: -9876
12+
u64
13+
i32
14+
i16
15+
-9876
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
@[has_globals]
2+
module main
3+
4+
#include "@DIR/c_file_for_c_extern.c"
5+
6+
// These could be macros, but without the @[c_extern] tag, they should still work.
7+
// V should treat their type as `&C.FILE`, and *NOT* as the default `int` type for C names.
8+
__global C.stdout &C.FILE
9+
__global C.stderr &C.FILE
10+
11+
// This should generate: `i16 My_non_extern_C_global = -9876;` . Note the lack of `extern `.
12+
__global C.My_non_extern_C_global = i16(-9876)
13+
14+
// These are defined in the included c_file_for_c_extern.c . Declaring them here,
15+
// should make V recognize their type.
16+
17+
@[c_extern]
18+
__global C.My_C_u64_global u64
19+
// This should generate: `extern u64 My_C_u64_global;`
20+
21+
@[c_extern]
22+
__global C.My_C_i32_global i32
23+
// This should generate: `extern i32 My_C_i32_global;`
24+
25+
unbuffer_stdout()
26+
println('types:')
27+
println(typeof(C.stdout).name)
28+
println(typeof(C.stderr).name)
29+
println('values:')
30+
println(C.stdout)
31+
println(C.stderr)
32+
// println(voidptr(C.stdout))
33+
// println(voidptr(C.stderr))
34+
C.fflush(C.stderr)
35+
C.fflush(C.stdout)
36+
C.fprintf(C.stderr, c'This should be on stderr.\n')
37+
C.fflush(C.stderr)
38+
C.fprintf(C.stdout, c'Hello world on stdout.\n')
39+
C.fprintf(C.stdout, c'My_C_u64_global: %ld\n', C.My_C_u64_global)
40+
C.fprintf(C.stdout, c'My_C_i32_global: %d\n', C.My_C_i32_global)
41+
C.fprintf(C.stdout, c'C.My_non_extern_C_global: %d\n', C.My_non_extern_C_global)
42+
C.fflush(C.stdout)
43+
44+
assert C.My_C_u64_global == 456
45+
assert C.My_C_i32_global == -123
46+
println(typeof(C.My_C_u64_global).name)
47+
println(typeof(C.My_C_i32_global).name)
48+
println(typeof(C.My_non_extern_C_global).name)
49+
println(C.My_non_extern_C_global)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
u64 My_C_u64_global = 456;
3+
4+
i32 My_C_i32_global = -123;

vlib/v/parser/parser.v

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2608,12 +2608,14 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
26082608
mut is_exported := false
26092609
mut is_weak := false
26102610
mut is_hidden := false
2611+
mut is_extern := false
26112612
for ga in attrs {
26122613
match ga.name {
26132614
'export' { is_exported = true }
26142615
'markused' { is_markused = true }
26152616
'weak' { is_weak = true }
26162617
'hidden' { is_hidden = true }
2618+
'c_extern' { is_extern = true }
26172619
else {}
26182620
}
26192621
}
@@ -2648,8 +2650,10 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
26482650
if p.tok.kind == .rpar {
26492651
break
26502652
}
2653+
language := p.parse_language()
2654+
26512655
pos := p.tok.pos()
2652-
name := p.check_name()
2656+
mut name := p.check_name()
26532657
has_expr := p.tok.kind == .assign
26542658
mut expr := ast.empty_expr
26552659
mut typ := ast.void_type
@@ -2687,6 +2691,9 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
26872691
typ_pos = p.tok.pos()
26882692
typ = p.parse_type()
26892693
}
2694+
if language == .c {
2695+
name = 'C.' + name
2696+
}
26902697
field := ast.GlobalField{
26912698
name: name
26922699
has_expr: has_expr
@@ -2700,6 +2707,8 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
27002707
is_exported: is_exported
27012708
is_weak: is_weak
27022709
is_hidden: is_hidden
2710+
is_extern: is_extern
2711+
language: language
27032712
}
27042713
fields << field
27052714
if name !in ast.global_reserved_type_names {

0 commit comments

Comments
 (0)