Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use Symbol = Semantic.Symbols.Symbol; | |
| 3 | ||
| 4 | // One in-flight assignment-receiver shield: the field held exempt | |
| 5 | // from the call transfer, and whether a call occurred while it was | |
| 6 | // held (so the assignment drops the field once the receiver is read). | |
| 7 | class SHIELD_FRAME is | |
| 8 | symbol: Symbol public; | |
| 9 | hit: bool public; | |
| 10 | ||
| 11 | init(symbol: Symbol) is | |
| 12 | self.symbol = symbol; | |
| 13 | hit = false; | |
| 14 | si | |
| 15 | si | |
| 16 | ||
| 17 | // The set of assignment-target receivers currently exempt from the | |
| 18 | // call transfer. A member/index assignment's receiver is read before | |
| 19 | // its right-hand side runs, so a call there must not drop the | |
| 20 | // receiver's presence; it is shielded for the duration of the | |
| 21 | // assignment and dropped afterwards only if a call actually occurred. | |
| 22 | // | |
| 23 | // A stack, so an assignment nested in a lambda or block inside an | |
| 24 | // outer assignment's right-hand side shields its own receiver without | |
| 25 | // disturbing the outer one. Each frame records whether a shielded | |
| 26 | // call occurred. Owned by NARROWING_FLOW, which consults it in the | |
| 27 | // call transfer (`on_call`). | |
| 28 | class RECEIVER_SHIELD is | |
| 29 | _frames: Collections.LIST[SHIELD_FRAME]; | |
| 30 | ||
| 31 | init() is | |
| 32 | _frames = Collections.LIST[SHIELD_FRAME](); | |
| 33 | si | |
| 34 | ||
| 35 | // Shield `v` until its frame is released, returning the frame to | |
| 36 | // release with — null (a no-op frame) when `v` is null. Frames | |
| 37 | // stack for nested assignments. | |
| 38 | push(v: Symbol?) -> SHIELD_FRAME? is | |
| 39 | if !v? then | |
| 40 | return null; | |
| 41 | fi | |
| 42 | ||
| 43 | let frame = SHIELD_FRAME(v); | |
| 44 | ||
| 45 | _frames.add(frame); | |
| 46 | ||
| 47 | return frame; | |
| 48 | si | |
| 49 | ||
| 50 | // Release a frame from `push`. Returns true iff a call occurred | |
| 51 | // while it was shielding — the caller then drops the field, since | |
| 52 | // the receiver read that justified the shield is done and later | |
| 53 | // reads must observe the post-call state. | |
| 54 | release(frame: SHIELD_FRAME?) -> bool is | |
| 55 | if !frame? then | |
| 56 | return false; | |
| 57 | fi | |
| 58 | ||
| 59 | _frames.remove(frame); | |
| 60 | ||
| 61 | return frame.hit; | |
| 62 | si | |
| 63 | ||
| 64 | // True iff `v` is shielded by an in-flight assignment receiver. | |
| 65 | // Marks every frame shielding it as hit so its assignment drops | |
| 66 | // it once the receiver has been read. | |
| 67 | mark_if_shielded(v: Symbol) -> bool is | |
| 68 | let shielded mut = false; | |
| 69 | ||
| 70 | for frame in _frames do | |
| 71 | if frame.symbol == v then | |
| 72 | frame.hit = true; | |
| 73 | shielded = true; | |
| 74 | fi | |
| 75 | od | |
| 76 | ||
| 77 | return shielded; | |
| 78 | si | |
| 79 | si | |
| 80 | si |