Skip to content

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

@medvednikov

Description

@medvednikov

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugs.vlang.ioReported via the bugs.vlang.io crash reporter

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions