Sum/alias short-name filters, builtin-symbol probes, sum constructor
callee checks, generic cast callee checks and receiver method suffix
indexing now work on existing spelling ids or raw text instead of
interning and re-parsing every name they see. A spelling that was never
interned cannot be a collected struct, enum or sum type, so the probes
return early without entering the interner.
* compiler: add -show-instructions to report retired instructions per stage
-show-timings only prints wall time, which is noisy on a loaded machine.
-show-instructions implies -show-timings and appends the retired CPU
instructions of each stage (summed over all threads) and of the whole
process, read from proc_pid_rusage on macOS.
* transform: cache bare struct candidates and use interned names for registry lookups
Receiver method resolution scanned every checker struct for each bare
type name. Build a short-name -> qualified-struct index once per struct
registry and invalidate it when generic templates are erased.
Generic name checks rebuilt name-id indexes of the enum and transform
registries whenever they grew; look the interned name up in the
registry map directly instead.
* markused: build interface dispatch keys once per scan
The used-interface dispatch pass rebuilt every implementer method key
on each worklist round. Precompute the groups and keys once, skip the
pass when no new functions were marked, and enqueue IError keys once.
Follow-up to #31271. Reader threads call source_type_existing_id while
writer threads intern new spellings, so the shared table gains new chunks
and the hash index is regrown and republished several times during the
lookups. The readers clear recent-cache slots to force index probes and
check that known spellings keep their IDs, absent spellings stay absent,
lookups never intern, and a writer's earlier spellings are visible once a
later one is.
ReceiverMethodCache was reset only when the module or the transformer fn
table size changed. A method registered only in the checker (generated
specializations, signatures merged back from scoped transform batches)
left that size unchanged, so a cached miss kept returning none.
Record the checker signature snapshot (checker pointer, return-type
revision, return/param table sizes) with the cached misses and drop them
when it changes. The check runs only on negative answers, so cache hits
keep their cost.
Add late_registration_cache_test.v, which covers cached negative
is_known_fn_name and receiver-method answers followed by late
registration: in the transformer, in the checker only, in a scoped batch
worker before and after merge, in the next batch worker, and across
module switches and fn-return cache clears.
* types: skip the pref test-file check for non-test paths
* types: reuse the serial check batch arena
* transform: cache promoted scoped texts by content
* driver: reuse eager import-scan sources for the parse
The -building-v import scan read every compiler source file with
os.read_file, and the eager parse then read each file again. The scan now
reads with parser.read_batch_source into C allocator memory and the eager
parse preloads those texts, so each file is opened and read once. The
import keyword test compares bytes instead of allocating a substring.
* types: use the cached import info for active import checks
* transform: skip repeated used-fn marking for a name
* transform: front interface type lookups with a direct-mapped cache
* transform: avoid copying receiver names in method matching
* transform: match noreturn candidates without building strings
* cgen: generate fn bodies in 16 scoped batches instead of 256
Every scoped cgen batch starts with a fresh worker, so generator and
checker caches (C names, parsed types, interned types, local scopes)
are cold again. With 256 batches the self-host build re-derives the
same facts hundreds of times. 16 batches keep most of the cache warmth
while still bounding the retained scratch: peak RSS is unchanged and
the generated C is byte-identical. cgen retires about 0.5G fewer
instructions on the self-compile benchmark.
* markused: probe node visibility before names and skip repeated fn_decls miss
* cgen: return a view from const_call_name_suffix instead of a copy
* cgen: skip fixed-array return wrapper scans for types without fixed-array returns
* types: build receiver method keys lazily in selected-file collection
Try the plain receiver method key before building the escaped one, and
compare module short names through short_name_view instead of allocating
all_after_last. Self-compile fixture: -247M instructions (-0.42%), C identical.
* transform: memoize variadic and array builtin method name lookups
Probe the variadic recent cache before scanning call names for the
generic `_T_` marker, and cache `array.<method>` known-fn lookups per
method spelling, invalidated when the signature maps grow.
* cgen: build the struct field index once for serial codegen
* markused: bind fn param types without a temporary name table
* transform: add direct-mapped fronts for base type, known fn and receiver method lookups
* types: test the test-file exemption only after a private declaration is found
* types: match `mut <name> :=` without building the needle string
* transform: avoid a per-node heap copy in the escape scan
* cgen: prefilter local name exception lookups with a bitset
* markused: skip omitted-param scans for calls that pass every param
Count a callee's positional params first, return when it has none, and
stop counting call arguments once they cover all params. The scan over
omitted param types moves to an out-of-line helper, and the params loop
stops at the first body statement.
* cgen: share resolved call target matching between call_key and generic base lookup
* types: keep only set call flags in the call_info bloom
* transform: copy only live bindings when entering an implicit err scope
* cgen: reuse one scratch arena across serial codegen batches
* cgen: skip redundant call target and type resolution in call argument helpers
* types: resolve a selector's declared value type once per call-info lookup
* types: skip reflected method argument checks when no methods loop is parsed
* cgen: reuse canonical declared fn return types in the call return memo
* types: size the const selector bloom to the const tables
The shared 4096-bit name bloom saturates on the self-compile const tables,
so almost every selector field fell through to two map lookups. Build a
two-probe bloom sized to about 16 bits per const key and suffix instead.
* types: try only non-collection receiver names when no array or map method has the name
* types: skip the bodyless-method lookup when no bodyless declaration has the method name
check_call resolved every method receiver and built its `Type.method` key
whenever the program had any bodyless declaration. Collect the method names
of bodyless declarations and skip the lookup for other names. The set is
never pruned, so it stays a superset of the names that can match.
* transform: resolve the scoped node payload once when promoting batch results
* cgen: skip the veb ctx receiver scan for non-Result returns
* cgen: skip builtin leaf types when collecting referenced C structs
* cgen: walk C extern references over direct child ranges
Skip non-identifier leaves before recursing and compare the `C.`
prefix inline. The visit order and the collected reference set are
unchanged.
* transform: prefilter fixed-array view escapes by stack-local arguments
Every mark in mark_fixed_array_reference_argument_escapes needs a receiver
or argument that is stack-local storage holding an inline fixed array.
Check that per argument before resolving the callee signature, and skip
the arguments that cannot be marked.
* cgen: compare current-module selector bases without allocating
* transform: keep compilation-table text ids across promotion windows
* cgen: skip impossible special-name comparisons in direct_call_name
* builtin: let the compiler skip unmapping prealloc arenas at exit
* markused: cache the positional param count of fn decls
* cgen: record a local's declaration facts under one storage key
* builtin: recycle 1M prealloc scope blocks instead of refaulting them
Scope arenas grow to 1M blocks, but the per-thread recycle cache was
capped at 2 MiB, so it held at most two of them. Every scope end unmapped
the rest and the next scope faulted the same pages in again. Keep up to
16 blocks or 16 MiB per thread.
* compiler: cache structural type queries by spelling ID
* compiler: close remaining getter-based structural type queries
* compiler: compact numeric type syntax and cache registry queries
* compiler: defer unused type views and index syntax by numeric ID
* compiler: make rare type views lazy and remove cold query allocations
* compiler: reuse numeric type metadata and inline syntax hits
* compiler: finish numeric warm queries and defer rare name views
* compiler: defer uncommon lexical type views until requested
* compiler: skip redundant syntax lookups and defer qualifier views
* Reuse unchanged type IDs and compact plain-name syntax publication
* compiler: compact immutable type query metadata
Store query flags in bitsets and omit zero and owner-identity scalar and span values from the persistent payload. Preserve full-width signed query values and process-owned immutable views.
* compiler: bound type syntax scratch allocations and packing work
* flat: share syntax layouts and reclaim query construction scratch
* flat: pack spelling metadata and avoid short render arenas
* flat: reduce immutable syntax lookup and name packing work
* compiler: avoid interning unused written type operands
Preserve decimal coefficient and scale internally, trim normal string output, and provide explicit fixed-place formatting. Keep default division at sixteen fractional places regardless of stored operand scale. Supersedes #31215 with the requested Go-style semantics.
Validated with literal ./v self, decimal tests under normal GC and GC none, formatting, Markdown and the executable README example. Independent agent review approved the exact source and validation evidence.
Co-authored-by: Albris W <admin@albrisllc.com>
* fastc: dereference pointer-returning calls with indexed arguments
* builtin: avoid FastC symbol collision in two-byte string search
* tools: build the macOS tray example as a project folder
* tests: align parser and Wasm audits with supported features
* transform: preserve caller types in generic specializations
* cgen: preserve source module identity in cached declarations
* cgen: resolve cached capture array fields in their source module
* test: assert the default SLH-DSA key description
* ci: respect macOS self-test workers with fail-fast
* builtin: keep vgc thread-cache test allocation at runtime
* driver: align wasm array gate tests with string support
* tests: use selected C compiler for raw assembly fixture
* compiler: reduce repeated self-compilation lookups and copies
Reuse immutable type and call metadata, avoid unnecessary name work and
node copies, and accelerate standard map, string and builder operations.
Add focused coverage for cache invalidation and borrowed storage lifetime.
* docs: record paired self-compilation results
* fastc: avoid repeated local type and member lookups
* fastc: reduce token, dispatch, and compilation overhead
* fastc: match native write count to current uint ABI
* cgen: preserve deferred constant dependency context
* driver: reuse diagnostic state when printing type errors
* cgen: store deferred constant dependencies beside their names
* driver: reuse diagnostic state when printing type errors
* gg, test-all: repair macOS geometry bridges and portable C checks
* test-all: propagate output expectation failures to the summary
* gg: document touched macOS drawing and screen helpers
Carry compiler type operands and explicit generic arguments as interned u32 spelling IDs through checking, transforms, reachability and backends. Cache composite spellings and immutable syntax-query metadata. Keep semantic TypeId values distinct and preserve textual parsing, diagnostics and emission boundaries.
Resolve integration with current master, including typeof operand relocation in both parallel parser merge paths and the diagnostic state variable rename.
Validation: all-backend/ownership compiler C generation, Clang syntax checks, twelve affected-source -check runs, typeof compile and diagnostic comparisons, matched loom2v C and diagnostics, and five chained ./v self replacements without fallback. Assertion and test-suite execution remains deferred by the user until performance parity.
* cgen: scope cleanup switch cases for older Apple Clang
* checker: let zero assignments leave reference sum variants
* math.big: pass signed byte counts to memory helpers
* checker: limit zero literal inspection to reference narrowing
* checker: stop loop narrowing scans before the current assignment
* tests: repair integer migration inputs for macOS self tests
* checker: return platform int for translated pointer differences
* examples: use signed byte counts for graphics buffers
* cgen: preserve headerless mmap bindings in cached modules
* fastc: repair pointer difference formatting and bootstrap fallback
* compiler: reduce repeated work in self-compilation hot paths
* tests: remove diagnostic traces from compiler regressions
* docs: record verified self-compilation improvement
* fastc: promote mixed numeric operands the way the checker does
FastC typed `u8 * int` as u8 and `int * i64` as int, so values were
truncated and `typeof` disagreed with cgen. Infix arithmetic now uses
the checker's rule: the operand the other one widens to wins, f32 only
stays f32 against non-f64 operands, and an integer literal adopts the
other operand's type unless it does not fit it. `**` uses the same
rule instead of its own copy.
A prefix `&` now addresses only the operand before a top-level `+` or
`-`, so `&u8(raw) + n` stays a `u8` pointer step. The old "left side
wins" rule hid that scope bug.
* fastc: spell typeof names the way V does
`typeof(x).name` returned FastC's C spelling of the type (`Array_int`,
`Point*`, `strings__Builder`, `FixedArray_3_FASTC_ARRAY_OF_int`) and the
literal pseudo-types (`integer literal`). Convert them to V's spelling,
as cgen does: `[]int`, `&Point`, `strings.Builder`, `[3]int`, and
`int literal` / `f64` for literal expressions.
program_output_test.v compiles and runs a FastC program and compares
its output; later FastC printing fixes add their cases there.
* fastc: print non-string values with eprint and eprintln
`eprintln(5)` reached the C compiler as `builtin__eprintln(5)`, an int
passed for a string. eprint and eprintln now take the selfhost print
path of print and println: a value is converted with its `str()` method,
or formatted as string interpolation formats it, and an enum goes
through its generated `str` helper, since the enum print helper writes
to stdout. The simple call fast path no longer takes them either.
The program output test helper now merges stderr into the output it
returns, as its comment said.
* fastc: print inline map literals
FastC could not infer the type of an inline map literal, so
`println({'k': 1})`, a map literal passed to a function and an array of
map literals were emitted as raw tokens and failed to compile. Infer
`{key: value, ...}` from its first entry, as the streaming map literal
reader does, and register the map typedef where the literal argument is
rendered.
* fastc: fold integer powers in enum discriminants
C has no `**` operator, so an enum value like `a = 2 ** 3` made FastC
stop with "does not support enum discriminant". Fold every `**` whose
operands are integer literals before the discriminant is rendered,
innermost first, so `2 ** 2 ** 2`, `1 + 2 ** 4` and `(2 ** 2) ** 3`
become constants as they do in the C backend. Parenthesized literal
operands now fold as well.
* fastc: keep the module prefix and indent nested values in default str()
The default struct `str()` printed `Pt{` for `mymod.Pt` and left nested
structs, arrays of structs and sum type fields unindented. Print the
source type name (stripping only `main.` and `builtin.`) and indent
nested aggregate fields the way cgen does, with a self-contained C
helper (builtin string functions may be pruned).
* fastc: print pointers and options like cgen
Interpolation and print/println of a `&T` value keep the `&` (`&nil` for
a null pointer), and options print as `Option(5)` / `Option(none)`, as
cgen does. Struct fields of these types print the same way. A `mut`
local or a smartcast, which C holds through a pointer, prints as its
value.
* fastc: monomorphize generic struct operators and self-referencing methods
Operator methods (`fn (a Gen[T]) + (b Gen[T]) Gen[T]`) were not scanned
as generic methods, because their name token is not a `.name`. Their
receiver then looked like a non-concrete instance, and any further
`Gen[T]` inside a method body or signature counted as a nested generic
reference. Both left the struct unresolved, so `Gen[int]{3} + ...`
stopped the parser with "top-level `+` after `main`".
Scan overloadable operator methods too, and render every own-struct
`S[T]` in a generic method as `S_mono_<C>`, like the receiver.
* fastc: update stale expectations in the reserved identifiers test
FastC now emits the platform int type (i64 on 64-bit targets) and
module-prefixed function names (main__calculate, main__auto), so the
keyword-escaped function name __vf_function_auto is no longer produced.
Update the expectations to the current output; the program still
compiles with tcc and prints 66.
* driver: link the MinGW runtime statically
GCC on MinGW keeps thread-local variables with emutls, which calls into
libwinpthread, and its driver adds -lpthread after all user arguments.
Programs built with MinGW GCC or Clang therefore needed
libwinpthread-1.dll next to the executable.
End the link with -Wl,-Bstatic, after the program's own libraries, so the
libraries that the MinGW driver appends come from static archives. The
program's libraries keep their import libraries. MSVC, TinyCC, object
builds and freestanding builds are unchanged.
* sync: look up TryAcquireSRWLock* at run time under TinyCC on Windows
TinyCC links kernel32.dll through its own lib/kernel32.def, which lists
neither TryAcquireSRWLockExclusive nor TryAcquireSRWLockShared. A program
built with `-d windows_7` calls them from Mutex.try_lock,
RwMutex.try_rlock and RwMutex.try_wlock, and did not link:
"tcc: error: unresolved reference to 'TryAcquireSRWLockExclusive'".
sync_windows_tcc.h now maps both names to small wrappers that find the
function in kernel32.dll with GetProcAddress once and cache the address.
The executable no longer imports them, so the bundled .def can stay as
it is. GCC, Clang and MSVC builds do not include the header and still
import the functions directly.
try_lock_windows_7_test.v builds with `-d windows_7` on Windows, so the
TinyCC Windows job now links and runs these calls.
* vgc: start the arenas at 0xc000000000 on 64-bit macOS and Linux
macOS hands out low addresses first. In a large heap the arenas reached
0x300000000, where data constants like 0x300000001 point, and the
conservative scan kept objects alive through them.
vgc_os_alloc_heap now passes an address hint to mmap, starting at
0xc000000000 (the hint Go uses for the same reason) and advancing by the
arena size, rounded to 64 MB. The hint is only advisory: when the range is
taken, macOS searches upward from it, and Linux picks its default, which
is already high. Hints stay below 2^47, because macOS silently returns low
addresses for hints at or above 2^48. A failed mmap falls back to the plain
one. Other systems and 32-bit targets keep the plain mmap; Windows keeps
MEM_TOP_DOWN.
test_the_heap_is_far_from_small_numbers now also checks 64-bit macOS and
Linux.
* vgc: back off instead of spinning while the world is stopped
Safepoint waiters, the collector waiting for them, the unregister loop
and the vgc mutex now pause, then yield, then sleep 50 us (Windows:
SwitchToThread) instead of spinning on sched_yield. Threads acknowledge
the stop request by epoch instead of a shared counter, so a waiter that
is still leaving one collection is not counted for the next one. The
collector waits at most 1 ms for the stops.
* parser: accept a negated right operand after a binary minus
`5 - -a`, `c - -1.2` and `a - - b` failed with "invalid expression:
unexpected token `-`". The parser rejected every prefix `-` that had
exactly one `-` before it in the source text.
V 0.5.2 rejects a doubled minus only when the right operand negates a
literal again: `5 - - -5` and `5 - -(3)`. `-3` is a number literal there,
so `5 - -3` is accepted. Check the parsed right operand of a binary `-`
instead of the source text, and keep translated code exempt.
translated_expressions_test.v used `13 - -5` as the ordinary-code
rejection; it now uses `13 - - -5`, which 0.5.2 also rejects.
* cgen: print f32 assert operands with f32 digits
A failed assert printed an f32 operand through f64__str((double)x), so
`p := f32(1.2)` showed as `1.2000000476837158`. Use f32__str for f32
values in interface_implicit_str_expr, which also serves generic
println and the debugger, so they print `1.2` like V 0.5.2.
* cgen: treat named float constants as f32 next to an f32
With `const c = 1.2` and `p := f32(1.2)`, `p == c` was false and
`p - c` was not 0. An untyped float const is a C double macro, and cgen
cast only literals and literal arithmetic to `float` where an f32 is
expected. V 0.5.2 keeps such a const an untyped float, so it compares
equal.
untyped_numeric_literal_expr_info now follows a const name (bare,
module-qualified or a selector) to its initializer, and the const is
cast as a whole, `(float)(main__c)`. Locals and parameters shadow
consts, and inside an initializer only the bare consts of the const's
own module are followed.
* transform: promote named and folded int constants next to a small int
With `const c = 3` and `u := u8(255)`, `u + c` was 258 and
`typeof(u + c).name` was `int`; V 0.5.2 and V3's checker give 2 and
`u8`. The same happened for `u + 0x03`, `u + (3)`, `u + (1 + 2)` and
`i8(100) * c`, and i8/i16 operands were never promoted.
promote_numeric_literal_infix_type accepted only a bare decimal int
literal next to an unsigned type. It now evaluates untyped int
constants built from literals in any base, signs, parentheses,
`+ - * / %` and named constants, and gives the other operand's type
when that type is u8, u16, i8, i16, u32, u64 or usize and the value
fits it. A value that does not fit keeps the previous type, so
`u8(255) + 300` is still an `int`.
* vet: keep every $if branch, so strings in other hosts' branches are skipped
`v vet` parsed each file with only `is_fmt` set, so the parser evaluated
`$if` conditions for the host and dropped the false branches. Their
string literals never reached vet's AST walk, which is what marks the
lines of a multi-line string as exempt from the indentation and
trailing whitespace checks, so those lines were reported as space
indentation. The result depended on the host: the arm64 native tests in
vlib/v/gen/arm64, which embed V programs inside `$if macos && arm64`,
passed on macOS arm64 and failed `v test-cleancode` everywhere else,
including the docker-ubuntu-musl and docker-alpine-musl-gcc CI jobs.
Set `preserve_comptime_conditionals`, as vfmt does, so every branch is
kept and walked.
* types, driver: count only the program's own exports as a library
A project without a `main` module was accepted whenever any function in
the build had `@[export]`. Builtin exports `backtrace` and `data_start`
under `-d musl` (added for `-cc musl-gcc` and on musl hosts), and stbi
exports its allocator, so with musl, or with `import stbi`, a module
without `main` built silently, while glibc rejected it. On Alpine 15
compiler_errors_test fixtures failed because of that.
The checker now looks for exported functions only in the files it
reports diagnostics for, and the driver no longer passes "any export"
as an exemption.
test_all.vsh compiles and smoke-runs examples/2048 and examples/tetris
whenever `xvfb-run` exists, which it does on the GitHub ubuntu-24.04
image. The compiler job only installed gobjc, so the 2048 compile failed
with "library 'Xrandr' not found" / "X11/Xlib.h: No such file or
directory", failing section 5/8 (Unlocked examples C oracle) on every
master push.
Install the headers and libraries gg and sokol link against
(X11, Xi, Xcursor, Xrandr, GL, EGL), plus the Mesa DRI drivers for the
xvfb run.
* builtin,v.workers: declare linked Linux helpers with @[c_extern] for FastC
FastC only emits prototypes for @[c_extern] C functions (or header-free
.c.v files whose stem matches a linked .c/.o input). v3_linux_name_worker
(syscalls_linux.c) and the v__gc_*_linux helpers (gc_boehm_helpers.c) are
defined in linked objects, so FastC called them undeclared, which the
bundled tcc rejects as an implicit declaration on Linux.
cgen is unaffected in ordering: the static copies embedded in cross/object
output are emitted before the runtime prototypes, and a plain prototype
after a static definition keeps internal linkage.
* builtin: declare GC_set_no_dls with @[c_extern] for FastC
FastC emits C prototypes only for `@[c_extern]` C functions. The Linux
debugger-init path calls `C.GC_set_no_dls`, so FastC Linux builds of
programs using the real builtin failed under tcc with "implicit
declaration of function 'GC_set_no_dls'" (vlib/v/gen/fastc/
vlib_constructs_test.v). The other GC entry points used at startup are
already declared this way.
* driver: name GNU ld missing libraries without the trailing colon
GNU ld reports `cannot find -lobjc: No such file or directory`, so the
missing-library hint said C library `objc:` was not found. Strip the
separator that follows the library name.
* ci: install libobjc-14-dev next to gobjc on ubuntu-24.04
clang on the runner selects the GCC 14 installation, while gobjc only
provides the libobjc.so development link for GCC 13, so linking the
Objective-C tests with `-lobjc` failed.
* fastc: keep static initializers for target globals with fixed-array struct defaults
Dynamic struct literals copy fixed-array fields with memcpy since #31036.
global_initializer_is_constant rejected that call, so such globals were
initialized at startup and target_global_startup_test failed. Accept the
memcpy only in the temporary-allowing pass; the static re-render is
checked again without temporaries.
* fastc: skip prestarted worker test where prestart is unsupported
fastc_start_capture_input only starts compiler processes on macOS, so on
Linux workers.active is false and the test failed in the V3 shards.
* v3 tests: scope fixed-array heap check to the tested function
On Linux glibc the backtrace reader in vlib/builtin keeps its read buffer
on the heap because it escapes into the returned string. The whole-file
assert matched that helper; check only the body of local_score.
A serial non-prod self-build of cmd/v now averages about 65s on the
ubuntu-24.04 runner, above the default 60s `v repeat` limit, so the
perf-compare job failed with "Performance regression detected" for the
master build itself. Raising only the limit would not help: 3 repeats of
2x12 runs of that series alone take about 78 minutes, more than the
80 minute job timeout once the builds and the other series are counted.
Run the cmd/v -check, -o .c and -o .exe series once, with 3 measured
runs and 1 warmup each, and a 300s limit.
C++ rejects initializing the `u8*` string field from a `const char*`
literal. The `_SL` macro alone produced about 19k of the errors in the
Other CI g++ step; the const initializers, the debugger string literal
and the error sentinel global produced most of the rest.
The thread handle typedef and its waiter were only registered by `spawn`.
A `[]thread T` that is waited on in code whose only `spawn` is compiled
out by `$if` referenced both without defining them. The driver does this
with its prestarted FastC workers, which only macOS spawns, so FastC
builds of cmd/v failed on Linux with
`'__vf_thread_kF_5f_5fFastcPrestartedCUnits' undeclared`.
`.wait()` lowering now registers the typedef and waiter when the file
has not done so yet.
FastC skipped extern prototypes for helpers declared in vlib files that
also include a C header, or whose linked object has a different stem
than the declaring file. On Linux with tcc this produced implicit
declarations of `v__gc_debugger_present_linux` and
`v3_linux_name_worker`, breaking the FastC channel, miscompile and vlib
construct tests.
Mirror cgen's rule: declare `v3_`, `v_runtime_`, `v_time_`,
`v_builtin_`, `v_set_` (and FastC's `v__gc_`) helpers when their
declaring vlib file links a C/Objective-C source or object via `#flag`.
* checker: accept `mut a ?T` with a pointer `T` in generic instances
A `mut a ?T` parameter is stored as its semantic type `&?&T` (see
mut_param_semantic_type). Substituting `T=&Point` into that gave
`&?&&Point`, so `bump[&Point](mut pr)` with `pr ?&Point` failed with
"cannot use `&?&Point` as `&?&&Point`", for generic fns, methods of
generic structs and generic interface methods. V 0.5.2 accepts it.
Such a parameter is now re-derived from its declared `&?T` text,
substituted, and given its semantic type again, so the instance takes
`&?&Point`, the type of a `mut a ?&Point` parameter.
* checker: keep the `mut` of parameters in generic fn and method values
The value of a generic fn (`f := clear[Point]`) or of a generic method
(`m := s.set[Point]`) got a fn type without parameter mutability, so
calling it with `mut` args failed with "parameter 1 is not `mut`",
while the value of a plain fn keeps it. V 0.5.2 accepts the generic fn
values (and panics on the generic method values).
Both now take the declared mutability of the fn's parameters; a method
value drops its receiver's, since the receiver is bound.
* transform: name the smartcast type in `typeof` of generic bodies
`typeof(a).name` in a generic body is folded while the body is cloned
for an instance, from the declared type of `a`. It ignored the
narrowing of the enclosing `if a != none`, `if a == none {} else`,
`if a is T`, `if a !is T {} else` and `match a` branches, so it named
`?Point` (or `IntOrStr`, `Named`) where the same non-generic body
names `Point` (`int`, `&Dog`).
The clone now records those narrowings, with the instance's types, for
the branches it clones, and the `typeof` fold names the narrowed type
as the non-generic fold does (`&Point` for a `mut a ?Point` parameter,
`&Dog` for an interface).
* checker: report `if v := x` over a non-option in generic instances
A generic body like `fn show[T](a T) { if v := a { ... } }` called with
a `T` that is neither an option nor a result was accepted by the
checker and then failed in the C compiler. The clone of the instance
does report "expression should either return an Option or a Result",
but check_concrete_instance_members dropped it with every other error
that is not known to be reliable for clones.
Keep that error when the guarded operand is a name, a literal or a
field of them, as is done for operator errors, and its declared type
is not an option or a result. The instance is now reported at the
guard, as in a non-generic body and as in V 0.5.2, whose output the
new checker fixture matches byte for byte. Guards over option
instances, `?T` and `mut ?T` parameters, map indexes and options that
a smartcast narrowed to their payload (accepted since #30979) stay
valid.
* types, transform: end a sum type narrowing when another variant is assigned
After `if mut b.v is int { b.v = 5; y := b.v + 1 }` the checker typed
`b.v` as `int`, but the transform had dropped the narrowing at the
assignment, so cgen read the sum value as an int and emitted invalid C.
Assigning another variant (`b.v = 'str'`) left the checker narrowed
too, so `b.v + 1` was accepted after it.
The checker now decides whether an assignment keeps a narrowing. A
value of the narrowed variant keeps it, as in V 0.5.2. Any other value
(another variant, a `Val(...)` sum value) ends it, and so does an
assignment to a parent of the narrowed value. (0.5.2 rejects such an
assignment to a narrowed member, and keeps a local narrowed after it,
reading a string as an int.) The lexical smartcast queries that the
transform relies on ask the same question (assignment_keeps_narrowing),
in nested blocks, `for` bodies and `match` branches as well, so the
checker, the transform and cgen agree.
* types, transform: check compound assignments to narrowed sums on the variant
`b.v += 2` inside `if mut b.v is int` was rejected ("operator `+=` not
defined on left operand type `Val`") although `b.v++` and
`b.v = b.v + 2` work there, because the operator was checked on the
declared sum type.
A compound assignment to a sum value narrowed by `is` or `match` now
operates on the narrowed variant, for members, locals and values
reached through a pointer, and keeps the narrowing, so the later reads
in the checker, the transform and cgen all use the variant.
* parser: reject `|` and `^` operator overloads
token.is_overloadable came from the tinyv parser and includes `|` and
`^`, so `fn (a T) | (b T) T` parsed as an operator method and passed
the checker. Nothing lowers those operators, though: transform's
struct_operator_symbol only maps the documented `+ - * ** / % < ==`
(and the comparisons derived from them), so `a | b` on struct operands
was emitted unchanged and failed in the C compiler.
V 0.5.2 never accepted these overloads (its parser reports `expecting
method name` at the operator), and the docs list only `+ - * ** / % <
==`. Report the same error, at the same token. The declaration is still
parsed as an operator method, so the rest of the file parses normally.
A `|` inside an unclosed receiver (`fn (x int | string)`) already has
its own error, so it is not reported a second time.
* types: reject operator overloads on enums and interfaces
The checker validated the arguments and return type of an operator
method, but not its receiver kind, so `fn (a Color) + (b Color) Color`
on an enum compiled. Nothing lowers such a method: transform only turns
infix expressions on struct, alias and (for `==`) sum type operands into
operator calls. So `Color.red + Color.red` kept the enum's integer
addition and silently ignored the overload. On an interface, `==` kept
the builtin interface equality and `+` left `a + b` on the interface's C
struct, which failed in the C compiler.
V 0.5.2 rejects these with `operator methods are only allowed for
struct and type alias`, at the declaration. Report the same error for
receivers that are enums or interfaces. An alias of an enum keeps
overloading operators, as in 0.5.2. Unlike 0.5.2, `**` is checked too:
0.5.2 left it out of the list of checked operators. Also unlike 0.5.2,
sum types are not rejected: #29578 lowers their `==` overloads, and
v.types relies on one (`Type.==`).
* parser: reject explicit `!=`, `>`, `<=` and `>=` overloads
The docs say these operators are generated from `==` and `<` and cannot
be overridden, but token.is_overloadable includes them, so
`fn (a T) != (b T) bool` parsed as an operator method and transform
then called it, in place of the negated `==`, for every `a != b`.
V 0.5.2 rejects these declarations in its parser with `cannot overload
`!=`, `>`, `<=` and `>=` as they are auto generated from `==` and `<``,
at the operator. Report the same error, at the same token (0.5.2's
message lacks the space before `<`; it is added here).
* cgen: give operator overloads of enum aliases their own C names
An alias of an enum can overload operators, but its methods are named
by enum_method_c_name_in_module, which spells a method as
`<Enum>_<cname(method)>`. cname of a bare operator is `_`, so every
operator of the alias got the same C name (`Pitch__`): only one body
was emitted, and `a - b` or `a < b` called the `+` overload.
Name operator methods there the way naming.c_name names every other
operator overload, in the internal namespace
(`__v3_internal_symbol_op_Pitch__plus`), so they cannot collide with
each other or with a method of the same word.
* types: derive `>`, `<=` and `>=` from an alias's own `<`
An alias that declares `<` gets `>`, `<=` and `>=` generated from it,
and transform lowers them that way, but check_infix only skipped the
builtin checks of the aliased type for an operator the alias declares
itself. So `a > b` fell through to the checks of the aliased type: an
alias of a struct without `<` reported `undefined operation`, and an
alias of an enum `only `==` and `!=` are defined on `enum``. Only an
alias of a primitive, whose own ordering is valid, compiled.
Look up the alias's `<` for these operators, as V 0.5.2 does.
* transform: convert fixed array elements in dynamic array field inits
A `[2]Item` used to initialize a `[]Named` (or `[]Shape`) struct field,
as in `Holder{ f: x }`, was copied into the dynamic array as it was, so
the field held concrete `Item` values where interface or sum type values
are read: `h.f[0].name()` panicked with `interface method Named.name not
implemented`, and a `match` over the sum type values matched nothing.
Returns, assignments, arguments and field assignments convert the
elements. transform_struct_fields and the struct update lowering sent
every fixed array value of a dynamic array field to
fixed_array_value_to_owned_array, which only copies. They now use
fixed_array_value_to_field_array, which converts elements stored
differently through transform_array_value_for_dynamic_target, as call
arguments do, and copies the other fixed arrays as before. This covers
positional, nested, anonymous, embedded and promoted fields, struct
updates, generic structs and `@[params]` arguments.
* types: reject numbers behind pointers that are stored differently
`fn take(p &u64)` accepted `take(&x)` with `x u8`, and read 8 bytes
from a 1-byte variable. The pointer branch of type_compatible compared
the pointees with plain type_compatible, which converts numbers, and
checked layouts only for containers. The call-argument fallbacks for
explicit and implicit references compared the pointees the same way.
A number behind a pointer is read in place, so its storage must match:
`&u8` as `&u64`, `&u64` as `&f64` and `&int` as `&i64` are now errors
in V calls, assignments, returns, field values, appends and generic
instances. The sign does not count, so `&char`, `&u8` and `&i8` stay
interchangeable. C calls, `unsafe` blocks and translated C code keep
accepting the number pointees they accepted before; voidptr and
explicit casts are unchanged.
* tests: fix two Linux-only V3 shard fixtures
- fastc spawn_parameter_escape_test: gate.h declared set_state_value
with `long long`, while the C definition used FastC's `i64`
(`int64_t`, which is `long` on Linux). Use `int64_t` in both.
- inline_asm_c_lowering_test: the amd64 runtime program took the
address of a fixed array element outside `unsafe`, which the checker
rejects. Wrap it in `unsafe {}`.
* tests: print the cc command in the #flag target filter tests
The V3 driver prints the C compiler command only with `-showcc` or
`-v`, so on Linux `compile.output` was empty and the `-ldl` assertion
failed. The negative "does not leak" assertions passed vacuously for
the same reason. Pass `-showcc`, as the Objective-C flags test does.
* tests: pin arm64 in windows_header_macro_names_test so tcc stays TinyCC
On amd64 hosts the driver replaces `-os windows -cc tcc` with MinGW gcc,
so cgen never emitted the over-aligned TinyCC alloca and the test failed
on the Linux V3 shards. No MinGW gcc exists for arm64, so tcc is kept.
* tests: expect uint for sizeof in match_clone_test after #31113
* toml: parse deeply nested inline tables and arrays in less stack
Since #29578 sum type values are tagged unions stored inline, so every
`ast.Value` local and temporary of the recursive parser functions takes
the size of the largest variant. The frames of `inline_table`, `value`
and `array` grew to 5-7 KB each, and one level of inline table nesting
took 18 KB of stack (tcc, arm64), so the toml CI job overflowed the 8 MB
main thread on `qa-table-inline-nested-1000.toml` of the iarna suite.
The recursive path now writes each value into its destination slot
instead of returning it: `value_into`, `array_into` (which pushes a
placeholder and parses into it) and `assigned_value_into`, called from
`noinline` key parsers. The work around the recursive calls (separators,
comments, errors, insertion, the immutable keys of the enclosing inline
tables, now kept in `outer_immutables`) moves to `noinline` helpers.
One level of inline table nesting now takes 3.9 KB, an array level
2.3 KB (were 18.4 KB and 6.6 KB). `value()` and `array()` keep their
public signatures.
nested_depth_test.v parses 1001 levels of inline tables, arrays and
dotted keys in inline tables, and 500 levels of arrays of inline tables,
each in a spawned thread (8 MB stack on every 64-bit target).
* toml: keep dotted keys and table headers from extending arrays of tables
Since da62dd4bd7, find_in_table_view selects the last element of any
array segment for every caller. Table headers need that for their
leading segments (`[[fruit]]` then `[fruit.physical]`), but two callers
must not descend into an array:
- the dotted keys of key/value pairs, which may only extend tables, so
`[[tab.arr]]`, `[tab]`, `arr.val1=1` was accepted (toml-test
invalid/array/extend-defined-aot.toml). The same held for static
arrays: `a=[{}]` followed by `a.b=1`;
- the last segment of a `[x]` header, so `[[tbl]]` followed by `[tbl]`
was accepted as a redeclaration (toml-test
invalid/table/duplicate-key-07.toml).
Add find_key_table_view, which never enters arrays, and use it for the
dotted keys in root tables, table contents, inline tables and array
of tables entries. allocate_in_table now resolves the header's parent
path as before but requires its last segment to be a table.
array_of_tables_extension_test covers both toml-test cases, the static
array and inline table variants, and the valid documents that still
need to descend into arrays of tables.
vab runs `v -dump-modules <file> -dump-defines <file> -dump-c-flags <file>`
and parses the results line by line. V3 broke it in four ways:
* `-dump-defines` was swallowed by the `-d` define prefix branch, and
`-dump-modules` was ignored, so neither file was written.
* Module names must be the full dotted import paths (`sokol.c`), not the
last path segment.
* The C flags dump was written after link preparation, which the Android
builds never reach; it is now written as soon as the flags are known.
* Value-taking flags (`-I`, `-L`, `-isystem`, `-include`, `-framework`)
are kept on one line with their value, as V 0.5.2 printed them.
Defines are dumped as `system,<os>,true` and `user,<name>,<value>`.
* cgen: fix OpenBSD struct stat field order in the headerless prelude
OpenBSD keeps `__st_birthtim` last in `struct stat`, after `st_gen`.
The headerless prelude put it after `st_ctim`, as on FreeBSD and
NetBSD. Every field from `st_size` on was then read 16 bytes too far:
`st_size` read `st_blksize | st_flags << 32`, which is 16384 for most
files on FFS. The fields up to `st_ctim` were not affected.
Generated compilers therefore got 16384 from `os.stat().size`,
`os.file_size()` and every raw `fstat` size on OpenBSD. That broke the
`tcc-openbsd` CI job twice over:
- Since the parallel source reader (#30874), `v2` reads source files
with `fstat` + `read` of `st_size` bytes, so every file bigger than
16 KiB was cut at byte 16384 and `v2 -> v` failed with parse errors
at that offset (driver.v, os.v, string.v, format.v, ...).
- Before that, `$embed_file` took its length from `os.file_size()`, so
the C prelude that `./v` embeds was over-read (int128_helpers.h,
into rodata: `\x00division by zero\x00...`) or cut
(manual_stdlib_c_headers.h), and the first program `./v` compiled
failed with `unrecognized character \x00` and `unknown type name`.
Add a test that checks the emitted field order and, with the host C
compiler, the LP64 offsets and size from OpenBSD's <sys/stat.h>.
* cgen: fix OpenBSD addrinfo order and EAI_SYSTEM in headerless prelude
OpenBSD's `struct addrinfo` has `ai_addr` before `ai_canonname`, like
glibc and musl. The headerless prelude grouped OpenBSD with Darwin,
FreeBSD, NetBSD and DragonFly, which put `ai_canonname` first. On
OpenBSD, `net` then copied every resolved address from the
`ai_canonname` slot. That slot is null, because `net` never asks for
AI_CANONNAME, so every successful lookup crashed in memcpy(). The
essential tests of the `tcc-openbsd` job (net/tcp_test.v, the
net.http tests) resolve addresses.
OpenBSD's EAI_* codes are negative, and EAI_SYSTEM is -11, but the
prelude defined 11 for every BSD. getaddrinfo() failures with
EAI_SYSTEM were then reported with gai_strerror() text instead of
the errno message.
Move OpenBSD to the `ai_addr`-first layout, and pass EAI_SYSTEM to
headerless_bsd_constants per platform: -11 on OpenBSD, 11 on NetBSD,
FreeBSD and DragonFly, as in their netdb.h.
Add a test that preprocesses the emitted prelude for each platform
macro and checks both against the platform headers.
* math.bits: use _BitScanReverse for msvc leading_zeros
`__lzcnt`/`__lzcnt64` need the LZCNT target feature. MinGW GCC 13 declares
them always_inline with `target("lzcnt")`, so compiling `-cc msvc` C output
with it fails ("inlining failed in call to 'always_inline' '__lzcnt64'").
`_BitScanReverse` has no such requirement and is available in MSVC and MinGW.
The zero input is already handled before the intrinsic call.
Declare `_BitScanReverse`/`_BitScanReverse64` in the headerless Windows SDK
prelude and keep them as preserved C functions.
* ci: build v_win.c with -cross in Cross CI
The step links the generated C with MinGW, outside the V driver. Without
`-cross`, the C output relies on native helper bodies (`v_toolcache_*`,
`v_set_unhandled_exception_filter`, `_udiv128`) that the driver compiles
separately, so the MinGW link fails with undefined references. `-cross`
emits a self-contained portable C file.
Since #29365 the worker is started with an argument array, not a shell
command, so the padding of `${i:5}` reached the flag parser, which
rejected ` 0` as an integer. This broke the parser-silent CI job.
#31002 added `without_line_comment` to cmd/tools/vdoc/document, but
only a test calls it; `declaration_shape` already skips `//` comments
itself. The FreeBSD CI job runs `v -N -W -check build-tools`, where the
"unused function" warning is an error, so it has failed on master since
then.
Remove the function and its test assertion.
On Unix, `disk_usage` takes `available` from `f_bavail`, which leaves
out the blocks reserved for root (`f_bfree - f_bavail`, e.g. on ext4).
Those blocks are neither available nor used, so `available + used`
can be smaller than `total`. The space-paths-linux CI job failed on
`du.total == du.available + du.used` with 154894188544 vs
154877411328 (16 MiB reserved). Assert `<=` instead.
`v vet` parsed files without `preserve_comptime_conditionals`, so the
parser dropped `$if` branches that do not match the host. The lines of
multi-line string literals inside those branches were then checked as
code, and space-indented assembly in the `$if macos && arm64` branches
of vlib/v/gen/arm64/native_*_test.v failed `v vet` on Linux.
Preserve the conditionals, like vfmt does, so the vetted AST matches
the source on every host.
* v3: carry source type IDs through resolution and assignment helpers
* v3: compare pointer spellings and index generic receivers by ID
* v3: avoid spelling allocation in declaration and method-value checks
* cgen: escape Windows header macros in V identifiers
* cgen: use TinyCC alloca for aligned stack values
* cgen: keep V identifiers from shadowing TinyCC alloca
* tests: check alloca escaping without changing existing spellings
* tests: compile TinyCC aligned locals with alloca names
* v: fix Darwin ABI and specialization support in cached builds
* v: preserve concrete collection and optional types during lowering
* driver: restore the TinyCC libgc store for macOS comma paths
* tests: honor VTMP and use finite recursive parity fixtures
* tools: fix strict builds, subcommand completion and MCP pagination
* tools: validate wrapped doc summaries and repair documentation examples
* v: preserve callback array types and complete commands after flags
* docs: clarify inferred aggregate ownership
* docs: require clone support before copying an inferred owner
* docs: qualify Copy for structs with owning fields
* cgen: omit unreferenced C struct layouts
* cgen: retain C layouts referenced by size and offset operators
* cgen: skip fixed array metadata for unused C structs
* cgen: retain C layouts used by fixed-array aliases
* tests: exercise referenced C binding in fixed array folding fixture
* tests: keep C layout opaque in extern declaration fixture
* fix C struct retention for auxiliary declarations
* cgen: omit unused C layouts from interface dispatch
* cgen: omit optional payloads from unused C structs
* transform: preserve local types in generic field loops
* docs: clarify type identity in generic field loops
* transform: preserve main alias and enum generic arguments
* transform: restrict join_path lowering to os
* docs: clarify local function names beside library functions
* transform: preserve local join_path bindings and helper reachability