Skip to content

Commit 1072dfb

Browse files
committed
v3: preserve fastc imports and comptime types
1 parent 53d2f25 commit 1072dfb

2 files changed

Lines changed: 184 additions & 21 deletions

File tree

vlib/v3/gen/fastc/fastc.v

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,10 @@ mut:
270270
}
271271

272272
struct FastcSourceHeader {
273-
module_name string
274-
imports map[string]string
275-
has_globals bool
273+
module_name string
274+
imports map[string]string
275+
blank_imports []string
276+
has_globals bool
276277
}
277278

278279
struct FastcSourceFile {
@@ -377,7 +378,7 @@ mut:
377378
// error; FastC never retries through an AST-based backend.
378379
pub fn generate(source string, path string, prefs &pref.Preferences) !string {
379380
header := fastc_scan_source_header(source, path, prefs)!
380-
if header.imports.len > 0 {
381+
if header.imports.len > 0 || header.blank_imports.len > 0 {
381382
return error('fastc parser does not support imports through the single-source API in ${path}')
382383
}
383384
return generate_source_files([
@@ -704,9 +705,10 @@ fn fastc_resolve_source_files(paths []string, prefs &pref.Preferences) ![]FastcS
704705
mut header := fastc_scan_source_header(source, path, prefs)!
705706
if queued.module_name != '' {
706707
header = FastcSourceHeader{
707-
module_name: queued.module_name
708-
imports: header.imports
709-
has_globals: header.has_globals
708+
module_name: queued.module_name
709+
imports: header.imports
710+
blank_imports: header.blank_imports
711+
has_globals: header.has_globals
710712
}
711713
}
712714
sources << FastcSourceFile{
@@ -715,7 +717,7 @@ fn fastc_resolve_source_files(paths []string, prefs &pref.Preferences) ![]FastcS
715717
header: header
716718
}
717719
mut discovered_imports := map[string]bool{}
718-
for imported_module in header.imports.values() {
720+
for imported_module in fastc_header_imported_modules(header) {
719721
if discovered_imports[imported_module] {
720722
continue
721723
}
@@ -741,6 +743,12 @@ fn fastc_resolve_source_files(paths []string, prefs &pref.Preferences) ![]FastcS
741743
return sources
742744
}
743745

746+
fn fastc_header_imported_modules(header FastcSourceHeader) []string {
747+
mut modules := header.imports.values()
748+
modules << header.blank_imports
749+
return modules
750+
}
751+
744752
fn fastc_sources_in_dependency_order(sources []FastcSourceFile) ![]FastcSourceFile {
745753
mut module_order := []string{}
746754
for source_file in sources {
@@ -827,8 +835,7 @@ fn fastc_append_module_sources(module_name string, sources []FastcSourceFile, mu
827835
if source_file.header.module_name != module_name {
828836
continue
829837
}
830-
imports := source_file.header.imports.clone()
831-
for dependency in imports.values() {
838+
for dependency in fastc_header_imported_modules(source_file.header) {
832839
if dependency != module_name && dependency !in dependencies {
833840
dependencies << dependency
834841
}
@@ -861,6 +868,7 @@ fn fastc_scan_source_header(source string, path string, prefs &pref.Preferences)
861868
scan.init(file, source)
862869
mut module_name := ''
863870
mut imports := map[string]string{}
871+
mut blank_imports := []string{}
864872
mut has_globals := false
865873
mut brace_depth := 0
866874
mut tok := scan.scan()
@@ -913,7 +921,8 @@ fn fastc_scan_source_header(source string, path string, prefs &pref.Preferences)
913921
}
914922
import_path, alias, selected_names, next_token :=
915923
fastc_scan_import(mut scan, tok, path)!
916-
fastc_register_import_alias(import_path, alias, path, mut imports)!
924+
fastc_register_import_alias(import_path, alias, path, mut imports, mut
925+
blank_imports)!
917926
fastc_register_selective_imports(import_path, selected_names, path, mut imports)!
918927
tok = next_token
919928
}
@@ -923,7 +932,7 @@ fn fastc_scan_source_header(source string, path string, prefs &pref.Preferences)
923932
continue
924933
}
925934
import_path, alias, selected_names, next_token := fastc_scan_import(mut scan, tok, path)!
926-
fastc_register_import_alias(import_path, alias, path, mut imports)!
935+
fastc_register_import_alias(import_path, alias, path, mut imports, mut blank_imports)!
927936
fastc_register_selective_imports(import_path, selected_names, path, mut imports)!
928937
tok = next_token
929938
}
@@ -935,18 +944,21 @@ fn fastc_scan_source_header(source string, path string, prefs &pref.Preferences)
935944
imports['driver'] = imports['fastcdriver']
936945
}
937946
return FastcSourceHeader{
938-
module_name: module_name
939-
imports: imports
940-
has_globals: has_globals
947+
module_name: module_name
948+
imports: imports
949+
blank_imports: blank_imports
950+
has_globals: has_globals
941951
}
942952
}
943953

944-
fn fastc_register_import_alias(import_path string, alias string, path string, mut imports map[string]string) ! {
945-
if alias != '_' {
946-
if existing_module := imports[alias] {
947-
if existing_module != import_path {
948-
return error('fastc parser cannot reuse import alias `${alias}` for `${import_path}` after `${existing_module}` in ${path}')
949-
}
954+
fn fastc_register_import_alias(import_path string, alias string, path string, mut imports map[string]string, mut blank_imports []string) ! {
955+
if alias == '_' {
956+
blank_imports << import_path
957+
return
958+
}
959+
if existing_module := imports[alias] {
960+
if existing_module != import_path {
961+
return error('fastc parser cannot reuse import alias `${alias}` for `${import_path}` after `${existing_module}` in ${path}')
950962
}
951963
}
952964
imports[alias] = import_path
@@ -1024,6 +1036,19 @@ fn collect_declared_types(source string, path string, module_name string, prefs
10241036
mut previous_tok := token.Token.unknown
10251037
mut tok := scan.scan()
10261038
for tok != .eof {
1039+
if brace_depth == 0 && tok == .dollar {
1040+
mut lookahead := scan
1041+
if lookahead.scan() == .key_if {
1042+
selected := fastc_scan_selected_comptime_branch(mut scan, scan.scan(), path, prefs)!
1043+
if selected.source != '' {
1044+
collect_declared_types(selected.source, path, module_name, prefs, mut
1045+
declared_types, mut declared_kinds)!
1046+
}
1047+
tok = selected.tok
1048+
previous_tok = .unknown
1049+
continue
1050+
}
1051+
}
10271052
if brace_depth == 0 && tok == .attribute {
10281053
mut attribute_depth := 1
10291054
mut is_typedef := false
@@ -1760,6 +1785,26 @@ fn fastc_emit_source_type_declarations(source_file FastcSourceFile, prefs &pref.
17601785
mut next_enum_is_flag := false
17611786
mut tok := scan.scan()
17621787
for tok != .eof {
1788+
if depth == 0 && tok == .dollar {
1789+
mut lookahead := scan
1790+
if lookahead.scan() == .key_if {
1791+
selected := fastc_scan_selected_comptime_branch(mut scan, scan.scan(),
1792+
source_file.path, prefs)!
1793+
if selected.source != '' {
1794+
selected_source := FastcSourceFile{
1795+
path: source_file.path
1796+
source: selected.source
1797+
header: source_file.header
1798+
}
1799+
fastc_emit_source_type_declarations(selected_source, prefs, declared_types,
1800+
declared_kinds, constants, public_constants, mut struct_fields, mut
1801+
struct_field_info, mut composite_types, mut out)!
1802+
}
1803+
tok = selected.tok
1804+
next_enum_is_flag = false
1805+
continue
1806+
}
1807+
}
17631808
if depth == 0 && tok == .attribute {
17641809
tok, next_enum_is_flag = fastc_scan_type_attribute(mut scan)!
17651810
continue
@@ -3128,6 +3173,18 @@ fn collect_interface_method_signatures(source string, path string, header FastcS
31283173
mut tok := scan.scan()
31293174
mut depth := 0
31303175
for tok != .eof {
3176+
if depth == 0 && tok == .dollar {
3177+
mut lookahead := scan
3178+
if lookahead.scan() == .key_if {
3179+
selected := fastc_scan_selected_comptime_branch(mut scan, scan.scan(), path, prefs)!
3180+
if selected.source != '' {
3181+
collect_interface_method_signatures(selected.source, path, header, prefs,
3182+
declared_types, mut functions, mut interface_methods)!
3183+
}
3184+
tok = selected.tok
3185+
continue
3186+
}
3187+
}
31313188
if depth != 0 || tok != .key_interface {
31323189
if tok == .lcbr {
31333190
depth++

vlib/v3/gen/fastc/fastc_test.v

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,64 @@ fn test_generate_files_resolves_modules_without_an_ast() {
118118
assert run_result.output.trim_space() == '42'
119119
}
120120

121+
fn test_generate_files_preserves_all_blank_imports() {
122+
root := os.join_path(os.vtmp_dir(), 'v3_fastc_blank_imports_${os.getpid()}')
123+
os.rmdir_all(root) or {}
124+
os.mkdir_all(os.join_path(root, 'alpha')) or { panic(err) }
125+
os.mkdir_all(os.join_path(root, 'beta')) or { panic(err) }
126+
defer {
127+
os.rmdir_all(root) or {}
128+
}
129+
main_file := os.join_path(root, 'main.v')
130+
os.write_file(main_file, "module main
131+
132+
import alpha as _
133+
import beta as _
134+
135+
fn main() {
136+
println('main')
137+
}
138+
") or {
139+
panic(err)
140+
}
141+
os.write_file(os.join_path(root, 'alpha', 'alpha.v'), "module alpha
142+
143+
fn init() {
144+
println('alpha init')
145+
}
146+
") or {
147+
panic(err)
148+
}
149+
os.write_file(os.join_path(root, 'beta', 'beta.v'), "module beta
150+
151+
fn init() {
152+
println('beta init')
153+
}
154+
") or {
155+
panic(err)
156+
}
157+
mut prefs := pref.new_preferences()
158+
prefs.module_search_paths = [root]
159+
header := fastc_scan_source_header(os.read_file(main_file) or { panic(err) }, main_file, prefs) or {
160+
panic(err)
161+
}
162+
assert header.blank_imports == ['alpha', 'beta']
163+
assert '_' !in header.imports
164+
c_source := generate_files([main_file], prefs) or { panic(err) }
165+
assert c_source.contains('\talpha__init();'), c_source
166+
assert c_source.contains('\tbeta__init();'), c_source
167+
168+
c_file := os.join_path(root, 'program.c')
169+
bin_file := os.join_path(root, 'program')
170+
os.write_file(c_file, c_source) or { panic(err) }
171+
tcc := os.join_path(prefs.vroot, 'thirdparty', 'tcc', 'tcc.exe')
172+
compile_result := cmdexec.run(tcc, ['-std=gnu11', '-o', bin_file, c_file])
173+
assert compile_result.exit_code == 0, compile_result.output
174+
run_result := cmdexec.run(bin_file, [])
175+
assert run_result.exit_code == 0, run_result.output
176+
assert run_result.output.trim_space() == 'alpha init\nbeta init\nmain'
177+
}
178+
121179
fn test_generate_files_rejects_private_imported_functions() {
122180
root := os.join_path(os.vtmp_dir(), 'v3_fastc_private_import_${os.getpid()}')
123181
os.rmdir_all(root) or {}
@@ -679,6 +737,54 @@ fn main() {
679737
assert !c_source.contains('return "wrong";'), c_source
680738
}
681739

740+
fn test_selected_top_level_comptime_types_are_collected_and_emitted() {
741+
mut prefs := pref.new_preferences()
742+
prefs.building_v = true
743+
prefs.target = pref.target_from('linux', pref.host_arch()) or { panic(err) }
744+
c_source := generate('module main
745+
746+
$if windows {
747+
struct Choice {
748+
wrong bool
749+
}
750+
} $else $if linux {
751+
struct Choice {
752+
value int
753+
}
754+
755+
enum Mode {
756+
selected
757+
}
758+
759+
type ChoiceId = int
760+
761+
union Payload {
762+
number int
763+
}
764+
765+
interface Named {
766+
name() string
767+
}
768+
}
769+
770+
fn main() {
771+
choice := Choice{
772+
value: 42
773+
}
774+
println(choice.value)
775+
}
776+
',
777+
'top_level_comptime_types.v', prefs) or { panic(err) }
778+
assert c_source.contains('struct Choice {\n\tint value;'), c_source
779+
assert !c_source.contains('bool wrong;'), c_source
780+
assert c_source.contains('#define Mode__selected ((Mode)0)'), c_source
781+
assert c_source.contains('typedef int ChoiceId;'), c_source
782+
assert c_source.contains('union Payload {\n\tint number;'), c_source
783+
assert c_source.contains('struct Named { void *_object; u32 _typ; void *_methods; };'), c_source
784+
assert c_source.contains('Named_name(Named value) {'), c_source
785+
assert c_source.contains('__typeof__(((Choice){.value=(42)})) choice'), c_source
786+
}
787+
682788
fn test_initialized_global_value_is_emitted() {
683789
mut prefs := pref.new_preferences()
684790
prefs.enable_globals = true

0 commit comments

Comments
 (0)