Skip to content

Commit e462e2a

Browse files
committed
v2: cleanc/transformer fixes
1 parent e45337e commit e462e2a

16 files changed

Lines changed: 1561 additions & 141 deletions

File tree

‎vlib/gg/gg_darwin.m‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ gg__Size gg_get_screen_size() {
5959
}
6060

6161
void darwin_draw_string(int x, int y, string s, gg__TextCfg cfg) {
62-
NSFont* font = [NSFont userFontOfSize:0]; // cfg.size];
62+
NSFont* font = [NSFont userFontOfSize:cfg.size];
6363
// # NSFont* font = [NSFont fontWithName:@"Roboto Mono" size:cfg.size];
6464
if (cfg.mono) {
6565
// # font = [NSFont fontWithName:@"Roboto Mono" size:cfg.size];

‎vlib/v2/gen/cleanc/cheaders.v‎

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,8 @@ fn (mut g Gen) collect_directives_from_stmts(stmts []ast.Stmt, file_name string,
163163

164164
fn (mut g Gen) collect_directives_from_expr(expr ast.Expr, file_name string, mut seen map[string]bool) {
165165
if expr is ast.ComptimeExpr {
166-
eprintln('[directives-debug] ComptimeExpr in ${file_name}')
167166
g.collect_directives_from_expr(expr.expr, file_name, mut seen)
168167
} else if expr is ast.IfExpr {
169-
eprintln('[directives-debug] IfExpr in ${file_name}, stmts.len=${expr.stmts.len}')
170168
g.collect_directives_from_stmts(expr.stmts, file_name, mut seen)
171169
if expr.else_expr !is ast.EmptyExpr {
172170
g.collect_directives_from_expr(expr.else_expr, file_name, mut seen)
@@ -400,22 +398,8 @@ fn (mut g Gen) emit_runtime_aliases() {
400398
}
401399
g.sb.writeln('typedef array ${name};')
402400
}
403-
// Emit pointer element typedefs needed by array helper functions.
404-
// e.g. Array_Coordptr needs 'typedef Coord* Coordptr;'
405-
// Must come after array aliases so Array_int etc. are defined first.
406-
for name in array_names {
407-
if name.starts_with('Array_fixed_') {
408-
continue
409-
}
410-
elem := name['Array_'.len..]
411-
if elem.len > 3 && elem.ends_with('ptr') {
412-
base := elem[..elem.len - 3]
413-
if base !in ['void', 'char', 'byte'] && !elem.starts_with('Array_')
414-
&& !elem.starts_with('Map_') {
415-
g.sb.writeln('typedef ${base}* ${elem};')
416-
}
417-
}
418-
}
401+
// Pointer element typedefs (e.g. 'typedef Coord* Coordptr;') are deferred
402+
// to emit_pointer_typedefs() which runs after pass 2 (enum/alias definitions).
419403
// Emit primitive fixed array typedefs (non-primitive ones deferred until after struct defs)
420404
for name in array_names {
421405
if !name.starts_with('Array_fixed_') {
@@ -481,6 +465,27 @@ fn (mut g Gen) emit_runtime_aliases() {
481465
}
482466
}
483467

468+
// emit_pointer_typedefs emits pointer element typedefs needed by array helper functions.
469+
// e.g. Array_Coordptr needs 'typedef Coord* Coordptr;'.
470+
// Must run AFTER pass 2 so that enum and type alias definitions are available.
471+
fn (mut g Gen) emit_pointer_typedefs() {
472+
mut array_names := g.array_aliases.keys()
473+
array_names.sort()
474+
for name in array_names {
475+
if name.starts_with('Array_fixed_') {
476+
continue
477+
}
478+
elem := name['Array_'.len..]
479+
if elem.len > 3 && elem.ends_with('ptr') {
480+
base := elem[..elem.len - 3]
481+
if base !in ['void', 'char', 'byte'] && !elem.starts_with('Array_')
482+
&& !elem.starts_with('Map_') {
483+
g.sb.writeln('typedef ${base}* ${elem};')
484+
}
485+
}
486+
}
487+
}
488+
484489
fn (mut g Gen) get_enum_name(node ast.EnumDecl) string {
485490
if g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin' {
486491
return '${g.cur_module}__${node.name}'

‎vlib/v2/gen/cleanc/cleanc.v‎

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ mut:
3030
runtime_local_types map[string]string
3131
cur_fn_returned_idents map[string]bool
3232
active_generic_types map[string]types.Type
33+
// Comptime $for field iteration state
34+
comptime_field_var string // variable name (e.g., 'field')
35+
comptime_field_name string // current field name (e.g., 'id')
36+
comptime_field_type string // current field C type name
37+
comptime_field_raw_type types.Type = types.Struct{} // raw types.Type for comptime checks
38+
comptime_field_attrs []string // current field attributes
39+
comptime_field_idx int // current field index
40+
comptime_val_var string // the struct variable being decoded (e.g., 'val')
41+
comptime_val_type string // C type of val (e.g., 'Slack')
3342

3443
fixed_array_fields map[string]bool
3544
fixed_array_field_elem map[string]string
@@ -76,21 +85,25 @@ mut:
7685
resolved_module_names map[string]string // per-function cache for resolve_module_name
7786
cached_env_scopes map[string]voidptr // cache of env_scope results (avoids repeated locking)
7887

79-
const_exprs map[string]string // const name → C expression string (for inlining)
80-
const_types map[string]string // const name → C type string
81-
runtime_const_targets map[string]bool // module-scoped consts initialized in __v_init_consts_*
82-
used_fn_keys map[string]bool
83-
force_emit_fn_names map[string]bool // function C names that must be emitted regardless of mark_used
84-
export_fn_names map[string]string // V-qualified name → export name (from @[export:] attribute)
85-
called_fn_names map[string]bool
86-
generic_spec_index map[string][]string // fn_name → matching keys in env.generic_types
87-
anon_fn_defs []string // lifted anonymous function definitions
88-
pass5_start_pos int // position in sb where pass 5 starts
89-
deferred_m_includes []string // Objective-C .m file #include lines deferred until after type definitions
90-
spawned_fns map[string]bool // spawn wrapper names already emitted
91-
spawn_wrapper_defs []string // spawn wrapper struct + function definitions
92-
emitted_trampolines map[string]bool // bound method trampoline names already emitted
93-
trampoline_defs []string // bound method trampoline definitions
88+
const_exprs map[string]string // const name → C expression string (for inlining)
89+
const_types map[string]string // const name → C type string
90+
runtime_const_targets map[string]bool // module-scoped consts initialized in __v_init_consts_*
91+
used_fn_keys map[string]bool
92+
force_emit_fn_names map[string]bool // function C names that must be emitted regardless of mark_used
93+
export_fn_names map[string]string // V-qualified name → export name (from @[export:] attribute)
94+
called_fn_names map[string]bool
95+
generic_spec_index map[string][]string // fn_name → matching keys in env.generic_types
96+
late_generic_specs map[string][]map[string]types.Type // additional comptime-discovered specs
97+
anon_fn_defs []string // lifted anonymous function definitions
98+
late_struct_defs []string // struct definitions discovered during pass 5 codegen
99+
pending_late_body_keys map[string]bool // body_keys in late_struct_defs but not yet flushed to g.sb
100+
late_generic_str_instances []string // c_names of late generic struct instances needing str macro check
101+
pass5_start_pos int // position in sb where pass 5 starts
102+
deferred_m_includes []string // Objective-C .m file #include lines deferred until after type definitions
103+
spawned_fns map[string]bool // spawn wrapper names already emitted
104+
spawn_wrapper_defs []string // spawn wrapper struct + function definitions
105+
emitted_trampolines map[string]bool // bound method trampoline names already emitted
106+
trampoline_defs []string // bound method trampoline definitions
94107
// @[live] hot code reloading
95108
live_fns []LiveFnInfo // @[live] functions detected during code generation
96109
live_source_file string // source file containing @[live] functions
@@ -99,10 +112,20 @@ mut:
99112
fn_owner_file map[string]int // fn_key -> first file index (for parallel dedup)
100113
global_owner_file map[string]int // global_name -> first file index (for parallel dedup)
101114
generic_struct_bindings map[string]map[string]types.Type // struct_name -> {T: concrete_type}
102-
c_file_fn_keys map[string]bool // fn_key -> emitted from a .c.v file, so plain .v fallback should be skipped
103-
typedef_c_types map[string]bool // C struct names with @[typedef] attribute (emit without 'struct' prefix)
104-
blocked_fn_keys map[string]bool // worker-only fn keys reserved to other pass5 chunks
105-
cached_vhash string // cached git short hash for @VHASH/@VCURRENTHASH
115+
// Multi-instantiation support: maps base struct C name (e.g. "json2__Node") to
116+
// a list of (suffix, bindings) pairs for each distinct concrete instantiation.
117+
// E.g. [("json2__ValueInfo", {T: ValueInfo}), ("json2__StructFieldInfo", {T: StructFieldInfo})]
118+
generic_struct_instances map[string][]GenericStructInstance
119+
c_file_fn_keys map[string]bool // fn_key -> emitted from a .c.v file, so plain .v fallback should be skipped
120+
typedef_c_types map[string]bool // C struct names with @[typedef] attribute (emit without 'struct' prefix)
121+
blocked_fn_keys map[string]bool // worker-only fn keys reserved to other pass5 chunks
122+
cached_vhash string // cached git short hash for @VHASH/@VCURRENTHASH
123+
}
124+
125+
struct GenericStructInstance {
126+
params_key string // e.g. "json2__ValueInfo" — unique key per instantiation
127+
bindings map[string]types.Type // e.g. {T: ValueInfo}
128+
c_name string // full C struct name, e.g. "json2__Node_T_json2__StructFieldInfo"
106129
}
107130

108131
struct LiveFnInfo {
@@ -534,9 +557,9 @@ pub fn (mut g Gen) gen_passes_1_to_4() {
534557

535558
g.write_preamble()
536559
g.collect_typedef_c_types()
560+
g.collect_generic_struct_bindings()
537561
g.collect_module_type_names()
538562
g.collect_runtime_aliases()
539-
g.collect_generic_struct_bindings()
540563
// Force eventbus generic structs to use T=string binding.
541564
// Without full monomorphization, eventbus methods assume T=string
542565
// (see fn.v hardcoded eventbus workaround).
@@ -552,6 +575,7 @@ pub fn (mut g Gen) gen_passes_1_to_4() {
552575
g.generic_struct_bindings['stdatomic__AtomicVal'] = {
553576
'T': types.Type(types.f64_)
554577
}
578+
g.discover_comptime_generic_specs()
555579
g.collect_fn_signatures()
556580
g.collect_c_file_fn_keys()
557581
g.collect_runtime_const_targets()
@@ -597,6 +621,19 @@ pub fn (mut g Gen) gen_passes_1_to_4() {
597621
g.emitted_types[name] = true
598622
keyword := if stmt.is_union { 'union' } else { 'struct' }
599623
g.sb.writeln('typedef ${keyword} ${name} ${name};')
624+
// Also emit forward declarations for additional generic instances
625+
if stmt.generic_params.len > 0 {
626+
instances := g.generic_struct_instances[name]
627+
for inst in instances {
628+
if inst.c_name == name {
629+
continue
630+
}
631+
if inst.c_name !in g.emitted_types {
632+
g.emitted_types[inst.c_name] = true
633+
g.sb.writeln('typedef ${keyword} ${inst.c_name} ${inst.c_name};')
634+
}
635+
}
636+
}
600637
} else if stmt is ast.TypeDecl {
601638
if stmt.variants.len > 0 {
602639
// Sum type needs forward struct declaration
@@ -676,6 +713,10 @@ pub fn (mut g Gen) gen_passes_1_to_4() {
676713
stage_start = g.mark_cgen_step(stats_enabled, stats_scope, mut stats_sw, stage_start,
677714
'pass 2 type declarations')
678715

716+
// Emit pointer element typedefs (e.g. 'typedef Color* Colorptr;') now that
717+
// enums and type aliases have been defined.
718+
g.emit_pointer_typedefs()
719+
679720
// Pass 3: Full struct definitions (use named struct/union to match forward decls)
680721
// Collect all struct decls, then emit in dependency order
681722
mut all_structs := []StructDeclInfo{}
@@ -756,6 +797,9 @@ pub fn (mut g Gen) gen_passes_1_to_4() {
756797
'pass 3.1 fixed arrays')
757798

758799
// Pass 3.25: Tuple aliases (multiple-return lowering support)
800+
if 'body_array' !in g.emitted_types {
801+
eprintln('WARNING: body_array not emitted before pass 3.25 tuples')
802+
}
759803
g.emit_tuple_aliases()
760804
if g.tuple_aliases.len > 0 {
761805
g.sb.writeln('')
@@ -1088,10 +1132,35 @@ pub fn (mut g Gen) gen_finalize() string {
10881132
g.emit_exported_const_symbols()
10891133

10901134
mut out := ''
1091-
if g.anon_fn_defs.len > 0 || g.spawn_wrapper_defs.len > 0 || g.trampoline_defs.len > 0 {
1135+
// Emit deferred str macros for late-discovered generic struct instances.
1136+
// At this point fn_return_types is fully populated (pass 4 complete).
1137+
for inst_name in g.late_generic_str_instances {
1138+
str_fn := '${inst_name}__str'
1139+
if str_fn !in g.fn_return_types {
1140+
label := '${inst_name}{}'
1141+
g.late_struct_defs << '#define ${inst_name}__str(v) ((string){.str = "${label}", .len = ${label.len}, .is_lit = 1})\n#define ${inst_name}_str(v) ${inst_name}__str(v)\n'
1142+
}
1143+
}
1144+
if g.anon_fn_defs.len > 0 || g.spawn_wrapper_defs.len > 0 || g.trampoline_defs.len > 0
1145+
|| g.late_struct_defs.len > 0 || g.pending_late_body_keys.len > 0 {
10921146
full := g.sb.str()
10931147
mut out_sb := strings.new_builder(full.len + 4096)
10941148
unsafe { out_sb.write_ptr(full.str, g.pass5_start_pos) }
1149+
// Late-discovered generic struct definitions (discovered during setup/pass 4 codegen)
1150+
for def in g.late_struct_defs {
1151+
out_sb.write_string(def)
1152+
}
1153+
// Mark pending late body keys as emitted now that they're in the output.
1154+
for key, _ in g.pending_late_body_keys {
1155+
g.emitted_types[key] = true
1156+
}
1157+
g.pending_late_body_keys = map[string]bool{}
1158+
// Emit any option/result wrappers that were deferred because their payload
1159+
// types were only in late_struct_defs. Temporarily swap sb with out_sb.
1160+
g.sb = out_sb
1161+
g.emit_option_result_structs()
1162+
out_sb = g.sb
1163+
g.sb = strings.new_builder(0)
10951164
mut seen_spawn_defs := map[string]bool{}
10961165
for def in g.spawn_wrapper_defs {
10971166
if def in seen_spawn_defs {
@@ -1263,6 +1332,9 @@ pub fn (g &Gen) new_pass5_worker(file_indices []int, worker_id int) &Gen {
12631332
export_fn_names: g.export_fn_names.clone()
12641333
called_fn_names: g.called_fn_names.clone()
12651334
generic_spec_index: g.generic_spec_index.clone()
1335+
late_generic_specs: g.late_generic_specs.clone()
1336+
generic_struct_bindings: g.generic_struct_bindings.clone()
1337+
generic_struct_instances: g.generic_struct_instances.clone()
12661338
typedef_c_types: g.typedef_c_types.clone()
12671339
// Per-worker mutable state (starts fresh).
12681340
// Each worker gets a unique tmp_counter offset to avoid name collisions

0 commit comments

Comments
 (0)