V Compiler: Generic function instantiations silently dropped when multiple modules share the same name #18
Describe the bug
When a V project contains two or more modules with the same module name (declaring identical module <name>) located in different directory paths, and both modules:
- Define struct types with identical short names (e.g. both define
LoginByAccountResp) - Call the same generic functions with those types (e.g.
json.decode[T],json_success_200[T],ctx.json[T])...the V compiler may fail to generate C code for some of the generic function instantiations. The C compilation then fails because the generated C code calls functions that were never emitted.The bug is non‑deterministic — it triggers with higher probability when more duplicate‑name module pairs exist (threshold ≈ 6+ pairs), but can occasionally trigger with as few as 5 pairs.
Reproduction Steps
A self‑contained reproduction is available at repro/:
repro/
├── v.mod
├── main.v
├── common/api/ (generic struct + generic helper functions)
├── structs/ (Context embedding veb.Context)
├── route/ (24 route files, each importing one module)
├── m0a/common/ ~ m11b/common/
│ (12 module pairs = 24 modules)
│ all named `common`, all with same 5 type names
Compile:
cd repro/ && v .
Expected output: ok (clean build)
Actual output (100% reproducible with 12+ pairs):
cc: ... warning: implicit declaration of function
'common__api__json_success_200_T_m6a__common__Type_cResp'
cc: ... error: cannot convert 'int' to
'struct common__api__ApiSuccessResponse_T_m6a__common__Type_cResp'
The exact function that goes missing varies between runs (json_success_200<...>, json.decode<...>, ctx.json<...> for different type parameters).
Expected Behavior
success
Current Behavior
================== C compilation error (from tcc): ==============
cc: /tmp/v_1000/repro.01KTNKD8B9FG731TRV2N6QT47H.tmp.c:37783: warning: implicit declaration of function 'x__json2__decode_T_m7a__common__Type_dReq'
cc: /tmp/v_1000/repro.01KTNKD8B9FG731TRV2N6QT47H.tmp.c:37783: error: '{' expected (got ";")
...
cc: /tmp/v_1000/repro.01KTNKD8B9FG731TRV2N6QT47H.tmp.c:37783: error: '{' expected (got ";")
(note: the original output was 21 lines long; it was truncated to its first 2 lines + the last line)
=================================================================
Try passing `-g` when compiling, to see a .v file:line information, that correlates more with the C error.
(Alternatively, pass `-show-c-output`, to print the full C error message).
C compiler bug report was not sent to https://bugs.vlang.io/bug-report: v bug-report-send: net.mbedtls SSLConn.dial, failed to connect to host
builder error:
==================
C error found while compiling generated C code.
This can be caused by invalid C interop code, C compiler flags, or a V compiler bug.
If your code is pure V and this still happens, please report it using `v bug file.v`,
or goto https://github.com/vlang/v/issues/new/choose .
You can also use #help on Discord: https://discord.gg/vlang .
Possible Solution
The V compiler uses a type‑name‑keyed map internally to track which generic function instantiations need to be generated in C code.
When two modules in different directories both declare module common, and both define a type called Type_aResp, the compiler's internal type‑tracking map uses the short type name (Type_aResp) as the key, rather than the fully‑qualified name (m0a.common.Type_aResp vs m0b.common.Type_aResp).
As a result, the second module processed overwrites the first module's type entry in the map. The C code generator then only emits ONE C function for json_success_200<Type_aResp> (for the "winning" module), while both modules' generated C code call this function. The losing module's call site references a function that was never generated, causing the C compiler to report an implicit declaration error.
This explains all observed behaviour:
| Observation | Explanation |
|---|---|
| Non‑deterministic | File processing order varies between runs |
| Some instantiations work, some don't | Which module "wins" per type depends on processing order |
| Different generic functions affected (json_success_200, json.decode, ctx.json) | All use the same internal type‑tracking map |
| Error only in C compilation, not V compilation | V sees both calls as valid; C is missing the actual function body |
| More module pairs = higher failure rate | More collisions increase the chance of at least one being wrong |
Workaround
Avoid sharing short type names across modules that have the same module name. Use unique struct type names for each module, or avoid having two modules with identical names.
In the specific project that triggered this bug, the workaround applied was:
- Replace
api.json_success_200(result)calls with directapi.ApiSuccessResponse[T]{...}struct literals — this avoids calling the generic function, so the compiler only needs to instantiate the generic struct (which it does correctly). - Add
constinstantiation‑forcing forjson.decode[T]calls that cannot be eliminated.A cleaner fix would be for the V compiler to key generic instantiations by fully‑qualified type name (module path + type name) rather than by the short type name alone.
Additional Information/Context
No response
V version
V 0.5.1 a063235
Environment details (OS name and version, etc.)
|V full version |V 0.5.1 3767be3e1f7b0cbbfe0d938c69d944e687f2e601.a063235 |:-------------------|:------------------- |OS |linux, Deepin 25 |Processor |20 cpus, 64bit, little endian, 13th Gen Intel(R) Core(TM) i5-13500 |Memory |8.68GB/31.02GB | | |V executable |/home/Jengro/opt/v/v |V last modified time|2026-06-09 05:52:31 | | |V home dir |OK, value: /home/Jengro/opt/v |VMODULES |OK, value: /home/Jengro/.vmodules |VTMP |OK, value: /tmp/v_1000 |Current working dir |OK, value: /home/Jengro | | |Git version |git version 2.51.0 |V git status |0.5.1-1874-ga0632356 |.git/config present |true | | |cc version |cc (Deepin 12.3.0-17deepin17) 12.3.0 |gcc version |gcc (Deepin 12.3.0-17deepin17) 12.3.0 |clang version |Deepin clang version 17.0.6 (5deepin7) |tcc version |tcc version 0.9.28rc 2025-02-13 HEAD@f8bd136d (x86_64 Linux) |tcc git status |thirdparty-linux-amd64 696c1d84 |emcc version |N/A |glibc version |ldd (Debian GLIBC 2.38-6deepin24) 2.38
[!NOTE] You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote. Other reactions and those to comments will not be taken into account.