Jaclang Release Notes#
This document provides a summary of new features, improvements, and bug fixes in each version of Jaclang. For details on changes that might require updates to your existing code, please refer to the Breaking Changes page.
jaclang 0.16.7 (Latest Release)#
Breaking Changes#
- Lambda parameters now require a known type (#6804): A lambda whose parameter type cannot be inferred from context, such as a bare
f = lambda x: -x;, is now a type error (E1119) instead of silently typing the parameter as unknown. Annotate the parameter (lambda x: int : x + 1; the return type is still inferred) or use the lambda where its type is fixed, e.g. as a typed callback (sorted(xs, key=lambda x: ...)) or bound to aCallable-typed target.
jaclang 0.16.6#
New Features#
- Optimistic concurrency for concurrent check-then-create (#6266): A walker or function endpoint that does "look it up, create it if missing" against the same node no longer yields duplicate children under concurrency. Each node carries a version; an edge-list write to a node the request read is applied with a compare-and-swap, and the request boundary aborts and replays the loser so find-or-create converges on the winner. A blind append (no preceding read) still merges lock-free. The read is recorded independently of how the traversal resolves -- the topology-index/backend fast path and edge-reference reads (
[edge -->]) take the dependency just as the linear edge-walk does -- and the read snapshot advances after each commit, so a request that commits more than once never conflicts with itself. A newon_commit(...)ambient builtin defers external side effects to run exactly once after a successful commit (and is discarded on abort/replay). The policy is configurable injac.tomlunder[serve]:on_conflict(retry/fail),conflict_max_attempts, andconflict_backoff_ms. The losing unit of work is atomic:SqliteMemory.apply()rolls the whole transaction back on a conflict, so a lost race leaves the store unchanged with no orphaned child, and diagnostics accrued by an aborted attempt are cleared so a converged replay's200response carries no stale permission warnings.jac db fscknow also sweeps half-linked edges (cited by only one of their two endpoints) and the nodes they strand, reclaiming any residual orphan. See Persistence -> Concurrent writes. - New
clientproject kind: Added aclientproject kind for browser-only apps with no backend.jac runserves it as a static page (no API server), andjac build/jac start(via jac-client) produce a portable, self-contained dist.
Bug Fixes#
- Fix:
globdeclared in an impl annex now resolves on the native (.na.jac) backend (#6754): A global variable declared in an impl-annex file (*.na.impl.jac), rather than the head.na.jacfile, is now registered in the native module's globals and resolved by references in annexed ability bodies, matching the@svand ecmascript backends. Previously such a reference failed to lower witherror[E5090]: Native pathway does not yet support expression 'Name'(or'BinaryExpr'), an opaque diagnostic that blamed the reference expression rather than the unresolved global, and produced a silently-degraded build when the global only fed a loop bound. The root cause was annex weaving being re-derived per backend at codegen time:@sv/ecmascript wove the annex bodies in (_merge_module_bodies), while native's statement scan (iter_context_stmts) walked only the head module's body. Annex handling is now centralized in the front end: a singleModuleCodegenPass.iter_woven_segmentsis the one source of truth for which statements an annexed module contributes (all three backends derive their woven view from it), a native (.na.jac) head's impl/test annexes are coerced to NATIVE code-context once attached -- keyed off the head, so both the canonical barefoo.impl.jacand the variant-specificfoo.na.impl.jacforms are covered -- and type inference runs over annex modules so native codegen finds the types it needs (covering both the same-module and the imported-module cases). - Fix:
skipin an impl-annex walker event ability no longer breaks nativejac test(#6794): A walker event ability that usesskipand is split decl/impl -- thecan flood with Area entry;declaration in the head.na.jacand its body in animpl Walker.flood with Area entry {...}annex -- compiled and ran correctly on its own, but a native test (or any module) importing it failed withRuntimeError: native engine unavailablefor every test. The native capability authority acceptsskipinside a walkerwith X entryevent ability (where it lowers as a no-value return) and rejects it in a value-returning function; it found the enclosing ability by walking up to anAbilitynode. When the body lives in an annex theskipis enclosed by anImplDef(carrying theEventSignaturein itsspec), not an inlineAbility, so onceiter_context_stmtsbegan scanning annex bodies (#6754) the authority reached the annexskip, saw no enclosingAbility, and falsely flagged it as unsupported. That dropped the imported module's LLVM IR (E5090), so linking the importer failed (E5024) and no JIT engine was produced. The capability check now recognizes the decl/impl-split form: askipwhose nearest enclosingImplDefhas anEventSignatureis accepted exactly like an inline event-abilityskip, while askipin a value-returning function (inline or annex) is still rejected. - Fix:
sv importof a node/obj type no longer crashes the client bundle (#6795): A.cl.jacfile thatsv imports a function together with the node/obj type it returns used to emit two declarations with the same name in the compiled JS, anasync function <Type>()RPC stub and the wireclass <Type>, which crashed the bundler withThe symbol "<Type>" has already been declaredeven thoughjac checkpassed. The ecmascript codegen (EsastGenPass._generate_sv_import_stubs) was generating an HTTP RPC stub for every imported item regardless of kind; it now skips any item whose resolved symbol is a type (node/obj/edge/enum) or a walker, since a node/obj/edge already lowers to a wireclass, an enum to a frozen object, and a walker is reached through__jacSpawn. Onlydef:pubabilities get an RPC stub. This mirrors the server-side (sv -> sv) Python-path fix from #6710.
jaclang 0.16.5#
New Features#
- Feature:
jac ejectcompiles a project into a runnable FastAPI + JavaScript app:jac ejectnow writes a project-internalejected/folder split into a FastAPIbackend/(each.sv.jac/.jacwalker compiled to Python and exposed asPOST /walker/<Name>, functions asPOST /function/<name>) and afrontend/(the.cl.jacUI compiled to JavaScript with a Vite setup). The backend runs the walkers on the installedjaclangruntime and uses jaclang-native auth:/user/register+/user/loginviaUserManager, with:pubwalkers open and the rest token-gated (each user gets their own persistent root graph). Oneuvicorn main:appserves the API and the built frontend. Re-running regenerates the folder without--force(it carries a.jac-ejectedmarker), and the output directory is overridable via[eject].outputin jac.toml. jac installnow usesuvas its pip backend when available: Dependency installs run throughuv pipfor significantly faster resolution and downloads. A new--no-uvflag onjac install(orJAC_NO_UV=1env var for all install paths) opts back to plain pip.jac install <pkg>installs packages into the activated environment (#6703):jac installnow accepts package arguments (e.g.jac install numpy pandas), routing them directly to pip against the currently activated Python environment - nojac.tomlmodification. Existing no-arg behavior (jac installfrom a project directory) is unchanged.- Feature: User-defined iterators in native code (#6403): Any
objthat implements__iter__/__next__can now be iterated in the native (na {}→ LLVM) backend. It works inforloops, via first-classiter()/next(), through themap()adapter, and across function boundaries asIterator[T], withStopIterationending iteration as in Python.
Bug Fixes#
- Fix: Native and clib type annotations now reject unresolved type names instead of silently defaulting to i64 (issue #6676): C-library import declarations and native-context annotations (
.na.jac/na {}) whose parameter, return, or field type names the compiler cannot resolve are now a hardE2018error atjac check(not just aW2001warning) and are reported honestly atnacompileviaE2018/E5090, instead of being silently widened to a signedi64at the FFI boundary with a wrong ABI and no diagnostic. A clib declaration with no return arrow (def f(x: T);) now correctly lowers its return tovoid(matching an explicit-> None) instead of silently declaring the void C function as returning a signedi64. - Fix: Unresolved object-field type names are now named in the diagnostic, not rendered as
<Unknown>: an object field whose annotation names an unresolved type (a typedef'd C handle, an unmodeled width likeuint32_t) reaches native codegen through a non-expression node, so theE2018used to read the uselessUndefined name '<Unknown>'--str()of the checker'sUnknownType-- losing the name the user wrote. The label now recovers it from the field's type tag, so the error points at the actual type name. (The front-endE2018covers clib/native signature positions but not object field declarations, so the codegen pass is currently the sole catcher for bad field types.) - Fix: Clear diagnostic for a re-wrapped expression in a JSX slot: Re-wrapping a single expression in extra
{...}inside a JSX slot body ({"text"},{n + 1},{<li/>}) used to fall through to a misleadingE0002 Missing ';'that pointed at the following line, while the control-flow form ({if ...}) got a clear error at the{. The redundant expression wrap is now reported as a dedicated error (E2028) pointing at the{itself: the expression sibling of the control-flowE2023, co-located in the parser so the whole redundant-slot-wrap rule lives in one place. The parser recovers by dropping the redundant braces (exactly as the control-flow form does), so the rest of the file still parses without an error cascade. Genuine collection literals ({1, 2},{a: 1}) carry a,/:and are left untouched (#6733). - Fix: Bare expression statements in JSX slots no longer silently render nothing: Inside a JSX
{...}statement slot, a bare expression statement (a string, f-string, identifier, or call) was evaluated and discarded instead of being added to the slot's children, so conditional content written that way compiled but rendered nothing. Every value-producing expression statement directly inside a slot body is now accumulated as a child, consistent with the{expr}slot and bare JSX elements. The misleadingW0060"docstring" warning that previously fired on such strings is suppressed inside slot bodies (it still fires for genuine misplaced docstrings elsewhere). - Fix:
by postinitfields no longer show a false warning: A field declared withhas x: T by postinitwas reporting a spuriousW1051("Expression type could not be resolved") warning even though the code was correct. The warning is no longer raised. - Fix:
jac run/jac starton a.sv.jacor.cl.jacentry: Running or serving a server (.sv.jac) or client (.cl.jac) file directly by path failed withCannot find module main.sv/No module named 'main.sv', becauseproc_filestripped only the trailing.jacfrom the compound codespace suffix and leftmain.sv. It now strips the full suffix for all three compound codespaces (.na.jac/.sv.jac/.cl.jac) so the entry resolves to its bare module stem. - Fix: Native
bytesconcatenation and thebytes()/bytearray()constructors:bytes + bytesno longer crashes native code generation, andbytes([...])/bytearray([...])now return a length-aware value that preserves embedded NUL bytes, matching the interpreter. - Fix: complete the native
byteslength-aware migration (issue #6749): the native (.na.jacto LLVM) backend madebytesa distinct length-aware struct in #6727/#6745, but seven operations still assumed the old NUL-terminatedi8*representation and either crashed, failed to compile, or returned silently wrong results. They are now all length-aware on the struct, matching the sv (CPython) backend including embedded NUL bytes: repeat (b * n), membership (x in b, viamemmemso it sees past NULs), iteration (for x in banditer(b), yieldingintbyte values), truthiness (if bandbool(b)are emptiness, not a null-pointer test, so emptybytesis correctly falsy),str(b)(the exact CPythonb'...'repr with\t/\n/\r/\\/\xNNescapes and CPython quote selection),bytesdict keys ({b: v}andd[b]compare by value/length instead of pointer identity, so distinct literals and keys differing only by an embedded NUL or length resolve correctly), and ordering (</>/<=/>=, lexicographic over unsigned bytes with a length tiebreak). - Fix: null-safe
bytescomparison againstNoneon the native backend (issue #6751): comparing abytes | Nonevalue tobyteswhen the value isNoneno longer crashes or silently drops the result on the native (.na.jacto LLVM) backend. The length-awarebytescomparison helpers dereferenced both operands unconditionally, so aNone(a nullbytespointer) caused a segfault for a branchy union and silent empty output for a constantNone(the null load was undefined behavior the optimizer deleted).==/!=now follow CPython identity semantics (equal only when both operands areNone, soNone == b"x"isFalseandNone == NoneisTrue) and only read the bytes contents when both operands are non-null, while ordering (</>/<=/>=) againstNoneraisesTypeErrorto match CPython instead of dereferencing null. - Type Checker: Infer unannotated lambda parameter types from the expected callable: A lambda passed to a generic builtin like
sorted(rows, key=lambda d: d["n"])no longer reports a falseerror[E1054]("No matching overload"). The lambda parameter's type is now solved from the call context (the same waydef-typed callbacks already worked), so the body type-checks, hover shows the inferred type, and the result type is correct. Works forsorted,max,min,filter,zip, andenumerate.
Documentation#
- Docs: Client skill-guide fixes: Corrected contradictions and non-compiling examples in the client (cl) skill guides that led AI code generation to emit broken Jac (file-based-vs-manual routing rule, subscript hook/server-result access, and verified sorting + number-formatting recipes).
jaclang 0.16.4#
New Features#
- Feature: Kind-aware
jac create:jac create --kind <kind>scaffolds a project for a specific project kind, stamping[project] kindintojac.tomland laying the entry-point in the right codespace so the new project's barejac rundispatches deterministically (execute / serve / build). The eight core kinds (cli, native-app, native-binary, shared-library, api-service, microservices, pypi-package, npm-package) ship withjaclang; plugin kinds fail fast with an install hint.--kindand--useare mutually exclusive, andjac create --list_jacpacksnow lists the available kinds and named variants. Theregister_project_templatehook is now aggregating (was first-result), so every installed plugin contributes its kind templates. - Feature:
isinstanceon ananyvalue in native code: The native (na {}→ LLVM) backend now lowersisinstance(x, T)forint,bool,float,str,list,dict,set, and tuples of those (isinstance(x, (int, str))). It works whetherxis anany/JacVal(runtime tag check) or a concrete value (folds to a constant), reusing the existingJacValbox scheme.boolis now boxed under its own tag so it stays distinguishable fromint, matching CPython'sbool ⊂ int(isinstance(True, bool)andisinstance(True, int)are both true,isinstance(5, bool)is false). Assigning an explicitanyinto a concrete-typed slot (z: int = a) is also allowed in native context now, unboxing at the coercion seam to stay congruent with the sv pathway. This unblocks generic-over-anyMechanism-B stdlib code (json,collections, …) in the native standard-library roadmap (#6404). - Feature: Crash-atomic graph writes: Graph mutations now flush as a single unit of work in referential-integrity order, so a crash mid-request can no longer leave dangling references, and a walker that fails no longer persists partial mutations.
jac browseactionability, console access, WebGL, and a real viewport: closes the four load-bearing gaps from head-to-head QA vs agent-browser (jaseci-labs/jaseci#6597). Offscreen interactions no longer silently no-op while reporting success - everyclick/type/filltarget (CSS or@ref) is scrolled into view and must be visible, position-stable, inside the viewport, and the top element at its action point perelementFromPoint, else the command hard-errors with the reason. A newconsole [--clear]action reads buffered console/log/exception output by draining Chrome's per-context replay on attach (no resident listener). Headless launch now enables software WebGL via SwiftShader (canvas/wasm content renders) and defaults to an exact 1280x720 viewport (--viewport WxHto change), calibrated against window-chrome height loss. Also addswait <ms|selector>andscroll <dir|top|bottom|selector> [px]actions, and Chrome discovery on macOS. (jaseci-labs/jaseci#6635)- Native OSP: graph navigation, cross-module archetypes, and edge disconnect (issue #6642):
jac nacompilenow lowers the remaining Object-Spatial constructs a real multi-module graph app needs. Edge-reference trailers lower as general expressions in every form (typed and untyped,[edge ...]edge objects, trailing node-type filters like[--> [?:Area]], and any origin including a plaindefparameter), not only as a barevisittarget.del eon an edge now disconnects it from the in-memory adjacency instead of silently doing nothing. Andspawn/connect/ edge-ref on archetypes imported from another native module now work across the boundary: imported OSP archetypes materialize into the consumer with deterministic sorted type tags, so a library walker (compiled with the library's tags) and the consumer's graph wiring agree at runtime without recompiling library code. - Native OSP: general cross-module type tags and bare-edge connects (issue #6642): native Object-Spatial type tags are now globally-stable content hashes instead of per-module positions, so spawning, connecting, and edge-referencing archetypes imported from another native module works in every configuration, including a consumer that defines its own OSP archetypes or imports OSP from more than one library (previously these shifted the shared tags and were rejected). And a bare-edge-type connect,
a +>: Edge :+> bwithout the(), now records the edge with its type instead of silently dropping it, matching theEdge()form and the server/client pathways. - Feature: Read-path dangling-reference healing,
jac db fsck, and aget_persistent_memoryhook (#6619): Graph traversal now resolve-or-skips a dangling reference instead of raising "is not a valid reference" on every read shape (the #6587 incident): a genuinely-missing referent is filed into the quarantine store under a newDANGLING_REFreason code and its stale citation pruned, staged as a normal edge-list-delta intent so even a read-only request self-heals on commit; a recoverable (schema-drift / class-missing) quarantine is left intact forjac db recover. A newjac db fsckaction scans for dangling references and orphan edges/nodes (read-only by default) and, withjac db fsck repair, prunes danglers and collects orphans in one transaction. A newget_persistent_memoryplugin hookspec lets a DB-backend plugin supply its ownPersistentMemory(consulted byTieredMemorybefore falling back toSqliteMemory) instead of cloning the whole execution-context joint. - Typed edge endpoints (issue #6657): an
edgemay now declare the source and target node types it connects, after a:and using the traversal arrow --edge Follow: Profile --> Profile {}. A neighbour traversal then infers the declared node type instead ofany:[x ->:Follow:->]islist[Profile](target) and[x <-:Follow:<-]islist[Profile](source), with no[?:Type]filter -- so a field read off the result resolves statically and reads correctly on the native backend. The declaration is opt-in (an untypededge Link {}keepsany -> anyconnectivity andlist[any]results), is a bound (Profileor a subtype), and is inherited by subtype edges (edge TimedFollow(Follow) {}) unless re-declared; the()after the name stays reserved for edge inheritance. The clause is edge-only -- placing it on anode/walker/objis a newE2027. - Feature: Project-root-absolute imports and scoped
jac testdiscovery: No-dot imports (e.g.import from engine.math.vec3 { Vec3 }) now resolve against the project root (the nearestjac.tomldirectory) from any depth, so a test in atests/subdirectory uses the same import path it would at the repo root. No-argumentjac testhonors[test] directoryinjac.toml, scoping collection to that directory so application modules whose top-levelwith entryruns on import are not executed during test discovery. Native and interpreted resolution now break same-name collisions identically, preferring the importing file's own directory. - Feature: Kind-aware
jac run: In a project, a barejac run(no file) resolves the project kind from[project] kindinjac.toml(or infers it from the entry-point's codespace) and does the natural action for that kind - execute (cli/native-app), serve (api-service/fullstack/...), or build (native-binary/shared-library/pypi/npm packages).jac run --showprints the resolved plan and equivalent primitive command without running it. Explicitjac run <file>is unchanged. - Feature: first-class
iter()/next()anditer()over range/dict/set in native code: The native (na {}/.na.jacto LLVM) backend now supports first-class iterator values.it = iter(x); next(it)andfor x in itover a stored iterator work, withnext()raisingStopIterationat exhaustion. The native type checker infersiter(x) -> Iterator[T]andnext(it) -> Tby resolving the element type through the same__iter__/__next__machinery the for-loop uses, withIterator[T]/Iterable[T]stubs added to the native ambient surface.iter()now also works overrange(both directions),dict(yielding keys), andset, in addition to the existinglist/str/map/filter/enumerate/zipforms. Plain container for-loops keep their inlined fast paths, so there is no hot-path change. This is phase 2 and M1 of the native iterator protocol (#6403). - Feature: GDB-debuggable native binaries (
jac nacompile -g): Passing-g/--debugtojac nacompileemits DWARF line tables andDISubprograms and has the pure-Python ELF linker relocate the.debug_*sections and write a.symtabplus a full section header table, sogdb/lldbcan resolve source-level breakpoints and backtraces.-gimplies--scrubso a cached non-debug build isn't reused; non-debug builds remain byte-identical to before. - Feature: length-aware
bytes, thestructmodule, and binary file reads in native code (issue #6404): The native (na {}/.na.jacto LLVM) backend gains the Python-congruent byte-handling surface a binary parser needs, so the sameimport struct; struct.unpack(...)andopen(path, "rb").read()source compiles and runs on both the sv (CPython) and na (LLVM) backends.bytesis now a distinct length-aware type (a pointer to{ i64 len, [N x i8] data }) instead of an alias for the NUL-terminatedstr, so embedded NUL bytes survive andlen()is exact: byte-string literals, indexing (returning anint, with negative and bounds-checked indices), slicing (returningbytes),==/!=,str.encode(),bytes.decode(),int.from_bytes()andint.to_bytes()all key off the explicit length (the oldfrom_bytesusedstrlenand truncated at the first NUL). Thestructmodule lowers a compile-time format literal to byte-exact, host-independent reinterpretation of ints and floats with little/big-endian, repeat counts, and pad bytes:unpackreturns a tuple,packreturnsbytes, andcalcsizereturns the byte width.open(path, "rb")returns aBinaryFilewhoseread()/readline()yield length-awarebytesand whosewrite()takesbytes(mirroring CPython's BufferedReader vs TextIOWrapper split), modeled with anopenoverload in the native ambient stubs so the static type and the emitted struct always agree; text-modeopen()is unchanged.
Bug Fixes#
- Fix: Catch bare re-declarations in
.impl.jacfiles: Added a new E2026 compiler diagnostic so baredef foo() { ... }definitions in.impl.jacfiles are flagged instead of silently failing to link to their declaration stubs. Use animplblock to provide the implementation body. The diagnostic is emitted once per offending definition even when cross-variantconnect_implswould otherwise report it multiple times. - Fix: Direct calls to JSX-returning functions now normalize to component props: Functions that
return <JsxElement>may lower to aconst { a, b } = propsdestructuring pattern. Direct calls likeGreeting("World"), keyword calls likeGreeting(name="World"), and positional spread calls likeGreeting(*vals)now compile to the same single-props ABI that JSX runtime calls use, instead of passing the raw positional shape through. - Fix: console
cyan/dimSpan roles render unstyled under jac-super: Facade and Rich renderer now use semantic roleshighlightandmutedinstead of raw color names, so styled chrome matches across ANSI and Rich backends. - Fix:
jac2pycrash on f-strings with single-quoted literals around interpolation (#6564):PyastGenPass.exit_multi_stringno longer wraps an already-generatedJoinedStrinside a secondJoinedStrwhen the parser represents an f-string asMultiString([FString]). Mixed implicit concatenation ("pre" f"{x}" "post",f"a" f"b") now flattens to a spec-compliantJoinedStrwhose values are onlyConstant(str)andFormattedValue, soast.unparse()injac2pysucceeds. - Native OSP stopgap gate made sound (issue #6636):
visitover a list target (a walker-field list or an inline list literal) now enqueues the list's elements instead of aborting at runtime after a clean compile;skipnow lowers to a bare return (it was silently dropped, turning the documentedif here in self.seen { skip; }visit-once guard into dead code); edge connects lower in any module that uses OSP constructs rather than only modules that happen to define a walker; andspawn/connect/edge-ref on OSP archetypes imported from another native module fail with an E5090 whose help text names the defining module and the cross-module materialization gap (#6146) instead of a generic unsupported-op. - Native diagnostics:
jac check/jac nacompileparity (issue #6636 follow-ups):jac nacompilenow renders diagnostics with theirhelp:text (viaAlert.pretty_print, likejac check/jac run) instead of dropping it, so native E5090 guidance (the cross-module-OSP hint and the unsupported-stdlib-import hint) finally reaches the user; andskipin a value-returning native function (an object-spatial walker statement that native lowers only inside a walkerwithevent ability) is now caught atjac checktime via the sharednative_capability_violationsauthority, instead of passingcheckand failing only atjac nacompile. As a side effectjac checknow also previews the other structural native gaps (non-allowlisted imports, structural match patterns). - Fix: spurious W1051 on named entry-block labels (#6644): A named entry block such as
with entry:__main__orwith entry:foono longer emits a bogus W1051 ("Expression type could not be resolved") on the entry-point label. The label is aModuleCode.nameName node with no symbol, not a variable reference; the type checker'sexit_namehandler now skips it alongside the other label-like Name nodes (member-access targets, keyword-argument keys, type-annotation tags, JSX names) instead of type-evaluating it to Unknown. This warning previously fired on the idiomaticwith entry:__main__guard of nearly every runnable Jac program. - Native OSP: reading a field off an edge-ref / node-ref result (issue #6646): a graph navigation like
[edge here ->:Portal:->]or[here --> [?:Area]]returns its elements as opaque handles, so reading a field off one (ports[0].cx,for e in [...] { e.cx }, a[?:Area]-filterednodes[0].name) used to drop to E5090 or crash the compiler with'IntType' object has no attribute 'is_opaque'. Native now recovers the archetype struct pointer from the handle, so edge fields and filtered node fields read correctly; an unfiltered node-ref element (typedany) stays a clean E5090 instead of an uncaught traceback. - Native OSP: actionable error for field access on an untyped neighbour traversal (issue #6646): reading a field off an unfiltered node-ref element (
[here ->:Portal:->][0].tag) cannot resolve a layout on native, because an edge may connect any node types so the neighbour's element type isany. That access still E5090s, but the diagnostic now explains the cause and points to the only sound fix today, a[?:Type]node filter (e.g.[... -->[?:NodeType]]), instead of a bare "does not yet support attribute access". The longer-term language fix, typed edge endpoints, is tracked in issue #6657. - Native OSP: cross-module node field layout is now sound (issue #6659): a
node/walker/edgearchetype imported from another native module is now laid out identically to its defining module, fixing three cross-module corruption bugs. (1) A consumer that only constructs and reads an imported node (nospawn/connect/visit, so it does not "use OSP") reserved no OSP header, soa.fieldread the vtable slot and returned garbage; imported archetypes now always reserve the header to match the defining module, while loading the kernel stays gated on real OSP usage. (2) Writing an inherited field of an imported subtype (p.yaw = ...,p.pitch += ...) raisedE5090because the consumer's layout registry never saw the imported base classes; the importing module now copies each imported module's archetype hierarchy and MRO, so inherited fields resolve for both reads and writes (imported ancestors are also laid out before their subtypes). (3) A cross-moduleArea | Nonereturn, narrowed withis not Noneand then read, passed to anArea-typed parameter, or used as aspawntarget, returned garbage or crashed at runtime; with the layout sound it now agrees with the@svpathway. - JS codegen:
except Exceptioncatches native JS errors (issue #6656): On the ECMAScript target,try { ... } except Exception { ... }lowered to a typed__jac_e instanceof _jac.exc.Exceptionguard. Native JS throws (aSyntaxErrorfromJSON.parse, an interopTypeError, host/libraryErrors, thrown non-Errorvalues) are not part of the_jac.excclass hierarchy, so they fell through to the re-throwelse, silently breaking the common best-efforttry { … } except Exception { }resilience pattern and diverging from both Python and the pre-0.16 codegen. The generated runtime now exposes a_jac.exc.matches(e, name)predicate (derived fromJAC_TYPE_REGISTRY) encoding Python catch semantics:BaseException(and bareexcept) catches everything;Exceptioncatches every JacExceptionplus any non-Jac throw; a narrower builtin stays a hierarchy-awareinstanceofso native errors are not swallowed by e.g.except ValueError; and a user-defined exception archetype emits__jac_e instanceof Xinstead of the oldif (true)catch-all that caught every exception. - Fix: JIR-reloaded
FuncCallkeepscallee_decl/call_kind(issue #6656):JirReader._read_noderebuilds every AST node withcls.__new__, which skips__init__/postinit, then re-seeds each node type's postinit-only fields;FuncCallhad no such branch, so itscallee_declandcall_kindvanished on a JIR-cache roundtrip (absent, notNone). The native and ECMAScript (client/JS) backends read.callee_decloff the call node to lower kwargs into ordered positional args and have no re-resolution fallback, so on a warm build they crashed with'FuncCall' object has no attribute 'callee_decl'. The reader now seeds both postinit defaults forFuncCall, so a JIR-reloaded call carries a clean "unresolved" declaration (exactly what a builtin/unresolved target gets) instead of a missing attribute, and warm builds no longer crash. - Native:
is not Noneflow narrowing now applies on the imported-module path (issue #6667): aT | Noneguarded byif x is not None { ... x.field ... }(or theis notternary) narrowed toTwhen the code was the entry module passed tojac nacompile, but stayedT | None(E1099/E1115/E1053) when the same code lived in animported module -- blocking any multi-module native app that returns optionals from library code and consumes them elsewhere. Flow narrowing inside the type checker walks the control-flow graph (bb_in/bb_out), which a full compile builds viaCFGBuildPass; an imported dependency was compiled symtab-only (CFG skipped) yet native still ran inference on it (the cross-module linker lowers a provider's annotations throughExpr.type), so the narrowing silently no-op'd. The CFG is now built before inference whenever it was not already built (gated onsymtab_ir_only, so the entry path is untouched), making imported-module narrowing identical to the entry path. - Native OSP: kernel links when an imported module does the spawn/visit (issue #6669): a layered native binary whose Object-Spatial
spawn/visitlives in an imported module rather than the thin entry now links the OSP kernel and runs, instead of building cleanly but failing at load withundefined symbol: __jac_glob_init_osp_kernel. - Native:
jac checknow surfaces untyped-traversal and multi-hop edge-ref errors that previously only failed atjac nacompile(issues #6646 / #6636): a field read off an untyped (any) graph-traversal handle (e.g.nbrs[0].fieldwherenbrs = [here -->]) and a multi-hop / value-compare-filter edge reference ([here -->-->],[?:Area(x > 1)]) were rejected withE5090only by the native backend, whichjac checknever runs (it compiles with codegen off). Both checks now live in the centralnative_capability_violationsauthority -- detected structurally from the checker type / AST shape -- so they report identically underjac checkandjac nacompile. The now-redundant codegen guards (plus a redundantMatchOralternative check) are removed. - Fix: Native OSP graph survives a cross-module spawn (#6678): when the graph was built in one module (e.g. a
build_arena(world)helper) and a walker was spawned in another, the spawning module's firstspawnwiped the graph --[world -->]collapsed from its real count to0after a single spawn (headless), or aborted rendering a freed graph (windowed). The OSP kernel's adjacency globals (_e_src/_walk_stack) are link-merged singletons, but__osp_setupand its__osp_initedguard are per-module (internal), so each module re-ran the shared kernel glob-init (__jac_glob_init_osp_kernel) the first time it spawned/connected, re-initializing_e_src = []. The kernel glob-init now runs exactly once program-wide behind a shared__osp_kernel_initedflag, while per-module descriptor setup still runs once per module. - Native: Object-Spatial nodes built in a non-OSP module now carry their dispatch tag (#6683): a native program that constructs a node/edge/walker in a module that does no
spawn/visit/connect of its own -- e.g. building the world graph in the entry module while the walker that spawns over it lives in an imported module -- no longer aborts mid-walk (Aborted (core dumped)) when the imported code spawns on that instance. - Native:
jac runloads C shared libraries declared in imported modules (#6687): a native program whose FFI lives behind a logical ($ORIGIN-relative) C-library import in an imported module no longer segfaults underjac run(in-process JIT) -- the library is now loaded so its symbols resolve, matching thejac nacompilestandalone binary that already ran fine. - Fix: native
for (k, v) in d.items()over-released the dict's key/val: The items loop bound the borrowed key/val handles (returned by__dict_get_key/__dict_get_val) into pointer locals that scope-exit cleanup releases, but never retained them, so a dict's last key+val were over-released and their heap backing was freed under the still-live dict. In a long-running native OSP loop that read a node'sstrfield each spawn this surfaced as a use-after-free, eventually handing a danglingchar*into a C-FFI call. The loop now retains each borrow and releases the prior occupant per iteration, matching the existinglist/dictdiscipline (issue #6688). - Fix: native
os.getenv(key, default)segfaulted on an unset variable: The native lowering ignored thedefaultargument and returned a NULL pointer when the variable was unset, so any downstream string use of the result (len,+,!=) ranstrlenon NULL and crashed at startup.os.getenvnow substitutes the provided default (matching Python: the default is returned when the variable is unset,Nonewhen no default is given). This was the real cause of the issue #6688 crash (os.getenv("QP_SMOKE", "") != ""crashing./maininstantly whenQP_SMOKEwas unset), which had been misattributed to a per-frame node-field refcount over-release. - Fix: native iterator over a stored iterator double-released its reference (use-after-free): In native (
na {}/.na.jacto LLVM) code, consuming a stored iterator value (for x in iter(it),it2 = iter(it), or an adapter such asfor x in map(f, it)) returned the same boxedJacIterthe variable already owned without taking a new reference, while the consumer (the loop, the new binding, or the adapter's owned upstream) released it as if it owned a+1. The single reference was therefore released twice: once by the consumer and once when the source variable went out of scope.iter()of an already-iterator now retains the reference so it genuinely carries the+1its consumer releases, matching the ownership contract for every otheriter()result. Also,iter()/next()are now correctly rejected by the native type checker when called with a keyword argument (e.g.next(it=x)), matching CPython where both are positional-only, instead of type-checking a form the backend could not lower (#6403). - Fix: Typed archetype returns rehydrate across
sv importboundaries: A server-to-serversv importwhose declared return type is an archetype no longer hands the value back as a raw_jac_typedict (which made attribute access raise'dict' object has no attribute ...). Boundary-type collection previously accepted the consumer's ownsv importbinding (a non-type node) and short-circuited the provider AST-walk fallback when the provider was not in the program hub at codegen time, as happens during per-service compilation underjac start. It now only accepts a symbol that resolves to anArchetype/Enum, so the return is wrapped in<RetType>._from_wire(...)and the imported type lowers to a proper boundary stub class. - Fix: Native OSP archetypes wired only from an impl annex now compile: A native module that imports a node/edge/walker and connects or spawns it only from a companion
.impl.jacfile no longer fails to compile (E5090 ... not materialized) or crash at runtime; impl-annex usage is now recognized the same as inline usage.
Refactors#
- Analysis centralization (train 9 - plan complete): the One Owner Per Analysis relocation (jaseci-labs/jaseci#6542) closes out. Codegen-time expression-type reads have one owner (
type_utils.expr_primitive_name: stamp when present, lazy authority query otherwise - the ES backend contains zero type-evaluator references); the explicit-native rejection table (NATIVE_REJECT_NODES) rejects yield/edge-disconnect/inline-Python pre-codegen, fixing a silent drop of::py::blocks in native binaries; spawn-walker detection consumes the checker-stamped symbol; and the backend-purity test becomes the standing end-state contract - migration debt zero, with all remaining analysis-API matches sanctioned by exact count and per-entry rationale. (jaseci-labs/jaseci#6542) - Refactor:
get_all_sub_nodesdropsbrute_force(#6670): The_sub_node_tabdescendant-by-type index is now invalidated on everykidmutation (set_kids,add_kids_left,add_kids_right,insert_kids_at_pos, plus the direct.kidwriters inesast_genandjac_auto_lint), so it stays authoritative.UniPass.get_all_sub_nodesreads the table unconditionally: thebrute_forceparameter, itsValueErrorfallback, and the recursive kid-walk are removed along with the flag at every call site. This also closes a latent staleness bug where a stale but non-empty cache silently returned wrong results instead of falling back to the walk. - Refactor: Eliminate cross-skill duplications and introduce jac-shadcn-blocks skill: Introduce
jac-shadcn-blocks-*skill into a detailing design system constants and composition patterns. - Refactor: Centralize OSP compile-time analysis into one source (#6725): All object-spatial (OSP) compile-time facts -- archetype kinds, globally-stable type tags, the is-a closure, dispatch slots, and construct usage -- are now derived once in a single
osp_model.jacmodule (OspAnalysis.compute -> OspModel) and stored onCodeGenTarget.osp_model, replacing the duplicated and diverged derivation logic that the native (LLVM) and ECMAScript backends each carried. Both backends now read the same model, the sharedstable_osp_tagdefinition eliminates the prior hash-vs-positional tag drift between them, and tags are masked to 53 bits so they stay exactly representable in the ECMAScript backend's JSNumber(keeping the compile-time collision check sound for both backends).
Documentation#
- Skill guides hardened against a real production project: corrections measured against the flagship showcase app - endpoint registration via client
sv importstubs (no entry-module import needed), WritePerm vs ConnectPerm grant levels for field-mutation interactions,jobj()cross-user lookup, plain-name C-FFI$ORIGINresolution - plus a newjac-sv-streamingguide (SSE/Generator endpoints), the client.impl.jachandler-annex pattern, the real wasm import surface (host-side allocator,WebAssembly.Module.imports()introspection), and a server-contract drift-detection workflow. Thejac guidelisting is now grouped by domain, and ten diagnostic codes (E1050, E2025, E1102, W0060, W3037, ...) now print-> run 'jac guide <name>'hints.
jaclang 0.16.3#
New Features#
- Declarative schema repair for persisted archetypes (
__jac_schema__): Nodes and edges can now declare how their stored shape evolved by defining astatic def __jac_schema__() -> None;whose body calls the new ambient builders:schema_was("old.mod.Name")for class renames,schema_alias("name", stored="username")for field renames,schema_drop("legacy")for removed fields, andschema_upgrade(fn, when=pred)for arbitrary transforms. Rules are validated against the live fields at registration and applied automatically when old documents load: renamed fields are repaired in place, removed/unknown field values are preserved in a__jac_attic__sub-document instead of silently dropped, aliased fields dual-write their old stored name so old app versions keep reading during rolling deploys, and fields added after a document was written are backfilled from their defaults (fixing a latentAttributeErrorondefault_factoryfields). TheJAC_SCHEMA_REPAIRenvironment variable gates the engine (repair/detect/off), andjac db schema ruleslists the registered rules. testblocks run natively in the na codespace: atestin a.na.jacmodule or inlinena {}block compiles to native code, executes via the JIT underjac test, and reports through the standard test pipeline with source-located assertion failures. (jaseci-labs/jaseci#6599)
Bug Fixes#
- Fix: Jac
or/andwithgetattr/dict.getdefaults no longer emit invalid JS:getattr(obj, key, default)anddict.get(key, default)lower to nullish coalescing (??). When that result is combined with Jacororand(lowered to||/&&), the unparenthesized??subexpression produced a JavaScriptSyntaxError. ES codegen now parenthesizes the??default so||/&&chains compile and run correctly. - Fix: Port collisions no longer break the dev server: Vite starts after the API server resolves its port, so the proxy always targets the real API even when ports collide. The startup banner shows the actually-bound ports.
- Perf: fixed a regression that made compiling and starting plain Jac programs roughly 2-3x slower and use about twice the memory. Type checking now runs only when it is actually needed (native and client builds, or
jac check), so a normaljac runno longer pays for it. Restores first-install, startup, and memory back to previous levels. (jaseci-labs/jaseci#6621)
Refactors#
- Analysis centralization (train 6): the JIR-carries-semantics question is measured and decided (semantic annotations stay recompute-on-load; warm cache hits already skip analysis entirely, with numbers recorded in the architecture spec); foreign calls now classify centrally as
CallKind.CLIB- the seam the Phase 8 marshalling plan builds on. (jaseci-labs/jaseci#6542) - Analysis centralization (train 7): the native foreign (clib) boundary now has a single owner. A central foreign declaration model (
compiler/targets/foreign.jac) owns the declared struct vocabulary and C data layouts; foreign call marshalling plans (struct classification, coerce expansion, sret promotion) are decided byclassify_foreign_fnin the pure target ABI library, with the backend reduced to LLVM materialization. The backend'sfield_type_nodeannotation-AST cache is deleted and the purity ratchet tightens. Also pins the type registry's bootstrap compile order via a module-level checker import, keeping first-use compilation memory flat. (jaseci-labs/jaseci#6542) - Analysis centralization (train 8): the foreign declaration model now owns declared clib function signatures (
collect_foreign_fns; the backend's_clib_type_nameannotation walker is deleted); declaratively-unsupported native constructs (disqualifier nodes, structural match patterns, non-allowlisted Python imports) are rejected by a single pre-codegen capability sweep instead of scattered mid-emission checks; and the ES backend's primitive dispatch reads checker-stampedExpr.typeinstead of re-running inference at codegen, with two typed-tree stamp gaps (resolved bare names,CfgExprnarrowing wrappers) closed at the source - which also drops cold-bootstrap compile cost (~10% wall, ~0.6GB peak). (jaseci-labs/jaseci#6542)
Documentation#
- Generated AGENTS.md covers the full dev loop: projects created with
jac createnow point coding agents atjac startfor running fullstack apps andjac browsefor headless-browser QA, alongsidejac checkandjac run. - Agent skill guides overhauled (22 → 35): 17 verified accuracy fixes across the existing
jac guideset (typed-edge syntax, generic-entry semantics, native stdlib/subset claims, impl-file layouts, endpoint auth/isolation semantics,Ref[T]refs, and more), plus 13 new guides - testing, debugging, jac.toml config, a project-kinds router, microservices, deployment, client JS interop, desktop, mobile, C-ABI shared libraries, wasm, Python interop, and concurrency. Every``jac example in every guide now passesjac check, and related doc/CLI corrections (including the brokenjac create` hint flags) ride along.
jaclang 0.16.2#
New Features#
jac install -enow accepts multiple packages: Pass multiple editable paths in one command - space-separated (-e ./a ./b) or with repeated flags (-e ./a -e ./b) or mixed. Dependencies across all packages are resolved in a single pip call so cross-package conflicts are caught upfront. (jaseci-labs/jaseci#6414)- Feature: System-browser SSO login for the desktop shell: The client runtime gains
jacSsoLogin()(plusjacSetToken()). Inside the Jac-native desktop shell it hands login to the user's real system browser via the loopback OAuth broker (RFC 8252) and reads the token back, instead of forcing a fresh login inside the embedded webview; on the web it redirects to the server's/sso/<platform>/<operation>endpoint as before. (jaseci-labs/jaseci#6485) - Feature: native Object-Spatial graph traversal: The native (LLVM) backend now lowers
visit,disengage, andreport, plus edge connect (++>,+>: Edge :+>) and edge references ([-->],[->: Edge :->]), to the portableosp_kernel. Edges are stored in a native in-memory adjacency (osp_graph.jac) compiled into the kernel module, so a walker can traverse a graph natively (typed refs filter by edge type). OSP instances live in a program-lifetime graph arena so the walk's borrowed location handles are not refcount-freed mid-traversal.reportcollects onto a built-in walkerreportslist that the kernel'son_completedelivers at the spawn boundary, sow.reportsreads back. All six cross-backend Object-Spatial equivalence fixtures (visit order, exit ordering, disengage, report, typed edges, subtype dispatch) now run on the native backend. (jaseci-labs/jaseci#6541) root.sharedaddresses the public graph: Any walker can read and extend the deployment's shared root - the graph unauthenticated requests run on - directly viaroot.shared, with commons-by-default access (readable and attachable by every user) in place ofallroots()scans and app-managed arming grants. (jaseci-labs/jaseci#6554)- Zig shooter twin gains the self-screenshot protocol: The raylib shooter example's Zig baseline honors the same dormant
.screenshotcapture file as the Jac build, so tooling can screenshot both engines' real runs. (jaseci-labs/jaseci#6573) - Native:
Callable = 0(NULL fn-ptr) defaults in clib struct fields: In native (.na.jac) modules an integer may now initialize aCallable[[...], ret]slot, typically the literal0for a C NULL function pointer in clib vtable structs (CEF/libuv callback slots). Regular Jac modules keep rejecting the assignment (E1001). - Native: C→Jac callback trampolines for clib vtable structs: A Jac
defstored into aCallablefield of a clib struct now goes through a cached C-ABI trampoline ({fn}.__clibcb.{n}) carrying the slot's exact C signature, so C libraries (CEF, libuv) can invoke Jac callbacks with correct argument/return marshalling, including integer sign extension. - Native: flat C-owned allocation for clib vtable structs: Instantiating a clib struct with function-pointer slots now produces a zero-initialized
callocblock with the C memory layout (no RC header, destructor, or retain/release traffic), so C runtimes that validatestruct->sizeat offset 0 and hold the vtable beyond the creating scope (e.g. CEF) see exactly the struct they expect. - Dev-server build health via
GET /__build_status: The hot reloader now exposes its build status (ok/compiling/error/unavailable, with the failing file and message on error) so external tooling can tell a clean build from a broken one.
Bug Fixes#
- Fix: Scheduler uses UTC consistently: The runtime
Schedulernow usesdatetime.now(UTC)for all scheduling comparisons and treats naivedate=...strings on@scheduleas UTC, so static scheduled tasks fire at the same wall-clock moment regardless of the container/process local timezone. - Fix: Type narrowing now works inside list comprehensions in ternary expressions: When you write
[x for x in items.data] if items else [], the type checker now correctly understands thatitemscannot beNoneinside the comprehension. Previously it raised a false error on the attribute access even though the condition already proved it was safe. - Fix:
x = None; if cond { x = real_value; }no longer raises a false E1002: the idiomatic None-then-real pattern was dropping its new type post-branch because the CFG narrowing walker rejectedset[str]as not assignable toNoneType. It now applies the existing evolving-locals rule, matching Pyright. - Fix: bare
jac runreports a missing filename instead of crashing: Runningjac runwith no file argument passedNoneinto the handler and raised aTypeError. CLI positionals without defaults are now treated as required, so users get a clear argparse error (the following arguments are required: filename). - Fix: byte-string literals (
b"...") are typed asbytes, notstr: Assigning ab"..."literal to abytesvariable or adding it to other bytes no longer raises false type errors. - Fix:
jac2jscrash onisinstancewith builtin types (#6458):isinstance(x, str)(and any other builtin-type check, e.g. the shadcnsidebarcomponent) no longer aborts JavaScript codegen with a bareIndexError: list index out of range. The ES pass now lowers builtin-type names (str,int,list, ...) used as values, andisinstance/issubclassroute through a runtime helper that implements correct Python semantics for builtin types, tuples of types, and user archetypes (includingboolas a subclass ofint). Any remaining builtin-call codegen failure now surfaces a locatedE5016diagnostic instead of crashing the build. As a consequence, a builtin name used as a dict key (e.g.{type: "email"}) now correctly lowers to that key instead of the accidentalkeyfallback it produced before;JacForm's field renderer is updated to readconfig.typeaccordingly sofield_configinput types (e.g.type: "email") are honored and native HTML validation works again. - Fix: dev server banner now links to the frontend, not the API: In HMR dev mode the headline
Local:URL pointed at the API port instead of the Vite dev server, so the link opened the wrong address. It now points at the frontend you actually open in the browser. - Fix: DOM member access off
document.documentElement/body/headin client code: Reaching members like.classListoffdocument's root elements in.cl.jaccode now type-checks instead of failing withE1030: Type "object" has no attribute "classList". (jaseci-labs/jaseci#6489) - Fix: Slicing a list-typed object field no longer crashes in the native backend: In the native (LLVM) backend, slicing a list-typed object field with a range slice (such as
b.items[:pos] + b.items[pos:]) aborted at runtime with a spurious out-of-bounds error when the index was conditionally computed. The slice was mistakenly lowered as a plain index. Field slices now behave identically to plain-list locals. - Fix: Native field access on union/tuple-trigger values: On the native (LLVM) backend, accessing a field on a union-typed value (
x: A | B; x.val), and in particularhere.valinside an OSP tuple trigger likewith (Sub, Other) entry, silently emitted no code, leaving the function body empty and producing no diagnostic. The implicithere/visitorfor a tuple trigger now type-checks as the union of its member types (instead of a tuple), and native codegen lowers the access through a representative member layout, raising a new E5091 diagnostic when members disagree on a shared field's offset. - Fix: a cl→sv endpoint module can also be a server→server consumer: A server
.jacreached by the browser via a client'ssv importthat also did its own relativesv import from .other_servicesilently lost all of its own endpoints (walkers/functions vanished from the API with no error). The relative provider name was recorded with its leading dots (.other_service) but looked up bare (other_service), so no RPC stub was generated and the import fell through to a plain Python import that crashed the module at load time. Relative and absolutesv importnow resolve identically, and a provider module that fails to import is logged instead of silently dropped. (jaseci-labs/jaseci#6507) - Fix: Native backend no longer drops statements silently: A class of native (LLVM) codegen gaps used to compile clean and then misbehave at runtime, because a statement whose sub-expression could not be lowered (an unresolved attribute/index access, an untracked container, an unsupported
foriterable such as aset) was dropped with no diagnostic. These cases now raise E5090 at compile time, covering return values,assert/if/while/forconditions, assignment right-hand sides, subscript and field stores, andfor-loop targets/iterables, so the failure is reported at its source instead of producing an empty body or a default return. - Fix: Concrete scalars stored into
anytuple slots in a bare list are now boxed in the native backend: In the native (LLVM) backend, putting a tuple built from a concreteint/floatinto a barelistwhose elements areany(such asstack.append((7, 0))after seeding the list with anany-typed value) stored the scalar without boxing it, so reading it back through theanyseam returned 0/garbage. The scalar is now boxed to match the list's element layout, matching the Python backend. - Fix: Imported public walkers and functions are now reliably reachable without auth: Serving an app that imports a
:pubwalker or function from another module could intermittently reject anonymous requests withUnauthorized. The endpoint's access level is now resolved deterministically regardless of import order, so public imported endpoints stay public. - Fix: server-to-server
sv importcalls no longer report a spurious missing-awaiterror: Binding the result of a function imported from another server viasv importto its declared return type was type-checked as if it returned an un-awaited coroutine. Server-to-server calls resolve synchronously, so they now type-check directly to the provider's return type, while client-to-serversv importcalls still correctly requireawait. - Fix: Jac client components can now forward refs: A client JSX component that declares a trailing
ref: Refparameter now lowers toforwardRef(function C(props, ref) {...})instead of a plain React function component, so it can be used as a ref target. Previously there was no way to author a ref-forwardable component (the trailing param was wrongly destructured out ofprops), which silently broke every radixasChildcomposition over a Jac component (DropdownMenu / Tooltip / Popover / HoverCard / Dialog triggers never positioned). Components that declare norefparam compile exactly as before. - Fix: typed connect operators format compactly:
jac formatnow renders typed connect operators such as+>:Edge:+>and+>:Edge:weight=5:+>without spaces around the inner colons, matching the existing traversal (->:Edge:->) and disconnect (del ->:Edge:->) forms. - Fix: false type error on classes shared through a circular import: When two modules imported from each other and one of them was the file you ran
jac checkon, a class shared across the cycle could be reported as not matching itself (e.g.Cannot return tuple[Tok, Tok], expected tuple[Tok, Tok]). Such code now type-checks correctly. - Native for-loop dispatch hardening: the unified for-loop skeleton's element fetch now dispatches explicitly per iteration kind and raises an E9002 internal-contract error on an unknown kind, instead of silently falling through to key-fetch semantics. (jaseci-labs/jaseci#6542)
- Fix: Python classes with both
__init__andinitno longer rejected as duplicate methods: Importing a Python class that defines__init__alongside a plain method namedinit(such as Pillow'sPIL.ImageFile.PyCodec) raisederror[E0076]: Duplicate method 'init' in class body, so any program importing Pillow failed to compile. The duplicate-method check now distinguishes the constructor from a regularinitmethod the same way Python code generation does, while still flagging genuine duplicates.
Refactors#
- Analysis centralization (One Owner Per Analysis, first phases): type inference now runs on every codegen-bearing compile so
Expr.typeis a pipeline invariant; new semantic facts live on the unitree (Symbol.storage,Archetype.arch_kind,Ability.event_triggers,LambdaExpr.captures,FuncCall.call_kind) and the ECMAScript/native backends consume them instead of re-deriving types, scopes, captures, and call classifications locally. A missing semantic annotation at codegen is now anE9002internal error instead of a silenti64fallback. (jaseci-labs/jaseci#6542) clmarkers are never needed in.cl.jacfiles: The.cl.jacsuffix is the client marker - bare top-level globs and imports type-check and compile as client code, and reactiveuseStatelowering applies only to component (def) state, never to plain object fields. (jaseci-labs/jaseci#6557)- Analysis centralization (Phase 4, call resolution): the type checker now stamps the resolved callee declaration on every call node (
FuncCall.callee_decl); the native backend reads resolved signatures (parameters, default-argument expressions) off the call site and records interop-marshalling return facts at declaration time, deleting its name-keyed AST-signature cache (method_ast_sigs). Default-argument resolution now respects user shadowing the way the checker does. (jaseci-labs/jaseci#6542) - Analysis centralization (Phase 7, ownership - first slice): the reference-counting ownership invariant for expression results now lives in one place (
compiler/ownership.jac: OWNED / BORROWED / STATIC / UNKNOWN with the documented rules); the native backend applies it at a single seam in expression dispatch, deleting the 13 scattered per-site owned-marking decisions and the_return_tuple_ctxemission flag (return-position tuple packing is now read off the tree). (jaseci-labs/jaseci#6542) - Native backend dedup (analysis centralization train): every native
forform (range, list, dict keys, dict items, string) now lowers through one indexed-loop skeleton with a single home for the loop-variable RC discipline and loop-exit releases, replacing five duplicated emission paths; the duplicated non-primary-ancestor method walk in archetype registration is unified into one declare/emit-mode helper. (jaseci-labs/jaseci#6542) - Analysis centralization (train 4): one capability/portability authority (
capability_check_pass.jacwith declarative tables;UniTreeEnrichPassandPortabilityCheckPassdeleted); declarations lower checker-stamped types (annotation-walk allowlist at zero for expr/globals); a pure target-ABI library (compiler/targets/abi.jac) with direct unit tests owns by-value C struct classification; user-definedmap/filter/rangeno longer get misrouted into builtin fast paths. (jaseci-labs/jaseci#6542) - Analysis centralization (train 5): loop-exit release lists become a central ownership fact (
ownership.loop_body_locals), deleting the emission-order snapshot diffing at every loop site; ES call resolution drops its scope-lookup and evaluator fallbacks (first ES purity-allowlist ratchet); native import legality is classified by the capability authority; the One Owner Per Analysis contract and authority map are promoted into the compiler architecture spec. (jaseci-labs/jaseci#6542)
jaclang 0.16.1#
New Features#
- Feature:
testblocks run in the client (cl) codespace: Atestin a.cl.jacfile /cl {}block now actually runs - it compiles to JS, executes underbun, and reports through the samejac test/pytest summary as server tests (codespace inferred from the file/block, no new keyword). Full-stackcltests boot the backend theysv importon an ephemeral port (falling back to a siblingserver.jac/main.jac, orJAC_CL_TEST_SERVER) so client logic is exercised over the real cl->sv fetch/auth bridge. Seeexamples/littleX/tests/test_fullstack.cl.jac: two users sign up, post, follow, and feed propagation is verified end-to-end. jac bundlenow produces both a wheel and an sdist: Thebundlecommand generates a PEP 625-compliant.tar.gzsource distribution alongside the.whlfile in a single run, using a fixed mtime for reproducible archives. Both artifact sizes are reported and written to the output directory together.- Native C-FFI: standalone binaries find sibling shared libraries from any directory: A
jac nacompileexecutable now resolves its imported shared libraries relative to the executable's own location instead of the launch directory. The ELF linker emitsDT_RUNPATH=$ORIGIN(and the Mach-O linker loads local libraries via@loader_path), and a relative import with a directory component (e.g.import from "./libfoo.so") records just the soname so the runpath applies. Absolute system paths and bare sonames are kept as-is. This means a library shipped next to the binary is found no matter where the program is run from, and a singleimport from "libfoo.so"resolves across Linux and macOS when the right binary is staged beside the executable. (jaseci-labs/jaseci#6353 gaps #4/#5) - Native C-FFI: Python-style extensionless imports with per-platform library resolution: Native imports can now name a shared library by a dotted, extensionless logical name instead of a literal path with a baked-in extension.
import from raylib { ... }resolves tolibraylib.so/libraylib.dylib/raylib.dllfrom the target triple, so a single unchanged.na.jactargets Linux, macOS, and Windows. A leading.(import from .raylib) is source/binary-relative and a dotted path (import from libs.raylib) maps to a sub-directory, both pinned to the loader origin ($ORIGINon ELF,@loader_pathon Mach-O) and composing with the runpath from the prior release. The explicit string-path form is unchanged for pinned or versioned sonames. (jaseci-labs/jaseci#6353 gap #5) - Native iterator protocol: lazy
map/filter/enumerate/zipin for-loops: Native (na) for-loops can now iteratemap,filter,enumerate, andzip, and the adapters compose lazily (for examplemap(f, filter(p, items))) without building intermediate lists. (jaseci-labs/jaseci#6403) - Feature: Python-congruent native standard library (v1): The native (
na {}-> LLVM) backend now supportsimport math,time,sys,os/os.path, andrandomwith the sameimport X; X.func(...)surface as the Python pathway.mathlowers to libm (ULP-congruent);timeuses POSIX clocks;sysaddsmaxsize/byteorder/platform;os/os.pathcover common filesystem calls; andrandomships a CPython-faithful MT19937 sorandom.seed(n)reproduces the same sequence on both pathways. Unsupported members are rejected at compile time rather than silently producing a wrong binary. jac nacompile --target wasm32and full-stackna+clwasm apps: The native backend can now targetwasm32-unknown-unknownand link the relocatable object into an instantiable module with a pure-Jac wasm linker -- nowasm-ldor emscripten -- where undefined externs (e.g.import from raylib { ... }) become the module's wasm imports.jac startandjac buildcompile anna {}codespace to/static/main.wasmas part of the client build, so annagame/library and theclpage that drives it ship from a single module.- API server serves a conventionally-named client page at
/: When[serve] base_route_appis unset, the server serves a conventionally-named client page (app,index,main,home, orroot) at the root path; otherwise the root path stays the JSON API index. - Perf: Cache
get_type_hintsviatypecachemodule for fast deserialization:get_type_hintswas called per-anchor during deserialization, accounting for ~77% of deserialization cost. Field-type resolution is now cached per-class in a newtypecachemodule, yielding a 122x speedup on that path and reducing total deserialization time from ~452ms to ~107ms for 999 anchors. jac nacompile --target windowsand a pure-Jac PE/COFF linker: The native backend can now produce a runnable Windows.exe, closing the last gap in the cross-platform native story (Linux/ELF and macOS/Mach-O already shipped). A newPeLinker(jaclang/compiler/passes/native/pe_linker.jac) parses the relocatable COFF object llvmlite emits for a*-pc-windows-msvctriple and writes a PE32+ image entirely with Python'sstruct, with nolink.exe/lld-link/external toolchain, matching the ELF/Mach-O/wasm linkers. It lays out.text/.rdata/.data/.idataat a fixedImageBasewith base relocations stripped (no ASLR/.reloc), builds the import directory + IAT with per-function import thunks (the PE analogue of the ELF PLT) so DLL imports resolve at load, applies the COFF relocation kinds the backend emits (ADDR64/ADDR32/ADDR32NB/REL32[_1..5]), resolves weak externals (the__rc_*runtime globals) to their local definitions, and registers.pdataas the exception directory. Undefined externals bind tomsvcrt.dllby default (withmalloc_usable_sizealiased to_msizeandsnprintfto_snprintffor glibc-isms), while aclibimport that names a real.dllis honored and grouped into its own import descriptor; a Linux.so/macOS.dylibclib path falls back to msvcrt. The PE entry stub recoversargc/argvvia msvcrt__getmainargsand defines the MSVC_fltusedmarker locally.jac nacompile hello.na.jac --target windows(or anyJAC_NATIVE_TARGET=x86_64-pc-windows-*) emitshello.exe. (jaseci-labs/jaseci#6439)- Feature: Native
walker spawn nodethrough the portable OSP kernel: The native (LLVM) backend now lowers Object-Spatialspawninto the sharedosp_kernel, dispatching by the location's runtime type tag instead of an exact-type stopgap. This fixes the cases the stopgap got silently wrong (computed locations likew spawn items[0], tuple triggers likewith (A, B) entry, subtype dispatch such aswith Base entryon aSub, exit abilities, and spawns nested inside an ability) and enforces native equivalence with the server viarequire=["na"]on the entry-spawn equivalence fixtures. jac nacompile --sharedbuilds C-ABI shared libraries (.so/.dylib/.dll): The native backend can now package a.na.jacmodule as a shared library that any C/C++/Python/Rust host candlopen/link, the inverse ofimport from "lib.so" { ... }. The export surface is whatever you mark:pub(functions and globals; re-exported transitively across imported native modules), so it stays curated rather than dumping every symbol. Jac objects/strings cross the boundary as opaquevoid*handles, and the library exportsjac_retain/jac_releaseso a host can manage their reference-counted lifetime; module globals initialize automatically on load (ELFDT_INIT_ARRAY, Mach-O__mod_init_func, PEDllMain). The same toolchain-free pipeline emits a position-independent ELFET_DYN(with a real.dynsym/.hashand section headers, sogcc -l,readelfandnm -Dall work), a Mach-OMH_DYLIB(export trie,LC_ID_DYLIB, ad-hoc code signing on arm64), or a PEIMAGE_FILE_DLL(export directory,.reloc,DllMain) --jac nacompile mathlib.na.jac --shared [--target macos|windows].
Bug Fixes#
- Fix: ANSI console honors
style=:emit_rawapplies semantic roles and legacy token styles on the default backend; CLI call sites migrated off inline Rich markup toconsole.print(..., style=)and helpers. - Fix: CFG dot output incorrectly shows full ternary return statement in function-entry block: When a function body contained
return x if c else y, thejac tool ir cfg.output placed the entire return statement text inside the function-entry block alongside the function signature. The entry block now shows only the function signature, and each branch of the ternary is correctly labeled asreturn <value>in its own block. - Fix: no spurious CFG edge for a comprehension inside a ternary:
jac tool ir cfgno longer draws an invalid edge out of a function whosereturnis a ternary with a list comprehension in its true branch (e.g.return [x for x in c.items.data] if c.items else [];). The control flow graph now renders correctly for these functions. - Fix: keyword parameter name no longer cascades into spurious argument-count errors: Using a Jac keyword as a parameter name (e.g.
def f(by: float)) reported the correctE0013keyword error but then dropped that parameter and every parameter after it, truncating the signature so every call site cascaded into spuriousE1051"Too many positional arguments". The parser now keeps the offending parameter for error recovery, so only the realE0013(with its backtick-escape hint) is reported, and the escaped form (def f(`by: float)) still compiles. Applies to both function and lambda parameters. - Fix:
rootparameter name no longer breaks bytecode precompile: Threejac0coremodules used the reserved keywordrootas a parameter name, which raised anEmpty py_ast on ParamVarinternal compiler error and left those modules out of the shipped precompiled bytecode (degrading cold start). The bindings were renamed toroot_node. - Fix: parameterless functions with no return type are no longer dropped by the native backend:
def foo { ... }(no parameter list and no->return type) is semanticallydef foo() -> None, but the RD parser collapsed its empty signature toNone, leaving the ability with noFuncSignaturenode.jac nacompilethen silently elided such functions and every call to them (e.g. adef end_frame { EndDrawing(); }wrapper compiled to nothing). The parser now always keeps the (empty) signature, so an absent return type consistently means aNonereturn across type checking, Python codegen, and the native backend; the no-parens form still round-trips to source unchanged. - Fix: includes topology changes in the hash computation: Previously the graph topology changes are removed from hash generation. Therefore, the topology changes are not persisted properly in MongoDB. Now we include the topology in the hash computation.
- Fix: avoid stack overflow: Replace Python recursion with an explicit stack in _visit_recursive (sync) and _visit_recursive_async (async) in osp_kernel.jac / osp_kernel_sv.jac.
- Fix: reassigning an unannotated literal-typed local no longer errors: An unannotated local's declared type is now widened past its first binding's literal (
s = ""freezessasstr, notLiteral[""]), sos = "other"is accepted instead of reportingCannot assign Literal["other"] to Literal[""]. A genuine cross-type reassignment (e.g.strtoint) is still rejected, and its message reports the base type (str), not aLiteral[...]. - Fix: native Mach-O clib symbols resolve to the right dylib: The pure-Python Mach-O linker hardcoded the libSystem ordinal for every undefined symbol, so functions imported from another shared library (e.g.
import from raylib) bound against libSystem and crashed at launch withdyld: Symbol not found. The native backend now threads a per-symbol library map from the clib imports through to the linker, which emits the correct two-level-namespace dylib ordinal for each symbol. - Fix: clearer native and lowercase-literal diagnostics: Lowercase
true/false/null/nonenow get a "did you meanTrue/False/None?" hint on theW2001warning instead of only a confusing downstreamCannot return <Unknown>error.jac nacompilenow rejects constructs outside the native subset loudly with a clearE5090-- Python module imports (import math;), builtins that cannot be lowered for the given argument type (e.g.sumover arange), and calls to undefined functions -- instead of silently emitting a binary that prints empty/garbage. The native import allowlist (NATIVE_SUPPORTED_STDLIB) is the extension point for the Python-congruent native stdlib roadmap. - Fix: native
dict[*, any]literal use-after-free: Boxing a value into aJacValstored the inner pointer without retaining it, while the dict literal still released owned temporaries, so two or more owned string values in one literal (e.g.{"a": x + y, "b": z + w}) could be freed early and read back as garbage. The boxed path now retains the inner pointer to match the typed-set contract. - Fix: native
open()raisesFileNotFoundError: Opening a missing path now raisesFileNotFoundError(catchable asOSError/Exception) instead of returningNone, matching CPython. - Fix:
jac formatpreserves significant JSX whitespace around inline elements: When a JSX element mixes text with inline child elements (e.g.score <b>0</b> fps), the formatter used to break every child onto its own line and drop the same-line spaces, silently changing the rendered output toscore0fps. The mixed-children layout now follows Prettier's model: a significant space at a text/element boundary survives a line break as a{" "}whitespace marker (and a literal space when flat), short space-separated JSX stays on one line, and newline-separated children keep their existing layout. Re-formatting is idempotent (an emitted{" "}is recognized as whitespace on the way back in), and element-only children are left unchanged. - Fix: native list/
anycodegen gaps: In the native (LLVM) backend,list.pop(index)now honors the index (including negative) instead of always popping the last element; storing tuples in a list and popping/unpacking them round-trips correctly; a non-empty list literal with a boxedanyelement no longer collides withi64lists (it gets its ownjacvalhelper); and unpackingpop()of a barelisttype-checks the same in native codespace as on the server (gradualanyis treated as unpackable). - Fix: native container truthiness and silent acceleration failures: In the native (LLVM) backend,
while <list>/if <container>now tests whether the list/dict/set is empty (its length field) instead of testing the always-non-null pointer. Previously such loops never terminated and ranpop()past the end into out-of-bounds memory (the source of thetag_ofsegfault during OSP kernel bring-up). Native acceleration failures for.na.jacmodules, previously debug-logged and invisible, are now surfaced to stderr underJAC_NATIVE_DEBUG=1. - Fix: native backend decodes
\x/\u/\Ustring escapes: The native (na) backend previously passed numeric and codepoint escape sequences (\xNN,\uXXXX,\U00XXXXXX,\N{...}, octal) through string literals literally, decoding only simple escapes like\nand\t. This broke the na ⊆ sv guarantee, since the same source produced different string bytes depending on the pathway. The native string-literal and f-string codegen now decode through the sharedString.lit_value(the sameast.literal_eval-based decoder the sv pathway uses), so every escape form resolves identically across pathways by construction. - Fix: native gradual
anyvalues round-trip faithfully: In the native (LLVM) backend, scalar and pointer values now box into, and unbox out of, theany(JacVal) representation through a single coercion seam, so passing a literal to ananyparameter, reading a value out of adict[str, any]local, and storing a tuple with ananyelement in a list then unpacking it all preserve the value instead of failing to compile or coming back as0. Tuples containing ananyelement are now sized by LLVM's exact ABI layout, fixing the heap-overflow that corrupted them (the source of thetag_ofsegfault during OSP kernel bring-up). - Fix: edits to core compiler passes no longer silently ignored by stale caches: The bytecode caches are consolidated onto one content-addressed identity (a
SEC_MODKEYsection embedded in every JIR) validated by a single check shared by the module cache and the shipped_precompiled/bundle. Editing a.jac,.impl.jac, variant, or style file now reliably invalidates both, fixing the case where a precompiled bundle validated only the head source hash (ignoring impl hashes) and then laundered stale bytecode into the module cache with a fresh mtime. The precompiled bundle no longer needs amanifest.json, the bootstrap cache stores plain marshalled code objects, and two debug escape hatches were added:JAC_REBUILD=1(force recompile) andJAC_NO_PRECOMPILE=1(skip the bundle). The JIR format version bump transparently regenerates existing caches. - Fix: Native backend leaked owned temporaries: the LLVM native code-gen never released the temporary it consumed at several reference-counting seams (
for/comprehension iterables that are call results,in/not inoperands,set.addelements, and call/method arguments), so move-generation-heavy native programs grew memory without bound. Each seam now releases owned temporaries, and the debug-onlymalloc_usable_sizecall was moved behind the runtime trace flag so it is no longer invoked on every free. - Fix: varargs/kwargs params dropped in client codegen:
*args/**kwargsparameters now lower to a JS rest parameter (...args) in the ECMAScript target instead of being omitted from the signature, which previously left the function body referencing an undefined name. (jaseci-labs/jaseci#6466) - Fix: jac2js interprets string escape sequences: The ECMAScript (
cl) target now decodes string-literal escapes (\n,\t,\xNN, quotes, backslashes, and implicit concatenation) via the sharedlit_valuedecoder, matching the Python (sv) target instead of emitting the backslash verbatim (#6476). - Fix: overloaded functions imported through re-exports resolve correctly: Calling an
@overload-ed function imported via a re-export chain (e.g. SQLAlchemy'screate_engine) no longer reports a spurious missing-parameter error for calls that match a later overload.
Refactors#
- Refactor: unified client build pipeline moved into core: The bun toolchain, Vite bundler, client config loader, jac→js bridge, and build orchestration now live under
jaclang.runtimelib.client(one runtime, one bundler) instead of being duplicated inside the jac-client plugin. Downstream consumers import the bundler,JacClientConfig, andget_bunfromjaclang.runtimelib.clientrather than the oldjac_client.plugin.src.*paths. (jaseci-labs/jaseci#6390) - Refactor: version-guarded symbols resolve through the type checker: Symbols declared under
sys.version_info/TYPE_CHECKINGguards (including version-specific typeshed overloads likezip.__new__) now resolve to the branch live for the running Python version.
Documentation#
- Docs: sonner in the jac-shadcn component guide: The bundled component-selection guide now lists the sonner toast component. (jaseci-labs/jaseci#6467)
jaclang 0.16.0#
New Features#
- Feature: Full Object-Spatial traversal runs in client (cl) code: cl now compiles and runs complete Object-Spatial programs in-process through the portable kernel, not just entry-only spawn:
visit, edge connect (++>,+>:E:+>) and edge references ([-->],[->:E:->]),disengage,report, exit abilities, and subtype / tuple-trigger dispatch. A local-archetype walker runs locally; a server (sv-imported) walker spawned atrootkeeps the__jacSpawnRPC path unchanged. The emitted module carries a small in-memory graph runtime (nodes hold their incident edges) plus per-archetype dispatch descriptors, type tags, a subtypeis_atable, and a clientNoopHost. - Native C-FFI: System V AMD64 ABI lowering for by-value structs: Foreign (
import from "lib") functions that take or return structs by value now follow the platform System V AMD64 calling convention instead of being handed to LLVM as raw aggregates. Structs are classified per the eightbyte algorithm (INTEGER toiN, SSE tofloat/double/<2 x float>, aggregates over 16 bytes viabyval/sret), with nested structs flattened to their C layout. This fixes float-field math structs likeVector3/Camera3D(previously dropped fields or returned garbage) and removes a compile crash when a scalar integer return shared an LLVM type with a small struct's coerced type. x86_64 only for now; other targets are unaffected. - Native C-FFI: AArch64 AAPCS ABI lowering for by-value structs: Foreign (
import from "lib") functions that pass or return structs by value now follow the AArch64 AAPCS calling convention on arm64 targets, alongside the System V support added for x86_64. Homogeneous float aggregates go in SIMD registers ([N x float]/[N x double]), other aggregates up to 16 bytes use general registers (i64/[2 x i64]), and larger aggregates are passed indirectly and returned viasret, matching what clang/gcc emit. Also adds aJAC_NATIVE_TARGETenvironment override sojac nacompilecan cross-compile for another target triple, and fixes the ELF linker emitting an empty interpreter path when cross-compiling.
Bug Fixes#
jac check --disable-error-codenow suppresses diagnostics during compilation without mutating project config, and it consistently resolves direct codes, lint aliases, comma-separated values, and inline ignore tokens.jac nacompilenow produces working standalone aarch64 binaries instead of ones that crash at startup.
jaclang 0.15.6#
Breaking Changes#
- Breaking:
checkremoved in favor ofassert: Test assertions are written withasserton every backend; a failing test assertion now raises a plainAssertionError.
New Features#
- Feature:
jac check --disable-error-code: Suppress selected diagnostic codes for a single check run (e.g.jac check app.jac --disable-error-code E1030 W2001). Tokens accept codes or lint kebab aliases (same as[check].suppressinjac.toml), including comma-separated values; config changes are restored after the run. Named after mypy’s flag (issue #6108 originally suggested--ignore). - Feature: Object-Spatial programs compile and run natively: The native (LLVM) backend now compiles and executes Object-Spatial constructs (walkers, nodes, and
spawn) by routing the OSP runtime through a portable kernel and anOspHostseam (report sink and spawn commit boundary), with ability triggers emitted by the compiler instead of resolved via runtimeinspect. - Feature: Client-called endpoints serve without an explicit entry import: A server endpoint reached only through a client module's
sv importis now registered at serve time directly from the compile-time cross-boundary table, so the entry module no longer needs to re-import it. Both endpoint shapes are covered -deffunctions (/function/<name>) and walker archetypes (/walker/<name>) - and each keeps its own access level (apriv/protcallee stays auth-gated, secure by default). As a result an entry like littleX'smain.jaccan drop itsto sv:endpoint import entirely. The compiler no longer force-type-checks.cl.jacmodules to do this (boundary analysis reads the AST and symbol table, not resolved types), and the boundary-completion walk skips the built-in@jac/*framework runtime so it no longer leaks spurious type errors into client apps. jac ai --uicall-detail view for the token-usage stream: Clicking a token-usage row opens a modal that reconstructs that model call - its full input conversation, the diff vs the prior call (the prompt-cached prefix is collapsed to a one-line note so the call-by-call context growth is obvious), and the output the call produced. Prev/Next step through the calls of the same phase.jac ai --uicall-detail modal now shows the tool schemas: A call'stokens_inincludes the tool JSON schemas (the APItoolsparameter), which are not messages, so the modal could not previously account for them. A collapsible "tools available" section now lists each tool's schema with a token estimate, so the count reconciles as messages plus tool schemas.hasattr(x, "name")now narrows union types: Inside the true branch ofif hasattr(x, "foo"),xis narrowed to the union members that declarefoo; the else /not hasattrbranch keeps members without it. Inherited attributes count, classes with__getattr__are never filtered out, and a dynamic (non-literal) attribute name leaves the union as-is without erroring.- Feature:
jac airead-only fast path: A typed-bool classifier runs after the Plan phase to decide whether carrying out the request needs any file created or edited. Pure questions and read-only inspections now finish straight from Plan, skipping Build (and its wrote-nothing nudge loop) and QA, while anything that touches code still routes to Build, which stays the default so a misjudgement never silently skips real work. - Type variables are now inferred through function-typed arguments, so a generic like
apply(f: Callable[[], T]) -> TresolvesTfrom the function you pass in. This also lets@contextmanager/@asynccontextmanagermethods be recognized as context managers, removing the false "cannot be used in 'with' statement" errors onwithblocks that use them. - Feature: Module-level types are shared across codespaces: A module-level
obj/node/edge/walkerdeclaration is now shared across every codespace (sv/cl/na) - each backend's codegen materializes the shared types its own code references, so a program is declared once instead of redeclared per codespace. A declaration inside anna {}/cl {}/sv {}block still overrides the shared type for that target. - Bare-serve auto-caches reader endpoints: The dependency-free bare-serve client runtime now caches reader walker and function calls, dedups concurrent identical requests, and invalidates on writes and auth changes, matching jac-client.
- Feature: JavaScript globals type-check in client code: Standard ECMAScript and browser globals such as
Date,Math,JSON, andconsolenow resolve in.cl.jacclient code, andnew(cls, ...)keeps the constructed type instead of collapsing to a bare object. jac ai --uiactivity column and per-call token counts: The activity feed now lives in its own resizable column to the right of the phase graph, so the workspace reads as three side-by-side columns (conversation, graph, activity) with the activity feed mirroring the conversation across the graph. The token-usage call-detail modal also gains a per-box~N tokestimate on every message and tool-schema block, and its note now distinguishes calls that actually send tool schemas (Plan/Build/QA) from tool-less routing and classifier calls.jac ai --uiphase-context inspector, graph pan/zoom, and agent runtime hardening: The phase inspector now shows the selected phase's captured context (system prompt, the kickoff directive, and the tool turns it accumulated) via a newagent_phase_contextendpoint, and the phase graph is pannable (drag the background) and zoomable (scroll wheel). The agent runtime is also hardened: code intelligence clears its dirty flag only after a successful refresh, the--uimutating endpoints are guarded against running-turn races (with a real bus lock serializing turn start), the read-only classifier falls back to its Build-biased default on error while terminal runs always reclaim the dev server and browser, file I/O is pinned to utf-8, and a leaked log file handle inserve()and the UI launcher is closed.- Feature: Short JSX
returnstatements stay on one line: The formatter previously forced everyreturn <Element/>;onto a second indented line. It now keeps the element on thereturnline when the whole statement fits the print width (e.g.return <p>Hello</p>;orreturn <Tag prop={x}/>;), and only breaks it onto its own indented line when the statement overflows the print width or the element has multi-line children. - Feature: Object-Spatial walkers run in-process in client (cl) code: A walker whose archetype and abilities are compiled into a client unit now runs locally through the portable Object-Spatial kernel when spawned at a local node (
w spawn Node(...)), instead of issuing a__jacSpawnserver RPC. The compiler emits the kernel, per-archetype dispatch descriptors, type tags, and a clientNoopHost; abilities lower to kernel dispatch slots. Server (sv-imported) walkers spawned atrootkeep the RPC path unchanged. Entry-only spawn for now;visit/disengage/reportand edge traversal follow. - Feature: TypedDict support in the type checker: Indexing a
TypedDictwith a literal key (doc['name']) now resolves to that field's declared type, andget/pop/setdefaultreturn the precise per-key type. Subscripting an unknown key reports an error. Overload resolution also now discriminates on literal argument values, soLiteral-keyed overloads pick the matching signature. - Feature:
Literal[...]type annotations:Literal["a"],Literal[1, 2], andLiteral[True]now resolve to literal types in variable, parameter, and return annotations. Assigning or passing a value whose literal differs from the annotation is reported, and overloads keyed on distinctLiteralvalues select the matching signature. - Feature:
Literal[None]and negative-int literal annotations:Literal[None]resolves toNoneTypeand negative integer literals such asLiteral[-1, -2]resolve correctly, where both previously widened to an unresolved type. - Feature: npm package publishing via
jac bundle --target npm: Client-side Jac libraries can now be published to npmjs.org, mirroring the wheel/PyPI flow.jac bundle --target npmreadsjac.toml, compiles each client module to ESM JavaScript, generatespackage.json, and emits.d.tsTypeScript declarations (plus inline JSDoc), producing a reproducible.tgzthat any JS/TS project cannpm install. Modules that cross a server (sv) boundary are rejected; JSX/reactive modules get an@jaseci/runtimedependency wired in (andreact/react-domas peer dependencies when imported).--target npm-runtimepackages the Jac client runtime as@jaseci/runtime, and--target allbuilds both the wheel and the npm tarball. Upload stays out of scope (npm publish), as wheels leave it to twine.
Bug Fixes#
jac bundle --precompilenow fails fast on incomplete precompilation: Missingpython3.Xexecutables now emit a warning, finding zero interpreters is a hard error, and any per-version failure aborts the bundle - previously all three cases silently succeeded, shipping wheels with incomplete_precompiled/content.jac ai --uino longer duplicates the conversation in every phase prompt:run_phaseis a byLLMbyfunction, which serializes its parameters into the prompt as text inputs. It took the phase's tools and conversation as parameters AND passed them via thebyclause, so the whole conversation was sent twice (once as real messages, once as a Python repr) and the tool list leaked as object reprs. Tools and the conversation are now read from the agent runtime in thebyclause, leaving only the directive as a prompt input, which cuts the first phase call from roughly 14.5k to 5.5k tokens (and more on later calls).- Fix: client-to-server endpoints registered without a re-import in
main.jac: asv importinside a.cl.jacmodule now records its client-to-server binding at compile time, so thedef:pub/walker:pubendpoint registers correctly without a runtime walk or re-importing it inmain.jac(previously returned a misleading 405). - Generic free functions now narrow to the argument's actual type: calling
identity(d)wheredis a subclass returns the subclass, so accessing subclass-only members no longer raises a spurious "cannot access attribute" against the bound. - Fix: formatter spacing for enum base types: the Jac formatter no longer inserts a stray space before the typed-base colon (
enum X: int) or inside base-class parens (enum X(A, B)).DocIRGenPass.exit_enumnow spaces these forms like archetype base classes instead of routing them through the generic kid branch, which had appended a trailing space after every kid and producedenum X : int/enum X ( A, B ). - Fix:
try/awaiting/exceptJSX under bare-serve: Bare-serve now provides theJacAwaitingand client error-boundary wrappers the compiler emits for these views, so a bare-serve app using them no longer fails with an unresolved@jac/runtimeimport. - Loop bodies now correctly loop back in the control-flow graph: the end of a
for/whilebody now returns to the loop header instead of falling through to the code after the loop, fixing spurious type-narrowing and unreachable-code results inside loops. isinstancenow narrows values typedAny: a guard likeif isinstance(x, Foo)on anAny-typed value (e.g. an item from a barelist) now narrowsxtoFoo, so accessing the type's members no longer reports a spurious error.- Fix:
def:pubendpoints of a jaclang-bundled app no longer return 401: A fullstack app bundled under the jaclang package root (e.g.jac ai --ui) is routed by the compiler into a separate internal program, so its modules never land inJac.program.mod.hub. The server's access-level introspection looked the main module up only in that one hub, missed it, and left everydef:pubendpoint auth-required. Module resolution for path-keyed structural lookups (the auth/manifest introspection and the web target) now goes through a single program-spanning accessor,JacProgram.find_module, that resolves a path across every program without recompiling. The client bundler keeps its own single-hub lookup on purpose: it produces output JS and must compile each module in its own program context. - Fix: generator-returning
def:pubendpoints stream as Server-Sent Events instead of crashing: Adef:pubfunction whose result is a generator (e.g.-> Iterator[dict], the shapejac ai --ui'sagent_streamuses) is a stream, not a single value. The base server JSON-encoded it and raised "Object of type generator is not JSON serializable". It now detects a generator result and emits each yielded item as a canonicaldata: {json}\n\nSSE frame terminated byevent: end, matching the framing jac-scale's gateway emits, so one client consumer works against either server. - Fix:
sv import-bound endpoints of a jaclang-bundled app register instead of returning 405: A fullstack app bundled under the jaclang package root (e.g.jac ai --ui) is routed by the compiler into a separate internal program, so the cross-boundary bindings the static compiler records land in that hub, not inJac.program.mod.hub. The serve runtime's cross-boundary endpoint collector and its endpoint-effects aggregation scanned onlyJac.program.mod.hub, so every client-called server endpoint went unregistered and/function/*calls returned 405 with an empty UI. Both now union overJacProgram._search_hubs()-- the same program-spanning spanfind_moduleuses -- and the collector no longer early-returns when this program's own hub is the empty dict a bundled app leaves it. Completes the bundled-app resolution started in #6273 for the endpoint-registration path. - Server no longer prints a traceback when a client disconnects mid-response: A client that reloads, navigates away, or closes the tab drops the socket while the request handler is still reading or writing. The base handler only caught
TimeoutError, so the resultingBrokenPipeError/ConnectionResetError/ConnectionAbortedErrorescaped intosocketserverand printed an alarming traceback for a routine disconnect.JacRequestHandlernow overrideshandle_one_requestto absorb those errors and close the connection quietly. - Spread arguments no longer trigger a false duplicate-argument error: mixing a
*/**spread with an explicit keyword for the same parameter (e.g.greet(**defaults, name="x")orf(*args, c=3)) no longer reports a spurious "Parameter 'X' already matched"; the spread still satisfies the required-parameter check. with cm() as xnow typesxas the value the context manager yields (its__enter__result) instead of the context manager itself, andasync withis checked against the async__aenter__/__aexit__protocol. Using the bound variable inside the block no longer raises spurious type errors.- The type checker now evaluates
if TYPE_CHECKING:blocks as taken (andif not TYPE_CHECKING:as skipped), so types and aliases that libraries declare only for type checkers resolve correctly. Imports guarded byif TYPE_CHECKING:are now usable in annotations instead of resolving toUnknown. - Fix: Browser engine docstring typo: Corrected the subcommand name in the
browser_enginemodule docstring. - Fix: Container and string method calls on untyped values now work in the JavaScript backend: Methods like
.get(),.keys(),.append(), or.strip()on a value whose static type can't be proven now resolve to the correct operation at runtime instead of leaking the Python method name into the generated JavaScript. - A served
:pubwalker or function is no longer intermittently rejected withUnauthorized. Its public access level is now always recorded at compile time, so the server consistently treats it as auth-free. - Fix: CSS reset conflict with Tailwind v4 in JacCoder-generated projects: Removed the "resets" wording from
jac-fullstack-patternsand added an explicit no-reset rule (with a plain-CSS-only exception) to both skill files. - Fix: cl/ES codegen honors Python negative-indexing and collection truthiness: The ECMAScript backend now lowers
lst[-1]to count from the end (via.at()for known sequences or_jac.poly.getitemfor unknown types) instead of emittinglst[(-1)](which isundefinedin JS), and boolean contexts (if/while/not/and/or/ternary) on a collection now test Python emptiness rather than JS truthiness (empty list/dict/set is falsy). Negative-index list assignment is routed through_jac.poly.setitem. - Fix:
and/or-in-condition truthiness in cl/ES codegen: A condition that is itself anand/orexpression (which lowers to_jac.poly.and_/or_and returns an operand) is now wrapped in Python-truthiness coercion, soif x and y/while x and ytreat an empty-collection result as falsy instead of relying on JS truthiness (where[]/{}are truthy). Follow-up to #6320. - Fewer false type-check errors: the type checker no longer flags valid code that uses
withblocks or that unpacks values like byte strings and text into separate variables.
Refactors#
- Refactor: Centralize call-kind classification: Whether a call is a builtin, a class instantiation, a method, or a free function was decided independently in each backend by matching name strings, which meant a user symbol shadowing a builtin or class name (for example a local, or
def len(...)) was silently ignored. Both the native and ecmascript backends now share a single resolved-symbol-based classifier, so user definitions correctly shadow same-named builtins. - Refactor: Centralize primitive-type classification: The resolver that maps an expression's type to a primitive name (str/int/list/...) was embedded in the ECMAScript codegen backend, and the native backend hardcoded each primitive's method-name set in inline tuples that duplicated the emitter contract. The resolver now lives in the shared type system (
type_utils.primitive_type_name_of) and native primitive method dispatch derives each type's methods from its emitter, so both are defined in one place. - Interop: unified cross-boundary analysis into one IR-resident table:
InteropAnalysisPassandWalkerEffectsPassare merged into a single front-endBoundaryAnalysisPassthat runs once per module (server, client, and native) and produces one interop table (bindings, boundary types, endpoint effects, and per-symbol auth access). Codegen and the serve runtime now read that table instead of independently re-deriving the same facts; the runtime access AST-walk and EsastGen's standalonesv importresolver are removed. - Refactor: Fold literals via AST
.lit_valueinstead of re-parsing: Compile-time literal value extraction was reimplemented by parsing raw token text in the native const/enum codegen and in the type-system enum helper, duplicating the AST nodes' own.lit_valuegetters. They now reuse.lit_value, which also fixes a compiler-wide bug where hex/oct/bin integer-enum values (e.g.RED = 0xFF) raised an error during type checking because they were parsed as base-10. - Refactor: Single
@jac/runtimesurface contract: The client runtime surface shared by bare-serve and jac-client is now declared in one place, so the compiler's import injection and the cross-runtime alignment check derive from a single source instead of separately maintained copies. - Refactor: Native-speed archetype field writes: Removed the per-write
__setattr__dirty-field tracking on archetypes, which was redundant with the value-hash diff the memory backends already use to detect changes, restoring C-level attribute stores across the runtime. - Refactor: Sv now uses the portable OSP kernel for sync traversal:
JacWalker.spawn_call/JacWalker.visit/JacWalker.disengage/log_reportnow lower toosp_spawn/osp_visit/osp_disengage/osp_report(kernel) viamake_sv_runtime, and the sync legacy walker engine (_execute_entries,_execute_exits,_visit_node_recursive) is deleted. The kernel was extended to sv parity (DFS recursion, post-order exits, queue-FIFO entries across branches,WalkScope.path,osp_visitinsert_loc, opt-in visit-once viascope.ignores,osp_spawntakesstarts: listfor edge spawn). The async walker engine still uses the legacy CallState/OspHostpath; the kernel migration for async is future work.
Documentation#
- Docs: jac-shadcn component guide rewritten for the jac-super flow: The
jac-shadcn-componentsguide now teaches quoted, hyphen-preserving import paths, thejac add/jac remove/jac rethemeworkflow, corrected component names (e.g.InputOTP), and accurate style/theme/font/radius options.
jaclang 0.15.5#
New Features#
- CLI:
jac bundle --precompile:jac bundlenow accepts a--precompile(-p) flag that automatically discovers everypython3.XonPATH, compiles.jac→.jirbytecode in an isolated venv per version, and packages the results into the wheel, replacing the manual precompile CI step. jac ai --uistreams every token live: The web UI renders the agent's output token-by-token as it is generated, including the tokens behind tool calls and the QA phase's next-step routing decision, alongside a stop control and a per-turn stats summary.jac ai --uiadds resizable panes, a live ledger, and a phase inspector: The agent web UI now has drag-to-resize panels, a running ledger of changes and notes, and a per-phase inspector that shows each phase's prompt and activity, with its phase graph traced from the actual workflow.jac ai --uisettings panel for model and credentials: A topbar gear opens a settings dialog to pick the model (with quick-pick presets) and supply an API key, base URL, temperature, and context window, applied live in the running session. The panel auto-opens when the active model needs an API key the session does not have, detected up front and reactively on an authentication failure.jac ai --uishows per-call token usage and collapsible tool outputs: The web UI replaces the raw token firehose with a live per-call token-usage stream (tokens in/out, cache, latency, and phase), and renders each tool's output as a collapsible, scrollable dropdown.
Bug Fixes#
- Fix: Docstrings before declarations inside function bodies now work correctly: Writing a docstring before a class or function declaration inside a function body used to produce a
W0060warning and the docstring was ignored. It now attaches to the declaration the same way it does at module level. - Fix: Clear error when
globis used inside a function: Using theglobkeyword inside a function body now emitsE0063: 'glob' is only valid at module levelinstead of the misleadingE0002: Missing ';'error. Previously,jac runwould also silently execute wrong code in this case. - Fix:
jac start --devwatchdog install on cross-version venvs: When the Jac CLI ran on a different Python minor version than the project's.jac/venv,_ensure_watchdog_installedwould pip-installwatchdogsuccessfully but fail to import it, becauseget_venv_site_packagesresolved the path using the host interpreter's version. The venv path is now resolved frompyvenv.cfg(with alib/python*discovery fallback), the venv is added tosys.pathafter install, and the import is re-verified before HMR is reported as ready. - Fix:
jac createwithout a name initializes the current directory: Runningjac createwith no project name previously scaffolded a newjactastic/(orjactastic1/,jactastic2/, ...) subdirectory. It now matchescargo init/uv initbehavior: it initializes the project in the current working directory and derives the project name from that directory. Passing a name (jac create myapp) still creates a subdirectory as before. - Fix: tuple unpacking in
forloops now works in client code: Afor (i, item) in enumerate(items)loop (and any tuple target, including nestedfor ((a, b), c)and starredfor (a, *rest)) compiled without complaint but left the unpacked variables undefined at runtime in the browser, so lists rendered empty. The generated JavaScript now destructures the loop variable correctly. - Fix:
skipin a client function body now lowers to an early return:skipis a Return-family keyword, but the client/ECMAScript codegen lowered it to JScontinue;outside a JSX slot. That is illegal outside a loop and crashed the client build at runtime with nojac checkerror. It now emits a barereturn;, matching the Python target and the documented semantics; the JSX-slot fragment-return guard is unchanged. - Fix: calling an
asyncfunction (orsv importRPC stub) withoutawaitis now a type error: Anasync defcall evaluates to a coroutine, not its return value, but the type checker modeled neither the coroutine wrapping norawaitunwrapping, sotasks: list[Task] = get_tasks()(an un-awaitedsv importcall, which lowers to an async RPC stub on the client) type-checked clean and crashed at runtime withtasks is not iterable. Calls to async functions now resolve toCoroutine[Any, Any, T],awaitunwraps them back toT, and assigning or returning an un-awaited coroutine raisesE1042with a "did you forgetawait?" hint. - Fix:
globoutside module scope is now rejected before execution:globused inside a function, awith entryblock, or atestblock emitsE0063and now blocks code generation, sojac runexits without executing the invalid module instead of running it.globremains valid at module level and insidecl/sv/nacodespace blocks.
Refactors#
- Refactor: Centralize the builtin exception hierarchy: The builtin exception hierarchy (the parent relationships behind
except ArithmeticErrorcatchingZeroDivisionError, etc.) was duplicated in three places: the native backend, the ecmascript backend, and the generated JS runtime. It now lives in a single source of truth inJacTypeRegistry, and the native matching, ecmascript matching, and the JS runtime exception classes are all derived from it.
Documentation#
- Docs:
jac-cl-componentsskill update: Adds a pitfall on inline lambda vs anonymousdefin JSX handlers. - Docs:
sv importawait rules added to skill guides:sv importstubs areasyncfunctions --items = fetch()assigns aPromise, not the data. Updatedjac-fullstack-patterns,jac-cl-components, andjac-core-cheatsheetwith theawaitrule and the two valid async contexts:async can with entry(fetch on mount) andasync def handler -> None(handlers that callsv import).
jaclang 0.15.4#
Breaking Changes#
defviewdeclarator removed: replacedefview Name { ... }withdef:pub Name() -> JsxElement { return <>{...}</>; }and use the unified JSX statement-slot form for the body.obj Foo; obj Foo { ... }is now a hard error (E0077): Jac's separation model is decl/impl, not decl/decl -- module-wide symbol resolution means forward declarations are unnecessary. The pattern used to be silently accepted but produced an empty class scope that cascaded into spurious E1030 "no attribute" errors. To fix, either inline the body into a singleobj/node/edge/walker/enumblock, or split into a body-less declaration plus animpl Foo { ... }block (the legitimate decl/impl pattern stays valid sinceimplis anImplDef, not anArchetype).- Breaking: remove no-op
Jac.setup()and no-token request-context APIs:JacBasics.setup(empty),JacRuntime.set_request_context, andJacRuntime.clear_request_contextare gone; usepush_request_context/reset_request_contextwith the returned token for any code that scoped a context to a server request. - Bare
return;inside a JSX slot body is now rejected (E2020): the legacy slot early-exit form is replaced byskip;.E2020's scope broadened from "in template loop" to "anywhere in a slot body" with a message pointing atskip;.E2019(value-formreturn expr;) andE2021(break;in slot loop) messages updated to also mentionskip;.skip;is no longer rejected inside slot for-loops -- inside a loop it now exits the whole slot (abandoning remaining iterations and later JSX); usecontinue;to skip a single iteration.
New Features#
- Feature: S3-Compatible Storage Interface: Extended the base
Storageinterface with an abstractget_url()method to support public and pre-signed URL generation for all storage backends. - Feature: LocalStorage URI Support: Updated
LocalStorageto support the newget_url()interface, returningfile://URIs for local assets. - Publish: PyPI classifiers support in
jac bundle: The[project]section injac.tomlnow accepts aclassifierslist. Declared classifiers are emitted asClassifier:headers in the wheel'sMETADATAfile, enabling proper PyPI discoverability, license badge display, and search filtering. - JSX statement slots:
{...}in JSX child position now accepts either a single expression or a statement-form body whose control flow (if/for/while/match/switch/with/try) yields JSX children directly, withreturn;as an early-exit guard. unsafe_htmlambient builtin:unsafe_html(x)marks a string for raw-HTML rendering, lowering todangerouslySetInnerHTMLon jac-client andinnerHTMLon bare-serve.E20xx/W2019apply to JSX slot bodies: the slot-body rule diagnostics (value-returningreturn,return/break/skipinside template loops, keyless JSX in awhileloop) now target the unified slot scope.awaitingclause ontry: A new clause --try { ... } awaiting { ... }-- names the JSX (or value) to use during the dispatched-but-not-joined window of any async work inside thetrybody. On thecltarget the view-lowering pass wraps the slot in<JacAwaiting fallback={...}>{...}</JacAwaiting>from@jac/runtime(aReact.Suspenseshim); onsvandnathe awaiting body is dropped with a newW2020warning until the streaming-SSR and native-thread lowerings land.finallyalongsideawaitingis rejected withE2022.- QueryPlan IR + capability-driven planner:
jaclang.runtimelib.query_plandefines a typedQueryPlan/HopSpecIR withneeds()/is_trivial();jaclang.runtimelib.plannerpicks a strategy (pushdown → mixed → ordered → floor) against any backend that declarescapabilities(). SetJAC_QUERY_TRACE=1for per-call strategy logging. Memory.capabilities()+Memory.execute_plan(plan)protocol: backends opt into native query pushdown by declaring capabilities (type_pushdown,field_pushdown,id_in,slice) and implementingexecute_plan. Default impls return empty so every existing backend keeps working unchanged;TieredMemorydelegates to L3 and promotes hits into L1/L2.Memory.query_by_type(name, field_filter=None)convenience: ergonomic wrapper overexecute_planavailable on every backend; returns empty for backends without native pushdown so callers see a uniform API.{name}JSX attribute shorthand:<Box {title} {count} {onClick}/>is now sugar for<Box title={title} count={count} onClick={onClick}/>. The parser recognises{followed by a single bare identifier and a closing}as a shorthand; the resultingJsxNormalAttributecarries anis_shorthandflag so the formatter round-trips the source verbatim. Explicit dict-spread ({**expr}) is unaffected.- E2024:
has-field declarations inside a JSX{...}slot body are now rejected. A slot body is a statement template that re-runs on every render; declaring reactive state there would compile to a conditionaluseStateand violate React's rules of hooks. Declarehas-fields at the enclosing component scope (thedef -> JsxElementbody) instead. - W2021:
forloops in JSX slots now warn when their body emits JSX without akey=attribute, matching the existing W2019 forwhile. Closes the long-standing recursion gap inViewLowerPass._check_body(issue #6082 item 2). Annotate one child of each iteration withkey=to recover stable identity across re-renders. skip;for JSX slot early-exit:skip;inside a{...}JSX slot body is now the canonical slot early-exit -- the view-lower pass rewrites it to the samereturn <>{acc}</>;fragment-return shape that the legacy barereturn;used to lower to. Reads correctly ("skip the rest of this slot's accumulator") instead of misleadingly suggesting a function-exit.E2023for redundant{...}slot wrapping inside a slot body: writing{if cond { <X/> }}inside an already-slot-mode body (the common copy-paste mistake when moving conditional render from JSX-child position into a slot body) now produces a single targeted diagnostic with the fix written out, instead of a cascade ofE0001/E0002parse errors. The parser consumes the redundant braces and parses the inner statement so the rest of the file still parses cleanly; the formatter refuses to rewrite source whileerror_count > 0, so the file on disk is untouched.- Feature:
jac codestructural code-intelligence command: A newjac codecommand group answers structural questions a text search cannot -- where a symbol is defined, everywhere it is used, its type, the project map, and which walkers traverse a node type -- emitting machine-readable JSON by default (or--text) so terminal-driven agents can query the compiler the way they reach forgrep. - Native enums honor their declared type: Native compilation now treats an enum member as its enum type rather than a bare integer, and lowers
.valueand.nameon direct integer-enum members. Ref[T]has-fields lower touseRef(#6082 item 8): In client context (.cl.jac/cl {}), ahas-field of typeRef[T]constructed with= Ref()lowers to React'suseRef(null), and= Ref(initial)lowers touseRef(initial), mirroring howhas count: int = 0;lowers touseState(0).useRefis auto-imported fromreact, the field is non-reactive (no setter, nouseState), and the JSXref={...}attribute wires the handle to the DOM node. A new ambientRef[T]type gives.currenta real type (T | None), closing the long-standing.currenttype-stub gap. Client compilation now always runs the type checker (inference-only) so the lowering is driven by the field's resolved type.- E2025: a
Ref[T]-typedhas-field declared without aRef(...)construction is rejected. A barehas r: Ref[T];has no ref object to hold, so.currentwould never be defined; write= Ref()(DOM ref) or= Ref(initial)(value ref) instead, like every otherhas-field carries a value. - Feature:
.style.cssannex auto-scoping: A.style.cssfile sharing a base name with a.cl.jacmodule now declares scoped CSS classes. The compiler hashes each declared selector with a per-module digest, rewrites the CSS rule selectors and matching JSXclassName/classliterals to the hashed form, leaves:global(...)selectors verbatim, and emits the rewritten stylesheet asModule.gen.cssplus a side-effectimport "./<base>.css";. - Feature: W1037 now flags
typing.Anyannotations: The explicit-Any warning (W1037), previously limited to the Jacanykeyword, now also fires ontyping.Anyused in annotations: on parameters, return types, fields, assignments, and nested forms likelist[Any]orAny | None.typing.Anyis matched by resolved type identity against the prefetchedAnyclass, so aliases such asfrom typing import Any as Aare caught while a user-defined class namedAnyis not. This closes the silent loophole where importingAnybypassed the warning that theanykeyword triggers. exceptarms on a slottry/awaiting: On thecltarget, a JSX slottry { ... } awaiting { ... } except { ... }now lowers to a<JacClientErrorBoundary fallback={...}>(the error fallback) wrapping the existing<JacAwaiting fallback={...}>{...}</JacAwaiting>(the loading fallback). Both components are auto-imported from@jac/runtime, whereJacClientErrorBoundaryre-exportsreact-error-boundary'sErrorBoundary-- the same boundary jac-client installs at the app root. PreviouslyViewLowerPassreturned early once it built the<JacAwaiting>wrapper, so theexceptarms were parsed and then silently discarded; the authored error UI never reached the runtime (issue #6082 item 3). The except bodies are concatenated in source order into the boundary's fallback; per-type dispatch and the optionalexcept ... as <name>binding are not modeled, matching the app-level boundary's behavior.- Feature: Native
Filehas a central type model: Addedna_builtins.pyi, a native-scoped ambient stub modeling the runtimeFiletype (and the nativeopen() -> File). TheTypeEvaluatorloads it like the JSXdom_types.pyistub but, instead of merging it into the global builtins, resolves its names only inside.na.jacmodules via a native-context-gated lookup -- soFile/opennever leak into regular.jaccode.File-typed annotations now carry a realClassType("File")(previouslyUnknownType), andNaIRGenPass._lower_class_typelowersFileto its runtime struct on demand. This removes the last name-based fallback in_resolve_jac_typefor native code (the residual drops to zero hits across the native suite), unblocking the #6119 capstone. Part of #6114 / #6049 section B. - Feature:
jac aireports generation throughput: The per-turn stat line now shows generation speed in tokens per second (tok/s), computed from the turn's completion tokens over its wall-clock duration, alongside the existing token totals. - Feature: Walrus operator in native compilation: The
:=walrus operator now lowers in the native (LLVM) backend, binding a name inside an expression (such as anif/whilecondition) and evaluating to the assigned value. - Feature: Pipe-forward operator in native compilation: The
|>pipe-forward operator now lowers in the native (LLVM) backend, desugaringlhs |> ftof(lhs)(with a tuple left-hand side spreading into positional arguments). - Feature: C-style iter-for loop in native compilation: The
for <init> to <cond> by <step> { ... }loop now lowers in the native (LLVM) backend, withbreakandcontinuehonoring the step (continue still advances the counter). - Feature: Nested function definitions in native compilation: A
defnested inside another function now lowers in the native (LLVM) backend (including self-recursion and module-global access). Closure capture of enclosing-function locals is not yet supported and is rejected at compile time. - Feature: Match statements in native compilation:
matchnow lowers in the native (LLVM) backend for value, singleton, wildcard, capture (as), and OR patterns, plus case guards. Structural patterns (sequence/mapping/class) are not yet supported and are rejected at compile time. - Feature: async/await in native compilation (synchronous model): An
async defcompiles like a normal function andawait enow lowers to the value ofein the native (LLVM) backend. Native has no event loop, so awaits resolve immediately; this is correct for sequential async code and does not provide concurrency. - Feature: flow/wait in native compilation (synchronous model):
flow eandwait hnow lower in the native (LLVM) backend, running sequentially:flow eevaluateseeagerly andwait hreads the result. Results match a concurrent run for independent tasks; there is no actual parallelism. jac browseheadless browser command: Drive a headless Chrome/Chromium over the Chrome DevTools Protocol to navigate, interact with elements, snapshot the accessibility tree, and capture screenshots.- Feature:
property-to-nativeauto-lint (W3043): The auto-linter now rewrites Python-idiomatic@property/@x.setter/@x.deleterdeclarations into nativehas x: T { getter; setter(value: T); deleter; }accessor blocks, coordinating the matchingimpl Cls.xbodies intoimpl Cls.x.getter/.setter/.deleter. The bootstrap transpiler (jac0.py) also learned native property syntax, so the accessor form now works throughoutjac0coreas well.jaclang's getters/setters have been migrated to the native form codebase-wide. jac aidrives a built-in browser: The coding agent can open a headless browser, read pages, and exercise user flows through built-inbrowse_*tools, with no external browser CLI to install.jac airuns in Plan, Build, and QA phases: The coding agent now works through distinct planning, building, and QA stages, each with its own focused tools, and shows generation speed (tokens/sec) for each step.- Feature: Cross-run structural index cache:
CodeIntelligencenow persists a flat, parse-only structural index (archetypes, fields, signatures, and walker entry points) to aSEC_SYMINDEXJIR section, sojac code map/walkersserve the project map on a cold start without re-parsing. The cache is keyed by source content hash, invalidating per-module on edit. - Feature: Lazy code-intelligence load:
CodeIntelligence(lazy=True)builds only the cheap structural index up front and defers the full type-checked pipeline until the first symbol or diagnostic query that needs it. Thejac aiagent uses this soproject_mapand walker queries return immediately, paying semantic-analysis cost only on demand. jac ai --uiweb mode with live phase-graph visualizer: The coding agent can now run in a web UI that streams the agent's Plan/Build/QA progress in real time, with a live phase graph, activity feed, conversation view, and stats bar.
Bug Fixes#
- Fix:
with root entryin walker abilities now raises an error: Writingroot(lowercase) instead ofRoot(uppercase) as a walker ability trigger used to compile without any complaint but the ability body would never run at runtime. The type checker now emitsE1116pointing directly at the keyword, covering bareroot, union formRoot | root, and tuple form(Root, root). At runtime, using an archetype instance as a trigger annotation now raises a clearTypeErrorwith a hint pointing to the correct class. - Fix: clear error message when
jac.tomlis malformed: A TOML syntax error injac.tomlpreviously surfaced as multiple misleading"Install the missing package"plugin-load warnings.read_toml_with_encodingnow wrapsTOMLDecodeErroras a typedMalformedJacTomlErrorcarrying the file path, so every caller can produce an actionable error.import jaclangandget_disabled_pluginslog a single stderr warning and continue (notebooks, scripts, andjac formatkeep working). ThejacCLI's_load_project_configcatches the typed error and exits with a red banner pointing at the file plus common-cause hints. - Fix:
SqliteMemory.recover_allnow processes nodes before edges, & warns when a re-link target is missing: Quarantine recovery previously iterated in undefined DB order -- if anEdgeAnchorwas restored before its connectedNodeAnchor, the re-link step silently no-oped and leftdata.edgesempty even though both rows were nominally recovered. The batch is now sorted so everyNodeAnchoris written back first. An explicitlogger.warningis also emitted when the target node is absent from live anchors, giving operators a clear signal when recovery is partial. - Fix:
jac checkon.impl.jacfiles backed by.na.jacdeclarations no longer floods falseE1032errors: When a method implementation file (.impl.jac) has its class declaration in a.na.jacfile,jac checkfailed to find the declaration, soselfresolved asUnknownand everyself.xaccess raised a spuriousE1032: Type is Unknown. - Fix:
T | Nonenarrowing now propagates into JSX statement slot bodies:CFGBuildPassgained anexit_jsx_slothandler that sequentially links the slot body'sCodeBlockStmts and wires the first body statement'sbb_into the slot's enclosingCodeBlockStmt. A name reference inside{for ... { ... }}/{if ... { ... }}now sees narrowing established by an outerif x is None { return; }guard, eliminating false-positiveE1099("Cannot access attribute") inside slots. - Fix: registry-driven numeric kind for fixed-width primitives:
JacTypeRegistrynow records each primitive'sNumericKind(IntKind,FloatKind,NotNumeric) andTypeEvaluator._assign_classconsults it. Fixed-width primitives (i8..u64,f32,f64) are width refinements ofint/float, sox: f64 = 3.14,i32 + i32,f32 -> f64, andi32 -> f64all typecheck (the existingint -> floatallowance generalises to anyIntKind -> FloatKind);f64 -> i32and otherFloatKind -> IntKindmoves stay banned.jac_builtins.pyiis now flagged as a builtin stub so its fixed-width classes satisfyis_builtin(). Drops native-fixture type errors 171 -> 48 across the 80 fixtures. - Fix: topology-index queries now see cross-root edges: Edges crossing root boundaries are mirrored into the target's per-root index so reverse queries find them, and
plan_querybails out of multi-hop chains the moment a hop lands on a foreign-owned node (the natural edge walk takes over). Per-root index queries no longer return empty / undercounted sets for forward chains that cross into another user's subgraph. - Fix: type-checker import resolver prefers local files over typeshed:
resolve_module(inmodresolver.jac) used to checkvendor/typeshed/stdlib/before the importer'sbase_dir, so a localast.na.jacnext to the file doingimport from ast { ... }was silently shadowed bystdlib/ast.pyi. The type checker then saw the wrong symbols (or none), producing<Unknown>cascades that masked real types. Now the resolver checks_candidate_from(base_dir, ...)first -- mirrors NaIRGenPass's struct-registration fix and unblocksfix_shadow_entry.na.jacfor type-check. Closes the remaining shadow gap in #6058. - Fix: in-process clients no longer share app identity:
ExecutionContextnow owns its ownbase_path_dirandfull_target_path, so twoJacTestClientinstances (or an embedded REPL / MCP host) in the same process can't poison each other's L3 SQLite file or surface stray anchors throughallroots(). - Fix:
super.init(...)into a parent with an auto-generated constructor now type-checks cleanly: calls no longer raise a spurious unresolved-type warning. - Fix:
Union/Optionalwith string type names no longer produces false type errors: String names insideUnion[(...)],Union['T'], andOptional['T']were treated as plain text (Literal["T"]) instead of the actual types they refer to, so valid return values and assignments were incorrectly rejected. The type checker now resolves these strings to in-scope types when possible; unresolvable names still degrade toLiteral[...]as before. - Semantics change: Code that intentionally used
Union[('Foo',)]to meanLiteral["Foo"](non-standard) will now resolveFooas a type when it exists in scope. UseLiteral['Foo']for literal string types. - Fix: reassigning a module global from an
.impl.jacbody keeps its declared type:global x; x = ...in an implementation file no longer shadows the global with an untyped local, so int arithmetic on it type-checks correctly. - Fix: hyphenated bool CLI Args leaked a phantom
X-dashed: Truenamespace key (#6115):--Xand the auto--no-Xcompanion now share the underscored dest, andHookContext.get_argfalls back to the underscored form.get_arg("dry-run")andget_arg("dry_run")both resolve cleanly. - Sibling JSX slots no longer crash on render: Two
{...}statement slots in the same element's children now each lower to an accumulator IIFE that declares its ownletbinding. Previously every author-written slot reused the name__jac_view_kids, so the second sibling IIFE emitted a bare reassignment to a name scoped to the first (already-returned) IIFE, throwingReferenceError: __jac_view_kids is not definedunder ES-module strict mode -- anycl-target component with 2+ sibling slots crashed as soon as it rendered.ViewLowerPassnow suffixes each slot's accumulator (__jac_view_kids,__jac_view_kids_1, ...) off the existing nested-slot counter. - Fix: Native lexer parity on non-ASCII source: The lexer now uses byte offsets and rune-aware scanning (the Rust/Go/Zig convention), so the native and Python lexers produce identical token values, positions, and diagnostics. Previously token positions drifted after any non-ASCII character, illegal non-ASCII characters were over-counted (one error per UTF-8 byte), and embedded NUL truncated the source. Also implements native
bytes.decode(), which had returned an empty string.parse()now always routes through the native-capable lexer surface, so parses transparently use the native-compiled lexer wherever it has been built (setJAC_NATIVE_LEXER=0to force the Python lexer). - Fix: multiple impl-separated property accessors in one class:
impl Cls.prop.getter(and.setter/.deleter) now resolve per-property instead of colliding on a single class-level accessor symbol. Previously a class with more than one impl-separated native property silently left all but one getter unimplemented (emptypassbody), since everygetterregistered under the same bare name in the class scope. - Fix: assigning a plain field no longer fails with a false "read-only property" error after a cached recompile: When an object had both a plain
hasfield and a nativegetter/setterproperty, reopening or re-checking the file would falsely reject assignments to the plain field withE1005: Cannot assign to read-only property. A fresh compile was fine; only the cached run errored. The cache now correctly distinguishes a plain field from a property, so assignments work consistently whether or not the cache is warm. - Fix:
jac formatcorrupts a decoratedasyncarchetype: the formatter movedasyncahead of the decorator (async @decorator walker ...), producing code that no longer parsed while still exiting successfully.asyncnow stays before the archetype keyword, so formatting is a no-op. - Fix: a null field no longer quarantines your entire node graph: A single stored null on a node field used to cascade into thousands of quarantined nodes and edges. The runtime now recovers the field to its declared default and loads the node normally; only nodes with no recoverable default are quarantined, and only that one node.
- Fix:
jac formatmerges animplability name into itswithevent clause: formatting a separated implementation such asimpl Greet.start with Root entry { ... }dropped the space between the target name and the event keyword, emittingimpl Greet.startwith Root entry, which no longer parsed while the formatter still exited successfully. The event signature now keeps its leading space, so the implementation stays valid and formatting is a no-op. - Formatter spacing for set literals and assign comprehensions:
jac formatnow puts a space after commas in set literals and assign comprehensions, and wraps long set literals one element per line, matching how lists, tuples, and dicts are formatted.
Refactors#
- Refactor: NaIRGenPass consumes the central type checker for type lowering: NA's parallel AST-walking type resolver (
_resolve_jac_type) is replaced by aTypeBase -> ir.Typelowerer (_lower_type) that consumesExpr.typeproduced byTypeEvaluator.JacCompiler.compile()now auto-runs the type-check schedule for any.na.jacmodule before codegen (gated by a re-entrancy guard, with inference-only diagnostic suppression so native keeps its own type discipline).TypeCheckPass.exit_sub_tagpersists.typeon every annotation tag so codegen backends can lower it directly._resolve_jac_typeshrinks 364 -> 195 lines;_llvm_to_spec,_resolve_callable_type, and_resolve_func_ptr_typeare removed. Native test suite passes 342/342; legacy AST-fallback usage across the 80 native fixtures drops from 2025 to 60. Follow-ups tracked in #6049. topo_utilsis now strictly index maintenance:plan_query,plan_query_ordered,batch_load_nodes,batch_load_nodes_by_ids,_extract_type_name_from_filter, and_filter_has_predicatesare removed (~297 lines). Filter introspection moves into the planner.topo_utilswrites the topology index on graph mutations; the planner reads it for queries.refs()injac0corenow drives the whole pushdown ladder through a singleplanner.execute_pathcall.- Refactor: Native builds are type-checked: Implicit native (
.na.jac) compilation keeps its type diagnostics and a type error fails the build, holding native code to the same type discipline as the rest of Jac. - Refactor: Imported native modules load type-checked instead of raw-parsed: When
NaIRGenPass._register_imported_struct_typeshits a.na.jacimport that is not already in the module hub, it now pulls it in throughJacProgram.compile(which runs the implicit native type-check and hub-caches the result) rather than a raw parse. The raw-parse fallback produced a second AST that never ranTypeCheckPass, leaving.type = Noneon its annotations and silently routing importedf64/str/enum types to name-based resolution; the pipeline load makes them lower through the unified_lower_typepath.prog._compile_optionsis saved/restored across the nested compile so subsequent native codegen keeps itsskip_native_engine/aot_mode. Closes the last divergent un-type-checked path; the common hub-resident path was already covered by #6096 / #6050. Part of #6114 / #6049 section B. - Refactor: Native global-init type inference consumes
Expr.type:NaIRGenPass._infer_global_init_typenow lowers the initializer's central.type(populated byTypeEvaluator) through_lower_typebefore falling back to initializer-shape inference, so un-annotated native globals resolve through the same unified seam as annotated ones instead of an ad-hoc AST walk. The name-based fallback in_resolve_jac_typeis retained as a documented safety net until imported native modules are type-checked (#6117) and nativeFileis modeled centrally (#6118); collapsing it to the unified path is the tracked follow-up. Part of #6114 / #6049 section B. - Refactor: Native type lowering is fully central -- name-based fallback removed: With imported native modules type-checked (#6117) and native
Filemodeled centrally (#6118), every.na.jacannotation now resolves through the unified_lower_typepath.NaIRGenPass._resolve_jac_typeis collapsed to the unified path plus ani64default; its name-based fallback (thestruct_types/type_maplookups) is removed after instrumentation showed it reached zero times across the native suite. The unreachableenum_typesbranch in_lower_class_typeis also dropped (every enum name is registered intype_maptoo, which is checked first -- the same proof as #6120). No behavior change. Part of #6114 / #6049 section B. - Refactor: JSX
{...}slots lower atclcodegen, surface AST stays faithful: TheViewLowerPassthat desugared eachJsxSlotbody in place into the__jac_view_kidsaccumulator IIFE before analysis is removed. Its two jobs are split: the slot body-rule diagnostics (E2019-E2024, W2019-W2021, including theawaitingE2022/W2020) move toASTValidationPassas a non-mutating walk over the author's body, and the accumulator IIFE plus thetry/awaiting/except-><JacAwaiting>/<JacClientErrorBoundary>wrappers are built as direct ECMAScript AST inEsastGenPass(thecltarget only). Because the surface tree is no longer mutated, theJsxSlot.view_raw_srcverbatim-source snapshot is deleted and the formatter/unparser round-trip slots from real tokens, so comments and formatting inside a slot body work the same as anywhere else. Each slot's IIFE still declares its ownletaccumulator with a unique name (preserving the #6128 sibling-slot fix). No behavior change to emitted output. - Refactor: Unify client endpoint-effect classification into one pass: The client SWR auto-cache's reader/writer
{is_writer, touches}tagging was produced by two divergent classifiers (a lazy spawn-site analysis inEsastGenPass._analyze_body_effects, and a weaker runtime duplicate inModuleIntrospector._analyze_endpoint_effectsthat always emittedtouches: ["*"]), which could disagree and never classified imported-module walkers precisely. Both are replaced by a single authority,WalkerEffectsPass, that runs after symbol resolution and beforeEsastGenPass, classifying every walker archetype and public server-contextdefat its declaration via resolved symbols (no syntactic fallback).EsastGenPassnow seeds its manifest from the precomputed effects, and the server's_load_manifestaggregatesendpoint_effectsacross all compiled modules so imported-module endpoints are covered precisely. Removes roughly 340 lines of duplicated logic; full compiler and runtimelib suites pass (2878 passed, 57 skipped). - Refactor: Console facade/renderer split with a structural trust boundary:
JacConsolebecomes a capability/routing facade that owns terminal capability detection (use_color/use_emoji) and stream routing (_resolve_stream), composes its 15-method surface once, and delegates drawing to a swappableConsoleRenderer(base renders ANSI). Caller data now flows asSpan{text, style|None}/Contentvalues that every renderer must emit verbatim, so markup/ANSI in paths, URLs, diagnostics, and source snippets is never interpreted (the #5995 injection class). Also fixesprint(..., file=...)routing to arbitrary streams. Plugins customize output by overriding_make_rendererrather than re-implementing the surface.
Documentation#
- Skill:
jac-sv-multi-user: Newjac guideskill for cross-user data sharing (ambientgrant/revoke/allroots, per-user roles, access-level table);jac-sv-authback-links to it. - Docs:
jac-cl-componentsskill: Clarified JSX comment syntax -{#* ... *#}is only valid inside JSX element children; using it outside JSX or using JS-style{/* ... */}is a parse error (E0001).
jaclang 0.15.3#
Breaking Changes#
- Breaking:
defviewis now a reserved keyword:defviewintroduces the new view declarator. The single-token nameviewis not reserved -- onlydefviewis -- so existing variables, parameters, fields, and walker names calledviewkeep working unchanged.
New Features#
- Views:
defviewdeclarator: Newdefviewdeclarator, sugar fordef:pub Name(...) -> JsxElementwith a statement-based template body, statement-position control flow (if/for/while/match/switch/with/try), and a barereturn;guard. The parameter list is optional, so a view with no props can be writtendefview Name { ... }. - Views: dynamic JSX tags:
<@expr />resolves its element tag from an expression (an identifier, a dotted access, or a brace-wrapped<@{expr}>) to a host-tag string, a view reference, or astr | typevalue. - CLI:
jac guidereference guides: The curated Jac reference guides now ship inside the compiler.jac guidelists every guide,jac guide <topic>prints one,--searchfilters by keyword,--jsonemits machine-readable output, and--export <dir>writes the guides as a Claude Code skills directory. - CLI: Diagnostics link to guides:
jac checkdiagnostics now point at the relevant guide when a code maps to one (e.g. a type error printsrun 'jac guide jac-types' for guidance). - CLI:
jac createseedsAGENTS.md: New projects are scaffolded with anAGENTS.mdthat points AI coding agents atjac guide. - Feature:
expr as Typecast operator: Added a first-class, type-erased cast.value as Typeevaluates to the operand at runtime and takes the named type statically (the same semantics astyping.cast, with no import). It gives an explicit escape hatch for re-typing anAnysource, such asresult.reports[0] as list[TweetView], without tripping the strict-gradualE1001.with/exceptkeep their ownasalias; parenthesize a cast used in those positions. - Feature: Type-check JSX props-bag components: A component declared with a single
propsparameter (the props-bag pattern that codegen already supports) is no longer rejected by the JSX prop checker. An untypedprops: anybag accepts all JSX attributes; a typed props bag validates each attribute against the members of the props object's type. New warningW1052is emitted once at the definition of a component using an untypedprops: anybag, noting it opts out of all prop name and type validation and pointing at typed alternatives. jac aiinteractive coding agent: A newjac aicommand launches an interactive Jac coding agent in the terminal, working with local or cloud byLLM models.- Feature: Freeze inferred local variable types at first binding: An unannotated local variable's type is now inferred and frozen at its first binding, enforced as if it had an explicit annotation; reassigning an incompatible type is now a type error (E1001). The
None-sentinel pattern and the discard name_remain exempt. - Type Checker: Warn on explicit
anyannotations: Explicitly typing a parameter, return type, field, or assignmentanynow emits a W1037 warning. - Feat:
cl { }/sv { }/na { }braced blocks no longer deprecated: Module-level codespace braced blocks no longer emit theW0064deprecation warning; the diagnostic code is removed. Braced blocks andto cl:/to sv:/to na:section headers are now both fully supported ways to mix codespaces in a file -- braced blocks are recommended because the braces bracket exactly the tagged region, while section headers are the flatter alternative for a file that is mostly one codespace.
Bug Fixes#
- Fix:
by llm()in async abilities now emitsawait acall_llm(...)instead of a blockingcall_llm(...): The compiler and runtime hook interface are extended so async walkers get a non-blocking LLM call path. Sync walkers are unaffected. - Fix: Client
inoperator on dynamic values:in/not inon a value of unknown static type now produces Python-equivalent membership in transpiled client code instead of emitting a JavaScriptinthat could crash or return wrong results. - Fix:
jac guidereference-guide inaccuracies: Correctedjac check-verified errors in the bundled guides -- ajac-sv-persistenceexample that failed type-check (E1002), ajac-typesAnyexample that contradicted its own pitfall, and stale claims injac-core-cheatsheet,jac-walker-patterns,jac-node-edge-patterns, andjac-scaffold. Guides now omit-> Noneon void functions (W3037). A newtest_guide.jactest extracts every```jacexample and runs it through the compiler, so guide examples can no longer drift from the language. - Fix: Walker effect classification follows helper calls: The reader/writer classifier used for client-side cache invalidation now recurses into
def/ability helpers, so a walker whose graph mutation lives in a helper is correctly classified as a writer instead of a reader (which previously left the client cache stale until a full page reload). - Fix: cascade-quarantine dangling edges on schema drift: When a
NodeAnchor's archetype becomes unresolvable (e.g. a node type is removed between deploys),SqliteMemorynow also quarantines every connectedEdgeAnchorand strips those IDs from the source node'sdata.edges, preventing permanently corrupt traversal state. Recovery (recover-all) re-links edges back to their source node, fully restoring graph connectivity. - Fix: jac.toml writes no longer corrupt or misroute: Saving
jac.toml(viajac add/remove/update,jac config set/unset, orjac plugins) is now handled by a single canonical TOML serializer, soauthors/maintainersinline tables round-trip correctly, deeply nested tables like[dependencies.npm.dev]are preserved, and no stray empty[plugins]table is emitted. Mutating commands now warn before writing to a parent project'sjac.tomlwhen the current directory has none of its own. - Fix: remaining
jac guidereference-guide inaccuracies: Follow-up QA pass correcting seven claims verified wrong against the compiler --jac-cl-auth(jacSignupreturns an always-truthy dict, soif not signup_resultnever catches failure),jac-sv-endpoints(a plaindefis a registered endpoint, not an internal helper),jac-cl-styling(cn()is variadic),jac-npm-packages(jac-shadcn ships only clsx/tailwind-merge/tw-animate-css),jac-types(lowercaseanyis the gradual type, not capitalizedAny),jac-impl-files(absis unenforced -- a missing impl silently returnsNone), andjac-by-llm(glob llmis an optional override). All guide examples still passtest_guide.jac. - Fix:
localStorage/sessionStoragenow type-check in frontend Jac: The type checker had no declaration for the browser Web Storage globals, so they resolved to Unknown and every.getItem/.setItem/.removeItemaccess raised a spuriousE1032. Added aStorageambient class pluslocalStorage/sessionStorageglobals todom_types.pyi; a bogus attribute now raises the preciseE1030instead. - Fix: Native compilation now works on macOS: The native LLVM backend emitted calls to
malloc_usable_size, a glibc symbol absent from macOS libSystem, so every native binary crashed at load withSymbol not found: _malloc_usable_size. The GC-debug emitters now selectmalloc_sizeon Darwin andmalloc_usable_sizeelsewhere. - Fix: Packaged Jac wheels import without an
__init__.py: Apip installed Jac package now resolves its.jacsubmodules automatically, sojac bundleprojects need no hand-written__init__.pybootstrap. - Fix: Native function-scope global rebinding now matches Python/sv semantics: A bare function-scope assignment to a name that is also a module
glob(e.g.counter = 5) rebound the module global in the native (.na.jac) pathway, while the sv pathway treats it as a new function-local per Python scoping rules, so identical source produced different behavior across pathways. Native now binds a function-local in that case; rebinding a module global requires an explicitglobal x;, matching CPython and the sv pathway. Native code that relied on the implicit rebind must now addglobal x;. - Fix: version-guarded stdlib symbols now type-check: Any stdlib symbol defined inside a top-level
if sys.version_info >= (...)block in its.pyistub (over 120 stdlib modules, includingdatetime.UTC,abc,hashlib,weakref,locale, …) was invisible to the type checker, so using it produced spuriousW1051: Expression type could not be resolvedwarnings andE1053parameter-type errors. These symbols now resolve to their declared types regardless of where they appear in the user code (value, annotation, function argument). - Fix: client runtime
Anyannotations now resolve as the Jac top-type:client_runtime.cl.jacand its impl usedAnyin 88 annotation sites but never imported it, so the checker treated every occurrence as an undefined name and cascaded W2001/W1051/E1032 across the file. Renaming to the canonical lowercaseanybuiltin clears 107 W2001 warnings, 359 W1051 warnings, and 56 errors with no runtime change. - Fix: client runtime
dict/listannotations now declare element types: 38 baredict/listparameters and returns inclient_runtime.cl.jacand its impl now spell out their element types, usingdict[str, any]/list[any]for genuinely heterogeneous JSX prop bags, RPC payloads, and hook return tuples;dict[str, str]for CSS style maps; andlist[str]where the site iterates strings. Clears all 38 W1036 warnings in the file with no runtime change. - Fix:
anykeyword now resolves totyping.Anyat runtime:PyastGenPass.exit_builtin_typeemitted literalanyin Python, which evaluated tobuiltins.any(the iterable predicate) when tools like Pydantic BaseModel or FastAPI calledget_type_hints()on an annotation. Disambiguation is now by AST position: aBuiltinType("any")that is the immediatetargetof aFuncCall(e.g.any(iter)) still emitsbuiltins.any; everywhere else it emitsName("Any")with an auto-import from typing. With the runtime semantics fixed, 369 .jac files in the tree are normalised to the canonical lowercaseanyin type-annotation positions; identifier uses (enum members,.Anyattribute access) and runtime-value uses (cast(Any, ...),else Any,isinstance(_, Any)) are preserved.
Documentation#
- CLI:
jac-packagingreference guide: A newjac guideentry covers packaging a Jac project as a wheel and publishing it to PyPI:jac.tomlmetadata, the package-directory layout, the__init__.pybootstrap that makes.jacmodules importable after install, console-script entry points,jac bundle, and twine.
jaclang 0.15.2#
Bug Fixes#
- Native: Bug Fixes: Fixed multiple bugs in the
jac-nativeAOT/JIT compiler, including symbol-collision detection at link time (E5026) with promotion of import compile/link failures from warnings to errors (E5024/E5025), and type-safe handling of typed variable re-declarations (x: T = ...) where the new type differs from the previous alloca's type. - Native: Incremental Compilation:
jac nacompilenow caches per-module LLVM IR under.jac_ir/keyed by source SHA-256 hash and skips recompilation of unchanged modules. A new--scrubflag wipes the cache for a clean rebuild, andjac run --no-cachenow also clears.jac_ir/directories under the cwd. - Fix: Cross-user root discovery:
allroots()now returns every root across all memory tiers instead of only the requesting user's own root.
jaclang 0.15.1#
New Features#
- Feature: Native property syntax with
getter/setter/deleteraccessor blocks: Adds first-class property declarations onhasbindings:has x: T { getter; setter; deleter; }. Properties are pure computed accessors -- backing storage is always declared explicitly via a separatehas _x: T = value;field and referenced asself._xfrom accessor bodies. Supports dotted-impl bodies (impl Cls.x.getter, etc.), read-only enforcement whensetteris omitted (E1005), and write-type checking against the property's declared type. Combining an initializer with an accessor block emitsE0080; an empty accessor block emitsE0081.
jaclang 0.15.0#
Breaking Changes#
- Breaking: Strict
Anysemantics in Jac modules: In.jacfiles,Anycan no longer be silently assigned to a declared non-Any, non-objectdestination. The rule applies at every "destination has a declared type" site (annotated assignment, has-var initializer, function argument, return, yield, edge-connection assign) and recurses into containers (list[any] -> list[T]errors via element-wise recursion). Inferred destinations and explicitanyannotations remain permissive -- they are the user's escape valve.Any -> objectandAny -> TypeVarstay permissive too, soprint(x)and generic-bound calls keep working. Python modules (.py/.pyi) keep PEP 484 semantics. Migration: either type the source (typed walkerhas reports, typed Python stubs) or accept Any explicitly at the boundary (x: any = py_call();).
New Features#
- Add: SSE-aware
sv_service_callhookspec + streaming generator returns: The defaultsv_service_callimpl now inspects the downstream response'sContent-Type; when it'stext/event-stream, the call returns a Python generator that yields parseddata:event dicts and terminates on theevent: endframing (or raisesRuntimeErroronevent: error). Lifecycle of the underlyinghttpxconnection follows the generator (closing on exhaust, drop, or scope-exit). Pairs with a_finalize_call_responsefix inruntimelib/server: theisgeneratorcheck was onreportsrather thanresult, so explicit generator returns fromdef:pubwalkers/functions silently fell into thestr()fallback and were never streamed. Newruntimelib/sv_sse.jacexposes the consumer-side helpers. - Lint: W3042 Auto-Convert
.map(lambda → JSX)to List Comprehension:jac lint --fixnow rewrites verbose React-style.map()lambda patterns into Jac's native JSX comprehension syntax.items.map(lambda item: any -> any { return <li>{item}</li>; })becomes[<li>{item}</li> for item in items]. A leading.filter()chain is also collapsed:items.filter(lambda item: any -> bool { return item.active; }).map(lambda item: any -> any { return <li>{item.name}</li>; })becomes[<li>{item.name}</li> for item in items if item.active]. Only single-parameter lambdas are converted; two-parameter forms (e.g.lambda item: any, idx: int -> any { ... }) are left unchanged, as there is no reliable way to statically determine which parameter is the index. The rule is suppressible via# noqa: W3042ormap-lambda-to-comprehensioninjac.toml. - Topology Index: O(1) Typed Lookups + MRO + Planner Bypass: Reimplement the topology index so type-filtered chain steps resolve in O(1) per filter via a per-source type-keyed lookup map (instead of scanning the full adjacency list), with MRO fan-out so parent-type queries (
[-->[?:Base]]) return all subtype instances at zero extra cost. Bidirectional indexing (IN/OUT/ANY) shares the lookup path. The encoded blob stays the same compact size as before (only canonical edges + a small per-type MRO chain are persisted; the lookup map is rebuilt on decode).plan_querynow short-circuits to aNonereturn when every chain step has no edge-type and no node-type filter, so fully-unfiltered traversals ([node-->]) skip the index decode entirely and fall through to the natural edge walk. Single-hop typed queries see up to 51× speedup at low selectivity; 2-hop chains compose for 10×+ wins. Old version-1 blobs decode to an empty index and repopulate on the next mutation. - Feature: Curated typing constructs are ambient:
Callable,Protocol,TypeVar,Generic,Iterable,Mapping, and similar annotation-only typing names resolve in user code withoutimport from typing { ... }. - Feature: Lint redundant imports of ambient typing names: New W1103/W1104 warnings flag
import from typing { ... }(andfrom collections.abc { ... }) of names that are now ambient -- e.g.Callable,Mapping,Protocol,TypeVar-- and recommend the lowercaseanykeyword in place oftyping.Any. Driven bytyping_ambient.pyi's__all__so the lint stays in lockstep with the merged builtins. Aliased and*imports are skipped. - Feature: Type-check walker typed reports: Walker archetypes can now declare
has reports: list[T] = [];to type their report channel end-to-end. NewE1112rejects non-listreportsshapes andE1113flagsreport Xwhose value type isn't assignable to the declared element typeT. Untyped walkers keep the inheritedlist[Any]and stay permissive. - Feature:
sv importsupportswalker:pubarchetypes: A walker on the provider can now be imported across the sv-to-sv boundary; the compiler generates a stub class whose construction issues aPOST /walker/<name>and rehydrates the response into a real walker instance withhasfields andreportspopulated. Previously walker symbols silently fell through to a regular in-process import and call sites failed at runtime with<Walker>() got an unexpected keyword argument. - Feature:
import typefor annotation-only imports: A new opt-inimport type from foo { Bar }form lowers toif TYPE_CHECKING: from foo import Barin the generated Python, breaking circular imports between modules that reference each other only in type signatures. - Feat: Storage-layer pushdown for sliced edge-ref traversals:
[-->][?:T][a:b]now bounds the storage-layer load to the sliced subset instead of materializing every neighbor first. The compiler fuses the trailingIndexSliceinto aslc=kwarg on the emittedJac.refs(...)call; the runtime hands the slice to a newplan_query_orderedplanner that walks the topology index's insertion-orderedadj_list, applies type filters via the existing buckets, and returns the bounded ordered UUID list. The persistence backend is then asked only for those UUIDs viabatch_get. Phase 1 covers single-hop chains with type-only filters; multi-hop and predicate-filtered chains fall back to the existing path with a post-load Python slice (semantics unchanged). On a 2,000-neighbor graph,[-->][?:Lead][0:50]drops from 4,400 SQLite fetches / 710 ms to 50 fetches / 14 ms (52x). - Feature:
--extrasflag forjac install: Optional dependency groups defined under[optional-dependencies]injac.tomlcan now be installed directly viajac install --extras <group>(short:-x). Works for both editable (-e) and venv installs. Self-referencing group entries (e.g."pkg[all]" = "*") are expanded transitively via BFS; third-party extras liketestcontainers[mongodb,redis]pass through to pip unchanged. Resolution logic lives in the newDependencyInstaller.get_extra_package_specs()method. - Feat: Multi-hop slice pushdown for edge-ref traversals: Extends the single-hop slice-pushdown from #5912 to multi-hop chains.
[r-->-->[?:T]][0:50]now resolves non-final hops via the set-basedresolve_chainand drives the final hop through the existingresolve_chain_orderedadj_list walk, so storage is asked only for the sliced subset instead of materializing the full type-filtered neighbor set. The plugin contract is unchanged (storage still sees onlybatch_get); the enrichment lives entirely in the topology index layer so the optimization stays plugin-agnostic acrossMemorybackends. Predicate filters still bail (Gap 2 of #5913 remains open). Addresses Gap 1 of #5913. - Feature: Portable
new(...)ambient builtin: A new ambient builtinnew(Cls, ...args)is now available without import in every codespace. On the server it returnsCls(*args); inclblocks the compiler lowers the call toReflect.construct(Cls, [args])in the generated JavaScript. This lets the same call site work whether the surrounding code targets Python or JavaScript, and replaces the previous client-onlyReflect.construct(...)idiom. DiagnosticE0012now recommendsnew(target, ...args)instead of the bareReflect.construct(...)spelling, and the AST validation pass no longer rejectsnewas a standalone identifier. - Feature: Expose pip install flags via
jac install: Adds--force-reinstall,--no-cache-dir,--pre,--dry-run,--no-deps,--quiet, and--prefer-binaryflags tojac install, passing them through to the underlying pip invocation.--upgraderemains hardcoded inDependencyInstaller.install_package, preserving existing upgrade behaviour across all commands. Internally,DependencyInstallernow accepts aninstall_flags: list[str]field for additional pip flags.
Bug Fixes#
- Fix: CLI registry preserves extensions across higher-priority command replacement: When two plugins register the same command name and the higher-priority one wins,
pre_hooks,post_hooks, andextra_argsregistered viaextend_commandon the original spec are now carried over instead of silently dropped. This unblocks cases likejac setup mobile, where jac-client's_handle_setup_targetwas being wiped by jac-scale'ssetupre-registration. - Fix: SqliteMemory dirty-field tracking prevents concurrent walker data loss:
sync()now tracks changes at the individual field level rather than comparing whole archetypes. Only fields that actually changed since the last DB load are written back, so two walkers modifying different fields on the same node no longer clobber each other. In-place container mutations (e.g.list.append) are also detected correctly. As a side effect, read-only walkers no longer trigger spurious write-access checks or access-denied log entries during synchronization. - Fix: Type narrowing through subscript access: Resolved a false-positive type error where
items[0].attrwas not narrowed after anis not Noneorisinstanceguard onitems[0]. - Fix:
cast()with a union target type: Resolved a false-positive type error wherecast((A | B), x).attrwas rejected becausecastreturned class types instead of instances for union targets. - Parser: Actionable diagnostics for malformed typed connection operators: Replaces generic cascade errors with two precise codes (E0027, E0028) for typed connection operators (
+>:/<+:) missing their closing token. Error recovery viasynchronize()suppresses cascade noise. - Fix: walker
hasfields with mutable defaults no longer crash request validation: walkers likehas results: list[T] = []were unusable over HTTP because the dataclass_HAS_DEFAULT_FACTORYsentinel leaked into the Pydantic body schema;introspect_walkernow resolves the factory and returns the actual default. - Fix: align bare-serve
@jac/runtimewith React Router v6 surface: Bare-serve's runtime declared a hand-rolled router (Route(path, component, guard),Link(props: dict),createRouter([...])) while jac-client re-exports React Router v6 (<Route element>,<Link to>,<Routes>, ...).jac checkcorrectly rejected valid React Router JSX against bare-serve's stale stubs even though jac-client rendered the same code fine; following the stub's advice (<Route component={...}/>) compiled clean but rendered a blank page. Bare-serve now natively implements the React Router v6 surface (Router,Routes,<Route path element>,<Link to>,Navigate,Outlet, plususeNavigate/useLocation/useParams/useRouter) over the existing signal-based reactive context, with no React/Vite/Bun dependencies. The legacycreateRouter/RouteConfig/function-styleRoute(path, component, guard)are removed; user code with router JSX is now portable between bare-serve and jac-client unchanged.jacSignupalso gains aprofileparameter and the syncjacSpawnwrapper is added so apps written against either runtime work in the other without per-call edits. A newtests/runtimelib/test_runtime_surface_alignment.jacparses bothclient_runtime.cl.jacfiles and asserts bidirectional alignment of the routing/hooks/walker/auth surface, so future drift fails CI. - Fix:
sv importcalls with kwargs no longer drop named arguments: calling ansv importeddef:pubserver function from.cl.jacclient code with kwargs (e.g.await greet(name=x)) silently compiled to__jacCallFunction("greet", {}):EsastGenPasscollected kwargs intopropsthen immediately reassignedprops = []and rebuilt the RPC args object from positional arguments only, so the runtime received an empty body and double-wrapped the value, returning a 422 from the server. The call-site rewrite now resolves positional and keyword arguments through a single ordered(name, expr)pass with one place applying__to_wire()boundary wrapping, soawait greet(name=x), out-of-order kwargs, mixed positional+kwarg, and**dictspread all compile to the same shape positional calls produce. New diagnosticE5080rejects the duplicate-argument case (greet(x, name=y)) at compile time, mirroring Python'sTypeError: got multiple values for argument. UpstreamKWPairhandling also now lowers**dicttoes.SpreadElementinstead of aPropertykeyed by the literal'key', so the spread works for every call site, not just the server-call rewrite. - Fix:
jac bundlefails with "No bytecode found" due torootkeyword conflict: Renamed the local variableroottoproj_rootinbuilder.impl.jac,metadata.impl.jacandsources.impl.jac.rootis a reserved Jac keyword (graph root node) and its use as a plain variable prevented bytecode generation, causingjac bundleto crash on import. Also corrected theset_configsignature to acceptJacConfig | None, matching its actual usage. - Fix: JavaScript keyword argument lowering: Corrected ECMAScript generation for keyword arguments, defaults, method calls, and
**kwargsspreads. - Fix: language server refreshes cross-file diagnostics when a dependency changes: an open consumer file's diagnostics no longer go stale when an imported module is edited;
JacProgramnow mtime-validates its hub on every lookup and the LSP fans out re-checks to open consumers when a dependency receives adid_save/did_change. - Fix: language-server diagnostics no longer drop after a debounce-driven type check: in
_do_type_check, the defensive cache eviction inside_fanout_dependentsran after the primary task was queued, racing with the dispatcher worker. When the worker won the race,invalidate_modulewiped the IR entry the worker had just written intomod.hub, and the file's diagnostics disappeared. Eviction now runs before the task is queued so the worker's write is final. - Fix: type checker now narrows
T | Noneafter==/!=againstNone:classify_conditiononly recognizedis/is not Noneand let the equality forms fall through to the literal-eq/ne branch (which excludesNull), soif x != None { x.attr }produced spurious E1099 / E1096 / E1097 errors whileif x is not None { x.attr }worked. The sameNull-on-the-right CompareExpr branch now also acceptsTok.EEandTok.NE, reusing the existingNONE_IS/NONE_IS_NOTpredicates so AND/OR combinators, assert and early-return CFG paths get symmetric narrowing for free. - Fix: type-checker validates
visitagainst the runtime contract:visitaccepted anylist/tuple/set/frozensettarget unconditionally, sovisit ["a", "b"]andvisit [1, 2, 3]slipped through with no diagnostic, whilevisit some_edgeandvisit list_of_edgeswere incorrectly flagged. The validator now matchesJacWalker.visitexactly: a target may be a node, edge,ObjectSpatialPath, or a collection of nodes/edges. Element types of parameterized collections and members of unions are checked recursively;Any/Unknownand unparameterized collections still pass. DiagnosticE1094text updated accordingly. - Fix: type-checker validates
spawnoperand types:spawnpreviously returnedright_typeunconditionally with no operand checks, sop spawn 42,p spawn NotAWalker(1), andn spawn 3.14all passed silently. The checker now mirrorsJacWalker.spawn's runtime contract: exactly one side must be a walker instance (orlist[Walker]); the other must be a node, edge, or collection thereof. New diagnosticsE1114(no walker on either side) andE1115(invalid spawn target) cover the gap.is_walker_type()is added onClassTypefor symmetry withis_node_type/is_edge_type. Spawn now also returns the actual walker type regardless of operand order, so(W() spawn p).reportsand(p spawn W()).reportsare typed alike. - Fix: type-checker validates ability trigger archetype kind:
can ... with X entry/exittriggers had no archetype-kind check, so structurally invalid abilities likewalker W { can on with int entry; }andnode N { can on with Place entry; }slipped through. The validator now mirrorsJacWalker._execute_entries/_execute_exits: walker abilities must trigger on a node, edge, orRoot; node/edge abilities must trigger on a walker. Tuple(A, B)form is checked element-wise;A | Bunion members must all be valid. Untypedwith entry/with exit(fires on every step) and Any/Unknown remain valid. New diagnosticsE1116(invalid walker trigger) andE1117(invalid node/edge trigger) cover the gap. - Fix: type-checker validates typed-edge in
disconnect:del a ->:T:-> bpreviously accepted any expression as the typed-edgeTbecause the existingE1098check inget_type_of_binary_operationwas gated onisinstance(expr.op, uni.ConnectOp)andDisconnectOpfell through. The check is extracted into avalidate_conn_typehelper and is now invoked for bothConnectOp.conn_typeandDisconnectOp.edge_spec.filter_cond.f_type, sodel a ->:NodeA:-> banddel a <-:NodeB:<- bnow reportE1098("Connection type must be an edge instance"). Connect's assign-compr validation stays gated onConnectOpsince disconnect has no assign-compr in the grammar. - Fix: type-checker validates filter-compr compare operand types: filter comparisons inside edge-ref filters (
[a ->:E:field<x:->],del a ->:E:field<x:-> b) only validated field existence (E1033); the comparison operand types fell through with a TODO. TheFilterComprhandler now delegates each compare to theCompareExprcase viaget_type_of_expression(cmp), so each(left, op, right)triple is validated against the resolved field type and emitsE1110("Operator not supported between types") /E1111("right operand must support__contains__") for incompatible operands. Behavior matches statement-levelCompareExpr:==/!=cross-type operands stay valid (Python returnsFalse, notTypeError); only ordered comparisons<,<=,>,>=andin/not inare flagged. Closes gap 5 of #5885. - Fix: typed
[edge ...]traversals propagate element type:[edge a ->:T:-> b]now resolves tolist[T]instead of degrading tolist[<Any>], so assignments into alist[T]no longer require casts. Closes gap 6 of #5885. - Fix: type-checker promotes undefined names in OSP-shape positions to E-class: undefined identifiers in
ConnectOp.conn_type,DisconnectOp's typed-edgef_type, andEventSignature.arch_tag_info(a +>: NoSuchEdge :+> b,a del ->:NoSuchEdge:-> b,can ... with NoSuchType entry) previously emitted only the stylisticW2001"may be undefined" warning, so structurally broken programs slipped throughjac check. The static analysis pass now walks the parent chain of each unresolved name and, when it lands in one of those three positions, emits the newE2018instead ofW2001. Tuple(A, B)and unionA | Btrigger forms are validated element-wise; backtick-escaped names (e.g.`root) are skipped to preserve existing behavior. Closes gap 7 of #5885. - Fix: type-checker rejects unparameterized connect/disconnect operands:
a ++> b(and the disconnect and typed-edge formsa del --> b,a +>: T :+> b,a del ->:T:-> b) silently accepted operands typed as barelist,tuple,set, orfrozenset. The operand validator inget_type_of_binary_operationreturnedTruefor unparameterized collections because the runtime accepts any collection of nodes and the checker could not verify the element type, with a long-standing TODO to tighten this in strict mode. The validator now isolates the unparameterized case via a smallunparameterized_connect_collectionhelper and emits the newE1118before falling through to the genericE1096/E1097"must be a node instance" checks. Parameterized operands (list[Place],tuple[Place, Place]) and list literals whose element types infer to a node continue to pass;is_valid_node_operandno longer returnsTruefor unparameterized list as a recursive element, solist[list]is also rejected through the same path. Closes gap 8 of #5885. - Fix: type-checker assigns
boolresult todel(DisconnectOp) instead of Unknown:a del --> b(and its typeda del ->:T:-> band reverse-directiona del <-:T:<- bforms) used to fall throughapply_connect_or_disconnectinoperations.jacwithout setting a result type, so every disconnect site emitted a spuriousW1051"Expression type could not be resolved" and assignments likeremoved: bool = a del --> bfailed withE1001(Unknown to bool). TheDisconnectOpbranch now returns the prefetchedboolinstance, matching the runtime contract thatJac.disconnect(...)returnsbool. Closes gap 9 of #5885. - Fix: clear diagnostic for connect/disconnect with missing right operand: connect (
++>,+>:T:+>) and disconnect (del -->,del ->:T:->) operators that are missing the right operand previously surfaced a misleadingUnexpected token in expression: ';'trail (canonicala del -->;form) or aMissing ';'+Unexpected token in expression: '-->'cascade (non-canonicaldel a-->;form). Both shapes now emit a singleE0026("Connect/disconnect operator requires a right operand") with a help line pointing at the canonical syntax. Closes gap 10 of #5885. - Fix: non-UTF-8
jac.tomland source files crash with confusing tracebacks:tomllib.load()does a strict UTF-8 decode and crashed deep inside the parser when ajac.tomlcontained cp1252 or UTF-16 bytes; a newread_toml_with_encodinghelper reads via the existingread_file_with_encodingfallback chain before parsing, and several unguardedopen()/read_text()calls on user content now specifyencoding="utf-8"explicitly so they no longer mojibake under the Windows cp1252 locale default. - Fix:
jac install -e <path>now resolves the target package'sjac.tomlcorrectly when run from outside the package directory. Previously the config was discovered from the current working directory (and could be served stale from a process-level cache), causing the wrong project to be installed. - Fix: JS codegen scopes
letper sibling block in compound statements:.cl.jac-> JS codegen leaked the if-body'slet-tracked names into sibling else/elif branches (and analogously into except/finally for try statements), so the alternate branch emitted a barex = ...against an undeclared name. Strict-mode ES modules then threwReferenceError: x is not definedwhenever the alternate path ran. The pass now follows an explicit scoping model where each compound statement owns one block scope (its primary body) and alternate-branch blocks are sibling scopes that finalize the primary on entry. Closes #5930. - Fix: JS-style
new Foo(...)now emits the migration-friendly diagnostic: Afternewbecame an ambient builtin, JS-stylenew Foo(...)parsed as a barenewidentifier followed by a stray expression and surfaced only the genericE0002 Missing ';'error. The parser now detects the<value> = new <atom>and barenew <atom>;shapes and emitsE0012, whose template recommendsnew(target, ...args). Legitimate first-class references likef = new;are unaffected. - Fix: Docstring before
cl/sv/nacontext prefix: A"""docstring"""immediately before a single-statementcl,sv, ornaprefix no longer fails withE0005; the docstring attaches to the inner declaration and the prefix tags its code context as before. - Fix: Pipe-forward/backward expressions emit spurious W1051: The type checker had no rule for
|>,:>,<|, or<:and returnedUnknownTypefor every pipe expression, both producing W1051 noise on each use and silently letting argument-type bugs through. Pipe operators now type as the equivalent function call (with the argument-side tuple unpacked, matching codegen), so5 |> add_oneresolves tointand"oops" |> takes_intreports the sameE1053astakes_int("oops"). - Fix: type narrowing now reaches inside comprehensions:
T | Nonenarrowing established by an enclosing guard propagates into list/dict/set/generator comprehension iterators, conditionals, and output expressions, eliminating spuriousE1099false positives. - Fix: JSX block comments crash the parser: A
{#* ... *#}slot inside JSX previously fell into the expression branch and surfaced as a syntax error, leaving no comment-only escape hatch inside markup. The lexer now recognises{followed by#* ... *#followed by}as a dedicatedJSX_COMMENTtoken, the parser lowers it to aJsxCommentAST node that round-trips through the formatter and emits nothing in the rendered output. - Fix: Native compiler reports unsupported features instead of silently dropping them: Constructs that the
.na.jac/na {}pipeline cannot yet lower (such asmatch, C-stylefor, walrus:=,yield,await,flow/wait, walker event abilities, and nested function definitions) previously compiled clean and produced empty function bodies that returned0at runtime; they now raise a structuredE5090diagnostic at compile time. - Type checker: JIR cache load restores stub
name_specon literal AST nodes:JirReadernow fabricates the stubNamethatAstSymbolStubNode._init_stubbuilds in postinit forMultiString/FString/ListVal/SetVal/TupleVal/DictVal/{List,Dict,Set,Gen}Compr/IndexSlice/EdgeOpRef/FilterCompr/AssignCompr/JsxElement. Without it,DeclImplMatchPass._resolve_symbols_in_subtreeraisedAttributeErrorafter a cache hit whenever a literal sat at the head of an attribute chain (e.g."x".upper()), aborting type checking of the whole module. - Type checker:
@<name>.setterno longer reported as a duplicate method:ASTValidationPasspreviously emitted E0076 for the second def of any@property+@<name>.setter(or.getter/.deleter) pair. The dup-detection skip is now semantic: it collects names declared with@propertyin the class body and only excuses a method whose decorator targets one of those names. Cross-property patterns like@bar.setter def foo(wherebaris not a property) still raise whenfoocollides with anotherdef foo, and two@x.setter def xfor the same property are still flagged as duplicate accessors.
Refactors#
- Refactor: esast call-lowering helpers: Consolidates duplicated logic between
EsastGenPass.exit_func_calland_generate_sv_import_stubs. Adds_param_type_map_from_ability,_wrap_to_wire_if_boundary,_extract_return_type,_make_kwargs_spread_context,_wrap_return_with_boundary, and_try_lower_server_rpc_call. Fixes a latent bug where the sv-import wrapper generator silently dropped posonly and kwonly params from the JS function signature, the__jacCallFunctionargs object, and the__to_wireboundary check. - Refactor: unify
[package]into[project]config section: Merges the separate[package]TOML section andPackageConfiginto a unified[project]section andProjectConfig, combining runtime fields (entry_point,jac_version) and publishing fields (authors,maintainers,keywords,include, etc.) in one place. Updates alljac.tomlfiles and config parsing accordingly. - Refactor: InnerCompr becomes a CFG node (AST foundation): Adds the
UniCFGNodemixin toInnerComprso the control flow graph can model comprehension iteration and filter chains. AST-only foundation: no edges are wired yet, so CFG output and type-checking behavior are unchanged. Updatesget_head/get_tailcoalescer-stop tuples and flipsis_cfg=TrueforInnerComprin the JIR registry.
jaclang 0.14.1#
Breaking Changes#
- Breaking: Identity-based auth payload for
/user/registerand/user/login: The built-in HTTP server's auth endpoints now require{identity(ies): {type, value}, credential: {type, password}}, matching jac-scale's shape. Legacy{username, password}payloads return 400. Onlyidentity.type == "username"is supported by jaclang's SQLite user store;emailand other types are rejected with 400 until the store gains an email column. Newjaclang.runtimelib.auth_modelsexposesIdentityType,CredentialType,IdentityInput,CredentialInput,LoginRequest, andRegisterRequestfor callers that want typed validators.
New Features#
- Lint: W3038 Auto-Convert React
useStateto Jachas:jac lint --fixnow rewrites the ReactuseStatehook pattern into Jac native state declarations.[name, setName] = useState("")becomeshas name: str = "", allsetName(expr)calls throughout the function body are rewritten to directname = exprassignments, and the unuseduseStateimport is automatically removed. The type is inferred from the initial value. - Feat: Native
jac bundlepipeline withjac.toml: Replacespyproject.tomlwithjac.tomlas the project manifest for Jac packages. Addsjac bundlecommand that validates, precompiles.jac→.jirbytecode, and builds a standards-compliant Python wheel (pip install-ready) entirely in Jac - nopyproject.tomlorsetuptoolsrequired. - Add: SSE-aware
sv_service_callhookspec + streaming generator returns: The defaultsv_service_callimpl now inspects the downstream response'sContent-Type; when it'stext/event-stream, the call returns a Python generator that yields parseddata:event dicts and terminates on theevent: endframing (or raisesRuntimeErroronevent: error). Lifecycle of the underlyinghttpxconnection follows the generator (closing on exhaust, drop, or scope-exit). Pairs with a_finalize_call_responsefix inruntimelib/server: theisgeneratorcheck was onreportsrather thanresult, so explicit generator returns fromdef:pubwalkers/functions silently fell into thestr()fallback and were never streamed. Newruntimelib/sv_sse.jacexposes the consumer-side helpers. - Type Checker: W1051 Unresolved Expression Type Warning: The type checker now emits
W1051whenever an expression fails to resolve to a concrete type (falls back toUnknown). This surfaces gaps in type inference so users can add annotations or fix imports where type information is missing. Enum member names and CFG edge classification for if/elif/else branches were also tightened alongside this change. - Type Checker: Enum member declarations resolve to
EnumMemberType: Enum member declaration sites (e.g.PERSONALinenum Category { WORK, PERSONAL }) now resolve toEnumMemberTypeinstead of falling through to the enum'sClassTypeorUnknown. IDE hover showsPERSONAL: <Category.PERSONAL>and per-member annotation validation (e.g.PENDING: int = "wrong") now lives alongside enum member construction. Covers both inline and impl-based enum forms.W1051is also suppressed inside Python stub modules to avoid noise from stub-internal forward references. - Type Checker: Comparison Operator Validation (E1110/E1111): The type checker now validates that comparison operators (
<,>,<=,>=,==,!=) are supported between the operand types by checking for the corresponding magic methods (__lt__,__gt__, etc.). Membership tests (in,not in) now verify that the right operand supports__contains__. Previously, expressions like"hello" > 5or5 in 10passed silently throughjac check; they now produce clear diagnostics. Identity checks (is,is not) remain unrestricted. Chained comparisons validate each adjacent pair independently. - Type Checker: W2080 Warning for Undeclared obj Attributes: The type checker now detects annotated self-assignments in
init/postinitbodies ofobj/node/edge/walkerarchetypes that are not declared ashasfields. AW2080warning guides the user to add the properhasdeclaration. The attribute is still resolved for type checking to prevent cascadingE1030"has no attribute" errors downstream. This improves both the developer experience (actionable warning) and type checker accuracy (fewer false positives from unresolved attributes). - Feature: Path pattern support in .jacignore and
jac check --ignore: Ignore patterns containing/are now matched against the full file path (e.g.jac-client/jac_client/plugin/cli.jac), allowing specific files in specific packages to be ignored while their same-named counterparts in other packages are checked. Simple patterns (no/) retain existing behavior of matching against individual path components. - Auto-Lint W3039 (getattr-to-null-ok):
jac format --lintfixnow rewritesgetattr(x, "attr", None)call sites to the Jac-nativex?.attrsyntax. Only the safe 3-arg form with a literalNonedefault is rewritten; 2-arggetattr(which raises on miss) and calls with non-None defaults are preserved. The rule covers every expression context (assignment, return, comparison, boolean op, ternary, function argument,if-condition), backed by a new_replace_child_in_parentAST helper. Gated by the existingjac-checkworkflow. - Feat: Partial Anchor Updates: Optimizes MongoDB writes by skipping full document replacement when only archetype fields change. Implements four-layer system with dirty-field tracking, selective serialization, and smart routing to targeted
$setoperations on changed fields, while preserving full rewrites for structural changes or first inserts. - New lint: W3040 filter comparison tautology: The compiler now warns when both sides of a comparison in a filter expression resolve to the same name (e.g.,
[?:Task, to_user == to_user]), which is always true due to node field shadowing. Suggests using a different variable name for the intended enclosing-scope value. rootis a reserved keyword again: barerootis the canonical form; backtick-escape (`root) to shadow it.root()is deprecated (W0062) but keeps working - the compiler lowers it to the sameJac.root()call.- Client error stacks resolve to
.jacsource: The/cl/__error__server endpoint now source-maps both the JS stack and React's component stack onto the originating.jacfiles and line numbers, and drops non-source frames (node_modules, build artifacts) by default. Set[client_errors] verbose = trueinjac.tomlto keep them, e.g. when diagnosing a stale per-file source map. - JSX: W5015 Single-
propsComponent Definition Warning: A JSX component declared with a single parameter literally namedpropsis now flagged withW5015. The single-propsshape is passed through verbatim instead of destructured, so JSX call sites cannot be validated per prop -- the type-checker keys per-attribute checking on parameter names, so<Foo title="..." />wouldE1101against anyprops-bundle signature. The diagnostic recommends direct named parameters (e.g.def Greeting(name: str, age: int)) and reserves the bundle shape for forwarding/HOC patterns via inline# jac:ignore[W5015]suppression. Includes a tutorial rewrite oftutorials/fullstack/components.mdto lead with direct named params. - Runtime: PermissionDenied diagnostics on cross-user writes: Cross-user write attempts (edge create, field mutation, delete) used to silently no-op when the actor's root lacked sufficient access (HTTP 200 with no signal). The runtime now records a typed
PermissionDenied(operation, target id/type, target owner, required level, actual level) on a request-scopedExecutionContext.diagnosticsaccumulator and surfaces it aswarnings: [...]in the response envelope, including persist-time field-write drops. A newJacRuntime.strict_permissionsflag (driven byJAC_STRICT_PERMISSIONSenv var) escalates denials toPermissionError. The diagnostic message names the canonical fix:grant(node, level=...)at the owner side or routing the write through a node ability. Fixes #5788. - Type Checker: W1036 Bare-Generic Coverage in Function Signatures: The bare-generic warning (
W1036) now also fires on function/method return type annotations (e.g.def f -> list) and parameter type annotations (e.g.def f(x: dict)), in addition to the existing coverage onhasfields andx: T = ...assignments. Bare-generic checks now also recurse into union branches, solist | Nonewarns on the barelist. The warning is deduped across split decl/impl method definitions (only the decl side fires). - Language: Typed-Base Enum Syntax: New shorthand
enum X: T { ... }declares an enum whose members are instances ofT.enum X: intdesugars toclass X(IntEnum),enum X: strtoclass X(StrEnum), andenum X: Tfor any otherTto the Python mixin formclass X(T, Enum)so members behave asTinstances without an explicit.value. Plainenum X { ... }keeps the existingEnum-only semantics. The seed compiler, RD parser, codegen, type checker, and unparse pass are all updated end to end. As a real-world dogfood,JacSemTokenTypeinjac0core/constant.jacis refactored from the verboseclass JacSemTokenType(IntEnum) { with entry { ... } }form toenum JacSemTokenType: int { ... }. (flag XforIntFlagis deferred becauseflagis used as an identifier in 50+ sites and reserving it would be backward-incompatible.) - Lint: W3041 Stale
hasRead in Reactive Effect:jac checknow warns when a reactivehasfield is read after being assigned in the samecan with entrybody in a client-side component. The patternfield = expr; if field { ... }lowers to auseStatesetter call followed by a closure-captured read of the previous value (the textbook React stale-closure bug); the warning surfaces it at compile time so the issue is caught in the compile-loop instead of at runtime. Suggested fix: capture the value in a local first (local = expr; field = local; if local { ... }).
Bug Fixes#
- Type Checker:
isinstanceGuards with Empty Branches: Type narrowing now applies correctly when anisinstanceguard has an empty branch, e.g.if isinstance(x, T) { } else { return; }or its mirrorif isinstance(x, T) { return; } else { }. Previously, neither pattern narrowedxafter the guard, producing spurious attribute and return-type errors on otherwise correct code. - Fix: Bidirectional Edge Traversal: Resolved a bug where undirected edges (created with
<+:edge:+>or<++>) were only traversable from the source node to the target node. The runtime now correctly calculates aneffective_dirfor undirected edges, enabling bidirectional traversal regardless of the requested direction. - Type Checker: Walker
.reportsAttribute Resolution: Accessing.reportson a spawned walker no longer produces a spuriousE1030error. Walker and node archetypes now inherit from their builtin base types (Walker,Node) in the type system's MRO, so fields likereports: list[any]resolve through normal inheritance. Users can also declarehas reports: list[MyType]on a walker for compile-time type checking ofreportstatements. - Fix: Component
hasVariables Visible inimplMethod Bodies:impl app.addTodoand similar nested impls now correctly resolvehasvariables declared in the parent component function. Previously, assignments to component state created untyped local shadows instead of resolving to the declared type, causing falseUnknowntypes and downstream type errors. - Formatter: Bare GenAI Ability
byand Semicolon Preservation: The Jac formatter no longer strips thebykeyword and trailing semicolon from bare genai ability declarations (e.g.def classify(s: str) -> str by llm;). Previously, formatting such a declaration would silently dropby <model>;, producing invalid output. The fix inspects child tokens of theNamenode inDocIRGenPass.exit_nameand correctly renders the fullby <name>;fragment when present. Formatting is also idempotent. - Fix: Component
hasVariables Visible inimplMethod Bodies:impl app.addTodoand similar nested impls now correctly resolvehasvariables declared in the parent component function. Previously, assignments to component state (e.g.,todos = todos + [...]) created untyped local shadows instead of resolving to the declaredhas todos: list[dict], causing falseUnknowntypes and downstreamE1055errors. - Fix: False E1001 on Annotated Self-Assignments: Fixed a bug where annotated self-assignments like
self.x: int = valproduced falseE1001 "Cannot assign int to int"errors. The type annotation was resolving to the class type instead of the instance type, causing same-type assignments to be rejected. Real type mismatches (e.g.,self.x: int = "bad") continue to be caught correctly. - Fix: Suppress spurious W1051 warnings on member-access attribute names: The type checker no longer emits false W1051 ("Expression type could not be resolved") warnings for attribute names in member-access expressions (e.g.
stripinx.strip()). This eliminates ~68% of W1051 false positives across the codebase, particularly for builtin method calls likestr.strip(),dict.get(),list.append(), andPath.read_text(). - Fix: Suppress spurious W1051 for keyword argument names and type annotation tags: The type checker no longer emits false W1051 warnings for keyword argument names in function calls (e.g.
name=inThing(name="x")) or for names inside type annotation SubTags (e.g.: int). Combined with the previous member-access fix (#5619), this eliminates ~97% of false W1051 warnings across the codebase. - Fix: Implement optional chaining (
?.) type resolution: The type checker now properly handles the?.(null-safe) operator. When accessing an attribute via?.that doesn't exist on the base type, the checker returnsNoneTypeinstead of emitting a false E1030 ("has no attribute") error. This implements the previously-stubbed TODO for<expr>?.membertype resolution. - Fix: Prevent cascading Unknown from partial union member access: When accessing an attribute on a union type where some (but not all) members have the attribute, the type checker now returns the resolved type from matching members instead of Unknown. The E1099 diagnostic is still emitted for the missing members, but downstream expressions no longer cascade into false E1002/E1053/W1051 errors. This eliminates ~17% of errors in union-heavy code (e.g. 35 → 29 in the root causes test fixture).
- Fix: isinstance narrowing now propagates through AND conditions: When
isinstance(x, T)appears as the left operand of anandexpression, the right operand now seesxnarrowed to typeT. This enables common patterns likeif isinstance(nd, IfStmt) and nd.bodywherend.bodyrequires the narrowed type. Previously, the RHS saw the original unnarrowed type, producing false E1030 errors. - Fix: byllm Sem Strings Dropped for Subpackage Imports: MTIR entries are now stored under the module's dotted import name, so byllm resolves
.jactypes imported from any subfolder depth. Previously only top-level imports matched the compile-time file-stem key. - Fix: inherited members on classes that share a name with a member: Calls like
datetime.datetime.fromisoformat(...)no longer report a false "has no attribute" error when the child class defines a member with the same name as one of its base classes. - Fix:
super()calls in methods:super().method(...)inside an archetype method now resolves to the parent class's method with the correct return type, so chained calls likebase = super().to_dict()no longer trigger spurious type errors. - BugFix: Fixed false E1053 errors for
@propertymembers with optional return types inside truthiness guards by applying CFG-based narrowing to the property resolution path. - Fix: Concurrent Server Requests No Longer Block on Sync IO: Walker and function endpoints now run concurrently under load. Sync user code is offloaded so blocking operations (
sleep, network calls, byllm, heavy CPU) do not stall the event loop; async user code continues to run natively on the loop. Request-lifecycle persistence (user resolution, context setup, post-walker commit) is wired through an end-to-end async path so concurrent requests no longer serialize on shared setup or commit. Each request now executes inside its own forkedExecutionContextpushed into a task-local_request_context, which isolatesuser_root/entry_nodemutations so concurrent requests from different users can no longer overwrite each other's identity. Three narrower follow-ups remain tracked as separate issues: per-request SQLite connection pool (removes the stopgapUserManager._lock),SqliteMemoryUnit-of-Work write batching, andbyllmasyncainvokesurface. - BugFix: Resolved all 15 type errors in
cfg_build_passby restructuring subtype attribute access patterns for proper type narrowing. - Fix: Concurrent walker edge loss: SQLite persistence now uses
BEGIN IMMEDIATEtransactions and delta-based edge merging to prevent concurrent walkers from overwriting each other's edge changes.SqliteMemory.put()is deferred tosync()andclose()flushes pending writes. AddedNodeAnchor.edge_delta()for computing per-request edge additions/removals. - Fix: generator yield/return type-checking:
yield xandreturn xinside a generator function are now validated against the appropriate type parameter of the declared return (Generator[Y, S, R]/Iterator[Y]/Iterable[Y]/ async counterparts /AwaitableGenerator) instead of the whole return type. Resolves spuriousE1093andE1002errors on valid generator definitions. - Fix: "User profile not found" race after walker completion (SQLite):
SqliteMemory.put()is now deferred tosync()so atomicity is per-walker instead of per-anchor, eliminating the cross-anchor race reintroduced by #5644 that caused subsequent walkers to see partial state on local/SQLite dev. - Fix: Suppress spurious W1051 on intrinsic JSX tag names: The type checker no longer emits false
W1051 (Expression type could not be resolved)warnings on intrinsic HTML tag names in JSX (e.g.div,button,p). Name nodes under aJsxElementNameare resolved by theJsxElementevaluator (lowercase tags bind against_JsxIntrinsicElements, uppercase component parts resolve normally);exit_namenow skips them, matching the existing treatment ofAtomTrailer.right,KWPair.key, andSubTag. Fixes six spurious warnings per<div>...</div>-style element in examples likemini_todo/main.jac. - Fix: Resolved type errors in
jaccomplete: Shell completion script generation functions (_static_bash,_static_zsh,_static_fish) now have proper type annotations on thecommandsparameter. Previously the untypedlistcaused 17 downstream type errors on tuple destructuring loops. - Fix: JSX HTML entity decoding: HTML entities (
&,>,>,>, etc.) in JSX text content and string attribute values are now decoded at compile time, matching the JSX specification and standard implementations (React, Babel, Preact, Solid). - Fix: Infinite recursion in LiteralString→str type assignability check: Added a guard to prevent infinite recursion when both source and destination types are
LiteralStringin theassign_typedelegation path. This latent bug could surface whenClassDetailsSharedequality semantics change in future fixes. - Fix: Correct type annotations in PyastGenPass sv-to-sv stubs: Fixed
binding,boundary_types, andinfoparameters that were typed asobjector baredict, causing false type checker errors. Also typedInteropBinding.param_namesandparam_typesaslist[str]and removed an invalidget_weatherstub. - Fix: Jac
anyannotation typing: The type checker now treats Jac's lowercaseanybuiltin type annotation asAnyinstead of resolving it as Python's built-inany()function. - Type Checker: Classmethod Calls Returning
Self: Calling a classmethod with-> Selfand chaining attribute access (e.g.datetime.now(tz).isoformat()) no longer raises a spuriousE1031"Cannot access attribute X for type Self". The receiver class is now bound at member-access time, so the result resolves correctly even when the bound method is captured into a variable. - Fix: Type narrowing through parenthesized OR in
andconditions:if x and (f(x) or g(x))now correctly narrowsxinside the OR's operands. Previously the wrapping CFG node around the nested boolean expression appeared as a phantom predecessor and forced narrowing to widen back to the declared type, producing spuriousE1053errors forT | Noneand similar union types. - Fix: decorated class inside a function body now works correctly: A class with a decorator (e.g.
@dataclass) declared inside a function body was not recognised as a class by the type checker. Calling it returnedNoneTypeand accessing any field gave attribute errors. This is now fixed. - Native: Garbage Collector Bug Fixes: Fixed multiple bugs in the
jac-nativeruntime garbage collector. - Parser: friendlier error for JavaScript
=>arrow syntax: Writinge => fn(e)in expression position (JSX slots, assignments, f-strings) now produces a singleE0025diagnostic with alambdahint instead of a cascade of unrelated parse errors. - Fix: Go-to-definition for bare
anykeyword: Bareany(lowercase Jac keyword) in type annotations now resolves totyping.Anyfor LSP go-to-definition, jumping to the class definition in typing.pyi. Previously the symbol was not attached and the LSP returned no location. - Fix: Stop leaking typing-internal names as ambient builtins:
jac_builtins.pyinow uses__all__to declare its public surface. The TypeEvaluator's merge loop honors it, soAny,Callable, andProtocol(imported only for use in this stub's own annotations) are no longer silently re-exported as ambient names. BareAny/Callable/Protocolin user code withoutimport from typing { ... }now correctly emit W2001 ("may be undefined"), matching every other typing-module name. - Fix:
jac formatwhitespace bugs in lambda and if inline blocks: Fixed three formatting issues whereifblocks inside multi-statement lambda bodies stayed inline instead of expanding to multi-line, inline single-statement lambda bodies were missing a space before}, and the flat-capableifpath produced a double space before}. The formatter is now idempotent for these cases. - Type Checker: Enum Iteration Yields Enum-Instance Type: Iterating an enum class in a
forloop or comprehension now resolves the iteration variable to an instance of that enum, so.nameand.valueaccess type-check correctly. Previously the variable fell through toUnknown(Python'sEnumMeta.__iter__is on the metaclass, not in the enum's MRO), or forStrEnuminherited a misleading__iter__fromstrand yieldedLiteralString, producing cascading falseE1032/W1051diagnostics on patterns like[i.name.lower() for i in JacSemTokenType]. - Fix: Type narrowing inside while loop bodies: Resolved a false-positive type error where a variable guarded by
if x is not Noneinside a while loop was still treated asT | Nonein the loop body. - Fix: JSX preserves spaces between adjacent expression children:
<div>{a} {b}</div>now renders with a literal space between values instead of dropping the separator (e.g."3 tasks remaining"instead of"3tasks remaining"). - Fix: Typed-edge ref
[expr->:Type:cond:->]parses after a starting expression:[root()->:Scheduled:priority>=3:->]and similar typed-edge filter chains now parse the same way the bare-start form already did. - Fix: Plugin-load failures now surface as warnings: When an installed jac plugin fails to load (most often because one of its own runtime dependencies isn't installed),
jaclangnow prints a warning to stderr naming the plugin and the underlying error, instead of silently falling back to a degraded feature set. For example, a missingsqlalchemyinstall used to makejac-scale's enhanced API server (with/docsand/graphroutes) silently disappear; now the user sees the cause and what to do about it. - Fix: Typed-edge filter predicates were silently dropped by the topology-index fast path: Queries like
[root()->:Scheduled:priority>=3:->]previously returned every Scheduled-connected node when ajac.tomlwas present (which makes the topology index default to enabled), becauseplan_queryextracted only the type name from the filter lambda and discarded the predicate. The planner now bails out and lets the linear path evaluate the lambda whenever a destination's edge filter has compares -- or a non-final hop's node filter has compares -- so the result matches the non-indexed semantics. Type-only filters and final-hop node predicates still go through the index unchanged. - Fix: Spurious W1051/W2001 warnings on
sem Foo.fieldtargets: Dotted-path targets insemdeclarations no longer produceW1051 "Expression type could not be resolved"orW2001 "may be undefined"warnings on their path segments. - Fix: Remove duplicate
Transform.emitand_is_suppressedimpl blocks: Both methods were duplicated verbatim intransform.impl.jac, causing type checker errors. The redundant second copy has been removed. - Fix: Functions with fewer parameters now satisfy callables expecting more: Assigning a zero-arg handler to a JSX prop like
onClick={handler}(or anyCallable[[MouseEvent], None]slot) is accepted, matching TypeScript's contravariant-arity rule. - Fix:
Callable[..., T]accepted as a|union arm: TypeAlias unions ending inCallable[..., Any](e.g. typeshed'sinspect._SourceObjectType) no longer collapse toUnionType | type[Any]and reject every caller with E1053. - Fix:
Never/NoReturnresolved to NeverType and accepted as union arms: Annotations usingNeverorNoReturn(alone or as a|arm such asint | Never) no longer fall back to_SpecialFormand collapse the surrounding union;Never-returning calls are also assignable wherever a value is expected (PEP 484 bottom type). - Fix:
is notand f-string format specs in JS codegen:is not Nonenow compiles to!== nullinstead of being silently inverted to===, and f-string format specs likef"{x:.2f}"are honored via the_jac.builtin.formatruntime helper instead of being dropped.
Refactors#
- Refactor: Sqlite memory type narrowing: Tightened internal type annotations in
runtimelib/impl/memory.impl.jac. No runtime behavior change. - Console Proxy Typing: Annotated the lazy
consoleglobal injaclang.cli.consoleasJacConsole(with acastover the_ConsoleProxyinstance) so static type checkers and IDEs resolveconsole.print(...),console.error(...),console.spinner(...), etc. against the real interface instead of theAnyreturned by_ConsoleProxy.__getattr__. No runtime change.
Documentation#
- Standardized
anyType Documentation: Replaced all instances of capitalizedAnywith lowercaseanyacross the documentation for consistency with the built-in type. anyBuilt-in Function Convention: Standardized the use of the backtick prefix (`any) for the built-in function to distinguish it from theanytype in documentation.- Redundant Import Cleanup: Removed redundant
import from typing { Any }statements across the documentation. - Strict Data Model Enforcement: Jac now enforces a strictly declarative data model for all objects (
obj,node,edge,walker). All instance attributes must be declared using thehaskeyword. - Dynamic Assignment Warned: Assigning attributes to an instance that were not declared in its
hasblock is now explicitly documented as an anti-pattern. - Future Compliance: Future versions of the Jac compiler will strictly forbid dynamic attribute assignment to ensure portability across server, client, and native codespaces.
- Best Practice: Use
by postinitfor attributes that require initialization logic beyond simple defaults.
jaclang 0.14.0#
- Fix: byllm Provider Config Ignored from
jac.toml: The byllm plugin now correctly reads the provider and model from[plugins.byllm.model]injac.toml. Previously,PluginConfigBaseresolvedproject_dirviacwd, causing the config lookup to miss the project'sjac.tomland fall back to the OpenAI default.PluginConfigBasenow derives the project directory fromJacRuntime.full_target_path(set byjac runbefore compilation). - 1 internal refactor.
- Feat:
to cl:/to sv:/to na:Section Headers: Module-level section headers set the default client/server/native context for every following statement until the next header; the legacycl { ... }/sv { ... }/na { ... }braced blocks now emit a deprecation warning. - Format:
to cl:/to sv:/to na:Section Headers:jac formatnow emits section headers on their own line with a blank-line separator and the body dedented to module scope, and the parser models them as implicitClientBlock/ServerBlock/NativeBlockso existing codegen and analysis passes work on sections unchanged. - Type Checker: Unannotated Variables Widen on Reassignment: The classic
x = None; ...; x = value;sentinel pattern no longer errors withE1001: Cannot assign T to NoneType. Reassigning any unannotated variable now widens its inferred type, matching Pyright. Variables with an explicit annotation (x: T = ...) still enforce the declared type. - Fix: W3025 Identical-Branch Lint False Positives:
jac format --lintfixno longer flags branches that only look alike at the statement-type level (e.g. both branches assign to a subscript target or both call the same function with different kwargs); identical-branch detection now compares canonical unparsed source rather than a shallow type-name key. - Type Checker: Property-Aware Assignment Checking: Assigning to a
@cached_propertyattribute (e.g.,self.archetype.__jac__ = self) no longer produces a falseE1001against the raw function type. The type checker now delegates toget_property_write_typein the evaluator, which classifies the target in a single decorator walk:@cached_propertyassignments type-check against the getter's return type (non-data descriptor semantics),@propertywith a@name.setteruses the setter's value-parameter type, and@propertywithout a setter emits a newE1005"Cannot assign to read-only property" diagnostic instead of the misleading "Cannot assign T to \<function>" message. Setter detection walksnames_in_scope_overloadin the enclosing class scope to find sibling@name.settermethods. - Type Checker: Solve
Selffrom Call Arguments: Methods returningSelfnow have their return type specialized at the call site by inferringSelffrom the call's first argument. Fixesunloaded = object.__new__(self.__class__)(and similar patterns) whereunloadedwas previously typed as the unboundSelfTypeVar, causing spurious E1031 errors on subsequent member access. The fix synthesizes the implicitcls: type[Self]/self: Selfannotations that typeshed routinely omits, runs TypeVar binding for everyFunctionTypecall (not only those with explicittype_params), and distinguishes constructor calls (ClassName(args), whereclsis implicit) from direct staticmethod-style__new__calls (whereclsis explicit). Also fixes a related latent bug where unbound method calls (Class.method(instance, ...)) misaligned positional arguments against generic parameters in_collect_type_var_bindings. - Feat:
jac ejectCommand: Newjac ejectcompiles a Jac project to a self-contained output folder containing only.pyand.jsfiles (zero.jacfiles). Server-side.sv.jacbecomes plain Python viagen.py, client-side.cl.jacbecomes plain JavaScript viagen.js, impl files merge automatically, and the generatedbackend/serve.pyboots the existingJacAPIServerrequest handler without invoking the Jac compiler at runtime. Also emitsrequirements.txt,package.json,vite.config.js(with backend proxy),index.html, and arun.shthat starts both processes. Ejected backends still depend onpip install jaclangfor the runtime helpers (walker dispatch, auth, persistence). - Refactor: Public Helpers for
jac ejectPlugin Authors:jaclang.cli.commands.impl.ejectnow exportsresolve_eject_output(src, output)andload_eject_project_metadata(src)as public helpers. Plugin pre/post hooks that extendjac eject(e.g., a hypotheticaljac eject --scale) can call these to stay in sync with the default command's output-directory fallback andjac.tomlparsing logic instead of duplicating it. - Fix:
jac ejectno longer chokes on the.jac/build cache:_collect_jac_filesusedPath.rglob("*.jac"), which matches both files and directories whose name ends in.jac. Any project containing the.jac/build cache (i.e. any project that had been built before ejecting) was passing the cache directory itself toJacProgram.compile, failing with[Errno 21] Is a directory: '<src>/.jac'and aborting the eject. Non-files are now skipped at the top of the loop so eject is idempotent regardless of build state. - Feat:
jac ejectRuns Full-Stack Apps End-to-End:jac ejectnow produces more robust output that runs a complete fullstack Jac app (validated on sophisticated backend + SPA + streaming RAG chat) from./run.shalone. - Feat: SV-to-SV Eager Auto-Spawn:
jac start consumer.jacnow brings up everysv import-ed provider (including transitive ones via BFS) at consumer startup, before the first request arrives; the env var path stays for cross-host providers. - Feat: SV-to-SV Microservice Interop:
sv importbetween two server modules now generates Python HTTP client stubs that route calls throughjaclang.runtimelib.sv_clientinstead of loading the provider in-process. - Fix: ES Codegen Method Dispatch on
.cl.jacFiles: Operator-side primitive dispatch now walks the receiver's MRO, so"a" in name.lower()lowers to.includes()instead of a runtime-crashing JSin. When the receiver's static type isn't a JS-native primitive, Python method idioms (lower,upper,strip,lstrip,rstrip,append,extend,pop) now route through_jac.poly.*runtime helpers thattypeof-dispatch to the native JS operation, instead of leaking Python identifiers into the JS output. - Fix: Console BrokenPipeError in Sidecar Mode: Console
print()andflush()now catchBrokenPipeError/OSError, preventing crashes when stdout is closed (e.g., Tauri sidecar after port discovery). - Fix: Scheduler Null Safety:
stop()andwait()now guard againstNoneon_stop_event,_done_event, and_thread, preventingAttributeErrorduring early shutdown. - Fix:
JAC_DATA_PATHEnvironment Variable for Read-Only Deployments:UserManagerandget_db_path()now honor theJAC_DATA_PATHenv var, redirecting writable runtime data (database,.jac/data/) to a specified path. Enables deployments where the base path is read-only (e.g., AppImage). - Cleanup: Removed Unused Client Error Handler: Removed
_handle_client_error()and_client_error_loggerfrom the server runtime. - Fix: UTF-8 Encoding for Windows: Added explicit
encoding="utf-8"toopen()calls in compiler passes and CLI commands. Preventscharmapcodec errors on Windows where the default encoding iscp1252. - Type Checker: Improved Narrowing for AND/OR and Ternary Expressions: Type narrowing now works correctly in nested ternary expressions, AND/OR chains, and
isinstanceon unknown-typed variables. - Native: Bug Fixes and Stability Improvements: Fixed several issues in the
jac-nativecompilation pipeline, including silent failures when type-checker errors occur during.na.jaccompilation, incorrect ordering of default/non-defaulthasattributes in native structs, and transitive C-library import resolution for imported.na.jacmodules. - Native:
jac-gdbDebugger Support: Addedjac-gdb, a GDB-based debugger integration for native Jac programs. The LLVM module ID is now set from the source file name so GDB can locate source files, and optional DWARF debug metadata (function locations, compile-unit info) is emitted whenJAC_NATIVE_DEBUG=1is set. - Fix: ES Codegen
newExpression forAny-Typed Callees: Calling a variable typed asAnyno longer emitsnew handler(payload)in the generated JavaScript. - Type Checker: Expression-Level CFG Narrowing: Type narrowing in
and/orchains and ternary expressions now uses the same CFG backward walk asif/assertnarrowing. Fixes edge cases in nested ternaries and compound boolean guards. - Type Checker: Fix
object.__new__()Direct Calls:object.__new__(cls)no longer raises a false E1051 "Too many positional arguments" error.__new__is now correctly modeled as a staticmethod (matching CPython) instead of a classmethod, so itsclsparameter is not auto-stripped when called directly. Constructor resolution (MyClass(...)) is unaffected;_validate_constructor_methodnow explicitly strips self/cls before argument matching rather than relying on the implicit classmethod stripping. - Serializer:
ref_modefor Shared and Circular Object Graphs:Serializer.serialize(..., ref_mode=True)now emits{"$ref": "<uuid-hex>"}for any Jac object seen more than once, instead of inlining it repeatedly. This prevents both data bloat from shared references and infinite recursion on circular graphs (e.g.alice.friend = bob; bob.friend = alice). The first occurrence is always fully inlined; subsequent occurrences become a compact$ref._deserialize_valueautomatically resolves$refdicts back to live archetypes (via context memory) or lazyNodeAnchorstubs.jid()on plainobjarchetypes now also registers them into context memory so$refround-trips work without graph traversal. - Persistence: Schema Drift Just Works: Editing a
node/obj's fields no longer forces wiping.jac/data/.SqliteMemorynow stores anchors as JSON, stamps each row with a per-archetype fingerprint, tolerates added/removed fields, coerces primitive type changes (str <-> int/float/bool, ISO strings ->datetime/date/time,str -> UUID,list <-> tuple,Enum,Optional), and quarantines truly unloadable rows in a newanchors_quarantinetable instead of deleting them. Class renames are handled by the@archetype_alias("old.module.OldName")decorator. Legacy pickle DBs auto-migrate on first open. See Persistence & Schema Migration. - CLI:
jac dbOperator Toolkit: Newjac dbcommand group:inspect,quarantine list/show,alias list/add/remove(DB-resident rescue aliases, no code deploy needed), andrecover/recover-all(with live class re-stamping). Backend-agnostic: thePersistentMemoryinterface grew eight new abstract methods thatjac dbdispatches through, soSqliteMemory(default) and any plugin-provided backend (e.g. jac-scale'sMongoBackend) work through the same commands. See CLI -> Database Operations. - Fix: Python-Compatible Scoping for Assignments in Non-Scoping Blocks: Unannotated assignments inside
try/except/finally,if/else,for/while, andmatchblocks now bind in the nearest enclosing Python scope instead of creating a shadow symbol in the block's faux scope. This eliminates spuriousW2003"defined but never used" warnings when the same name is assigned across sibling branches (e.g.,categoryintry { ... } except { ... }), and also fully resolves the former "string slice loses type" type-checker root cause:while chomp.startswith('.') { chomp = chomp[1:]; ... }no longer degradesstrtoUnknownbecause the re-assignment updates the single outer symbol instead of forking a branch-local one. Mirrors the existing walrus (:=) handling and matches Python semantics exactly. - Fix:
by postinitSymbol Resolution: Fields declared withby postinitno longer show a false W2001 ("'postinit' may be undefined") warning and go-to-definition now works correctly on them. - Fix:: jac0 bootstrapper no longer emits orphan or duplicate impl methods in generated Python output.
- Fix: Reject impl for methods that already have a body (E2016/E2017):
decl_impl_match_passnow checksneeds_implbefore overwriting a declaration's body. Providing an impl for a method with an existing body raises E2016. Providing a duplicate impl for a stub that already has one raises E2017. - Fix: Symbol Resolution for Standalone Constructs in
.impl.jacFiles:DeclImplMatchPassnow resolves Load-context names across the full subtree of every annex module, not justTypeAlias/GlobalVars/ModuleCodeitems. Standalonedef/obj/node/edge/walker/enum/classconstructs defined directly in a.impl.jacfile (with no matching decl in the primary) had their body reads left with.sym = None, which (a) suppressed type inference on every local (e.g.system = platform.system();showed as untyped, even thoughplatform.system() -> stris correctly stubbed) and (b) emitted spuriousW2001: Name 'x' may be undefinedwarnings for every downstream use of a local. The hand-picked container whitelist is gone; resolution is now uniform across primary, impl, and variant modules. - Fix: Remove Unsound TYPE_CHECKING Auto-Demotion:
PyastGenPassno longer rewrites annotation-only imports intoif typing.TYPE_CHECKING:blocks. Dataclass, Pydantic, attrs, and anything else that resolves string annotations at runtime needs names likeClassVar/Protocol/TypedDictimportable, and the classifier couldn't see those uses (immediate trigger:JacTestCheck.test_suite_pathcrashing dataclass withmutable default <class 'dict'>). Explicitwith entry { if TYPE_CHECKING { ... } }blocks in Jac source continue to work and remain the right way to break import cycles. - Feat:
{**expr}Dict-Spread in JSX Attributes: JSX spread attributes now support the Pythonic{**props}syntax alongside the existing{...props}form.{**props}is the preferred Jac idiom; using{...props}emits a W0063 style warning. - Fix: Type Narrowing Through BoolExpr Inside isinstance Guards:
isinstancenarrowing now works correctly when the guarded block containsand/orexpressions. Previously, the short-circuitCfgExprsub-graph created for a standaloneBoolExprwas disconnected from the enclosing CFG, so the backward narrowing walk could not reach theisinstancecondition. The CFG build pass now links standaloneBoolExproperand chains to their enclosing flow node (matching the existing ternary handling), and the narrowing walker treatsCfgExprpredecessors as transparent when they don't mention the target symbol, instead of prematurely marking the path as unnarrowed. - Fix: Assignment Is a Narrowing Barrier: Re-assigning a variable inside a narrowed scope (e.g. inside a
casebody) now correctly resets its type for every access that follows. - Fix: Narrowing Through Parenthesized
and/orConditions:if (a and b):now narrows types the same way asif a and b:. - Fix: Tuple-Unpacking Assignment Is a Narrowing Barrier:
(x, _) = f()now resets type narrowing on the reassigned names, just like a plain assignment does. - Fix: Starred-Unpack Assignment Is a Narrowing Barrier:
(head, *rest) = f()now resets type narrowing on the starred name too. Closes a leak where prior narrowing onrestcould survive past the unpack. - Fix: Ternary Narrowing Through
and/orConditions:x if a and b else ynow narrows its branches the same way as the equivalentif a and b:statement. - Fix: Pre-Statement Narrowing Reaches Inside Ternary Expressions: A ternary's branches now see the narrowing established by earlier statements (e.g.
if x is None: return), so access on the narrowed type works the same insidex.m() if cond else x.m()as it does in a plainif/else. - Fix: Destructuring Assignment Narrows by RHS Element Type:
(x, _) = (5, "ok")now narrowsx: int | Nonetointby extracting the element type at position 0 from the concrete tuple RHS, matching pyright. Works for tuple literals,-> tuple[T1, T2]function return types, and nested destructuring like((a, b), c) = .... Falls back to the previous conservative reset when the RHS isn't a concrete tuple or the extracted element type doesn't strictly narrow the declared type. - Fix: Hover on LHS Assignment Shows Post-Write Type: Hover on an assignment target (flat or destructuring) now reflects the post-write effective type instead of the pre-write declared type, matching pyright/pylance.
- Fix: NamedTuple Destructure Narrowing: Destructuring a
NamedTuple((x, _) = point) now narrows each target to its field type, matching the behavior already available for plain tuple literals. - Removed: W2052 Broad Exception Warning: Removed the
W2052warning that flaggedexcept Exceptionas overly broad. CatchingExceptionis a legitimate and common pattern at system boundaries (e.g., LLM calls, network I/O), and the warning produced false positives in these cases. - Type Checker: JSX Reserved Props and Intrinsic Tag Resolution:
keyandrefare now recognized as framework-reserved JSX attributes and no longer produce false E1101 errors on user-defined components. Intrinsic HTML tag names (div,button, etc.) are resolved against_JsxIntrinsicElementsfrom the type stubs, eliminating false W2001 "may be undefined" warnings and enabling IDE hover/goto-definition on tag names; unrecognized intrinsic tags emit a dedicated W1050 warning. - Type Checker: JSX Prop Validation and Event Handler Types: JSX attributes are now type-checked against the component's function signature (
<Button onClick={42}/>errors) and intrinsic HTML element prop schemas (<button onClick={42}/>errors). Includes bidirectional lambda inference so<Button onClick={lambda e { e.clientX; }}/>inferseasMouseEvent, spread-prop validation rejecting non-dict spreads, ambient event handler aliases (MouseEventHandler,KeyboardEventHandler, etc.), and intrinsic prop classes for common HTML elements. - Fix:
Callable,Any,ProtocolResolve in User Code: Fixed a bug whereCallable[[X], Y]annotations in function parameters resolved toUnknown. The builtins merge now correctly overrides typeshed-internal imports with the jac_builtins.pyi re-exports, so ambient typing names pass thelookup_symtabimport filter. - Fix: Formatter JSX/Lambda Inline + CLI Bracket Stripping:
jac format -sno longer strips bracketed Jac syntax like[root()-->]ortodos + [t]under the Rich-backed console (missingmarkup=False, highlight=Falseonconsole.print). JSX elements with only text/expression children (<button>Add</button>) and single-statementifbodies inside lambdas (lambda e: T { if cond { stmt; } }) now stay flat when they fit the print width instead of force-breaking; statement-levelifstill always breaks. - Fix: Formatter Width Accounting and Multi-Statement Lambdas: Deeply-nested JSX opening tags could render past the 88-column limit (e.g.
<p style={{...}}>two levels inside a<div>rendering at 90 columns) because print-width accounting drifted by one column per level of nesting. Lines now respect the width budget at any nesting depth. Multi-statement lambda bodies (lambda -> T { stmt1; stmt2; stmt3; }) also now render each statement on its own indented line instead of collapsing them onto a single logical line with no separators. - CLI:
jac jac2js(andjac jsdeprecated): Addedjac jac2jsas the canonical Jac→JavaScript transform command, mirroringjac jac2py.jac jsstill works but now emits a deprecation warning on stderr and forwards tojac jac2js; it will be removed in a future release. Also fixed a pre-existing bug whereJacSuperConsole.warningwrote to stdout instead of stderr. - Type Checker: Block-Body Lambda Return Type Inference: Block-body lambdas (
lambda e: T { stmts; }) now infer their return type fromreturnstatements instead of defaulting toUnknown. Lambdas with no return statement correctly resolve toNoneType, fixing falseE1103errors on JSX event handlers likeonClick={lambda e: MouseEvent { doStuff(); }}. Mixed-return paths (some branches return a value, others fall through) produce a union that includesNoneType. - Type Checker: Relative
.pyiRe-Exports and ParamSpec Parity: Three related improvements that jointly fix cases likecurrent_path.split(os.pathsep)whereos.pathsepcollapsed to<Unknown>and surfaced as a misleadingE1054 "No matching overload found". (1) The module resolver's_candidate_fromnow probes.pyi(and__init__.pyi) alongside.py/.jac, so relative imports inside typeshed packages (e.g.from .path import (pathsep as pathsep, ...)inos/__init__.pyi) resolve to the actual sibling stub instead of returning an unextended path that failsexists(). (2)is_typevar_class/is_paramspec_class/is_type_var_tuple_classnow fall back to a canonical-name match gated to stdlib stubs (is_from_stdlib_stub), sotyping.ParamSpecandtyping_extensions.ParamSpec-- distinct classes in typeshed -- both count as ParamSpec and_P = ParamSpec("_P")from either module produces a properTypeVarType(is_param_spec=True). (3) Attribute access.args/.kwargson a ParamSpecTypeVarTypereturnsAnyType(gradual), which is the minimum needed to type-check signatures likedef submit(self, fn: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]without spuriousE1031; fullParamSpecArgs/ParamSpecKwargsmodelling is left as future work. - Type Checker:
@jac/runtimeVirtual Module Resolution:import from "@jac/runtime" { ... }no longer collapses to<Unknown>during static analysis. The@jac/*alias was previously only resolved by an AST-node method consulted at bundle time, so the type evaluator'sresolve_relative_path_list(which routes through the module-levelresolve_module) emittedW1100 "Module not found"and every imported symbol (e.g.jacIsLoggedIn -> bool) decayed to Unknown, producing spuriousE1001 "Cannot assign <Unknown> to bool"errors on perfectly correct client code.@jac/*resolution is now centralized inmodresolver.jacand consumed uniformly by the resolver, the type checker, and the ECMAScript bundler, so static analysis agrees with the runtime. - Type Checker: Relative Import Resolution and Unresolved-Name Diagnostics: Fixed relative imports like
import from .components.TodoItem { TodoItem }silently decaying to<Unknown>. The path-offset correction inget_type_of_modulenow applies to Jac namespace packages (not just Python__init__.pyparents), the package-dir fallback recognizes.cl.jac/.sv.jac/.na.jac, and addedW1101 "Cannot import name '{name}' from module '{module}'"so unresolved symbols surface instead of silently flowing into JSX prop validation as Unknown. - LSP: Goto-Definition on JSX Component Tags: Clicking a
<MyButton>tag in a.cl.jacfile now jumps to the component's function declaration. - 2 small refactors/changes.
jaclang 0.13.5#
- Native: Lambda Expressions and Capturing Closures: Added lambda expression support in the
na(native LLVM) codespace. Simple lambdas compile to anonymous LLVM IR functions returned as function pointers. Capturing closures -- lambdas that reference variables from the enclosing scope -- pass captured values as hidden extra parameters, with automatic injection at call sites. No heap allocation required for captures. Leverages the existing indirect function pointer call infrastructure.
jaclang 0.13.4#
- Native: Lambda Expressions: Added lambda expression support in the
na(native LLVM) codespace. Lambdas compile to anonymous LLVM IR functions returned as function pointers. Leverages the existing indirect function pointer call infrastructure for invocation. - Type Checker: Expression-Based CFG Narrowing for Dotted Paths: The type checker now narrows dotted attribute expressions (e.g.,
obj.field) through the CFG backward walk, not just simple variable names. Patterns likeif obj.speed is None { obj.speed = 0; } return obj.speed;now correctly resolveobj.speedtointinstead ofint | None. IntroducedNarrowingTargetabstraction to generalize the CFG narrowing pipeline for both symbols and expression keys, extendedaffected_symbolsto track dotted paths, and removed scope-based narrowing for dotted paths in favor of the more precise CFG analysis that accounts for assignments inside branches. - Refactor: Remove Scope-Based Narrowing for If/Match (CFG Handles It): Removed the redundant scope-based
push/pop_narrowing_scopefrom if-statement and match-case handlers. The CFG backward walk now fully handles these via_find_predicate_foron IfStmt/MatchStmt predecessors. Also removedpromote_inverted_narrowingsand_if_body_always_terminates, as the CFG join logic naturally handles early-exit guard patterns. This fixes a false positive whereAny | strwas being mistyped asintby the old scope narrowing, and correctly reports previously-suppressed errors like attribute access on base types after isinstance guard returns. - Refactor: Move AND/OR and Ternary Narrowing from Pass to Evaluator: Progressive narrowing for AND/OR expressions (
x is not None and x.bark()) and ternary branch narrowing (x if isinstance(x, T) else y) have been moved fromTypeCheckPassenter/exit handlers into the type evaluator's expression evaluation. The pass now delegates viaprune()+get_type_of_expression(), keeping all narrowing logic in one place. The scope stack is still used internally by the evaluator for these intra-expression narrowing cases, since the statement-level CFG cannot see between sub-expression operands. - Type Checker: Ambient DOM Types for JSX Event Handlers: Added
dom_types.pyiwith 39 ambient types covering DOM elements (HTMLElement,HTMLInputElement,HTMLFormElement, etc.) and events (Event,ChangeEvent,KeyboardEvent,MouseEvent,FocusEvent,DragEvent, etc.). These are available without import in all Jac modules, replacing thee: anyworkaround in JSX event handler lambdas with proper typed alternatives likee: ChangeEventande: KeyboardEvent. - Fix: Filter Comprehension Type Inference:
[root-->][?:Todo]and[-->[?:Todo]]now correctly infer aslist[Todo]instead ofUnknown. Previously, theFilterComprhandler in the type evaluator was missing a return statement, andAtomTrailerhad no branch for filter comprehension trailers, causing false E1002 return-type errors and cascading E1032 attribute-access errors on the filtered results. - Type Checker: Centralized Ambient Builtins via
jac_builtins.pyi: All user-facing ambient names (llm,jid,jobj,printgraph,grant,revoke,allroots,save,commit,store,destroy, permission constants,restspec,schedule,ScheduleTrigger,APIProtocol) are now declared injac_builtins.pyias the single source of truth. The type checker resolves these through the builtins scope chain, eliminating false "undefined name" warnings (e.g.,llminby llm()expressions). Removed the redundantTYPE_CHECKINGvariable declarations frombuiltin.jac. - Feat: Topology Index Persists Across Sessions: The topology index (
topology_index_dataon rootNodeAnchor) now survives Serializer round-trips via base64 encoding, enabling DB-backed graphs (MongoDB, SQLite) to retain the adjacency index across sessions instead of rebuilding from scratch. The index is excluded from_compute_hashdirty-checking since it's a derived cache, and serialization is scoped to root anchors only to avoid overhead on regular nodes. - Type Checker: Bare Generic Iteration Defaults to
Any: Iterating over bare generic types likelist,dict, orset(without type arguments) no longer produces falseE1032"Type is Unknown" errors on attribute access. Bare generics are now treated aslist[Any],dict[Any, Any], etc. per PEP 484, so iteration variables resolve toAnyinstead ofUnknown. A newW1036warning is emitted to nudge users toward adding explicit type arguments (e.g.,list[Todo]instead of barelist). - Lint: W3037 Auto-Fix for Unnecessary
-> None:jac lint --fixnow auto-removes redundant-> Nonereturn type annotations from functions and lambdas that have no return statement. Handles both regular abilities (def add -> None { ... }→def add { ... }) and paren-less lambdas (lambda e: ChangeEvent -> None { ... }→lambda e: ChangeEvent { ... }). - Fix: False W2003 Warning on
by llm()Parameters: Parameters in GenAI abilities (def foo(x: str) -> T by llm()) no longer trigger spurious "defined but never used" warnings. The static analysis pass now recognizesis_genai_abilityalongsideneeds_implwhen skipping parameter usage checks. - Lint: Lint Warnings in Compile/LSP Pipeline and
-> NoneCheck: The linter now runs as part of the standard compile and LSP pipeline via a newJacLintCheckPass(report-only mode), so lint warnings like unnecessary pass, mutable defaults, and repeated conditions appear as editor warnings without modifying the AST. AddedW3037(unnecessary-none-return): warns when a function or lambda has an explicit-> Nonereturn type annotation but noreturnstatement in its body, since functions implicitly returnNone. Thejac lintreport also now displays warnings alongside errors. - Fix: False W2003 Warning on
cl defHas Variables: Assignments tohas-declared variables inside nested abilities of acl def(e.g.,can with entry { items = get_items(); }) no longer trigger spurious "defined but never used" warnings. The symbol table build pass now recognizes these as state mutations of the enclosing component's reactivehasvariables rather than new local variable definitions. Bare assignments tohasnames insideobjmethods still correctly warn (useself.xinstead). - Type Checker:
callable()Type Narrowing with Unified Condition Dispatch:callable(x)now narrows union types in both true and false branches -- e.g.,Callable[[int], str] | strnarrows toCallable[[int], str]insideif callable(x)and tostrin the else branch. Works withnot callable(), early returns, explicit else, and multi-type unions includingNone. The narrowing system was also refactored into a unified condition dispatch: a singleclassify_conditionclassifier andNarrowingKindenum replace the duplicated if/elif chains that previously existed across 5 methods (process_condition,process_negated_condition,_find_predicate_for,collect_narrowing_symbols, plus leaf extractors). Adding a new narrowing kind now requires ~25 lines across 4 touch-points instead of copy-pasting AST matching across 5 scattered methods. The coreexclude_type_from_unionand_type_matches_isinstanceutilities were also generalized to handle non-ClassType members (e.g.,FunctionType) via identity comparison, so all narrowing kinds flow through the standard_apply_narrowingpath without special flags. - Type Checker: TypeAlias Resolution for Type Narrowing:
TypeAlias-annotated definitions (glob MyType: TypeAlias = Foo | None) are now resolved to their underlying types, enablingisinstance(),callable(), andNone-check narrowing to work through type aliases. Detection uses symbol-based verification (_SpecialFormshared instance + name) rather than string matching. Expression RHS is evaluated directly; string RHS (forward references like'Callable[[A], B] | C') is parsed via Python'sastmodule and recursively resolved against the Jac symbol table. Stub module (.pyi) TypeAlias evaluation suppresses diagnostics since stubs may reference types the Jac resolver cannot fully handle. Previously, parameters typed with aTypeAliasreceived the_SpecialFormclass type instead of the aliased union, making all narrowing produce<Unknown>. - 1 small refactor/change.
- Cleanup: Remove Outdated
__specs__from littleX Examples: Removed deprecatedobj __specs__ { static has auth: bool = False; }blocks fromload_user_profileswalker in bothlittleX.jacandlittleX_single.jac. - ES Codegen:
jid()Moved to Client Runtime:jid()is now a proper runtime function (_jac.builtin.jid()) instead of an inline property access (x._jac_id). This provides clear, actionable error messages when called onnull(e.g. server returned an error) or non-node objects, with stack traces pointing to the.jacsource line. Theassert_no_jac_keywordstest was also improved to strip string literals before scanning, preventing false positives from English words in error messages.
jaclang 0.13.3#
- ES Codegen:
jid()Builtin for Unified Node Identity: Addedjid()as a builtin function in the ES codegen, providing a consistent API for accessing node identity across both server and client code. On the client side,jid(node)emitsnode._jac_idin the generated JavaScript. The previous implicit.idalias (this.id = this._jac_id) on generated node class constructors has been removed in favor of the explicitjid()call. - Fix: Windows Client Bundle Compilation: Fixed client bundle compilation failing on Windows with
Client function 'app' not founderror. Added cross-platform path normalization for module hub lookups to handle Windows case-insensitivity and path separator differences. Extracted helper functions to the client bundle module for consistent path handling across the bundle builder and module introspector. The ES pass is now explicitly triggered when generated JavaScript is empty, ensuring code generation completes on Windows where the pass may be skipped during initial compilation due to re-entrancy guards. - Fix: Windows Console Unicode Encoding: Fixed codec encoding crashes on Windows cmd/PowerShell when printing Unicode characters (emojis, symbols). The console now detects Windows legacy terminals and replaces unencodable characters with safe fallbacks. Windows Terminal continues to use full Unicode support.
- Fix: JIR Cache Preserves
code_contextfor.cl.jac/.sv.jac/.na.jacModules: The JIR binary cache now serializes the per-nodecode_contextfield (CLIENT,SERVER,NATIVE) via a new overlay byte (bit 3 of overlay flags). Previously,code_contextwas apostinitfield not included in the JIR spec, so it always defaulted toSERVERon cache reload. This caused.cl.jacmodules loaded from cache as transitive dependencies to lose theirCLIENTcontext, making the ES code generator filter out all statements and produce empty JavaScript, breaking the admin UI and other client builds on second compilation. - Perf: Remove Redundant
_recalculate_parentsTree Traversals: Eliminated 1–2 redundant O(n) recursive AST traversals per compiled file.UniNode.postinit()already sets parent pointers at construction time andset_kids()fixes them on mutation, making the standalone_recalculate_parents()call after everyrd_parseand inside_coerce_modulepurely redundant. For.cl.jac/.sv.jac/.na.jacfiles this removes 3 full tree walks down to zero extra traversals. - Fix: ES Codegen Await Precedence and List Concatenation: Fixed two ECMAScript code generation bugs affecting full-stack apps. (1) Await in member access:
AwaitExpressionused as the object of aMemberExpression(e.g.,(await fn()).map(...)) was not parenthesized, causing.map()to bind to the Promise instead of the resolved value. The ES unparser now parenthesizesAwaitExpression,YieldExpression, and other low-precedence expressions when they appear as member access targets. (2) List+in impl files: When the type evaluator cannot determine the operand type (common in.impl.jacfiles),list + [item]fell through to the genericBinaryExpression('+')handler, producing JS string concatenation instead of array concatenation. A new heuristic inexit_binary_exprdetectsArrayExpressionoperands and emits[...left, ...right]spread syntax as a safe fallback.
jaclang 0.13.2#
- Typed Interop: dict[K,V] Return Hydration and Walker Spawn Serialization: The ES codegen now supports
dict[str, T]return types with automatic value hydration (Object.fromEntries(Object.entries(...).map(...))), wrapslist[T]returns at call sites with.map(x => T.__from_wire(x)), and serializes typed walkerhasfields with__to_wire()when spawning from client code. The interop analysis pass also now correctly extracts multi-parameter subscript types (e.g.,dict[str, Metric]was previously reduced to baredict). - Fix: ES Codegen RecursionError on Walker/Typed Arg Fixtures: Guarded an unprotected
get_type_evaluator()call inexit_func_callthat caused infinite recursion when compiling fixtures with walker spawns or typed function arguments. - 2 small refactors/changes.
- Type Checker: 8 Root-Cause Fixes for False Positive Errors: Eliminated several classes of false positive type errors across Jac codebases. (1) Raise/return guard narrowing: After
if obj.attr is None { raise; }, dotted attribute paths likeobj.attrnow stay narrowed for the rest of the scope. (2) Cascading Unknown suppression: When an attribute access produces an Unknown type, subsequent accesses on that result no longer emit redundant E1032 errors. (3) BareCallablesupport:Callablewithout type arguments now resolves to a gradual callable type (Callable[..., Any]) instead of_SpecialForm, fixing__name__access and parameter passing. (4) Assignment in narrowed branches:if obj.field is None { obj.field = value; }now type-checks the assignment against the declared field type, not the narrowedNoneType. (5) Structural protocol matching:SupportsIndex,Sized,Hashable,Iterable, and 10 other typing protocols are now matched structurally -- any class implementing the required dunder method satisfies the protocol. Includes astrsubscript fallback for__getitem__overload resolution. (6)Anyin unions: Attribute access on union types containingAny(e.g.,Any | Foo) no longer reports missing attributes. (7) TypeVar inimplblocks: Module-level TypeVars used in function signatures withinimplblocks are now recognized as valid generic parameters. (8) Special form type generalization:_get_special_form_typereturn type widened toTypeBaseto support returning non-ClassType special forms like gradual callables. - Fix:
enum.auto()Support: Usingauto()inIntEnumclass definitions now works correctly. Previously produced false type errors. - Fix: JIR Cache Preserves Impl-Defined Instance Variables: Instance variables defined in
.impl.jacfiles (e.g.,self.xininit) are now preserved after JIR cache reload. Previously, these symbols were lost becauseimpl_modis not serialized in JIR. - Fix:
SyntaxWarningDuring Bootstrap: Fixed invalid\#escape sequence in the ECMAScript private identifier generator that produced aSyntaxWarningon everyjac purge/ startup. - Fix: JIR Cache Computes MRO for Subtype Checking: When modules are loaded from JIR cache, class types may have empty MRO (Method Resolution Order) lists since type information is not serialized. The
is_sub_classcheck now computes MRO on demand, ensuring subtype relationships (e.g.,DerivedextendsBase) are correctly recognized on cached module loads. - Fix: Scope Narrowing Leak Between
implBodies: Guard-pattern narrowings (e.g.,if x is not None { return; }promotingxtoNoneType) no longer leak from oneimplbody into subsequentimplbodies that share the same local variable name. Previously,promote_inverted_narrowingsaccumulated on the parent scope stack becauseexit_abilitytraversedImplDefbodies without isolating the narrowing scope, causing falseE1030errors likeType "NoneType" has no attribute "mem". - Fix:
MemoryBase Interface Missing Methods: Promotedget_mem,get_gc, andremove_from_gcto the abstractMemorybase interface. These methods are implemented by all concrete memory types (VolatileMemory,SqliteMemory) and were being called through the baseMemorytype onExecutionContext.mem, producing falseE1030errors. - Fix: Generic Function TypeVar Resolution: Generic functions like
find_parent_of_type[T](typ: type[T]) -> T | Nonenow correctly resolve TypeVar bindings at call sites. Previously returnedUnknownbecause function-level type parameters were never stored,T | Nonecollapsed toUnknowndue tois_any_type()short-circuiting, andtype_paramswere dropped during method cloning. - Refactor: Compiler Passes Converted from
classtoobjwithpostinit: All compiler pass classes now use theobjstyle withhasdeclarations andpostinitmethods instead ofclasswith custominit. This aligns passes with Jac's dataclass-like initialization pattern. - Refactor: Native Pass
hasDeclarations and Duplicate Cleanup:NaIRGenPassnow declares all 83 instance attributes inhaswith proper defaults. Removed ~500 lines of duplicate method declarations and stub implementations fromprimitives_native. - Refactor: TypeEvaluator Converted to
objStyle withhasandpostinit:TypeEvaluatornow uses explicithasdeclarations for all 24 instance attributes with proper defaults, replacing the manualinitmethod withpostinit. - Fix: Project Dependencies Now Available to Subprocesses: Packages installed via
jac install(stored in.jac/venv/) are now accessible to subprocesses spawned from your code. Previously, runningsubprocess.Popen(["jac", "mcp", ...])failed because the venv'sbin/directory wasn't in PATH. Nowadd_venv_to_path()also prepends the venv'sbin/directory toos.environ["PATH"], so commands likejac mcpwork correctly when jac-mcp is installed as a project dependency injac.toml.
jaclang 0.13.1#
- Fix: MRO Resolution for Classes Imported Through Python
__init__.pyRe-exports: Inheriting from a class imported through a Python package's__init__.pyre-export (e.g.,from pkg import Basewherepkg/__init__.pydoesfrom .submod import Base) now works correctly. Previously, the base class resolved toUnknownType, breaking the MRO and causing false "has no attribute" errors on inherited members. - Fix:
ExecutionContextField Types Corrected to Non-Optional: Changedsystem_root,user_root, andentry_nodefields onExecutionContextfromNodeAnchor | NonetoNodeAnchor. These fields are always initialized inpostinit(defaulting tosystem_root), so the| Nonewas incorrect and forced unnecessary null-guard workarounds throughout access validation and anchor persistence code. - Fix: TypeVar Annotations in
self.attrAssignments: Type annotations on self-member assignments in generic classinitmethods (e.g.,self.value: V = input) now correctly propagate through inheritance chains. Previously, accessing such attributes in subclasses produced false "has no attribute" errors because the TypeVar type wasn't stored on the declaration node. - Fix: Ternary Expression Type Narrowing: The type checker now applies branch-specific narrowing inside ternary (
if-else) expressions. The walker manually traverses the true branch with narrowing from the condition and the false branch with inverse narrowing, preventing false-positive type errors whenisinstanceguards are used in ternary expressions. - Fix: CFG Symbol Propagation Through Already-Linked Nodes:
link_bbsnow propagates newly addedaffected_symbolsvia iterative BFS to already-linked internal CFG nodes. This fixes cases whereexit_if_stmtlinked body nodes before_link_sequentialadded upstream symbols, causing the backward CFG walk to miss narrowing predicates. - Fix: Runtime Null Safety for
user_rootandvisitExpressions:check_access_levelnow returnsNO_ACCESSwhenuser_rootisNoneinstead of crashing, andvisitgracefully handles expressions that are neitherNodeArchetypenorEdgeArchetypeby producing an empty traversal list instead of failing. - Fix: Scope Narrowing for AtomTrailer Nodes: The pre-cache scope narrowing check in
get_type_of_expressionnow handlesAtomTrailernodes (attribute access likeobj.attr) in addition toNameandNameAtomnodes. Previously, attribute access expressions insideandchains and ternary branches returned stale cached types, bypassing truthiness and isinstance narrowing. This eliminates 12 false positive errors inruntime.impl.jac. - Fix:
add_scope_narrowingUnion Replacement: When an existing scope narrowing is aUnionType(e.g., from truthiness excludingNone) and a more specific type arrives (e.g., fromisinstance), the specific type now replaces the union instead of being silently dropped. - Fix: Compound
orGuard Narrowing:_find_predicate_fornow handles inverted predicates inorconditions (e.g.,not x or not x.attr). Previously it bailed out when anyoroperand was inverted, preventing DeMorgan narrowing on the false branch of compoundorguards. - Fix: String method transpilation on local vars and chained calls in
.cl.jac:.lower(),.upper(),.strip()now correctly map to JS equivalents on local variables and chained calls, not just typedhasvariables. - Fix: Ternary Narrowing for Function Args, Attribute Access, and Generic Types: The type checker now eagerly evaluates
IfElseExprnodes inenter_if_else_exprso the type evaluator's narrowing scopes are active before the walker visits child nodes. Previously, walker callbacks likeexit_atom_trailerandexit_func_callfired before narrowing was set up, producing false positive errors on attribute access and function arguments inside ternary branches. Additionally, when inside ternary/boolean expression contexts, scope narrowing now takes priority over CFG narrowing in the AtomTrailer handler, since CFG operates at statement granularity and cannot see ternary-internal branches. This resolves known gaps B and C in ternary narrowing and fixes false errors likeisinstance(expr, list)failing to excludelist[T]variants in the else branch. - Fix: Stdlib Star Re-export Resolution: The type checker now follows star imports (
from .submod import *) in stub files when resolving imported symbols. Previously, symbols likeasyncio.Queue(defined inasyncio.queuesbut re-exported via star import inasyncio/__init__.pyi) resolved toUnknown, cascading into false errors on method calls like.empty()and.get_nowait(). This fixes code importing star-re-exported stdlib symbols. - Fix: isinstance Narrowing Preserves Generic Type Parameters:
isinstance(x, list)onx: list[int] | strnow narrows tolist[int]instead of barelist. The scope-based isinstance narrowing paths (process_conditionandIfElseExprhandler) now use_refine_isinstance_typeto filter the variable's declared union type, keeping only members that match the isinstance class while preserving their type arguments. This eliminates falseUnknownelement types when iterating over narrowed generic containers. - Fix: Type Narrowing Through
try/finallyandwhileLoops: The type checker now correctly propagates narrowing throughtry/finallyblocks andwhileloops. Previously, variables narrowed before atryblock (e.g., viaif not x { x = Foo(); }) would lose their narrowed type inside thefinallyblock, producing false positive errors. This also fixes narrowing for the commonContextVar.get(None)/ reassign pattern used in the runtime. - Fix: OR-Expression Result Type Narrowing: The type evaluator now excludes
NoneTypefrom non-last operands oforexpressions, following Pyright's short-circuit semantics. Forx or defaultwherex: T | None, the result type is nowT | type(default)instead ofT | None | type(default), becauseNone(always falsy) causesorto short-circuit to the next operand. This enables CFG assignment-based narrowing for the commonx = x or []idiom, eliminating false type errors when the narrowed variable is later used as a non-optional parameter. - Portability Check Pass (W6001–W6004): New compiler pass detects JS-idiomatic usage patterns that break cross-codespace portability. Warns on JS-specific globals (
console,Math,JSON,Array,Promise, etc.), JS-idiomatic methods (.push(),.forEach(),.indexOf(),.splice(), etc.), JS-specific keywords (undefined,typeof,void), and JS-specific property access (.lengthinstead oflen()). Each warning suggests the portable Jac alternative. - 1 small change/refactor.
- Fix: Narrowing Cache Key Uses Symbol Identity: The CFG narrowing cache in
_compute_narrowed_atnow usesid(symbol)instead ofsym_namein its cache key. When a variable is reassigned inside a branch (e.g.,if x is None { x = make_ctx(); }), Jac creates a separate symbol for the inner assignment. The old name-based key caused stale narrowing results from one symbol to be returned for the other, preventing the post-join type from reflecting the reassignment.
jaclang 0.13.0#
- Performance: Batch Edge Prefetch: Edge traversal (
-->,->:E:->, etc.) now collects all edge/target IDs up front and loads them in two batch queries instead of one-at-a-time. No syntax changes; same-->operators, just fewer DB round-trips under the hood. - First-Class Fixed-Width Numeric Types:
i8,u8,i16,u16,i32,u32,i64,u64,f32, andf64are now first-class builtin types, on par withintandfloat. They are recognized as keywords by the lexer, parsed asBuiltinTypeAST nodes, and prefetched by the type evaluator -- eliminating prior special-case handling where they were resolved as plain identifiers. - Type Checker: TypeVar Union and Overload Resolution: Fixed three root causes that made generic method return types resolve to
<Unknown>: (1)TypeVarType.is_any_type()short-circuited the|operator so_VT | Nonein typeshed stubs becameUnknownTypeinstead of a proper union; (2)is_annotation_type()did not recognizeTypeVarTypeas valid for union creation; (3) added TypeVar constraint solving to overload resolution -- method-level TypeVars (e.g.,_DinContextVar.get(default: _D) -> _D | _T) are now inferred from call arguments with bounds, constraints, and consistency validation via the existingassign_type_to_type_varinfrastructure. This fixesdict[K,V].get(),ContextVar[T].get(), and similar generic methods returning<Unknown>for user-defined types. - Type Checker:
typing.cast()Special-Form:cast(T, val)now returnsTinstead of matching theobjectoverload and returningAny. The type checker detectstyping.castcalls and extracts the target type directly. - Type Checker:
match/caseNarrowing: The match subject type is now narrowed insidecasebodies.case Dog():narrows the subject toDog, enabling access to subclass-specific attributes without false errors. - Fix: Enum Static Method Resolution: Static methods on
class-based enums (e.g.,class Color(Enum)) are no longer misidentified as enum members. Regenerated precompiled bytecode to include the updatedbuild_enum_membersfilter that excludesAbilitynodes. - Fix:
jac start --devWatchdog Installation: Ensures watchdog is automatically installed properly in the active environment or project's environment when runningjac start --dev, preventing failing with No module named 'watchdog'. - Error: Explicit
selfinobj/node/edge/walkerMethods (E2015): The type checker now reports an error when a method in anobj,node,edge, orwalkerarchetype explicitly declaresselfas a parameter. In these archetypes,selfis implicitly injected by the compiler; declaring it explicitly results in a duplicate parameter. Python-styleclassdefinitions are unaffected. - Parallel
jac format:jac formatnow processes multiple files in parallel usingProcessPoolExecutor, significantly reducing CI time for large codebases (e.g., 11 min → ~2 min). - Fix:
semstrings silently dropped for functions starting with 's', 'e', or 'm' (#5233):SemDefMatchPassusedlstrip('sem.')to strip thesem.prefix, which strips individual characters from the set{s, e, m, .}rather than the prefix string. This corrupted function names likesummarize_text→ummarize_text, causing the symbol table lookup to fail and the sem string to be silently discarded. Fixed by replacinglstrip('sem.')withremoveprefix('sem.'). - Hash-based anchor dirty checking: Replaced
__setattr__/is_updatedflag with hash-based change detection. Anchors are now snapshot-hashed on load and compared at sync time, eliminating unnecessary writes on read-only requests and automatically detecting all mutation types including in-place mutations (list.append(),dict[k]=v,set.add(), nested objects). - Type Checking Enabled by Default: All user modules are now type-checked during compilation. Bootstrap modules skip type checking automatically to avoid circular imports.
- Type Checker: Enum
.value/.nameResolution: Accessing.valueor.nameon enum instances now returns the correct type. For plain enums, the value type is inferred from members. - Fix: Static Analysis False Positive on Attribute Access: The "Name may be undefined" warning (W2001) no longer fires on attribute-access names (e.g.,
obj.value), which are member lookups, not standalone name references. - Type Checker: Walrus Operator Narrowing:
if (x := expr) is not None:andif isinstance((x := expr), T):now correctly narrowxinside the block. - Fix:
jac runPython Parsing Edge Cases: Resolved crashes and translation issues involving decorated classes (e.g.,@torch.compile()), f-string format specifiers, andsuper().__init__()calls by ensuring proper AST traversal and restoring__init__when targetingsuper(). - Type Checker:
type[T]Member Access: Accessing class-level members (e.g.,ClassVar) ontype[T]parameters now works correctly.cls: type[MyClass]→cls.my_class_varresolves toMyClass's members. - Type Checker: Property Support:
@propertyand@cached_propertynow correctly type-check. Accessingobj.my_propertyreturns the property's return type instead ofFunctionType. - Fix: Property on Union Types Returns Function Type: Accessing a
@propertyon a union type (eg:, afterisinstance(x, (Dog, Cat))) now correctly returns the property's return type. Previously, the union type code path skipped property unwrapping and returned the rawFunctionType. - Fix: Property Return Type
SelfResolution: Properties returningSelf(e.g.,Path.parent) now correctly resolve to the owning class instance type. Previously, the rawSelfTypeVar was returned without specialization, causing falseCannot access attributeerrors on chained access likePath(...).parent.parent. - Fix:
list(Generator)Type Inference:list(Generator[Item, None, None])now correctly returnslist[Item]instead oflist[NoneType]. TypeVar resolution through multi-level generic inheritance (Generator -> Iterator -> Iterable) now works correctly. - Fix: Static Methods on Class-Based Enums: Static methods on
IntEnum/StrEnumclasses now correctly return their declared type instead of<Unknown>. - Type Checker: Ternary isinstance Narrowing:
[x] if isinstance(x, T) else xnow correctly narrowsxtoTin the true branch and to the complement type in the else branch. Hover info shows the narrowed type. - Fix: Generic Constructor Type Inference:
set(my_list),enumerate(items), and similar generic constructors now correctly infer type parameters from iterable arguments (e.g.,set(list[Item])→set[Item]). Previously returned unparameterized types. - Type Checker: Generic Inheritance & Bidirectional Inference: Added MRO-aware type argument resolution (
specialize_for_base_class,build_solution_from_specialized_class) so multi-level generic inheritance chains are properly tracked._assign_classnow validates type args through the MRO with covariant/contravariant/invariant variance.type[X]annotations are now covariant (type[SubClass]assignable totype[BaseClass]).FunctionType.specializeandspecialize_member_typeuse transitive TypeVar resolution.infer_type_argswalks inherited constructors. Container literals (list,dict,set,tuple) now support bidirectional type inference viaexpected_typepropagation from return statements, assignments, and function arguments, with element-level validation before adopting the expected type. - Type Checker: Parameterized Types in Error Messages:
ClassType.__str__now displays type arguments for parameterized types (e.g.,list[int],dict[str, bool]) instead of bare class names. Error messages likeCannot return <class list>, expected <class list>now readCannot return list[Dog | Cat], expected list[int], making type mismatches immediately actionable. - Client-Side Error Reporting: Unhandled JavaScript errors and promise rejections in Jac client apps are now automatically captured and forwarded to the server via
POST /cl/__error__, where they are logged through both thejaclang.client_errorslogger and the dev console. Global error handlers (window.onerror,unhandledrejection) are installed at app initialization, and theErrorBoundaryfallback component also reports caught errors. Works with both the stdlib HTTP server and jac-scale (FastAPI). - Centralized Source Mapping: Added
source_mappingmodule toruntimelibwith VLQ encode/decode, source map generation/parsing, and two-layer resolution (bundle → compiled JS → .jac). Server error endpoints now resolve JS stack traces back to.jacfile locations with exact line numbers. The ES unparse pass tracks output-line → source-line mappings per-node viaes_to_js_with_map(), with proper newline accounting ingen_block_statementfor accurate nested statement mapping. IncludesSourceMappercaching with mtime-based invalidation,ErrorRateLimiterfor deduplication (10s window) and rate capping (20/min), andread_source_snippet()for diagnostics integration. - Centralized Source Mapping: Added
source_mappingmodule toruntimelibwith VLQ encode/decode, source map generation/parsing, and two-layer resolution (bundle → compiled JS → .jac). Server error endpoints now resolve JS stack traces back to.jacfile locations. The ES unparse pass tracks output-line → source-line mappings viaes_to_js_with_map(). - Automatic
TYPE_CHECKINGImport Guards: The compiler now detects imports that are only used in type annotations (parameter types, return types, field types) and automatically wraps them inif typing.TYPE_CHECKING:guards in the generated Python. This eliminates the need for manualif TYPE_CHECKING { ... }blocks in Jac source. Mixed imports (some items type-only, some runtime) are automatically split. Existing manualTYPE_CHECKINGblocks continue to work. - Fix:
jac checkand LSP Silently Swallowed Errors for Jaclang-Internal Files: Files inside thejaclang/package directory (e.g., compiler test fixtures) had their type errors routed to the compiler'sinternal_programinstead of the caller'sJacProgram, causingjac checkto report PASSED and the LSP to miss diagnostics. Addedforce_target_programoption toCompileOptionsso user-initiated operations always record errors on the correct program. Removed the LSP'sinternal_programerror aggregation workaround. - Fix: Formatter Inline Comment Swallows Next Argument: Fixed a bug where
jac formatwould merge the next function argument into an inline comment when the comment appeared after a comma in a multi-line call (e.g.,candidate[:-4], # strip trailing .jacwould absorb the followingos.path.abspath(base_dir)into the comment text). TheCommentInjectionPassnow upgrades soft line breaks to hard line breaks when they follow an inline comment, ensuring subsequent tokens always start on a new line. - Fix: Diagnostic Underlines for Multi-Line Spans: Fixed
jac checkrendering absurdly wide^^^^^underlines when a diagnostic pointed at a node spanning multiple lines (e.g., W2052 on an entireexceptblock). The W2052 warning now points at the exception type name token instead of the whole block, and the underline renderer clamps caret width to the end of the source line for any multi-line span. - Fix: Native Cross-Module Method Calls: Calling a method on a struct type imported from another
.na.jacmodule (e.g.,lx.next_token(),c.increment()) was silently dropped, leaving the target variable as a null pointer and producing runtime crashes. Methods on imported struct types are now correctly resolved and emitted. - Lintfix Improvement: Add lintfix format to context menu in VS Code.
- Improve:
jac formatGrouped Error Summary for Syntax Errors:jac formatnow displays a groupedFAILURESsection (file path + error messages) and afailed filessummary when files have syntax errors, consistent withjac checkoutput. Previously, errors were printed inline without grouping. - Fix: Garbled Emojis and Markup in
jac --versionBanner: Non-ASCII characters and emojis now render correctly in the version banner. - CLI:
jac run --diagnosticsFlag: The-e/--diagnosticsflag onjac runnow accepts a verbosity level:error(default -- fail on errors with full details, suppress warnings),all(show errors and warnings), ornone(suppress all diagnostics). By default,jac runnow fails with exit code 1 when compilation errors are detected, printing full diagnostic details. This can be configured project-wide via[run].diagnosticsinjac.toml. Replaces the previous--show-errorsboolean flag. rootRemoved as Language Keyword:rootis no longer a reserved keyword (KW_ROOT) in the Jac grammar. It is now an ambient built-in name resolved through the runtime builtin module's lazy__getattr__mechanism (same asjid,jobj,save, etc.), returningJac.root(). Existing code usingrootcontinues to work unchanged. Backtick escaping (`root`) is no longer necessary when usingrootas an identifier.- Fix: Impl Matching with Forward Declarations:
impl MyClass.methodnow correctly matches declarations whenMyClasshas forward declarations or is reassigned elsewhere. Previously failed with E2009. - Fix: Goto Definition for Import Paths and Imported Items: Goto definition now works correctly on all parts of an import statement. Previously, intermediate path segments failed to resolve because each was resolved independently; now the full dotted path is resolved once and intermediate paths are derived by walking up the directory tree.
- Fix: TypeVar Deduplication and Transitive Propagation in Generic Specialization: Fixed two bugs in the type checker's generic TypeVar resolution. (1)
_extract_type_paramsnow deduplicates TypeVars by name when a class inherits from multiple generic bases with overlapping TypeVars (e.g.,Mapping(Collection[_KT], Generic[_KT, _VT_co])previously collected_KTtwice, causing positional mismatches). (2)build_type_var_solutionnow propagates TypeVar bindings transitively through thebase_classeschain, so renamed TypeVars across inheritance levels (e.g.,_VT_coinMappingmapped through_VTinMutableMapping) are correctly resolved when specializing methods likedict[str, int].get(). - Fix: Generic[] Base Class Canonical TypeVar Ordering:
_extract_type_paramsnow processesGeneric[...]base classes first to establish the canonical type parameter order (PEP 484). Previously, TypeVars were collected in encounter order across all bases, sodict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co])produced[_VT_co, _KT_co]instead of[_KT_co, _VT_co], swapping key and value types fordict.keys()anddict.values(). - Fix: No E1004 for Ellipsis Stub Bodies: Functions with ellipsis-only bodies (
...;) are now recognized as stubs and no longer trigger E1004. - Fix:
jac checkon.impl.jac/.test.jacFiles Produced False Errors: Runningjac checkdirectly on an annex file (e.g.,runtime.impl.jac) compiled it as a standalone module, bypassing the annex pipeline (DeclImplMatchPass, scope linking) and producing hundreds of false type errors. - Fix: JIR Cache Corrupted Function Signatures: The decl/impl matching pass replaced parameter nodes with references outside the AST tree, causing JIR serialization to drop all parameter info. Imported functions loaded from cache appeared to have zero parameters, producing false argument-count errors and incorrect semantic highlighting.
- Fix: JIR Cache Lost Generic Class Fields: Generic type inference (e.g.,
Box[int].get()returningint) failed on cached modules becauseArchetype.bodywas serialized with the wrong field kind and restored asNone. Now works correctly on both fresh and cached runs. - Fix: JIR Cache Not Invalidated on Compiler Version Change:
is_module_cache_validonly checked file mtimes, so stale.jircaches written by a buggy compiler persisted across upgrades. The cache now reads the JIR header and rejects files whosejaclang_version_hashor Python version doesn't match the running compiler. - Fix: False "Name may be undefined" on Keyword Arguments: The static analysis pass (W2001) incorrectly flagged keyword argument names (e.g.,
name=infunc(name="check")) as potentially undefined variables. Keyword argument keys are now skipped. - Fix: User Module JIR Cache Now Project-Local: User module
.jircache files are now stored in the project's.jac/cache/directory (or.jac/cache/next to the source file when outside a project) instead of the global~/.cache/jac/jir/modules/directory. Compiler and bootstrap caches remain global. - Enforce Type Annotations on Function Parameters (E0052): The
ASTValidationPassnow reports an error when a function or method parameter is missing a type annotation. Lambda parameters are exempt (types are inferred). This catches missing annotations early during AST validation, before the type checker runs. - Fix: IntEnum/StrEnum Member Type Checking:
IntEnumandStrEnummembers now work correctly as function arguments and variable assignments. Previously, code likecheck_level(AccessLevel.READ)incorrectly failed with "Cannot assign int to AccessLevel". - Fix: Lambda Return Type False Positive:
returninside a lambda now type-checks against the lambda's own return type, not the enclosing function's. Previously,lambda -> dict { return {...}; }inside a-> JsxElementfunction would incorrectly error with "Cannot return dict, expected JsxElement". - Fix: JIR Cache Lost List Iteration Types: Iterating over a typed list (e.g.,
for anchor in node.edges) resolved correctly on the first compile but producedType is Unknownerrors on subsequent cached runs. TheIndexSlice.Sliceinner class was not aUniNode, so the JIR serializer could not assign it a node ID and silently dropped all slice data.Sliceis now a properUniNode, ensuringIndexSlice.slicessurvives cache round-trips. - Error: Duplicate Method Definitions in Class Body (E0076): The
ASTValidationPassnow reports an error when a class (obj,node,edge,walker, orclass) body contains two or more methods with the same name. Previously, the second definition silently shadowed the first with no diagnostic. - Topology Index for Graph Query Optimization: Edge-op chains with type filters (
-->:EdgeType:-->[?:NodeType]) now use a compact adjacency index stored on root nodes to resolve which nodes match before fetching from the database. This avoids loading all intermediate edges and non-matching nodes, reducing L3 (SQLite) fetches by up to 91% and improving query latency up to 7.6x on large graphs. The index is maintained automatically onconnect/disconnect/destroyand encoded as a compact binary blob on the root anchor. Enabled by default; can be disabled with[run] topology_index = falseinjac.toml. - Post-Hoc Filter Fusion for Topology Index: Post-hoc type filters (
[root-->:Edge:->][?:NodeType]) are now fused into the preceding edge-op'srefs()call at codegen time, so the topology index can optimize them the same way as inline filters (root-->:Edge:->[?:NodeType]). Both forms now produce identical results and benefit from index-accelerated queries. - Fix:
typing.AnyAttribute Access: Accessing attributes onAny-typed values no longer incorrectly raises E1030. - Fix:
objectType No Longer Treated asAny: Accessing unknown attributes onobject-typed values now correctly raises E1030. Previously,objectwas treated likeAny, allowing arbitrary attribute access. - Fix:
Final[T]Attribute Access: Accessing members onFinal[T]-typed values now works correctly (e.g.,Final[list[int]].count()). - Type Checker: Function Type Attributes: Accessing
__code__,__name__,__module__, and other function attributes onCallabletypes now works correctly. Previously returned<Unknown>. - Type Checker: OR Short-Circuit Type Narrowing: The type checker now narrows types through
orshort-circuit evaluation. In patterns likenot x or x.method()andx is None or x.method(), the right-hand operand is evaluated in a context wherexis known to be truthy (notNone), so attribute access and method calls are accepted without false errors. Also works forself.field(AtomTrailer) narrowing andnot isinstance(x, T) or x.method()patterns. Narrowing is scoped to theorexpression and does not persist past it. - Fix: Walrus isinstance Narrowing Lost After Nested Control Flow:
isinstance((x := expr), T)narrowing was lost after nestedif/whileblocks inside the guarded scope. The scope-based narrowing path failed becauseget_narrowing_keydid not unwrapAtomUnit(parentheses) around the walrus expression, so_extract_isinstance_inforeturnedNoneand no scope narrowing was stored. AddedAtomUnitunwrapping toget_narrowing_key. - Type Checker: Ternary Else-Branch Type Narrowing: The type checker now applies inverse narrowing in the else branch of ternary (if-else) expressions for
is None,is not None, and truthiness conditions. Previously onlyisinstanceconditions were narrowed in the else branch.[] if (x is None) else xnow correctly narrowsxto excludeNonein the else branch, anddefault if (not x) else xnarrowsxto non-Nonein the else branch. - Compiler: Unified Backend Analysis via
UniTreeEnrichPass: Backend-neutral analysis (native compatibility detection, type tags for LLVM codegen, JS declaration hints for ECMAScript codegen) is now pre-computed once byUniTreeEnrichPassduring IR generation and stored on AST nodes. Backend passes (NativeCompatCheckPass,NaIRGenPass,EsastGenPass) consume these pre-computed values instead of re-deriving them, eliminating redundant AST walks.NativeCompatCheckPassis now a pure read-and-promote pass with no analysis logic. - Type Checker:
ParamSpecandTypeVarTupleSupport:P = ParamSpec('P')andTs = TypeVarTuple('Ts')now produce properTypeVarTypeentries in the symbol table (withis_param_spec=True) instead of<Unknown>.Callable[P, T]no longer emits a false E1071 error.ParamSpecandTypeVarTupleare tracked via the prefetch mechanism (same asTypeVar) so identity checks use type-system identity rather than string matching. - Fix: Module-Level Dunder Variables:
__name__,__file__,__doc__,__package__, and__spec__are now declared injac_builtins.pyiwith their correct types. Previously,getLogger(__name__)and similar calls produced E1053 because__name__resolved to<Unknown>. - 8 small refactors/changes.
- Request-Scoped Execution Context:Introduced request-scoped execution contexts using
ContextVarinJacRuntime.get_context(), enabling isolated per-request state and L1 caches in web environments while preserving global context behavior for CLI and tests. - Type Checker: Warn on Return Value in Event-Driven Abilities (W2014): The type checker now emits warning W2014 when an event-driven ability (
can X with Y entry) usesreturn <value>. Since walker-triggered abilities ignore return values, the warning guides developers to update node or walker fields directly instead. Plainreturn;(no value) andreturn None;are not flagged. - Fix:
jac py2jacPerformance on Large Files: Fixed exponential slowdown when converting large Python files to Jac. A 10k-line file now converts in ~10s instead of 37s (3.9x faster), and a 20k-line file in ~16s instead of ~3 minutes (11x faster).
jaclang 0.12.2#
- Fix:
os.path.dirname()Type Check Error: Callingos.path.dirname()no longer fails with "No matching overload found". This also fixes other stdlib functions accessed through wildcard imports (e.g.,os.path,datetime) that have multiple@overloadsignatures. - Type Checker: Constrained TypeVar Support: TypeVars with explicit constraints (
T = TypeVar("T", Foo, Bar)) now validate operations against all constraint types. - Fix: Generic Iterator Type Inference:
enumerate,zip, and other generic iterators now correctly infer element types from their arguments. Previously, version-dependent overloads in typeshed caused type resolution to fail. - Scheduling: DYNAMIC Trigger Support:
@schedule(trigger=DYNAMIC)now attaches a spec and delegates execution to a registered_dynamic_schedule_handler(e.g. jac-scale) instead of raisingNotImplementedError. - 2 small refactors/changes.
- Fix: Formatter Comment Displacement in Multi-Assignment Globals: Fixed a bug where
jac formatwould displace comments inside multi-assignmentglobdeclarations to the end of the file. TheCommentInjectionPassnow handlesGlobalVarsnodes (which useAlignlayout) alongsideArchHasnodes. Added safety error E5051 that blocks saving when comment displacement is detected. - Fix:
jac testRobustness and--test_nameSpace Support:jac test myfile.jac --test_name "my test name"previously ran 0 tests silently. It now works the same as--test_name my_test_name. Also improved error messages when the file is missing or--test_nameis used without a filepath. - Inline Diagnostic Suppression: Added
# jac:ignore[CODE]syntax to suppress specific diagnostics on individual lines, giving developers fine-grained control over which warnings and errors to silence. - Fix: Improved Error for Assigning to Built-in References: Assigning to
rootorsuper(which compile to function calls, not variables) now emits a clear errorE0024with help text suggesting backtick escaping, instead of crashing with a cryptic PythonValueError. Bytecode compilation failures are also now caught and reported asE5043with file and line context. - ASTValidationPass: Post-Parse Semantic Checks: Moved 12 semantic checks from the parser into a dedicated
ASTValidationPassthat runs after parsing, following the modern compiler pattern of "parse permissively, validate later". The parser now focuses purely on tree construction while the validation pass provides consistent, context-rich error messages for structural violations (e.g.,pass/newkeyword usage, parameter ordering, emptymatch/switch/enum/trybodies, walrus operator LHS). All validations emit structured diagnostic codes (E0010, E0020, E0023, E0031, E0040–E0051) with inline suppression support via# jac:ignore[CODE]. - Bracket Filter Comprehension Syntax
[?:Type, cond]: Introduced new bracket-based filter comprehension syntax[?:Type, cond]as a replacement for the parenthesized(?:Type, cond)form. The new syntax works uniformly in all contexts: standalone (my_list[?:Foo, val<3]), after edge traversals ([root-->][?:A]), and inside edge ref chains ([root-->[?:A]]). The old(?:...)syntax is now deprecated and emits warning W0061 with guidance to migrate. The formatter automatically normalizes old syntax to the new bracket form onjac format. - Fix: Layout Pass Warnings Report Correct Source Locations: Layout-compatibility warnings (W5031, W5032) now point to the actual field declaration instead of line 1 of the file. The
_validate_obj_fieldsmethod was not passing the field AST node toemit, causing it to fall back to the module root location. - 24 New Diagnostic Checks Across 3 Passes: Added 24 new compiler diagnostics covering context validity, semantic analysis, and lint patterns. ASTValidationPass (7 checks): yield outside function (E0055), break/continue outside loop (E0058), nonlocal at module level (E0062), duplicate keyword argument (E0065), positional after keyword argument (E0066), bare except not last (E0071), duplicate base class (E0075). StaticAnalysisPass (8 checks): division by zero (W2070), None comparison with
==/!=(W2058), self-assignment (W2063), f-string without placeholders (W2074), redundant boolean comparison (W2075), overly broad except (W2052), return in finally (E2055), disengage outside walker (E2083). JacAutoLintPass (9 checks): unnecessary pass (W3020), unnecessary else after return (W3021), nested if-to-elif (W3022), return-bool simplification (W3023), repeated condition (W3024), identical branches (W3025), too many parameters (W3030),iswith literal (W3035), mutable default argument (W3036).
jaclang 0.12.1#
- Automatic Jac Import Hook via
.pthFile: Installing jaclang now automatically registers a lightweight lazy import finder at Python startup via a.pthfile. This means.jacmodules can be imported from Python without needingimport jaclangfirst. Jac imports Just Work. The lazy finder adds ~0.1ms to non-Jac Python startup and only triggers the full jaclang bootstrap on first.jacimport. - Fix: TOML Serializer Preserves Special Chars and Env Vars: Fixed two bugs in the
jac.tomlserializer triggered when programmatically saving config changes: (1) Table headers containing special characters (e.g.,[plugins.client.npm.auth."//npm.pkg.github.com/"]) now retain proper quoting instead of being written unquoted, and (2) Environment variable placeholders like${NODE_AUTH_TOKEN}are preserved as-is instead of being interpolated to their runtime values. Previously, these bugs would corruptjac.tomlfiles when dependencies were auto-updated. - Fix: With Statement Alias Type Inference:
with open(...) as fnow correctly typesfinstead ofUnknown. The type binding was moved fromexit_with_stmttoenter_with_stmtso the alias type is set before the body is visited. - Fix: Tuple Unpacking in For Loops:
for (a, b, c) in list[tuple[A, B, C]]now correctly infers types for unpacked variables instead ofUnknownType. - Refactor:
GUESTConstant for Guest Username: Added aGUEST = '__guest__'constant toConstantsenum and replaced hardcoded'__guest__'strings in the stdlib HTTP server withCon.GUEST.valuefor improved maintainability and consistency. - Fix: Native Cross-Module Global Variable Access: Module-level globals declared in one
.na.jacfile are now correctly accessible from importing modules. Previously, accessing such a global caused a segfault at runtime. - 16 small refactors/changes.
- Fix: HMR Recursive recompilation: Fixed client-side code recursive recompilation process, preventing cyclic recompilation, and ensuring that all dependencies are up to date.
- Fix: Errors in
.impl.jacFiles Now Reported byjac checkandjac run: Undefined names and unreachable code inside.impl.jacfiles were previously silently ignored. They are now correctly reported as warnings, pointing to the exact file and line. - Fix: HTTP Server Authentication for Imported
:pubFunctions: Fixed server incorrectly requiring authentication (401) for imported:pubfunctions. The server now inspects source file ASTs to determine access levels for imported function endpoints, matching the existing behavior for imported walkers. - Fix: Python Package Imports: Fixed two import bugs. (1)
import from mypkg { MyClass }now works whenmypkg/__init__.pyre-exports viafrom .mymod import *- previously the type checker couldn't findMyClass. (2)import from mypkg { subpkg }now correctly typessubpkgas a module - previously it failed whensubpkgis a sub-package insidemypkg. - Fix: Type Checker JIR Cache Compatibility & ClassVar Support: Fixed symbol resolution and
_SpecialFormdetection for JIR-cached modules. AddedClassVar[T]unwrapping. For-loops overAny/object/TypeVarno longer produce false iterable errors. class defSyntax andSelfType for Classmethods: Addedclass defas a first-class classmethod modifier (alongsidedefandstatic def), withSelfas a polymorphic type reference that resolves to the enclosing archetype.Selfmaps toclsin classmethods,type(self)in instance methods, and the class name in type annotations. Works across all archetype kinds (obj, node, edge, walker), generics, impl blocks, and inheritance chains. Existing@classmethoddecorator usage remains supported.- Compiler Warns on
@classmethod/@staticmethodinobjDefinitions: Using@classmethodor@staticmethodinsideobj,node,edge, orwalkernow emits a warning. Use thestatickeyword instead, orclass deffor classmethods. Compilation warnings are now also surfaced duringjac run. - Refactor: Console Uses Raw ANSI Codes: Replaced jacpretty markup parsing with direct ANSI escape codes. User data passes through unchanged; colors auto-disable in CI/pipes/non-TTY environments.
- Fix: Union Type Member Access Errors:
x.attron a union type now errors when the attribute is missing from any variant (previously silently returnedUnknownType). Reports which variant(s) lack the attribute. - Type Checker: TypeVar Validation: Added
TypeVarchecks - name must match the variable (e.g.T = TypeVar("T"), notT2 = TypeVar("T3")), must be assigned to a simple name (notd["k"] = TypeVar("T")), and must have zero or 2+ constraints (notTypeVar("T", str)). - Type Checker: TypeVar Scope Validation: TypeVars are now validated at usage sites. A TypeVar must be declared via
Generic[T]on the enclosing class, or appear in the enclosing function's signature. Using a TypeVar at module level, in a nested class that re-declares an outer TypeVar, or in a runtime context (e.g.list[T]()) is now an error. - Type Checker: TypeVar Variance Validation: Covariant TypeVars (
covariant=True) are now rejected as direct method parameter types when class-scoped (e.g.def f(self, a: T_co)is an error). Contravariant TypeVars (contravariant=True) are rejected in method return types, including inferred returns (e.g.def f(self) -> T_contrais an error). Both checks are skipped when the TypeVar is not class-scoped, and nested positions likelist[T_co]are allowed. - Type Checker: Unbounded TypeVar Operation Validation: Operations on unbounded TypeVar values are now errors. Attribute access (except universal ones like
__class__), calls, subscripts, binary/unary ops, augmented assignment,await, and iteration (for x in t) all produce errors when the operand is an unbounded TypeVar (e.g.a + 1wherea: Tis an error). - Type Checker: Bounded TypeVar Operation Validation: TypeVars declared with
bound=(e.g.T = TypeVar("T", bound=Foo)) now use the bound type for all operation checks. Attribute access, calls, subscripts, binary/unary ops, augmented assignment,await, and iteration are validated against the bound type's methods - soa.var1is ok ifFoohasvar1, buta.var2errors ifFoohas novar2.Unionbounds (e.g.bound=Union[Foo, Bar]) are not yet supported and are treated as unbounded for now. - Fix: Module-Level Overload Resolution:
math.floor(),math.ceil()and other module-level overloaded functions now correctly resolve all@overloadsignatures instead of only the first. - Fix: Parameter Type Highlighting and Go-to-Definition: Types used in function parameters (e.g.
uni.Module) now highlight correctly and support go-to-definition in.jacdeclaration files. - Stdlib Protocol Detection: Added Pyright-style
ModuleSourceFlagsfor production-grade stdlib type detection. Protocol types like_SupportsFloorand_SupportsTruncare now properly recognized, enablingmath.floor(3.7)andmath.trunc(4.9)to type-check correctly. - Fix: Native Cross-Module Method Calls: Calling a method on a struct type imported from another
.na.jacmodule (e.g.,lx.next_token(),c.increment()) was silently dropped, leaving the target variable as a null pointer and producing runtime crashes. Methods on imported struct types are now correctly resolved and emitted. - Fix:
jac format --lintfixFile Deletion on Parse Errors: Fixed a critical bug wherejac format --lintfixwould completely wipe out file contents when encountering parse errors. The formatter now preserves the original file when parse/lex errors are present, while still allowing files with type errors (but valid syntax) to be formatted normally. Added a safety check informat_single_file()that prevents writing empty formatted output to disk. - CFG Build Pass Rewrite: Rewrote the control flow graph construction pass from a fragile
to_connectworklist design to a clean entry/exit visitor pattern with type-specific handlers (exit_if_stmt,exit_while_stmt, etc.). The new pass is stateless (no mutable pass-level state), explicitly handles 20 control flow constructs (vs 6 previously), and adds short-circuit boolean wiring forand/orconditions. PromotedMatchCase,SwitchCase,Test, andBoolExprto CFG nodes for proper edge modeling. Fixes missing edges fortry/except/finally,with,switch/case,match/case, and nestedif-without-elseinside compound statements. Unreachable code afterraise/disengageis now correctly disconnected. - Fix: Raw ANSI Codes in Error Output: Fixed
[0;31mescape fragments appearing as literal text in terminal error messages.pretty_print(colors=True)was injecting raw ANSI codes that conflicted with the Rich-based console from jac-super. Error formatting now delegates all styling to the console layer. - Stricter RD Parser Enforcement: The recursive-descent parser now enforces missing semicolons as errors instead of silently accepting them. Docstrings inside function/test bodies that belong at element level are reported and must be placed before the signature as
docstring_target. Keywords used as parameter names now produce clear error messages suggesting backtick escaping (e.g.,`default). Builtin type names (str,int,float,list,tuple,set,dict,bool,bytes,any,type) are treated as contextual identifiers and can be used as parameter names without escaping. - Native: Function Pointer Support for C FFI Callbacks: Jac
deffunctions can now be passed as raw function pointers to C library calls in native code, enabling callback-based C APIs (e.g. libuv timers, async I/O) to be driven directly from Jac. - Fix: Match Case Crash on Empty Wildcard Body: Fixed
list index out of rangecrash when amatch/caseblock has a bare;(empty statement) as its only body, e.g.case _: ;. - Improved Internal sv Compiler Error Diagnostics: Helper added that raises a structured ICE with source file, line, column, and node type instead of a bare
list index out of range. - Fix: Type Checker Crash on
Final[UnionType]: Fixed crash when type checkingFinal[int | str]annotations. UnwrappingFinal[T]now correctly handles union types instead of failing with'UnionType' has no attribute 'shared'.
jaclang 0.12.0#
-
27 small refactors/changes.
-
Fix: Formatter Semicolon & Decorator Spacing: Fixed spacing bugs in the formatter where
@decorators produced@ decoratorinstead of@decorator, and statement semicolons producedraise ;instead ofraise;. - Fix: Type Checker Validates Args Against Parameterless
init: The type checker now correctly reports an error when arguments are passed to a constructor whoseinittakes no parameters. Named args raiseNamed argument does not match any parameterand extra positional args raiseToo many positional arguments. Calling with no args (MyObj()) remains valid. - Automatic Port Fallback for
jac start: When starting the built-in HTTP server, if the specified port is already in use, the server now automatically finds and uses the next available port instead of crashing with "Address already in use". A warning message displays when using an alternative port. Theon_readycallback signature updated toCallable[[int], None]to pass the actual bound port. - Fix: LSP Impl File Diagnostics: Editing
.impl.jacor.test.jacnow shows errors correctly across all related files. - Fix: Type Checker Support for
__getattr__: Classes defining__getattr__no longer produce false "has no attribute" errors. Dynamic attribute access now correctly resolves to the__getattr__return type, andAnyis callable (enabling proxy patterns likeconsole.error("msg")). IDE hover shows dynamic attributes as(dynamic attribute) name: type. - HMR Terminal Output Cleanup: Styled HMR logs with
console.success/error/warningand stripped absolute paths from compile errors. - Fix: Implicit
runNot Detecting Flags Before Filename:jac --autonative file.jacfailed to insert the implicitrunsubcommand because the detection only checked if the first argument was a.jacfile. Now scans all arguments for a.jac/.pyfile, so flags like--autonativeand--no-cachebefore the filename are correctly passed through tojac run. - Unified JIR Cache: Single Binary Cache File Per Module: Introduced JIR (Jac IR), a compact binary format that unifies all per-module caches -- type-checked AST, bytecode, MTIR, LLVM IR, and interop metadata -- into a single
.jirfile under~/.cache/jac/jir/. The format uses a 32-byte header, zlib-compressed AST payload, and optional TLV sections for each artifact type. On subsequentjac checkorjac runinvocations, cached modules are deserialized directly instead of being re-parsed, scope-built, and type-inferred, yielding 1.8-2.5x speedup on real compiler files. Cache entries are invalidated automatically via mtime comparison against source, impl, and variant files. The oldDiskBytecodeCacheandprecompiled.jacmechanisms have been removed in favor of this single unified format. A newjac gen-jir-registrycommand (itself a Jac module) auto-generates the 141-type node registry used for binary serialization, with a--verifymode for CI enforcement. - AST Declarative Field Conversion: Converted all 143 AST node classes in
unitree.jacfrom manualdef initconstructors to declarativehasfield declarations, enabling proper dataclass field inheritance across the node hierarchy. Token hierarchy classes retain manual init with direct field setup to avoid__post_init__MRO dispatch issues. Bootstrap transpiler (jac0.py) updated withby postinitparsing support,postinitto__post_init__mapping, and conditionalkw_only=Truefor subclassed nodes. - Type System Improvement: Fixed type narrowing not working correctly inside while loops, for loops with break/continue, and loop else blocks.
- Fixed-Width Numeric Types Promoted to Language Level:
i8,u8,i16,u16,i32,u32,i64,u64,f32, andf64are now recognized as built-in types across all backends, not just native codespace. The type checker resolves them, IDE hover/autocomplete works, and arithmetic between fixed-width types follows width-based promotion rules (e.g.,i8 + i32promotes toi32). On the Python backend they behave asint/float; on the native backend they map to exact LLVM IR types. - Fix: Match-Case & Walrus Type Narrowing: Variables inside
match/caseblocks now correctly narrow to the matched type. Walrus operator (if (x := get_optional())) now narrowsxto excludeNonein the true branch. - Fix: Formatter Comment Injection for
na {}Blocks: Fixed a bug wherejac formatwould orphan comments insidena {}(native) blocks, dumping them at the end of the file. - Fix: Native Empty Dict/List
{}in Struct Constructor Null Pointer: Passing an empty dict or list literal as a keyword argument in a struct constructor (e.g.Container(d={})) no longer stores a null pointer in the field. The compiler now falls back tohelpers["new"]()when_codegen_dict_val/_codegen_list_valreturnsNonefor an empty literal, matching the existing fix for global variable initializers. - Native Primitives: Set Algebra & Dict
setdefault: Implemented 6 new native LLVM emitters --set.symmetric_difference,set.update,set.intersection_update,set.difference_update,set.symmetric_difference_update, anddict.setdefault. Native primitive coverage rises from 47% → 49% implemented (147/299) and 45% → 47% tested (140/299), with SetEmitter at 61% and DictEmitter at 81%. - Native Primitives: Set Operators & Builtins: Added full set operator dispatch (
|,&,-,^,==,!=,<=,<,>=,>) and augmented assignments (&=,-=,^=) to the native LLVM backend, plussum,any,all, anddivmodbuiltins. SetEmitter reaches 100% native coverage (31/31). Overall native primitive coverage rises from 49% → 55% implemented (163/299) and 47% → 52% tested (156/299). - Native Primitives: List Operators, Frozenset & Sorted/Reversed Builtins: Added
list * intrepetition, and all six list comparison operators (==,!=,<,>,<=,>=) to the native LLVM backend.frozenset[T]now resolves to the same LLVM struct asset[T], giving it full operator and method coverage transparently.sorted()andreversed()builtins are now implemented (copy + in-place sort/reverse). - Native Primitives: 80% Coverage Milestone: Native LLVM primitive coverage reaches 80% (240/299 operations implemented and tested), up from 55%.
- Native Primitives: 84% Coverage: Added 6 native emitters (
int.to_bytes,int.from_bytes,float.fromhex,str.format,dict.items,ascii) with static method dispatch for class-level calls (int.from_bytes(),float.fromhex()). - Native
slice()Builtin & List/String Slicing:slice()constructor,.start/.stop/.stepattribute access, andlst[1:3]/s[1:3]slice subscript syntax now work in the native LLVM backend. - Implicit
selfinimplSignatures & Generic Ability Support: Updated the bootstrap transpiler (jac0.py) to skip generic type parameters (def foo[T, E](...)). - Fix: Native
for k in dict[K,V]Loop Body Elided: Fixedfor k in dover a dict function parameter emitting no loop IR - the dict/set parameter type was never registered invar_dict_type, causing_codegen_forto silently skip the body. - Native Codegen:
del d[k]andd.remove(key)for Dicts:del d[key]andd.remove(key)now correctly remove entries from native dicts. Previously both operations were silently dropped, leaving the dict unchanged. Works for bothdict[int, int]and object-value dicts (dict[int, T]). - Zero-Copy Native Struct Marshalling: Native-to-Python interop no longer deep-copies struct fields into Python dicts.
NativeStructViewandNativeListViewusectypes.Structure.from_address()to create zero-copy views over native memory -- field reads go directly through ctypes descriptors, and string fields are decoded on demand viactypes.string_at(). - Native Multiple Inheritance with C3 MRO: The native LLVM backend now supports multiple inheritance with Python-compatible C3 linearization. Struct layouts flatten fields from all ancestors in MRO order with diamond deduplication. Vtables merge method slots from all parents, and method resolution follows C3 semantics (first implementation in MRO wins). Non-primary parent methods are re-compiled against the child's struct layout for correct field access. All MRO computation happens at compile time with zero runtime overhead - dispatch remains O(1) vtable lookup. Includes tests for two-parent inheritance, diamond patterns, MRO method resolution, and three-mixin composition.
- Native Performance: Bump Allocator & Value-Type Tuples: Replaced per-object
mallocin the native LLVM backend with a bump allocator using chained 128MB arenas, eliminating malloc/free overhead for the ~30M heap allocations typical of complex programs. Tuples are now emitted as LLVMinsertvalue/extractvaluevalue-type structs instead of heap-allocated pointers, with automatic boxing only when stored in containers. Destructors skip individualfreecalls for arena-allocated memory; arenas are reclaimed on process exit. - Fix: Native Cross-Module Struct Type Resolution: Importing a user-defined
objfrom another.na.jacmodule and using it as a function parameter type no longer crashes the compiler. The importing module now walks the imported module's AST to fully register imported archetypes (struct layout, field types, field indices), and interop binding type strings resolve against both primitive types and struct types. - Replace Vendored LSP Stack with Custom
jaclang/lsp/Package: Removed ~32,700 lines of vendored Python code (pygls, lsprotocol, cattrs, attrs) and replaced them with a lightweight ~1,400-line Jac-nativejaclang/lsp/package providing LSP 3.17.0 types, JSON-RPC transport, UTF-16 position encoding, and workspace/document management. - Fix: IDE Hover Types for Comprehension Variables: Iteration variables in comprehensions (
pin[x for p in pool],any(... for p in pool)) now display their inferred type on hover. - Fix:
Union[]andOptional[]Special Forms:Union[int, str]andOptional[str]fromtypingnow work correctly for type checking, narrowing. - NamedTuple Type Checking: Classes extending
NamedTuplenow support proper constructor validation (no false "Too many positional arguments" errors), IDE hover for field-related variables including tuple unpacking ((x, y) = point) and for-loop iteration (for coord in point), and correct field type inference. - Fix:
py2jacBinOp operator precedence:(a - b - c) // 2was incorrectly converted toa - b - c // 2. Fixed by wrapping same-op chains inAtomUnitso parent operators bind to the whole group. - New: jacpretty: Implment an new library for enhanced CLI colors and designs.
- Type Checker Soundness Fixes (9 bugs): Fixed
is/inoperators returningUnknownTypeinstead ofbool(missing return), list literal type inference only using the first element instead of the union of all elements, MRO linearization using DFS instead of C3, protocol conformance checking only method names instead of full signatures, missing type inference fordict/tuple/setliterals,and/orexpressions returning only the last operand's type instead of the union, missing handlers for comparison and ternary expressions (both returnedUnknownType), and generic type argument checking in class assignment (list[int]was assignable tolist[str]). - Stricter UnknownType Discipline:
UnknownTypeis no longer treated asAny-- assigning an Unknown-typed value to a typed variable (e.g.,x: int = unknown_val) now raises a type error. Unknown destinations (unresolved type aliases) still accept any source. Functions without return annotations now inferNoneinstead ofUnknown. - Fix: List/Dict Subscript Type Inference:
list[Foo][0]and similar subscript expressions now correctly resolve element types. Previously, theIndexSliceAST node was passed directly to__getitem__overload resolution instead of extracting the inner expression, causing all overloads to fail and returningUnknown. - New: Decorator Support on
testBlocks:testblocks now accept decorators using the same@decoratorsyntax as abilities. Example:@skip @timeout(5000) test "slow operation" { ... }. This enables patterns like@skip,@timeout,@tag, or any custom decorator on tests. - Fix: String Literal Type Checking: Fixed false positive errors for
str.split()[0] + " suffix", string reassignment (msg += " world"),LiteralStringwithlen(), and byte string (b"...") type inference. - Centralized Type Layout & Symbol Resolution: Extracted class hierarchy computation (C3 MRO, field layout, vtable structure) and symbol resolution utilities into shared modules (
layout_pass.jac,symbol_utils.jac) that all backends query. The native LLVM backend and ES backend now delegate to these centralized implementations instead of maintaining independent copies of the C3 linearization algorithm, hierarchy extraction, symbol lookup, and field collection logic (~128 lines of duplicated code removed across backends). - CType Struct Contract for
obj: Formalizedobjarchetypes as CType-compatible structs with a strict layout contract. Allobjfields must use layout-compatible types: primitives (int,float,bool,str,bytes, fixed-widthi8–u64,f32,f64), otherobjtypes (as pointers), enums, typed collections (list[T],dict[K,V],set[T]), optional types (T | None), or function pointer signatures. The layout pass now validates field types at compile time with warnings for non-compatible types (e.g.,Any, untyped fields). Added function pointer type resolution in the native LLVM backend (FuncSignature→ir.FunctionType.as_pointer()), extendedNativeFieldInfowith function pointer metadata (is_func_ptr,func_param_types,func_ret_type), and updated the zero-copy ctypes marshalling layer to wrap function pointer fields as callableCFUNCTYPEobjects on access. - Fix: jac-check wanings not printing to CLI:
jac-checkwas not printing warnings fixed by minor if statement/for loop changes. - Type Checker Gaps #46–#50: Fixed 5 type checker gaps: standalone
.impl.jacfiles now resolve their parent module's scope (Gap #46); dynamicself.attrassignments onobj/node/edge/walkerarchetypes are now flagged as errors since Jac requires explicithasdeclarations (Gap #47);ClassType.lookup_member_symbolno longer produces false positives when checking imported class self-members (Gap #48); string annotations used as forward references underTYPE_CHECKINGguards now resolve correctly instead of being treated asLiteral(Gap #49);by postinitfields are now validated to ensure thepostinitmethod body assigns to all declared fields (Gap #50). Also fixed an LSP go-to-definition assertion for 0-indexed line numbers. - Type Checker Soundness Fixes (9 more shortcomings): Removed unsound
set→frozensetimplicit coercion. Added iterability checks for destructuring RHS ((a, b) = 5now errors) and for-loop collections (for x in 42now errors). Added type inference for list/set/dict/generator comprehensions, lambda expressions, andboolliterals (all previously returnedUnknownType). Added exception type narrowing inexceptclauses so the bound variable carries the declared exception type. Addedraisestatement validation to reject non-exception types. Added context manager protocol checking inwithstatements (verifies__enter__exists). Added missing-return detection for functions with non-Nonereturn type annotations that may implicitly returnNone. - Fix: Native Cross-Module Glob Initializers:
globdeclarations with initializers (e.g.,glob x: list[T] = []) in imported.na.jacmodules are now correctly initialized before first use, preventing segfaults on access.
jaclang 0.11.3#
- Static Analysis Pass: Unused Variables, Undefined Names, Unreachable Code: Added a new
StaticAnalysisPassto the type-check pipeline that detects three classes of issues: (1) variables defined but never referenced, (2) name references that fail to resolve, and (3) code followingreturn/raise/break/continuestatements. All diagnostics surface as warnings in bothjac checkoutput and LSP (IDE squiggles). The pass runs afterTypeCheckPassand respects conventional skip patterns (_-prefixed names,hasfields, imported symbols, abstract ability parameters, archetype/ability definitions). - Overload Resolution:
lookup_all()Symbol Table Method: AddedUniScopeNode.lookup_all(name, deep)which returns the primary symbol plus all overloads for a given name. The type evaluator andClassType.lookup_member_symbolnow use this centralized method instead of directly accessing the internalnames_in_scope_overloaddict, improving encapsulation and consistency of overload handling. - Remove Dead
expr_typeString Type Representation: Eliminated the legacyExpr._sym_typestring field andexpr_typeproperty which were initialized to'NoType'and never written to. All consumers (tree printer,clean_type, native IR gen fallback) now use the structuredTypeBaseobject (Expr.type) set by the type checker, providing accurate type display in AST dumps and IDE hover. - Exception Hierarchy for NA and CL Backends: Added hierarchy-aware exception matching to both native (LLVM) and client (ES) codegen.
except ArithmeticErrornow correctly catchesZeroDivisionError,except LookupErrorcatchesKeyError/IndexError, etc. NA backend OR-chains descendant type IDs at compile time; CL backend usesinstanceofchecks against a full Error subclass hierarchy (_jac.exc.*). All runtime error throws in the JS runtime now use typed exceptions (e.g.,_jac.exc.ValueError,_jac.exc.KeyError) instead of plainError. Includes cross-backend equivalence tests for both explicitraiseand implicit runtime errors (division by zero, index OOB, missing keys). - Native Memory Management: Reference Counting Replaces Boehm GC: Replaced the external Boehm GC (
libgc) dependency with a self-contained reference counting scheme. All heap allocations use an 8-byte RC header (rc_alloc), container data arrays use plainmalloc/free, and type-specific destructors are emitted for lists, dicts, sets, and archetypes. String literals are copied into RC-managed memory on use. Old values are released on variable reassignment and container growth paths free old data arrays. This eliminates thelibgcsystem dependency entirely -- the native compiler only requireslibc. - Fix: string escape : Support string escape decoding.
- Fix: Py2Jac String Escapes: Fixed
py2jacto correctly preserve escape sequences in strings, including hex (\x1b), octal (\033), and standard escapes (\n,\t). Previously these were being lost or corrupted during conversion. jac nacompileMach-O Support (macOS arm64): Extendedjac nacompileto produce standalone Mach-O executables on macOS arm64, in addition to the existing ELF support on Linux. Includes a pure-Python Mach-O linker (macho_linker.jac) that handles GOT construction, stub generation, rebase/bind opcodes, and ad-hoc code signing with SHA-256 page hashes. Platform is auto-detected -- the samejac nacompile program.na.jaccommand works on both Linux and macOS.- Type Narrowing Improvements: Fixed scope-based narrowing for AND expressions (
isinstance(x, T) and x.member), assignment narrowing (a = 90narrowsa: int|Nonetoint), guard patterns (if not isinstance(x, T): return), member access chains, truthiness checks, and assert statements. Else-branch no longer incorrectly inherits true-branch narrowings. - Union Type Member Access:
x.namewherex: Dog | Catnow resolves member types and enables go-to-definition. - IDE Hover Types: Function parameters and
hasvars now display types on hover. - Fix: Bug Fix: Stop appending lint warnings to
py2jacconverted files. - Structured GitHub Issue Forms: Replaced blank markdown issue templates with guided YAML forms, making it easier to submit well-structured bug reports, feature requests, and docs issues.
- 1 Minor refactor/change.
- Native Codegen: Split-File Chess Engine & Major IR Gen Fixes: Enabled complex multi-file native applications (declaration
.na.jac+ implementation.impl.jac) by fixing 10+ IR generation bugs. - Native Auto-Promotion (
--autonative): Regular.jacmodules can now be automatically promoted to native (LLVM JIT) execution without requiring the.na.jacextension. - Native Compiler: Constructor
initMethod Auto-Invocation: Fixed a bug where object constructors with positional parameters were not automatically calling theinitmethod. Now,initis properly invoked with constructor arguments during object instantiation, enabling proper initialization of objects with parameterized constructors. jac nacompileaccepts.jacfiles:jac nacompilenow auto-promotes compatible.jacfiles to native compilation, with a clear error message when a file uses unsupported constructs.- Native
sys.argvandsys.exit()Support: Native programs can now access command-line arguments viaimport sys; args = sys.argv;and exit with a status code viasys.exit(code).sys.argvis alist[str]whereargv[0]is the program/binary name. Works with bothjac run --autonativeand standalone binaries compiled viajac nacompile. - Native Compilation Reference Documentation: Added a comprehensive reference page to the docs covering inline
na {}blocks, Python-native interop, standalone binaries viajac nacompile,--autonativeauto-promotion, the type system, all supported language features, C library interopsys.argv/sys.exit(), and platform support. - Fix: Native Global Empty Dict/List Init Null Pointer: Declaring a module-level global with an empty dict or list literal (
glob x: dict[str, int] = {}) no longer leaves the global as a null pointer. Any subsequent dict/list operation would previously segfault; the compiler now falls back tohelpers["new"]()to produce an initialised empty container. - Fix: Native Codegen Crash on Omitted Default Parameters: Calling a method or free function while omitting trailing default-valued parameters (e.g.,
obj.method(x)wheremethoddeclaresparam: int = 0) no longer crashes the compiler withlist index out of rangeinbuilder.call. Missing arguments are now filled from the AST default expressions before the call is emitted. - Fix: Native
str.replace(old, new, count)Count Argument Ignored: The thirdcountparameter tostr.replaceis now respected. Aremainingcount phi is threaded through the replacement loop and decrements on each substitution; when it reaches zero the rest of the string is copied unchanged. Omitting the argument (or passing a negative value) retains the replace-all behaviour. - Fix: Native
for (k, v) in d.items()Iteration: Dict.items()iteration in native codegen was silently elided. Fixed by adding a__dict_get_valindex helper (mirroring the existing__dict_get_key) and a dedicated items-loop path in_codegen_forthat detects thed.items()method-call pattern, binds both loop variables, and emits a standard index-based loop. - Fix: Native
for c in strString Iteration:for c in strin native codegen previously crashed with a type-mismatch error (%"List.ptr"* != i8*) because the string variable was misclassified aslist[ptr]and routed through the list helpers. Fixed by adding a dedicated string-iteration branch in_codegen_forthat detectsi8*collections, callsstrlenfor the loop bound, and yields each character as an RC-managed single-char string via the existing_codegen_string_indexhelper.
jaclang 0.11.2#
- Improved Memory Efficiency for Large Graphs: Jac now uses lazy loading for graph data in MongoDB/Redis, nodes and edges are fetched only when accessed, instead of loading the entire graph upfront.
- Fix: Impl File Import Resolution: Impl files (
.impl.jac) can now access imports from their parent.jacfile without requiring duplicate import statements. Also fixed internal builtins imports (likeSupportsAdd,types) incorrectly being visible to user code. - Fix: Union of Subclasses Assignable to Base Class: Fixed type checker rejecting valid assignments where a union of subclasses (e.g.,
Dog | Cat) is passed to a parameter expecting the base class (e.g.,Animal). This commonly occurs after match statement narrowing and now works correctly. - Fix: Compound AND Narrowing: Multiple isinstance checks in the same AND expression now narrow to the most specific type. Example:
isinstance(x, BaseNode) and isinstance(x, CFGNode)correctly narrowsxtoCFGNodeinside the if block. - Fix: Progressive Narrowing in AND Expressions: Earlier isinstance checks in an AND expression now narrow the type for subsequent parts. Example:
isinstance(x, CFGNode) and x.bb_outworks correctly becausex.bb_outseesxasCFGNode. - Fix: Assert isinstance Type Narrowing:
assert isinstance(x, T)now narrows the type ofxtoTfor subsequent statements. Example:assert isinstance(x, CFGNode); x.bb_out;works correctly. - Fix: Type Narrowing for Inheritance-Based isinstance: Fixed
isinstance(nd, SubClass)not narrowing the type when the variable is declared as a base class (e.g.,nd: BaseNode). Previously, type narrowing only worked with union types; now single-class types are correctly narrowed to their subclass after isinstance checks. - Fix: Native Global Pointer Variables Collected by GC: MCJIT global variables (e.g.
glob WHITE_SYMBOLS: dict[...]) live outside Boehm GC's scanned memory, causing global dicts/lists/objects to be freed after enough allocations trigger a collection. Fixed by emittingGC_add_rootscalls for every pointer-typed global after initialization. - Fix: Native Dict Tuple Key Comparison: Dict key comparison for tuple/struct pointer types used pointer equality instead of structural comparison, so two separately-allocated tuples with the same values would never match. Fixed by using
memcmpfor tuple keys, matching the existing pattern in set helpers. - Match Case Type Narrowing: The type checker now narrows variable types inside match cases based on the pattern being matched. For example,
case MyClass():narrows the matched variable toMyClass, and union patterns likecase A() | B():narrow toA | B. - Fix:
jac create --usewith jac-scale endpoints: Fixedjac create --use <URL>failing when the URL points to a jac-scale@restspecendpoint. The template loader now unwraps theTransportResponseenvelope ({data: {result: {...}}}) before parsing the jacpack JSON. - Fix: Formatter Line-Breaking, Comment Spacing, and DocIR Generation: Improved
jac formatline-breaking by accounting for trailing sibling width when deciding group breaks, fixed budget tracking after newlines, preserved original source spacing for inline comments, added proper indentation for ternary (if-else) continuation lines, among others. jac nacompile-- Standalone ELF Binaries: Newjac nacompileCLI command compiles.na.jacfiles to standalone ELF executables with no external compiler or linker required. Uses llvmlite's code generator to emit object code and a pure-Python ELF linker (elf_linker.jac) to produce dynamically-linked ELF binaries against libc/libgc. The_startentry point is written in pure LLVM IR (zero inline assembly), making the entire pipeline architecture-agnostic. Includes automatic GC fallback (rewritesGC_malloctomallocat the IR level when libgc is unavailable). Usage:jac nacompile program.na.jacorjac nacompile program.na.jac -o mybin.- Fix: py2jac docstring conversion: Fix py2jac to correctly convert
Docstringswith escape sequences. - 2 Minor refactors/changes
jaclang 0.11.1#
- Perf: Type Narrowing Optimization: Fixed exponential slowdown in
jac checkwith manyifstatements (~1 min → ~2s). Member access now uses narrowed types and reports errors for invalid attribute access onNone. - Import Path Alias Resolution: The module resolver now supports path aliases configured in
[plugins.client.paths]injac.toml. Aliases like@components/Buttonare resolved to their filesystem paths before standard module lookup, enabling cleaner imports in client-side Jac code. - Native Codegen: C Library Import Syntax (
import from "lib" { def ...; }): Added first-class parser and IR generation support for importing C shared libraries. Declarations inside the braces are parsed as extern function signatures (no body), producing LLVMdeclarestatements that MCJIT resolves from the loaded.so/.dylib. Includes fixed-width C-compatible types (i8,u8,i16,u16,i32,u32,i64,u64,f32,f64,c_void) and automatic type coercion (i64↔i32, f64↔f32) at call boundaries. - Native Codegen: C Struct Value-Type Coercion: C structs declared inside
import from "lib" { obj Color { has r: u8, g: u8, b: u8, a: u8; } }blocks are used as normal Jac objects (heap-allocated, pointer semantics) but automatically coerced to C value semantics at call boundaries. Small integer-only structs (<=64 bits) are ABI-coerced to register-sized integers (e.g.,Colortoi32), matching the x86_64 SysV calling convention. - Fix:
jac formatUnicode Error on Windows: Fixed'charmap' codec can't encode charactererror when formatting files with emojis or non-ASCII text on Windows. - Remove Vendored pluggy and interegular: Replaced the vendored
pluggylibrary (~1,700 lines) with a lightweight custom plugin system (jaclang/plugin.py, ~200 lines) that provides the same hook spec/impl/dispatch API. Removed the unused vendoredinteregularlibrary (~2,200 lines). - Scheduler Support for Walkers and Functions: Added a built-in scheduler that enables time-based execution of walkers and functions using the
@scheduledecorator. Supports three scheduling modes: one-shot (date), recurring (interval), and cron-based (cron). Scheduling is classified as either static or dynamic via thetriggerparameter (ScheduleTrigger.STATICorScheduleTrigger.DYNAMIC). Static scheduling (the default) runs tasks on a background daemon thread duringjac startand automatically discovers@schedule-decorated walkers and functions. Dynamic scheduling requiresjac-scale. Scheduled walkers and functions are not exposed as API endpoints. - Enhanced jac check output: The
jac checkcommand now provides a more detailed and user-friendly output format, including file progress, failure details, and timing information. - Fix: Grammar Extraction Well-Formedness: Improved
jac grammarto produce well-formed EBNF by pruning unreachable rules, detecting savepoint-backtrack patterns, suppressing duplicate guard tokens, and handling broader condition forms (check_name, nested boolean exprs,orchains). - LSP: ReadWriteLock for Concurrent Queries: Replaced the single
RLockin the language server with a writer-priorityReadWriteLock, allowing hover, completion, go-to-definition, and other read operations to run concurrently without blocking on type checking. Also fixed several race conditions where shared state (mod.hub,sem_managers) was accessed without any lock. - Fix: Self-Member Attributes in Impl Files:
self.x = valueassignments in impl blocks (including separate.impl.jacfiles) now correctly register as archetype attributes. Go-to-definition and type checking work seamlessly without requiring explicithasdeclarations. - 3 Minor refactor
- Fix: Unparenthesized Lambda with Keyword Parameter Name: Fixed
parse_lambda_paramrejecting keyword tokens (e.g.,props,root,here) as unparenthesized lambda parameter names. The parser now correctly accepts special var ref keywords and emits a targeted error for other keywords with an escape hint. - Refactor: Merge
JacSerializerintoSerializer: Removed theJacSerializerwrapper class fromruntimelib.serverand merged its API-response behavior intoSerializervia a newapi_mode: bool = Falseparameter. CallSerializer.serialize(obj, api_mode=True)to get clean API output with_jac_type,_jac_id, and_jac_archetypemetadata onArchetypeobjects (previously done byJacSerializer). Storage backends continue to useSerializer.serialize(obj, include_type=True)unchanged. Import fromjaclang.runtimelib.serializer. This eliminates a redundant wrapper class with no unique serialization logic. Addedsocial_graph.jacexample fixture in jac-scale demonstrating native persistence anddb.find_nodes()for querying persisted nodes with MongoDB filters.
jaclang 0.11.0#
- Automatic Endpoint Caching: The compiler now statically analyzes walker and server function bodies to classify endpoints as readers or writers, and propagates this metadata (
endpoint_effects) through theClientManifestto the client runtime. Reader endpoints are automatically cached on the client side, and writer endpoints auto-invalidate overlapping reader caches based on shared node types -- zero developer configuration required. - HMR Server-Side Reloading Refactor: Improved HMR functionality with better handling of
.impl.jacfiles and optimized caching to avoid unnecessary recompilations during development - Builtin
llmName:llmis now a builtin name in the Jac runtime, enablingby llm()syntax without requiring an explicit import orglob llmdeclaration. The runtime provides a stub default that plugins (e.g. byllm) override with a fully configured LLM model. - Fix: Impl Block Variables Lose Type Info Across Files: Fixed a bug where variables declared with
has(e.g.,has tasks: list = []) in a component lost their type annotation when referenced from a separate.impl.jacfile. The shadowed symbol fixup was also optimized from O(N*M) to O(N+M) by batching lookups and traversing impl body nodes in a single pass. - Fix: Duplicate
__jacCallFunctionImport in.cl.jacwith.impl.jac: Fixed the ES codegen emitting duplicateimport { __jacCallFunction } from "@jac/runtime"when both a.cl.jacfile and its.impl.jacannex usesv import. Child module imports are now deduplicated by source path during merge. - Variant Module Annexing (
.sv.jac,.cl.jac,.na.jac): A module can now be split across variant files that are automatically discovered, compiled, and merged. Givenmain.jac, any siblingmain.sv.jac,main.cl.jac, ormain.na.jacfiles are annexed as variant modules with their respective code contexts (SERVER, CLIENT, NATIVE). - Fix: Bare Impl Files Not Matched to Variant Modules: Fixed
discover_annex_filesrejecting bare annex files (e.g.,foo.impl.jac) when the source is a variant (e.g.,foo.cl.jac) with no plainfoo.jachead. Bare annex files now match any variant source unless a bare.jachead exists that would claim them. - Fix: Bare-Dot Relative Import (
from . import x) Not Resolved: Fixedimport from . { x }silently resolving toUnknownType. The import path is now computed directly from the current file's directory, ensuring sibling modules are correctly found and type-checked. - Fix:: update the jac-check command to print the file names of the files that failed to have clean error message.
- 2 Small refactors/changes.
- ES Codegen: Near-Complete Primitive Test Coverage (92%): Added cross-backend equivalence tests for 110 additional primitive emitter interfaces (275/299 total), covering float operators, complex arithmetic, bytes methods (with full
_jac.bytesruntime namespace), set/frozenset algebra and operators, and extra builtins. Fixed column-awareexpandtabsfor str and bytes,complex.pow/complex.eqmixed-type handling, andascii()quoting to match Python semantics. - Refactor: Native Jac Generics in Primitives: Replaced Python-style
Generic[(V, C)]with native Jac bracket syntax[V, C]across all emitter classes inprimitives.jacand removed unusedTypeVar/Genericimports. - Fix:
jac formatMisplaces Comments Around Generic Type Params: Fixedjac formatmoving section comments (e.g.,# === String Types ===) into the[V, C]brackets of the preceding class. The parser was generating synthetic comma tokens between type parameters with incorrect source locations;parse_type_paramsnow preserves the real comma tokens from the source. - 4 Minor refactors/chages
- Native Codegen: Expanded Primitive Coverage: Added 45 new LLVM IR emitter implementations and inline codegen across 8 emitters.
- Native Codegen: Expanded Primitive Coverage: Added 17 new LLVM IR emitter implementations across 6 emitters: IntEmitter (
conjugate,bit_length,bit_count), FloatEmitter (conjugate,is_integer,op_floordiv), StrEmitter (title,rfind,ljust,rjust,zfill), SetEmitter (clear,discard,copy), ListEmitter (extend,insert), and BuiltinEmitter (bool). Fixed string comparison operators (<,>,<=,>=) viastrcmpand added float floor division (fdiv+floor). Fixedrfindinfinite loop on empty substring. 108/299 implemented (36%), 105/299 tested (35%). - ES Codegen: Comprehension Support (Set, Dict, Nested Loops): Added
SetCompr→new Set(...),DictCompr→Object.fromEntries(...), and nested loop →.flatMap()chain support to the ES transpile pass. Fixed arrow function parenthesization for destructuring params (([k, v]) => ...). Includes 17 new test cases covering all comprehension types. - Fix: Walker
result.reportsin CLI Mode: Fixedreportkeyword not populatingresult.reportswhen running walkers viajac runorjac test. jac formatSupport for Generic Syntax:jac formatnow correctly handles native generic type parameters (class Foo[V, C]) and type aliases (type Result[T, E] = T | E;). Previously, formatting would lose type parameter names and produce broken output.- Bootstrap Compiler (jac0) Native Generic Support: Extended the jac0 bootstrap transpiler to parse and emit PEP 695 generic class syntax (
class Foo[T, V](Base)) and type alias statements (type Alias[T] = Expr), enabling jac0core infrastructure files to use native Jac generics instead of Python-styleGeneric[T]/TypeVarpatterns. - Migrate jac0core and Compiler Passes to Native Generics: Replaced
Generic[(T, V)]inheritance andTypeVardeclarations with native[T, V]syntax acrosstransform.jac,unitree.jac,base_ast_gen_pass.jac, and allTransform[(X, Y)]subscription sites in the compiler pipeline.
jaclang 0.10.5#
- Fix:
sv importofdef:pubFunctions Generates RPC Stubs: Fixedsv import from module { func }in.cl.jacfiles not generating fordef:pubserver functions. - Fix: Type Narrowing Infinite Loop on Large Files: Fixed
jac checkhanging indefinitely on large.jacfiles (e.g. standalone.impl.jacmodules). The backward CFG walk in_compute_narrowed_athad no depth bound, causing combinatorial explosion when the module-level CFG contained hundreds of basic blocks. Added a depth limit to the walk; narrowing beyond the limit conservatively returns the declared type.
jaclang 0.10.4#
jac check/lint --ignoreMulti-Arg & Wildcard Support: Enhanced--ignoreflag to accept multiple space-separated patterns (--ignore dir1 dir2 dir3) instead of comma-separated strings. Added wildcard support using glob patterns (e.g.,--ignore "jac-*" test) for flexible directory matching.- CI: Type Check All Jac Files: Updated CI workflow to run
jac checkon all.jacfiles (excluding test fixtures and error cases) in preparation for removing.jacignore. - Fix:
_jacES Runtime Correctness: Fixedstr.splitwithmaxsplitto keep the remainder (matching Python behavior),dict.eqto compare key-by-key instead of order-dependentJSON.stringify, and builtin dispatch (e.g.,sorted(key=lambda...)) to correctly pass keyword arguments to the runtime. - Fix: Remove Dead
absPrefix Modifier: Removed the unusedabsprefix on archetypes (abs obj Foo { }) from the grammar and parser. The prefix was parsed but silently discarded; archetype abstractness is computed from contained abstract abilities. Theabskeyword remains valid only as an ability body terminator (can foo() abs;). - ES Codegen: Expanded Primitive Coverage: Added
bool()with Python truthiness semantics (empty list/dict/set are falsy),range()builtin (supportsfor i in range(n)),slice()constructor,bytearray()constructor, dedicatedBoolEmitterfor correct&/|/^bool-returning bitwise ops, enhancedformat()with format-spec support (f,d,b,o,x,e,%, width, alignment), and fixedint()to handle booleans and floats correctly viaMath.trunc(Number(x)). - Fix: Lexer Infinite Loop on Malformed JSX: Fixed three infinite-loop scenarios where the lexer would hang forever when hitting EOF inside a non-NORMAL mode (JSX content, JSX tag, or f-string). Added a stuck detector in
tokenize()that forces EOF when the lexer stops advancing or overshoots the source, preventingjac run,jac start, andjac jsfrom hanging on malformed input (e.g., unterminated JSX like<div>hellowith no closing tag). - Fix: Bare
<in JSX Content No Longer Hangs Lexer: A<character in JSX content that does not start a valid tag (e.g.,<--) is now consumed as text instead of causing an infinite loop. The text scanner only breaks on<when the next character forms a real JSX construct (</,<>, or<+ identifier). - Fix: Grammar Extraction Accuracy: Fixed multiple issues in
jac grammaroutput:atomic_chainandjsx_attributesnow show*repetition,compareno longer duplicates operators,assignment_with_targetcorrectly extracts ternary expressions, excessive top-level?wrapping is stripped from multi-branch rules, f-string tokens use proper quoting, and addedGPlus(one-or-more+) grammar expression type. - 1 Minor refactors/changes
jaclang 0.10.3#
- Fix: Type Narrowing in Loops: Fixed type narrowing loss in loops and also improved CFG accuracy.
- Fix: Config Discovery from Target File Path: Fixed
jac startcommands to discoverjac.tomlfrom the target file's directory instead of the current working directory when using absolute/relative paths. - Fix: Unbound Method Call Type Checking: Fixed "Parameter already matched" error when calling parent class methods with explicit
selfin inheritance patterns (e.g.,ParentClass.init(self, name=name)). The type checker now correctly handles unbound method calls onobj/node/walker/edgetypes whereselfis implicit. - Enhanced Type Narrowing: Extended CFG-based type narrowing to support additional patterns: parenthesized isinstance
(isinstance(x, T)), NOT expressionsnot isinstance(x, T), compound AND/OR conditions, isinstance with tuple of typesisinstance(x, (A, B)), truthiness narrowingif x:(excludes None), literal equalityx == "lit", and inheritance-aware isinstance that correctly narrows to subclasses in unions. - Fix: Bare Callable Type Annotation: Using
Callablewithout type parameters (e.g.,fn: Callable) no longer causes type errors. - Type Inference for Tuple Unpacking: The type evaluator now infers element types for variables in tuple/list unpacking assignments (e.g.,
(row, col) = pos;wherepos: tuple[int, int]), eliminating the need for explicit pre-declarations before unpacking. Types that cannot be inferred still require explicit annotations. - Fix: Display detailed syntax error messages: Display detailed syntax error messages in
jac runandjac startcommands instead of generic import errors. - Enum Type Checking: Enums now have proper type checking. Accessing
.namereturnsstr,.valuereturns the correct type based on your enum values (int or str). Passing wrong types to functions expecting enums now shows type errors. - Fix: False type errors on class-based enums: Classes inheriting from
StrEnum,IntEnum, orIntFlagno longer produce false type errors. - Fix: LSP features in nested impl blocks: Go-to-definition, hover, and syntax highlighting now work correctly for symbols inside if/while/for statements within impl blocks.
- Fix: JS useState scope bug: Fixed
hasvars incorrectly triggeringsetState()in sibling functions with same variable name. - Fix: Inherited field default override: Fixed false "missing required parameter" error when a child class provides a default for a parent's required field.
parametrize()Test Helper: Added aparametrize(base_name, params, test_func, id_fn=None)runtime helper that registers one test per parameter viaJacTestCheck.add_test().- Generic Primitive Emitter Interface: Refactored the primitive codegen emitter contracts. Emitters are now stateless singletons with per-call context, and dispatch uses typed instance methods instead of static class-as-namespace calls.
- Human-Readable Tokens in Errors and Grammar Spec: Parser error messages and
jac grammaroutput now display actual token text ("{","if",";") instead of internal names (LBRACE,KW_IF,SEMI), making syntax errors and the grammar specification much more readable. - Support Bare
typeParameter Assignment: Functions withtypeparameter annotations now correctly accept class types as arguments (e.g.,process(cls: type)can be called withprocess(MyClass)). - Operator Primitive Dispatch for ES Codegen: Wired type-aware operator dispatch into the JavaScript code generation pass. Binary, comparison, unary, and augmented assignment operators now query the type evaluator and delegate to the appropriate primitive emitter, producing correct JS semantics for Python-style operators (e.g.,
list + listemits spread concatenation[...a, ...b],str * nemits.repeat(n),x in listemits.includes(x)). - 3 Minor refactors/changes.
- Fix: Lexer
<Comparison vs JSX Tag Disambiguation: Fixed an infinite loop wherei<pointsin a for-loop caused the lexer to enter JSX tag mode. The lexer now tracks the previous token to distinguish<as a comparison operator (after values) from a JSX opening tag (after keywords likereturn, operators, or delimiters). - Fix: Quoted JSX Text Produces Invalid JS: Fixed JSX text containing quote characters (e.g.,
<p>"text"</p>) generating invalid double-double-quoted JavaScript (""text""). Inner quotes are now properly escaped in the emitted JS string literals. - Fix:
unittest.mock.patchCompatibility in Jac Tests: Fixedunittest.mock.patchnot intercepting calls in Jac test blocks. - Modern Generics:
typeAliases & Inline Type Parameters: Added PEP 695-styletypealias statements (type JsonPrimitive = str | int | float | bool | None;) and inline generic type parameters on archetypes (obj Result[T, E = Exception] { ... }). Supports bounded type vars (T: Comparable), default type values, and recursive type aliases. Compiles to native Python 3.12ast.TypeAliasandast.TypeVarnodes. - Perf: Precompiled Bytecode for Zero Cold Start: Ship precompiled
.jbcbytecode per Python version (3.12, 3.13, 3.14) inside each Jac package wheel. Cold start (jac purge && jac --help) drops from ~29s to <1s. jac runScript Arguments:jac runnow passes arguments to the script using Python-like semantics - everything after the filename goes to the script (e.g.,jac run script.jac arg1 arg2), accessible viasys.argv[1:]. Jac flags like--no-cachemust come before the filename, just likepython -O script.py.- Fix: Operator Precedence for Bitwise vs Logical Operators: Fixed operator precedence so bitwise operators (
|,^,&,<<,>>) bind tighter than logical operators (or,and,not), matching Python's semantics. Previously3 & 1 == 1was parsed as3 & (1 == 1)instead of(3 & 1) == 1. - Fix:
_jacPrimitive Runtime for ES Codegen: Fixed unification of compiled JavaScript that uses Python-like primitive operations (list.sort(key=...),list.count(),str.capitalize(),int % int, etc.). - 2 Minor refactors/changes.
jaclang 0.10.2#
- Unified Primitive Codegen Interface: Added abstract emitter contracts (
primitives.jac) for all Jac primitive type methods and builtin functions. Each compilation backend (Python, ECMAScript, Native) must implement these interfaces, ensuring consistent primitive support across all code generation pathways. Python, JS, and Native backend implementations provided. - Pytest Plugin for Native Jac Tests: Added a
pytest11entry-point plugin (jaclang.pytest_plugin) that discovers and runstestblocks in.jacfiles alongside Python tests with zero configuration. Migrated ~79 language integration tests and 8 compilation tests from Python to native Jactestkeyword. - Perf: Bootstrap Bytecode Cache: Cache the jac0-transpiled bytecode for jac0core modules on disk, eliminating ~200ms of repeated transpilation on every invocation.
jac purge -fclears both caches. - Perf: Cache
len()in Lexer/Parser Hot Paths: Cached source and token list lengths in the jac0 bootstrap transpiler and the RD parser/lexer, eliminating ~1.8M redundantlen()calls per startup. - 3 Minor refactors/changes.
- Fix:
jac grammarCommand Broken Path: Fixed thejac grammarCLI command. - Grammar Extraction Pass Improvements & Spec Snapshot Test: Improved
jac grammarextraction accuracy for negated-check loops, optional dispatch branches,while Trueparse-and-break patterns, and standalonematch_tokcalls. Added a golden-file snapshot test (jac.spec) that validates extracted grammar rules against a checked-in spec, catching unintended grammar drift on every CI run. - Black-style Grammar Formatting: Replaced alignment-based
jac grammarformatting with Black-style fixed 4-space indentation, blank lines between rules, and 88-char line width. Uses a recursive tree-based formatter instead of the previous string-based wrapping. - RD Parser Spec Convergence: Improved strictness of jac parser and specification.
- 4 Minor refactors/changes.
jaclang 0.10.1#
- Stale Persistence Cache Handling: Resolved an issue where running different Jac applications sequentially caused NodeAnchor [UUID] is not a valid reference! errors due to stale anchors persisting in SQLite/MongoDB/Redis backends. The runtime now validates that an anchor’s archetype still exists before loading it and automatically removes invalid entries. This removes the need for manual cache deletion in normal workflows.
jac purgeCommand: Addedjac purgeto clear the bytecode cache. Works even when the cache is corrupted.format_build_errorPlugin Hook: Addedformat_build_error(error_output, project_dir, config)hook toJacMachineInterface, allowing plugins to provide custom error formatting for client bundle build failures. The default implementation returns raw error output; plugins likejac-clientcan override to display structured diagnostics.- Fix: MTIR scope key uses file stem for portability: Fixed MTIR scope key generation to use only the file stem (filename without extension) instead of path-relative module names. This ensures consistent scope keys across different execution environments (local vs Docker) and enables compiled bytecode to be portable across different directory structures.
- Fix: False Type Errors in Nested Functions: Fixed incorrect type checking errors when using nested functions inside
implblocks. Nested functions now correctly validate their return statements against their own return type instead of the outer function's return type. - Fix:
jac startOutput Ordering: Server startup messages now appear after compilation completes instead of before, ensuring users see build progress first and "Server ready" only when the server is actually accepting connections. Addedon_readycallback parameter toJacAPIServer.start()for consistent startup message handling across stdlib and jac-scale servers. jac format --checkMode: Added a--checkflag to validate formatting without modifying files. Exits with status 1 if files need formatting or have syntax errors.- Fix:
jac startOutput Ordering: Server startup messages now appear after compilation completes instead of before, ensuring users see build progress first and "Server ready" only when the server is actually accepting connections. - PWA Build Detection: The stdlib server now detects existing PWA builds and serves Vite-hashed client files (
client.*.js) correctly. - Fix: Serve JSON and JS files as static assets: Added
.jsonand.jsto the list of recognized asset extensions, fixing PWAmanifest.jsonandsw.jsserving. - Code refactors: Backtick escape, TS cleanup, etc.
- Bootstrap Compiler (
jac0): Added a single-file Python transpiler (jac0.py, ~1900 lines) that compiles the Jac subset produced bypy2jacinto equivalent Python source code. This closes the bootstrap loop. - RD Parser: Broad Grammar Parity Fixes: Fixed 16 grammar gaps in the recursive descent parser, raising walk-check match rate from 95.3% to 98.7%.
jac --versionShows Installed Plugins: The version banner now lists all installed Jac plugins with their versions, making it easy to see the full environment at a glance.- Support Go to Definition for Inherited Members: "Go to Definition" now works correctly for inherited methods and attributes on classes without an explicit parent class.
- Type Checker Improvements:
- Callable Type Annotation Support: Added full support for
Callable[[ParamTypes], ReturnType]type annotations. Includes gradual callable form (Callable[..., T]), parameter contravariance, return type covariance, automatic self/cls filtering for methods and classmethods, and validation that extra source parameters have defaults. - Fix: Type Checker Crashes: Fixed crashes when type-checking default/star imports (
import from mod { default as X }) and walker entry/exit handlers. - Fix: LiteralString Type Support: Added
LiteralStringclass to the type checker, improving binary operator chain handling and ensuring type compatibility betweenLiteralStringandstrtypes. - Type Checking for
super.init()Calls: Added validation forsuper.init()calls, catching argument errors against parent class initializers with proper MRO resolution. - Fix: Native Code Cache False Positive: Fixed a bug where "Setting up Jac for first use" appeared on every run instead of only the first time.
- Fix: LiteralString String type Compatibility: LiteralStrings and Strings are now type compatible with type checker.
- Lark Parser Removal: Replaced the Lark-based parser with a hand-written recursive descent parser as the default. Deleted
jac_parser.py,jac.lark,lark_jac_parser.py, and the vendoredlark/directory. All 110 language tests, 438 format tests, and 15 LSP server tests pass with the new parser. - 1 Small Refactors
- Docs update: return type
any->JsxElement - Fix: Spurious Write Access Warning on System Root During Sync
- 3 Small Refactors
jaclang 0.10.0#
- KWESC_NAME syntax changed from
<>to backtick: Keyword-escaped names now use a backtick prefix (`node) instead of the angle-bracket prefix (<>node). All.jacsource files, the lexer, parser, unparse/DocIR passes, and auto-lint rules have been updated accordingly. - Remove Backtick Type Operator: Removed the backtick (
`)TYPE_OPtoken andTypeRefAST node from the language. TheRoottype is now referenced directly by name (e.g.,with Root entryinstead ofwith `root entry). Filter comprehension syntax changed from(`?Type:field==val)to(?:Type, field==val).Rootis automatically imported fromjaclibwhen used in walker event signatures. APIProtocolBuiltin Enum: AddedAPIProtocolenum (HTTP,WEBHOOK,WEBSOCKET) as a builtin, replacing the booleanwebhookflag inRestSpecswith a typedprotocolfield. Use@restspec(protocol=APIProtocol.WEBSOCKET)directly without imports.- Native Compiler: Cross-Module Linking: Native
.na.jacmodules can now import and call functions from other.na.jacmodules. The compiler performs LLVM IR-level linking enabling modular native code organization withimport from module { func1, func2 }syntax. - LSP Debounced Type Checking: The language server now waits for a brief pause in typing (300ms) before starting analysis, eliminating lag during rapid edits.
- Fix: LSP Multi-File Race Condition: Fixed a race condition when switching between files that could cause stale diagnostics.
jac grammarCommand: Added ajac grammarCLI command that extracts the Jac grammar directly from the recursive descent parser's AST and prints it in EBNF or Lark format. Usejac grammarfor EBNF output,jac grammar --larkfor Lark format, and-o <file>to write to a file. Powered by a newGrammarExtractPasscompiler pass that analyzesparse_*method implementations to reconstruct grammar rules from token-consumption patterns and control-flow structures.- Type Checker Improvements: Fixed several systemic issues in the type checker:
UnionTypeoperands are now handled correctly in connection operations (++>,-->) and instance conversion; the_get_enclosing_classhelper no longer silently crashes due to a variable name bug; unannotated functions no longer produce false return-type errors; and a new rule requires return type annotations on top-level functions that return a value (barereturnandreturn Noneremain annotation-free). - Support custom Vite Configurations to
devmode: Added support for custom Vite configuration fromjac.toml. - Native Compiler: Multi-Parameter Print Fix: The
print()builtin in native code now correctly handles multiple arguments, printing them space-separated on a single line (e.g.,print("x =", 10, "y =", 20)outputsx = 10 y = 20). Previously only the first argument was printed. - Fix: HMR server-side
sys.modulesrefresh: Imported modules now correctly reload in--devmode by clearing stalesys.modulesentries across the project. - Auto-install watchdog for
--devmode:jac start --devautomatically installswatchdogif missing, eliminating the manualjac install --devstep. - Trim Redundant Parser Test Fixtures: Removed 46 redundant entries from the micro parser test suite by eliminating exact duplicates across directories, near-identical examples, and files that add no unique syntax coverage, reducing the fixture list from 481 to 435 files.
- RD Parser Grammar Gap Fixes: Fixed 9 coverage gaps in the recursive descent parser to match the Lark grammar spec:
skipstatement,@=operator,nacontext blocks, typed context blocks (-> Type { ... }),isseparator insemdefinitions,implinside archetype bodies, raw f-strings (rf"..."), parenthesized yield expressions, and*args/**kwargsin lambda parameters. - Refactor: Centralized NormalizePass: Extracted the ~104
normalize()methods from individual AST node classes inunitree.pyinto a dedicatedNormalizePassimplemented in Jac (normalize_pass.jac+impl/normalize_pass.impl.jac). This keeps AST classes purely structural and follows the same self-hosted pass pattern used byUnparsePassand other tool passes. - RD Parser: Yield in Assignments & Grammar Extraction Improvements: The RD parser now correctly handles
x = yield exprin assignments. Thejac grammarextraction pass was improved to accurately display binary operator rules (e.g.,logical_ornow showslogical_and (KW_OR logical_and)*instead of the incorrectlogical_and KW_OR*). - RD Parser: Async, Impl & F-String Gap Fixes: Fixed 6 more coverage gaps in the recursive descent parser:
async withstatements, async comprehensions (list/set/gen),async fortoken in AST kid lists,implwith event clause missingwithtoken,implby-expression extrabytoken, and nested{expr}inside f-string format specs (e.g.,f"{value:{width}}"). - RD Parser: Enum & Match Pattern Gap Fixes: Fixed 3 more coverage gaps: multistring (concatenated string literals) in match literal patterns,
py_code_block(inline Python) in enum blocks, andfree_code(with entryblocks) in enum blocks. - Fix:
IsADirectoryErroron dotted imports: Fixed a crash where the compiler attempted to read directory paths as files when resolving dotted module imports (e.g.,include impl.imps). - CLI Dependency Command Refactor: Redesigned the
jac install,jac add,jac remove, and newjac updatecommands for cleaner, more consistent behavior.jac installnow syncs all dependency types including plugin-provided ones (npm, etc.).jac addrequires package arguments (no longer silently falls through to install) and errors on missingjac.toml. When no version is specified,jac addqueries the installed version and records a~=X.Ycompatible-release spec instead of>=0.0.0. The newjac update [pkg]command updates all or specific dependencies to their latest compatible versions and writes~=X.Yspecs back tojac.toml. - Fix: Fixed
config.save()to correctly persist dependency removals and git dependencies to disk. - RD Parser: Strictness Parity with Lark: Tightened the RD parser to reject constructs that the Lark grammar also rejects, closing 7 permissiveness gaps.
jaclang 0.9.15#
- Fix: Type Errors in Impl Files Now Show Correct Location: Type errors in
.impl.jacfiles now point to the actual error location instead of the declaration in the main file. - First-Run Progress Messages: The first time
jacis run after installation, it now prints clear progress messages to stderr showing each internal compiler module being compiled and cached, so users understand why the first launch is slower and don't think the process is hanging. jac addDependency Resolution:jac addnow installs all Python dependencies in a single batch, allowingpipto resolve compatible versions across interdependent packages and preventing runtime errors caused by version mismatches.- Self-Hosted Recursive Descent Parser: Added a hand-written recursive descent parser and lexer implemented entirely in Jac (
jac/jaclang/compiler/parser/). The parser is designed for native compilation with no runtime reflection - grammar rules are encoded directly in AST type definitions. Features include a 28-level expression precedence chain matching the Lark grammar, contextual lexing for f-strings and JSX, and comprehensive pattern matching support. This lays the groundwork for a fully self-hosted Jac compiler. - LSP Responsiveness During Rapid Typing: Improved editor responsiveness when typing quickly by properly cancelling outdated type-check operations.
- Native Compiler: Dictionaries and Sets: The native backend now supports
dictandsettypes with full codegen for literals,len(), key/value access, subscript assignment,inmembership testing,set.add(), and iteration over dict keys. Both integer and string keyed dictionaries are supported. Global-scope dict and set declarations are also handled. Validated with a comprehensivedicts_sets.na.jactest suite. - Native Compiler: Comprehensions: Added code generation for list, dict, and set comprehensions including nested
forclauses andiffilters. List comprehensions with conditions, dict comprehensions mapping positions to pieces, and set comprehensions collecting move targets all compile to native LLVM IR. - Native Compiler: Tuples: Tuples are now a first-class type in the native backend. Supports tuple literals, tuple indexing, tuple unpacking assignments (e.g.,
(row, col) = pos;), and tuples as dict keys and set elements. Positions throughout the chess test case are now represented astuple[int, int]. - Native Compiler: Inherited Method Wrappers: The native backend now generates wrapper functions for inherited methods, enabling vtable-based virtual dispatch to correctly resolve methods defined on base classes when called through subclass instances.
- Native Compiler: Bitwise and Extended Operators: Full support for bitwise operators (
&,|,^,~,<<,>>), power operator (**), and all augmented assignment variants (&=,|=,^=,<<=,>>=,**=,//=,%=). Hex (0x), octal (0o), and binary (0b) integer literals are also handled. - Native Compiler: Dict/Set Comprehensions and Iteration: Dict comprehensions, set comprehensions, and
for-over-dict iteration (iterating keys of a dictionary) now compile to native code. Tuple membership testing in sets (target in attacked_set) is also supported. - Native Compiler: Exception Handling: Full
try/except/else/finallysupport in the native backend. Includesraisewith exception type and message, multipleexceptclauses with type matching, bareexceptcatch-all,asbinding for caught exceptions, and nested try blocks. Exceptions use a lightweight stack-based handler model withsetjmp/longjmpunder the hood. - Native Compiler: File I/O: The
open()builtin now compiles to native code, returning aFilestruct backed by Cfopen. File methodsread(),write(),readline(),close(), andflush()are all supported. NULL handle checks are generated for failed opens. - Native Compiler: Context Managers:
withstatements compile to native LLVM IR.__enter__is called on entry,__exit__on exit (including when exceptions occur). Theasbinding form (with open(path) as f) is supported. File objects implement the context manager protocol for automatic resource cleanup. - Native Compiler: Runtime Error Checks: The native backend now generates runtime safety checks that raise structured exceptions:
ZeroDivisionErrorfor integer and float division/modulo by zero,IndexErrorfor list index out of bounds,KeyErrorfor missing dictionary keys,OverflowErrorfor integer arithmetic overflow,AttributeErrorfor null pointer dereference,ValueErrorfor invalidint()parsing, andAssertionErrorfor failed assertions. - Native Compiler: Python↔Native Interop: Added cross-boundary function call support between Python (
sv) and native (na) codespaces within the same module. Native functions can now call Python functions via LLVM extern declarations backed by ctypes callbacks, and Python code can call native functions via auto-generated ctypes stubs. - Fix:
sv importLost During Unparse in.cl.jacFiles - Fix: ESM Script Loading in Legacy Runtime: Added
type="module"to the generated<script>tag in the legacy runtime HTML renderer, matching the same fix applied in jac-client.
jaclang 0.9.14#
- Fix:
jac formatNo Longer Deletes Files with Syntax Errors: Fixed a bug wherejac formatwould overwrite a file's contents with an empty string when the file contained syntax errors. The formatter now checks for parse errors before writing and leaves the original file untouched. jac lintCommand: Added a dedicatedjac lintcommand that reports all lint violations as errors with file, line, and column info. Usejac lint --fixto auto-fix violations. Lint rules are configured via[check.lint]injac.toml. All enabled rules are treated as errors (not warnings). The--fixflag has been removed fromjac format, which is now pure formatting only.- CLI Autocompletion: Added
jac completionscommand for shell auto completion. Runjac completions --installto enable autocompletion for subcommands, options, and file paths. Supports bash, zsh, and fish (auto-install), plus PowerShell and tcsh (manual). - Centralized project URLs: Project URLs (docs, Discord, GitHub, issues) are now defined as constants in
banners.jacand reused across the CLI banner, server error messages, and help epilog instead of being hardcoded in multiple places. - Client bundle error help message: When the client bundle build fails during
jac start, the server now prints a troubleshooting suggestion to runjac clean --alland a link to the Discord community for support. - Native Compiler Buildout: Major expansion of the native binary compilation pipeline for
.na.jacfiles. The native backend now supports enums, boolean short-circuit evaluation, break/continue, for loops, ternary expressions, string literals and f-strings, objects with fields/methods/postinit, GC-managed lists, single inheritance with vtable-based virtual dispatch, complex access chains, indexed field assignment, string methods (strip, split, indexing), builtins (ord, int, input), augmented assignment operators (+=,-=,*=,//=,%=), andwith entry { ... }blocks. All heap allocations use Boehm GC. Validated end-to-end with a fully native chess game. jac runfor.na.jacFiles: Runningjac run file.na.jacnow compiles the file to native machine code and executes thejac_entryfunction directly, bypassing the Python import machinery entirely. Native execution runs as pure machine code with zero Python interpreter overhead at runtime.- LSP Semantic Token Manager Refactor: Refactored the language server's
SemTokManagerfor production robustness. Deduplicated ~170 lines of shared symbol resolution logic. -
- Automatic Version Pinning in
jac.toml: When runningjac add <package_name>without specifying a version, the detected installed version is automatically added tojac.toml, falling back to>=0.0.0if detection fails.
- Automatic Version Pinning in
- Configuration Profiles: Added multi-file configuration support with profile-based overrides. Projects can now use
jac.<profile>.tomlfiles (e.g.,jac.prod.toml,jac.staging.toml) for environment-specific settings andjac.local.tomlfor developer-specific overrides that are automatically gitignored. Files are merged in priority order:jac.toml(base) <jac.<profile>.toml<[environments.<profile>]in-file overrides <jac.local.toml. Activate a profile via--profileflag on execution commands (e.g.,jac run app.jac --profile prod,jac start --profile staging), theJAC_PROFILEenvironment variable, or[environment].default_profileinjac.toml. Thejac config pathcommand now displays all loaded config files with their priority labels. TheJAC_ENVenvironment variable is deprecated in favor ofJAC_PROFILE. - Configuration Profile Bug Fixes: Fixed several issues in the multi-profile config implementation:
storage.typekey now correctly maps to thestorage_typefield during profile merges, nested plugin configs use deep merge instead of shallow overwrite (preserving nested keys),apply_profilenow handles all config sections (build, format, dot, cache, storage, check, project, dependencies, scripts -- previously only run, serve, test, and plugins), circular profile inheritance is detected and short-circuited instead of causingRecursionError, mutable defaultconfig_filesfield replaced withNonesentinel to prevent cross-instance sharing, and config-to-CLI-args bridging added so profile values (e.g.,serve.port) correctly override argparse defaults at runtime. - JsxElement Builtin Type: Added
JsxElementbuiltin type for strict type checking of JSX expressions for client-side UI components. - 1 Small Refactors
jaclang 0.9.13#
- Configurable Lint Rules: Auto-lint rules are now individually configurable via
jac.toml[check.lint]section using a select/ignore model. ALintRuleenum defines all 12 rules with kebab-case names. Useselect = ["default"]for code-transforming rules only,select = ["all"]to enable every rule including warning-only rules,ignore = ["rule-name"]to disable specific ones, orselect = ["rule1", "rule2"]to enable only listed rules. - No-Print Lint Rule: Added a
no-printlint rule that errors on bareprint()calls in.jacfiles, encouraging use of the console abstraction instead. Included in the"all"group; enable viaselect = ["all"]orselect = ["default", "no-print"]in[check.lint]. - Format Command Lint Errors:
jac format --fixnow reports lint errors (e.g.,[no-print]) with file, line, and column info, and returns exit code 1 when violations are found. - ES Module Export Generation: Exports now generated at compiler level via ESTree nodes instead of regex post-processing. Only
:pubdeclarations are exported. - Hot fix: call state: Normal spawn calls inside API spawn calls supported.
--no_clientflag forjac start: Added--no_clientCLI flag that skips eager client bundling on server startup. Useful when we need to run server only.- Enhanced Client Compilation for Development: Improved the
jac start --devcommand to perform initial client compilation for HMR.
jaclang 0.9.12#
- Native Binary Compilation via
na {}Blocks and.na.jacFiles: Added a third compilation target to Jac usingna {}context blocks and.na.jacfile conventions. Code within thenacontext compiles to native LLVM IR via llvmlite and is JIT-compiled to machine code at runtime. Functions defined inna {}blocks are callable via ctypes function pointers. Supports integer, float, and boolean types, arithmetic and comparison operators, if/else and while control flow, recursive function calls, local variables with type inference, andprint()mapped to nativeprintf. Native code is fully isolated from Python (sv) and JavaScript (cl) codegen --nafunctions are excluded from bothpy_astandes_astoutput, and vice versa. Thellvmlitepackage is now a core dependency. - SPA Catch-All for BrowserRouter Support: The
jac startHTTP server now serves SPA HTML for unmatched extensionless paths whenbase_route_appis configured injac.toml. This enables BrowserRouter-style client-side routing where direct navigation to/aboutor page refresh on/dashboard/settingsserves the app shell instead of returning 404. API paths (/functions,/walkers,/walker/,/function/,/user/),/cl/routes, and static file paths are excluded from the catch-all. The vanilla (non-React) client runtime (createRouter,navigate,Link) has also been updated to usepushStatenavigation andwindow.location.pathnameinstead of hash-based routing. - Startup error handling improvements: Aggregates initialization errors and displays concise, formatted Vite/Bun bundling failures after the API endpoint list.
- Venv-Based Dependency Management: Migrated
jac add/jac remove/jac installfrompip install --targetto stdlibvenvat.jac/venv/. This eliminates manual RECORD-based uninstall logic and metadata cleanup workarounds, delegating all package management to the venv's own pip. No third-party dependencies added. - GET Method Support: Added full support for HTTP GET requests for both walkers and functions, including correct mapping of query parameters, support for both dynamic (HMR) and static endpoints, and customization via
@restspec(method=HTTPMethod.GET). - Enhanced Hot Module Replacement: Improved client code recompilation to handle exports comprehensively, ensuring all exported symbols are properly updated during hot reloads.
- Rest API Specifications Supported: Rest api specifications supported from jaclang. Developers can utilize it using
@restspec()decorator. - Ensurepip Error Handling: Added a clear error message when venv creation fails due to missing
ensurepip(common on Debian/Ubuntu wherepython3-venvis a separate package), with platform-specific install instructions. - Suppress Warnings in
jac check: Added--nowarnflag tojac checkcommand to suppress warning output while still counting warnings in the summary. - Rest API Specifications Supported: The
@restspecdecorator now supports custom HTTP methods and custom endpoint paths for both walkers and functions. - Custom Methods: Use
method=HTTPMethod.GET,method=HTTPMethod.PUT, etc. - Custom Paths: Use
path="/my/custom/path"to override the default routing. - Storage Abstraction: Added pluggable
Storageinterface withLocalStoragedefault implementation. Usestore()builtin to get a configured storage instance. Configure viajac.toml [storage]or environment variables. - Static files support HMR: Added infrastructure for Hot Module Replacement during development. The file watcher now supports static assets files such as
.cssand images (.png,.jpg,.jpeg) in addition to.jacfiles, enabling automatic reloading of client-side code changes. - Internal: Explicitly declared all postinit fields across the codebase.
- Build (jacpack):
.jac/.gitignorenow contains only a comment (not*), so compiled assets (e.g.,compiled/) aren't ignored and Tailwind builds correctly. - Support Go to Definition for Nested Unpacking Assignments: Fixed symbol table generation to support recursive nested unpacking (e.g.,
[a, [b, c]] = val) ensuring all inner variables are registered. - Fix: Module Name Truncation in MTIR Scope Resolution: Fixed a bug where module names ending with 'j', 'a', or 'c' were incorrectly truncated due to using
.rstrip(".jac")instead of.removesuffix(".jac"). This caused MTIR lookup failures and degraded functionality when the runtime tried to fetch metadata with the correct module name but found truncated keys (e.g.,test_schema→test_schem).
jaclang 0.9.11#
-
MTIR Generation Pass: Added
MTIRGenPasscompiler pass that extracts semantic type information from GenAIbycall sites at compile time. The pass captures parameter types, return types, semstrings, tool schemas, and class structures intoInfodataclasses (FunctionInfo,MethodInfo,ClassInfo,ParamInfo,FieldInfo). MTIR is stored inJacProgram.mtir_mapkeyed by scope path. -
MTIR Bytecode Caching: Extended
DiskBytecodeCacheto cache MTIR maps alongside bytecode (.mtir.pklfiles). MTIR is automatically saved after compilation and restored from cache on subsequent runs, avoiding redundant extraction. -
Reactive Effects with
can with entry/exit: Thecan with entryandcan with exitsyntax now automatically generates ReactuseEffecthooks in client-side code. When used inside aclcodespace,async can with entry { items = await fetch(); }generatesuseEffect(() => { (async () => { setItems(await fetch()); })(); }, []);. Supports dependency arrays using list or tuple syntax:can with (userId, count) entry { ... }generates effects that re-run when dependencies change. Thecan with exitvariant generates cleanup functions viareturn () => { ... }inside the effect. This provides a declarative, Jac-native way to handle component lifecycle without manualuseEffectboilerplate. -
@jac/runtimeImport Syntax: Client-side runtime imports now use the npm-style@jac/runtimescoped package syntax instead of the previousjac:client_runtimeprefix notation. Writecl import from "@jac/runtime" { useState, useEffect, createSignal, ... }in place of the oldcl import from jac:client_runtime { ... }. The grammar no longer supports theNAME:prefix on import paths. The core bundler inlines@jac/runtimeinto the client bundle automatically, so no external dependencies are needed for basic fullstack apps. -
JSX Comprehension Syntax: List and set comprehensions containing JSX elements now compile to JavaScript
.map()and.filter().map()chains. Instead of verbose{items.map(lambda item: dict -> any { return <li>{item}</li>; })}, you can now write{[<li key={item.id}>{item.title}</li> for item in items]}or use double-brace syntax{{ <li>{item}</li> for item in items }}. Filtered comprehensions like{[<li>{item}</li> for item in items if item.active]}generate.filter(item => item.active).map(item => ...). This brings Python-style comprehension elegance to JSX rendering. -
Type Checking Improvements:
- Permissive Type Check for Node Collections in Connections: The type checker now accepts collections (list, tuple, set, frozenset) on the right side of connection operators (
++>,<++>, etc.). Previously, code likeroot ++> [Node1(), Node2(), Node3()];was incorrectly rejected. This is a temporary workaround until element type inference for list literals is fully implemented. - Exclude
by postinitFields from Required Constructor Parameters: Fields declared withby postinitare now correctly excluded from required constructor parameters during type checking. Previously, instantiating an object likeSomeObj()withby postinitfields would incorrectly report missing required arguments, even though these fields are initialized via thepostinitmethod.
jaclang 0.9.10#
- Formatter Spacing Fixes: Fixed extra spaces before semicolons in
reportanddelstatements, and corrected semantic definition formatting to properly handle dot notation and=operator spacing.
jaclang 0.9.9#
Breaking Changes#
- Removed
jac buildCommand and JIR File Support: Thejac buildcommand and.jir(Jac Intermediate Representation) file format have been removed. Users should run.jacfiles directly withjac run. The bytecode cache (.jbcfiles in.jac/cache/) continues to provide compilation caching automatically. If you have existing.jirfiles, simply delete them and run the.jacsource files directly.
Features and Improvements#
-
Console Plugin Architecture: Refactored console system to use a plugin-based architecture, removing the
richdependency from core jaclang. The baseJacConsolenow uses pure Pythonprint()for all output, keeping jaclang dependency-free. Plugins (likejac-super) can override the console implementation via theget_console()hook to provide Rich-enhanced output with themes, panels, tables, and spinners. This maintains backward compatibility while allowing optional aesthetic enhancements through plugins. -
User Management Endpoints: Added new user management endpoints to the
jac startAPI server: GET /user/info- Retrieve authenticated user's information (username, token, root_id)PUT /user/username- Update the authenticated user's username-
PUT /user/password- Update the authenticated user's password All endpoints require authentication via Bearer token and include proper validation to prevent unauthorized access. -
Unified User and Application Database: The
jac startbasic user authentication system now stores users in the same SQLite database (main.db) as application data, instead of a separateusers.jsonfile. This provides ACID transactions for user data, better concurrency with WAL mode, and simplified backup/restore with a single database file as a reference (overridden for production jac-scale). -
Improved JSX Formatter: The JSX formatter now uses soft line breaks with automatic line-length detection instead of forcing multiline formatting. Attributes stay on the same line when they fit within the line width limit (88 characters), producing more compact and readable output. For example,
<button id="submit" disabled />now stays on one line instead of breaking each attribute onto separate lines. -
Template Bundling Infrastructure: Added
jac jacpack packcommand to bundle project templates into distributable.jacpackfiles. Templates are defined by adding a[jacpack]section tojac.tomlwith metadata and options, alongside template source files with{{name}}placeholders. The bundled JSON format embeds all file contents for easy distribution, and templates can be loaded from either directories or.jacpackfiles for use withjac create --use. -
Secure by Default API Endpoints: Walkers and functions exposed as API endpoints via
jac startnow require authentication by default. Previously, endpoints without an explicit access modifier were treated as public. Now, only endpoints explicitly marked with: pubare publicly accessible without authentication. This "secure by default" approach prevents accidental exposure of sensitive endpoints. Use: pubto make endpoints public (e.g.,walker : pub MyPublicWalker { ... }). -
Default
main.jacforjac start: Thejac startcommand now defaults tomain.jacwhen no filename is provided, making it easier to start applications in standard project structures. You can still specify a different file explicitly (e.g.,jac start app.jac), and the command provides helpful error messages ifmain.jacis not found. -
Renamed Template Flags for
jac create: The--template/-tflag has been renamed to--use/-u, and--list-templates/-lhas been renamed to--list-jacpacks/-l. This aligns the CLI with jacpack terminology for clearer naming (e.g.,jac create myapp --use client,jac create --list-jacpacks). -
Flexible Template Sources for
jac create: The--useflag now supports local file paths to.jacpackfiles, template directories, and URLs for downloading remote templates (e.g.,jac create --use ./my.jacpackorjac create --use https://example.com/template.jacpack).
jaclang 0.9.8#
- Recursive DFS Walker Traversal with Deferred Exits: Walker traversal semantics have been fundamentally changed to use recursive post-order exit execution. Entry abilities now execute when entering a node, while exit abilities are deferred until all descendants are visited. This means exits execute in LIFO order (last visited node exits first), similar to function call stack unwinding. The
walker.pathfield is now actively populated during traversal, tracking visited nodes in order. - Imported Functions and Walkers as API Endpoints: The
jac startcommand now automatically convert imported functions and walkers to API endpoints, in addition to locally defined ones. Previously, only functions and walkers defined directly in the target file were exposed as endpoints. Now, any function or walker explicitly imported into the file will also be available as an API endpoint. - Hot Module Replacement (HMR): Added
--devflag tojac startfor live development with automatic reload on.jacfile changes. When enabled, the file watcher detects changes and automatically recompiles backend code while Vite handles frontend hot-reloading. New options include-d/--devto enable HMR mode,--api-portto set a separate API port, and--no-clientfor API-only mode without frontend bundling. Example usage:jac start --dev. - Default Watchdog Dependency: The
jac createcommand now includeswatchdogin[dev-dependencies]by default, enabling HMR support out of the box. Install withjac install --dev. - Simplified
.jacDirectory Gitignore: Thejac createcommand now creates a.gitignorefile inside the.jac/directory containing*to ignore all build artifacts, instead of modifying the project root.gitignore. This keeps project roots cleaner and makes the.jacdirectory self-contained. - Ignore Patterns for Type Checking: Added
--ignoreflag to thejac checkcommand, allowing users to exclude specific files or folders from type checking. The flag accepts a comma-separated list of patterns (e.g.,--ignore fixtures,tests,__pycache__). Patterns are matched against path components, so--ignore testswill exclude any file or folder namedtestsat any depth in the directory tree. - Project Clean Command: Added
jac cleancommand to remove build artifacts from the.jac/directory. By default, it cleans the data directory (.jac/data). Use--allto clean all artifacts (data, cache, packages, client), or specify individual directories with--data,--cache, or--packagesflags. The--forceflag skips the confirmation prompt.
jaclang 0.9.7#
- Unified
jac startCommand: Thejac servecommand has been renamed tojac start. Thejac scalecommand (from jac-scale plugin) now usesjac start --scaleinstead of a separate command. This provides a unified interface for running Jac applications locally or deploying to Kubernetes. - Eager Client Bundle Loading: The
jac startcommand now builds the client bundle at server startup instead of lazily on first request. - Configuration Management CLI: Added
jac configcommand for viewing and modifyingjac.tomlproject settings. Supports actions:show(display explicitly set values),list(display all settings including defaults),get/set/unset(manage individual settings),path(show config file location), andgroups(list available configuration sections). Output formats include table, JSON, and TOML. Filter by configuration group with-gflag. - Reactive State Variables in Client Context: The
haskeyword now supports reactive state in client-side code. When used inside acl {}block,has count: int = 0;automatically generatesconst [count, setCount] = useState(0);, and assignments likecount = count + 1;are transformed tosetCount(count + 1);. This provides a cleaner, more declarative syntax for React state management without explicituseStatedestructuring. - Consolidated Build Artifacts Directory: All Jac project build artifacts are now organized under a single
.jac/directory instead of being scattered across the project root. This includes bytecode cache (.jac/cache/), Python packages (.jac/packages/), client build artifacts (.jac/client/), and runtime data like ShelfDB (.jac/data/). The base directory is configurable via[build].dirinjac.toml. This simplifies.gitignoreto a single entry and provides cleaner project structures. - Format Command Auto-Formats Related Files: The
jac formatcommand now automatically formats all associated annex files (.impl.jacand.cl.jac) when formatting a main.jacfile. The format passes traverse impl modules in a single pass, and all related files are written together, ensuring consistent formatting across module boundaries. - Auto-Lint: Empty Parentheses Removal for Impl Blocks: The
jac format --fixcommand now removes unnecessary empty parentheses fromimplblock signatures, matching the existing behavior for function declarations. For example,impl Foo.bar() -> intbecomesimpl Foo.bar -> int. - Enhanced Plugin Management CLI: The
jac pluginscommand now provides comprehensive plugin management withlist,disable,enable, anddisabledsubcommands. Plugins are displayed organized by PyPI package with fully qualified names (package:plugin) for unambiguous identification. Plugin settings persist injac.tomlunder[plugins].disabled, and theJAC_DISABLED_PLUGINSenvironment variable provides runtime override support. Use*to disable all external plugins, orpackage:*to disable all plugins from a specific package. - Simplified NonGPT Implementation: NonGPT is now a native default that activates automatically when no LLM plugin is installed. The implementation no longer fakes the
byllmimport, providing cleaner behavior out of the box.
jaclang 0.9.4#
letKeyword Removed: Theletkeyword has been removed from Jaclang. Variable declarations now use direct assignment syntax (e.g.,x = 10instead oflet x = 10), aligning with Python's approach to variable binding.- Py2Jac Robustness Improvements: Improved reliability of Python-to-Jac conversion with better handling of f-strings (smart quote switching, no keyword escaping in interpolations), match pattern class names, attribute access formatting (no extra spaces around dots), and nested docstrings in classes and functions.
- Format Command Enhancements: The
jac formatcommand now tracks and reports which files were actually changed during formatting. The summary output shows both total files processed and the count of files that were modified (e.g.,Formatted 10/12 '.jac' files (3 changed).). Additionally, syntax errors encountered during formatting are now printed with full error details. - Py2Jac Stability: Fixed conversion of Python code with augmented assignments and nested docstrings so generated Jac no longer redeclares targets or merges docstrings into following defs.
- Support JS Switch Statement: Javascript transpilation for switch statement is supported.
- F-String Escape Sequence Fix: Fixed a bug where escape sequences like
\n,\t, etc. inside f-strings were not being properly decoded, causing literal backslash-n to appear in output instead of actual newlines. The fix correctly decodes escape sequences for f-string literal fragments inunitree.py. - Python
-mModule Execution Support: Added ability for Jac modules to be executed directly viapython -m module_name. When jaclang is auto-imported at Python startup (via a.pthfile likejaclang_hook.pth), both single-file Jac modules and Jac packages (with__main__.jac) can be run using Python's standard-mflag. - Use Keywords as variable: Developers can now use any jaclang keywords as variable by using escape character
<>. Example:<>from. - Props support: Support Component props system with Python kwargs style with
propskeyword. Ex:props.children. - Standalone
.cl.jacModule Detection:.cl.jacfiles are now recognized as Jac modules both as standalone import targets (when no.jacexists) and as attachable client annexes. - Use Keywords as variable: Developers can now use any jaclang keywords as variable by using escape character
<>. Example:<>from. - Strings supported without escaping within jsx: Strings supported without escaping within jsx. Example usage:
<h1> "Authentication" App </h1> - Support output format for dot command: Output format for dot command is supported. Example Usage:
jac dot filename.jac --format json - Shared
impl/Folder for Annex Discovery: Impl files can now be organized in a sharedimpl/folder within the same directory as the target module. For example,impl/foo.impl.jacwill be discovered and attached tofoo.jac, alongside the existing discovery methods (same directory and module-specific.impl/folders). - Type Checking Enhancements: Added type checking support for
Finaltype hint. - Unified Plugin Configuration System: Introduced a standardized configuration interface for Jac plugins through
jac.toml. Plugins can now register configuration schemas viaget_plugin_metadata()andget_config_schema()hooks, with settings defined under[plugins.<plugin_name>]sections. This replaces environment variable-based configuration with a centralized, type-safe approach. Applied to jac-client, jac-scale and jac-byllm plugins. - Auto-Lint: hasattr to Null-Safe Conversion: The
jac format --fixcommand now automatically convertshasattr(obj, "attr")patterns to null-safe access syntax (obj?.attr). This applies to hasattr calls in conditionals, boolean expressions (and/or), and ternary expressions. Additionally, patterns likeobj.attr if hasattr(obj, "attr") else defaultare fully converted toobj?.attr if obj?.attr else default, ensuring consistent null-safe access throughout the expression. - Auto-Lint: Ternary to Or Expression Simplification: The auto-lint pass now simplifies redundant ternary expressions where the value and condition are identical. Patterns like
x if x else defaultare automatically converted to the more concisex or default. This works with any expression type including null-safe access (e.g.,obj?.attr if obj?.attr else fallbackbecomesobj?.attr or fallback). - Improved Null-Safe Subscript Operator
?[]: The null-safe subscript operator now safely handles invalid subscripts in addition to None containers (e.g.,list?[10]returnsNoneinstead of raising an error;dict?["missing"]returnsNonefor missing keys). This applies to all subscriptable types and makes?[]a fully safe access operator, preventing index and key errors. - Support cl File without Main File: Developers can write only cl file without main jac files whenever main file is not required.
- Support Custom headers to Response: Custom headers can be added by using an enviornmental variable
[environments.response.headers]and mentioning the custom headers (Ex:"Cross-Origin-Opener-Policy" = "same-origin").
jaclang 0.9.3#
- Fixed JSX Text Parsing for Keywords: Fixed a parser issue where keywords like
to,as,in,is,for,if, etc. appearing as text content within JSX elements would cause parse errors. The grammar now correctly recognizes these common English words as valid JSX text content. - Support iter for statement: Iter for statement is supported in order to utilize traditional for loop in javascript.
- JavaScript Export Semantics for Public Declarations: Declarations explicitly annotated with
:pubnow generate JavaScriptexportstatements. This applies to classes (obj :pub), functions (def :pub), enums (enum :pub), and global variables (glob :pub), enabling proper ES module exports in generated JavaScript code. - Cross-Language Type Checking for JS/TS Dependencies: The type checker now supports loading and analyzing JavaScript (
.js) and TypeScript (.ts,.jsx,.tsx) file dependencies when used with client-side (cl) imports. This enables type checking across language boundaries for files with client-language elements, allowing the compiler to parse and include JS/TS modules in the module hub for proper type resolution. - Formatter Improvements and Standardization: Enhanced the Jac code formatter with improved consistency and standardization across formatting rules.
jaclang 0.9.1#
-Side effect imports supported: side effect imports supported which will help to inject css.
- Plugin for sending static files: Added extensible plugin system for sending static files, enabling custom static file serving strategies and integration with various storage backends.
- Type Checking Enhancements:
- Added type checking support for object spatial codes including the connect operator
- Added type checking support for assign comprehensions and filter comprehensions
- Improved type inference from return statements
- Fixed inheritance-based member lookup in type system by properly iterating through MRO (Method Resolution Order) chain
- Improved synthesized
__init__method generation for dataclasses to correctly collect parameters from all base classes in inheritance hierarchy - LSP Improvements: Added "Go to Definition" support for
hereandvisitorkeywords in the language server
jaclang 0.9.0#
- Generics TypeChecking: Type checking for generics in vscode extension has implemented, i.e.
dict[int, str]can be now checked by the lsp. - Plugin Architecture for Server Rendering: Added extensible plugin system for server-side page rendering, allowing custom rendering engines and third-party templating integration with transform, cache, and customization capabilities.
- Improvements to Runtime Error reporting: Made various improvements to runtime error CLI reporting.
- Node Spawn Walker supported: Spawning walker on a node with
jac start(formerlyjac serve) is supported.
jaclang 0.8.10#
- Frontend + Backend with
clKeyword (Experimental): Introduced a major experimental feature enabling unified frontend and backend development in a single Jac codebase. The newcl(client) keyword marks declarations for client-side compilation, creating a dual compilation pipeline that generates both Python (server) and pure JavaScript (client) code. Includes full JSX language integration for building modern web UIs, allowing developers to write React-style components directly in Jac with seamless interoperability between server and client code. - Optional Ability Names: Ability declarations now support optional names, enabling anonymous abilities with event clauses (e.g.,
can with entry { ... }). When a name is not provided, the compiler automatically generates a unique internal name based on the event type and source location. This feature simplifies walker definitions by reducing boilerplate for simple entry/exit abilities. - LSP Threading Performance Improvements: Updated the Language Server Protocol (LSP) implementation with improved threading architecture for better performance and responsiveness in the VS Code extension.
- Parser Performance Optimization: Refactored parser node tracking to use O(N) complexity instead of O(N²), drastically reducing parsing time for large files by replacing list membership checks with set-based ID lookups.
- OPath Designation for Object Spatial Paths: Moved Path designation for object spatial paths to OPath to avoid conflicts with Python's standard library
pathlib.Path. This change ensures better interoperability when using Python's path utilities alongside Jac's object-spatial programming features. - Import System Refactoring: Refactored and simplified the Jac import system to better leverage Python's PEP 302 and PEP 451 import protocols. Removed over-engineered custom import logic in favor of standard Python meta path finders and module loaders, improving reliability and compatibility.
- Module Cache Synchronization Fix: Fixed module cache synchronization issues between
JacMachine.loaded_modulesandsys.modulesthat caused test failures and module loading inconsistencies. The machine now properly manages module lifecycles while preserving special Python modules like__main__. - Formatted String Literals (f-strings): Added improved and comprehensive support for Python-style formatted string literals in Jac with full feature parity.
- Switch Case Statement: Switch statement is introduced and javascript style fallthrough behavior is also supported.
jaclang 0.8.9#
- Typed Context Blocks (OSP): Fully implemented typed context blocks (
-> NodeType { }and-> WalkerType { }) for Object-Spatial Programming, enabling conditional code execution based on runtime types. - Parser Infinite Loop Fix: Fixed a major parser bug that caused infinite recursion when encountering malformed tuple assignments (e.g.,
with entry { a, b = 1, 2; }), preventing the parser from hanging. - Triple Quoted F-String Support: Added support for triple quoted f-strings in the language, enabling multi-line formatted strings with embedded expressions (e.g.,
f"""Hello {name}"""). - Library Mode Interface: Added new
jaclang.libmodule that provides a clean, user-friendly interface for accessing JacMachine functionality. This module auto-exposes all static methods fromJacMachineInterfaceas module-level functions, making it easier to use Jac as a Python library. - Enhanced
jac2pyCLI Command: Thejac2pycommand now generates cleaner Python code suitable for library use with direct imports fromjaclang.lib(e.g.,from jaclang.lib import Walker), producing more readable and maintainable Python output. - Clean generator expression within function calls: Enhanced the grammar to support generator expressions without braces in a function call. And python to jac conversion will also make it clean.
- Support attribute pattern in Match Case: With the latest bug fix, attribute pattern in match case is supported. Therefore developers use match case pattern like
case a.b.c. - Py2Jac Empty File Support: Added support for converting empty Python files to Jac code, ensuring the Py2Jac handles files with no content.
- Formatter Enhancements: Improved the Jac code formatter with several fixes and enhancements, including:
- Corrected indentation issues in nested blocks and after comments
- Removed extra spaces in statements like
assert - Preserved docstrings without unintended modifications
- Enhanced handling of long expressions and line breaks for better readability
- VSCE Improvements: Improved environment management and autocompletion in the Jac VS Code extension, enhancing developer experience and productivity.
jaclang 0.8.8#
- Better Syntax Error Messages: Initial improvements to syntax error diagnostics, providing clearer and more descriptive messages that highlight the location and cause of errors (e.g.,
Missing semicolon). - Check Statements Removed: The
checkkeyword has been removed from Jaclang. All testing functionality previously provided bycheckstatements is now handled byassertstatements within test blocks. Assert statements now behave differently depending on context: in regular code they raiseAssertionErrorexceptions, while withintestblocks they integrate with Jac's testing framework to report test failures. This unification simplifies the language by using a single construct for both validation and testing purposes. - Jac Import of Python Files: This upgrade allows Python files in the current working directory to be imported using the Jac import system by running
export JAC_PYFILE_RAISE=true. To extend Jac import functionality to all Python files, including those in site-packages, developers can enable it by runningexport JAC_PYFILE_RAISE_ALL=true. - Consistent Jac Code Execution: Fixed an issue allowing Jac code to be executed both as a standalone program and as an application. Running
jac runnow executes themain()function, whilejac start(formerlyjac serve) launches the application without invokingmain(). - Run transformed pytorch codes: With
export JAC_PREDYNAMO_PASS=true, pytorch breaking if statements will be transformed into non breaking torch.where statements. It improves the efficiency of pytorch programs. - Complete Python Function Parameter Syntax Support: Added full support for advanced Python function parameter patterns including positional-only parameters (
/separator), keyword-only parameters (*separator without type hints), and complex parameter combinations (e.g.,def foo(a, b, /, *, c, d=1, **kwargs): ...). This enhancement enables seamless Python-to-Jac conversion (py2jac) by supporting the complete Python function signature syntax. - Type Checking Enhancements:
- Added support for
Selftype resolution - Enabled method type checking for tools
- Improved inherited symbol resolution (e.g.,
Catrecognized as subtype ofAnimal) - Added float type validation
- Implemented parameter–argument matching in function calls
- Enhanced call expression parameter type checking
- Enhanced import symbol type resolution for better type inference and error detection
- VSCE Improvements:
- Language Server can now be restarted without requiring a full VS Code window reload
- Improved environment handling: prompts users to select a valid Jac environment instead of showing long error messages
- Formatter Bug Fixes:
- Fixed
if/elif/elseexpression formatting - Improved comprehension formatting (list/dict/set/gen)
- Corrected decorator and boolean operator formatting
- Fixed function args/calls formatting (removed extra commas/spaces)
- Fixed index slice spacing and redundant atom units
jaclang 0.8.7#
- Fix
jac run same_name_of_jac.py- there was a bug which only runs jac file if both jac and python files were having same name. It was fixed so that developers run python files which has same name as jac withjac runcommand. (Ex:jac run example.jac,jac run example.py) - Fix
jac run pythonfile.pybugs: Few bugs such asinitis not found,SubTagast node issue, are fixed. So that developers can runjac runof python files without these issues. - Fix
lambda self injection in abilities: Removed unintendedselfparameter in lambdas declared inside abilities/methods. -
Fix
jac2py lambda annotations: Stripped type annotations from lambda parameters during jac2py conversion to ensure valid Python output while keeping them in Jac AST for type checking. -
TypeChecker Diagnostics: Introduced type checking capabilities to catch errors early and improve code quality! The new type checker pass provides static analysis including:
- Type Annotation Validation: Checks explicit type annotations in variable assignments for type mismatches
- Type Inference: Simple type inference for assignments with validation against declared types
- Member Access Type Checking: Type checking for member access patterns (e.g.,
obj.field.subfield) - Import Symbol Type Checking: Type inference for imported symbols (Basic support)
- Function Call Return Type Validation: Return type checking for function calls (parameter validation not yet supported)
- Magic Method Support: Type checking for special methods like
__call__,__add__,__mul__ - Binary Operation Type Checking: Operator type validation with simple custom operator support
- Class Instantiation: Type checking for class constructor calls and member access
- Cyclic Symbol Detection: Detection of self-referencing variable assignments
- Missing Import Detection: Detection of imports from non-existent modules
Type errors now appear in the Jac VS Code extension (VSCE) with error highlighting during editing.
- VSCE Semantic Token Refresh Optimization: Introduced a debounce mechanism for semantic token refresh in the Jac Language Server, significantly improving editor responsiveness:
- Reduces redundant deep checks during rapid file changes
-
Optimizes semantic token updates for smoother editing experience
-
Windows LSP Improvements: Fixed an issue where outdated syntax and type errors persisted on Windows. Now, only current errors are displayed
jaclang 0.8.6#
jaclang 0.8.5#
- Removed LLM Override:
function_call() by llm()has been removed as it was introduce ambiguity in the grammer with LALR(1) shift/reduce error. This feature will be reintroduced in a future release with a different syntax. - Enhanced Python File Support: The
jac runcommand now supports direct execution of.pyfiles, expanding interoperability between Python and Jac environments. - Jac-Streamlit Plugin: Introduced comprehensive Streamlit integration for Jac applications with two new CLI commands:
jac streamlit- Run Streamlit applications written in Jac directly from.jacfilesjac dot_view- Visualize Jac graph structures in interactive Streamlit applications with both static (pygraphviz)- Improved Windows Compatibility: Fixed file encoding issues that previously caused
UnicodeDecodeErroron Windows systems, ensuring seamless cross-platform development. -
Fixed CFG inaccuracies: Fixed issues when handling If statements with no Else body where the else edge was not captured in the CFG (as a NOOP) causing a missing branch on the CFG of the UniiR. Also fixed inaccuracies when terminating CFG branches where return statements and HasVariables had unexpected outgoing edges which are now being removed. However, the return still keeps connected to following code which are in the same scope(body) which are dead-code.
-
CFG print tool for CLI: The CFG for a given program can be printed as a dot graph by running
jac tool ir cfg. filename.jacCLI command.
jaclang 0.8.4#
- Support Spawning a Walker with List of Nodes and Edges: Introduced the ability to spawn a walker on a list of nodes and edges. This feature enables initiating traversal across multiple graph elements simultaneously, providing greater flexibility and efficiency in handling complex graph structures.
- Bug fix on supporting while loop with else part: Now we are supporting while loop with else part.
jaclang 0.8.3#
- JacMachine Interface Reorganization: The machine and interface have been refactored to maintain a shared global state(similar to Python's
sys.modules) removing the need to explicitly pass execution context and dramatically improving performance. - Native Jac Imports: Native import statements can now be used to import Jac modules seamlessly into python code, eliminating the need to use
_.jac_import(). - Unicode String Literal Support: Fixed unicode character handling in string literals. Unicode characters like "", "○", emojis, and other international characters are now properly preserved during compilation instead of being corrupted into byte sequences.
- Removed Ignore Statements: The
ignorekeyword and ignore statements have been removed as this functionality can be achieved more elegantly by modifying path collection expressions directly in visit statements.
jaclang 0.8.1#
- Function Renaming: The
dotgenbuilt-in function has been renamed toprintgraph. This change aims to make the function's purpose clearer, asprintgraphmore accurately reflects its action of outputting graph data. It can output in DOT format and also supports JSON output via theas_json=Trueparameter. Future enhancements may include support for other formats like Mermaid. - Queue Insertion Index for Visit Statements: Visit statements now support queue insertion indices (e.g.,
visit:0: [-->]for depth-first,visit:-1: [-->]for breadth-first) that control where new destinations are inserted in the walker's traversal queue. Any positive or negative index can be used, enabling fine-grained control over traversal patterns and supporting complex graph algorithms beyond simple depth-first or breadth-first strategies. - Edge Ability Execution Semantics: Enhanced edge traversal behavior with explicit edge references. By default,
[-->]returns connected nodes, while[edge -->]returns edge objects. When walkers visit edges explicitly usingvisit [edge -->], abilities are executed on both the edge and its connected node. Additionally, spawning a walker on an edge automatically queues both the edge and its target node for processing, ensuring complete traversal of the topological structure. - Jac Imports Execution: Jac imports (
Jac.jac_import) now run in a Python-like interpreter mode by default. Full compilation with dependency inclusion can only occur when explicitly callingcompilefrom theJacProgramobject. - Concurrent Execution with
flowandwait: Introducedflowandwaitkeywords for concurrent expressions.flowinitiates parallel execution of expressions, andwaitsynchronizes these parallel operations. This enables efficient parallel processing and asynchronous operations directly within Jac with separate (and better) semantics than python's async/await.
Version 0.8.0#
implKeyword for Implementation: Introduced theimplkeyword for a simpler, more explicit way to implement abilities and methods for objects, nodes, edges, and other types, replacing the previous colon-based syntax.- Updated Inheritance Syntax: Changed the syntax for specifying inheritance from colons to parentheses (e.g.,
obj Car(Vehicle)) for better alignment with common object-oriented programming languages. defKeyword for Functions: Thedefkeyword is now used for traditional Python-like functions and methods, whilecanis reserved for object-spatial abilities.visitorKeyword: Introduced thevisitorkeyword to reference the walker context within nodes/edges, replacing the ambiguous use ofherein such contexts.hereis now used only in walker abilities to reference the current node/edge.- Lambda Syntax Update: The lambda syntax has been updated from
with x: int can x;tolambda x: int: x * x;, aligning it more closely with Python's lambda syntax. - Object-Spatial Arrow Notation Update: Typed arrow notations
-:MyEdge:->and+:MyEdge:+>are now->:MyEdge:->and+>:MyEdge:+>respectively, to avoid conflicts with Python-style list slicing. - Import
fromSyntax Update: The syntax for importing specific modules from a package now uses curly braces (e.g.,import from utils { helper, math_utils }) for improved clarity. - Auto-Resolved Imports: Removed the need for explicit language annotations (
:py,:jac) in import statements; the compiler now automatically resolves imports.