Skip to content

Commit 24daad2

Browse files
committed
v3: enforce fastc global declarations
1 parent c2261a1 commit 24daad2

5 files changed

Lines changed: 163 additions & 23 deletions

File tree

vlib/v3/driver/driver.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7325,6 +7325,7 @@ pub fn run(args []string) {
73257325
prefs.ccompiler = effective_c_compiler
73267326
prefs.c99 = c99
73277327
prefs.force_bounds_checking = force_bounds_checking
7328+
prefs.enable_globals = enable_globals_compat
73287329
prefs.user_defines = user_defines
73297330
prefs.compile_values = compile_values.clone()
73307331
prefs.vroot = if pref.has_macos_v3_caller_environment() && prefs.vexe.len > 0 {

vlib/v3/gen/fastc/fastc.v

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ mut:
272272
struct FastcSourceHeader {
273273
module_name string
274274
imports map[string]string
275+
has_globals bool
275276
}
276277

277278
struct FastcSourceFile {
@@ -332,6 +333,7 @@ struct Parser {
332333
constants map[string]string
333334
public_constants map[string]bool
334335
globals map[string]string
336+
public_globals map[string]bool
335337
used_function_names map[string]bool
336338
module_init_calls []string
337339
selfhost bool
@@ -401,15 +403,16 @@ fn generate_source_files(sources []FastcSourceFile, prefs &pref.Preferences) !st
401403
mut constants := map[string]string{}
402404
mut public_constants := map[string]bool{}
403405
mut globals := map[string]string{}
406+
mut public_globals := map[string]bool{}
404407
for source_file in sources {
405408
collect_declared_types(source_file.source, source_file.path,
406409
source_file.header.module_name, prefs, mut declared_types, mut declared_kinds)!
407410
}
408411
for source_file in sources {
409412
collect_constant_names(source_file.source, source_file.path,
410413
source_file.header.module_name, prefs, mut constants, mut public_constants)!
411-
collect_global_names(source_file.source, source_file.path, source_file.header.module_name,
412-
prefs, mut globals)!
414+
collect_global_names(source_file.source, source_file.path, source_file.header, prefs, mut
415+
globals, mut public_globals)!
413416
}
414417
mut functions := map[string]FastcFunctionSignature{}
415418
mut interface_methods := map[string]bool{}
@@ -439,13 +442,13 @@ fn generate_source_files(sources []FastcSourceFile, prefs &pref.Preferences) !st
439442
mut global_types := map[string]string{}
440443
fastc_render_struct_field_defaults(prefs, declared_types, declared_kinds, struct_fields, mut
441444
struct_field_info, functions, constants, public_constants, constant_types, globals,
442-
global_types)!
445+
public_globals, global_types)!
443446
constant_output := fastc_generate_constant_declarations(sources, prefs, declared_types,
444-
declared_kinds, struct_fields, struct_field_info, functions, constants, public_constants, mut
445-
constant_types)!
447+
declared_kinds, struct_fields, struct_field_info, functions, constants, public_constants,
448+
globals, public_globals, mut constant_types)!
446449
global_output := fastc_generate_global_declarations(sources, prefs, declared_types,
447450
declared_kinds, struct_fields, struct_field_info, functions, constants, public_constants,
448-
constant_types, globals, mut global_types)!
451+
constant_types, globals, public_globals, mut global_types)!
449452
for constant_type in constant_types.values() {
450453
fastc_register_composite_type(constant_type, mut composite_types)
451454
}
@@ -472,6 +475,7 @@ fn generate_source_files(sources []FastcSourceFile, prefs &pref.Preferences) !st
472475
constants: constants
473476
public_constants: public_constants
474477
globals: globals
478+
public_globals: public_globals
475479
used_function_names: used_function_names
476480
module_init_calls: module_init_calls
477481
selfhost: prefs.building_v
@@ -687,6 +691,7 @@ fn fastc_resolve_source_files(paths []string, prefs &pref.Preferences) ![]FastcS
687691
header = FastcSourceHeader{
688692
module_name: queued.module_name
689693
imports: header.imports
694+
has_globals: header.has_globals
690695
}
691696
}
692697
sources << FastcSourceFile{
@@ -806,9 +811,26 @@ fn fastc_scan_source_header(source string, path string, prefs &pref.Preferences)
806811
scan.init(file, source)
807812
mut module_name := ''
808813
mut imports := map[string]string{}
814+
mut has_globals := false
809815
mut brace_depth := 0
810816
mut tok := scan.scan()
811817
for tok != .eof {
818+
if module_name == '' && tok == .attribute {
819+
mut attribute_depth := 1
820+
tok = scan.scan()
821+
for attribute_depth > 0 && tok != .eof {
822+
if tok == .name && scan.lit == 'has_globals' {
823+
has_globals = true
824+
}
825+
if tok == .lsbr {
826+
attribute_depth++
827+
} else if tok == .rsbr {
828+
attribute_depth--
829+
}
830+
tok = scan.scan()
831+
}
832+
continue
833+
}
812834
if module_name == '' && tok == .key_module {
813835
tok = scan.scan()
814836
if tok != .name {
@@ -865,6 +887,7 @@ fn fastc_scan_source_header(source string, path string, prefs &pref.Preferences)
865887
return FastcSourceHeader{
866888
module_name: module_name
867889
imports: imports
890+
has_globals: has_globals
868891
}
869892
}
870893

@@ -1077,35 +1100,39 @@ fn fastc_register_constant(module_name string, name string, is_public bool, path
10771100
}
10781101
}
10791102

1080-
fn collect_global_names(source string, path string, module_name string, prefs &pref.Preferences, mut globals map[string]string) ! {
1103+
fn collect_global_names(source string, path string, header FastcSourceHeader, prefs &pref.Preferences, mut globals map[string]string, mut public_globals map[string]bool) ! {
10811104
mut file_set := token.FileSet.new()
10821105
mut file := file_set.add_file(path, source.len)
10831106
file.index_lines(source)
10841107
mut scan := scanner.new_scanner(prefs, .normal)
10851108
scan.init(file, source)
10861109
mut depth := 0
1110+
mut previous_tok := token.Token.unknown
10871111
mut tok := scan.scan()
10881112
for tok != .eof {
10891113
if depth == 0 && tok == .key_global {
1114+
is_public := previous_tok == .key_pub
10901115
tok = scan.scan()
10911116
if tok == .lpar {
10921117
tok = scan.scan()
10931118
mut at_start := true
10941119
for tok != .rpar && tok != .eof {
10951120
if tok == .semicolon {
10961121
at_start = true
1097-
} else if at_start && tok == .name && scan.lit != 'C' {
1098-
key := fastc_global_key(module_name, scan.lit)
1099-
globals[key] = fastc_c_global_name(key)
1122+
} else if at_start && tok == .name {
1123+
if scan.lit != 'C' {
1124+
fastc_register_global(header, scan.lit, is_public, path, prefs, mut
1125+
globals, mut public_globals)!
1126+
}
11001127
at_start = false
11011128
}
11021129
tok = scan.scan()
11031130
}
11041131
continue
11051132
}
11061133
if tok == .name && scan.lit != 'C' {
1107-
key := fastc_global_key(module_name, scan.lit)
1108-
globals[key] = fastc_c_global_name(key)
1134+
fastc_register_global(header, scan.lit, is_public, path, prefs, mut globals, mut
1135+
public_globals)!
11091136
}
11101137
continue
11111138
}
@@ -1114,11 +1141,27 @@ fn collect_global_names(source string, path string, module_name string, prefs &p
11141141
} else if tok == .rcbr && depth > 0 {
11151142
depth--
11161143
}
1144+
previous_tok = tok
11171145
tok = scan.scan()
11181146
}
11191147
}
11201148

1121-
fn fastc_generate_global_declarations(sources []FastcSourceFile, prefs &pref.Preferences, declared_types map[string]bool, declared_kinds map[string]FastcDeclaredTypeKind, struct_fields map[string]map[string]string, struct_field_info map[string][]FastcStructField, functions map[string]FastcFunctionSignature, constants map[string]string, public_constants map[string]bool, constant_types map[string]string, globals map[string]string, mut global_types map[string]string) !FastcGlobalDeclarations {
1149+
fn fastc_register_global(header FastcSourceHeader, name string, is_public bool, path string, prefs &pref.Preferences, mut globals map[string]string, mut public_globals map[string]bool) ! {
1150+
if !prefs.enable_globals && !prefs.building_v && header.module_name != 'builtin'
1151+
&& !header.has_globals {
1152+
return error('use `v -enable-globals ...` to enable globals in ${path}')
1153+
}
1154+
key := fastc_global_key(header.module_name, name)
1155+
if key in globals {
1156+
return error('fastc parser does not support duplicate global `${name}` in ${path}')
1157+
}
1158+
globals[key] = fastc_c_global_name(key)
1159+
if is_public {
1160+
public_globals[key] = true
1161+
}
1162+
}
1163+
1164+
fn fastc_generate_global_declarations(sources []FastcSourceFile, prefs &pref.Preferences, declared_types map[string]bool, declared_kinds map[string]FastcDeclaredTypeKind, struct_fields map[string]map[string]string, struct_field_info map[string][]FastcStructField, functions map[string]FastcFunctionSignature, constants map[string]string, public_constants map[string]bool, constant_types map[string]string, globals map[string]string, public_globals map[string]bool, mut global_types map[string]string) !FastcGlobalDeclarations {
11221165
mut out := strings.new_builder(1024)
11231166
mut initializers := strings.new_builder(1024)
11241167
ordered_sources := fastc_sources_in_dependency_order(sources)!
@@ -1138,6 +1181,7 @@ fn fastc_generate_global_declarations(sources []FastcSourceFile, prefs &pref.Pre
11381181
constants: constants
11391182
public_constants: public_constants
11401183
globals: globals
1184+
public_globals: public_globals
11411185
selfhost: prefs.building_v
11421186
s: scanner.new_scanner(prefs, .normal)
11431187
out: strings.new_builder(0)
@@ -1233,7 +1277,7 @@ fn (mut g Parser) parse_global_declaration(mut out strings.Builder, mut initiali
12331277
g.skip_semicolons()
12341278
}
12351279

1236-
fn fastc_render_struct_field_defaults(prefs &pref.Preferences, declared_types map[string]bool, declared_kinds map[string]FastcDeclaredTypeKind, struct_fields map[string]map[string]string, mut struct_field_info map[string][]FastcStructField, functions map[string]FastcFunctionSignature, constants map[string]string, public_constants map[string]bool, constant_types map[string]string, globals map[string]string, global_types map[string]string) ! {
1280+
fn fastc_render_struct_field_defaults(prefs &pref.Preferences, declared_types map[string]bool, declared_kinds map[string]FastcDeclaredTypeKind, struct_fields map[string]map[string]string, mut struct_field_info map[string][]FastcStructField, functions map[string]FastcFunctionSignature, constants map[string]string, public_constants map[string]bool, constant_types map[string]string, globals map[string]string, public_globals map[string]bool, global_types map[string]string) ! {
12371281
mut type_names := struct_field_info.keys()
12381282
type_names.sort()
12391283
for type_name in type_names {
@@ -1258,6 +1302,7 @@ fn fastc_render_struct_field_defaults(prefs &pref.Preferences, declared_types ma
12581302
constants: constants
12591303
public_constants: public_constants
12601304
globals: globals
1305+
public_globals: public_globals
12611306
selfhost: prefs.building_v
12621307
s: scanner.new_scanner(prefs, .normal)
12631308
out: strings.new_builder(0)
@@ -1292,7 +1337,7 @@ fn fastc_render_struct_field_defaults(prefs &pref.Preferences, declared_types ma
12921337
}
12931338
}
12941339

1295-
fn fastc_generate_constant_declarations(sources []FastcSourceFile, prefs &pref.Preferences, declared_types map[string]bool, declared_kinds map[string]FastcDeclaredTypeKind, struct_fields map[string]map[string]string, struct_field_info map[string][]FastcStructField, functions map[string]FastcFunctionSignature, constants map[string]string, public_constants map[string]bool, mut constant_types map[string]string) !FastcConstantDeclarations {
1340+
fn fastc_generate_constant_declarations(sources []FastcSourceFile, prefs &pref.Preferences, declared_types map[string]bool, declared_kinds map[string]FastcDeclaredTypeKind, struct_fields map[string]map[string]string, struct_field_info map[string][]FastcStructField, functions map[string]FastcFunctionSignature, constants map[string]string, public_constants map[string]bool, globals map[string]string, public_globals map[string]bool, mut constant_types map[string]string) !FastcConstantDeclarations {
12961341
mut values := []FastcConstantValue{}
12971342
ordered_sources := fastc_sources_in_dependency_order(sources)!
12981343
for source_file in ordered_sources {
@@ -1310,6 +1355,8 @@ fn fastc_generate_constant_declarations(sources []FastcSourceFile, prefs &pref.P
13101355
struct_field_info: struct_field_info
13111356
constants: constants
13121357
public_constants: public_constants
1358+
globals: globals
1359+
public_globals: public_globals
13131360
selfhost: prefs.building_v
13141361
s: scanner.new_scanner(prefs, .normal)
13151362
out: strings.new_builder(256)
@@ -9931,12 +9978,20 @@ fn (g &Parser) expression_name(previous token.Token, qualified_name_owner string
99319978
if constant_key in g.constants && constant_key !in g.public_constants {
99329979
return g.unsupported('private constant `${g.lit}` from imported module `${imported_module}`')
99339980
}
9981+
g.validate_imported_global_visibility(imported_module, g.lit)!
99349982
}
99359983
}
99369984
g.validate_expression_name(g.lit, previous)!
99379985
return g.resolved_expression_name(g.lit, previous)
99389986
}
99399987

9988+
fn (g &Parser) validate_imported_global_visibility(imported_module string, name string) ! {
9989+
global_key := fastc_global_key(imported_module, name)
9990+
if global_key in g.globals && global_key !in g.public_globals {
9991+
return g.unsupported('private global `${name}` from imported module `${imported_module}`')
9992+
}
9993+
}
9994+
99409995
fn (g &Parser) resolved_expression_name(name string, previous token.Token) string {
99419996
if previous != .dot && name == 'C' {
99429997
return ''
@@ -10823,9 +10878,9 @@ fn (g &Parser) infer_expression_type(tokens []FastcExpressionToken) !string {
1082310878
if fastc_constant_key(imported_module, tokens[start + 2].lit) in g.constants {
1082410879
return 'integer literal'
1082510880
}
10826-
if global_type := g.global_types[fastc_global_key(imported_module,
10827-
tokens[start + 2].lit)]
10828-
{
10881+
global_name := tokens[start + 2].lit
10882+
if global_type := g.global_types[fastc_global_key(imported_module, global_name)] {
10883+
g.validate_imported_global_visibility(imported_module, global_name)!
1082910884
return global_type
1083010885
}
1083110886
}

vlib/v3/gen/fastc/fastc_test.v

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,79 @@ fn test_duplicate_constant_declarations_are_rejected() {
188188
}
189189
}
190190

191+
fn test_global_declarations_require_enable_globals_or_module_attribute() {
192+
prefs := pref.new_preferences()
193+
mut message := ''
194+
_ := generate('module main\n__global answer = 42\nfn main() {}\n', 'plain_global.v', prefs) or {
195+
message = err.msg()
196+
''
197+
}
198+
assert message.contains('use `v -enable-globals ...` to enable globals'), message
199+
200+
attributed_source := generate('@[has_globals]\nmodule main\n__global answer = 42\nfn main() {}\n',
201+
'attributed_global.v', prefs) or { panic(err) }
202+
assert attributed_source.contains('static int answer;'), attributed_source
203+
204+
mut enabled_prefs := pref.new_preferences()
205+
enabled_prefs.enable_globals = true
206+
enabled_source := generate('module main\n__global answer = 42\nfn main() {}\n',
207+
'enabled_global.v', enabled_prefs) or { panic(err) }
208+
assert enabled_source.contains('static int answer;'), enabled_source
209+
}
210+
211+
fn test_duplicate_global_declarations_are_rejected() {
212+
mut prefs := pref.new_preferences()
213+
prefs.enable_globals = true
214+
mut message := ''
215+
_ := generate('module main\n__global answer = 1\n__global answer = 2\nfn main() {}\n',
216+
'duplicate_global.v', prefs) or {
217+
message = err.msg()
218+
''
219+
}
220+
assert message.contains('duplicate global `answer`'), message
221+
}
222+
223+
fn test_generate_files_rejects_private_imported_globals() {
224+
root := os.join_path(os.vtmp_dir(), 'v3_fastc_private_global_${os.getpid()}')
225+
os.rmdir_all(root) or {}
226+
os.mkdir_all(os.join_path(root, 'secrets')) or { panic(err) }
227+
defer {
228+
os.rmdir_all(root) or {}
229+
}
230+
main_file := os.join_path(root, 'main.v')
231+
module_file := os.join_path(root, 'secrets', 'secrets.v')
232+
os.write_file(main_file, 'module main\nimport secrets\nfn main() { println(secrets.secret) }\n') or {
233+
panic(err)
234+
}
235+
os.write_file(module_file, 'module secrets\n__global secret = 42\n') or { panic(err) }
236+
mut prefs := pref.new_preferences()
237+
prefs.enable_globals = true
238+
prefs.module_search_paths = [root]
239+
mut message := ''
240+
_ := generate_files([main_file], prefs) or {
241+
message = err.msg()
242+
''
243+
}
244+
assert message.contains('private global `secret` from imported module `secrets`'), message
245+
os.write_file(main_file,
246+
'module main\nimport secrets\nconst copied = secrets.secret\nfn main() { println(copied) }\n') or {
247+
panic(err)
248+
}
249+
message = ''
250+
_ := generate_files([main_file], prefs) or {
251+
message = err.msg()
252+
''
253+
}
254+
assert message.contains('private global `secret` from imported module `secrets`'), message
255+
256+
os.write_file(module_file, 'module secrets\npub __global secret = 42\n') or { panic(err) }
257+
os.write_file(main_file, 'module main\nimport secrets\nfn main() { println(secrets.secret) }\n') or {
258+
panic(err)
259+
}
260+
c_source := generate_files([main_file], prefs) or { panic(err) }
261+
assert c_source.contains('println(secrets__secret);'), c_source
262+
}
263+
191264
fn test_generate_files_rejects_private_imported_types() {
192265
root := os.join_path(os.vtmp_dir(), 'v3_fastc_private_types_${os.getpid()}')
193266
os.rmdir_all(root) or {}
@@ -573,7 +646,8 @@ fn main() {
573646
}
574647

575648
fn test_initialized_global_value_is_emitted() {
576-
prefs := pref.new_preferences()
649+
mut prefs := pref.new_preferences()
650+
prefs.enable_globals = true
577651
c_source := generate('module main
578652
579653
__global answer = 42
@@ -589,7 +663,8 @@ fn main() {
589663
}
590664

591665
fn test_script_main_initializes_globals_before_statements() {
592-
prefs := pref.new_preferences()
666+
mut prefs := pref.new_preferences()
667+
prefs.enable_globals = true
593668
c_source := generate('module main
594669
595670
__global answer = 42
@@ -617,7 +692,8 @@ fn test_runtime_constants_are_materialized_exactly_once() {
617692
defer {
618693
os.rmdir_all(root) or {}
619694
}
620-
prefs := pref.new_preferences()
695+
mut prefs := pref.new_preferences()
696+
prefs.enable_globals = true
621697
c_source := generate('module main
622698
623699
__global calls int
@@ -726,6 +802,7 @@ pub fn current() int {
726802
panic(err)
727803
}
728804
mut prefs := pref.new_preferences()
805+
prefs.enable_globals = true
729806
prefs.module_search_paths = [root]
730807
c_source := generate_files([main_file], prefs) or { panic(err) }
731808
dependency_initializer := c_source.index('dep__current_value = 42;') or { -1 }
@@ -784,6 +861,7 @@ pub fn value() int {
784861
panic(err)
785862
}
786863
mut prefs := pref.new_preferences()
864+
prefs.enable_globals = true
787865
prefs.module_search_paths = [root]
788866
c_source := generate_files([main_file], prefs) or { panic(err) }
789867
main_source := c_source.all_after('int main(void) {')

vlib/v3/pref/pref.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub mut:
3535
ccompiler string = 'gcc'
3636
c99 bool
3737
force_bounds_checking bool
38+
enable_globals bool
3839
vroot string = detect_vroot()
3940
vexe string = detect_vexe()
4041
vhash string

0 commit comments

Comments
 (0)