further steps to closure support
This commit is contained in:
parent
0d4c8ec70c
commit
632aece191
20 changed files with 379 additions and 138 deletions
|
|
@ -189,7 +189,8 @@ type
|
||||||
nkProcTy, # proc type
|
nkProcTy, # proc type
|
||||||
nkEnumTy, # enum body
|
nkEnumTy, # enum body
|
||||||
nkEnumFieldDef, # `ident = expr` in an enumeration
|
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]
|
TNodeKinds* = set[TNodeKind]
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -977,11 +978,12 @@ proc hasSonWith(n: PNode, kind: TNodeKind): bool =
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc containsNode*(n: PNode, kinds: TNodeKinds): bool =
|
proc containsNode*(n: PNode, kinds: TNodeKinds): bool =
|
||||||
|
if n == nil: return
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkEmpty..nkNilLit: result = n.kind in kinds
|
of nkEmpty..nkNilLit: result = n.kind in kinds
|
||||||
else:
|
else:
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
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 =
|
proc hasSubnodeWith(n: PNode, kind: TNodeKind): bool =
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -1037,6 +1039,10 @@ proc isGenericRoutine*(s: PSym): bool =
|
||||||
result = s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty
|
result = s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty
|
||||||
else: nil
|
else: nil
|
||||||
|
|
||||||
|
proc isRoutine*(s: PSym): bool {.inline.} =
|
||||||
|
result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod,
|
||||||
|
skConverter}
|
||||||
|
|
||||||
iterator items*(n: PNode): PNode =
|
iterator items*(n: PNode): PNode =
|
||||||
for i in 0.. <n.len: yield n.sons[i]
|
for i in 0.. <n.len: yield n.sons[i]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, pl: PRope) =
|
||||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||||
assert(d.t != nil) # generate an assignment to d:
|
assert(d.t != nil) # generate an assignment to d:
|
||||||
var list: TLoc
|
var list: TLoc
|
||||||
initLoc(list, locCall, nil, OnUnknown)
|
initLoc(list, locCall, d.t, OnUnknown)
|
||||||
list.r = pl
|
list.r = pl
|
||||||
genAssignment(p, d, list, {}) # no need for deep copying
|
genAssignment(p, d, list, {}) # no need for deep copying
|
||||||
else:
|
else:
|
||||||
|
|
@ -137,6 +137,67 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
if i < length - 1: app(pl, ", ")
|
if i < length - 1: app(pl, ", ")
|
||||||
fixupCall(p, le, ri, d, pl)
|
fixupCall(p, le, ri, d, pl)
|
||||||
|
|
||||||
|
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
|
|
||||||
|
proc getRawProcType(p: BProc, t: PType): PRope =
|
||||||
|
var d = copyType(t, t.owner, false)
|
||||||
|
d.callConv = ccDefault
|
||||||
|
result = getTypeDesc(p.module, d)
|
||||||
|
|
||||||
|
proc addComma(r: PRope): PRope =
|
||||||
|
result = if r == nil: r else: con(r, ", ")
|
||||||
|
|
||||||
|
const CallPattern = "$1.ClEnv? $1.ClPrc($3$1.ClEnv) : (($4)($1.ClPrc))($2)"
|
||||||
|
var op: TLoc
|
||||||
|
initLocExpr(p, ri.sons[0], op)
|
||||||
|
var pl: PRope
|
||||||
|
var typ = ri.sons[0].typ
|
||||||
|
assert(typ.kind == tyProc)
|
||||||
|
var length = sonsLen(ri)
|
||||||
|
for i in countup(1, length - 1):
|
||||||
|
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||||
|
if i < sonsLen(typ):
|
||||||
|
assert(typ.n.sons[i].kind == nkSym)
|
||||||
|
app(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym))
|
||||||
|
else:
|
||||||
|
app(pl, genArgNoParam(p, ri.sons[i]))
|
||||||
|
if i < length - 1: app(pl, ", ")
|
||||||
|
|
||||||
|
template genCallPattern =
|
||||||
|
appf(p.s[cpsStmts], CallPattern, op.r, pl, pl.addComma, rawProc)
|
||||||
|
|
||||||
|
let rawProc = getRawProcType(p, typ)
|
||||||
|
if typ.sons[0] != nil:
|
||||||
|
if isInvalidReturnType(typ.sons[0]):
|
||||||
|
if sonsLen(ri) > 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) =
|
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
var op, a: TLoc
|
var op, a: TLoc
|
||||||
initLocExpr(p, ri.sons[0], op)
|
initLocExpr(p, ri.sons[0], op)
|
||||||
|
|
@ -224,7 +285,9 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||||
appf(p.s[cpsStmts], ";$n")
|
appf(p.s[cpsStmts], ";$n")
|
||||||
|
|
||||||
proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
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:
|
e.len >= 2:
|
||||||
genInfixCall(p, nil, e, d)
|
genInfixCall(p, nil, e, d)
|
||||||
elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
|
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)
|
if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
|
||||||
|
|
||||||
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
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:
|
ri.len >= 2:
|
||||||
genInfixCall(p, le, ri, d)
|
genInfixCall(p, le, ri, d)
|
||||||
elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
|
elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||||
appcg(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
|
appcg(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
|
||||||
[addrLoc(dest), rdLoc(src)])
|
[addrLoc(dest), rdLoc(src)])
|
||||||
if needToKeepAlive in flags: keepAlive(p, dest)
|
if needToKeepAlive in flags: keepAlive(p, dest)
|
||||||
of tyTuple, tyObject:
|
of tyTuple, tyObject, tyProc:
|
||||||
# XXX: check for subtyping?
|
# XXX: check for subtyping?
|
||||||
if needsComplexAssignment(dest.t):
|
if needsComplexAssignment(dest.t):
|
||||||
genGenericAsgn(p, dest, src, flags)
|
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))])
|
[rdLoc(dest), rdLoc(src), toRope(getSize(dest.t))])
|
||||||
else:
|
else:
|
||||||
appcg(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
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:
|
tyInt..tyFloat128, tyRange:
|
||||||
appcg(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
appcg(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
||||||
else: InternalError("genAssignment(" & $ty.kind & ')')
|
else: InternalError("genAssignment(" & $ty.kind & ')')
|
||||||
|
|
@ -1308,7 +1308,6 @@ proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
[rdLoc(a)]))
|
[rdLoc(a)]))
|
||||||
|
|
||||||
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
|
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
# XXX we don't generate keep alive info here
|
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExpr(p, n.sons[0], a)
|
initLocExpr(p, n.sons[0], a)
|
||||||
putIntoDest(p, d, skipTypes(n.typ, abstractVar),
|
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)])
|
[rdLoc(d), mangleRecFieldName(t.n.sons[i].sym, t)])
|
||||||
expr(p, it, rec)
|
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) =
|
proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
var arr: TLoc
|
var arr: TLoc
|
||||||
if not handleConstExpr(p, n, d):
|
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:
|
if sym.loc.r == nil or sym.loc.t == nil:
|
||||||
InternalError(e.info, "expr: proc not init " & sym.name.s)
|
InternalError(e.info, "expr: proc not init " & sym.name.s)
|
||||||
putLocIntoDest(p, d, sym.loc)
|
putLocIntoDest(p, d, sym.loc)
|
||||||
|
of nkClosure: genClosure(p, e, d)
|
||||||
of nkMetaNode: expr(p, e.sons[0], d)
|
of nkMetaNode: expr(p, e.sons[0], d)
|
||||||
else: InternalError(e.info, "expr(" & $e.kind & "); unknown node kind")
|
else: InternalError(e.info, "expr(" & $e.kind & "); unknown node kind")
|
||||||
|
|
||||||
|
|
@ -1751,7 +1773,7 @@ proc genConstExpr(p: BProc, n: PNode): PRope =
|
||||||
var cs: TBitSet
|
var cs: TBitSet
|
||||||
toBitSet(n, cs)
|
toBitSet(n, cs)
|
||||||
result = genRawSetData(cs, int(getSize(n.typ)))
|
result = genRawSetData(cs, int(getSize(n.typ)))
|
||||||
of nkBracket, nkPar:
|
of nkBracket, nkPar, nkClosure:
|
||||||
var t = skipTypes(n.typ, abstractInst)
|
var t = skipTypes(n.typ, abstractInst)
|
||||||
if t.kind == tySequence:
|
if t.kind == tySequence:
|
||||||
result = genConstSeq(p, n, t)
|
result = genConstSeq(p, n, t)
|
||||||
|
|
|
||||||
|
|
@ -57,13 +57,24 @@ proc genSingleVar(p: BProc, a: PNode) =
|
||||||
genLineDir(p, a)
|
genLineDir(p, a)
|
||||||
loadInto(p, a.sons[0], a.sons[2], v.loc)
|
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) =
|
proc genVarStmt(p: BProc, n: PNode) =
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
var a = n.sons[i]
|
var a = n.sons[i]
|
||||||
if a.kind == nkCommentStmt: continue
|
if a.kind == nkCommentStmt: continue
|
||||||
if a.kind == nkIdentDefs:
|
if a.kind == nkIdentDefs:
|
||||||
assert(a.sons[0].kind == nkSym)
|
# can be a lifted var nowadays ...
|
||||||
genSingleVar(p, a)
|
if a.sons[0].kind == nkSym:
|
||||||
|
genSingleVar(p, a)
|
||||||
|
else:
|
||||||
|
genClosureVar(p, a)
|
||||||
else:
|
else:
|
||||||
genVarTuple(p, a)
|
genVarTuple(p, a)
|
||||||
|
|
||||||
|
|
@ -704,7 +715,7 @@ proc genStmts(p: BProc, t: PNode) =
|
||||||
of nkReturnStmt: genReturnStmt(p, t)
|
of nkReturnStmt: genReturnStmt(p, t)
|
||||||
of nkBreakStmt: genBreakStmt(p, t)
|
of nkBreakStmt: genBreakStmt(p, t)
|
||||||
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand,
|
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand,
|
||||||
nkCallStrLit:
|
nkCallStrLit, nkClosure:
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
initLocExpr(p, t, a)
|
initLocExpr(p, t, a)
|
||||||
of nkAsgn: genAsgn(p, t, fastAsgn=false)
|
of nkAsgn: genAsgn(p, t, fastAsgn=false)
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ proc mapType(typ: PType): TCTypeKind =
|
||||||
else: result = ctPtr
|
else: result = ctPtr
|
||||||
of tyPointer: result = ctPtr
|
of tyPointer: result = ctPtr
|
||||||
of tySequence: result = ctNimSeq
|
of tySequence: result = ctNimSeq
|
||||||
of tyProc: result = ctProc
|
of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct
|
||||||
of tyString: result = ctNimStr
|
of tyString: result = ctNimStr
|
||||||
of tyCString: result = ctCString
|
of tyCString: result = ctCString
|
||||||
of tyInt..tyFloat128:
|
of tyInt..tyFloat128:
|
||||||
|
|
@ -215,7 +215,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var PRope,
|
||||||
appff(params, " Result", " @Result", [])
|
appff(params, " Result", " @Result", [])
|
||||||
if t.callConv == ccClosure:
|
if t.callConv == ccClosure:
|
||||||
if params != nil: app(params, ", ")
|
if params != nil: app(params, ", ")
|
||||||
app(params, "void* ClPart")
|
app(params, "void* ClEnv")
|
||||||
if tfVarargs in t.flags:
|
if tfVarargs in t.flags:
|
||||||
if params != nil: app(params, ", ")
|
if params != nil: app(params, ", ")
|
||||||
app(params, "...")
|
app(params, "...")
|
||||||
|
|
@ -331,7 +331,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||||
appf(result, "} $1;$n", [uname])
|
appf(result, "} $1;$n", [uname])
|
||||||
of nkSym:
|
of nkSym:
|
||||||
field = n.sym
|
field = n.sym
|
||||||
assert(field.ast == nil)
|
#assert(field.ast == nil)
|
||||||
sname = mangleRecFieldName(field, rectype)
|
sname = mangleRecFieldName(field, rectype)
|
||||||
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
|
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
|
||||||
else: ae = sname
|
else: ae = sname
|
||||||
|
|
@ -437,8 +437,9 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
|
||||||
appf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",
|
appf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",
|
||||||
[toRope(CallingConvToStr[t.callConv]), rettype, result, desc])
|
[toRope(CallingConvToStr[t.callConv]), rettype, result, desc])
|
||||||
else:
|
else:
|
||||||
appf(m.s[cfsTypes], "typedef struct $1 {$n" &
|
appf(m.s[cfsTypes], "typedef struct {$n" &
|
||||||
"N_CDECL_PTR($2, PrcPart) $3;$n" & "void* ClPart;$n};$n",
|
"N_CDECL_PTR($2, ClPrc) $3;$n" &
|
||||||
|
"void* ClEnv;$n} $1;$n",
|
||||||
[result, rettype, desc])
|
[result, rettype, desc])
|
||||||
of tySequence:
|
of tySequence:
|
||||||
# we cannot use getTypeForward here because then t would be associated
|
# 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)
|
var tmp2 = getNimNode(m)
|
||||||
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(i), tmp2])
|
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(i), tmp2])
|
||||||
appf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
|
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",
|
"$1.name = \"Field$3\";$n",
|
||||||
[tmp2, getTypeDesc(m, typ), toRope(i), genTypeInfo(m, a)])
|
[tmp2, getTypeDesc(m, typ), toRope(i), genTypeInfo(m, a)])
|
||||||
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
|
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) =
|
proc genArrayInfo(m: BModule, typ: PType, name: PRope) =
|
||||||
genTypeInfoAuxBase(m, typ, name, genTypeInfo(m, typ.sons[1]))
|
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 =
|
proc genTypeInfo(m: BModule, typ: PType): PRope =
|
||||||
var t = getUniqueType(typ)
|
var t = getUniqueType(typ)
|
||||||
# gNimDat contains all the type information nowadays:
|
# gNimDat contains all the type information nowadays:
|
||||||
|
|
@ -750,9 +760,13 @@ proc genTypeInfo(m: BModule, typ: PType): PRope =
|
||||||
if dataGenerated: return
|
if dataGenerated: return
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyEmpty: result = toRope"0"
|
of tyEmpty: result = toRope"0"
|
||||||
of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128,
|
of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128, tyVar:
|
||||||
tyVar:
|
|
||||||
genTypeInfoAuxBase(gNimDat, t, result, toRope"0")
|
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 tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(gNimDat, t, result)
|
||||||
of tyArrayConstr, tyArray: genArrayInfo(gNimDat, t, result)
|
of tyArrayConstr, tyArray: genArrayInfo(gNimDat, t, result)
|
||||||
of tySet: genSetInfo(gNimDat, t, result)
|
of tySet: genSetInfo(gNimDat, t, result)
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,10 @@ proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) =
|
||||||
if a.r == nil: a.r = r
|
if a.r == nil: a.r = r
|
||||||
|
|
||||||
proc isSimpleConst(typ: PType): bool =
|
proc isSimpleConst(typ: PType): bool =
|
||||||
result = skipTypes(typ, abstractVar).kind notin
|
let t = skipTypes(typ, abstractVar)
|
||||||
{tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence}
|
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) =
|
proc useHeader(m: BModule, sym: PSym) =
|
||||||
if lfHeader in sym.loc.Flags:
|
if lfHeader in sym.loc.Flags:
|
||||||
|
|
@ -223,11 +225,12 @@ type
|
||||||
|
|
||||||
proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
|
proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
|
||||||
|
|
||||||
const
|
proc isComplexValueType(t: PType): bool {.inline.} =
|
||||||
complexValueType = {tyArray, tyArrayConstr, tySet, tyTuple, tyObject}
|
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) =
|
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:
|
if containsGcref and p.WithInLoop > 0:
|
||||||
appf(p.s[cpsInit], "$1 = 0;$n", [rdLoc(loc)])
|
appf(p.s[cpsInit], "$1 = 0;$n", [rdLoc(loc)])
|
||||||
var nilLoc: TLoc
|
var nilLoc: TLoc
|
||||||
|
|
@ -250,7 +253,7 @@ proc zeroVar(p: BProc, loc: TLoc, containsGCref: bool) =
|
||||||
genObjectInit(p, cpsStmts, loc.t, loc, true)
|
genObjectInit(p, cpsStmts, loc.t, loc, true)
|
||||||
|
|
||||||
proc zeroTemp(p: BProc, loc: TLoc) =
|
proc zeroTemp(p: BProc, loc: TLoc) =
|
||||||
if skipTypes(loc.t, abstractVarRange).Kind notin complexValueType:
|
if not isComplexValueType(skipTypes(loc.t, abstractVarRange)):
|
||||||
appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(loc)])
|
appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(loc)])
|
||||||
when false:
|
when false:
|
||||||
var nilLoc: TLoc
|
var nilLoc: TLoc
|
||||||
|
|
@ -313,7 +316,7 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) =
|
||||||
result.s = OnStack
|
result.s = OnStack
|
||||||
result.flags = {}
|
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)])
|
appf(p.s[cpsStmts], "$1 = $2;$n", [rdLoc(result), rdLoc(toKeepAlive)])
|
||||||
else:
|
else:
|
||||||
appcg(p, cpsStmts,
|
appcg(p, cpsStmts,
|
||||||
|
|
@ -571,6 +574,15 @@ proc initFrame(p: BProc, procname, filename: PRope): PRope =
|
||||||
proc deinitFrame(p: BProc): PRope =
|
proc deinitFrame(p: BProc): PRope =
|
||||||
result = ropecg(p.module, "#popFrame();$n")
|
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) =
|
proc genProcAux(m: BModule, prc: PSym) =
|
||||||
var p = newProc(prc, m)
|
var p = newProc(prc, m)
|
||||||
var header = genProcHeader(m, prc)
|
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):
|
for i in countup(1, sonsLen(prc.typ.n) - 1):
|
||||||
var param = prc.typ.n.sons[i].sym
|
var param = prc.typ.n.sons[i].sym
|
||||||
assignParam(p, param)
|
assignParam(p, param)
|
||||||
|
closureSetup(p, prc)
|
||||||
genStmts(p, prc.getBody) # modifies p.locals, p.init, etc.
|
genStmts(p, prc.getBody) # modifies p.locals, p.init, etc.
|
||||||
var generatedProc: PRope
|
var generatedProc: PRope
|
||||||
if sfPure in prc.flags:
|
if sfPure in prc.flags:
|
||||||
|
|
|
||||||
|
|
@ -9,34 +9,46 @@
|
||||||
|
|
||||||
# This include file implements lambda lifting for the transformator.
|
# 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
|
const
|
||||||
procDefs = {nkLambda, nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef,
|
procDefs = {nkLambda, nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef,
|
||||||
nkConverterDef}
|
nkConverterDef}
|
||||||
|
|
||||||
proc indirectAccess(a, b: PSym): PNode =
|
proc indirectAccess(a, b: PSym, info: TLineInfo): PNode =
|
||||||
# returns a[].b as a node
|
# returns a[].b as a node
|
||||||
var x = newSymNode(a)
|
let x = newSymNode(a)
|
||||||
var y = newSymNode(b)
|
var deref = newNodeI(nkHiddenDeref, info)
|
||||||
var deref = newNodeI(nkHiddenDeref, x.info)
|
|
||||||
deref.typ = x.typ.sons[0]
|
deref.typ = x.typ.sons[0]
|
||||||
|
|
||||||
|
let field = getSymFromList(deref.typ.n, b.name)
|
||||||
addSon(deref, x)
|
addSon(deref, x)
|
||||||
result = newNodeI(nkDotExpr, x.info)
|
result = newNodeI(nkDotExpr, info)
|
||||||
addSon(result, deref)
|
addSon(result, deref)
|
||||||
addSon(result, y)
|
addSon(result, newSymNode(field))
|
||||||
result.typ = y.typ
|
result.typ = field.typ
|
||||||
|
|
||||||
proc Incl(container: PNode, s: PSym) =
|
type
|
||||||
for x in container:
|
TCapture = seq[PSym]
|
||||||
if x.sym.id == s.id: return
|
|
||||||
container.add(newSymNode(s))
|
|
||||||
|
|
||||||
proc gatherVars(c: PTransf, n: PNode, owner: PSym, container: PNode) =
|
proc Capture(cap: var TCapture, s: PSym) =
|
||||||
# gather used vars for closure generation
|
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
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var s = n.sym
|
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 skVar, skLet: found = sfGlobal notin s.flags
|
||||||
of skTemp, skForVar, skParam, skResult: found = true
|
of skTemp, skForVar, skParam, skResult: found = true
|
||||||
else: nil
|
else: nil
|
||||||
if found and owner.id != s.owner.id:
|
if found and outerProc.id == s.owner.id:
|
||||||
incl(container, s)
|
#echo "captured: ", s.name.s
|
||||||
|
Capture(cap, s)
|
||||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil
|
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil
|
||||||
else:
|
else:
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
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):
|
for i in countup(0, safeLen(n) - 1):
|
||||||
if n.kind == nkSym:
|
let a = n.sons[i]
|
||||||
let s = n.sym
|
if a.kind == nkSym:
|
||||||
|
let s = a.sym
|
||||||
var found = false
|
var found = false
|
||||||
case s.kind
|
case s.kind
|
||||||
of skVar, skLet: found = sfGlobal notin s.flags
|
of skVar, skLet: found = sfGlobal notin s.flags
|
||||||
of skTemp, skForVar, skParam, skResult: found = true
|
of skTemp, skForVar, skParam, skResult: found = true
|
||||||
else: nil
|
else: nil
|
||||||
if found and owner.id != s.owner.id:
|
if found and outerProc.id == s.owner.id:
|
||||||
# access through the closure param:
|
# access through the closure param:
|
||||||
n.sons[i] = indirectAccess(env, s)
|
n.sons[i] = indirectAccess(env, s, n.info)
|
||||||
else:
|
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) =
|
proc addFormalParam(routine: PSym, param: PSym) =
|
||||||
addSon(routine.typ, param.typ)
|
#addFormalParam(routine.typ, param)
|
||||||
addSon(routine.ast.sons[paramsPos], newSymNode(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
|
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
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
if isInnerProc(s, owner):
|
if isInnerProc(s, outerProc):
|
||||||
gatherVars(c, s.getBody, owner, container)
|
gatherVars(c, s.getBody, outerProc, cap)
|
||||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil
|
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil
|
||||||
else:
|
else:
|
||||||
for i in 0.. <len(n):
|
for i in 0.. <len(n):
|
||||||
searchForInnerProcs(c, n.sons[i], owner, container)
|
searchForInnerProcs(c, n.sons[i], outerProc, cap)
|
||||||
|
|
||||||
proc makeClosure(c: PTransf, prc, env: PSym): PNode =
|
proc makeClosure(c: PTransf, prc, env: PSym, info: TLineInfo): PNode =
|
||||||
var tup = newType(tyTuple, c.module)
|
result = newNodeIT(nkClosure, info, prc.typ)
|
||||||
tup.addson(prc.typ)
|
|
||||||
tup.addson(env.typ)
|
|
||||||
result = newNodeIT(nkPar, prc.info, tup)
|
|
||||||
result.add(newSymNode(prc))
|
result.add(newSymNode(prc))
|
||||||
result.add(newSymNode(env))
|
result.add(newSymNode(env))
|
||||||
|
|
||||||
proc transformInnerProcs(c: PTransf, n: PNode, owner, env: PSym) =
|
proc transformInnerProcs(c: PTransf, n: PNode, outerProc, env: PSym) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let innerProc = n.sym
|
let innerProc = n.sym
|
||||||
if isInnerProc(innerProc, owner):
|
if isInnerProc(innerProc, outerProc):
|
||||||
# inner proc could capture outer vars:
|
# inner proc could capture outer vars:
|
||||||
var param = newTemp(c, env.typ, n.info)
|
var param = newTemp(c, env.typ, n.info)
|
||||||
param.kind = skParam
|
param.kind = skParam
|
||||||
addFormalParam(innerProc, param)
|
addFormalParam(innerProc, param)
|
||||||
# 'anon' should be replaced by '(anon, env)':
|
# 'anon' should be replaced by '(anon, env)':
|
||||||
IdNodeTablePut(c.transCon.mapping, innerProc,
|
IdNodeTablePut(c.transCon.mapping, innerProc,
|
||||||
makeClosure(c, innerProc, env))
|
makeClosure(c, innerProc, env, n.info))
|
||||||
|
|
||||||
# access all non-local vars through the 'env' param:
|
# access all non-local vars through the 'env' param:
|
||||||
var body = innerProc.getBody
|
var body = innerProc.getBody
|
||||||
replaceVars(c, body, innerProc, param)
|
# XXX does not work with recursion!
|
||||||
|
replaceVars(c, body, outerProc, param)
|
||||||
|
innerProc.ast.sons[bodyPos] = body
|
||||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil
|
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil
|
||||||
else:
|
else:
|
||||||
for i in 0.. <len(n):
|
for i in 0.. <len(n):
|
||||||
transformInnerProcs(c, n.sons[i], owner, env)
|
transformInnerProcs(c, n.sons[i], outerProc, env)
|
||||||
|
|
||||||
proc newCall(a, b: PSym): PNode =
|
proc newCall(a, b: PSym): PNode =
|
||||||
result = newNodeI(nkCall, a.info)
|
result = newNodeI(nkCall, a.info)
|
||||||
result.add newSymNode(a)
|
result.add newSymNode(a)
|
||||||
result.add newSymNode(b)
|
result.add newSymNode(b)
|
||||||
|
|
||||||
proc createEnvStmt(c: PTransf, varList: PNode, env: PSym): PTransNode =
|
proc createEnvStmt(c: PTransf, varList: TCapture, env: PSym): PTransNode =
|
||||||
# 'varlist' can contain parameters or variables. We don't eliminate yet
|
# 'varlist' can contain parameters or variables. We don't eliminate yet
|
||||||
# local vars that end up in an environment. This could even be a for loop
|
# local vars that end up in an environment. This could even be a for loop
|
||||||
# var!
|
# var!
|
||||||
|
|
@ -129,16 +147,32 @@ proc createEnvStmt(c: PTransf, varList: PNode, env: PSym): PTransNode =
|
||||||
addVar(v, newSymNode(env))
|
addVar(v, newSymNode(env))
|
||||||
result.add(v.ptransNode)
|
result.add(v.ptransNode)
|
||||||
# add 'new' statement:
|
# add 'new' statement:
|
||||||
result.add(newCall(getSysSym"new", env).ptransnode)
|
result.add(newCall(getSysSym"internalNew", env).ptransnode)
|
||||||
|
|
||||||
# add assignment statements:
|
# add assignment statements:
|
||||||
for v in varList:
|
for v in varList:
|
||||||
assert v.kind == nkSym
|
let fieldAccess = indirectAccess(env, v, env.info)
|
||||||
let fieldAccess = indirectAccess(env, v.sym)
|
if v.kind == skParam:
|
||||||
if v.sym.kind == skParam:
|
|
||||||
# add ``env.param = param``
|
# add ``env.param = param``
|
||||||
result.add(newAsgnStmt(c, fieldAccess, v.ptransNode))
|
result.add(newAsgnStmt(c, fieldAccess, newSymNode(v).ptransNode))
|
||||||
IdNodeTablePut(c.transCon.mapping, v.sym, fieldAccess)
|
IdNodeTablePut(c.transCon.mapping, v, fieldAccess)
|
||||||
|
|
||||||
|
proc transformProcFin(c: PTransf, n: PNode, s: PSym): PTransNode =
|
||||||
|
# to be safe: XXX this a mystery how it could ever happen that: s.ast != n.
|
||||||
|
s.ast.sons[bodyPos] = n.sons[bodyPos]
|
||||||
|
if n.kind == nkMethodDef: methodDef(s, false)
|
||||||
|
|
||||||
|
# should 's' be replaced by a tuple ('s', env)?
|
||||||
|
var tc = c.transCon
|
||||||
|
var repl: PNode = nil
|
||||||
|
while tc != nil:
|
||||||
|
repl = IdNodeTableGet(tc.mapping, s)
|
||||||
|
if repl != nil: break
|
||||||
|
tc = tc.next
|
||||||
|
if repl != nil:
|
||||||
|
result = PTransNode(repl)
|
||||||
|
else:
|
||||||
|
result = PTransNode(n)
|
||||||
|
|
||||||
proc transformProc(c: PTransf, n: PNode): PTransNode =
|
proc transformProc(c: PTransf, n: PNode): PTransNode =
|
||||||
# don't process generics:
|
# don't process generics:
|
||||||
|
|
@ -147,34 +181,32 @@ proc transformProc(c: PTransf, n: PNode): PTransNode =
|
||||||
|
|
||||||
var s = n.sons[namePos].sym
|
var s = n.sons[namePos].sym
|
||||||
var body = s.getBody
|
var body = s.getBody
|
||||||
|
if body.kind == nkEmpty:
|
||||||
|
return PTransNode(n)
|
||||||
|
|
||||||
if not containsNode(body, procDefs):
|
if not containsNode(body, procDefs):
|
||||||
# fast path: no inner procs, so no closure needed:
|
# fast path: no inner procs, so no closure needed:
|
||||||
n.sons[bodyPos] = PNode(transform(c, body))
|
n.sons[bodyPos] = PNode(transform(c, body))
|
||||||
if n.kind == nkMethodDef: methodDef(s, false)
|
return transformProcFin(c, n, s)
|
||||||
return PTransNode(n)
|
|
||||||
|
|
||||||
var closure = newNodeI(nkRecList, n.info)
|
|
||||||
searchForInnerProcs(c, body, s, closure)
|
|
||||||
|
|
||||||
if closure.len == 0:
|
|
||||||
# fast path: no captured variables, so no closure needed:
|
|
||||||
n.sons[bodyPos] = PNode(transform(c, body))
|
|
||||||
if n.kind == nkMethodDef: methodDef(s, false)
|
|
||||||
return PTransNode(n)
|
|
||||||
|
|
||||||
# create environment:
|
# create environment:
|
||||||
var envDesc = newType(tyObject, s)
|
var cap: TCapture = @[]
|
||||||
envDesc.n = closure
|
searchForInnerProcs(c, body, s, cap)
|
||||||
addSon(envDesc, nil) # no super class
|
|
||||||
var envType = newType(tyRef, s)
|
|
||||||
addSon(envType, envDesc)
|
|
||||||
|
|
||||||
# XXX currently we always do a heap allocation. A simple escape analysis
|
if cap.len == 0:
|
||||||
# could turn the closure into a stack allocation. Later versions will
|
# fast path: no captured variables, so no closure needed:
|
||||||
# implement that.
|
n.sons[bodyPos] = PNode(transform(c, body))
|
||||||
|
return transformProcFin(c, n, s)
|
||||||
|
|
||||||
|
var envType = newType(tyRef, s)
|
||||||
|
addSon(envType, captureToTuple(cap, s))
|
||||||
|
|
||||||
|
# Currently we always do a heap allocation. A simple escape analysis
|
||||||
|
# could turn the closure into a stack allocation. Later versions might
|
||||||
|
# implement that. This would require backend changes too though.
|
||||||
var envSym = newTemp(c, envType, s.info)
|
var envSym = newTemp(c, envType, s.info)
|
||||||
|
|
||||||
var newBody = createEnvStmt(c, closure, envSym)
|
var newBody = createEnvStmt(c, cap, envSym)
|
||||||
# modify any local proc to gain a new parameter; this also creates the
|
# modify any local proc to gain a new parameter; this also creates the
|
||||||
# mapping entries that turn (localProc) into (localProc, env):
|
# mapping entries that turn (localProc) into (localProc, env):
|
||||||
transformInnerProcs(c, body, s, envSym)
|
transformInnerProcs(c, body, s, envSym)
|
||||||
|
|
@ -183,19 +215,18 @@ proc transformProc(c: PTransf, n: PNode): PTransNode =
|
||||||
# Careful this transforms the inner procs too!
|
# Careful this transforms the inner procs too!
|
||||||
newBody.add(transform(c, body))
|
newBody.add(transform(c, body))
|
||||||
n.sons[bodyPos] = newBody.pnode
|
n.sons[bodyPos] = newBody.pnode
|
||||||
if n.kind == nkMethodDef: methodDef(s, false)
|
result = transformProcFin(c, n, s)
|
||||||
result = newBody
|
|
||||||
|
|
||||||
proc generateThunk(c: PTransf, prc: PNode, closure: PType): PNode =
|
proc generateThunk(c: PTransf, prc: PNode, dest: PType): PNode =
|
||||||
## Converts 'prc' into '(thunk, nil)' so that it's compatible with
|
## Converts 'prc' into '(thunk, nil)' so that it's compatible with
|
||||||
## a closure.
|
## a closure.
|
||||||
|
|
||||||
# XXX we hack around here by generating a 'cast' instead of a proper thunk.
|
# XXX we hack around here by generating a 'cast' instead of a proper thunk.
|
||||||
result = newNodeIT(nkPar, prc.info, closure)
|
result = newNodeIT(nkClosure, prc.info, dest)
|
||||||
var conv = newNodeIT(nkHiddenStdConv, prc.info, closure.sons[0])
|
var conv = newNodeIT(nkHiddenStdConv, prc.info, dest)
|
||||||
conv.add(emptyNode)
|
conv.add(emptyNode)
|
||||||
conv.add(prc)
|
conv.add(prc)
|
||||||
result.add(conv)
|
result.add(conv)
|
||||||
result.add(newNodeIT(nkNilLit, prc.info, closure.sons[1]))
|
result.add(newNodeIT(nkNilLit, prc.info, getSysType(tyNil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ type
|
||||||
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
|
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
|
||||||
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
|
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
|
||||||
errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
|
errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
|
||||||
errThreadvarCannotInit, errWrongSymbolX,
|
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
|
||||||
errUser,
|
errUser,
|
||||||
warnCannotOpenFile,
|
warnCannotOpenFile,
|
||||||
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
||||||
|
|
@ -325,6 +325,7 @@ const
|
||||||
errLetNeedsInit: "'let' symbol requires an initialization",
|
errLetNeedsInit: "'let' symbol requires an initialization",
|
||||||
errThreadvarCannotInit: "a thread var cannot be initialized explicitly",
|
errThreadvarCannotInit: "a thread var cannot be initialized explicitly",
|
||||||
errWrongSymbolX: "usage of \'$1\' is a user-defined error",
|
errWrongSymbolX: "usage of \'$1\' is a user-defined error",
|
||||||
|
errIllegalCaptureX: "illegal capture '$1'",
|
||||||
errUser: "$1",
|
errUser: "$1",
|
||||||
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
||||||
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
||||||
|
|
|
||||||
|
|
@ -339,7 +339,7 @@ proc lsub(n: PNode): int =
|
||||||
of nkHiddenAddr, nkHiddenDeref: result = lsub(n.sons[0])
|
of nkHiddenAddr, nkHiddenDeref: result = lsub(n.sons[0])
|
||||||
of nkCommand: result = lsub(n.sons[0]) + lcomma(n, 1) + 1
|
of nkCommand: result = lsub(n.sons[0]) + lcomma(n, 1) + 1
|
||||||
of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(n) + 3
|
of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(n) + 3
|
||||||
of nkPar, nkCurly, nkBracket: result = lcomma(n) + 2
|
of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(n) + 2
|
||||||
of nkTableConstr:
|
of nkTableConstr:
|
||||||
result = if n.len > 0: lcomma(n) + 2 else: len("{:}")
|
result = if n.len > 0: lcomma(n) + 2 else: len("{:}")
|
||||||
of nkSymChoice: result = lsons(n) + len("()") + sonsLen(n) - 1
|
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, "|")
|
if i > 0: put(g, tkOpr, "|")
|
||||||
gsub(g, n.sons[i], c)
|
gsub(g, n.sons[i], c)
|
||||||
put(g, tkParRi, ")")
|
put(g, tkParRi, ")")
|
||||||
of nkPar:
|
of nkPar, nkClosure:
|
||||||
put(g, tkParLe, "(")
|
put(g, tkParLe, "(")
|
||||||
gcomma(g, n, c)
|
gcomma(g, n, c)
|
||||||
put(g, tkParRi, ")")
|
put(g, tkParRi, ")")
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,10 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
|
||||||
result.typ = s.typ
|
result.typ = s.typ
|
||||||
result.info = n.info
|
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 =
|
proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||||
case s.kind
|
case s.kind
|
||||||
of skProc, skMethod, skIterator, skConverter:
|
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)
|
result = newSymNode(s, n.info)
|
||||||
of skMacro: result = semMacroExpr(c, n, s)
|
of skMacro: result = semMacroExpr(c, n, s)
|
||||||
of skTemplate: result = semTemplateExpr(c, n, s)
|
of skTemplate: result = semTemplateExpr(c, n, s)
|
||||||
of skVar, skLet, skResult:
|
of skVar, skLet, skResult, skParam:
|
||||||
markUsed(n, s)
|
markUsed(n, s)
|
||||||
# if a proc accesses a global variable, it is not side effect free:
|
# 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)
|
result = newSymNode(s, n.info)
|
||||||
of skGenericParam:
|
of skGenericParam:
|
||||||
if s.ast == nil: InternalError(n.info, "no default for")
|
if s.ast == nil: InternalError(n.info, "no default for")
|
||||||
|
|
|
||||||
|
|
@ -644,8 +644,6 @@ proc semLambda(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
s.typ = newTypeS(tyProc, c)
|
s.typ = newTypeS(tyProc, c)
|
||||||
addSon(s.typ, nil)
|
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:
|
if n.sons[pragmasPos].kind != nkEmpty:
|
||||||
pragma(c, s, n.sons[pragmasPos], lambdaPragmas)
|
pragma(c, s, n.sons[pragmasPos], lambdaPragmas)
|
||||||
s.options = gOptions
|
s.options = gOptions
|
||||||
|
|
@ -697,9 +695,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
# open for parameters
|
# open for parameters
|
||||||
if proto == nil:
|
if proto == nil:
|
||||||
s.typ.callConv = lastOptionEntry(c).defaultCC
|
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:
|
# add it here, so that recursive procs are possible:
|
||||||
# -2 because we have a scope open for parameters
|
# -2 because we have a scope open for parameters
|
||||||
if kind in OverloadableSyms:
|
if kind in OverloadableSyms:
|
||||||
|
|
|
||||||
|
|
@ -360,7 +360,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
|
||||||
if n.sons[0].kind != nkEmpty: result = analyse(c, n.sons[0])
|
if n.sons[0].kind != nkEmpty: result = analyse(c, n.sons[0])
|
||||||
else: result = toVoid
|
else: result = toVoid
|
||||||
of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef,
|
of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef,
|
||||||
nkConverterDef, nkMacroDef, nkTemplateDef:
|
nkConverterDef, nkMacroDef, nkTemplateDef, nkLambda:
|
||||||
result = toVoid
|
result = toVoid
|
||||||
of nkExprColonExpr:
|
of nkExprColonExpr:
|
||||||
result = analyse(c, n.sons[1])
|
result = analyse(c, n.sons[1])
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
# * performes contant folding
|
# * performes contant folding
|
||||||
# * converts "continue" to "break"
|
# * converts "continue" to "break"
|
||||||
# * introduces method dispatchers
|
# * introduces method dispatchers
|
||||||
|
# * performs lambda lifting for closure support
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
|
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
|
||||||
|
|
@ -356,6 +357,8 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
|
||||||
# addr ( deref ( x )) --> x
|
# addr ( deref ( x )) --> x
|
||||||
result = PTransNode(n.sons[0].sons[0])
|
result = PTransNode(n.sons[0].sons[0])
|
||||||
|
|
||||||
|
include lambdalifting
|
||||||
|
|
||||||
proc transformConv(c: PTransf, n: PNode): PTransNode =
|
proc transformConv(c: PTransf, n: PNode): PTransNode =
|
||||||
# numeric types need range checks:
|
# numeric types need range checks:
|
||||||
var dest = skipTypes(n.typ, abstractVarRange)
|
var dest = skipTypes(n.typ, abstractVarRange)
|
||||||
|
|
@ -428,6 +431,11 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
|
||||||
result[0] = transform(c, n.sons[1])
|
result[0] = transform(c, n.sons[1])
|
||||||
else:
|
else:
|
||||||
result = transform(c, n.sons[1])
|
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:
|
of tyGenericParam, tyOrdinal:
|
||||||
result = transform(c, n.sons[1])
|
result = transform(c, n.sons[1])
|
||||||
# happens sometimes for generated assignments, etc.
|
# happens sometimes for generated assignments, etc.
|
||||||
|
|
@ -510,8 +518,6 @@ proc getMagicOp(call: PNode): TMagic =
|
||||||
else:
|
else:
|
||||||
result = mNone
|
result = mNone
|
||||||
|
|
||||||
include lambdalifting
|
|
||||||
|
|
||||||
proc transformCase(c: PTransf, n: PNode): PTransNode =
|
proc transformCase(c: PTransf, n: PNode): PTransNode =
|
||||||
# removes `elif` branches of a case stmt
|
# removes `elif` branches of a case stmt
|
||||||
# adds ``else: nil`` if needed for the code generator
|
# adds ``else: nil`` if needed for the code generator
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ proc isDeepConstExpr*(n: PNode): bool =
|
||||||
result = true
|
result = true
|
||||||
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
|
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
|
||||||
result = isDeepConstExpr(n.sons[1])
|
result = isDeepConstExpr(n.sons[1])
|
||||||
of nkCurly, nkBracket, nkPar:
|
of nkCurly, nkBracket, nkPar, nkClosure:
|
||||||
for i in 0 .. <n.len:
|
for i in 0 .. <n.len:
|
||||||
if not isDeepConstExpr(n.sons[i]): return false
|
if not isDeepConstExpr(n.sons[i]): return false
|
||||||
result = true
|
result = true
|
||||||
|
|
|
||||||
|
|
@ -276,16 +276,17 @@ proc analyseObjectWithTypeField(t: PType): TTypeFieldResult =
|
||||||
var marker = InitIntSet()
|
var marker = InitIntSet()
|
||||||
result = analyseObjectWithTypeFieldAux(t, marker)
|
result = analyseObjectWithTypeFieldAux(t, marker)
|
||||||
|
|
||||||
proc isGBCRef(t: PType): bool =
|
proc isGCRef(t: PType): bool =
|
||||||
result = t.kind in GcTypeKinds
|
result = t.kind in GcTypeKinds or
|
||||||
|
(t.kind == tyProc and t.callConv == ccClosure)
|
||||||
|
|
||||||
proc containsGarbageCollectedRef(typ: PType): bool =
|
proc containsGarbageCollectedRef(typ: PType): bool =
|
||||||
# returns true if typ contains a reference, sequence or string (all the
|
# returns true if typ contains a reference, sequence or string (all the
|
||||||
# things that are garbage-collected)
|
# things that are garbage-collected)
|
||||||
result = searchTypeFor(typ, isGBCRef)
|
result = searchTypeFor(typ, isGCRef)
|
||||||
|
|
||||||
proc isTyRef(t: PType): bool =
|
proc isTyRef(t: PType): bool =
|
||||||
result = t.kind == tyRef
|
result = t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure)
|
||||||
|
|
||||||
proc containsTyRef*(typ: PType): bool =
|
proc containsTyRef*(typ: PType): bool =
|
||||||
# returns true if typ contains a 'ref'
|
# returns true if typ contains a 'ref'
|
||||||
|
|
@ -332,6 +333,8 @@ proc canFormAcycleAux(marker: var TIntSet, typ: PType, startId: int): bool =
|
||||||
nil
|
nil
|
||||||
|
|
||||||
proc canFormAcycle(typ: PType): bool =
|
proc canFormAcycle(typ: PType): bool =
|
||||||
|
# XXX as I expect cycles introduced by closures are very rare, we pretend
|
||||||
|
# they can't happen here.
|
||||||
var marker = InitIntSet()
|
var marker = InitIntSet()
|
||||||
result = canFormAcycleAux(marker, typ, typ.id)
|
result = canFormAcycleAux(marker, typ, typ.id)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -373,7 +373,7 @@ Design
|
||||||
|
|
||||||
A ``closure`` proc var can call ordinary procs of the default Nimrod calling
|
A ``closure`` proc var can call ordinary procs of the default Nimrod calling
|
||||||
convention. But not the other way round! A closure is implemented as a
|
convention. But not the other way round! A closure is implemented as a
|
||||||
``tuple[prc, data]``. ``data`` can be nil implying a call without a closure.
|
``tuple[prc, env]``. ``env`` can be nil implying a call without a closure.
|
||||||
This means that a call through a closure generates an ``if`` but the
|
This means that a call through a closure generates an ``if`` but the
|
||||||
interoperability is worth the cost of the ``if``. Thunk generation would be
|
interoperability is worth the cost of the ``if``. Thunk generation would be
|
||||||
possible too, but it's slightly more effort to implement.
|
possible too, but it's slightly more effort to implement.
|
||||||
|
|
@ -381,6 +381,18 @@ possible too, but it's slightly more effort to implement.
|
||||||
Tests with GCC on Amd64 showed that it's really beneficical if the
|
Tests with GCC on Amd64 showed that it's really beneficical if the
|
||||||
'environment' pointer is passed as the last argument, not as the first argument.
|
'environment' pointer is passed as the last argument, not as the first argument.
|
||||||
|
|
||||||
|
Proper thunk generation is harder because the proc that is to wrap
|
||||||
|
could stem from a complex expression:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
receivesClosure(returnsDefaultCC[i])
|
||||||
|
|
||||||
|
A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require
|
||||||
|
an *additional* closure generation... Ok, not really, but it requires to pass
|
||||||
|
the function to call. So we'd end up with 2 indirect calls instead of one.
|
||||||
|
Another much more severe problem which this solution is that it's not GC-safe
|
||||||
|
to pass a proc pointer around via a generic ``ref`` type.
|
||||||
|
|
||||||
|
|
||||||
Example code:
|
Example code:
|
||||||
|
|
||||||
|
|
@ -492,3 +504,19 @@ Accumulator
|
||||||
echo a() + b()
|
echo a() + b()
|
||||||
|
|
||||||
|
|
||||||
|
Internals
|
||||||
|
---------
|
||||||
|
|
||||||
|
Lambda lifting is implemented as part of the ``transf`` pass. The ``transf``
|
||||||
|
pass generates code to setup the environment and to pass it around. However,
|
||||||
|
this pass does not change the types! So we have some kind of mismatch here; on
|
||||||
|
the one hand the proc expression becomes an explicit tuple, on the other hand
|
||||||
|
the tyProc(ccClosure) type is not changed. For C code generation it's also
|
||||||
|
important the hidden formal param is ``void*`` and not something more
|
||||||
|
specialized. However the more specialized env type needs to passed to the
|
||||||
|
backend somehow. We deal with this by modifying ``s.ast[paramPos]`` to contain
|
||||||
|
the formal hidden parameter, but not ``s.typ``!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1037,7 +1037,7 @@ A `procedural type`:idx: is internally a pointer to a procedure. ``nil`` is
|
||||||
an allowed value for variables of a procedural type. Nimrod uses procedural
|
an allowed value for variables of a procedural type. Nimrod uses procedural
|
||||||
types to achieve `functional`:idx: programming techniques.
|
types to achieve `functional`:idx: programming techniques.
|
||||||
|
|
||||||
Example:
|
Examples:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
|
|
||||||
|
|
@ -1051,12 +1051,30 @@ Example:
|
||||||
|
|
||||||
forEach(printItem) # this will NOT work because calling conventions differ
|
forEach(printItem) # this will NOT work because calling conventions differ
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
|
||||||
|
type
|
||||||
|
TOnMouseMove = proc (x, y: int) {.closure.}
|
||||||
|
|
||||||
|
proc onMouseMove(mouseX, mouseY: int) =
|
||||||
|
# has default calling convention
|
||||||
|
echo "x: ", mouseX, " y: ", mouseY
|
||||||
|
|
||||||
|
proc setOnMouseMove(mouseMoveEvent: TOnMouseMove) = nil
|
||||||
|
|
||||||
|
# ok, 'onMouseMove' has the default calling convention, which is compatible
|
||||||
|
# to 'closure':
|
||||||
|
setOnMouseMove(onMouseMove)
|
||||||
|
|
||||||
|
|
||||||
A subtle issue with procedural types is that the calling convention of the
|
A subtle issue with procedural types is that the calling convention of the
|
||||||
procedure influences the type compatibility: procedural types are only
|
procedure influences the type compatibility: procedural types are only
|
||||||
compatible if they have the same calling convention.
|
compatible if they have the same calling convention. As a special extension,
|
||||||
|
a procedure of the calling convention ``nimcall`` can be passed to a parameter
|
||||||
|
that expects a proc of the calling convention ``closure``.
|
||||||
|
|
||||||
Nimrod supports these `calling conventions`:idx:, which are all incompatible to
|
Nimrod supports these `calling conventions`:idx:\:
|
||||||
each other:
|
|
||||||
|
|
||||||
`stdcall`:idx:
|
`stdcall`:idx:
|
||||||
This the stdcall convention as specified by Microsoft. The generated C
|
This the stdcall convention as specified by Microsoft. The generated C
|
||||||
|
|
@ -1089,8 +1107,10 @@ each other:
|
||||||
same as ``fastcall``, but only for C compilers that support ``fastcall``.
|
same as ``fastcall``, but only for C compilers that support ``fastcall``.
|
||||||
|
|
||||||
`closure`:idx:
|
`closure`:idx:
|
||||||
indicates that the procedure expects a context, a closure that needs
|
indicates that the procedure has a hidden implicit parameter
|
||||||
to be passed to the procedure.
|
(an *environment*). Proc vars that have the calling convention ``closure``
|
||||||
|
take up two machine words: One for the proc pointer and another one for
|
||||||
|
the pointer to implicitely passed environment.
|
||||||
|
|
||||||
`syscall`:idx:
|
`syscall`:idx:
|
||||||
The syscall convention is the same as ``__syscall`` in C. It is used for
|
The syscall convention is the same as ``__syscall`` in C. It is used for
|
||||||
|
|
@ -1114,6 +1134,11 @@ of the following conditions hold:
|
||||||
The rules' purpose is to prevent the case that extending a non-``procvar``
|
The rules' purpose is to prevent the case that extending a non-``procvar``
|
||||||
procedure with default parameters breaks client code.
|
procedure with default parameters breaks client code.
|
||||||
|
|
||||||
|
The default calling convention is ``nimcall``, unless it is an inner proc (
|
||||||
|
a proc inside of a proc). For an inner proc an analysis is performed wether it
|
||||||
|
accesses its environment. If it does so, it has the calling convention
|
||||||
|
``closure``, otherwise it has the calling convention ``nimcall``.
|
||||||
|
|
||||||
|
|
||||||
Distinct type
|
Distinct type
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,9 @@ proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
|
||||||
## creates a new object of type ``T`` and returns a safe (traced)
|
## creates a new object of type ``T`` and returns a safe (traced)
|
||||||
## reference to it in ``a``.
|
## reference to it in ``a``.
|
||||||
|
|
||||||
|
proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
|
||||||
|
## leaked implementation detail. Do not use.
|
||||||
|
|
||||||
proc new*[T](a: var ref T, finalizer: proc (x: ref T)) {.
|
proc new*[T](a: var ref T, finalizer: proc (x: ref T)) {.
|
||||||
magic: "NewFinalize", noSideEffect.}
|
magic: "NewFinalize", noSideEffect.}
|
||||||
## creates a new object of type ``T`` and returns a safe (traced)
|
## creates a new object of type ``T`` and returns a safe (traced)
|
||||||
|
|
|
||||||
14
todo.txt
14
todo.txt
|
|
@ -2,6 +2,13 @@ version 0.8.14
|
||||||
==============
|
==============
|
||||||
|
|
||||||
- implement closures
|
- implement closures
|
||||||
|
- test evals.nim with closures
|
||||||
|
- deactivate lambda lifting for JS backend
|
||||||
|
- Test that iterators within closures work etc; test generics;
|
||||||
|
test recursion
|
||||||
|
- test constant closures
|
||||||
|
- 'closureEnv' magic for easy interfacing with C
|
||||||
|
|
||||||
- object {.pure, final.} does not work again!
|
- object {.pure, final.} does not work again!
|
||||||
- bug: tsortdev does not run with native GC?
|
- bug: tsortdev does not run with native GC?
|
||||||
- ``=`` should be overloadable; requires specialization for ``=``?
|
- ``=`` should be overloadable; requires specialization for ``=``?
|
||||||
|
|
@ -45,8 +52,7 @@ Bugs
|
||||||
result = forward(x)
|
result = forward(x)
|
||||||
|
|
||||||
- bug: stress testing basic method example (eval example)
|
- bug: stress testing basic method example (eval example)
|
||||||
without ``-d:release`` leaks memory; good way to figure out how a
|
without ``-d:release`` leaks memory?
|
||||||
fixed amount of stack can hold an arbitrary number of GC roots!
|
|
||||||
- bug: temp2.nim triggers weird compiler and except.nim bug
|
- bug: temp2.nim triggers weird compiler and except.nim bug
|
||||||
- bug: negative array indexes fail to index check
|
- bug: negative array indexes fail to index check
|
||||||
|
|
||||||
|
|
@ -131,6 +137,8 @@ Low priority
|
||||||
- warning for implicit openArray -> varargs conversion
|
- warning for implicit openArray -> varargs conversion
|
||||||
- implement explicit varargs; **but** ``len(varargs)`` problem remains!
|
- implement explicit varargs; **but** ``len(varargs)`` problem remains!
|
||||||
--> solve by implicit conversion from varargs to openarray
|
--> solve by implicit conversion from varargs to openarray
|
||||||
|
- implement closures that support nesting > 1
|
||||||
|
|
||||||
|
|
||||||
Version 2
|
Version 2
|
||||||
=========
|
=========
|
||||||
|
|
@ -166,6 +174,4 @@ Version 2
|
||||||
a full blown statement; a ``try`` expression might be a good idea to make
|
a full blown statement; a ``try`` expression might be a good idea to make
|
||||||
error handling more light-weight
|
error handling more light-weight
|
||||||
people also want ``inc a; inc b``
|
people also want ``inc a; inc b``
|
||||||
--> solved by providing an expr version of most control structures?
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
News
|
News
|
||||||
====
|
====
|
||||||
|
|
||||||
2012-XX-XX Version 0.8.14 released
|
2012-02-XX Version 0.8.14 released
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
Version 0.8.14 has been released! Get it `here <download.html>`_.
|
Version 0.8.14 has been released! Get it `here <download.html>`_.
|
||||||
|
|
@ -125,6 +125,8 @@ Compiler Additions
|
||||||
for ``on|off`` switches in pragmas. In order to not break existing code,
|
for ``on|off`` switches in pragmas. In order to not break existing code,
|
||||||
``on`` and ``off`` are now aliases for ``true`` and ``false`` and declared
|
``on`` and ``off`` are now aliases for ``true`` and ``false`` and declared
|
||||||
in the system module.
|
in the system module.
|
||||||
|
- The compiler finally supports **closures**. This is a preliminary
|
||||||
|
implementation, which does not yet support nestings deeper than 1 level.
|
||||||
|
|
||||||
|
|
||||||
Library Additions
|
Library Additions
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue