Skip to content

C codegen: nested or blocks emit invalid C (*(T*) _t.data = ;) when outer call result is discarded #27012

@quaesitor-scientiam

Description

@quaesitor-scientiam

V version

V 0.5.1 1510663 on Windows 11, MSVC backend (-cc msvc).

Description

When a function returning !T (T non-void) is called as a statement (return value discarded) with an or { ... } block whose tail expression is itself an or block on a !void call, the C generator emits an assignment with an empty right-hand side:

*(T*) _t.data = ;

MSVC rejects this with error C2059: syntax error: ';'.

Minimal reproduction

module main

struct Item {
	id int
}

fn get_item() !Item {
	return Item{id: 1}
}

fn do_void() ! {
	return error('boom')
}

fn main() {
	get_item() or {
		do_void() or {
			println('inner: ${err}')
		}
	}
}

Build:

v -cc msvc -keepc repro.v

Result:

repro.exe.tmp.c(8373): error C2059: syntax error: ';'
builder error: msvc error

Generated C (excerpt)

VV_LOC void main__main(void) {
    _result_main__Item _t1 = main__get_item();
    if (_t1.is_error) {
        IError err = _t1.err;
        _result_void _t2 = main__do_void();
        if (_t2.is_error) {
            IError err = _t2.err;
            builtin__println(...);
        ;
        }

        *(main__Item*) _t1.data = ;   // <-- invalid C, empty RHS
    }

 ;
}

The compiler appears to treat the inner or block (a !void expression used as a statement) as a value expression yielding Item, then emits the standard "store the or-block's value into _t1.data" assignment with no RHS because there is no value.

Expected behavior

Either:

  1. No assignment should be emitted at all, since the outer call's result is discarded (the call is used as a statement); the or block exists only for side-effecting error handling, or
  2. A V-level error/warning should reject the program if codegen cannot lower it.

Workarounds

Any of these compile and behave correctly:

  • Use the if x := call() { } else { } form on the outer call instead of or { ... }.
  • Extract the inner do_void() or { ... } into a helper function so the outer or block does not end with a nested or expression.
  • Add a non-or trailing statement inside the outer or block (e.g. return, continue, or another non-or call).

Notes

  • Reproduces on all three C backends I tested (Windows 11):
    • -cc msvcerror C2059: syntax error: ';'
    • -cc tccerror: identifier expected
    • -cc gccerror: expected expression before ';' token
  • Triggered specifically by nesting — a single-level or { do_void() or { ... } } is the smallest shape that hits it. A non-or tail in the outer block (e.g. or { do_void() or { ... }; return }) avoids the bug.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions