Skip to content

Bare symbols are no longer guest sources; add the 'T borrow mode; rename receiver to subject - #151

Merged
TheLazyCat00 merged 32 commits into
mainfrom
claude/guest-source-restriction-jk2m
Jul 31, 2026
Merged

Bare symbols are no longer guest sources; add the 'T borrow mode; rename receiver to subject#151
TheLazyCat00 merged 32 commits into
mainfrom
claude/guest-source-restriction-jk2m

Conversation

@TheLazyCat00

@TheLazyCat00 TheLazyCat00 commented Jul 30, 2026

Copy link
Copy Markdown
Member

Replaces #150, which is closed without merging. Branched from agent/fix-host-overwrite-reuse and targeting main directly, so the anchor-pool work that was open as #147 lands here too rather than in a separate PR.

The change

The starting point was this program:

main Player()
second Player()
guest &Player = main
second = main

Line 4 moves the object out of main's slot. What guest then denotes — the object that left, or the slot it left from — has no answer that is right in both readings, and the spelling gives the reader no way to say which they meant.

A bare symbol is no longer a guest source (memory.md §2.8.1). It stays a place expression — readable, mutable, movable-from, borrowable — but a new & may be minted only from a field access or an &T parameter. Line 3 is now a compile error, at the line that creates the problem rather than the line that reveals it. A guest to a bare symbol never bought any reach in the first place: the symbol is already in scope everywhere a guest to it could be declared.

Removing that source leaves a hole where a bare symbol needs to reach a callee, so a reference type gains a third passing mode (memory.md §2.9):

Mode Written Caller supplies The callee may
Swallow T a move-source take hosting access; the caller downgrades to a guest
Guest &T a guest source store it in & storage or return it as &T
Borrow 'T any place expression, including a bare symbol read and mutate it for the duration of the call only

'T is parameter-only — never a storage, field, element, or return type.

What follows from it

  • The subject parameter is never a swallow position, so it chooses between only two of the three modes: written bare it is a borrow, and this &T is the guest subject a method writes when it stores or returns the object. ' is never written on this. That the subject is not swallowed is not new — a reference-type this used to be an implicit guest, likewise never swallowed; the borrow mode only renamed what bare this already was. This also makes the two type worlds agree: a mut subject is a mutable borrow of the caller's slot for values and references alike.
  • A returned &T must be rooted in an &T parameter (lifetimes.md §1.7). A swallowing T is a bare symbol, and a borrow ends with the call.
  • A field reached through a 'T parameter is not a guest source either — otherwise a borrow escapes wrapped in one layer of indirection.
  • Overloads may not differ only by the passing mode (functions.md §4.1), widening the rule that already covered T vs &T.
  • An & field and a recursive #variant case are fed from a field or an & parameter, so a recursive structure is rooted in a field rather than a bare local (adt.md §4.1, types.md §3.9). This is the one place the change costs something visibleExpr.flip(tree.root) rather than Expr.flip(leaf).

receiversubject

Stating the rule about this exposed a word that had never been examined. "Receiver" is inherited from Smalltalk, where a call was a message and the object received it; Zane has no messages, so by the test in naming-terms.md §3 the word neither fights nor feeds — it is dead metaphor. It was also covering three distinct things: the object a method is called on, the this parameter whose form fixes the passing mode, and the expression left of : / ! at the call site.

Both are fixed. The three senses are separated and defined in functions.md §2.1 (glossary §3.38), and the term is renamed to subject — grammar, matching the verb the spec already chose, so a call reads subject–verb–object. 91 sites across 12 spec docs. Two sites meant the callee taking a lambda argument rather than the method subject, and now say so.

Merged story chapters keep the old word, since they record the language as it was — so the two doc trees disagree by construction, and the new story chapter names itself as the mapping.

What is not in this PR

The anchor system is untouched. #150 deleted it and asserted that a hosted object never relocates; that model could not express a conditional move and contradicted itself on backing-store growth. Anchors exist to let objects move, and they still do. There are no changes to bench/.

Stories

stories/memory.md gains "The slot that could not be pointed at" and "Three ways to hand over an object" — including the defence of ' over redefining &, since both forms are marked and T is the unmarked one, so continuity decided it. stories/lifetimes.md gains "Where a guest may be rooted". stories/functions.md gains "What does a receiver receive?", which argues subject over receiver and records the candidates rejected. In-prose spec references are pinned to b10eaed and 97e5bf2; all were verified to resolve at their pinned commits.

contributing/writing-stories-docs.md §5 also records what append-only actually freezes: the unit is the pull request, not the commit, so a branch's own unmerged chapters are still drafts. The git diff origin/main check already encoded this correctly.

Validation

Both CLAUDE.md guards are clean. The guard itself was fixed in this PR: '[A-Z] is removed from the retired-generics-forms list (a leading ' is now the borrow marker), a second guard for bare-symbol guest sources is added, and the greps are made recursive — the old form printed bench/: Is a directory and silently skipped it.

🤖 Generated with Claude Code

https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7


Generated by Claude Code

TheLazyCat00 and others added 18 commits July 25, 2026 13:08
* docs: fix review defects in the arena and global-anchor change

Repair the integration half of the memory-model revision and the
cross-references it left stale.

- Restore the story pointer at memory.md §3.6; it named a chapter
  heading that no longer exists, so the link resolved to nothing.
- Point the sections whose rules the revision changed (§3.1, §3.2,
  §3.6, §4.1, §4.5, §4.6) at the new story chapter; §4.6 had no
  pointer at all.
- Restore the story's placement chapter to its previous wording: the
  chapter predates this change, and its in-prose spec reference is a
  commit-pinned permalink rather than a living relative link.
- Update the glossary entries for anchor cell and arena placement,
  which still described a scope-local anchor region.
- Record the both-sides-guests move restriction in lifetimes.md, the
  home of move legality, as §1.10 with a summary row.
- Restore the arena-granularity latitude and the destruction-timing
  distinction dropped from §3.1 and §3.2, and note that the
  fixed-size region reclaims nothing individually.
- State how the anchor pool avoids issuing identity 0, restore the
  address formula, drop a contrast with storage the language has no
  form for, and square the segmented-offset diagram.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016YnNCXtguHg4tSbLCtTT2c

* docs: sharpen the scope-drain guarantee and the move-liveness summary

Address review on the promotion and move-liveness wording.

- State the drain guarantee as reachability: a scope's memory is
  released at drain and no guest resolves into released memory, with
  an escaping value promoted out first and its guests following the
  canonical anchor. The previous phrasing claimed source-arena memory
  outlives every guest that can reach it, which promotion breaks.
- Say in the glossary that promotion copies the fixed-size bytes —
  the inline payload or the handle — while a dynamic backing store
  transfers without being copied.
- Complete the move-liveness summary row with the case where neither
  side has live guests: a moved-from slot that stays readable anchors
  lazily.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016YnNCXtguHg4tSbLCtTT2c

---------

Co-authored-by: Claude <noreply@anthropic.com>
Define rehosting as copying the complete hosted representation into destination-owned storage. Inline bytes move into the destination slot, dynamic backing stores relocate into equal-size destination-region allocations, old source storage ceases to be live, and the source host-capable slot becomes a guest through the canonical tether.

Keep anchor bookkeeping O(1) in the number of guests while stating the physical relocation cost explicitly. Align the normative memory model, glossary, and design story, and complete the glossary's verb list with operators and lambdas.
A new `&` may now be minted only from a field access or an `&T` parameter.
A bare symbol stays a place expression but is no longer a guest source, so
nothing can point at a local's own hosting slot and the slot stays free to
be overwritten or moved from. This is what makes the aliasing case that
started this — a guest to `main`, then `second = main` — a compile error at
the line that mints the guest rather than a question about what the guest
then denotes.

Removing that source leaves a hole where a bare symbol needs to reach a
callee, so reference types gain a third passing mode: `'T`, a borrow. The
three modes are now `T` (swallow), `&T` (guest), and `'T` (borrow, any
place including a bare symbol, read and mut for the call only, never
stored, returned, or moved). A reference receiver written bare is an
implicit `'T` borrow; `this &T` is the guest receiver a method writes when
it stores or returns the receiver.

Consequences carried through: a returned `&T` must be rooted in an `&T`
parameter; a field reached through a borrow is not a guest source either;
overloads may not differ only by passing mode; an `&` field and a recursive
`#variant` case are fed from a field or an `&` parameter, so a recursive
structure is rooted in a field rather than a bare local.

The anchor system is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
…odes

stories/memory.md gains "The slot that could not be pointed at" — the
five-line aliasing program, the roads tried in order (smarter guests, the
name/location/object middle layer, immovable objects, no guests at all) and
why each failed, and the narrowing that finally settled it. Then "Three ways
to hand over an object": why an `&T`-parameter exemption would have needed
interprocedural inference, how the borrow fell out of that, why the receiver
became a borrow too, and the defence of `'` over redefining `&`.

stories/lifetimes.md gains "Where a guest may be rooted" — what the source
rule did to the scope check and the return rule, and why a field reached
through a borrow is not a guest source either.

Spec sections point into the new chapters; in-prose references are pinned to
b10eaed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: zane-lang/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f0d6283d-21d6-4585-b24f-c88d888ac002

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR revises Zane’s guest, borrow, and swallowing modes, tightens guest-source and lifetime rules, updates callable syntax and semantics, and documents new arena, anchor, and tether behavior. Related glossary, stories, README, and validation guidance are synchronized.

Changes

Guest and memory model semantics

Layer / File(s) Summary
Surface syntax and terminology
spec/lexical.md, spec/syntax.md, spec/glossary.md, spec/effects.md, spec/foundations.md, spec/concurrency.md
Defines guest and borrow type forms and updates receiver, terminology, and concurrency rules.
Passing modes and guest-source rules
spec/functions.md, spec/adt.md, spec/memory.md, spec/lifetimes.md, spec/types.md, stories/lifetimes.md
Applies guest, borrow, and swallowing rules to calls, receivers, recursive construction, field initialization, lifetime checks, and returned guests.
Memory layout and anchor lifecycle
spec/memory.md, spec/glossary.md, stories/memory.md
Documents dual arena regions, global anchors, segmented-offset tethers, relocation, retirement, and synchronization.
Validation and reference synchronization
CLAUDE.md, README.md
Updates validation commands and expands design-story index descriptions.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title accurately names two major changes but also claims that the receiver was renamed to subject, which is not present in the changeset. Remove “rename receiver to subject” unless that change exists; keep the title focused on guest sources and the new 'T borrow mode.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/guest-source-restriction-jk2m

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 61-68: Update the guest-source validation grep in CLAUDE.md to
ignore trailing whitespace and inline comments while still detecting bare-symbol
ampersand assignments; preserve the existing allowance for field accesses such
as `car.engine` and `&T` parameters.

In `@spec/glossary.md`:
- Line 68: Update the place-expression definition in spec/glossary.md to exclude
field accesses whose base chain passes through a 'T borrow, matching the
restriction in spec/memory.md §2.8. Retain the existing exclusions for bare
symbols and [] expressions while clarifying that only eligible field accesses
can serve as guest sources.

In `@spec/memory.md`:
- Line 517: Update the thread-local anchor cache statement in Section 3.2 so it
does not promise unchanged global LIFO reuse ordering: either make reuse order
explicitly non-normative when caches are used, or require centralized
global-stack behavior for implementations that need exact LIFO semantics.
Preserve the existing anchor identity and lifetime guarantees.
- Line 319: Update the fenced ASCII diagram block in spec/memory.md to include a
language info string, using text or another suitable lexer, so the Markdown
fence satisfies MD040.

In `@spec/syntax.md`:
- Around line 308-309: The passing-mode description in spec/syntax.md must
explicitly limit the T, &T, and 'T choices to reference-type parameters and
receivers. Clarify that value-type parameters are read-only borrows and value
receivers are borrowed, consistent with spec/memory.md §2.9 and spec/effects.md.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: zane-lang/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ba47af9c-5017-4dd9-98db-2f10ccb8158a

📥 Commits

Reviewing files that changed from the base of the PR and between c53fdbf and 00de7e8.

📒 Files selected for processing (15)
  • CLAUDE.md
  • README.md
  • spec/adt.md
  • spec/concurrency.md
  • spec/effects.md
  • spec/foundations.md
  • spec/functions.md
  • spec/glossary.md
  • spec/lexical.md
  • spec/lifetimes.md
  • spec/memory.md
  • spec/syntax.md
  • spec/types.md
  • stories/lifetimes.md
  • stories/memory.md

Comment thread CLAUDE.md Outdated
Comment thread spec/glossary.md Outdated
Comment thread spec/memory.md Outdated
Comment thread spec/memory.md Outdated
Comment thread spec/syntax.md Outdated
- CLAUDE.md: the guest-source guard was end-anchored, so it matched no line
  carrying a `// ILLEGAL: ...` comment — which is every deliberate bad example
  in the spec. It now tolerates an inline comment and trailing whitespace, and
  catches all three (memory.md 150/159, lifetimes.md 27) instead of none.
- glossary.md §3.1, §3.33, §3.36: mirror memory.md §2.8's exclusion of field
  accesses whose base chain passes through a `'T` borrow. Without it the
  glossary made `borrowed.field` a legal guest source while the canonical rule
  rejected it.
- syntax.md §3.1, §3.2: scope the three passing modes to reference types. A
  value parameter is always a read-only borrow and a value receiver is borrowed,
  so neither selects a mode; the unqualified wording contradicted effects.md §2.
- memory.md §3.1: tag the segmented-offset diagram fence `text`. It was the only
  untagged opening fence in spec/.
- memory.md §4.8: stop promising unchanged reuse order under thread-local anchor
  caches. Which free slot an allocation receives is unobservable from the source
  language, so §3.2's LIFO describes the central pool rather than binding an
  implementation; what a cache may not change is identity uniqueness, the §4.6
  retirement events, and the lifetime guarantees resting on them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
@TheLazyCat00

Copy link
Copy Markdown
Member Author

All five findings were valid and are fixed in a9b2a8b. One is worth calling out because it means the guard I added in this PR was not doing its job:

The bare-symbol guest-source grep was end-anchored, so it matched no line carrying a // ILLEGAL: ... comment — which is every deliberate bad example in the spec. It found one hit (a grammar metavariable) and missed all three real ones. With the comment tolerance it now catches memory.md 150 and 159 and lifetimes.md 27, which is what "eyeball every hit" was supposed to mean.

The thread-local anchor cache finding is answered on its own thread — it took the "reuse order is non-normative" branch rather than the "require centralized-stack behavior" one, and the reasoning is there.

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

@TheLazyCat00 Thanks for the detailed follow-up. I’ve noted the guard’s former end-anchor blind spot and the intended comment-tolerant validation behavior. I’ll review the updated PR, including the separate anchor-cache resolution.


✏️ Learnings added
Learnt from: TheLazyCat00
URL: https://github.com/zane-lang/spec/pull/151

Timestamp: 2026-07-30T21:28:09.958Z
Learning: In PR `#151`, the bare-symbol guest-source validation grep must tolerate trailing `// ILLEGAL: ...` comments so it catches the deliberate invalid examples in `spec/memory.md` and `spec/lifetimes.md`; reviewers should manually inspect every resulting match. An end-anchored pattern previously missed those examples.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
spec/memory.md (1)

174-174: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Scope the non-escaping rule to reference-type borrows.

Line 174 says that a borrow cannot be stored in a field, but value-type parameters are also read-only borrows and may be copied into field or return storage. Change this to “a reference-type borrow ('T)” so the rule does not contradict the value-type copy semantics stated in the same paragraph.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@spec/memory.md` at line 174, Update the borrow definition in the paragraph to
scope the non-escaping restrictions—no anchor, field storage, or return—to
reference-type borrows (`'T`). Preserve the following value-type semantics that
parameters are read-only borrows and are copied when stored in fields or
returns.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 62-68: Update the guest-source guard regex and its surrounding
explanation in CLAUDE.md to recognize identifiers containing underscores, tab or
other whitespace around the assignment, and field accesses such as car.engine.
Keep the trailing comment-aware matching behavior intact and ensure the
documented invalid and valid forms align with the regex.

In `@spec/glossary.md`:
- Line 228: Update the guest definition’s minting rule to require that a field
access has a place as its base and that its base chain does not pass through a
'T borrow, matching the canonical wording in spec/memory.md. Replace the broader
“field access not rooted in a borrow” wording while preserving the other guest
semantics.

---

Outside diff comments:
In `@spec/memory.md`:
- Line 174: Update the borrow definition in the paragraph to scope the
non-escaping restrictions—no anchor, field storage, or return—to reference-type
borrows (`'T`). Preserve the following value-type semantics that parameters are
read-only borrows and are copied when stored in fields or returns.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: zane-lang/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ad687e35-cb17-4fe2-a4e7-3cb080cf664d

📥 Commits

Reviewing files that changed from the base of the PR and between 00de7e8 and a9b2a8b.

📒 Files selected for processing (4)
  • CLAUDE.md
  • spec/glossary.md
  • spec/memory.md
  • spec/syntax.md

Comment thread CLAUDE.md Outdated
Comment thread spec/glossary.md Outdated
- memory.md §2.9: the borrow paragraph said a borrow "cannot be stored in a
  field, and cannot be returned" and then that a value is copied when bound
  into "a field or return store" — a flat contradiction. Split the two: the
  borrow is never storage, but binding *through* one copies the value, and the
  copy escapes fine. A reference type has no copy, so a `'T` leaves nothing.
- glossary.md §3.33: restore the place requirement the shortened wording lost.
  "A field access not rooted in a borrow" would have admitted
  `makeCar().engine`; mirror memory.md §2.8 in full.
- CLAUDE.md: the guard now accepts a leading-underscore private name
  (`_engine`) and tab-formatted assignments. Prose rewritten to say what the
  guard does — it matches a bare-symbol right-hand side only, so every hit is a
  candidate bug and the legal forms never appear.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
@TheLazyCat00

Copy link
Copy Markdown
Member Author

Second round handled in fe62f1a. The glossary and CLAUDE.md findings are answered on their threads; the third arrived outside the diff range, so here:

memory.md §2.9, the borrow paragraph. The finding was right and it was a flat self-contradiction: the paragraph said a borrow "cannot be stored in a field, and cannot be returned" and then, two clauses later, that a value is copied when bound into "a field or return store". Both sentences were mine and they cannot both hold as written.

The fix separates the borrow from what is read through it, rather than scoping the rule to 'T as suggested. A value borrow cannot be stored as a borrow either, so restricting the sentence to reference types would have been the wrong repair. What is actually true: the borrow is never storage, and binding through one into a fresh slot copies the value — the copy escapes fine, the borrow does not. A reference type has no copy, so a 'T leaves nothing behind at all.

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- commit fe62f1a summary ---'
git show --stat --oneline --decorate --no-renames fe62f1a

echo
echo '--- focused diff: spec/memory.md ---'
git show --format=fuller --no-ext-diff --unified=35 fe62f1a -- spec/memory.md

echo
echo '--- outline ---'
ast-grep outline spec/memory.md --items all | sed -n '1,220p'

echo
echo '--- borrow/storage/return references with context ---'
rg -n -i -C 4 \
  '(§?2\.9|borrow|borrowed|field|return(?:ed)?|store|storage|copy|reference type)' \
  spec/memory.md

Length of output: 59800


@TheLazyCat00 Confirmed. The revised §2.9 wording correctly separates the non-escaping borrow from a value read through that borrow.

spec/memory.md Lines 174-176 now consistently establish that:

  • a borrow itself is never storage and cannot be stored or returned;
  • binding a value through a value borrow creates a distinct copied value that may escape in a field or return slot; and
  • a reference-type 'T borrow creates no copy, so it leaves no storable or returnable value behind.

This also agrees with the surrounding parameter-only and non-escaping 'T rules. No further concern on this change.

🐇🔍


✏️ Learnings added
Learnt from: TheLazyCat00
URL: https://github.com/zane-lang/spec/pull/151

Timestamp: 2026-07-30T21:36:39.095Z
Learning: In PR `#151`'s Zane memory model, `spec/memory.md` §2.9 distinguishes a borrow from a value read through it: a borrow is non-hosting, non-escaping access and is never storage, so it cannot be stored or returned. Binding through a value-type borrow into a fresh assignment, declaration, field, or return slot copies the value and that independent copy may escape; a reference-type `'T` borrow has no such copy and leaves nothing behind.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
spec/glossary.md (1)

198-199: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Clarify that only the borrow capability is non-movable.

The entry says value types are passed by borrow and may be copied into fresh storage, but then says a borrow “cannot be ... moved.” That can be read as forbidding movement/copying of the value itself. Specify that the borrow itself cannot be stored, returned, or moved, while values read through a value borrow may be copied.

Proposed wording
- Unlike a guest, a borrow has no anchor or tether and cannot be stored, returned, or moved.
+ Unlike a guest, the borrow itself has no anchor or tether and cannot be stored, returned, or used as a move source.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@spec/glossary.md` around lines 198 - 199, Update the glossary entry’s “Why
this name” text to clarify that the borrow capability itself cannot be stored,
returned, or moved, while values read through a value borrow may still be copied
into fresh storage. Preserve the existing distinction between borrowing caller
storage and copying the borrowed value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 62-69: Update the guidance for reviewing grep hits so bare RHS
symbols remain candidates by default, but are retained when nearby declarations
establish the RHS as an &T parameter and therefore a legal guest source.
Continue excluding field-access sources syntactically, and keep deliberate //
ILLEGAL: examples or grammar metavariables as non-fixes.

---

Outside diff comments:
In `@spec/glossary.md`:
- Around line 198-199: Update the glossary entry’s “Why this name” text to
clarify that the borrow capability itself cannot be stored, returned, or moved,
while values read through a value borrow may still be copied into fresh storage.
Preserve the existing distinction between borrowing caller storage and copying
the borrowed value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: zane-lang/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7b3fde42-9ddd-4fd0-afed-80d06c9dc710

📥 Commits

Reviewing files that changed from the base of the PR and between a9b2a8b and fe62f1a.

📒 Files selected for processing (3)
  • CLAUDE.md
  • spec/glossary.md
  • spec/memory.md

Comment thread CLAUDE.md Outdated
- CLAUDE.md: the guard's prose claimed the legal sources "never match". That is
  true only of a field access, which `.` excludes syntactically. An `&T`
  parameter is written bare, so `r &Node = source` inside a callee matches and
  is correct — the reviewing rule now lists it as a keep, checked against the
  enclosing signature rather than the line.
- glossary.md §3.27: the borrow entry still carried the contradiction fixed in
  memory.md §2.9 — "cannot be stored, returned, or moved" against a value being
  copied into a fresh slot. Scope it to the borrow itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
@TheLazyCat00

Copy link
Copy Markdown
Member Author

Third round handled in 84c3505. The CLAUDE.md finding is answered on its thread; the glossary one arrived outside the diff range, so here:

glossary.md §3.27, the borrow entry. Same contradiction I fixed in memory.md §2.9 last round, still sitting in the glossary's "Why this name" — "cannot be stored, returned, or moved" against the entry's own statement that a value is copied when bound into a fresh slot. Now scoped to the borrow itself: it cannot be stored, returned, or used as a move source, which is a restriction on the borrow and not on the value read through it.

Worth noting the pattern, since it is the same defect twice: I fixed the canonical rule and left the glossary summarizing the old version of it. The glossary is a summary of the spec, so a correction to a rule has to be chased into its entry in the same change.

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- commit summary ---'
git show --stat --oneline --no-renames 84c3505

echo
echo '--- focused diff: glossary and canonical memory rule ---'
git show --format=fuller --no-ext-diff --unified=30 84c3505 -- spec/glossary.md spec/memory.md

echo
echo '--- glossary borrow entry candidates ---'
rg -n -i -C 12 '(^#{1,6} .*borrow\b|Why this name|cannot be stored|fresh slot|cop(?:y|ied)|move source)' spec/glossary.md

echo
echo '--- canonical borrow-rule candidates ---'
rg -n -i -C 10 '(§?2\.9|borrow itself|cannot be stored|fresh slot|cop(?:y|ied)|reference-type)' spec/memory.md

Length of output: 78376


@TheLazyCat00 Confirmed. spec/glossary.md Line 199 now correctly scopes the restriction to the borrow itself: it cannot be stored, returned, or used as a move source. It also preserves the distinct rule that a value read through a value borrow may be copied into a fresh slot.

That matches the canonical wording in spec/memory.md Lines 174-176, including the distinction that reference-type 'T borrows produce no independent copy. No further concern on this correction.

🐇🔍


✏️ Learnings added
Learnt from: TheLazyCat00
URL: https://github.com/zane-lang/spec/pull/151

Timestamp: 2026-07-30T21:45:45.425Z
Learning: For the Zane specification, when a canonical semantic rule changes, review and update its corresponding `spec/glossary.md` entry in the same change. In particular, `spec/glossary.md` §3.27 must preserve the distinction in `spec/memory.md` §2.9: a borrow itself is non-storable, non-returnable, and not a move source, while a value read through a value-type borrow may be copied into a fresh slot and escape independently.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Two findings on #151 were the same defect: a rule was corrected in its
canonical home and its glossary entry left paraphrasing the superseded
version, so the spec contradicted itself until review caught it. Both times
the author missed it, which makes it a habit worth writing down rather than a
one-off slip.

Adds it to the "Before you edit" list: after editing any normative rule, grep
spec/glossary.md for the concept and update the entry in the same commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
@TheLazyCat00

Copy link
Copy Markdown
Member Author

Two commits are still unreviewed — 84c3505 hit the review limit and 6b0c909 was skipped as an incremental push. Both are small: 84c3505 is the round-three fixes (already confirmed on their threads), and 6b0c909 adds the glossary-check habit to CLAUDE.md's "Before you edit" list.

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

@TheLazyCat00 I’ll review the previously skipped commits, including the glossary synchronization guidance in CLAUDE.md.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
spec/glossary.md (1)

242-246: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Clarify that borrow mode is only the non-swallowing way to pass a bare symbol.

This sentence conflicts with §3.34 and §3.37: a bare hosting symbol may also be passed to a T swallowing parameter, while 'T is the only non-swallowing mode that accepts it. Please change this to “Passing a bare symbol without swallowing it is the borrow mode’s job” or equivalent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@spec/glossary.md` around lines 242 - 246, Update the “guest source
restriction” Meaning text so the bare-symbol call rule states that borrow mode
is the only non-swallowing way to pass a bare symbol, while still allowing a
bare hosting symbol to be passed to a T swallowing parameter.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@spec/glossary.md`:
- Around line 242-246: Update the “guest source restriction” Meaning text so the
bare-symbol call rule states that borrow mode is the only non-swallowing way to
pass a bare symbol, while still allowing a bare hosting symbol to be passed to a
T swallowing parameter.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: zane-lang/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9878985d-0eb3-4f05-8638-bc7145cf3e3a

📥 Commits

Reviewing files that changed from the base of the PR and between fe62f1a and 6b0c909.

📒 Files selected for processing (2)
  • CLAUDE.md
  • spec/glossary.md

"Passing a bare symbol into a call is the borrow mode's job" overstated it: a
bare symbol is a move-source (lifetimes.md §1.2), so a plain `T` parameter
swallows one perfectly well. `'T` is the only *non-swallowing* mode that
accepts a bare symbol, which is the claim the surrounding rules actually
support.

Corrected in all three places that carried it — memory.md §2.8.1 (canonical),
glossary.md §3.36, and the foundations.md bullet — rather than only where the
review pointed, since the imprecision started in the canonical home and
propagated from there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
@TheLazyCat00

Copy link
Copy Markdown
Member Author

Valid, and fixed in 99e143b. The finding arrived outside the diff range, so answering here.

"Passing a bare symbol into a call is the borrow mode's job" overstated it. A bare symbol is a move-source (lifetimes.md §1.2), so a plain T parameter swallows one perfectly well — 'T is only the non-swallowing mode that accepts a bare symbol, which is what the surrounding rules actually support.

I fixed it in all three places that carried the claim, not just the flagged one: memory.md §2.8.1 is the canonical home where the imprecision started, and glossary.md §3.36 and the foundations.md bullet had both inherited it. Which is the same propagation pattern as last round, and the reason 6b0c909 added the glossary check to CLAUDE.md — worth noting the check needs to run in the other direction too, since here the canonical text was the one at fault.

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- commit 99e143b ---'
git show --stat --oneline --no-renames 99e143b
git show --format=fuller --no-ext-diff --unified=22 99e143b -- \
  spec/memory.md spec/glossary.md spec/foundations.md

echo
echo '--- stated canonical contexts ---'
for f in spec/memory.md spec/glossary.md spec/foundations.md spec/lifetimes.md; do
  echo "### $f"
  rg -n -i -C 7 \
    '(bare symbol|move-source|move source|swallow|swallowing|non-swallowing|borrow mode|passing mode|'\''T\b|plain `?T`?)' \
    "$f" || true
done

echo
echo '--- remaining instances of the superseded claim ---'
rg -n -i -C 3 \
  '(passing a bare symbol.*borrow|bare symbol.*borrow mode|borrow mode.*bare symbol)' \
  spec || true

Length of output: 68579


@TheLazyCat00 Confirmed. Commit 99e143b correctly repairs the overstatement.

  • spec/lifetimes.md §1.2 establishes that a direct host symbol is a move-source, so plain T can swallow a bare symbol.
  • spec/memory.md §2.8.1 now correctly reserves 'T for the case where the call must accept that bare symbol without taking hosting.
  • spec/glossary.md §3.36 and the spec/foundations.md §7 summary use the same distinction.
  • The targeted search found no remaining occurrence of the superseded “borrow mode’s job” formulation.

The bidirectional synchronization point is well taken: summaries must follow canonical corrections, and reviewing a summary can reveal that its canonical source is wrong. No further concern.

🐇🔍


✏️ Learnings added
Learnt from: TheLazyCat00
URL: https://github.com/zane-lang/spec/pull/151

Timestamp: 2026-07-30T22:25:27.943Z
Learning: In the Zane specification’s reference-type passing modes, a bare direct host symbol is a move-source under `spec/lifetimes.md` §1.2 and may be passed to a plain swallowing `T` parameter. Borrow `'T` is specifically the only non-swallowing parameter mode that accepts a bare symbol; `&T` requires a guest source. When this distinction changes, synchronize `spec/memory.md` §2.8.1, `spec/glossary.md` §3.36, and the related `spec/foundations.md` summary.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@TheLazyCat00

Copy link
Copy Markdown
Member Author

@coderabbitai review

Re-trigger: the full pass over 99e143b hit the review limit. The targeted verification of that commit already ran and confirmed it, so this is just to complete the pass over spec/foundations.md, spec/glossary.md, and spec/memory.md.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

@TheLazyCat00 I’ll complete the full review pass over the remaining changes in spec/foundations.md, spec/glossary.md, and spec/memory.md.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@spec/foundations.md`:
- Line 97: Update the guest-source summary in the “A guest comes from a field”
bullet to match the canonical rule: guests may originate only from a qualifying
field access whose base is a place and does not pass through a 'T borrow, or
from an &T parameter; explicitly exclude bare symbols.

In `@spec/memory.md`:
- Line 165: Update the guest-source description in spec/memory.md to replace “a
field, a container element, a callee” with wording that identifies a qualifying
field access and an &T parameter, consistent with the terminology in §2.8.1.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: zane-lang/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1ed78a36-0387-4442-ba17-a52774f0b2a4

📥 Commits

Reviewing files that changed from the base of the PR and between 6b0c909 and 99e143b.

📒 Files selected for processing (3)
  • spec/foundations.md
  • spec/glossary.md
  • spec/memory.md

Comment thread spec/foundations.md Outdated
Comment thread spec/memory.md Outdated
- foundations.md §7: the bullet said a guest is minted "from a field access or
  an `&T` parameter", which over-approximates memory.md §2.8.1 — the base must
  be a place and must not be reached through a `'T` borrow. Named both
  qualifiers compactly rather than reproducing the rule; foundations.md is a
  map that defers to canonical homes.
- memory.md §2.8.1: "a field, a container element, a callee" listed where a
  guest may live, not what may mint one, and was read as the latter. Says it in
  the precise terms instead — an `&` field, a container element, an `&T`
  parameter inside a callee.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
Comment thread spec/memory.md Outdated

// `&` parameter is a guest; it may be stored into an `&` field
Unit setEngine(this Car, engine &Engine) mut {
Unit setEngine(this 'Car, engine &Engine) mut {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i thought the type for this is implicit borrow? either way, need to decide for one option.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, and it was specified both ways at once — the prose directly under these examples said a bare this T on a reference type is an implicit 'T borrow, and then every example wrote this 'Car. Decided in dba89ba: ' is never written on this.

The reason to go that way rather than the other is that the alternative isn't really available. If the marker were required on the receiver, then a bare this T would have to mean the remaining mode — swallow — so every method written without a marker would consume its own receiver. Nothing wants that. So the borrow has to be what bare this T means, and once it is, this 'T is just a second spelling for the default.

It also lines the two type worlds up. A value receiver is a borrow and is written bare; now a reference receiver is too. this carries at most one marker, &, and its absence means borrow in both cases — which puts the mark on the exceptional thing (keeping the receiver past the call) rather than on the ordinary one.

Applied across memory.md, functions.md, lifetimes.md, syntax.md, glossary.md, and effects.md. That included dropping the this 'ReceiverType production from syntax.md §3.2 and the matching function-type form in §2.9, so the grammar no longer admits the spelling at all.


Generated by Claude Code

Comment thread spec/memory.md Outdated

// plain reference-type parameter: taken by hosting access, then moved into a hosting field of this
Unit setSpare(this Car, engine Engine) mut {
Unit setSpare(this 'Car, engine Engine) mut {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same issue of using explicit '

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dba89ba along with the other four — ' is never written on this now. Reasoning is on the first thread.


Generated by Claude Code

Comment thread spec/memory.md Outdated
// `&` parameter, read only: a reference-type object passed without consuming it
Int inspect(this Car, engine &Engine) {
// borrow parameter: a reference-type object read without consuming it and without minting a guest
Int inspect(this 'Car, engine 'Engine) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same issue as above

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dba89ba — reasoning on the first thread.


Generated by Claude Code

Comment thread spec/memory.md Outdated
```zane
Unit setEngineWrong(this Car, engine Engine) mut {
this.engine = engine // ILLEGAL: a swallowed host is not an `&` source
Unit setEngineSwallowed(this 'Car, engine Engine) mut {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same issue as above

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dba89ba — reasoning on the first thread.


Generated by Claude Code

Comment thread spec/memory.md Outdated
return Unit()
}

Unit setEngineBorrowed(this 'Car, engine 'Engine) mut {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same issue as above

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dba89ba — reasoning on the first thread.


Generated by Claude Code

Comment thread stories/memory.md Outdated
What falls out is a memory model that is uniformly 32-bit and, per tethered object, exactly twelve bytes of machinery: the four-byte tether wherever it is stored, the four-byte anchor cell, and the four-byte backpointer the payload carries home to that cell. The double indirection a tether walks — tether to cell, cell to payload — looks like it should cost two cache misses, and the arena is what makes it cost closer to zero: the cell read is a load into arena memory that is almost always already warm. The first thing we tried for that warmth was to drop each cell right beside the payload that mints it, so the two share a cache line and the second hop is paid for by the first. It worked for the deref, and it opened a loose end the next chapter has to pull on — a cell sitting in the payload stream is a cell a payload-only scan has to step over, and a payload's position starts to depend on how many of its neighbours were tethered before it.

The one motion this has to survive is escape. A value that outlives its scope is moved into a parent, and under arenas that means its payload is *copied* into the parent's arena — the child arena is about to be unmapped and cannot keep it. The backpointer is what makes that copy invisible: the runtime follows it to the payload's one anchor cell and rewrites the cell with the payload's new location. Every tether still points at that same cell and never learns the payload moved ([`memory.md` §4.5](https://github.com/zane-lang/spec/blob/dc33d1eae46bfc71bfb7e8e3b2f575926aa00059/spec/memory.md#45-moves-overwrites-and-promotion-update-one-cell-not-all-tethers)) — the same one-cell-update that made an in-place move O(1) makes a cross-arena promotion O(1) too. When the scope finally drains, its chunks are unmapped and its cells vanish with the objects they served, and the scope rules that governed tethers all along guarantee nothing still pointing could point into them.
The one motion this has to survive is escape. A value that outlives its scope is moved into a parent, and under arenas that means its payload is *copied* into the parent's arena — the child arena is about to be unmapped and cannot keep it. The backpointer is what makes that copy invisible: the runtime follows it to the payload's one anchor cell and rewrites the cell with the payload's new location. Every tether still points at that same cell and never learns the payload moved ([`memory.md` §4.5](https://github.com/zane-lang/spec/blob/dc33d1eae46bfc71bfb7e8e3b2f575926aa00059/spec/memory.md#45-moves-overwrites-and-promotion-update-one-cell-not-all-tethers)). The one-cell update keeps anchor bookkeeping O(1) in the number of tethers; physical promotion still costs proportionally to the representation copied. When the scope finally drains, its chunks are unmapped and its cells vanish with the objects they served, and the scope rules that governed tethers all along guarantee nothing still pointing could point into them.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's okay to change, just clarify the motivation behind changing it. it's from pr 147 but still important to take a look at

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, and you're right that it wasn't clear. Addressed in dba89ba, though by adding rather than editing — this chapter is history, so the guide says append.

What this paragraph describes is the one-cell promotion model: the runtime follows the backpointer to the single anchor cell and rewrites it, so every tether keeps working and never learns the payload moved. That was true when it was written and it is not true now.

It stopped being true for a reason worth naming, which is what was missing. One-cell promotion works only while a payload has exactly one cell to rewrite. As soon as both sides of a move are already anchored — the source has guests and so does the destination — there are two cells and one backpointer, so the runtime can update one path or the other but not both. That is the pressure that eventually takes anchors out of scope arenas entirely and into the runtime-global pool, where a cell may target another cell and a superseded source identity becomes a forwarder instead of needing a rewrite.

The chapter now ends with a short paragraph naming both things that didn't survive — the cell moving out of the payload stream, and one-cell promotion failing on two anchored sides — each pointing at the chapter that takes it up. It also flags that the twelve-byte figure quoted here becomes sixteen later, since that number is the kind of thing a reader will otherwise carry forward as current.


Generated by Claude Code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but if it's no longer true, it should instead be mentioned in an appended chapter instead of being changed, because the past is not changable. this being said, we should try to not modify the story of the main branch

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, and my fix was the same violation again — bolting a forward pointer onto the end of an existing chapter is still editing that chapter. Reverted in 19806f0.

Diffing against main also turned up the thing your original comment was actually pointing at, which I'd missed. One sentence in this chapter had already been rewritten before I touched it — it came in with the PR 147 commits, replacing

— the same one-cell-update that made an in-place move O(1) makes a cross-arena promotion O(1) too.

with a two-clause version separating anchor bookkeeping from physical copy cost. So the chapter on this branch was not main's chapter. It is now byte-identical to main again.

The correction that was in my forward pointer has moved into the appended chapter, where it names the claim it retires rather than reaching back to amend it: the segmented-offset chapter has promotion follow the backpointer to the one cell and rewrite it, and that holds only while a payload has one cell to rewrite — it fails the moment both sides of a move are anchored, which is what takes anchors out of scopes.

git diff origin/main -- stories/memory.md | grep -E "^-[^-]" now prints nothing.


Generated by Claude Code

Comment thread stories/memory.md
claude added 3 commits July 31, 2026 10:00
Maintainer review on #151.

The receiver was specified two ways at once: memory.md §2.9 said a bare
`this T` on a reference type is an implicit `'T` borrow, and then every
example in that same section wrote `this 'Car`. Decided in favour of the
implicit form — `'` is never written on `this`.

The rule is forced rather than chosen. If the marker were required on the
receiver, a bare `this T` would have to mean the method swallows its own
receiver, which no method wants; so the borrow has to be the bare meaning,
and `this 'T` is then a second spelling for the default. It also makes the
two type worlds agree: value and reference receivers are both borrows and
both written bare, with `&` the single marker `this` may carry.

Applied to memory.md, functions.md, lifetimes.md, syntax.md, glossary.md,
and effects.md — including dropping the `this 'ReceiverType` production from
syntax.md §3.2 and the function-type form in §2.9.

stories/memory.md: the segmented-offset chapter still ends on the one-cell
promotion model, which the later global-pool chapter superseded. Stories are
append-only, so rather than rewrite it, that chapter now carries a forward
pointer naming what did not survive and why — the cell leaving the payload
stream, and one-cell promotion failing once both sides of a move are
anchored.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
Maintainer ruling on #151: the story is append-only, and that is verified by
diffing against main — an existing chapter is not edited, and a new chapter
goes at the end rather than being slotted into the middle.

Two violations, both now gone:

- One sentence in "The last table problem, and the segmented offset" had been
  rewritten (it came in with the PR 147 commits, and I then appended a
  forward-pointer paragraph to the same chapter, which is the same violation
  again). Both reverted; that chapter is byte-identical to main.
- "Two payload streams, and the anchor that leaves the scope" had been
  inserted between two chapters that already existed on main. Moved to the
  append position, after "Two vocabularies" and before the chapters this PR
  adds — which is also the right chronology, since the arena and anchor work
  came after the host/guest rename.

The correction the forward pointer was carrying now lives where it belongs,
in the appended chapter: it names the one-cell promotion claim from the
segmented-offset chapter and says what retires it — that the claim holds only
while a payload has one cell to rewrite, and fails once both sides of a move
are anchored.

`git diff origin/main -- stories/memory.md` is now additions only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
The story guide licensed exactly what review just rejected: §5 said to "open a
new chapter (or extend the relevant one)", which reads as permission to edit an
existing chapter, and it said nothing about where a new chapter goes. Both
gaps were used in this PR — a chapter was edited and another was inserted
between two that already existed.

§5 now spells out the two teeth: an existing chapter is not touched at all,
not even to bolt a forward pointer onto its end, and a new chapter goes at the
end of the file rather than into the middle. The correction a forward pointer
would carry belongs in the new chapter, naming the older chapter's claim.

Both are mechanically checkable, so the guide and CLAUDE.md now carry the
check — `git diff origin/main -- stories/<topic>.md | grep -E "^-[^-]"` must
print nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
Comment thread spec/memory.md Outdated
```

Binding a plain (swallowed) parameter into `&` storage is illegal, because a swallowed value is hosted at the call site while an `&` field lives with the object that holds it — which may outlive the call, leaving the `&` dangling:
A reference-type receiver borrows by default, and **`'` is never written on `this`**: a bare `this T` on a reference type *is* the borrow. It has to be — if the marker were required here, bare `this T` would mean the method swallows its own receiver, which is not something a method wants. A method that needs to keep or hand back the receiver writes `this &T`, the guest receiver (see [`functions.md`](functions.md) §2.4). So `this` carries at most one marker, `&`, and its absence means borrow for value and reference receivers alike.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i thought the default T is swallowing?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is — on an ordinary parameter. The receiver is the exception, and my justification for that was wrong, so I've rewritten it in 5b7616a.

What I had written was that the borrow "has to be" the bare meaning because otherwise a method would swallow its own receiver. That argues from convenience and implies the swallow reading was ever a candidate. It wasn't. main already says it:

For a reference-type receiver, this is an implicit & reference to the object (never swallowed).

and memory.md §2.9 on main says the same — "this is an implicit guest to the object, never swallowed". So the receiver has never been a swallow position, and "bare T swallows" is a rule about ordinary parameters that the receiver was already outside of.

What this PR actually changes is narrower than it looked: only which non-swallowing mode a bare receiver is. It was an implicit guest; it is now an implicit borrow, because the receiver expression at a call site is usually a bare symbol and §2.8.1 no longer admits one as a guest source. this therefore picks between two of the three modes, never the third.

§2.9 now says that outright, including the part you'd otherwise have to notice yourself — that bare T does not mean the same thing in both positions — rather than implying a consistency that isn't there. The mode table is scoped to parameters, and functions.md §2.4 and both summary rows match.


Generated by Claude Code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what exactly do you mean by "the receiver"?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair — the word was doing three jobs at once and this rule is about only one of them. Split in ad39224:

  • the receiver is the object the method is called on;
  • the receiver parameter is this, whose surface form fixes the passing mode;
  • the receiver expression is what stands left of : / ! at the call site and supplies the object.

"Never a swallow position" is a claim about the second one — the declared parameter — so the line now reads "The receiver parameter is never a swallow position" and says this, the first parameter and only it. The third sense is what carries the reason it moved to the borrow (a call-site receiver expression is usually a bare symbol, which §2.8.1 no longer admits as a guest source), and the first is what "stores or returns the receiver" means.

Defined once in functions.md §2.1 with a glossary entry at §3.38, and the loose uses in the passages this PR touches are now spelled with whichever sense applies — including two summary rows that said "reference receiver" where they meant reference-type this.


Generated by Claude Code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh, i overlooked that. so the original sentence was actually right too.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was — the sentence was true as written, it just needed the reader to already know which sense of "receiver" was meant. That the question came up at all is the argument for keeping the split: the word is used across functions.md, memory.md, effects.md and concurrency.md and was defined nowhere, so a fresh reader had to infer it from context each time.

Keeping ad39224 unless you'd rather have it out; it changes no rule, only which words state them.


Generated by Claude Code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we call it receiver though? what does it receive?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing, in Zane. It's inherited from Smalltalk, where a call was a message and the object received the message. Every language that copied the object-dot-method shape carried the word along — Go, Swift, Ruby, Java's docs — long after the messages were gone.

Zane doesn't have messages. A method is a package-scope verb whose first parameter is this, and player!setScale(2) desugars to Pkg$setScale(player, 2). So by the test in naming-terms.md §3 the word neither fights nor feeds — it's simply dead metaphor, a label with no analogy left under it. That's exactly the "flat and descriptive" failure the guide rejects origin form for, except worse: origin form at least described something true.

If it's worth replacing, the candidate the repo has already half-committed to is subject. The spec calls callables verbs (naming-terms.md §1) — so grammar is already the register, and grammar supplies the missing word for free: subject–verb–object. player!setScale(2) is subject player, verb setScale, object 2. It feeds rather than fights (§3): a fresh reader's sense of "subject" points at the thing the verb acts from, which is what this is. It's short and ordinary in dense prose (§2.3), and it makes the whole parameter list nameable in one register instead of one word from Smalltalk and one from grammar.

Against it: subject has a prior programming claim (observer-pattern Subject, and "subject" as a security principal). Weaker than the matrix case the guide rejects, but not zero.

Two things worth saying before you decide:

  1. It's a real rename, not a word swap. 118 occurrences across 12 spec docs and 9 stories — and the stories are append-only, so the ones there stay as they are and the term ends up split across the two trees by construction.
  2. It also owes a story passage. §6 makes a coined term's rejected candidates part of the design history, so this would need a chapter in stories/functions.md arguing subject over receiver — which means it's an interview, not something I should draft alone.

My recommendation is to do it, but not in this PR#151 is about passing modes and is otherwise finished, and a 118-site rename would bury that diff. Happy to open it as its own branch. Say the word either way.


Generated by Claude Code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, subject is way better than receiver. so yes, please rename. also, it is important to mention that the story is append only per pr, not per commit, so you can rewrite the story this pr appends and insert the renaming at the beginning of the "story-pr-section".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — 97e5bf2 (rename) and dd1cbcb (story).

The rename. 91 sites across 12 spec docs, plus README.md and naming-terms.md. Four headings changed, which moves their anchors; every story link into them is a pinned permalink, so they all still resolve at their own commits — nothing to chase. Two hits were a different sense of the word and did not become subject: functions.md §7.2–7.3 used "receiver" for the verb taking a lambda argument, which is the callee, not the subject. Those now say callee.

subject is added to the term list in naming-terms.md §1 alongside verb, and the glossary's "why this name" now carries the grammar argument.

The story. "What does a receiver receive?" is appended to stories/functions.md — the term's home doc, and the only chapter this PR adds there, so it's first-and-only in that file's PR section. It records: that the word went unexamined because it arrived free with the object model; that the three-senses problem was the first symptom and your question was the one that landed; the Smalltalk message and its absence here; the dead-metaphor failure against §3 of the naming guide; grammar as the register verb had already chosen; and the candidates weighed — keeping receiver for cross-language familiarity, and target, which collides with "call target".

Two costs are stated rather than smoothed over: subject has prior claims (observer pattern, security principal), and the two doc trees now disagree by construction — merged chapters say "receiver" and always will, so a reader of the design history meets both words. The chapter ends by naming itself as the mapping between them.

On append-only per PR. That's a rule I had written too strictly into contributing/writing-stories-docs.md §5 and CLAUDE.md earlier in this PR — I'd said a chapter is frozen once written. Both now say the frozen unit is what has merged, and that a branch's own new chapters are drafts you may rewrite, reorder, or insert among until it lands. Worth noting the git diff origin/main check already encoded this correctly — it diffs against what's published, so it stays quiet while you rearrange your own branch and fires only when something merged moves. I'd been applying a stricter rule than my own check.

That clarification is also what let me update the two places this PR's own new chapters said "receiver" (stories/memory.md, stories/lifetimes.md) rather than leaving them inconsistent with the spec on day one.

Validation: both guards clean at the expected four hits, story diff against main is additions-only, and both new permalinks verified to resolve at 97e5bf2.

One thing I'd like you to check: the story's account of how the decision came about. I wrote it from this thread — the word survived unexamined until the passing-mode rule forced a sentence about this, the three-senses split came first and didn't fix it, then "what does it receive?" landed and nothing answered. If the actual order in your head was different, say so and I'll replace it; per the repo's own rule your account is the source of truth for the narrative, not my reconstruction of it.


Generated by Claude Code

claude added 4 commits July 31, 2026 13:44
Maintainer asked why bare `T` borrows on `this` when the default for `T` is
swallowing. Fair — the justification I had written was wrong. It said the
borrow "has to be" the bare meaning because otherwise a method would swallow
its own receiver, which implies the swallow reading was ever on the table.

It never was. main already says a reference receiver is an implicit `&`
reference "(never swallowed)", and memory.md §2.9 on main says the same. So
the receiver has never been a swallow position, and bare `T` meaning swallow
is a rule about ordinary parameters. What this PR changes is only *which*
non-swallowing mode a bare receiver is — guest before, borrow now — because
the call-site receiver is usually a bare symbol, which §2.8.1 no longer
admits as a guest source.

§2.9 now states that, says plainly that bare `T` does not mean the same thing
in both positions, and names the pre-existing rule that makes it consistent
rather than arguing from convenience. The §2.9 mode table is scoped to
parameters, and functions.md §2.4 and both summary rows match.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
"Receiver" was doing three jobs at once — the object a method is
called on, the `this` parameter that fixes the passing mode, and the
call-site expression that supplies the object — and the rule that it is
never a swallow position is about the second of those, not the first.

Define the three in functions.md §2.1, add a glossary entry, and use
the precise term wherever the distinction carries weight.
"Receiver" is Smalltalk residue — a call was a message and the object
received it. Zane has no messages, so the word named nothing: it neither
fought its old meaning nor fed the new one, it was simply dead metaphor.

"Subject" puts the concept in the register the spec already chose when
it named callables verbs. A call reads subject-verb-object, and the
subject is what the verb acts from.

Merged story chapters keep the old word — they record the language as it
was — so the term is split across the two trees by construction. Also
records that append-only is per pull request, not per commit: chapters a
branch has not merged yet are still drafts.
Appends "What does a receiver receive?" to stories/functions.md: the
word arrived free with the object model and went unexamined until a rule
about `this` forced us to say which of its three senses we meant, and
then until someone asked what a receiver receives. Records the dead
metaphor, the grammar register that supplied the replacement, the
candidates weighed, and the cost — merged chapters keep the old word, so
the two doc trees disagree by design.

Pointer added from functions.md §2.1; README row updated.
@TheLazyCat00 TheLazyCat00 changed the title Bare symbols are no longer guest sources; add the 'T borrow mode Bare symbols are no longer guest sources; add the 'T borrow mode; rename receiver to subject Jul 31, 2026
@TheLazyCat00

Copy link
Copy Markdown
Member Author

The failing Title check above is stale, not a real finding. It reports that "rename receiver to subject" is not in the changeset; the rename is 97e5bf2 and its story is dd1cbcb, both on the branch head. CodeRabbit's reviews are paused on this branch, so its walkthrough — and the check judged against it — predate those two commits. Verified on the pushed head:

$ git grep -c receiver origin/claude/guest-source-restriction-jk2m -- spec/
spec/functions.md:1

That single hit is the > **Story:** pointer naming the chapter "What does a receiver receive?", which keeps the old word deliberately. Everything else in spec/ says subject.

Not triggering a re-review for this — the title stands as written.


Generated by Claude Code

The rule lived in two places at once, and CLAUDE.md — which is agent
notes, not contributor documentation — had grown a full normative
restatement of it. Now the story guide §5 owns the rule and says how it
is enforced (by hand, by author and reviewer; there is no CI), and
CLAUDE.md keeps only the command to run and the two ways sessions have
actually got it wrong: editing a merged chapter, and refusing to edit an
unmerged one.

@TheLazyCat00 TheLazyCat00 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@TheLazyCat00
TheLazyCat00 merged commit d50c9aa into main Jul 31, 2026
1 check passed
@TheLazyCat00
TheLazyCat00 deleted the claude/guest-source-restriction-jk2m branch July 31, 2026 21:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants