top level closures should work; transf is not a pass anymore; next steps for first class iterator support

This commit is contained in:
Araq 2012-08-13 17:07:49 +02:00
commit 244c14db0b
17 changed files with 293 additions and 229 deletions

View file

@ -25,7 +25,6 @@ import
const
genPrefix* = ":tmp" # prefix for generated names
proc transfPass*(): TPass
# implementation
type
@ -109,54 +108,6 @@ proc transformSons(c: PTransf, n: PNode): PTransNode =
for i in countup(0, sonsLen(n)-1):
result[i] = transform(c, n.sons[i])
# Transforming iterators into non-inlined versions is pretty hard, but
# unavoidable for not bloating the code too much. If we had direct access to
# the program counter, things'd be much easier.
# ::
#
# iterator items(a: string): char =
# var i = 0
# while i < length(a):
# yield a[i]
# inc(i)
#
# for ch in items("hello world"): # `ch` is an iteration variable
# echo(ch)
#
# Should be transformed into::
#
# type
# TItemsClosure = record
# i: int
# state: int
# proc items(a: string, c: var TItemsClosure): char =
# case c.state
# of 0: goto L0 # very difficult without goto!
# of 1: goto L1 # can be implemented by GCC's computed gotos
#
# block L0:
# c.i = 0
# while c.i < length(a):
# c.state = 1
# return a[i]
# block L1: inc(c.i)
#
# More efficient, but not implementable::
#
# type
# TItemsClosure = record
# i: int
# pc: pointer
#
# proc items(a: string, c: var TItemsClosure): char =
# goto c.pc
# c.i = 0
# while c.i < length(a):
# c.pc = label1
# return a[i]
# label1: inc(c.i)
#
proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
result = newTransNode(nkFastAsgn, PNode(ri).info, 2)
result[0] = PTransNode(le)
@ -178,15 +129,6 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
if result != nil: return
tc = tc.next
result = b
when false:
case b.sym.kind
of skConst, skEnumField:
if sfFakeConst notin b.sym.flags:
if skipTypes(b.sym.typ, abstractInst).kind notin ConstantDataTypes:
result = getConstExpr(c.module, b)
if result == nil: InternalError(b.info, "transformSym: const")
else:
nil
proc transformSym(c: PTransf, n: PNode): PTransNode =
result = PTransNode(transformSymAux(c, n))
@ -378,20 +320,6 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
if n.sons[0].kind == a or n.sons[0].kind == b:
# addr ( deref ( x )) --> x
result = PTransNode(n.sons[0].sons[0])
proc generateThunk(c: PTransf, prc: PNode, dest: PType): PNode =
## Converts 'prc' into '(thunk, nil)' so that it's compatible with
## a closure.
# we cannot generate a proper thunk here for GC-safety reasons (see internal
# documentation):
if gCmd == cmdCompileToEcmaScript: return prc
result = newNodeIT(nkClosure, prc.info, dest)
var conv = newNodeIT(nkHiddenStdConv, prc.info, dest)
conv.add(emptyNode)
conv.add(prc)
result.add(conv)
result.add(newNodeIT(nkNilLit, prc.info, getSysType(tyNil)))
proc transformConv(c: PTransf, n: PNode): PTransNode =
# numeric types need range checks:
@ -466,12 +394,6 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result[0] = transform(c, n.sons[1])
else:
result = transform(c, n.sons[1])
of tyProc:
if dest.callConv == ccClosure and source.callConv == ccDefault:
let x = transform(c, n.sons[1]).pnode
result = generateThunk(c, x, dest).ptransnode
else:
result = transformSons(c, n)
of tyGenericParam, tyOrdinal, tyTypeClass:
result = transform(c, n.sons[1])
# happens sometimes for generated assignments, etc.
@ -657,21 +579,23 @@ proc transform(c: PTransf, n: PNode): PTransNode =
result = PTransNode(n)
of nkBracketExpr: result = transformArrayAccess(c, n)
of procDefs:
if n.sons[genericParamsPos].kind == nkEmpty:
var s = n.sons[namePos].sym
n.sons[bodyPos] = PNode(transform(c, s.getBody))
if s.ast.sons[bodyPos] != n.sons[bodyPos]:
# somehow this can happen ... :-/
s.ast.sons[bodyPos] = n.sons[bodyPos]
n.sons[bodyPos] = liftLambdas(n)
if n.kind == nkMethodDef: methodDef(s, false)
when false:
if n.sons[genericParamsPos].kind == nkEmpty:
var s = n.sons[namePos].sym
n.sons[bodyPos] = PNode(transform(c, s.getBody))
if s.ast.sons[bodyPos] != n.sons[bodyPos]:
# somehow this can happen ... :-/
s.ast.sons[bodyPos] = n.sons[bodyPos]
#n.sons[bodyPos] = liftLambdas(s, n)
#if n.kind == nkMethodDef: methodDef(s, false)
result = PTransNode(n)
of nkMacroDef:
# XXX no proper closure support yet:
if n.sons[genericParamsPos].kind == nkEmpty:
var s = n.sons[namePos].sym
n.sons[bodyPos] = PNode(transform(c, s.getBody))
if n.kind == nkMethodDef: methodDef(s, false)
when false:
if n.sons[genericParamsPos].kind == nkEmpty:
var s = n.sons[namePos].sym
n.sons[bodyPos] = PNode(transform(c, s.getBody))
if n.kind == nkMethodDef: methodDef(s, false)
result = PTransNode(n)
of nkForStmt:
inc c.inLoop
@ -735,38 +659,57 @@ proc transform(c: PTransf, n: PNode): PTransNode =
if cnst != nil and not dontInlineConstant(n, cnst):
result = PTransNode(cnst) # do not miss an optimization
proc processTransf(context: PPassContext, n: PNode): PNode =
proc processTransf(c: PTransf, n: PNode): PNode =
# Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
# this step! We have to rely that the semantic pass transforms too errornous
# nodes into an empty node.
if passes.skipCodegen(n) or context.fromCache or nfTransf in n.flags: return n
var c = PTransf(context)
if passes.skipCodegen(n) or c.fromCache or nfTransf in n.flags: return n
pushTransCon(c, newTransCon(getCurrOwner(c)))
result = PNode(transform(c, n))
popTransCon(c)
incl(result.flags, nfTransf)
proc openTransf(module: PSym, filename: string): PPassContext =
var n: PTransf
new(n)
n.contSyms = @[]
n.breakSyms = @[]
n.module = module
result = n
proc openTransf(module: PSym, filename: string): PTransf =
new(result)
result.contSyms = @[]
result.breakSyms = @[]
result.module = module
proc openTransfCached(module: PSym, filename: string,
rd: PRodReader): PPassContext =
result = openTransf(module, filename)
for m in items(rd.methods): methodDef(m, true)
when false:
proc openTransfCached(module: PSym, filename: string,
rd: PRodReader): PPassContext =
result = openTransf(module, filename)
for m in items(rd.methods): methodDef(m, true)
proc transfPass(): TPass =
initPass(result)
result.open = openTransf
result.openCached = openTransfCached
result.process = processTransf
result.close = processTransf # we need to process generics too!
proc transfPass(): TPass =
initPass(result)
result.open = openTransf
result.openCached = openTransfCached
result.process = processTransf
result.close = processTransf # we need to process generics too!
proc transform*(module: PSym, n: PNode): PNode =
proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
if nfTransf in n.flags or prc.kind in {skTemplate, skMacro}:
result = n
else:
var c = openTransf(module, "")
result = processTransf(c, n)
if prc.kind != skMacro:
# XXX no closures yet for macros:
result = liftLambdas(prc, result)
if prc.kind == skMethod: methodDef(prc, false)
incl(result.flags, nfTransf)
proc transformStmt*(module: PSym, n: PNode): PNode =
if nfTransf in n.flags:
result = n
else:
var c = openTransf(module, "")
result = processTransf(c, n)
result = liftLambdasForTopLevel(module, result)
incl(result.flags, nfTransf)
proc transformExpr*(module: PSym, n: PNode): PNode =
if nfTransf in n.flags:
result = n
else: