Build modes, C interop, Vinix · Time Your Own Number · Read the Prod Contract · Redeclare, Then Call · Write the Verdict
Time your own number. Read the prod contract. Redeclare, then call. Write the verdict.
Time your own number. Read the prod contract. Redeclare, then call. Write the verdict.
§I. Frame
Asr #5 closes the V bridge week. Four lessons covered the toolchain, the Go-shaped rules, structs and sum types, and memory with concurrency. Today asks the question a reviewer asks on day five: what does a production build change, how does V talk to C, and would we ever put it next to Rust?
Learn-only. Nothing here is adopted into a Hedronite product. Pin: vlang/v tag 0.5.2, commit 7647ce1. The lab Mac (macOS 27.0.1, arm64) with the prebuilt V at ~/src/vlang-duha6/v/v, called by absolute path. No source builds, no v up, no Vinix clone.
Auto-upload note (every V lesson carries it). V 0.5.2 sends a report to bugs.vlang.io whenever the C compiler fails on generated code. A report holds up to 40 V source lines on each side of the failing line, the C context, and the build options. v help build-c documents this; doc/docs.md does not. Turn it off with export V_C_ERROR_BUG_REPORT_DISABLED=1 before any v call. Both lab scripts set it first. Today's lab hit one real C compile error (technique 3), and nothing was uploaded.
§II. Four techniques
- Time Your Own Number. Build one program in dev mode and with
-prod, then report what you measured, never what you were told. - Read the Prod Contract.
-prodchanges what the program does: warnings become errors and asserts disappear. - Redeclare, Then Call. V has no C parser. You write the C signatures down on the V side, and the C compiler checks the rest.
- Write the Verdict. Three facts, three claims, three lines against Rust.
§III. Time Your Own Number
The seed for this session said a production build is about 150 times slower than a dev build. I looked for that figure in README.md and doc/docs.md at the tag and in the text of the compare page. It is in none of them, so it stays unverified and unused. Here is what the lab measured instead.
The program (lab/01_work.v) finds the longest Collatz chain below three million. It is pure integer work, so the build mode shows up in the run time.
v -o w_dev 01_work.v build 0.22 s run 2.00 s binary 169,000 B
v -prod -o w_prod 01_work.v build 0.51 s run 0.32 s binary 126,520 B
v -cc tcc -o w_tcc 01_work.v build 0.17 s run 2.11 s (one run each)
Dev and prod rows are the median of three runs; the first build of each was slower (0.55 s and 1.05 s) probably from cold caches. The prod build cost about 2.3 times the dev build here. The prod binary ran about 6 times faster and came out about a quarter smaller. One machine, one program, one afternoon: a data point, not a benchmark.
Two details surprised me. First, README says tcc is the compiler for development builds. On this Mac the plain build ran cc (Apple clang, no -O flag), and -showcc printed the command. The bundled thirdparty/tcc/tcc.exe exists and -cc tcc worked, so the default choice is something I saw, not something I traced to a cause. Second, the dev binary running at tcc speed tells you what "dev mode" means in V: compile fast, run slow, fix later.
Last, the README compile-speed claim (about 110k lines per second with a Clang backend, about 500k with native and tcc backends) is an author claim, measured on an Intel i5-7500 per the README itself. Today's program is 20 lines, so it says nothing about it.
§IV. Read the Prod Contract
Two rules from doc/docs.md changed program behavior in the lab.
Warnings become errors. The docs say an unused variable is a warning in development and, with -prod, "it will not compile at all (like in Go)". The lab confirmed it on a three-line file:
v 02_unused.v -> warning: unused variable: `a` (exit 0)
v -prod 02_unused.v -> error: unused variable: `a` (exit 1)
The docs add that deprecated functions behave the same way: warnings in dev, errors under -prod. This is the cleanest idea in the build story. The dev loop stays quiet and the release gate stays strict.
Asserts vanish. The docs say all assert statements are removed under -prod. 03_assert.v asserts x == 2 when x is 1:
v run 03_assert.v -> FAIL: assert x == 2, left value x = 1, V panic (exit 1)
v -prod run 03_assert.v -> assert did not stop me (exit 0)
The emitted C agrees: 01_work.v has one assert, and the dev C file has 18 lines containing the word assert while the prod C file has none. The rule is safe if you treat asserts as programmer checks only, which is what the docs ask. It is a trap if a check you cared about lives in an assert. In Rust, assert! stays in release builds and only debug_assert! drops out. Mapping a V assert onto Rust's assert! would be a wrong guess.
The performance section of the docs lists tuning switches (@[inline], @[direct_array_access], -fast-math, PGO) and warns they are rarely needed and should follow profiling. Note the drawback column for @[direct_array_access]: "Safety risks". That is the bounds-check opt-out from Asr #2.
§V. Redeclare, Then Call
Calling C from V. The docs are blunt: V has no parser for C code. An #include is copied into the generated C and V learns nothing from it. You redeclare each function you want, with only the fields you need. The lab library is one header, mylib.h, with a single inline function. First attempt, 04_c_interop.v:
#include "mylib.h"
#flag -I @VMODROOT
fn C.add_twice(a int, b int) int
fn C.sqrt(x f64) f64
Run 1 failed in V itself: @VMODROOT needs a v.mod file in a parent folder. I added one. Run 2 reached the C compiler and failed there: call to undeclared library function 'sqrt'. I had redeclared sqrt for V but never included <math.h> for C. That is exactly the failure the docs describe, and it is the kind of error V 0.5.2 would upload to bugs.vlang.io, with my source lines inside, had the opt-out not been set. Run 3 added #include <math.h>:
add_twice(3,4) = 14
sqrt(2) via libc = 1.4142135623730951
Read this as a feel for the boundary. V checks your redeclaration against nothing. If the signature you wrote disagrees with the header, the C compiler is the only judge, and V reports its message. The compare page claims "zero cost C interop"; the call site is a plain C call in the generated file, which fits that claim, but I did not benchmark it.
Calling V from C. The docs say: v -o file.c your_file.v and then use the C. In the lab, @[export: 'v_triple'] on a function produced VV_EXP int v_triple(int x); and an int v_triple(int x) definition in the emitted C. That proves the symbol exists in the output. I did not link a C main against it. The repo's examples/call_v_from_c shows the shared-library route; I did not run it.
Translating C to V. v translate test.c is documented, and the docs say it uses Clang's AST, so Clang must be installed. Clang is here, but I did not run it: the translator may fetch or build tooling on first use, and the lab rule forbids that. Not run.
§VI. Weigh Vinix
The README says Vinix "is an OS/kernel that already runs bash, GCC, V, and nano". That sentence is the whole pinned evidence. The README does not use the words "alpha" or "pre-alpha", and I did not clone the Vinix repository, so I cannot grade its maturity. What the sentence supports: someone built a kernel in V that hosts real programs. What it does not support: any claim about stability, hardware coverage, or security. The Vinix repo was not read today.
This is the reason Vinix matters to us as a watch item. Evan's Voenix drafts already name it as the base for a future distro layer. A language that can write a kernel has a story for the low end of the stack. One README line is thin ground for building on, and the next step is a read-only look at the Vinix repo, which is not today's work.
§VII. Lab
Scratch dir ~/src/vlang-asr5-lab/ on the lab Mac; copy in lab/.
| File | What it showed |
|---|---|
01_work.v | dev, prod and tcc build and run numbers; -showcc command lines |
02_unused.v | unused variable: warning in dev, error in -prod |
03_assert.v | assert fires in dev, vanishes in -prod |
04_c_interop.v, mylib.h, v.mod | C call from V; two real failures on the way (module root, missing include) |
05_call_v_from_c.v | @[export] symbol in emitted C |
lab.sh, lab2.sh, lab.log, lab2.log, lab.run1.log | scripts and output; each exports V_C_ERROR_BUG_REPORT_DISABLED=1 first; every command wrapped in a 60 s alarm |
Not run: v translate, the call_v_from_c example, any Vinix build, v3_ownership.
§VIII. Contributor note
Two small things from today's lab are worth a contributor's check at the tag. One: the default C compiler for dev builds on macOS arm64 was cc, while README says tcc for development. Two: the missing-header failure in 04 prints a long generic V banner after the C error. Neither is a bug claim. Both are questions worth asking in an issue search before anyone files anything. Duha #9 covers the contributor checklist; no upstream PR is opened from this lesson.
§IX. Common mistakes
- Treating
-prodas a pure speed flag, then shipping with anassertthat guarded something real. - Leaving an unused variable in a branch and finding out at release time.
- Calling a C function from V without the C
#includethat declares it. - Repeating a build-time ratio from a chat message instead of measuring it.
- Reading "runs bash, GCC, V, and nano" as proof of maturity.
- Running V labs without
V_C_ERROR_BUG_REPORT_DISABLED=1.
§X. Watch-not-adopt verdict: V next to Rust
Facts from the pinned sources and today's lab:
-prodturns unused-variable warnings into errors and removesassert(docs.md, lab 02 and 03).- Interop is by redeclaration; V has no C parser and the C compiler is the only checker of your signatures (docs.md, lab 04).
- The ownership system is optional, string-focused, "inspired by Rust", and behind
-ownership(ownership.md, Asr #4).
Claims, labelled as the authors' words, not verified here:
- Rust "is a complex language with a growing set of features and a steep learning curve"; V's goal is "maintainable and predictable software" and is "maybe even boring" (vlang.io/compare, fetched today).
- "Fearless concurrency (no data race guarantee at compilation)" is marked wip, and "no undefined behavior" is marked wip in README (README, compare page).
- "Performance: as fast as C" and the compile-speed figures (README).
The verdict, three lines against Rust:
- Memory model: V defaults to a tracing garbage collector, with an optional ownership mode for strings. Rust has compile-time ownership for everything. Rust is ahead.
- Safety guarantees: V's own pages mark data-race freedom and undefined-behavior freedom as unfinished. Rust is ahead, and the gap is stated by V's authors.
- Maturity: V 0.5.2 is pre-1.0 and its docs say there is no formal spec. One README line describes Vinix. Rust is ahead.
What V would buy us: a Go-sized learning curve, a C backend that makes C interop a plain function call, and a quick dev loop. What it would not buy us: the safety Hedronite's core wants from Rust, or an ecosystem we could lean on. So V stays a watch item and a learning exercise. Evan's V contributor path is the right way to keep it warm: fork, format, test, draft a PR, and keep any adoption question for a later decision with new evidence.
Related:
Time your own number. Read the prod contract. Redeclare, then call. Close the editor.