From 632aece1917dfdf7f4ed63c5cc2b9fad4e590450 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 6 Feb 2012 00:19:56 +0100 Subject: [PATCH] further steps to closure support --- compiler/ast.nim | 10 +- compiler/ccgcalls.nim | 71 +++++++++++++- compiler/ccgexprs.nim | 30 +++++- compiler/ccgstmts.nim | 19 +++- compiler/ccgtypes.nim | 32 +++++-- compiler/cgen.nim | 33 +++++-- compiler/lambdalifting.nim | 183 ++++++++++++++++++++++--------------- compiler/msgs.nim | 3 +- compiler/renderer.nim | 4 +- compiler/semexprs.nim | 14 ++- compiler/semstmts.nim | 5 - compiler/semthreads.nim | 2 +- compiler/transf.nim | 10 +- compiler/trees.nim | 2 +- compiler/types.nim | 11 ++- doc/intern.txt | 30 +++++- doc/manual.txt | 37 ++++++-- lib/system.nim | 3 + todo.txt | 14 ++- web/news.txt | 4 +- 20 files changed, 379 insertions(+), 138 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 45b1ebfd08..65d7bccf1e 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -189,7 +189,8 @@ type nkProcTy, # proc type nkEnumTy, # enum body nkEnumFieldDef, # `ident = expr` in an enumeration - nkReturnToken # token used for interpretation + nkReturnToken, # token used for interpretation + nkClosure # (prc, env)-pair (internally used for code gen) TNodeKinds* = set[TNodeKind] type @@ -977,11 +978,12 @@ proc hasSonWith(n: PNode, kind: TNodeKind): bool = result = false proc containsNode*(n: PNode, kinds: TNodeKinds): bool = + if n == nil: return case n.kind of nkEmpty..nkNilLit: result = n.kind in kinds else: for i in countup(0, sonsLen(n) - 1): - if containsNode(n.sons[i], kinds): return true + if n.kind in kinds or containsNode(n.sons[i], kinds): return true proc hasSubnodeWith(n: PNode, kind: TNodeKind): bool = case n.kind @@ -1037,6 +1039,10 @@ proc isGenericRoutine*(s: PSym): bool = result = s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty else: nil +proc isRoutine*(s: PSym): bool {.inline.} = + result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod, + skConverter} + iterator items*(n: PNode): PNode = for i in 0.. 1: app(pl, ", ") + # beware of 'result = p(result)'. We may need to allocate a temporary: + if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): + # Great, we can use 'd': + if d.k == locNone: getTemp(p, typ.sons[0], d) + elif d.k notin {locExpr, locTemp} and not hasNoInit(ri): + # reset before pass as 'result' var: + resetLoc(p, d) + app(pl, addrLoc(d)) + genCallPattern() + appf(p.s[cpsStmts], ";$n") + else: + var tmp: TLoc + getTemp(p, typ.sons[0], tmp) + app(pl, addrLoc(tmp)) + genCallPattern() + appf(p.s[cpsStmts], ";$n") + genAssignment(p, d, tmp, {}) # no need for deep copying + else: + if d.k == locNone: getTemp(p, typ.sons[0], d) + assert(d.t != nil) # generate an assignment to d: + var list: TLoc + initLoc(list, locCall, d.t, OnUnknown) + list.r = ropef(CallPattern, op.r, pl, pl.addComma, rawProc) + genAssignment(p, d, list, {}) # no need for deep copying + else: + genCallPattern() + appf(p.s[cpsStmts], ";$n") + proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = var op, a: TLoc initLocExpr(p, ri.sons[0], op) @@ -224,7 +285,9 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = appf(p.s[cpsStmts], ";$n") proc genCall(p: BProc, e: PNode, d: var TLoc) = - if e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and + if e.sons[0].typ.callConv == ccClosure: + genClosureCall(p, nil, e, d) + elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and e.len >= 2: genInfixCall(p, nil, e, d) elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags: @@ -235,7 +298,9 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) = if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d) proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = - if ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and + if ri.sons[0].typ.callConv == ccClosure: + genClosureCall(p, le, ri, d) + elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and ri.len >= 2: genInfixCall(p, le, ri, d) elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ef412d753f..6a8156220c 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -244,7 +244,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = appcg(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n", [addrLoc(dest), rdLoc(src)]) if needToKeepAlive in flags: keepAlive(p, dest) - of tyTuple, tyObject: + of tyTuple, tyObject, tyProc: # XXX: check for subtyping? if needsComplexAssignment(dest.t): genGenericAsgn(p, dest, src, flags) @@ -274,7 +274,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = [rdLoc(dest), rdLoc(src), toRope(getSize(dest.t))]) else: appcg(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) - of tyPtr, tyPointer, tyChar, tyBool, tyProc, tyEnum, tyCString, + of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString, tyInt..tyFloat128, tyRange: appcg(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) else: InternalError("genAssignment(" & $ty.kind & ')') @@ -1308,7 +1308,6 @@ proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = [rdLoc(a)])) proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = - # XXX we don't generate keep alive info here var a: TLoc initLocExpr(p, n.sons[0], a) putIntoDest(p, d, skipTypes(n.typ, abstractVar), @@ -1515,6 +1514,28 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = [rdLoc(d), mangleRecFieldName(t.n.sons[i].sym, t)]) expr(p, it, rec) +proc IsConstClosure(n: PNode): bool {.inline.} = + result = n.sons[0].kind == nkSym and isRoutine(n.sons[0].sym) and + n.sons[1].kind == nkNilLit + +proc genClosure(p: BProc, n: PNode, d: var TLoc) = + assert n.kind == nkClosure + + if IsConstClosure(n): + inc(p.labels) + var tmp = con("LOC", toRope(p.labels)) + appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", + [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)]) + putIntoDest(p, d, n.typ, tmp) + else: + var tmp, a, b: TLoc + initLocExpr(p, n.sons[0], a) + initLocExpr(p, n.sons[1], b) + getTemp(p, n.typ, tmp) + appcg(p, cpsStmts, "$1.ClPrc = $2; $1.ClEnv = $3;$n", + tmp.rdLoc, a.rdLoc, b.rdLoc) + putLocIntoDest(p, d, tmp) + proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = var arr: TLoc if not handleConstExpr(p, n, d): @@ -1705,6 +1726,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) = if sym.loc.r == nil or sym.loc.t == nil: InternalError(e.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) + of nkClosure: genClosure(p, e, d) of nkMetaNode: expr(p, e.sons[0], d) else: InternalError(e.info, "expr(" & $e.kind & "); unknown node kind") @@ -1751,7 +1773,7 @@ proc genConstExpr(p: BProc, n: PNode): PRope = var cs: TBitSet toBitSet(n, cs) result = genRawSetData(cs, int(getSize(n.typ))) - of nkBracket, nkPar: + of nkBracket, nkPar, nkClosure: var t = skipTypes(n.typ, abstractInst) if t.kind == tySequence: result = genConstSeq(p, n, t) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index ad1b5646f2..8e7b05c0f7 100755 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -57,13 +57,24 @@ proc genSingleVar(p: BProc, a: PNode) = genLineDir(p, a) loadInto(p, a.sons[0], a.sons[2], v.loc) +proc genClosureVar(p: BProc, a: PNode) = + var immediateAsgn = a.sons[2].kind != nkEmpty + if immediateAsgn: + var v: TLoc + initLocExpr(p, a.sons[0], v) + genLineDir(p, a) + loadInto(p, a.sons[0], a.sons[2], v) + proc genVarStmt(p: BProc, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if a.kind == nkIdentDefs: - assert(a.sons[0].kind == nkSym) - genSingleVar(p, a) + if a.kind == nkIdentDefs: + # can be a lifted var nowadays ... + if a.sons[0].kind == nkSym: + genSingleVar(p, a) + else: + genClosureVar(p, a) else: genVarTuple(p, a) @@ -704,7 +715,7 @@ proc genStmts(p: BProc, t: PNode) = of nkReturnStmt: genReturnStmt(p, t) of nkBreakStmt: genBreakStmt(p, t) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, - nkCallStrLit: + nkCallStrLit, nkClosure: genLineDir(p, t) initLocExpr(p, t, a) of nkAsgn: genAsgn(p, t, fastAsgn=false) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index a2faf3cbf7..c7002954a6 100755 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -94,7 +94,7 @@ proc mapType(typ: PType): TCTypeKind = else: result = ctPtr of tyPointer: result = ctPtr of tySequence: result = ctNimSeq - of tyProc: result = ctProc + of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct of tyString: result = ctNimStr of tyCString: result = ctCString of tyInt..tyFloat128: @@ -215,7 +215,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var PRope, appff(params, " Result", " @Result", []) if t.callConv == ccClosure: if params != nil: app(params, ", ") - app(params, "void* ClPart") + app(params, "void* ClEnv") if tfVarargs in t.flags: if params != nil: app(params, ", ") app(params, "...") @@ -331,7 +331,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode, appf(result, "} $1;$n", [uname]) of nkSym: field = n.sym - assert(field.ast == nil) + #assert(field.ast == nil) sname = mangleRecFieldName(field, rectype) if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname]) else: ae = sname @@ -436,9 +436,10 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope = if t.callConv != ccClosure: # procedure vars may need a closure! appf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", [toRope(CallingConvToStr[t.callConv]), rettype, result, desc]) - else: - appf(m.s[cfsTypes], "typedef struct $1 {$n" & - "N_CDECL_PTR($2, PrcPart) $3;$n" & "void* ClPart;$n};$n", + else: + appf(m.s[cfsTypes], "typedef struct {$n" & + "N_CDECL_PTR($2, ClPrc) $3;$n" & + "void* ClEnv;$n} $1;$n", [result, rettype, desc]) of tySequence: # we cannot use getTypeForward here because then t would be associated @@ -673,7 +674,8 @@ proc genTupleInfo(m: BModule, typ: PType, name: PRope) = var tmp2 = getNimNode(m) appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(i), tmp2]) appf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & - "$1.offset = offsetof($2, Field$3);$n" & "$1.typ = $4;$n" & + "$1.offset = offsetof($2, Field$3);$n" & + "$1.typ = $4;$n" & "$1.name = \"Field$3\";$n", [tmp2, getTypeDesc(m, typ), toRope(i), genTypeInfo(m, a)]) appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", @@ -736,6 +738,14 @@ proc genSetInfo(m: BModule, typ: PType, name: PRope) = proc genArrayInfo(m: BModule, typ: PType, name: PRope) = genTypeInfoAuxBase(m, typ, name, genTypeInfo(m, typ.sons[1])) +proc fakeClosureType(owner: PSym): PType = + # we generate the same RTTI as for a tuple[pointer, ref tuple[]] + result = newType(tyTuple, owner) + result.addSon(newType(tyPointer, owner)) + var r = newType(tyRef, owner) + r.addSon(newType(tyTuple, owner)) + result.addSon(r) + proc genTypeInfo(m: BModule, typ: PType): PRope = var t = getUniqueType(typ) # gNimDat contains all the type information nowadays: @@ -750,9 +760,13 @@ proc genTypeInfo(m: BModule, typ: PType): PRope = if dataGenerated: return case t.kind of tyEmpty: result = toRope"0" - of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128, - tyVar: + of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128, tyVar: genTypeInfoAuxBase(gNimDat, t, result, toRope"0") + of tyProc: + if t.callConv != ccClosure: + genTypeInfoAuxBase(gNimDat, t, result, toRope"0") + else: + genTupleInfo(gNimDat, fakeClosureType(t.owner), result) of tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(gNimDat, t, result) of tyArrayConstr, tyArray: genArrayInfo(gNimDat, t, result) of tySet: genSetInfo(gNimDat, t, result) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index d60f11639a..9784b21bbb 100755 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -75,8 +75,10 @@ proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) = if a.r == nil: a.r = r proc isSimpleConst(typ: PType): bool = - result = skipTypes(typ, abstractVar).kind notin - {tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence} + let t = skipTypes(typ, abstractVar) + result = t.kind notin + {tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence} and not + (t.kind == tyProc and t.callConv == ccClosure) proc useHeader(m: BModule, sym: PSym) = if lfHeader in sym.loc.Flags: @@ -187,7 +189,7 @@ proc rdLoc(a: TLoc): PRope = proc addrLoc(a: TLoc): PRope = result = a.r - if lfIndirect notin a.flags and mapType(a.t) != ctArray: + if lfIndirect notin a.flags and mapType(a.t) != ctArray: result = con("&", result) proc rdCharLoc(a: TLoc): PRope = @@ -196,7 +198,7 @@ proc rdCharLoc(a: TLoc): PRope = if skipTypes(a.t, abstractRange).kind == tyChar: result = ropef("((NU8)($1))", [result]) -proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc, +proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc, takeAddr: bool) = case analyseObjectWithTypeField(t) of frNone: @@ -223,11 +225,12 @@ type proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) -const - complexValueType = {tyArray, tyArrayConstr, tySet, tyTuple, tyObject} +proc isComplexValueType(t: PType): bool {.inline.} = + result = t.kind in {tyArray, tyArrayConstr, tySet, tyTuple, tyObject} or + (t.kind == tyProc and t.callConv == ccClosure) proc zeroVar(p: BProc, loc: TLoc, containsGCref: bool) = - if skipTypes(loc.t, abstractVarRange).Kind notin ComplexValueType: + if not isComplexValueType(skipTypes(loc.t, abstractVarRange)): if containsGcref and p.WithInLoop > 0: appf(p.s[cpsInit], "$1 = 0;$n", [rdLoc(loc)]) var nilLoc: TLoc @@ -249,8 +252,8 @@ proc zeroVar(p: BProc, loc: TLoc, containsGCref: bool) = [addrLoc(loc), rdLoc(loc)]) genObjectInit(p, cpsStmts, loc.t, loc, true) -proc zeroTemp(p: BProc, loc: TLoc) = - if skipTypes(loc.t, abstractVarRange).Kind notin complexValueType: +proc zeroTemp(p: BProc, loc: TLoc) = + if not isComplexValueType(skipTypes(loc.t, abstractVarRange)): appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(loc)]) when false: var nilLoc: TLoc @@ -313,7 +316,7 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) = result.s = OnStack result.flags = {} - if skipTypes(toKeepAlive.t, abstractVarRange).Kind notin complexValueType: + if not isComplexValueType(skipTypes(toKeepAlive.t, abstractVarRange)): appf(p.s[cpsStmts], "$1 = $2;$n", [rdLoc(result), rdLoc(toKeepAlive)]) else: appcg(p, cpsStmts, @@ -571,6 +574,15 @@ proc initFrame(p: BProc, procname, filename: PRope): PRope = proc deinitFrame(p: BProc): PRope = result = ropecg(p.module, "#popFrame();$n") +proc closureSetup(p: BProc, prc: PSym) = + if prc.typ.callConv != ccClosure: return + # prc.ast[paramsPos].last contains the type we're after: + var env = lastSon(prc.ast[paramsPos]).sym + assignLocalVar(p, env) + # generate cast assignment: + appcg(p, cpsStmts, "$1 = ($2) ClEnv;$n", rdLoc(env.loc), + getTypeDesc(p.module, env.typ)) + proc genProcAux(m: BModule, prc: PSym) = var p = newProc(prc, m) var header = genProcHeader(m, prc) @@ -594,6 +606,7 @@ proc genProcAux(m: BModule, prc: PSym) = for i in countup(1, sonsLen(prc.typ.n) - 1): var param = prc.typ.n.sons[i].sym assignParam(p, param) + closureSetup(p, prc) genStmts(p, prc.getBody) # modifies p.locals, p.init, etc. var generatedProc: PRope if sfPure in prc.flags: diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 8aa490bbb0..4a2a8997c3 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -9,34 +9,46 @@ # This include file implements lambda lifting for the transformator. -# - Things to consider: Does capturing of 'result' work? (unknown) -# - Do generic inner procs work? (should) -# - Does nesting of closures work? (not yet) -# - Test that iterators within closures work etc. - const procDefs = {nkLambda, nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkConverterDef} -proc indirectAccess(a, b: PSym): PNode = +proc indirectAccess(a, b: PSym, info: TLineInfo): PNode = # returns a[].b as a node - var x = newSymNode(a) - var y = newSymNode(b) - var deref = newNodeI(nkHiddenDeref, x.info) + let x = newSymNode(a) + var deref = newNodeI(nkHiddenDeref, info) deref.typ = x.typ.sons[0] + + let field = getSymFromList(deref.typ.n, b.name) addSon(deref, x) - result = newNodeI(nkDotExpr, x.info) + result = newNodeI(nkDotExpr, info) addSon(result, deref) - addSon(result, y) - result.typ = y.typ + addSon(result, newSymNode(field)) + result.typ = field.typ -proc Incl(container: PNode, s: PSym) = - for x in container: - if x.sym.id == s.id: return - container.add(newSymNode(s)) +type + TCapture = seq[PSym] -proc gatherVars(c: PTransf, n: PNode, owner: PSym, container: PNode) = - # gather used vars for closure generation +proc Capture(cap: var TCapture, s: PSym) = + for x in cap: + if x.name.id == s.name.id: return + cap.add(s) + +proc captureToTuple(cap: TCapture, owner: PSym): PType = + result = newType(tyTuple, owner) + result.n = newNodeI(nkRecList, owner.info) + for s in cap: + var field = newSym(skField, s.name, s.owner) + + let typ = s.typ + field.typ = typ + field.position = sonsLen(result) + + addSon(result.n, newSymNode(field)) + addSon(result, typ) + +proc gatherVars(c: PTransf, n: PNode, outerProc: PSym, cap: var TCapture) = + # gather used vars for closure generation into 'cap' case n.kind of nkSym: var s = n.sym @@ -45,82 +57,88 @@ proc gatherVars(c: PTransf, n: PNode, owner: PSym, container: PNode) = of skVar, skLet: found = sfGlobal notin s.flags of skTemp, skForVar, skParam, skResult: found = true else: nil - if found and owner.id != s.owner.id: - incl(container, s) + if found and outerProc.id == s.owner.id: + #echo "captured: ", s.name.s + Capture(cap, s) of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil - else: + else: for i in countup(0, sonsLen(n) - 1): - gatherVars(c, n.sons[i], owner, container) + gatherVars(c, n.sons[i], outerProc, cap) -proc replaceVars(c: PTransf, n: PNode, owner, env: PSym) = +proc replaceVars(c: PTransf, n: PNode, outerProc, env: PSym) = for i in countup(0, safeLen(n) - 1): - if n.kind == nkSym: - let s = n.sym + let a = n.sons[i] + if a.kind == nkSym: + let s = a.sym var found = false case s.kind of skVar, skLet: found = sfGlobal notin s.flags of skTemp, skForVar, skParam, skResult: found = true else: nil - if found and owner.id != s.owner.id: + if found and outerProc.id == s.owner.id: # access through the closure param: - n.sons[i] = indirectAccess(env, s) + n.sons[i] = indirectAccess(env, s, n.info) else: - replaceVars(c, n.sons[i], owner, env) - + replaceVars(c, a, outerProc, env) + +proc addFormalParam(routine: PType, param: PSym) = + addSon(routine, param.typ) + addSon(routine.n, newSymNode(param)) + proc addFormalParam(routine: PSym, param: PSym) = - addSon(routine.typ, param.typ) + #addFormalParam(routine.typ, param) addSon(routine.ast.sons[paramsPos], newSymNode(param)) -proc isInnerProc(s, owner: PSym): bool {.inline.} = +proc isInnerProc(s, outerProc: PSym): bool {.inline.} = result = s.kind in {skProc, skMacro, skIterator, skMethod, skConverter} and - s.owner.id == owner.id and not isGenericRoutine(s) + s.owner.id == outerProc.id and not isGenericRoutine(s) and + s.typ.callConv == ccClosure -proc searchForInnerProcs(c: PTransf, n: PNode, owner: PSym, container: PNode) = +proc searchForInnerProcs(c: PTransf, n: PNode, outerProc: PSym, + cap: var TCapture) = case n.kind of nkSym: let s = n.sym - if isInnerProc(s, owner): - gatherVars(c, s.getBody, owner, container) + if isInnerProc(s, outerProc): + gatherVars(c, s.getBody, outerProc, cap) of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil else: for i in 0.. 0: lcomma(n) + 2 else: len("{:}") of nkSymChoice: result = lsons(n) + len("()") + sonsLen(n) - 1 @@ -759,7 +759,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if i > 0: put(g, tkOpr, "|") gsub(g, n.sons[i], c) put(g, tkParRi, ")") - of nkPar: + of nkPar, nkClosure: put(g, tkParLe, "(") gcomma(g, n, c) put(g, tkParRi, ")") diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 32244af850..24161e85ea 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -50,6 +50,10 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} = result.typ = s.typ result.info = n.info +proc illegalCapture(s: PSym): bool {.inline.} = + result = skipTypes(s.typ, abstractInst).kind in {tyVar, tyOpenArray} or + s.kind == skResult + proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = case s.kind of skProc, skMethod, skIterator, skConverter: @@ -83,10 +87,16 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = newSymNode(s, n.info) of skMacro: result = semMacroExpr(c, n, s) of skTemplate: result = semTemplateExpr(c, n, s) - of skVar, skLet, skResult: + of skVar, skLet, skResult, skParam: markUsed(n, s) # if a proc accesses a global variable, it is not side effect free: - if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect) + if sfGlobal in s.flags: + incl(c.p.owner.flags, sfSideEffect) + elif s.owner != c.p.owner and s.owner.kind != skModule: + c.p.owner.typ.callConv = ccClosure + if illegalCapture(s) or c.p.next.owner != s.owner: + # Currently captures are restricted to a single level of nesting: + GlobalError(n.info, errIllegalCaptureX, s.name.s) result = newSymNode(s, n.info) of skGenericParam: if s.ast == nil: InternalError(n.info, "no default for") diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index ab8081031c..4249500565 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -644,8 +644,6 @@ proc semLambda(c: PContext, n: PNode): PNode = else: s.typ = newTypeS(tyProc, c) addSon(s.typ, nil) - # no! do a proper analysis to determine calling convention - when false: s.typ.callConv = ccClosure if n.sons[pragmasPos].kind != nkEmpty: pragma(c, s, n.sons[pragmasPos], lambdaPragmas) s.options = gOptions @@ -697,9 +695,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # open for parameters if proto == nil: s.typ.callConv = lastOptionEntry(c).defaultCC - when false: - # do a proper analysis here: - if c.p.owner.kind != skModule: s.typ.callConv = ccClosure # add it here, so that recursive procs are possible: # -2 because we have a scope open for parameters if kind in OverloadableSyms: diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim index 59ff4e6529..f542e04d66 100755 --- a/compiler/semthreads.nim +++ b/compiler/semthreads.nim @@ -360,7 +360,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner = if n.sons[0].kind != nkEmpty: result = analyse(c, n.sons[0]) else: result = toVoid of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef, - nkConverterDef, nkMacroDef, nkTemplateDef: + nkConverterDef, nkMacroDef, nkTemplateDef, nkLambda: result = toVoid of nkExprColonExpr: result = analyse(c, n.sons[1]) diff --git a/compiler/transf.nim b/compiler/transf.nim index 51188928c9..be8f7aeb55 100755 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -15,6 +15,7 @@ # * performes contant folding # * converts "continue" to "break" # * introduces method dispatchers +# * performs lambda lifting for closure support import intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, @@ -355,6 +356,8 @@ 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]) + +include lambdalifting proc transformConv(c: PTransf, n: PNode): PTransNode = # numeric types need range checks: @@ -428,6 +431,11 @@ 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: + result = generateThunk(c, n.sons[1], dest).ptransnode + else: + result = transformSons(c, n) of tyGenericParam, tyOrdinal: result = transform(c, n.sons[1]) # happens sometimes for generated assignments, etc. @@ -510,8 +518,6 @@ proc getMagicOp(call: PNode): TMagic = else: result = mNone -include lambdalifting - proc transformCase(c: PTransf, n: PNode): PTransNode = # removes `elif` branches of a case stmt # adds ``else: nil`` if needed for the code generator diff --git a/compiler/trees.nim b/compiler/trees.nim index f393bfc66a..57acfba8a4 100755 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -115,7 +115,7 @@ proc isDeepConstExpr*(n: PNode): bool = result = true of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: result = isDeepConstExpr(n.sons[1]) - of nkCurly, nkBracket, nkPar: + of nkCurly, nkBracket, nkPar, nkClosure: for i in 0 ..