bloqade.gemini.common.validation.call_site
call_site¶
bloqade.gemini.common.validation.call_site
Attribute validation errors back to the call that dragged them in.
Inlining is lossy in exactly the way that matters for diagnostics. InlinePass
splices a callee’s body into the caller and the invoke disappears, but the
spliced statements keep the callee’s source info. If one of them then fails
validation, the error names a file the user never opened::
Gemini Logical Validation:
- Non-constant iterable in for loop is not supported … File ”…/gemini/logical/stdlib/post_processing.py”, line 12, col 65 │ return logical.default_post_processing(qbs)
and the excerpt is a chimera. ValidationError.attach overwrites
source.lineno_begin with the entry method’s and resolves the source lines
against the entry’s py_func, so the reader gets the callee’s filename against
the caller’s line numbers and the caller’s code — line 12 above is in the
caller, not in post_processing.py. When the callee’s line number falls past
the end of the caller, which is the usual case, the excerpt comes out empty and
the diagnostic is a bare filename. That is bloqade-internal#449.
The lowering is not wrong to reject the program: default_post_processing
loops over range(1, len(register)), which only aggressive_unroll=True can
flatten. What is missing is which call introduced the offending code and what
to do about it. So InlineOrigins records the call sites before inlining
destroys them, and afterwards folds them back into the messages and re-points the
excerpt at the kernel the statement actually came from.
A statement is identified with the kernel it was written in by
(source.file, source.lineno_begin) — filename plus that kernel’s offset into
it. File alone is not enough: a helper defined a few lines above the kernel that
calls it is the common case, and it shares the caller’s filename while needing
exactly the same attribution.
Which call to blame comes from the static call graph: every method reachable from the entry inherits the call site of the first hop that reaches it, breadth-first so the shallowest — the one written in the user’s own kernel — wins. That is coarse enough to survive nested inlining and precise enough to name the one call the user can change.