lambda lifting support for iterToProc plugin

This commit is contained in:
Araq
2015-11-29 15:00:19 +01:00
parent d7433d02bc
commit 217e0ab6e9
5 changed files with 88 additions and 18 deletions

View File

@@ -126,6 +126,7 @@ type
fn, closureParam, state, resultSym: PSym # most are only valid if
# fn.kind == skClosureIterator
obj: PType
isIterator: bool
PEnv = ref TEnv
TEnv {.final.} = object of RootObj
@@ -197,24 +198,29 @@ proc getHiddenParam(routine: PSym): PSym =
result = hidden.sym
assert sfFromGeneric in result.flags
proc getEnvParam(routine: PSym): PSym =
proc getEnvParam*(routine: PSym): PSym =
let params = routine.ast.sons[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.name.s == paramName:
result = hidden.sym
assert sfFromGeneric in result.flags
proc initIter(iter: PSym): TIter =
proc initIter(iter: PSym; ptrType: PType = nil): TIter =
result.fn = iter
if iter.kind == skClosureIterator:
result.isIterator = ptrType != nil or iter.kind == skClosureIterator
#echo "fuck you ", ptrType != nil
if result.isIterator:
var cp = getEnvParam(iter)
if cp == nil:
result.obj = createEnvObj(iter)
result.obj = if ptrType != nil: ptrType.lastSon else: createEnvObj(iter)
cp = newSym(skParam, getIdent(paramName), iter, iter.info)
incl(cp.flags, sfFromGeneric)
cp.typ = newType(tyRef, iter)
rawAddSon(cp.typ, result.obj)
if ptrType != nil:
cp.typ = ptrType
else:
cp.typ = newType(tyRef, iter)
rawAddSon(cp.typ, result.obj)
addHiddenParam(iter, cp)
else:
result.obj = cp.typ.sons[0]
@@ -534,6 +540,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
let fn = n.sym
if isInnerProc(fn, o.fn) and not containsOrIncl(o.processed, fn.id):
let body = fn.getBody
if nfLL in body.flags: return
# handle deeply nested captures:
let ex = closureCreationPoint(body)
@@ -794,7 +801,7 @@ proc finishEnvironments(o: POuterContext) =
proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode =
if nfLL in n.flags:
result = nil
elif it.fn.kind == skClosureIterator:
elif it.isIterator:
# unfortunately control flow is still convoluted and we can end up
# multiple times here for the very same iterator. We shield against this
# with some rather primitive check for now:
@@ -843,7 +850,7 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
if newBody != nil:
local.ast.sons[bodyPos] = newBody
if it.fn.kind == skClosureIterator and interestingIterVar(local) and
if it.isIterator and interestingIterVar(local) and
it.fn == local.owner:
# every local goes through the closure:
#if not containsOrIncl(o.capturedVars, local.id):
@@ -931,7 +938,7 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
when false:
if n.sons[1].kind == nkSym:
var local = n.sons[1].sym
if it.fn.kind == skClosureIterator and interestingIterVar(local) and
if it.isIterator and interestingIterVar(local) and
it.fn == local.owner:
# every local goes through the closure:
addUniqueField(it.obj, local)
@@ -941,14 +948,25 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
if x != nil: n.sons[1] = x
result = transformOuterConv(n)
of nkYieldStmt:
if it.fn.kind == skClosureIterator: result = transformYield(o, n, it)
if it.isIterator: result = transformYield(o, n, it)
else: outerProcSons(o, n, it)
of nkReturnStmt:
if it.fn.kind == skClosureIterator: result = transformReturn(o, n, it)
if it.isIterator: result = transformReturn(o, n, it)
else: outerProcSons(o, n, it)
else:
outerProcSons(o, n, it)
proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
var o = newOuterContext(fn)
let ex = closureCreationPoint(body)
let env = newEnv(o, nil, ex, fn)
addParamsToEnv(fn, env)
searchForInnerProcs(o, body, env)
createEnvironments(o)
let it = initIter(fn, ptrType)
result = transformOuterProcBody(o, body, it)
finishEnvironments(o)
proc liftLambdas*(fn: PSym, body: PNode): PNode =
# XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ...

View File

@@ -0,0 +1,49 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Plugin to transform an inline iterator into a data structure.
import plugins, ast, astalgo, magicsys, lookups, semdata,
lambdalifting, msgs, rodread
proc iterToProcImpl(c: PContext, n: PNode): PNode =
result = newNodeI(nkStmtList, n.info)
let iter = n[1]
if iter.kind != nkSym or iter.sym.kind != skIterator:
localError(iter.info, "first argument needs to be an iterator")
return
if n[2].typ.isNil:
localError(n[2].info, "second argument needs to be a type")
return
if n[3].kind != nkIdent:
localError(n[3].info, "third argument needs to be an identifier")
return
let t = n[2].typ.skipTypes({tyTypeDesc, tyGenericInst})
if t.kind notin {tyRef, tyPtr} or t.lastSon.kind != tyObject:
localError(n[2].info,
"type must be a non-generic ref|ptr to object with state field")
return
let body = liftIterToProc(iter.sym, iter.sym.getBody, t)
let prc = newSym(skProc, n[3].ident, iter.sym.owner, iter.sym.info)
prc.typ = copyType(iter.sym.typ, prc, false)
excl prc.typ.flags, tfCapturesEnv
prc.typ.n.add newSymNode(getEnvParam(iter.sym))
prc.typ.rawAddSon t
let orig = iter.sym.ast
prc.ast = newProcNode(nkProcDef, n.info,
name = newSymNode(prc),
params = orig[paramsPos],
pragmas = orig[pragmasPos],
body = body)
prc.ast.add iter.sym.ast.sons[resultPos]
addInterfaceDecl(c, prc)
registerPlugin("stdlib", "system", "iterToProc", iterToProcImpl)

View File

@@ -871,11 +871,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
of tyExpr:
if procKind notin {skMacro, skTemplate}:
result = addImplicitGeneric(newTypeS(tyAnything, c))
#result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil)
of tyGenericParam:
markUsed(info, paramType.sym)
styleCheckUse(info, paramType.sym)
@@ -1002,7 +997,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
# see tchainediterators
# in cases like iterator foo(it: iterator): type(it)
# we don't need to change the return type to iter[T]
if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r])
result.flags.incl tfIterator
#if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r])
# XXX Would be nice if we could get rid of this
result.sons[0] = r
result.n.typ = r

View File

@@ -40,7 +40,7 @@ when defined(emscripten):
MAP_PRIVATE = 2'i32 # Changes are private
var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
type
type
PEmscriptenMMapBlock = ptr EmscriptenMMapBlock
EmscriptenMMapBlock {.pure, inheritable.} = object
realSize: int # size of previous chunk; for coalescing
@@ -399,6 +399,9 @@ iterator allObjects(m: MemRegion): pointer {.inline.} =
let c = cast[PBigChunk](c)
yield addr(c.data)
proc iterToProc*(iter: typed, envType: typedesc; procName: untyped) {.
magic: "Plugin", compileTime.}
proc isCell(p: pointer): bool {.inline.} =
result = cast[ptr FreeCell](p).zeroField >% 1

View File

@@ -6,8 +6,11 @@ discard """
8
12
'''
disabled: "true"
"""
# Will eventually fix it...
iterator map[T, U](s: iterator:T{.inline.}, f: proc(x: T): U): U =
for e in s: yield f(e)