# Milestones 20 / 20.5: `yield`, value blocks, and value if/loops. # # A `{ ... }`, an `if`, or a labeled `for`/`while` on the right of a declaration # or assignment is a *value source*: `yield ` supplies the value (the block # analogue of `return`). Every path must yield. Each section returns a distinct # code on failure; success falls through to 42. # --- value blocks (milestone 20) --------------------------------------------- # Untyped `::`: the local's type is the yield's natural type. basic func() i32 { x :: { a :: 20 b :: 22 yield a + b } return x } # Typed `T =`: the yield coerces to the annotation. typed func() i64 { x i64 = { yield 100 } return x } # The yielded value is captured before defers run: the defer mutates a block # local, but the captured value is unchanged. spill func() i32 { v :: { n i32 = 5 defer n = 999 yield n } return v # 5, not 999 } # Reassignment into an existing mutable local. reassign func() i32 { r i32 = 0 r = { yield 7 } return r } # --- value if-statements (milestone 20.5) ------------------------------------ # Untyped `::` over an `else if` chain; the first branch fixes the type. vif_untyped func(sel i32) i32 { r :: if (sel == 0) { yield 10 } else if (sel == 1) { yield 20 } else { yield 30 } return r } # Typed `T =`: every branch coerces to the annotation. vif_typed func(sel i32) i32 { r i32 = if (sel == 0) { yield 100 } else { yield 200 } return r } # Assigned into an existing local. vif_reassign func(sel i32) i32 { r i32 = 0 r = if (sel == 0) { yield 7 } else { yield 9 } return r } # A branch is a full value block: leading statements + a defer captured before # the yield. vif_defer func() i32 { r :: if (true) { n i32 = 5 defer n = 999 yield n } else { yield 0 } return r # 5 } # --- value loops (milestone 20.5) -------------------------------------------- # Labeled `for` used as a value: `yield :blk i` exits early with a value, the # trailing `yield none` supplies the value when the loop completes. The `{i, # none}` yields resolve the result to an optional. loop_search func() i32 { # first i in 0..10 whose square exceeds 40 (6*6=36 no, 7*7=49 yes -> 7). idx :: for 0..10 |i| blk: { if (i * i > 40) yield :blk i yield none } if idx |found| { if (found == 7) return 0 return 1 } return 2 } # Same loop, but nothing matches -> the fall-through `yield none` is the result. loop_none func() i32 { idx :: for 0..10 |i| blk: { if (i > 100) yield :blk i yield none } if idx |found| { _ = found return 1 # should be unreachable: no match } return 0 } # Labeled `while` value loop (label follows the `: update` clause). loop_while func() i32 { n i32 = 0 found :: while n < 100 : n += 1 blk: { if (n == 8) yield :blk n yield none } if found |v| { if (v == 8) return 0 return 1 } return 2 } # --- value if/loop follow-ups (milestone 20.6) ------------------------------- # A branch may exit on every path (here `return`) instead of yielding; the slot # read after the `if` is only reached on the yielding path. vif_return func(sel i32) i32 { r :: if (sel == 0) { yield 10 } else { return 55 # exits the function instead of yielding } return r + 1 # only when sel == 0: 10 + 1 = 11 } # unwrap-`if` as a value source: present -> transform, absent -> default. vif_unwrap func(opt ?i32) i32 { r :: if opt |v| { yield v * 2 } else { yield 99 } return r } # The simple "unwrap or fallback" case is just `orelse` (already a plain expression). orelse_value func(opt ?i32) i32 { r :: opt orelse 7 return r } # Untyped value loop where `none` is yielded (in a labeled yield) before any # concrete value: the element type still resolves to ? from `yield :blk i`. loop_none_first func() i32 { r :: for 0..10 |i| blk: { if (i > 100) yield :blk none if (i * i > 40) yield :blk i # first concrete yield: i == 7 yield none } if r |found| { if (found == 7) return 0 return 1 } return 2 } # --- labels: value blocks + outer-loop yield/break (milestone 20.7) ----------- # A labeled value block: `yield :blk` exits the block (past a nested `if`) with a # value. Every path must yield. lblock func(sel i32) i32 { r :: blk: { base :: 10 if (sel == 0) { yield :blk base } else { yield :blk base * 2 } } return r } # An early `yield :blk` skips the rest of the block; the value is captured before # the block's defer runs. lblock_defer func() i32 { r :: blk: { n i32 = 5 defer n = 999 if (true) yield :blk n yield :blk 0 } return r # 5, not 999 } # A labeled block whose `{T, none}` yields resolve the result to an optional. lblock_optional func(present i32) i32 { r :: blk: { if (present == 0) yield :blk none yield :blk 8 } if r |v| { return v } return -1 } # `yield :outer v` exits an OUTER value loop from inside an inner loop. yield_outer func(target i32) i32 { found :: for 0..3 |row| outer: { for 0..3 |col| { if (row * 3 + col == target) yield :outer (row * 10 + col) } yield none } if found |v| { return v } return -1 } # Plain `break :outer` exits an outer loop from an inner loop. break_outer func() i32 { count i32 = 0 for 0..3 |a| outer: { for 0..3 |b| { count += 1 if (a == 1 and b == 1) break :outer } } return count # 5 } # A labeled block *statement* (not a value source): `break :blk` exits it early. stmt_block func(early i32) i32 { x i32 = 0 blk: { x = 1 if (early == 1) break :blk x = 2 } return x # early == 1 -> 1, else 2 } # `break :search` escapes a nested loop and the block in one jump; the block's # defer still runs on the way out. stmt_block_escape func() i32 { hits i32 = 0 search: { defer hits += 1000 for 0..10 |i| { hits += 1 if (i == 3) break :search } hits += 100 # skipped by break :search } return hits # 4 + 1000 (defer) = 1004 } # Item B: a `none` yielded before a concrete `yield :blk` that references a block local. lblock_local func() i32 { r :: blk: { val :: 9 if (false) yield :blk none yield :blk val } if r |v| { return v } return -1 } main func() i32 { if (basic() != 42) return 101 if (typed() != 100) return 102 if (spill() != 5) return 103 if (reassign() != 7) return 104 if (vif_untyped(0) != 10) return 105 if (vif_untyped(1) != 20) return 106 if (vif_untyped(2) != 30) return 107 if (vif_typed(0) != 100) return 108 if (vif_typed(1) != 200) return 109 if (vif_reassign(0) != 7) return 110 if (vif_reassign(9) != 9) return 111 if (vif_defer() != 5) return 112 if (loop_search() != 0) return 113 if (loop_none() != 0) return 114 if (loop_while() != 0) return 115 if (vif_return(0) != 11) return 116 if (vif_return(1) != 55) return 117 if (vif_unwrap(21) != 42) return 118 if (vif_unwrap(none) != 99) return 119 if (orelse_value(5) != 5) return 120 if (orelse_value(none) != 7) return 121 if (loop_none_first() != 0) return 122 if (lblock(0) != 10) return 123 if (lblock(1) != 20) return 124 if (lblock_defer() != 5) return 125 if (lblock_optional(0) != -1) return 126 if (lblock_optional(1) != 8) return 127 if (yield_outer(4) != 11) return 128 if (yield_outer(99) != -1) return 129 if (break_outer() != 5) return 130 if (stmt_block(1) != 1) return 131 if (stmt_block(0) != 2) return 132 if (stmt_block_escape() != 1004) return 133 if (lblock_local() != 9) return 134 return 42 }