Goal
Add concise custom iteration syntax for ordinary calls that return optional values, while preserving built-in range, array, and slice loops.
Contract
- Custom syntax is
for item in producerCall().
producerCall() is an ordinary free-function, method, or pipe call.
- The call must return
?T; a present result binds one item of type T, and none ends iteration.
- The complete call evaluates on every attempt, including the terminating attempt. This includes its callee, receiver, and arguments.
continue starts another producer-call attempt; break and return do not advance again.
- Custom loops provide one item binding, not an implicit index. Built-in
index, value sequence behavior remains unchanged.
- Bare optional values, function values without a call, and objects with a
Next method are not producers unless the method call is written explicitly.
- Ordinary call dispatch remains authoritative: methods of any name, free functions, pipe calls, default arguments, borrow/move adaptation, effects, provenance, and cleanup all use existing call semantics.
- No implicit
Next protocol, source capture, iterator interface, vtable, boxing, generic constraint, or tuple prerequisite is added.
Architecture
The typechecker expands valid optional-producing call loops into ordinary checked call, optional-test, payload-binding, and loop-control statements before CFG, flow, effects, definite-init, ownership, cleanup, HIR, and MIR. Downstream phases consume the checked expansion and do not rediscover producer calls.
The expansion intentionally keeps the complete producer call inside the loop because ordinary call evaluation occurs once per attempt. Existing call checking owns receiver adaptation, default arguments, move/borrow behavior, reference provenance, effects, and cleanup.
Coverage
- Free-function, method, and pipe producers.
- Scalar, aggregate, owned-pointer, nested-optional, and legal reference items.
- Receiver borrowing, consumption, moves, body access, conflicting borrows, continue, break, return, empty loops, and nested exits.
- Negative coverage for non-optional calls, bare optionals, function values, index bindings, invalid arguments, move/borrow errors, and unsupported item shapes.
- Language specification, LSP hover coverage, semantic phase tests, and bundled runtime fixtures.
Validation
- Focused typechecker, ownership, effect, definite-init, CFG, HIR, MIR, and LSP tests.
go test ./...
go test -race ./...
go run ./scripts/bundle.go
PEEPER_BIN=$PWD/build/bin/peeper go test -count=1 ./x_test
gofmt and git diff --check
Closes #123
Goal
Add concise custom iteration syntax for ordinary calls that return optional values, while preserving built-in range, array, and slice loops.
Contract
for item in producerCall().producerCall()is an ordinary free-function, method, or pipe call.?T; a present result binds one item of typeT, andnoneends iteration.continuestarts another producer-call attempt;breakandreturndo not advance again.index, valuesequence behavior remains unchanged.Nextmethod are not producers unless the method call is written explicitly.Nextprotocol, source capture, iterator interface, vtable, boxing, generic constraint, or tuple prerequisite is added.Architecture
The typechecker expands valid optional-producing call loops into ordinary checked call, optional-test, payload-binding, and loop-control statements before CFG, flow, effects, definite-init, ownership, cleanup, HIR, and MIR. Downstream phases consume the checked expansion and do not rediscover producer calls.
The expansion intentionally keeps the complete producer call inside the loop because ordinary call evaluation occurs once per attempt. Existing call checking owns receiver adaptation, default arguments, move/borrow behavior, reference provenance, effects, and cleanup.
Coverage
Validation
go test ./...go test -race ./...go run ./scripts/bundle.goPEEPER_BIN=$PWD/build/bin/peeper go test -count=1 ./x_testgofmtandgit diff --checkCloses #123