vlang

/

v Public
0 commits 39 issues 0 pull requests 0 contributors Discussions Projects CI

cgen: corrupted C output for heap struct literal (&T{}) init — split identifier emitted at file scope #25

Description

A real-world program produced syntactically corrupted C for a heap struct literal initialization. The generated C splits a type identifier in half (main__Objectmain__Obj + ect) and splices a different element's initialization statements into the middle of an unfinished expression. The corrupted fragment is emitted outside any function (the C compiler reports "not in a function").

The struct is initialized via the all-fields &T{...} heap path (direct_heap_struct_init in vlib/v/gen/c/struct.v), which allocates with builtin___v_malloc(...) and then assigns each field individually. When several such heap initializations are reordered/hoisted (array element or nested element ordering), the statement-splitting bookkeeping (go_before_last_stmt / go_before_ternary) appears to cut at the wrong buffer position.

Generated C (from the bug report)

VV_LOC bool main__truthy(main__Object* obj) {
    return ((obj->kind == (_const_main__kind_always_true))? (true) : /* ... long ternary ... */ (true));
}
    main__Object* _t1 = (main__Obj  main__Object* _t2 = (main__Object*)builtin___v_malloc(sizeof(main__Object) == 0 ? 1 : sizeof(main__Object));
    _t2->kind = _const_main__kind_always_false;
    _t2->ival = 0;
    _t2->len_val = 0;
    ect*)builtin___v_malloc(sizeof(main__Object) == 0 ? 1 : sizeof(main__Ob main__Object* _t3 = (main__Object*)builtin___v_malloc(sizeof(main__Object) == 0 ? 1 : sizeof(main__Object));
    _t3->kind = _const_main__kind_length_based;

The _t1 initializer (main__Object*)builtin___v_malloc(... sizeof(main__Object)) is broken in two: it begins with (main__Obj, the full _t2 block is inserted, and the tail ect*)builtin___v_malloc(... sizeof(main__Ob reappears afterwards.

C compiler error

error: 'main__Obj' undeclared here (not in a function); did you mean 'main__Object'?

Triggering conditions (identified)

  • Several &T{...} heap struct literals with all fields specified (this selects direct_heap_struct_init, the per-field malloc+assign form, instead of the HEAP(T, (T){...}) compound-literal form), and
  • those initializations are reordered/hoisted (array elements and/or nested elements), and
  • the result lands at/near file scope.The struct in the report: kind/ival/len_val integer fields, with kind values being module-level consts (_const_main__kind_*), plus a truthy() method (the long ternary).

Repro status

Not yet reproduced on master (98ea23b). Targeted attempts — array of all-fields &Object{...}, nested child &Object (2–3 levels), and a top-level const array of &Object{...} — all produced correct, well-ordered C on current master (each nested allocation is fully hoisted before use). The exact trigger still needs to be isolated; the original source was not captured by the bug reporter (only the generated-C window above).

Environment

  • V 0.5.1
  • Original report: Linux (cc)
    [!NOTE] You can use the 👍 reaction to increase the issue's priority for developers.Please note that only the 👍 reaction to the issue itself counts as a vote. Other reactions and those to comments will not be taken into account.