| | 1 | = `$choose` statement = |
| | 2 | |
| | 3 | == current syntax == |
| | 4 | {{{ |
| | 5 | $choose{ |
| | 6 | S1 |
| | 7 | S2 |
| | 8 | ... |
| | 9 | default: Sn |
| | 10 | } |
| | 11 | }}} |
| | 12 | `Sk` (`k=1..n`) could be any statement: a compound statement, any kind of atomic statement, a guarded statement, any other statement (if-else, while, for, ...). |
| | 13 | |
| | 14 | |
| | 15 | === example === |
| | 16 | |
| | 17 | {{{ |
| | 18 | $choose{ |
| | 19 | $when(x<0){$when(y>0){int k; k=x+y;}} |
| | 20 | $when(1){ |
| | 21 | $comm_enqueue(comm, out); |
| | 22 | in = $comm_dequeue(comm, source, recvtag); |
| | 23 | } |
| | 24 | $when(1){ |
| | 25 | in = $comm_dequeue(comm, source, recvtag); |
| | 26 | $comm_enqueue(comm, out); |
| | 27 | } |
| | 28 | } |
| | 29 | }}} |
| | 30 | |
| | 31 | === the result of the current translation would be === |
| | 32 | |
| | 33 | `TRANS1`: |
| | 34 | |
| | 35 | {{{ |
| | 36 | LOC0: |
| | 37 | [x<0 && y>0] k=x+y -> LOC3 |
| | 38 | [true] $comm_enqueue(...) -> LOC1 |
| | 39 | [comm_probe()] $comm_dequeue(...) -> LOC2 |
| | 40 | |
| | 41 | LOC1: |
| | 42 | [$comm_probe()] $comm_dequeue(...) -> LOC3 |
| | 43 | |
| | 44 | LOC2: |
| | 45 | [true] $comm_enqueue(...) -> LOC3 |
| | 46 | |
| | 47 | LOC3: |
| | 48 | ... |
| | 49 | }}} |
| | 50 | |
| | 51 | === problem of current translation (`TRANS1`) === |
| | 52 | 1. when there are new scope introduced by the any clause, the clauses can't share the same source location in the model, because a location is associated with a unique scope; for example, at `LOC0` in `TRANS1`, the first statement accesses `k` but `k` isn't visible in the scope associated to `LOC0` (`LOC0` is corresponding to the scope at `$choose`) |
| | 53 | 2. a quick solution for 1 is introduce a noop at the beginning of each clause, and move the guard of the first statement of the clause to the noop; this would translate the example into `TRANS2`: |
| | 54 | |
| | 55 | `TRANS2`: |
| | 56 | {{{ |
| | 57 | LOC0: |
| | 58 | [x<0 && y>0] noop -> LOC1 |
| | 59 | [true] noop -> LOC2 |
| | 60 | [comm_probe()] noop -> LOC3 |
| | 61 | |
| | 62 | LOC1: |
| | 63 | [true] k=x+y; -> LOC |
| | 64 | |
| | 65 | LOC2: |
| | 66 | [true] $comm_enqueue(...) -> LOC4 |
| | 67 | |
| | 68 | LOC3: |
| | 69 | [true] $comm_dequeue(...) -> LOC5 |
| | 70 | |
| | 71 | LOC4: |
| | 72 | [$comm_probe()] $comm_dequeue(...) -> LOC6 |
| | 73 | |
| | 74 | LOC5: |
| | 75 | [true] $comm_enqueue(...) -> LOC6 |
| | 76 | |
| | 77 | LOC6: |
| | 78 | ... |
| | 79 | }}} |
| | 80 | |
| | 81 | === problem of `TRANS2` === |
| | 82 | There are still a few problems in the above translation (`TRANS2`): |
| | 83 | |
| | 84 | 1. it breaks the checking of potential deadlock. CIVL will report a false alarm of a potential deadlock. |
| | 85 | 2. the extra noop changes the semantics slightly: for example, if at LOC0, the first noop is chosen to executed, i.e., `x<0 && y>0` is evaluated to be true; and then another process updates the value of `x` or `y` and then `x<0 && y>0` is no longer true, but since the process is now at LOC1, so the statement `k=x+y` will still be executed. This conflicts with the semantics of the guarded statement `$when(x<0){$when(y>0){int k; k=x+y;}}`. |
| | 86 | |
| | 87 | == new solutions == |
| | 88 | |
| | 89 | === new syntax === |
| | 90 | |
| | 91 | - requires curly braces in each clause of `$choose` , not allowing clauses to introduce new scopes |
| | 92 | {{{ |
| | 93 | choose_stmt: ‘$choose’ ‘{‘ choose_clause+ ‘}’; |
| | 94 | |
| | 95 | choose_clause: |
| | 96 | ‘{‘ block_item* ‘}’ |
| | 97 | | when_stmt |
| | 98 | | labeled_stmt |
| | 99 | }}} |
| | 100 | |
| | 101 | - uses labels |
| | 102 | {{{ |
| | 103 | $choose { |
| | 104 | |
| | 105 | LABEL0: stmt*; |
| | 106 | |
| | 107 | LABEL1: stmt*; |
| | 108 | |
| | 109 | |
| | 110 | } |
| | 111 | }}} |
| | 112 | |
| | 113 | === translation notes === |
| | 114 | |
| | 115 | - inside a compound statement that is the body of a `$when` or a clause in a `$choose`, if the first item is a declaration a no-op is inserted before it in translation. Then you are guaranteed that the guard will never involve a new variable. |
| | 116 | |
| | 117 | example: |
| | 118 | |
| | 119 | {{{ |
| | 120 | $when (e) {int x; blah} translated to: |
| | 121 | LOC0 -e:noop-> LOC1(int x) |
| | 122 | $when (e) {$when (f) S} translated to: (let So the first atomic statement in S) |
| | 123 | LOC0 -e&&f:S0-> LOC1 if S0 is not a variable declaration |
| | 124 | or otherwise |
| | 125 | LOC0 -e&&f:noop-> LOC1(variable declared by S0) |
| | 126 | $when (e) {$comm_dequeue; …} translated to: |
| | 127 | LOC0 -e&&$comm_probe(…):$comm_dequeue()-> LOC1 |
| | 128 | }}} |
| | 129 | |