Skip to content

Commit 351709f

Browse files
authored
v3: compile Viper engine games (enum ${}, &T{} heap, generics, interfaces etc) (#27557)
1 parent 6e2d911 commit 351709f

29 files changed

Lines changed: 4804 additions & 783 deletions

vlib/v3/gen/c/array.v

Lines changed: 110 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ fn (mut g FlatGen) gen_array_literal_value(node flat.Node, elem_type types.Type)
5656
if i > 0 {
5757
g.write(', ')
5858
}
59-
g.gen_expr(g.a.child(&node, i))
59+
// Emit each element against the concrete element type, not the enclosing
60+
// `expected_expr_type` (which is the whole array). A bare generic struct element
61+
// (`Box{..}` in a `[]Box[int]` literal) otherwise sees the array type, fails the
62+
// `generic_struct_init_instance_type` array skip, and is emitted as the bare `Box`
63+
// while the array storage is `Box_int` — incompatible C.
64+
g.gen_expr_with_expected_type(g.a.child(&node, i), elem_type)
6065
}
6166
g.write('})')
6267
}
@@ -110,6 +115,15 @@ fn (mut g FlatGen) gen_fixed_array_data_arg(id flat.NodeId, arr types.ArrayFixed
110115
return
111116
}
112117
}
118+
// A fixed-array value (e.g. `[4]u8` color) is sometimes represented as a dynamic
119+
// `Array`; a C fixed-array parameter decays to `elem*`, so pass the data pointer.
120+
if g.tc.resolve_type(id) is types.Array {
121+
elem_ct := g.tc.c_type(arr.elem_type)
122+
g.write('(${elem_ct}*)(')
123+
g.gen_expr(id)
124+
g.write(').data')
125+
return
126+
}
113127
g.gen_expr(id)
114128
}
115129

@@ -321,32 +335,105 @@ fn (mut g FlatGen) gen_array_method_call(node flat.Node, fn_node &flat.Node, arr
321335
g.gen_expr(g.a.child(&node, 1))
322336
g.write(')')
323337
}
324-
else {
325-
best_mname := g.array_method_fallback(fn_node.value)
326-
if best_mname.len > 0 {
327-
g.write(c_name(best_mname))
328-
g.write('(')
329-
ptypes := g.tc.fn_param_types[best_mname]
330-
wants_ptr := ptypes.len > 0 && ptypes[0] is types.Pointer
331-
if wants_ptr && !is_ptr {
332-
g.write('&')
333-
} else if !wants_ptr && is_ptr {
334-
g.write('*')
335-
}
336-
g.gen_expr(base_id)
337-
for i in 1 .. node.children_count {
338-
g.write(', ')
339-
g.gen_expr(g.a.child(&node, i))
340-
}
341-
g.write(')')
338+
'wait' {
339+
// Only a thread array supports `.wait()` (joining every spawned thread and,
340+
// for non-void payloads, collecting their return values into a fresh `[]T`).
341+
// The element carries the thread payload in its name (`thread`/`thread T`).
342+
// Any other element type is not a thread, so route it through the normal
343+
// method fallback instead of joining arbitrary array data as pthread_t handles.
344+
mut is_thread := false
345+
elem := arr.elem_type
346+
if elem is types.Struct {
347+
tn := elem.name.trim_space()
348+
is_thread = tn == 'thread' || tn.starts_with('thread ')
349+
}
350+
if is_thread {
351+
g.gen_thread_array_wait(base_id, is_ptr, arr.elem_type)
342352
} else {
343-
g.gen_expr(g.a.child(&node, 0))
344-
g.write('(')
345-
g.gen_expr(base_id)
346-
g.write(')')
353+
g.gen_array_method_call_fallback(node, fn_node.value, base_id, is_ptr)
347354
}
348355
}
356+
else {
357+
g.gen_array_method_call_fallback(node, fn_node.value, base_id, is_ptr)
358+
}
359+
}
360+
}
361+
362+
// gen_array_method_call_fallback emits a call for an array method that has no dedicated
363+
// codegen arm: it resolves a `[]T.method` function when one is registered, and otherwise
364+
// emits the selector itself as a direct call. Shared by the catch-all `else` arm and by
365+
// `.wait()` on non-thread arrays (which is unsupported and falls through here rather than
366+
// joining elements as thread handles).
367+
fn (mut g FlatGen) gen_array_method_call_fallback(node flat.Node, mname string, base_id flat.NodeId, is_ptr bool) {
368+
best_mname := g.array_method_fallback(mname)
369+
if best_mname.len > 0 {
370+
g.write(c_name(best_mname))
371+
g.write('(')
372+
ptypes := g.tc.fn_param_types[best_mname]
373+
wants_ptr := ptypes.len > 0 && ptypes[0] is types.Pointer
374+
if wants_ptr && !is_ptr {
375+
g.write('&')
376+
} else if !wants_ptr && is_ptr {
377+
g.write('*')
378+
}
379+
g.gen_expr(base_id)
380+
for i in 1 .. node.children_count {
381+
g.write(', ')
382+
g.gen_expr(g.a.child(&node, i))
383+
}
384+
g.write(')')
385+
} else {
386+
g.gen_expr(g.a.child(&node, 0))
387+
g.write('(')
388+
g.gen_expr(base_id)
389+
g.write(')')
390+
}
391+
}
392+
393+
// gen_thread_array_wait emits a call to the (lazily generated) wait function for a
394+
// `[]thread T` receiver. The element type carries the thread's return type in its
395+
// name (`thread T`); a bare `thread` denotes a void payload.
396+
fn (mut g FlatGen) gen_thread_array_wait(base_id flat.NodeId, is_ptr bool, elem_type types.Type) {
397+
mut ret_name := ''
398+
if elem_type is types.Struct {
399+
trimmed := elem_type.name.trim_space()
400+
if trimmed != 'thread' && trimmed.starts_with('thread ') {
401+
ret_name = trimmed[7..].trim_space()
402+
}
403+
}
404+
fn_name := g.ensure_thread_arr_wait_fn(ret_name)
405+
g.write('${fn_name}(')
406+
if is_ptr {
407+
g.write('*')
408+
}
409+
g.gen_expr(base_id)
410+
g.write(')')
411+
}
412+
413+
// ensure_thread_arr_wait_fn registers (once per payload type) a function that joins
414+
// every thread handle in the array and, for a non-void payload, copies each thread's
415+
// heap-returned value into a result `[]T` (freeing the per-thread allocation).
416+
fn (mut g FlatGen) ensure_thread_arr_wait_fn(ret_name string) string {
417+
is_void := ret_name.len == 0
418+
// Match the ABI return type the spawn wrapper stores (gen_spawn_expr): an
419+
// option/result payload is `Optional_T`, a fixed-array payload its `_v_ret_*`
420+
// wrapper — not the bare `c_type`, or the malloc'd and read-back layouts diverge.
421+
ret_ct := if is_void { 'void' } else { g.fn_return_type_name(g.tc.parse_type(ret_name)) }
422+
key := 'threadwait|${ret_ct}'
423+
if name := g.spawn_wrapper_names[key] {
424+
return name
425+
}
426+
// Sanitize the payload C type (`Foo*`, `void*`, ...) into an identifier fragment
427+
// — `c_name` does not strip `*`, so a raw pointer return type would otherwise put
428+
// an asterisk in the helper symbol.
429+
name := c_name('__v_thread_arr_wait_${types.c_type_name_part(ret_ct)}')
430+
g.spawn_wrapper_names[key] = name
431+
if is_void {
432+
g.spawn_wrapper_defs << 'static void ${name}(Array a) { for (int __i = 0; __i < a.len; __i++) { void* __r = NULL; pthread_join((pthread_t)(((void**)a.data)[__i]), &__r); if (__r) free(__r); } }'
433+
} else {
434+
g.spawn_wrapper_defs << 'static Array ${name}(Array a) { Array __res = array_new(sizeof(${ret_ct}), a.len, a.len); for (int __i = 0; __i < a.len; __i++) { void* __r = NULL; pthread_join((pthread_t)(((void**)a.data)[__i]), &__r); if (__r) { ((${ret_ct}*)__res.data)[__i] = *(${ret_ct}*)__r; free(__r); } } return __res; }'
349435
}
436+
return name
350437
}
351438

352439
// array_lookup_suffix supports array lookup suffix handling for c.

0 commit comments

Comments
 (0)