agentleFS
vlangvlang/v/AGENTS.md

v

AGENTS.md38k starsChanged 3 years agoMITView on GitHub

Practical quick reference for the V compiler, standard library, and tools. Written for AI coding agents; useful for humans too. Then read Top Rules and Agent Rules before making changes.

Adopted by

Steady: 0 new projects took it in the last 4 weeks, none the 4 weeks before.thin data

Week of Sep 13: 0 new projectsWeek of Sep 20: 0 new projectsWeek of Sep 27: 0 new projectsWeek of Oct 4: 0 new projectsWeek of Oct 11: 0 new projectsWeek of Oct 18: 0 new projectsWeek of Oct 25: 0 new projectsWeek of Nov 1: 0 new projects
Sep 2026Oct 2026Nov 2026
New projects taking a version of this file each week, over the 8 weeks our latest run read: only 0 in all, so this is a hint, not a trend.

Who's talking about vlang/v

The biggest 5 of 123 mentions linking to the repository that our crawl found. Each line is their own headline.

See all 123

Talked about on GitHub, Hacker News, YouTube, Stack Overflow, Bluesky and the web (123 times).

On GitHub · 50

45 more on GitHub

On Hacker News · 38

33 more on Hacker News

On YouTube · 23

18 more on YouTube

On Bluesky · 5

On the web · 1

  • Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.

    Of the AGENTS.md files we read, the 31,542 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.

The file

Show its rules and text

We split this AGENTS.md into the rules it gives an agent

91 rules. About 0 are common, 48 are distinctive, and 43 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈48

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 43

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Very specific:

    Use ./v for everything:

    line 43
  2. Very specific:

    Run a file: ./v run examples/hello_world.v

    line 44
  3. Very specific:

    Use ./v for building, running, and testing.

    line 51
  4. Very specific:

    Put all V flags immediately after ./v and before the subcommand/file, e.g.

    line 52
  5. Distinctive:

    Never make the V compiler parse or expand C headers.

    line 56
  6. Distinctive:

    Run the smallest relevant tests.

    line 59
  7. Distinctive:

    Ask before large refactors or wide file touches (see Agent Rules).

    line 61
  8. Distinctive:

    Do not stash or modify unrelated files unless explicitly instructed.

    line 62
  9. Distinctive:

    When a summary is required, include behavior change, tests run, and touched file paths.

    line 65
  10. Distinctive:

    If instructions overlap, prefer Build & Rebuild, Testing, and Reporting.

    line 67
  11. Distinctive:

    If duplicates drift, treat Build & Rebuild, Testing, and Reporting as canonical and align other sections to them.

    line 69
  12. Very specific:

    All commands assume the repo root as the working directory.

    line 75
  13. Distinctive:

    Keep output easy to scan: short sections, bullets when listing, commands in backticks, no filler.

    line 84
  14. Distinctive:

    Use a strict, operational tone unless higher-priority instructions override it.

    line 86
  15. Distinctive:

    Ask only when required. If information is missing, ask a direct question.

    line 88
  16. Very specific:

    Run build, test, and format commands without asking for permission.

    line 96
  17. Distinctive:

    Only modify files required for the user request.

    line 99
  18. Distinctive:

    Avoid unrelated file changes.

    line 105
  19. Very specific:

    Avoid touching thirdparty/ unless explicitly requested.

    line 106
  20. Very specific:

    Ask before large refactors or wide file touches across multiple repo-root directories like cmd/, vlib/, doc/, and examples/.

    line 110
  21. Distinctive:

    Ask before large behavioral changes within a single subsystem or file, even if the file count is small.

    line 114
  22. Very specific:

    Ask before touching ci/ or Dockerfile* unless explicitly requested.

    line 120
  23. Distinctive:

    If you cannot complete a requested step, state the blocker and partial progress (what was attempted and what remains).

    line 128
  24. Distinctive:

    After substantial work, provide a short summary and list touched file paths.

    line 130
  25. Distinctive:

    Never change tests just to silence failures.

    line 134
  26. Very specific:

    Keep generated C when debugging backend issues: add -keepc.

    line 148
  27. Distinctive:

    Avoid hidden/bidirectional Unicode characters in source/markdown files.

    line 149
  28. Distinctive:

    If the compiler becomes unusable, recover with:

    line 151
  29. Distinctive:

    Check git status before stashing to avoid hiding unrelated work.

    line 155
  30. Distinctive:

    Do not stash unless explicitly instructed or the compiler is bricked.

    line 156
  31. Distinctive:

    Follow Common Workflow for the default execution order.

    line 167
  32. Very specific:

    Use this table to pick the minimum rebuild/tests quickly.

    line 171
  33. Distinctive:

    When in doubt, ask before proceeding.

    line 178
  34. Distinctive:

    If you read only one section for tests, read Testing.

    line 179
  35. Very specific:

    Before work: git status; ensure ./v exists.

    line 195
  36. Very specific:

    If ./v is missing, see Quick Start or Build & Rebuild.

    line 196
  37. Very specific:

    If compiler sources or core modules changed, rebuild ./v with ./v self (see Build & Rebuild).

    line 198
  38. Distinctive:

    Run the smallest relevant tests for the change scope (see Testing).

    line 202
  39. Distinctive:

    When a summary is required (see Agent Rules), it must include:

    line 207
  40. Very specific:

    If public behavior or tool output changes, update relevant docs (README.md, doc/, tutorials/) and note it.

    line 213
  41. Very specific:

    If you add or change a public API, update module docs or README and note it.

    line 217
  42. Very specific:

    Update CHANGELOG.md or ROADMAP.md only when explicitly requested.

    line 221
  43. Distinctive:

    If unsure whether behavior is public, ask the user before updating docs.

    line 227
  44. Distinctive:

    No rebuild is needed unless compiler or core modules changed.

    line 233
  45. Very specific:

    If you update .out files, state the rationale in the summary.

    line 235
  46. Very specific:

    Do not update .out files unless a behavior change is intended.

    line 236
  47. Distinctive:

    If blocked, note what was attempted and why it failed.

    line 242
  48. Distinctive:

    If tooling/network restrictions block a step, state the restriction and the closest viable alternative.

    line 243
  49. Distinctive:

    Use git status to confirm touched files before reporting.

    line 245
  50. Very specific:

    If ./v is missing, run make first, then build ./v.

    line 257
  51. Distinctive:

    If a rule appears elsewhere, defer to this section.

    line 259
  52. Very specific:

    If ./v exists but compiler sources changed, still rebuild ./v before tests.

    line 263
  53. Very specific:

    If unsure whether compiler/core changes happened, rebuild ./v.

    line 265
  54. Distinctive:

    Do not delete existing comments unless they are incorrect.

    line 279
  55. Very specific:

    Add V doc comments right before each new or modified public function or method.

    line 281
  56. Very specific:

    Avoid using unsafe{ code } blocks where possible, and minimize their scope.

    line 288
  57. Distinctive:

    Keep Markdown lines <= 100 chars (the checker is strict).

    line 293
  58. Very specific:

    Treat .vv files in vlib/v/slow_tests/inout/ as fixtures.

    line 299
  59. Very specific:

    Avoid using C. or JS. symbols in plain .v files.

    line 316
  60. Very specific:

    Use .c.v / .js.v files.

    line 317
  61. Very specific:

    Do not refactor existing C./JS.

    line 319
  62. Very specific:

    Do not use combinatorial suffixes like _d_flag_linux.c.v.

    line 342
  63. Distinctive:

    Use it for platform, compiler, and custom-flag checks:

    line 356
  64. Very specific:

    Only the following $-prefixed functions are supported:

    line 407
  65. Very specific:

    Prefer the unprefixed typeof, sizeof, isreftype, __offsetof, and dump forms in new code.

    line 427
  66. Very specific:

    Ask before running ./v test-all unless explicitly requested.

    line 493
  67. Distinctive:

    If unsure which tests apply, ask the user before proceeding.

    line 500
  68. Distinctive:

    If in doubt, prefer the smallest targeted test and ask.

    line 501
  69. Distinctive:

    Run all tests that apply. Start with the smallest targeted tests.

    line 502
  70. Very specific:

    Consider a stricter validation pass: ./v -cstrict -cc clang -silent test vlib/v/.

    line 522
  71. Very specific:

    Run vlib/v/slow_tests/inout/compiler_test.v and the tests under vlib/v/gen/c/ when output or codegen changes are likely.

    line 533
  72. Distinctive:

    If time-boxed, run at least the smallest relevant test and note skipped coverage in the summary.

    line 554
  73. Distinctive:

    Use only when a behavior change is intended.

    line 559
  74. Distinctive:

    Keep and inspect generated C:

    line 574
  75. Very specific:

    When debugging code generation issues, add unique comment tags to relevant code generation points (e.g., /*tom51*/) in the compiler source.

    line 592
  76. Very specific:

    Use retired CPU instructions per fixed workload and per parsed source line as the primary compiler optimization metrics.

    line 614
  77. Distinctive:

    Use the same host, OS, fixture, standard library, flags, environment, and filesystem placement.

    line 666
  78. Very specific:

    Keep executable, source, and output paths identical between roles where possible.

    line 667
  79. Distinctive:

    Run only one benchmark process at a time.

    line 671
  80. Distinctive:

    Run one warmup per role, then ten measured pairs, alternating baseline/candidate order: AB, BA, AB, BA, and so on.

    line 674
  81. Very specific:

    Use a distinct timing/stdout/stderr log for every run and the same generated C output path.

    line 676
  82. Distinctive:

    Do not select the fastest sample or compare medians from different measurement sessions as proof of a small improvement.

    line 686
  83. Very specific:

    When changing option/result behavior, add a regression test in vlib/v/tests/.

    line 740
  84. Distinctive:

    Treat new files as touched for formatting and markdown checks.

    line 751
  85. Very specific:

    Check *all* files are formatted: ./v -silent test-fmt.

    line 752
  86. Distinctive:

    Run only when asked or when validating the full tree.

    line 753
  87. Very specific:

    Check markdown: ./v check-md file.md for touched .md files (required before commits).

    line 754
  88. Very specific:

    Run only when asked or when making broad checker changes (more than 3 files in vlib/v/types/).

    line 757
  89. Very specific:

    Search: rg pattern (or git grep).

    line 760
  90. Distinctive:

    Keep commits focused: one logical change per commit.

    line 766
  91. Distinctive:

    If your branch has diverged and a rebase or merge is needed, do not force-push or drop hunks without asking.

    line 767

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md
<!-- V_AGENTS.md v1.1 2026-02-04 -->

V Repo Guide

Practical quick reference for the V compiler, standard library, and tools. Written for AI coding agents; useful for humans too.

Contents

  • Quick Start
  • Top Rules
  • Agent Rules
  • Safety (Do Not Brick the Repo)
  • Divergences From Repo Docs
  • Quick Decisions
  • Common Workflow
  • Reporting
  • Prerequisites
  • Build & Rebuild
  • Run Programs
  • Testing
  • Code Style
  • Modules and Imports
  • C/JS Interop Hygiene
  • Environment-Specific Code (files and $if)
  • Compile-Time Code and Reflection
  • Debug
  • Compiler Performance
  • Compiler Architecture
  • Key Directories
  • Test Locations
  • Error Reporting (checker/parser)
  • Option/Result Types
  • Tools
  • Commits and PRs
  • Environment Variables
  • Gotchas

Quick Start

Get operational from the repo root in three steps:

  1. Build once (only if ./v is missing): make
  2. Rebuild the working compiler in place: ./v self
  3. Use ./v for everything:
    • Run a file: ./v run examples/hello_world.v
    • Run tests: ./v -silent test vlib/v/
    • Format: ./v fmt -w path/to/file.v

Then read Top Rules and Agent Rules before making changes.

Top Rules

  • Use ./v for building, running, and testing.
  • Put all V flags immediately after ./v and before the subcommand/file, e.g. ./v -g run file.v (not ./v run file.v -g); flags after the subcommand are passed to that subcommand.
  • Never make the V compiler parse or expand C headers; see C/JS Interop Hygiene.
  • Rebuild ./v after compiler or core module changes (see Build & Rebuild).
  • Run the smallest relevant tests; see Testing for triggers and minimums.
  • Ask before large refactors or wide file touches (see Agent Rules).
  • Do not stash or modify unrelated files unless explicitly instructed.
  • This guide assumes agents run locally, not in CI; CI notes are informational only.
  • When a summary is required, include behavior change, tests run, and touched file paths.
  • If instructions overlap, prefer Build & Rebuild, Testing, and Reporting.
  • If duplicates drift, treat Build & Rebuild, Testing, and Reporting as canonical and align other sections to them.

Agent Rules

Repo root

All commands assume the repo root as the working directory. The default location is /opt/v, but this may differ in your environment. If a command fails due to missing paths, verify with pwd and adjust accordingly. Use a per-command workdir only when a task requires a subdir.

Tone and output

  • Be concise by default. If the user asks for depth, provide it while keeping structure tight.
  • Keep output easy to scan: short sections, bullets when listing, commands in backticks, no filler.
  • Use a strict, operational tone unless higher-priority instructions override it.
  • Ask only when required. If information is missing, ask a direct question.

File access and edit scope

  • You may read and edit all files in the V repo without asking for permission. This is file access, not change scope; scope is constrained below. Reading is always OK. Edits to ci/ or Dockerfile* still require an explicit ask.
  • Run build, test, and format commands without asking for permission. These are validation steps, not code changes. Only ask about edit scope, not about running fmt, targeted tests, or check-md.
  • Only modify files required for the user request; avoid unrelated refactors. If duplication is harmful, small refactors to remove it are OK only when needed. Only refactor duplication in code you are already touching, and only when it directly supports the request or fixes a bug there. "Touching" means files already modified for the request.
  • Avoid unrelated file changes; call them out if present.
  • Avoid touching thirdparty/ unless explicitly requested. If changes are needed there, ask for approval before proceeding.

When to ask

  • Ask before large refactors or wide file touches across multiple repo-root directories like cmd/, vlib/, doc/, and examples/. Exception: docs-only changes across many files are OK without asking; call them out in the summary.
  • Ask before large behavioral changes within a single subsystem or file, even if the file count is small. Examples: changes to parser rules, checker resolution, codegen output shape, diagnostic text/ordering, or tool CLI behavior. Large means user-visible changes in CLI flags, output, diagnostics, or codegen shape. If unsure whether a change is "large," ask.
  • Ask before touching ci/ or Dockerfile* unless explicitly requested. If changes are needed there, confirm whether local validation is expected or if CI-only coverage is acceptable.

Bootstrap

  • Bootstrap/compiler usage rules: see Top Rules and Build & Rebuild.

Completeness and summaries

  • If you cannot complete a requested step, state the blocker and partial progress (what was attempted and what remains).
  • After substantial work, provide a short summary and list touched file paths. Substantial work means any behavioral change, or changes in more than one file. Always include tests run (or "Not run" with reason) in the summary.
  • Never change tests just to silence failures. Update expectations only when behavior changes are intended, and note the rationale in the summary.

New files and deeper guidance

  • New file checklist: format with ./v fmt -w, add doc comments for any public functions, run ./v check-md for markdown files, and keep Markdown lines <= 100 chars. Add or update tests when introducing a new public API.
  • For deeper edge cases, consult CONTRIBUTING.md and TESTS.md.

Safety (Keep the Compiler Current)

  • Rebuild the working ./v binary in place with ./v self after compiler changes. Overwriting ./v is the intended workflow.
  • Keep generated C when debugging backend issues: add -keepc.
  • Avoid hidden/bidirectional Unicode characters in source/markdown files.
  • If the compiler becomes unusable, recover with:
    1. git stash
    2. make
    3. git stash apply Check git status before stashing to avoid hiding unrelated work. Do not stash unless explicitly instructed or the compiler is bricked.

Divergences From Repo Docs

The repo docs use v in examples. In this environment, use ./v for all builds, runs, tests, and in-place self-rebuilds.

  • TESTS.md suggests v test-all before PRs; ask before running ./v test-all. These overrides keep commands anchored to this checkout.

Quick Decisions

  • For rebuild and test choices, follow Build & Rebuild and Testing.
  • Follow Common Workflow for the default execution order.
  • For broader workflow guidance, see CONTRIBUTING.md.

Compact decision table (rebuild/tests)

Use this table to pick the minimum rebuild/tests quickly. See Build & Rebuild and Testing for full details and edge cases. Commands omit ./v for brevity; assume the ./v prefix. This table is the minimum set; check Testing for additional triggers. REPL and backend changes have additional triggers in Testing. Note: cmd/v/ is compiler scope; treat changes there as compiler changes. When in doubt, ask before proceeding. If you read only one section for tests, read Testing.

Change areaRebuild?Minimum tests to run
Docs only (.md)Nocheck-md file.md
Compiler (vlib/v/, cmd/v/)Yes-silent vlib/v/compiler_errors_test.v; test vlib/v/
Core modules (builtin/strings/os/strconv/time)YesSmallest relevant tests
Parser-only (vlib/v/parser/)Yestest vlib/v/parser/
Checker-only (vlib/v/types/)Yestest vlib/v/types/
Comptime (vlib/v/types/, vlib/v/transform/)Yestest vlib/v/tests/; comptime-related tests
vlib (non-compiler)NoNearest *_test.v or test vlib/path/
Tools (cmd/tools/)NoTool-specific test; else nearest *_test.v
Diagnostic/output changesYesvlib/v/slow_tests/inout/compiler_test.v
C codegen (vlib/v/gen/c/)Yestest vlib/v/gen/c/

Common Workflow

  1. Before work: git status; ensure ./v exists; rebuild if needed. If ./v is missing, see Quick Start or Build & Rebuild.
  2. Edit the relevant files.
  3. If compiler sources or core modules changed, rebuild ./v with ./v self (see Build & Rebuild).
  4. Format touched .v/.vsh files and run ./v check-md on touched markdown.
  5. Run the smallest relevant tests for the change scope (see Testing).

See Build & Rebuild for rebuild triggers and flags.

Reporting

  • When a summary is required (see Agent Rules), it must include:
    • Behavior changes (or "No behavior change").
    • Tests run (or "Not run" with a reason).
    • Touched file paths.
    • Note unrelated changes if present.
    • Note doc updates if public behavior/tool output changed.
  • If public behavior or tool output changes, update relevant docs (README.md, doc/, tutorials/) and note it. For example: README.md for top-level CLI usage, doc/ for compiler/tool docs, tutorials/ for learning material.
  • If you add or change a public API, update module docs or README and note it. Public API includes stdlib functions/types and user-visible compiler flags, diagnostics, tool CLI behavior (including cmd/tools and stdlib CLI tools), or output formats.
  • Update CHANGELOG.md or ROADMAP.md only when explicitly requested.
  • Public behavior includes compiler output, diagnostics, user-facing CLI, and stdlib API. This includes developer-facing flags, error codes, or output ordering changes.
  • Internal refactors with no public behavior change do not require doc updates.
  • If unsure whether behavior is public, ask the user before updating docs.
  • Acceptable reasons for not running tests: docs-only change, no relevant tests, or environment constraints. Be specific.
  • Docs-only changes: run ./v check-md file.md; no other tests required unless a test explicitly reads those docs. No rebuild is needed unless compiler or core modules changed.
  • For docs-only, explicitly mention check-md in the summary.
  • If you update .out files, state the rationale in the summary.
  • Do not update .out files unless a behavior change is intended; otherwise treat mismatches as regressions.
  • Example summary line: Behavior change: none or Behavior change: fixed X.
  • Behavior change includes output, API, error messages, and test expectations.
  • If blocked, note what was attempted and why it failed.
  • If tooling/network restrictions block a step, state the restriction and the closest viable alternative.
  • Use git status to confirm touched files before reporting.

Prerequisites

  • Toolchain: make, git, and a C compiler (clang or gcc).
  • Windows: use makev.bat for the initial build.

Build & Rebuild

  • Initial build (only if ./v is missing): make (Windows: makev.bat).
  • Rebuild ./v in place: ./v self
    • This intentionally overwrites the active compiler with one built from the current checkout.
  • If ./v is missing, run make first, then build ./v.
  • This section is the source of truth for rebuild triggers. If a rule appears elsewhere, defer to this section.
  • Rebuild triggers:
    • Compiler sources in vlib/v/ or cmd/v/.
    • Core modules: builtin, strings, os, strconv, time.
  • If ./v exists but compiler sources changed, still rebuild ./v before tests.
  • If unsure whether compiler/core changes happened, rebuild ./v.
  • Common flags:
    • -g debug info (V line numbers).
    • -cg debug info (C line numbers); often combined with -keepc.
    • -keepc keep generated C file(s).
    • -prod optimized build.
    • -o file output path/name.
    • -cc clang pick a C compiler.
    • -cstrict be stricter about the generated C.
    • -b js|native|wasm select a backend.
    • -os <os> cross-compile target selection (when supported).

Code Style

  • Comments: add succinct comments only when code is not self-explanatory. Do not delete existing comments unless they are incorrect; you may fix grammar or spelling without changing meaning. Add V doc comments right before each new or modified public function or method. The V doc comments should start with the name of the fn, example: // the_name does ...
  • Copy pasta: avoid copy pasta. If there's duplicate logic, move to a function only when it is required for the request and within code you are already touching.
  • Avoid using unsafe{ code } blocks where possible, and minimize their scope. unsafe is acceptable for low-level interop (e.g. C pointer casts, manual memory management) but should never wrap ordinary V logic. When unsafe is required, keep the block as small as possible and add a comment explaining why it is necessary.
  • Keep Markdown lines <= 100 chars (the checker is strict). Apply to touched lines in modified Markdown files.
  • Avoid hidden/bidirectional Unicode characters in source/markdown files.
  • Non-V files: keep existing formatting; only reformat if required by the change.
  • Treat .vv files in vlib/v/slow_tests/inout/ as fixtures; avoid formatting unless a behavior change is intended and output expectations are updated.
  • Formatting and check commands are in Tools.

Modules and Imports

  • Module names must match their directory name (no hierarchy in the module line). Mismatches cause silent import failures.
  • Imports follow the folder hierarchy (e.g. import abc.def).

C/JS Interop Hygiene

  • The V compiler must never parse, scan, recursively expand, or infer declarations from C headers, including bundled headers. Leave preprocessing and include-cycle handling to the C toolchain. Track native dependencies through compiler-produced manifests or treat files as opaque cache inputs; bypass reuse when the manifest cannot establish safety. Do not reintroduce header inspection for caches, native bridges, backend selection, or portable C output.
  • Avoid using C. or JS. symbols in plain .v files.
    • Use .c.v / .js.v files.
    • Enable -Wimpure-v to catch accidental impurity.
  • Do not refactor existing C./JS. uses in .v files unless required. If you must change those lines, prefer moving the interop code to .c.v/.js.v.
  • For C interop, use correct const_ prefixes in C function redeclarations when needed (helps with -cstrict and C static analysis tooling).

Environment-Specific Code (files and $if)

V supports environment-specific file suffixes. Prefer them when the whole file is platform/backend specific.

Common patterns:

  • Backend splits:
    • *.c.v (C backend), *.js.v (JS backend), *.native.v (native backend), *.wasm.v (WASM backend)
  • OS splits:
    • *_windows.c.v, *_linux.c.v, *_nix.c.v, with *_default.c.v as fallback
  • Flag splits:
    • *_d_customflag.v is included only with -d customflag
    • *_notd_customflag.v is included only when that flag is NOT passed

Notes:

  • Do not use combinatorial suffixes like _d_flag_linux.c.v; use _d_flag.v plus $if linux {} inside the file.
  • For smaller platform differences, use compile-time $if blocks:
    • $if windows { ... } $else { ... }
  • See Compile-Time Code and Reflection for the full $if reference.

Compile-Time Code and Reflection

V uses $ as a prefix for compile-time (comptime) operations. These are evaluated by the compiler, not at runtime. AI agents frequently confuse comptime and runtime constructs; this section clarifies the boundaries.

Compile-time $if

$if evaluates conditions at compile time. It is not a runtime if. Use it for platform, compiler, and custom-flag checks:

  • $if windows { ... } $else $if linux { ... } $else { ... }
  • $if debug { ... } (enabled by -g or -cg)
  • $if prod { ... } (enabled by -prod)
  • $if custom_flag ? { ... } (enabled by -d custom_flag)

Full list of builtin $if options: see the table at https://docs.vlang.io/conditional-compilation.html.

Common mistakes:

  • Using runtime if where $if is needed for platform-specific code. Runtime if will fail to compile if it references platform-specific symbols; $if excludes the block entirely on non-matching platforms.
  • Forgetting the ? suffix for custom flags: $if myflag ? { ... }. Without ?, the compiler treats it as a builtin option and silently ignores it.

Compile-time $for

$for iterates over type metadata at compile time. It works with:

  • StructType.fields - iterate struct fields
  • StructType.methods - iterate struct methods
  • EnumType.values - iterate enum values
  • StructType.attributes - iterate struct attributes
  • SumType.variants - iterate sum type variants
  • method.params - iterate method parameters

Inside $for blocks, use $if to branch on field/method types:

fn main() {
	$for field in MyStruct.fields {
		$if field.typ is string {
			println(field.name)
		}
	}
}

Also works with generics: T.fields, param.fields where fn gen[T](param T) {}.

Common mistakes:

  • Using runtime for to iterate struct fields. This does not work; V has no runtime reflection. Always use $for.
  • Calling obj.$method() outside a $for m in Type.methods block. The $method() call is only valid inside a comptime method iteration.
  • Assuming comptime $for produces a runtime loop. It does not; the compiler unrolls it into concrete code for each field/method/variant.

Compile-time functions

Only the following $-prefixed functions are supported:

  • $embed_file('path') - embed a file's contents into the binary. Paths can be absolute, relative to the source file, or use pseudo variables like @VEXEROOT or @VMODROOT.
  • $tmpl('path') - compile a V template file (used by veb).
  • $env('VAR') - read an environment variable at compile time.
  • $d('ident', default) - read a -d ident=value compile-time define, with a default fallback.
  • $res('path') - embed a resource (Android).
  • $compile_error('msg') - emit a compile-time error.
  • $compile_warn('msg') - emit a compile-time warning.
  • $pkgconfig('name') - query pkg-config at compile time.
  • $typeof(expr) - compatibility alias for typeof(expr).
  • $sizeof(Type) - compatibility alias for sizeof(Type).
  • $isreftype(expr) - compatibility alias for isreftype(expr).
  • $__offsetof(Struct, field_name) - compatibility alias for __offsetof(Struct, field_name).
  • $dump(expr) - compatibility alias for dump(expr).

Common mistakes:

  • Prefer the unprefixed typeof, sizeof, isreftype, __offsetof, and dump forms in new code. Their $-prefixed compatibility aliases are also accepted.
  • Assuming $embed_file returns a string. It returns an EmbedFileData object; use .to_string() or .to_bytes().
  • Using $env where a runtime os.getenv is appropriate, or vice versa. $env is baked in at compile time and cannot change at runtime.

Compile-time pseudo variables

These are @-prefixed identifiers substituted at compile time:

  • @FN - current function name.
  • @METHOD - ReceiverType.MethodName.
  • @MOD - current module name.
  • @STRUCT - current struct name.
  • @FILE, @DIR, @LINE, @COLUMN, @FILE_LINE - source location.
  • @LOCATION - file, line, and current type+method; good for logging.
  • @VEXE, @VEXEROOT - path to the V compiler and its directory.
  • @VHASH, @VCURRENTHASH - compiler commit hashes.
  • @VMOD_FILE, @VMODHASH, @VMODROOT - nearest v.mod info.
  • @BUILD_DATE, @BUILD_TIME, @BUILD_TIMESTAMP - build time (UTC). Override with the SOURCE_DATE_EPOCH env var for reproducible builds.
  • @OS, @CCOMPILER, @BACKEND, @PLATFORM - build environment.

Compile-time type groups

Comptime type groups combine multiple types into a higher-level type for use in generic or comptime $if checks:

  • $int - all integer types
  • $float - all float types
  • $array - all array types
  • $map - all map types
  • $struct - all struct types
  • $enum - all enum types
  • $alias - all type aliases
  • $sumtype - all sum types
  • $function - all function types
  • $interface - all interface types
  • $option - all option types

Example: $if field.typ is $int { ... }

Comptime changes and testing

Comptime logic lives primarily in vlib/v/types/checker_comptime.v and vlib/v/transform/comptime.v. Changes here require a rebuild of ./v and should be tested with ./v -silent test vlib/v/tests/ plus any comptime-specific tests. See the decision table and Testing.

Run Programs

  • Compile and run: ./v run file.v.
  • Just compile: ./v file.v (creates executable).
  • With debug info: ./v -g run file.v.
  • Debug run (C line numbers): ./v -keepc -cg run file.v.
  • Example: ./v run examples/hello_world.v.

Testing

Run:

  • File (shows test output): ./v path/to/file_test.v.
  • File (test runner report only): ./v test path/to/file_test.v.
  • Dir: ./v -silent test path/to/dir/.
  • Dir with statistics/metrics: ./v -stats test path/to/dir/.
  • Compiler: ./v -silent vlib/v/compiler_errors_test.v.
  • Fix outputs (only when intended): VAUTOFIX=1 ./v -silent vlib/v/compiler_errors_test.v.
  • All: ./v test-all. Ask before running ./v test-all unless explicitly requested.

When:

  • Rule of thumb: for localized changes, run the smallest relevant tests. If any trigger below matches, run the listed tests.
  • Minimum tests listed are the floor, not the ceiling; add targeted tests for cross-cutting changes.
  • If unsure which tests apply, ask the user before proceeding.
  • If in doubt, prefer the smallest targeted test and ask.
  • Run all tests that apply. Start with the smallest targeted tests; add slow tests as needed. Order does not matter.
  • Compiler changes (vlib/v/ or cmd/v/): Run ./v -silent vlib/v/compiler_errors_test.v, ./v -silent test vlib/v/.
  • Parser-only changes (vlib/v/parser/): Run ./v -silent test vlib/v/parser/.
  • Checker-only changes (vlib/v/types): Run ./v -silent test vlib/v/types/.
  • vlib changes: Run nearest *_test.v or ./v -silent test vlib/path/.
  • Tool changes (cmd/tools/): Run tool-specific tests. If none exist, run the smallest relevant *_test.v that exercises the tool. Note: cmd/v/ is compiler scope, not tools. Examples: cmd/tools/vfmt -> cmd/tools/vfmt_test.v and vlib/v/gen/v/gen_test.v. cmd/tools/vdoc -> cmd/tools/vdoc/vdoc_test.v.
  • Diagnostic/output changes: Run ./v -silent vlib/v/slow_tests/inout/compiler_test.v.
  • C codegen changes: Run ./v -silent test vlib/v/gen/c/. Consider a stricter validation pass: ./v -cstrict -cc clang -silent test vlib/v/.
  • REPL changes: Run ./v -silent vlib/v/slow_tests/repl/repl_test.v.
  • Broad refactors: Run ./v -silent test-all.
  • Backend-specific changes: run the smallest relevant tests for the affected backend. JS/native/WASM backends are incomplete, so avoid broad -b <backend> test vlib/ runs. Prefer targeted *_test.v files or small test dirs with -b js|native|wasm.

If time-constrained, prioritize ./v -silent vlib/v/compiler_errors_test.v and the smallest targeted tests. Run vlib/v/slow_tests/inout/compiler_test.v and the tests under vlib/v/gen/c/ when output or codegen changes are likely. See TESTS.md for more guidance on test selection and output expectations. See CONTRIBUTING.md for broader workflow guidance.

Concrete triggers:

  • vlib/v/slow_tests/inout/compiler_test.v when error text or output formatting changes, or changes in checker/parser error reporting.
  • ./v -silent test vlib/v/gen/c/ for changes under vlib/v/gen/c/ or C codegen output paths.

Types:

  • Standard: *_test.v files with test_ functions.
  • Output: .vv source + .out expected output in vlib/v/slow_tests/inout/. Example: ./v -silent vlib/v/slow_tests/inout/compiler_test.v.
  • vlib/v/tests/** may use .run.out expectations; run with ./v -silent test vlib/v/tests.

Docs-only guidance: see Reporting. If time-boxed, run at least the smallest relevant test and note skipped coverage in the summary.

Useful env variables and flags while testing

  • VAUTOFIX=1 - Auto-update .out files when tests fail (run twice). Use only when a behavior change is intended.
  • VTEST_ONLY=glob_pattern - Run only tests matching pattern.
  • VTEST_HIDE_OK=1 - Hide successful tests, show only failures.
  • ./v -silent test path/to/dir/ - Show only failed tests (if any), and a summary report.
  • -cc tcc can speed test builds when TCC is available.
  • Output expectations: update .out files only when behavior changes are intended; note the rationale in the summary.

Debug

  • See what the C compiler is doing:
    • -showcc prints the C compile command.
    • -show-c-output prints the C compiler output.
  • Build a temporary compiler for debug flags:
    • ./v -o ./w -d trace_checker cmd/v
    • Then run: ./w file.v
  • Keep and inspect generated C:
    • -keepc -cg is the common combo.
    • -printfn <name> -o file.c emits only the named C function to standard output. The name uses the modulename__fnname format (e.g. main__main). This flag can be repeated to print multiple functions. Methods/generics may use more complex C names; use -keepc to confirm exact symbols.
  • Trace stages: -d trace_scanner|trace_parser|trace_checker|trace_gen. These flags can help diagnose a problem when a stage stops earlier than expected.
  • Time stages: -d time_parsing|time_checking.
  • V panics: -keepc -g.
  • C segfaults: -keepc -cg -cc clang.
  • JS/native/wasm: prefer small, focused test files with -b js|native|wasm and -show-c-output where applicable; avoid broad -b <backend> test vlib/.
  • Quick code location technique: When debugging code generation issues, add unique comment tags to relevant code generation points (e.g., /*tom51*/) in the compiler source. Rebuild and generate C code, then search the generated file for these tags to quickly map generated C code back to the exact compiler source location. This is especially useful when multiple code paths generate similar-looking output and you need to identify which path is actually used. Example:
    // In vlib/v/gen/c/stmt.v:
    g.write('builtin___option_ok/*tom51*/(&(${styp}[]) { ')
    
    // After rebuild, search generated C code for "tom51":
    // builtin___option_ok/*tom51*/(&(int[]) { ... });
    Remember to remove these debug tags after fixing the issue.

Compiler Performance

For the fixed self-compilation benchmark, results, and agent regression policy, read vself_speed.md. Every compiler fix must preserve or improve that efficiency; investigate instruction-count regressions before merging.

Use retired CPU instructions per fixed workload and per parsed source line as the primary compiler optimization metrics; lower is better. On macOS arm64, /usr/bin/time -l reports instructions retired from hardware counters. Under matched conditions, this is the most reliable local measure of compiler work: it is less sensitive than elapsed time to CPU frequency and scheduling. Also retain elapsed time, cycles, and peak memory; fewer instructions do not guarantee lower latency or memory use. Compare counts on the same machine and architecture.

The instruction counter is scoped to the measured process; unrelated processes do not add their instructions to it. An idle host is not required for instruction comparisons. Counts are dynamic, not a static property of the binary: synchronization, retries, and runtime paths can vary. Use serial paired measurements to check that variation. Record host load as context for elapsed time and cycles; use controlled load when making latency claims.

For instructions per line, divide by physical lines in the .v and .vh files actually parsed, including builtins, imports, blank lines, and comments. Collect parsed .v lines plus parsed .vh lines in a separate unmeasured -v run with the same input flags. Preserve that inventory and verify that both compilers process the same files and line count. Keep the fixture fixed: padding files or changing source complexity must not count as an optimization. Retain total instructions alongside instructions/line; establish a new baseline when the fixture changes. Instructions per second depends on hardware, scheduling, and instruction mix; do not use it as a compiler-efficiency score. See vself_speed.md for the self-compilation fixture.

Build a production compiler outside the measured run, then measure compilation to C:

env -u VFLAGS -u VEXE VJOBS=1 SOURCE_DATE_EPOCH=0 V_MACOS_V3_NO_FALLBACK=1 \
  ./v -prod -cc clang self || exit 1
benchmark_out=$(mktemp -d "${TMPDIR:-/tmp}/v-perf.XXXXXX")
env -u VFLAGS -u VEXE VJOBS=1 SOURCE_DATE_EPOCH=0 V_MACOS_V3_NO_FALLBACK=1 \
  /usr/bin/time -l -o "$benchmark_out/time.txt" \
  ./v -nocache -o "$benchmark_out/sha3.c" /absolute/path/to/sha3.v \
  > "$benchmark_out/stdout.txt" 2> "$benchmark_out/stderr.txt"
benchmark_status=$?
printf '%s\n' "$benchmark_status" > "$benchmark_out/exit-status.txt"
cat "$benchmark_out/time.txt"

Replace the source path with the fixed benchmark fixture. Read the integer on the instructions retired line. The -prod above optimizes the compiler itself; the timed command uses the normal input compilation mode. Keep input flags identical between baseline and candidate. -o ...c includes parsing, checking, transforms, and C generation, while excluding the external C compiler and execution of the generated program. -check-syntax measures a different workload; label it separately and verify which stages that compiler version executes. -nocache disables compiler caching; it does not flush the operating system's filesystem cache. V_MACOS_V3_NO_FALLBACK=1 prevents silent legacy-compiler fallback when measuring V3 on macOS.

For a baseline/candidate comparison:

  1. Build both compilers with the same production build recipe and C toolchain. Verify correctness, diagnostics, and generated C before measuring. For a change intended to preserve output, require identical C, allowing only explicitly recorded build metadata differences. Validate generated C when changing the fixture or compiler representation; matching invalid C is not sufficient.
  2. Use the same host, OS, fixture, standard library, flags, environment, and filesystem placement. Keep executable, source, and output paths identical between roles where possible; record any remaining differences. Set VJOBS=1 and SOURCE_DATE_EPOCH=0, and disable tracing/profiling. Clear unintended compiler environment overrides, including VFLAGS and VEXE; record the effective environment. Measure only successfully rebuilt compilers, never a stale binary. Run only one benchmark process at a time. Other builds or tests on the host do not invalidate process instruction counts; do not pause unrelated work or wait for an idle host. Record that elapsed time and cycles collected under uncontrolled load are contextual measurements.
  3. Run one warmup per role, then ten measured pairs, alternating baseline/candidate order: AB, BA, AB, BA, and so on. Record all 22 runs, excluding both warmups from statistics. Use a distinct timing/stdout/stderr log for every run and the same generated C output path.
  4. Require exit status zero and exactly one positive instructions retired counter in every sample. Preserve failed or incomplete runs, but do not report them as a completed comparison. If the counter is missing or zero, report it as unavailable; elapsed time is not an instruction count.
  5. Report each role's median instructions/line and total instructions, the fixed parsed-line count, percentage change between medians, median paired instruction difference, and how many pairs made the candidate worse. Report memory and elapsed time alongside instructions. Retain all raw logs, exact commands/environment, compiler commits, binary hashes, fixture hashes, and output verification so the result can be reproduced.

Do not select the fastest sample or compare medians from different measurement sessions as proof of a small improvement. Investigate inconsistent pair results before accepting an optimization. For end-to-end build latency or parallel scaling, measure that workload separately with its intended worker count and external C compiler, and state that scope explicitly.

Compiler Architecture

The V compiler has the following stages, orchestrated by v.driver: v.scanner -> v.parser/v.flat -> v.types -> v.transform -> v.markused -> v.gen.c. Additional backends and lowerings include FastC, WebAssembly, SSA, MIR, instruction selection, and native ARM64 generation.

Key Directories

  • vlib/: Standard library (changes here can affect the compiler itself).
  • cmd/v/v.v: Compiler entry.
  • cmd/tools/: vfmt, vdoc, vup, vquest, etc.
  • examples/: Example programs.
  • thirdparty/: Bundled C libraries (tcc, mbedtls, sokol, etc.).
  • vlib/v/: Compiler modules.
    • scanner/ - Tokenizer
    • token/ - Token definitions
    • parser/ - Produces a flat AST
    • flat/ - Compact AST storage
    • types/ - Name resolution and type checking
    • transform/ - AST simplification
    • markused/ - Dead code eliminator
    • gen/c/ - C code generation (primary backend, known as cgen)
    • gen/fastc/ - Scanner-to-C fast path
    • gen/arm64/ - Native ARM64 backend
    • gen/wasm/ - WebAssembly backend

Test Locations

  • vlib/v/tests/: Compiler feature tests (including comptime).
  • vlib/v/slow_tests/: Output-matching and slow tests for the compiler.
  • vlib/v/slow_tests/inout/ - Output comparison tests (.vv + .out pairs)
  • vlib/v/parser/tests/ - Parser error tests
  • vlib/v/checker/tests/ - Checker error tests
  • vlib/v/gen/c/ - C codegen unit and integration tests
  • examples/compiletime/ - Comptime usage examples (including reflection.v)

Error Reporting (checker/parser)

Scanner and parser diagnostics carry v.token.Pos spans. Type-checker diagnostics are collected by v.types.TypeChecker; shared diagnostic formatting and message types live in vlib/v/errors/.

Option/Result Types

  • Syntax: ?Type (optional, can be none) vs !Type (result, can error).
  • Common bugs: Unwrapping in if guards, struct init with option fields, ternaries with options.
  • When changing option/result behavior, add a regression test in vlib/v/tests/.
  • Tests: Search vlib/v/tests/ for option/result test files.
  • Pitfall: Options in ternaries, SQL statements, and fixed arrays need special handling in cgen.

Tools

  • Note: if a rule overlaps with Testing, follow Testing.
  • Format: ./v fmt -w <file> for touched .v and .vsh files. Format only touched files unless explicitly asked to reformat broader scope.
  • Treat new files as touched for formatting and markdown checks.
  • Check all files are formatted: ./v -silent test-fmt. Run only when asked or when validating the full tree.
  • Check markdown: ./v check-md file.md for touched .md files (required before commits).
  • Code style checker: ./v vet vlib/v Run only when asked or when making broad checker changes (more than 3 files in vlib/v/types/).
  • Module docs: ./v doc -readme -all -l module_name.
  • Search: rg pattern (or git grep); list files: rg --files.
  • Auto-format hook: ./v git-fmt-hook install.

Commits and PRs

  • See CONTRIBUTING.md for full commit message conventions and PR workflow.
  • Keep commits focused: one logical change per commit.
  • If your branch has diverged and a rebase or merge is needed, do not force-push or drop hunks without asking. State the conflict and let the user decide the resolution strategy. When in doubt, prefer git rebase over git merge for a linear history, but always ask if the situation is ambiguous.

Environment Variables

  • VFLAGS: Pass flags to all V invocations (e.g., VFLAGS='-g' ./v test-all).
  • VEXE: Path to V compiler executable (useful in CI/scripts).
  • TMPDIR: Controls where .tmp.c files are written (V uses TMPDIR/v/).
  • SOURCE_DATE_EPOCH: Override build timestamp for reproducible builds (affects @BUILD_DATE, @BUILD_TIME, @BUILD_TIMESTAMP).
  • V2-specific: V2CC, V2CFLAGS, V2VERBOSE (for v2 development).

Gotchas

  • Core modules (builtin, strings, os, strconv, time) can affect the compiler because it is a V program. Rebuild ./v when they change.
  • Module names must match their directory name; mismatches cause silent import failures. See Modules and Imports.
  • Some V programs and tools hardcode os.execute('v ...') in their source. The ./v workflow does not protect against these; if you encounter unexpected behavior from such calls, check whether the code is invoking the system v instead of ./v and adjust accordingly.
  • Stale ./v can cause confusing failures; rebuild if behavior seems off.
  • Output tests require exact matches; whitespace changes break tests.
  • Formatting reminders: follow Code Style for fmt and check-md.
  • C compilation errors? Check generated C with -keepc (creates /tmp/*.tmp.c; see also TMPDIR in Environment Variables).
  • Compile-time code ($if, $for, $embed_file, etc.) is a common source of AI mistakes. See Compile-Time Code and Reflection for correct usage and pitfalls.
  • Broken compiler? See Safety for the recovery sequence.
  • Consult CONTRIBUTING.md and TESTS.md for:
    • New tests or test infra changes.
    • Output tests or .out updates.
    • Broad refactors or compiler-wide changes.
    • Unsure which slow test runner applies to your change.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.