Skip to content

feat(arxjit): resolve a compilable signature from Python annotations - #103

Open
Jaskirat-s7 wants to merge 2 commits into
arxlang:mainfrom
Jaskirat-s7:feat/arxjit-signature-reconcile
Open

feat(arxjit): resolve a compilable signature from Python annotations#103
Jaskirat-s7 wants to merge 2 commits into
arxlang:mainfrom
Jaskirat-s7:feat/arxjit-signature-reconcile

Conversation

@Jaskirat-s7

Copy link
Copy Markdown
Contributor

Follows #102. Closes the remaining open question in wiki Issue 2: "decide how Python annotations interact with explicit signatures."

The rules

New arxjit.reconcile decides which Signature a validated function will be compiled against:

given result
explicit signature= wins outright; annotations are not read at all
annotations only signature derived from them
neither reported at WARNING severity, function stays interpreted
some annotations, others missing or unsupported every remaining gap is an ERROR

Because an explicit signature short-circuits, a function may annotate freely without having to agree with it — there is deliberately no mismatch error, per the decision in the thread. The last row is the judgement call worth flagging: once a function annotates anything, it is read as asking to be compiled, so a gap elsewhere is an error rather than a silent fallback.

Annotations are read from the ast, not __annotations__

A module using from __future__ import annotations turns every annotation into a string at runtime, which would otherwise have to be evaluated back with eval_str. Reading the ast makes that irrelevant, and lets each diagnostic point at the exact annotation that caused it rather than at the function.

Only a bare int, float or bool is accepted; a subscripted (list[int]), dotted (np.float64) or quoted ("int") annotation is reported against its own location. One consequence worth naming: this can never produce i32 or f32, since Python has no annotation that distinguishes widths, so the narrow types stay explicit-signature only. That partly answers the "should we trim i32/f32?" question from #91 — they remain reachable, just not from annotations.

Positional-only parameters are included in the derived signature. Validation permits them and they are ordinary positional arguments to a compiled function, so leaving them out would silently drop an argument.

Fail-closed on argument shapes

Signature is a fixed list of positional argument types, so a variadic or keyword-only parameter has nowhere to go. Validation rejects those shapes first, which makes this unreachable through @jit — but resolve_signature is exported, and without a check it would leave the parameters out and hand back a signature that is quietly wrong (i64() for a function taking *args). It now reports that it cannot derive one instead. Four regression tests cover *args, **kwargs, keyword-only, and the mixed positional-plus-variadic case.

Location helpers moved

_char_column and _diagnostic move out of validation.py into a new arxjit.locations, now that a second stage builds diagnostics from ast nodes. Single-sourcing them keeps the two stages from drifting on the column contract (one-based Unicode characters, converted from ast's zero-based UTF-8 byte offsets at the boundary). No behaviour change — a move, plus a severity parameter so reconciliation can emit WARNING.

Note on scope

resolve_signature is exported and fully tested but not yet consulted by @jitcore.py is untouched here. Wiring it into the decorator, along with the strict flag and the warn-and-fall-back behaviour, is the next PR. Splitting it this way keeps the resolution rules reviewable on their own, separately from the decorator behaviour change.

Verification

169 tests, arxjit coverage 100%. All gates green locally: pytest+cov, ruff format/check, mypy strict (cd packages/arxjit && mypy src), bandit -iii -lll, vulture 80, mccabe 10, douki sync idempotent. Verified on CPython 3.10.19, 3.11.11 and 3.14.6.

Wiki Issue 2 leaves open how Python annotations interact with an explicit
signature. New arxjit.reconcile decides the Signature a validated
function will be compiled against: an explicit signature= wins outright
and the annotations are not read at all, so a function may annotate
freely without having to agree with it and there is deliberately no
mismatch error; otherwise the signature is derived from the annotations;
a function with neither is reported at WARNING severity and stays
interpreted, while a function that annotates some things but not others
is asking to be compiled, so every remaining gap is an ERROR.

Annotations are read from the ast rather than from __annotations__. A
module using "from __future__ import annotations" turns every annotation
into a string at runtime, which would otherwise have to be evaluated
back; reading the ast makes that irrelevant and lets each diagnostic
point at the exact annotation that caused it. Only a bare int, float or
bool is accepted, so a subscripted, dotted or quoted annotation is
reported against its own location. Note this can never produce i32 or
f32, since Python has no annotation that distinguishes widths: the
narrow types stay explicit-signature only.

Positional-only parameters are included in the derived signature.
Validation permits them and they are ordinary positional arguments to a
compiled function, so leaving them out would silently drop an argument.

Move the diagnostic location helpers out of validation into
arxjit.locations, now that a second stage builds diagnostics from ast
nodes, so the two cannot drift on the column contract.

resolve_signature is exported and tested but not yet consulted by @jit;
wiring it into the decorator is the next change.

160 tests, arxjit coverage 100%.
The douki schema accepts a parameter entry as either a string or a mapping
of type/description/optional/default/variadic. Three test helpers with
deliberately unannotated parameters were left with a bare key, which parses
as None and matches neither, so the douki-arxjit hook failed in CI while
reporting the files as unchanged.

Give those parameters a description; douki normalizes the string form to a
description mapping and is then idempotent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant