Short answer
The VAPI writer emits ? whenever the parameter's type, as resolved in the Vala AST at the moment the VAPI is written, is a nullable reference type. If you declared the parameter explicitly as nullable (string? name) in your own .vala source, that marker survives into --vapi output regardless of whether GObject Introspection data is present. GIRepository is not consulted when compiling your own sources — so introspection does not directly control the marker.
Why the output looks inconsistent
The variability you observed usually comes from symbol resolution, not from the VAPI writer itself. When the same symbol is visible both from your source and from a dependency's VAPI or GIR on the search path, the compiler can bind the reference to the introspected metadata version of that symbol. In that case the nullability comes from the GIR's (nullable) annotations — possibly modified by the dependency's Vala .metadata file — rather than from your declaration. A missing, stale, or differently-annotated GIR/metadata pair can therefore flip a parameter between nullable and non-null between two builds, which matches the "differs depending on whether introspection data is available" symptom.
Confirmed vs. likely
- Confirmed mechanism: explicit
T? in source → ? in --vapi output. Value types and non-null reference types are written without it.
- Likely explanation for your case: a name collision with an external symbol whose GIR-derived signature is being picked up, or a
.metadata override changing nullability. This is the part worth verifying on your machine.
- Uncertain: exact behavior for inferred or generic type parameters has changed across valac releases; treat version-specific output as unverified here.
Forcing preservation
There is no compiler flag that forces nullability into the VAPI. The reliable controls are:
- Declare the parameter nullable explicitly in source (
string? name).
- If a conflicting external symbol exists, avoid re-exporting it, or fix the dependency's
.metadata file so its GIR-derived nullability matches.
- Generate VAPI and GIR together (
valac --vapi=... --gir=... --library=...) so consumers see one consistent signature.
Verifying on your setup
valac --version
valac --vapi=out.vapi file.vala # inspect out.vapi for the '?'
# repeat with and without the suspect dependency:
valac --vapi=out2.vapi --pkg some-dep file.vala
Compare out.vapi and out2.vapi; a differing signature points at GIR/metadata resolution rather than the writer. Also check the dependency's .metadata for nullable overrides. Note that Vala nullability is advisory — a missing ? changes consumer-side warnings, not runtime safety.