Skip to content

Commit

Permalink
cps, cps/environment: only desym environment fields (#73)
Browse files Browse the repository at this point in the history
* cps, cps/environment: only desym environment access

This is a WIP commit.

Experiments shows that we only have to desym due to issues in
environment definition and access.

Upstream bug: nim-lang/Nim#17851

This commit disable blanket desymification and only perform them
on environment as a workaround for the mentioned bug, which fixes
the foreign symbol tests.

* cps: disable blanket desym with comments

* cps: nicer getCallSym interface

* cps/environment: don't desym Env type

The better fix in #72 removed the need for this 🎉.

* cps/[environment, spec]: added genField for generating object fields

A proper workaround for nim-lang/Nim#17851,
which lets us remove the desym injections into cps/environment.

* cps: cater getCallSym to reviews

* cps/spec: English is hard
  • Loading branch information
alaviss authored Apr 28, 2021
1 parent 5579b5f commit 58904bd
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 38 deletions.
19 changes: 11 additions & 8 deletions cps.nim
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ when defined(yourdaywillcomelittleonecommayourdaywillcomedotdotdot):

func getCallSym(n: NimNode): NimNode =
## Get the symbol that is being called
## Returns nil if there is no symbol
expectKind n, callish
result = n[0]
while result != nil:
Expand All @@ -37,18 +38,18 @@ func getCallSym(n: NimNode): NimNode =
of nnkSym:
break
of nnkIdent:
raise newException(CatchableError, "This call is not typed")
result = nil
else:
assert false, "unknown node type: " & $result.kind
assert not result.isNil
raise newException(Defect, "unknown node type: " & $result.kind)

proc isCpsCall(n: NimNode): bool =
## true if this node holds a call to a cps procedure
assert not n.isNil
if len(n) > 0:
if n.kind in callish:
let p = n.getCallSym.getImpl
result = p.hasPragma("cpsCall")
let callee = n.getCallSym
if not callee.isNil:
result = callee.getImpl.hasPragma("cpsCall")

proc firstReturn(p: NimNode): NimNode =
## find the first return statement within statement lists, or nil
Expand Down Expand Up @@ -808,9 +809,11 @@ proc cpsXfrmProc(T: NimNode, n: NimNode): NimNode =
env = env.storeType(force = off)

# Araq: "learn how to desemantic your ast, knuckleheads"
n.body = replacedSymsWithIdents(n.body)
types = replacedSymsWithIdents(types)
booty = replacedSymsWithIdents(booty)
# No longer necessary as we are desym-ing on a selective basis
when false:
n.body = replacedSymsWithIdents(n.body)
types = replacedSymsWithIdents(types)
booty = replacedSymsWithIdents(booty)

# lifting the generated proc bodies
result = lambdaLift(types, n)
Expand Down
12 changes: 6 additions & 6 deletions cps/environment.nim
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type
fn: NimNode # the sym we use for the goto target
ex: NimNode # the sym we use for stored exception
rs: NimNode # the sym we use for "yielded" result

func insideCps*(e: Env): bool = len(e.gotos) > 0 or len(e.breaks) > 0

template searchScope(env: Env; x: untyped;
Expand Down Expand Up @@ -204,13 +204,13 @@ proc init(e: var Env) =
e.seen = initHashSet[string]()
if e.fn.isNil:
when cpsFn:
e.fn = genSym(nskField, "fn")
e.fn = genField("fn")
else:
e.fn = ident"fn"
if e.ex.isNil:
e.ex = genSym(nskField, "ex")
e.ex = genField("ex")
if e.rs.isNil:
e.rs = genSym(nskField, "result")
e.rs = genField("result")
e.id = genSym(nskType, "env")

proc allPairs(e: Env): seq[Pair] =
Expand Down Expand Up @@ -393,7 +393,7 @@ iterator addIdentDef(e: var Env; kind: NimNodeKind; n: NimNode): Pair =
# iterate over the identifier names (a, b, c)
for name in n[0 ..< len(n)-2]: # ie. omit (:type) and (=default)
# create a new identifier for the object field
let field = genSym(nskField, name.strVal)
let field = genField(name.strVal)
let value = newTree(kind, # ident: <no var> type = default
newIdentDefs(name, stripVar(n[^2]), n[^1]))
e = e.set(field, value)
Expand All @@ -404,7 +404,7 @@ iterator addIdentDef(e: var Env; kind: NimNodeKind; n: NimNode): Pair =
let tup = n.last
for i in 0 ..< len(tup):
let name = n[i]
let field = genSym(nskField, name.strVal)
let field = genField(name.strVal)
let value = newTree(kind,
newIdentDefs(name, getTypeInst(tup[i]), tup[i]))
e = e.set(field, value)
Expand Down
6 changes: 6 additions & 0 deletions cps/spec.nim
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,9 @@ proc errorAst*(s: string): NimNode =
proc errorAst*(n: NimNode; s = "creepy ast"): NimNode =
## embed an error with a message
errorAst s & ":\n" & treeRepr(n) & "\n"

proc genField*(ident = ""): NimNode =
## generate a unique field to put inside an object definition
##
## made as a workaround for [nim-lang/Nim#17851](https://github.com/nim-lang/Nim/issues/17851)
desym genSym(nskField, ident)
42 changes: 18 additions & 24 deletions tests/taste.nim
Original file line number Diff line number Diff line change
Expand Up @@ -588,35 +588,29 @@ testes:

block:
## call a macro that calls a foreign symbol
when true:
skip "not working yet, see #53"
else:
r = 0
proc foo() {.cps: Cont.} =
inc r
let i = 42
noop()
inc r
check jsonifyImplicit(i) == $i
r = 0
proc foo() {.cps: Cont.} =
inc r
let i = 42
noop()
inc r
check jsonifyImplicit(i) == $i

trampoline foo()
check r == 2
trampoline foo()
check r == 2

block:
## call a template with explicit bind
when true:
skip "not working yet, see #53"
else:
r = 0
proc foo() {.cps: Cont.} =
inc r
let i = 42
noop()
inc r
check jsonifyBind(i) == $i
r = 0
proc foo() {.cps: Cont.} =
inc r
let i = 42
noop()
inc r
check jsonifyBind(i) == $i

trampoline foo()
check r == 2
trampoline foo()
check r == 2

block:
## template call in nested call nodes
Expand Down

0 comments on commit 58904bd

Please sign in to comment.