Skip to content

Commit 9922072

Browse files
authored
eraiser: result = v projects onto the value half (#2568)
A raising routine's `result = v` was rebuilt as `result = (Success, v)`. That nests `v` one level deep, and `cps` only ends a coroutine state where a suspension point is the ROOT of an assignment's value: a `.passive` call there was invisible, so `result = passiveCall()` in a raising coroutine returned 0, or failed with `cps.nim: state != -1` in tail position. The rebuild was redundant anyway: `result[0] = Success` is set at the declaration and every path that changes it leaves the routine, and a retyped local's check has already passed. So the assignment is simply `result[1] = v`, which the generic path already produces. `trAsgn` is gone.
1 parent 492c9b6 commit 9922072

3 files changed

Lines changed: 69 additions & 24 deletions

File tree

src/hexer/eraiser.nim

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ one pass that implements them.
1818
stood for becomes `tmp[1]`.
1919
- `let/var local = rcall(args)` retypes `local` to the success tuple and gets
2020
that same check; every other use of `local` projects onto `local[1]`.
21-
- `result = x` and `return x` build the tuple. `result` itself already IS the
22-
tuple, so `return result` needs no rebuild — and must not get one, see
23-
`trRet`.
21+
- `result[0] = Success` is set once, at `result`'s declaration: every path
22+
that changes the code half leaves the routine right away. So `result = x`,
23+
like any other use of `result` (and of a retyped local, whose check has
24+
passed), is simply `result[1] = x`. Rebuilding the whole tuple there would
25+
nest `x` one level deep, which hides a `.passive` call from `cps`: it ends
26+
a state only where a suspension point is the ROOT of the value.
27+
- `return x` builds the tuple. `result` itself already IS the tuple, so
28+
`return result` needs no rebuild — and must not get one, see `trRet`.
2429
- `raise e` becomes `raise (e, result)`.
2530
2631
**Doing the whole job here is the point.** The control-flow half (the temps
@@ -727,24 +732,6 @@ proc trTry(c: var Context; dest: var TokenBuf; n: var Cursor) =
727732
n = tryStart
728733
skip n
729734

730-
proc trAsgn(c: var Context; dest: var TokenBuf; n: var Cursor) =
731-
var nn = n.childCursor
732-
if nn.kind == Symbol and ((nn.symId == c.resultSym and c.canRaise) or
733-
c.tupleVars.contains(nn.symId)):
734-
let isResultSym = nn.symId == c.resultSym
735-
copyInto dest, n:
736-
dest.addSubtree n # the destination, NOT projected: it IS the tuple
737-
inc n
738-
let typ = if isResultSym: c.retType else: getType(c.typeCache, n)
739-
let maybeClose = produceSuccessTuple(c, dest, typ, n.info)
740-
tr c, dest, n
741-
if maybeClose:
742-
dest.addParRi() # tuple constructor
743-
else:
744-
copyInto dest, n:
745-
tr c, dest, n
746-
tr c, dest, n
747-
748735
proc trBreak(c: var Context; dest: var TokenBuf; n: var Cursor) =
749736
## Leaving a `block` or a loop runs the `finally` of every `try` between
750737
## here and it — but not of any `try` further out, which we are still in.
@@ -848,8 +835,6 @@ proc tr(c: var Context; dest: var TokenBuf; n: var Cursor) =
848835
trScope c, dest, n
849836
of StmtsS:
850837
trStmtList c, dest, n
851-
of AsgnS:
852-
trAsgn c, dest, n
853838
of RetS:
854839
trRet c, dest, n
855840
of RaiseS:
@@ -864,7 +849,7 @@ proc tr(c: var Context; dest: var TokenBuf; n: var Cursor) =
864849
trLoopOrBlock c, dest, n
865850
of MacroS, TemplateS, TypeS:
866851
takeTree dest, n
867-
of CallS, CmdS, IteratorS, EmitS, IfS, WhenS,
852+
of AsgnS, CallS, CmdS, IteratorS, EmitS, IfS, WhenS,
868853
ContinueS, ForS, CaseS, YldS,
869854
PragmasS, PragmaxS, InclS, ExclS, IncludeS, ImportS, ImportasS,
870855
FromimportS, ImportexceptS, ExportS, ExportexceptS, CommentS,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# `result = passiveCall()` inside a raising coroutine.
2+
#
3+
# `cps` ends a state where it finds a suspension point at the ROOT of a
4+
# statement. The eraiser used to rewrite `result = v` into
5+
# `result = (Success, v)`, which pushed the passive call one level down: the
6+
# state boundary then fell after the whole assignment, and the routine either
7+
# returned 0 or did not compile at all (`cps.nim: state != -1`).
8+
9+
import std / syncio
10+
11+
proc step() {.passive.} = discard
12+
13+
proc get(x: int): int {.passive.} =
14+
step()
15+
result = x * 10
16+
17+
proc viaResult(x: int): int {.passive, raises.} =
18+
if x < 0: raise SyntaxError
19+
result = get(x)
20+
21+
proc tail(x: int): int {.passive, raises.} =
22+
if x < 0: raise SyntaxError
23+
get(x)
24+
25+
proc viaReturn(x: int): int {.passive, raises.} =
26+
if x < 0: raise SyntaxError
27+
return get(x)
28+
29+
proc reassign(x: int): int {.passive, raises.} =
30+
var y = viaResult(x) # `y` holds the success tuple
31+
y = get(y) # a passive, non-raising call into it
32+
result = y
33+
34+
proc raiseAfter(x: int): int {.passive, raises.} =
35+
result = get(x)
36+
if result > 30: raise SyntaxError
37+
38+
proc main() {.passive.} =
39+
for i in [3, -1]:
40+
try:
41+
echo viaResult(i)
42+
echo tail(i)
43+
echo viaReturn(i)
44+
echo reassign(i)
45+
echo raiseAfter(i)
46+
except:
47+
echo "caught ", i
48+
try:
49+
echo raiseAfter(5)
50+
except:
51+
echo "caught 5"
52+
53+
main()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
30
2+
30
3+
30
4+
300
5+
30
6+
caught -1
7+
caught 5

0 commit comments

Comments
 (0)