@@ -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