Integrate all three PRs: package layout + buffer policies + codegen fixes & fuzzer#3
Merged
Merged
Conversation
Rename the four example/source files to their real extensions so they match the names already used in the README and file headers: Program.cs.txt -> Program.cs buffer.own.txt -> buffer.own ok_pool.own.txt -> ok_pool.own bad_leak_branch.own.txt -> bad_leak_branch.own
The modules used relative imports (.parser, .cfg, ...) and run_tests.py imported the 'ownlang' package, but everything sat flat in the repo root, so 'python tests/run_tests.py' failed with ModuleNotFoundError. Move files into the layout already documented in the README: ownlang/ <- the 8 .py modules + new __init__.py tests/ <- run_tests.py examples/ <- ok_pool.own, bad_leak_branch.own examples/golden_arraypool <- buffer.own, Program.cs Now passing: 42/42 analysis, 11/11 codegen, golden PASS; and the documented CLI (python -m ownlang check/emit/cfg) works.
Ignore __pycache__/ and *.pyc (generated when running the test suite) plus the .NET bin/ and obj/ output from the golden_arraypool demo, so build artifacts stay out of the repo.
README's lead commands pointed at examples/ok_extern_calls.own and examples/bad_maybe_release.own, which never existed as files (they were only inline test cases). Add them as standalone .own files, verified with the checker here: ok_extern_calls.own -> checks clean (extern borrow/borrow_mut/consume) bad_maybe_release.own -> fires OWN009 + OWN001, cfg/emit behave as documented For demo.csproj (referenced but never shipped, and unverifiable without a .NET SDK in this sandbox) fix the README instead of adding an untested project file: show how to wrap Program.cs in a console project, and drop the demo.csproj entry from the structure tree.
Two blocking issues from code review, both reproduced before fixing: 1. Soundness hole: returning an owner while it is borrowed checked clean. 'return b' is an escape/consume of an owned resource, so under a live loan it must fail like move/consume do. The Return handler validated moved/released state but never the active loans on the returned owner. Now it fires OWN007 (mirrors move_while_borrowed/consume_while_borrowed). Added regression test return_while_borrowed; updated the OWN007 row in the README code table to list return alongside move/consume. 2. README quickstart was broken: 'cd ownlang' steps into the package dir, after which 'python -m ownlang' fails (No module named ownlang) and the examples/ paths no longer resolve. Replaced with a note to run from the repo root. Suite: 43/43 analysis (was 42 + new case), 11/11 codegen, golden PASS.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
PR #2 was authored with package-style relative imports (from . import ...) but committed as flat files, so it never actually ran. Re-home it onto the package layout established by PR #1: modules into ownlang/, buffers.py and report.py alongside, the test runner + its buffer_scratch_program.cs.txt fixture into tests/, example programs into examples/. Merge .gitignore and splice PR #1's README quickstart fix into PR #2's README. No logic changes.
The straight-line hoist treated only acquire/buffer lets as releasable scopes, so a top-level `release` of an owned *parameter* (which opens no scope) fell through the "stray release, skip" branch and was dropped -- generating a method that never releases the resource (a leak). Any release reaching that loop is by construction unconsumed by a scope, so emit it faithfully at its source site. Found by the property fuzzer (release-count invariant: source=1, emitted=0).
PR #2's rewritten Return handling set the owner to ESCAPED without checking for a live loan, so `borrow b as s { return b; }` returned a resource out from under an outstanding borrow with no diagnostic. PR #1 caught this on the original analysis; re-apply it, adapted to PR #2's buffer-aware Return block: returning an owner needs Own permission, so a live loan is OWN007 (as for move).
Stack my codegen guards on top of the buffer feature. test_codegen.py asserts on the generated C# (release placement/count, declaration order, escape handling, span-typed borrowed params); test_codegen_props.py fuzzes thousands of random clean programs against an AST-derived release-accounting oracle (this is what caught the dropped owned-parameter release). Both are wired into run_tests.py and a GitHub Actions workflow runs the suite on 3.11/3.12/3.13 plus an extended 50k-draw fuzz pass.
4fb725f to
d0d8662
Compare
This was referenced Jun 14, 2026
PhysShell
pushed a commit
that referenced
this pull request
Jun 18, 2026
… re-validation Leak #4 in the SystemEvents fixture: a FileStream that IS disposed, but the Dispose() sits inside the try after a may-throw call (WriteByte), so it's skipped on the exceptional path. This is CodeQL's cs/dispose-not-called-on-throw (cs/local-not-disposed also models it). Own.NET used to miss it — disposed somewhere looked balanced — until the exception-edge model (77b2edd) inserted a throw edge before each may-throw statement in a try. Re-running the oracle on this fixture should land #4 in "Agree" with CodeQL, joining #2/#3, with #1 (subscription) staying Own.NET-only. Bumps the oracle sentinel to trigger the push-run.
PhysShell
pushed a commit
that referenced
this pull request
Jun 18, 2026
…oracle fixture
The exception-edge try-lowering injects an exceptional exit (a bare return while
the local is live) before each may-throw statement. A local never disposed then
leaks on BOTH that exit and the normal fall-through, so the core emits OWN001
once per exit. Every flow-local diagnostic remaps to the acquire line, so the two
collapse to byte-identical findings. The first oracle run surfaced this as
Program.cs:54 appearing twice in "Agree".
- ownir.py: drop byte-identical findings (same file/line/code/component/event/
handler/message/kind/severity) before sorting. Native-OwnLang leaks at distinct
lines stay distinct — the key includes line; only the bridge's line-collapse
makes them identical, so deduping is exactly right.
- test_ownir.py + flow_leak_two_exits fixture: a two-leaking-exits flow body
(acquire; if(*){return}; use) must yield exactly one OWN001 (TryNeverDisposed
'tfLeak'@105). Without the dedup it returns two.
Also tighten the SystemEvents fixture's dispose-on-throw case (#4): the three
tools anchor the same leak at different points (Own.NET acquire / CodeQL Dispose /
Infer# last-access), so a spread-out method puts them >3 lines apart and the
oracle's ±3 window splits one leak into own-only + oracle-only. Keep the try a
one-liner adjacent to the acquire so the anchors fall inside the window -> #4
joins #2/#3 in "Agree" across all three. Bumps the oracle sentinel.
PhysShell
pushed a commit
that referenced
this pull request
Jun 18, 2026
Layer a region-escape path onto the OwnIR bridge: a new `capture` subscription fact (a tokenless strong `event += h` whose source provably outlives the subscriber) lowers to the lifetime engine's `subscribe self to <source>` and surfaces as OWN014 — the WPF "escape to App" — instead of the bespoke OWN001 token tier. WPF subscriptions become a profile of the general owner/release-region model (ROADMAP Milestone 2). - lifetimes.py: stamp `subject` (source#line) on OWN014 so the bridge maps it back to the C# subscription (reuses _handle_of; invisible to render). - ownir.py: lower `capture` facts (to_module + to_own) to lifetime decls + a source param carrying its region + `subscribe`; map OWN014 back to a region-escape Finding. A `static` source is a process-lived (longest) region -> OWN014; an injected/unknown source stays conservatively silent (the region model is precise where the token tier only warns). - Additive + zero-regression: the current Roslyn extractor never emits `capture`, so existing OWN001/timer/field/pool/DI paths are untouched (58/58 bridge checks, ruff, mypy --strict all green). Exercised by a hand-written fixture until the extractor emits the fact (P-004 WPF005). New: tests/fixtures/ownir/capture.facts.json + test_ownir assertions. Docs: P-004 (WPF005 bridge+core done), lifetimes.md (slice #3 IR bridge), ROADMAP (Milestone 2 in progress). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
PhysShell
pushed a commit
that referenced
this pull request
Jun 18, 2026
Complete the WPF region-escape path end to end (P-004 WPF005). The Roslyn extractor now lowers a static-source `event += handler` (a process-lived static event, or a static field/property receiver) to a tokenless `capture` OwnIR fact instead of a token `subscription`, so a real SystemEvents-style leak surfaces as OWN014 (the subscriber is promoted to process lifetime) — the WPF escape as a profile of the general owner/release-region model. Extractor (Program.cs): one-line reroute — `source == "static"` ? "capture". Injected/unknown sources stay token `subscription` (OWN001, severity-tiered); timers stay `timer`. No existing sample is a non-timer static source, so the existing wpf-extractor assertions are unchanged. Bridge (ownir.py): a `capture` with a matching `-=` (`released: true`) is mitigated -> silent (the source no longer holds self on close), mirroring a released token subscription. Pinned by a released-capture fixture case. Sample: StaticEventEscapeViewModel.cs (instance handler on Calc.GlobalPing -> OWN014; an unsubscribed variant stays silent), wired into CI wpf-extractor with OWN014 + silence assertions. The facts->core effect is verified locally (simulated extractor output replays the exact CI greps); the C# extraction is CI-only (no local dotnet). Corpus: corpus/wpf/systemevents-region-escape — the SystemEvents leak through the region model (OWN014), paralleling the token-model view in corpus/real-world/screentogif-systemevents-leak. Oracle: OWN014 joins OWN_LEAK so cross-tool leak accounting still counts these. Docs: P-004 (WPF005 end-to-end), lifetimes.md (slice #3), ROADMAP (Milestone 2), README (P-001 extractor now emits OWN014 for static events). Local: ruff, mypy --strict, full suite (ownir 59/59, wpf 4/4), oracle 19/19, miner 8/8 all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
PhysShell
pushed a commit
that referenced
this pull request
Jun 22, 2026
…eturn only (Codex) Codex P2: treating a rented field passed to ANY field-stored `new X(field)` as released wrongly suppresses a real leak for NON-owning wrappers (`_view = new ReadOnlyMemory<byte>(_buf)` and other cached views), where the array is never returned. Soundly distinguishing an owning guard (that Returns the buffer) from a non-owning view is not worth it for the single SharedArrayPoolBuffer FP, so the guard-transfer is removed entirely. Fix #3 keeps only the sound, high-value part: a pooled FIELD is released if `pool.Return(field)` appears anywhere in the class (cross-member ctor-rent + Dispose-return — BufferedReadStream). SharedArrayPoolBuffer's indirect release via its LifetimeGuard is left as an honest known limitation. Sample/CI drop the PoolFieldTransferredToGuard case accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
PhysShell
pushed a commit
that referenced
this pull request
Jun 27, 2026
) Two cross-reference fixes flagged by Codex: - The cleanup-rule cross-walk was off: OWNTS004 is AbortController, which maps to EFF005 — not EFF004 (timers = OWNTS002). State the accurate one-to-one: OWNTS002/003/004 ↔ EFF004/EFF003/EFF005. Fixed in P-017's reference note and in P-020's open-question #3. - Stop marketing the OWNTS001 cleanup rule (→ OWN001) with the request-storm / Cloudflare story. That shape is EFF001/002 (new dependency-stability analysis the core lacks), per P-020's honest split — the conflation P-020 explicitly avoids. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QnGqHwVb2H1jZ71f498MFa
PhysShell
pushed a commit
that referenced
this pull request
Jun 27, 2026
…restore scaffolding Live protobuf oracle re-run confirms PropertyReturnsOwnedMember clears the CommandLineOptions XsltMessageEncountered self-cycle FP at the source: own-only 0, the finding absent from both own-only and the baselined section. Delete its baseline entry (protobuf baseline 5 -> 4 findings) and document the fix landed (root-cause #3 PARTLY FIXED). Restore the dev scaffolding: oracle-target.txt back to the systemevents-console fixture, remove the branch from the oracle.yml push filter. Newtonsoft serializer.Error stays baselined as an honest 'may outlive' warning (its source escapes and the handler is a parameter's delegate — non-leak is unprovable without lifetime modelling). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019846YSZ35c7CdkWQ1qX5gm
PhysShell
pushed a commit
that referenced
this pull request
Jul 3, 2026
Both reviewers converged; all corrections are factual/design refinements (no forks): - Crate DAG: own-codegen is a SIBLING of own-diagnostics (both consume own-cfg/own-analysis), not chained through it. Codegen is verdict-independent (AST/CFG-driven, matching Python codegen.generate), so the old own-diagnostics -> own-codegen edge would have forced diagnostics to re-export solver internals. Added a fitness function locking codegen !-> diagnostics (and !-> analysis for now). (Codex P2, CodeRabbit #1) - Oracle: do NOT reuse scripts/oracle_compare.py as the parity oracle — it is cross-tool fuzzy (leak-only, +-N line tolerance, coarse severity) and would mask off-by-one/label/subject/exit-status divergences. Spec a new exact harness over status+stdout+stderr+SARIF/JSON, with an exit/crash gate first, exact set-equality incl. evidence label text, and intra-tie ordering. (Codex P1, CodeRabbit #4) - CFG seam does not exist yet: python cfg prints human text, not JSON. Add+freeze a canonical cfg --format json before the ratchet uses that seam; added as migration step 0. (Codex P2) - State: arena+CoW likely wins this procedural workload; bench largest real function, wall-clock + RSS. Prior art: clippy lint-pass registry, prusti-viper encoding boundary. Repo-layout revisit trigger. (CodeRabbit #2/#3/#5/#6)
PhysShell
pushed a commit
that referenced
this pull request
Jul 4, 2026
Closes 3 of 4 open questions with sourced findings; #1 and #3 retain a 5-minute empirical residual (run on real Linux), not a design blocker. - gVisor x toolchain: LIKELY OK (277/351 syscalls; runtimes regression- tested). Residual: run gate.toml under runsc, watch for "Bad system call". Known gaps flagged (io_uring off, iptables partial, no KVM-in-sandbox). - Kata 2026: CLOSED — ~100-300ms boot, ~130-200MB/pod, needs KVM, no native egress, guest-kernel patch-currency required. Its edge is containerd/K8s drop-in; with no K8s here it adds overhead without advantage over direct Firecracker (L1) or Sandlock (L2). Fills the deep-research Kata gap. - QUIC/HTTP3 egress: CLOSED default — blanket UDP block suffices; CLI agent tools are TCP-first and fall back cleanly. Don't build MITM-CA/L7 proxy until a hard QUIC dependency appears. Residual: confirm nothing hangs. - TCO self-host vs managed: CLOSED — both cheap in $ at N=1; the fork is purpose/trust/offline, not cost. For a for-the-soul self-use project, build self-host; the L2 MVP is days not weeks. E2B is benchmark, not target. Also fixes the §6 trigger-table (an earlier note blockquote split the table mid-rows). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LMA4JwvgPBCZuEbAiG5LJL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Single integration of the three open PRs onto one linear history off
main,resolving the conflicts that came from all three independently restructuring
the repo and rewriting
codegen.py.What's included (in order):
ownlang/package,examples →
examples/,.gitignore, README quickstart fixes, and thereturn-under-borrow soundness fix.
stackalloc/scratch/pool/native/inline feature,
buffers.py/report.py,trace/counter hooks, and the
reportCLI. PR Add buffer storage policies with mandatory logging #2 was authored with packageimports but committed flat, so it never actually ran; re-homed into
ownlang/it does. Its own suite (95 analysis cases + buffer/report smokes)is green.
codegen: the hoist path dropped owned-parameter releases (a leak) —now emitted faithfully. Found by the property fuzzer (release-accounting
invariant), not by hand.
analysis: returning a resource while it is borrowed was unflagged —now OWN007 (PR Remove unnecessary .txt extensions from example and source files #1 caught this; PR Add buffer storage policies with mandatory logging #2's rewrite had lost it).
test_codegen.py(asserts on the generated C#)and
test_codegen_props.py(fuzzes thousands of random clean programsagainst an AST-derived oracle), wired into
run_tests.py, plus a GitHubActions workflow (3.11/3.12/3.13 + a 50k-draw fuzz pass).
Architecture note: no divergence. All three sit on the same pipeline
(lexer → parser → AST → CFG → loans/permissions dataflow → two-mode codegen).
The analysis core is structurally identical; PR #2 adds one IR node
(
AcquireBuffer) and buffer-escape diagnostics in-place, and reworks thecodegen hoist — which is exactly where the churn and the leak lived.
Verification (local + CI): analysis 95/95, codegen 32/32, golden/buffer/
escape/branchy/nesting/ordering/helper PASS, codegen-content 21/21, property
fuzz 0 failures (3k in-suite, 50k extended).
Supersedes #1 and #2.