Skip to content

Commit 6d89baa

Browse files
committed
v3: guard externs for uncertain C macros
1 parent 512edf2 commit 6d89baa

4 files changed

Lines changed: 73 additions & 7 deletions

File tree

vlib/v3/gen/c/cleanc.v

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ mut:
336336
inlined_c_typedef_names map[string]bool
337337
inlined_c_fns map[string]bool
338338
inlined_c_declared_fns map[string]bool
339+
possibly_active_c_macros map[string]bool
339340
inlined_c_static_fns map[string]bool
340341
cache_omitted_c_fns map[string]bool
341342
preserved_header_files_seen map[string]bool
@@ -1028,6 +1029,7 @@ pub fn FlatGen.new() FlatGen {
10281029
inlined_c_structs: map[string]bool{}
10291030
inlined_c_fns: map[string]bool{}
10301031
inlined_c_declared_fns: map[string]bool{}
1032+
possibly_active_c_macros: map[string]bool{}
10311033
inlined_c_static_fns: map[string]bool{}
10321034
cache_omitted_c_fns: map[string]bool{}
10331035
preserved_header_files_seen: map[string]bool{}
@@ -2587,6 +2589,7 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin
25872589
g.inlined_c_structs.clear()
25882590
g.inlined_c_fns.clear()
25892591
g.inlined_c_declared_fns.clear()
2592+
g.possibly_active_c_macros.clear()
25902593
g.inlined_c_static_fns.clear()
25912594
g.cache_omitted_c_fns.clear()
25922595
g.preserved_header_files_seen.clear()
@@ -4646,7 +4649,9 @@ fn (mut g FlatGen) collect_preserved_header_file_with_state_and_scope(path strin
46464649
g.collect_inlined_c_declared_fns(final_scan.text)
46474650
}
46484651
for macro_name in final_scan.possibly_active_macro_names {
4649-
g.inlined_c_declared_fns[macro_name] = true
4652+
if macro_name !in g.inlined_c_declared_fns {
4653+
g.possibly_active_c_macros[macro_name] = true
4654+
}
46504655
}
46514656
result := c_header_macro_state_clone(final_scan.final_state)
46524657
g.preserved_header_scan_results[visit_key] = c_header_macro_state_clone(result)
@@ -4789,7 +4794,7 @@ fn (mut g FlatGen) collect_possibly_active_header_macros(path string, include_di
47894794
scan := c_header_definitely_active_scan(text, state, c_effective_strict_iso_mode(g.c_flags,
47904795
g.c99_mode), g.target)
47914796
for macro_name in scan.possibly_active_macro_names {
4792-
g.inlined_c_declared_fns[macro_name] = true
4797+
g.possibly_active_c_macros[macro_name] = true
47934798
}
47944799
for raw_include_arg in scan.include_args {
47954800
include_arg := c_include_arg(raw_include_arg, g.compiler_vroot, real_path)

vlib/v3/gen/c/fn.v

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14710,7 +14710,8 @@ fn (mut g FlatGen) c_extern_forward_decls() {
1471014710
}
1471114711
specificity := program_decl_priority + c_extern_decl_specificity(g.a, node)
1471214712
if cfn !in decls || specificity > decl_specificity[cfn] {
14713-
decls[cfn] = c_macro_safe_extern_decl(cfn, g.c_extern_decl_line(node, cfn))
14713+
decls[cfn] = g.c_possibly_active_macro_extern_decl(cfn, c_macro_safe_extern_decl(cfn,
14714+
g.c_extern_decl_line(node, cfn)))
1471414715
decl_specificity[cfn] = specificity
1471514716
}
1471614717
}
@@ -14911,6 +14912,17 @@ fn c_macro_safe_extern_decl(cfn string, declaration string) string {
1491114912
return declaration.replace_once('${cfn}(', '(${cfn})(')
1491214913
}
1491314914

14915+
// c_possibly_active_macro_extern_decl keeps a prototype available when a lightweight
14916+
// preinclude scan saw a same-named macro only inside an unresolved preprocessor branch.
14917+
// The real C preprocessor selects exactly one safe form: the macro when it exists, or the
14918+
// declaration when it does not.
14919+
fn (g &FlatGen) c_possibly_active_macro_extern_decl(cfn string, declaration string) string {
14920+
if cfn !in g.possibly_active_c_macros {
14921+
return declaration
14922+
}
14923+
return '#ifndef ${cfn}\n${declaration}\n#endif'
14924+
}
14925+
1491414926
fn (g &FlatGen) should_emit_c_extern_decl(cfn string) bool {
1491514927
if cfn.contains('.') {
1491614928
return false

vlib/v3/gen/c/source_directive_test.v

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ fn test_preserved_header_keeps_conditional_macro_mutations_uncertain() {
233233
assert 'always_active' in g.inlined_c_declared_fns
234234
}
235235

236-
fn test_preserved_header_collects_possibly_active_function_macros() {
236+
fn test_preserved_header_guards_externs_for_possibly_active_function_macros() {
237237
root := os.join_path(os.vtmp_dir(), 'v3_preserved_possible_macro_${os.getpid()}')
238238
os.rmdir_all(root) or {}
239239
os.mkdir_all(root)!
@@ -242,7 +242,7 @@ fn test_preserved_header_collects_possibly_active_function_macros() {
242242
}
243243
header := os.join_path(root, 'api.h')
244244
os.write_file(header,
245-
'#ifdef __GNUC__\n#define compiler_api(x) ((x) + 1)\n#endif\n#if 0\n#define inactive_api(x) (x)\n#endif\n')!
245+
'#ifdef __MSVC_ONLY__\n#define compiler_api(x) ((x) + 1)\n#endif\n#if 0\n#define inactive_api(x) (x)\n#endif\n')!
246246

247247
mut g := FlatGen.new()
248248
g.collect_preserved_header_file_with_state(header, [root], CHeaderMacroState{
@@ -252,8 +252,12 @@ fn test_preserved_header_collects_possibly_active_function_macros() {
252252
external_macros_possible: true
253253
})
254254

255-
assert 'compiler_api' in g.inlined_c_declared_fns
255+
assert 'compiler_api' !in g.inlined_c_declared_fns
256+
assert 'compiler_api' in g.possibly_active_c_macros
257+
assert g.should_emit_c_extern_decl('compiler_api')
258+
assert g.c_possibly_active_macro_extern_decl('compiler_api', 'int compiler_api(int x);') == '#ifndef compiler_api\nint compiler_api(int x);\n#endif'
256259
assert 'inactive_api' !in g.inlined_c_declared_fns
260+
assert 'inactive_api' !in g.possibly_active_c_macros
257261
}
258262

259263
fn test_preserved_header_scans_includes_in_possibly_active_branches_for_macros() {
@@ -273,7 +277,8 @@ fn test_preserved_header_scans_includes_in_possibly_active_branches_for_macros()
273277
mut g := FlatGen.new()
274278
g.collect_preserved_header_file(parent, [root])
275279

276-
assert 'compiler_api' in g.inlined_c_declared_fns
280+
assert 'compiler_api' !in g.inlined_c_declared_fns
281+
assert 'compiler_api' in g.possibly_active_c_macros
277282
assert 'conditionally_declared_api' !in g.inlined_c_declared_fns
278283
assert 'always_declared_api' in g.inlined_c_declared_fns
279284
assert os.real_path(child) in g.preserved_header_files_seen
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import os
2+
3+
fn test_preinclude_uncertain_macro_keeps_c_extern_prototype() {
4+
$if macos || linux {
5+
root := os.join_path(os.vtmp_dir(), 'v3_preinclude_uncertain_macro_${os.getpid()}')
6+
os.rmdir_all(root) or {}
7+
os.mkdir_all(root)!
8+
defer {
9+
os.rmdir_all(root) or {}
10+
}
11+
header := os.join_path(root, 'api.h')
12+
implementation := os.join_path(root, 'api.c')
13+
source := os.join_path(root, 'main.c.v')
14+
output := os.join_path(root, 'main')
15+
os.write_file(header, '#ifdef __MSVC_ONLY__\n#define compiler_api(x) ((x) + 1)\n#endif\n')!
16+
os.write_file(implementation, 'int compiler_api(int x) { return x + 1; }\n')!
17+
os.write_file(source, 'module main
18+
19+
#preinclude "${header}"
20+
#flag ${implementation}
21+
22+
fn C.compiler_api(int) int
23+
24+
fn main() {
25+
assert C.compiler_api(41) == 42
26+
}
27+
')!
28+
mut environment := os.environ()
29+
environment['VFLAGS'] = ''
30+
environment['VOSARGS'] = ''
31+
mut compiler := os.new_process(@VEXE)
32+
compiler.set_args(['-new-compiler', '-gc', 'none', '-o', output, source])
33+
compiler.set_environment(environment)
34+
compiler.set_redirect_stdio()
35+
compiler.run()
36+
compiler.wait()
37+
compiler_output := compiler.stdout_slurp() + compiler.stderr_slurp()
38+
compiler_exit_code := compiler.code
39+
compiler.close()
40+
assert compiler_exit_code == 0, compiler_output
41+
run := os.execute(os.quoted_path(output))
42+
assert run.exit_code == 0, run.output
43+
}
44+
}

0 commit comments

Comments
 (0)