Merge branch 'master' of github.com:Araq/Nimrod into upstream

This commit is contained in:
Zahary Karadjov 2011-12-11 11:26:50 +02:00
commit 67bc23bb60
25 changed files with 485 additions and 200 deletions

182
compiler/aliases.nim Normal file
View file

@ -0,0 +1,182 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Simple alias analysis for the HLO and the code generators.
import
ast, astalgo, types, trees, intsets, msgs
type
TAnalysisResult* = enum
arNo, arMaybe, arYes
proc isPartOfAux(a, b: PType, marker: var TIntSet): TAnalysisResult
proc isPartOfAux(n: PNode, b: PType, marker: var TIntSet): TAnalysisResult =
result = arNo
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
result = isPartOfAux(n.sons[i], b, marker)
if result == arYes: return
of nkRecCase:
assert(n.sons[0].kind == nkSym)
result = isPartOfAux(n.sons[0], b, marker)
if result == arYes: return
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
result = isPartOfAux(lastSon(n.sons[i]), b, marker)
if result == arYes: return
else: internalError("isPartOfAux(record case branch)")
of nkSym:
result = isPartOfAux(n.sym.typ, b, marker)
else: internalError(n.info, "isPartOfAux()")
proc isPartOfAux(a, b: PType, marker: var TIntSet): TAnalysisResult =
result = arNo
if a == nil or b == nil: return
if ContainsOrIncl(marker, a.id): return
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
case a.kind
of tyObject:
result = isPartOfAux(a.sons[0], b, marker)
if result == arNo: result = isPartOfAux(a.n, b, marker)
of tyGenericInst, tyDistinct:
result = isPartOfAux(lastSon(a), b, marker)
of tyArray, tyArrayConstr, tySet, tyTuple:
for i in countup(0, sonsLen(a) - 1):
result = isPartOfAux(a.sons[i], b, marker)
if result == arYes: return
else: nil
proc isPartOf(a, b: PType): TAnalysisResult =
## checks iff 'a' can be part of 'b'. Iterates over VALUE types!
var marker = InitIntSet()
# watch out: parameters reversed because I'm too lazy to change the code...
result = isPartOfAux(b, a, marker)
proc isPartOf*(a, b: PNode): TAnalysisResult =
## checks if location `a` can be part of location `b`. We treat seqs and
## strings as pointers because the code gen often just passes them as such.
##
## Note: `a` can only be part of `b`, if `a`'s type can be part of `b`'s
## type. Since however type analysis is more expensive, we perform it only
## if necessary.
##
## cases:
##
## YES-cases:
## x <| x # for general trees
## x[] <| x
## x[i] <| x
## x.f <| x
##
## NO-cases:
## x !<| y # depending on type and symbol kind
## x[constA] !<| x[constB]
## x.f !<| x.g
## x.f !<| y.f iff x !<= y
##
## MAYBE-cases:
##
## x[] ?<| y[] iff compatible type
##
##
## x[] ?<| y depending on type
##
if a.kind == b.kind:
case a.kind
of nkSym:
const varKinds = {skVar, skTemp, skProc}
# same symbol: aliasing:
if a.sym.id == b.sym.id: result = arYes
elif a.sym.kind in varKinds or b.sym.kind in varKinds:
# actually, a param could alias a var but we know that cannot happen
# here. XXX make this more generic
result = arNo
else:
# use expensive type check:
if isPartOf(a.sym.typ, b.sym.typ) != arNo:
result = arMaybe
of nkBracketExpr:
result = isPartOf(a[0], b[0])
if len(a) >= 2 and len(b) >= 2:
# array accesses:
if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
# we know it's the same array and we have 2 constant indexes;
# if they are
var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1]
var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1]
if SameValue(x, y): result = arYes
else: result = arNo
# else: maybe and no are accurate
else:
# pointer derefs:
if result != arYes:
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
of nkDotExpr:
result = isPartOf(a[0], b[0])
if result != arNo:
# if the fields are different, it's not the same location
if a[1].sym.id != b[1].sym.id:
result = arNo
of nkHiddenDeref, nkDerefExpr:
result = isPartOf(a[0], b[0])
# weaken because of indirection:
if result != arYes:
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = isPartOf(a[1], b[1])
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
result = isPartOf(a[0], b[0])
else: nil
# Calls return a new location, so a default of ``arNo`` is fine.
else:
# go down recursively; this is quite demanding:
const
Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
nkCheckedFieldExpr}
Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv}
DerefKinds = {nkHiddenDeref, nkDerefExpr}
case b.kind
of Ix0Kinds:
# a* !<| b.f iff a* !<| b
result = isPartOf(a, b[0])
of DerefKinds:
# a* !<| b[] iff
if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a, b[0])
if result == arNo: result = arMaybe
of Ix1Kinds:
# a* !<| T(b) iff a* !<| b
result = isPartOf(a, b[1])
of nkSym:
# b is an atom, so we have to check a:
case a.kind
of Ix0Kinds:
# a.f !<| b* iff a.f !<| b*
result = isPartOf(a[0], b)
of Ix1Kinds:
result = isPartOf(a[1], b)
of DerefKinds:
if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a[0], b)
if result == arNo: result = arMaybe
else: nil
else: nil

View file

@ -371,7 +371,7 @@ type
mFields, mFieldPairs, mFields, mFieldPairs,
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
mAssert, mAstToStr, mRand, mIsPartOf, mAstToStr, mRand,
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
mNewString, mNewStringOfCap, mNewString, mNewStringOfCap,
mReset, mReset,
@ -669,36 +669,7 @@ const # for all kind of hash tables:
GrowthFactor* = 2 # must be power of 2, > 0 GrowthFactor* = 2 # must be power of 2, > 0
StartSize* = 8 # must be power of 2, > 0 StartSize* = 8 # must be power of 2, > 0
proc SameValue*(a, b: PNode): bool proc ValueToString*(a: PNode): string =
# a, b are literals
proc leValue*(a, b: PNode): bool
# a <= b? a, b are literals
proc ValueToString*(a: PNode): string
proc leValue(a, b: PNode): bool =
# a <= b?
result = false
case a.kind
of nkCharLit..nkInt64Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal
else: InternalError(a.info, "leValue")
proc SameValue(a, b: PNode): bool =
result = false
case a.kind
of nkCharLit..nkInt64Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal
else: InternalError(a.info, "SameValue")
proc ValueToString(a: PNode): string =
case a.kind case a.kind
of nkCharLit..nkInt64Lit: result = $(a.intVal) of nkCharLit..nkInt64Lit: result = $(a.intVal)
of nkFloatLit, nkFloat32Lit, nkFloat64Lit: result = $(a.floatVal) of nkFloatLit, nkFloat32Lit, nkFloat64Lit: result = $(a.floatVal)

View file

@ -146,6 +146,30 @@ proc IITablePut*(t: var TIITable, key, val: int)
# implementation # implementation
proc SameValue*(a, b: PNode): bool =
result = false
case a.kind
of nkCharLit..nkInt64Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal
else:
InternalError(a.info, "SameValue")
proc leValue*(a, b: PNode): bool =
# a <= b?
result = false
case a.kind
of nkCharLit..nkInt64Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal
else: InternalError(a.info, "leValue")
proc lookupInRecord(n: PNode, field: PIdent): PSym = proc lookupInRecord(n: PNode, field: PIdent): PSym =
result = nil result = nil
case n.kind case n.kind

View file

@ -10,16 +10,30 @@
type type
TAfterCallActions = tuple[p: BProc, actions: PRope] TAfterCallActions = tuple[p: BProc, actions: PRope]
proc fixupCall(p: BProc, t: PNode, d: var TLoc, pl: PRope) = proc leftAppearsOnRightSide(le, ri: PNode): bool =
if le != nil:
for i in 1 .. <ri.len:
if le.isPartOf(ri[i]) != arNo: return true
proc hasNoInit(call: PNode): bool {.inline.} =
result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags
proc resetLoc(p: BProc, d: var TLoc) =
zeroVar(p, d, containsGarbageCollectedRef(d.t))
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, pl: PRope) =
var pl = pl var pl = pl
var typ = t.sons[0].typ # getUniqueType() is too expensive here! var typ = ri.sons[0].typ # getUniqueType() is too expensive here!
if typ.sons[0] != nil: if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): if isInvalidReturnType(typ.sons[0]):
if sonsLen(t) > 1: app(pl, ", ") if sonsLen(ri) > 1: app(pl, ", ")
# beware of 'result = p(result)'. We always allocate a temporary: # beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone}: if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
# We already got a temp. Great, special case it: # Great, we can use 'd':
if d.k == locNone: getTemp(p, typ.sons[0], 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)) app(pl, addrLoc(d))
app(pl, ")") app(pl, ")")
app(p.s[cpsStmts], pl) app(p.s[cpsStmts], pl)
@ -117,40 +131,40 @@ proc genArgNoParam(aca: var TAfterCallActions, n: PNode): PRope =
initLocExpr(aca.p, n, a) initLocExpr(aca.p, n, a)
result = rdLoc(a) result = rdLoc(a)
proc genCall(p: BProc, t: PNode, d: var TLoc) = proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op: TLoc var op: TLoc
var aca: TAfterCallActions var aca: TAfterCallActions
aca.p = p aca.p = p
# this is a hotspot in the compiler # this is a hotspot in the compiler
initLocExpr(p, t.sons[0], op) initLocExpr(p, ri.sons[0], op)
var pl = con(op.r, "(") var pl = con(op.r, "(")
var typ = t.sons[0].typ # getUniqueType() is too expensive here! var typ = ri.sons[0].typ # getUniqueType() is too expensive here!
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
var length = sonsLen(t) var length = sonsLen(ri)
for i in countup(1, length - 1): for i in countup(1, length - 1):
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if i < sonsLen(typ): if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym) assert(typ.n.sons[i].kind == nkSym)
app(pl, genArg(aca, t.sons[i], typ.n.sons[i].sym)) app(pl, genArg(aca, ri.sons[i], typ.n.sons[i].sym))
else: else:
app(pl, genArgNoParam(aca, t.sons[i])) app(pl, genArgNoParam(aca, ri.sons[i]))
if i < length - 1: app(pl, ", ") if i < length - 1: app(pl, ", ")
fixupCall(p, t, d, pl) fixupCall(p, le, ri, d, pl)
emitAfterCallActions(aca) emitAfterCallActions(aca)
proc genInfixCall(p: BProc, t: PNode, d: var TLoc) = proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op, a: TLoc var op, a: TLoc
var aca: TAfterCallActions var aca: TAfterCallActions
aca.p = p aca.p = p
initLocExpr(p, t.sons[0], op) initLocExpr(p, ri.sons[0], op)
var pl: PRope = nil var pl: PRope = nil
var typ = t.sons[0].typ # getUniqueType() is too expensive here! var typ = ri.sons[0].typ # getUniqueType() is too expensive here!
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
var length = sonsLen(t) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
var param = typ.n.sons[1].sym var param = typ.n.sons[1].sym
app(pl, genArg(aca, t.sons[1], param)) app(pl, genArg(aca, ri.sons[1], param))
if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, "->") if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, "->")
else: app(pl, ".") else: app(pl, ".")
@ -160,45 +174,45 @@ proc genInfixCall(p: BProc, t: PNode, d: var TLoc) =
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if i < sonsLen(typ): if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym) assert(typ.n.sons[i].kind == nkSym)
app(pl, genArg(aca, t.sons[i], typ.n.sons[i].sym)) app(pl, genArg(aca, ri.sons[i], typ.n.sons[i].sym))
else: else:
app(pl, genArgNoParam(aca, t.sons[i])) app(pl, genArgNoParam(aca, ri.sons[i]))
if i < length - 1: app(pl, ", ") if i < length - 1: app(pl, ", ")
fixupCall(p, t, d, pl) fixupCall(p, le, ri, d, pl)
emitAfterCallActions(aca) emitAfterCallActions(aca)
proc genNamedParamCall(p: BProc, t: PNode, d: var TLoc) = proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# generates a crappy ObjC call # generates a crappy ObjC call
var op, a: TLoc var op, a: TLoc
var aca: TAfterCallActions var aca: TAfterCallActions
aca.p = p aca.p = p
initLocExpr(p, t.sons[0], op) initLocExpr(p, ri.sons[0], op)
var pl = toRope"[" var pl = toRope"["
var typ = t.sons[0].typ # getUniqueType() is too expensive here! var typ = ri.sons[0].typ # getUniqueType() is too expensive here!
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
var length = sonsLen(t) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if length > 1: if length > 1:
app(pl, genArg(aca, t.sons[1], typ.n.sons[1].sym)) app(pl, genArg(aca, ri.sons[1], typ.n.sons[1].sym))
app(pl, " ") app(pl, " ")
app(pl, op.r) app(pl, op.r)
if length > 2: if length > 2:
app(pl, ": ") app(pl, ": ")
app(pl, genArg(aca, t.sons[2], typ.n.sons[2].sym)) app(pl, genArg(aca, ri.sons[2], typ.n.sons[2].sym))
for i in countup(3, length-1): for i in countup(3, length-1):
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if i >= sonsLen(typ): if i >= sonsLen(typ):
InternalError(t.info, "varargs for objective C method?") InternalError(ri.info, "varargs for objective C method?")
assert(typ.n.sons[i].kind == nkSym) assert(typ.n.sons[i].kind == nkSym)
var param = typ.n.sons[i].sym var param = typ.n.sons[i].sym
app(pl, " ") app(pl, " ")
app(pl, param.name.s) app(pl, param.name.s)
app(pl, ": ") app(pl, ": ")
app(pl, genArg(aca, t.sons[i], param)) app(pl, genArg(aca, ri.sons[i], param))
if typ.sons[0] != nil: if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): if isInvalidReturnType(typ.sons[0]):
if sonsLen(t) > 1: app(pl, " ") if sonsLen(ri) > 1: app(pl, " ")
# beware of 'result = p(result)'. We always allocate a temporary: # beware of 'result = p(result)'. We always allocate a temporary:
if d.k in {locTemp, locNone}: if d.k in {locTemp, locNone}:
# We already got a temp. Great, special case it: # We already got a temp. Great, special case it:
@ -230,3 +244,21 @@ proc genNamedParamCall(p: BProc, t: PNode, d: var TLoc) =
appf(p.s[cpsStmts], ";$n") appf(p.s[cpsStmts], ";$n")
emitAfterCallActions(aca) emitAfterCallActions(aca)
proc genCall(p: BProc, e: PNode, d: var TLoc) =
if 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:
genNamedParamCall(p, e, d)
else:
genPrefixCall(p, nil, e, 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
ri.len >= 2:
genInfixCall(p, le, ri, d)
elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
genNamedParamCall(p, ri, d)
else:
genPrefixCall(p, le, ri, d)

View file

@ -1378,13 +1378,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
of mStrToStr: expr(p, e.sons[1], d) of mStrToStr: expr(p, e.sons[1], d)
of mEnumToStr: genRepr(p, e, d) of mEnumToStr: genRepr(p, e, d)
of mAssert:
if optAssert in p.Options:
expr(p, e.sons[1], d)
line = toRope(toLinenumber(e.info))
filen = makeCString(ToFilename(e.info))
appcg(p, cpsStmts, "#internalAssert($1, $2, $3);$n",
[filen, line, rdLoc(d)])
of mOf: genOf(p, e, d) of mOf: genOf(p, e, d)
of mNew: genNew(p, e) of mNew: genNew(p, e)
of mNewFinalize: genNewFinalize(p, e) of mNewFinalize: genNewFinalize(p, e)
@ -1635,11 +1628,6 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
nkCallStrLit: nkCallStrLit:
if e.sons[0].kind == nkSym and e.sons[0].sym.magic != mNone: if e.sons[0].kind == nkSym and e.sons[0].sym.magic != mNone:
genMagicExpr(p, e, d, e.sons[0].sym.magic) genMagicExpr(p, e, d, e.sons[0].sym.magic)
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and
e.len >= 2:
genInfixCall(p, e, d)
elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
genNamedParamCall(p, e, d)
else: else:
genCall(p, e, d) genCall(p, e, d)
of nkCurly: of nkCurly:

View file

@ -18,16 +18,16 @@ proc genVarTuple(p: BProc, n: PNode) =
if n.kind != nkVarTuple: InternalError(n.info, "genVarTuple") if n.kind != nkVarTuple: InternalError(n.info, "genVarTuple")
var L = sonsLen(n) var L = sonsLen(n)
genLineDir(p, n) genLineDir(p, n)
initLocExpr(p, n.sons[L - 1], tup) initLocExpr(p, n.sons[L-1], tup)
var t = tup.t var t = tup.t
for i in countup(0, L - 3): for i in countup(0, L-3):
var v = n.sons[i].sym var v = n.sons[i].sym
if sfGlobal in v.flags: if sfGlobal in v.flags:
assignGlobalVar(p, v) assignGlobalVar(p, v)
genObjectInit(p, cpsInit, v.typ, v.loc, true) genObjectInit(p, cpsInit, v.typ, v.loc, true)
else: else:
assignLocalVar(p, v) assignLocalVar(p, v)
initVariable(p, v) initLocalVar(p, v, immediateAsgn=true)
initLoc(field, locExpr, t.sons[i], tup.s) initLoc(field, locExpr, t.sons[i], tup.s)
if t.n == nil: if t.n == nil:
field.r = ropef("$1.Field$2", [rdLoc(tup), toRope(i)]) field.r = ropef("$1.Field$2", [rdLoc(tup), toRope(i)])
@ -37,17 +37,25 @@ proc genVarTuple(p: BProc, n: PNode) =
[rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)]) [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)])
putLocIntoDest(p, v.loc, field) putLocIntoDest(p, v.loc, field)
proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
if ri.kind in nkCallKinds and (ri.sons[0].kind != nkSym or
ri.sons[0].sym.magic == mNone):
genAsgnCall(p, le, ri, a)
else:
expr(p, ri, a)
proc genSingleVar(p: BProc, a: PNode) = proc genSingleVar(p: BProc, a: PNode) =
var v = a.sons[0].sym var v = a.sons[0].sym
var immediateAsgn = a.sons[2].kind != nkEmpty
if sfGlobal in v.flags: if sfGlobal in v.flags:
assignGlobalVar(p, v) assignGlobalVar(p, v)
genObjectInit(p, cpsInit, v.typ, v.loc, true) genObjectInit(p, cpsInit, v.typ, v.loc, true)
else: else:
assignLocalVar(p, v) assignLocalVar(p, v)
initVariable(p, v) initLocalVar(p, v, immediateAsgn)
if a.sons[2].kind != nkEmpty: if immediateAsgn:
genLineDir(p, a) genLineDir(p, a)
expr(p, a.sons[2], v.loc) loadInto(p, a.sons[0], a.sons[2], v.loc)
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):
@ -658,7 +666,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
InitLocExpr(p, e.sons[0], a) InitLocExpr(p, e.sons[0], a)
if fastAsgn: incl(a.flags, lfNoDeepCopy) if fastAsgn: incl(a.flags, lfNoDeepCopy)
assert(a.t != nil) assert(a.t != nil)
expr(p, e.sons[1], a) loadInto(p, e.sons[0], e.sons[1], a)
else: else:
asgnFieldDiscriminant(p, e) asgnFieldDiscriminant(p, e)

View file

@ -14,7 +14,7 @@ import
options, intsets, options, intsets,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
rodutils, renderer, idgen, cgendata, ccgmerge, semfold rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases
when options.hasTinyCBackend: when options.hasTinyCBackend:
import tccgen import tccgen
@ -239,12 +239,13 @@ proc zeroVar(p: BProc, loc: TLoc, containsGCref: bool) =
if containsGcref and p.WithInLoop > 0: if containsGcref and p.WithInLoop > 0:
appf(p.s[cpsInit], "memset((void*)$1, 0, sizeof($2));$n", appf(p.s[cpsInit], "memset((void*)$1, 0, sizeof($2));$n",
[addrLoc(loc), rdLoc(loc)]) [addrLoc(loc), rdLoc(loc)])
genObjectInit(p, cpsInit, loc.t, loc, true)
appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n", appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
[addrLoc(loc), genTypeInfo(p.module, loc.t)]) [addrLoc(loc), genTypeInfo(p.module, loc.t)])
else: else:
appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n", appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n",
[addrLoc(loc), rdLoc(loc)]) [addrLoc(loc), rdLoc(loc)])
genObjectInit(p, cpsInit, 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 if skipTypes(loc.t, abstractVarRange).Kind notin
@ -259,15 +260,22 @@ proc zeroTemp(p: BProc, loc: TLoc) =
else: else:
appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n", appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n",
[addrLoc(loc), rdLoc(loc)]) [addrLoc(loc), rdLoc(loc)])
# XXX no object init necessary for temporaries?
when false: when false:
appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n", appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
[addrLoc(loc), genTypeInfo(p.module, loc.t)]) [addrLoc(loc), genTypeInfo(p.module, loc.t)])
proc initVariable(p: BProc, v: PSym) = proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
if sfNoInit notin v.flags: if sfNoInit notin v.flags:
var b = containsGarbageCollectedRef(v.typ) # we know it is a local variable and thus on the stack!
if b or v.ast == nil: # If ``not immediateAsgn`` it is not initialized in a binding like
zeroVar(p, v.loc, b) # ``var v = X`` and thus we need to init it.
# If ``v`` contains a GC-ref we may pass it to ``unsureAsgnRef`` somehow
# which requires initialization. However this can really only happen if
# ``var v = X()`` gets transformed into ``X(&v)``.
# Nowadays the logic in ccgcalls deals with this case however.
if not immediateAsgn:
zeroVar(p, v.loc, containsGarbageCollectedRef(v.typ))
proc initTemp(p: BProc, tmp: var TLoc) = proc initTemp(p: BProc, tmp: var TLoc) =
if containsGarbageCollectedRef(tmp.t) or isInvalidReturnType(tmp.t): if containsGarbageCollectedRef(tmp.t) or isInvalidReturnType(tmp.t):
@ -535,11 +543,12 @@ proc genProcAux(m: BModule, prc: PSym) =
if sfPure notin prc.flags and prc.typ.sons[0] != nil: if sfPure notin prc.flags and prc.typ.sons[0] != nil:
var res = prc.ast.sons[resultPos].sym # get result symbol var res = prc.ast.sons[resultPos].sym # get result symbol
if not isInvalidReturnType(prc.typ.sons[0]): if not isInvalidReturnType(prc.typ.sons[0]):
if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
# declare the result symbol: # declare the result symbol:
assignLocalVar(p, res) assignLocalVar(p, res)
assert(res.loc.r != nil) assert(res.loc.r != nil)
returnStmt = ropeff("return $1;$n", "ret $1$n", [rdLoc(res.loc)]) returnStmt = ropeff("return $1;$n", "ret $1$n", [rdLoc(res.loc)])
initVariable(p, res) initLocalVar(p, res, immediateAsgn=false)
else: else:
fillResult(res) fillResult(res)
assignParam(p, res) assignParam(p, res)

View file

@ -720,7 +720,8 @@ proc generateHeader(p: var TProc, typ: PType): PRope =
const const
nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString, nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString,
nkCStringToString, nkCall, nkCommand, nkHiddenCallConv, nkCallStrLit} nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix,
nkCommand, nkHiddenCallConv, nkCallStrLit}
proc needsNoCopy(y: PNode): bool = proc needsNoCopy(y: PNode): bool =
result = (y.kind in nodeKindsNeedNoCopy) or result = (y.kind in nodeKindsNeedNoCopy) or
@ -1131,14 +1132,6 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
of mIsNil: unaryExpr(p, n, r, "", "$1 == null") of mIsNil: unaryExpr(p, n, r, "", "$1 == null")
of mEnumToStr: genRepr(p, n, r) of mEnumToStr: genRepr(p, n, r)
of mAssert:
if (optAssert in p.Options):
useMagic(p, "internalAssert")
gen(p, n.sons[1], a)
line = toRope(toLinenumber(n.info))
filen = makeCString(ToFilename(n.info))
appf(r.com, "if (!($3)) internalAssert($1, $2)",
[filen, line, mergeExpr(a)])
of mNew, mNewFinalize: genNew(p, n, r) of mNew, mNewFinalize: genNew(p, n, r)
of mSizeOf: r.res = toRope(getSize(n.sons[1].typ)) of mSizeOf: r.res = toRope(getSize(n.sons[1].typ))
of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do
@ -1402,7 +1395,7 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) =
else: r.res = toRope(f.ToStrMaxPrecision) else: r.res = toRope(f.ToStrMaxPrecision)
of nkBlockExpr: genBlock(p, n, r) of nkBlockExpr: genBlock(p, n, r)
of nkIfExpr: genIfExpr(p, n, r) of nkIfExpr: genIfExpr(p, n, r)
of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit: of nkCallKinds:
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
genMagic(p, n, r) genMagic(p, n, r)
else: else:

View file

@ -696,12 +696,6 @@ proc evalNewSeq(c: PEvalContext, n: PNode): PNode =
a.sons[i] = getNullValue(t.sons[0], n.info) a.sons[i] = getNullValue(t.sons[0], n.info)
result = emptyNode result = emptyNode
proc evalAssert(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n.sons[1], {})
if isSpecial(result): return
if getOrdValue(result) != 0: result = emptyNode
else: stackTrace(c, n, errAssertionFailed)
proc evalIncl(c: PEvalContext, n: PNode): PNode = proc evalIncl(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n.sons[1], {efLValue}) result = evalAux(c, n.sons[1], {efLValue})
if isSpecial(result): return if isSpecial(result): return
@ -876,7 +870,6 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
of mOf: result = evalOf(c, n) of mOf: result = evalOf(c, n)
of mSizeOf: result = raiseCannotEval(c, n) of mSizeOf: result = raiseCannotEval(c, n)
of mHigh: result = evalHigh(c, n) of mHigh: result = evalHigh(c, n)
of mAssert: result = evalAssert(c, n)
of mExit: result = evalExit(c, n) of mExit: result = evalExit(c, n)
of mNew, mNewFinalize: result = evalNew(c, n) of mNew, mNewFinalize: result = evalNew(c, n)
of mNewSeq: result = evalNewSeq(c, n) of mNewSeq: result = evalNewSeq(c, n)

View file

@ -23,7 +23,7 @@ const
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wNoStackFrame, wError, wDiscardable} wNoStackFrame, wError, wDiscardable, wNoInit}
converterPragmas* = procPragmas converterPragmas* = procPragmas
methodPragmas* = procPragmas methodPragmas* = procPragmas
macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,

View file

@ -14,7 +14,7 @@ import
wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math, wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
magicsys, parser, nversion, semdata, nimsets, semfold, importer, magicsys, parser, nversion, semdata, nimsets, semfold, importer,
procfind, lookups, rodread, pragmas, passes, semtypinst, sigmatch, suggest, procfind, lookups, rodread, pragmas, passes, semtypinst, sigmatch, suggest,
semthreads, intsets, transf, evals, idgen semthreads, intsets, transf, evals, idgen, aliases
proc semPass*(): TPass proc semPass*(): TPass
# implementation # implementation

View file

@ -453,6 +453,14 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
skipTypes(t.sons[i], abstractInst).kind == tyVar: skipTypes(t.sons[i], abstractInst).kind == tyVar:
n.sons[i] = analyseIfAddressTaken(c, n.sons[i]) n.sons[i] = analyseIfAddressTaken(c, n.sons[i])
proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
result = c.semAndEvalConstExpr(n.sons[i+1])
if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
GlobalError(result.info, errStringLiteralExpected)
include semmagic
proc semDirectCallAnalyseEffects(c: PContext, n: PNode, proc semDirectCallAnalyseEffects(c: PContext, n: PNode,
flags: TExprFlags): PNode = flags: TExprFlags): PNode =
if efWantIterator in flags: if efWantIterator in flags:
@ -520,6 +528,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
renderTree(n, {renderNoComments})) renderTree(n, {renderNoComments}))
fixAbstractType(c, result) fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result) analyseIfAddressTakenInCall(c, result)
if result.sons[0].kind == nkSym and result.sons[0].sym.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags)
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# this seems to be a hotspot in the compiler! # this seems to be a hotspot in the compiler!
@ -530,6 +540,8 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
if result == nil: GlobalError(n.Info, errGenerated, getNotFoundError(c, n)) if result == nil: GlobalError(n.Info, errGenerated, getNotFoundError(c, n))
fixAbstractType(c, result) fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result) analyseIfAddressTakenInCall(c, result)
if result.sons[0].sym.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags)
proc buildStringify(c: PContext, arg: PNode): PNode = proc buildStringify(c: PContext, arg: PNode): PNode =
if arg.typ != nil and skipTypes(arg.typ, abstractInst).kind == tyString: if arg.typ != nil and skipTypes(arg.typ, abstractInst).kind == tyString:
@ -914,12 +926,6 @@ proc setMs(n: PNode, s: PSym): PNode =
n.sons[0] = newSymNode(s) n.sons[0] = newSymNode(s)
n.sons[0].info = n.info n.sons[0].info = n.info
proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
result = c.semAndEvalConstExpr(n.sons[i+1])
if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
GlobalError(result.info, errStringLiteralExpected)
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym = proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
## The argument to the proc should be nkCall(...) or similar ## The argument to the proc should be nkCall(...) or similar
## Returns the macro/template symbol ## Returns the macro/template symbol
@ -954,21 +960,6 @@ proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
else: else:
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode =
if sonsLen(n) == 2:
var a = expectStringArg(c, n, 0)
try:
var filename = a.strVal.FindFile
var content = readFile(filename)
result = newStrNode(nkStrLit, content)
result.typ = getSysType(tyString)
result.info = n.info
c.slurpedFiles.add(filename)
except EIO:
GlobalError(a.info, errCannotOpenFile, a.strVal)
else:
result = semDirectOp(c, n, flags)
proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
# this is a hotspot in the compiler! # this is a hotspot in the compiler!
result = n result = n
@ -991,14 +982,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
result = semAsgn(c, result) result = semAsgn(c, result)
else: else:
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
of mSlurp: result = semSlurp(c, n, flags)
of mExpandToAst: result = semExpandToAst(c, n, s, flags) of mExpandToAst: result = semExpandToAst(c, n, s, flags)
of mAstToStr:
if sonsLen(n) == 2:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
result.typ = getSysType(tyString)
else:
result = semDirectOp(c, n, flags)
else: result = semDirectOp(c, n, flags) else: result = semDirectOp(c, n, flags)
proc semIfExpr(c: PContext, n: PNode): PNode = proc semIfExpr(c: PContext, n: PNode): PNode =

View file

@ -204,7 +204,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
result = newIntNodeT(Ord( result = newIntNodeT(Ord(
testCompileOptionArg(getStr(a), getStr(b), n.info)), n) testCompileOptionArg(getStr(a), getStr(b), n.info)), n)
of mNewString, mNewStringOfCap, of mNewString, mNewStringOfCap,
mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mAppendStrCh, mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mParseExprToAst, mParseStmtToAst, mExpandToAst,
mNLen..mNError, mEqRef: mNLen..mNError, mEqRef:

38
compiler/semmagic.nim Normal file
View file

@ -0,0 +1,38 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This include file implements the semantic checking for magics.
proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
var r = isPartOf(n[1], n[2])
result = newIntNodeT(ord(r), n)
proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode =
assert sonsLen(n) == 2
var a = expectStringArg(c, n, 0)
try:
var filename = a.strVal.FindFile
var content = readFile(filename)
result = newStrNode(nkStrLit, content)
result.typ = getSysType(tyString)
result.info = n.info
c.slurpedFiles.add(filename)
except EIO:
GlobalError(a.info, errCannotOpenFile, a.strVal)
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
flags: TExprFlags): PNode =
case n[0].sym.magic
of mSlurp: result = semSlurp(c, n, flags)
of mIsPartOf: result = semIsPartOf(c, n, flags)
of mAstToStr:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
result.typ = getSysType(tyString)
else: result = n

View file

@ -1024,7 +1024,6 @@ proc computeSize(typ: PType): biggestInt =
proc getReturnType*(s: PSym): PType = proc getReturnType*(s: PSym): PType =
# Obtains the return type of a iterator/proc/macro/template # Obtains the return type of a iterator/proc/macro/template
assert s.kind in {skProc, skTemplate, skMacro, skIterator} assert s.kind in {skProc, skTemplate, skMacro, skIterator}
# XXX ask Zahary if this change broke something
result = s.typ.sons[0] result = s.typ.sons[0]
proc getSize(typ: PType): biggestInt = proc getSize(typ: PType): biggestInt =

View file

@ -1584,6 +1584,11 @@ The implicit initialization can be avoided for optimization reasons with the
var var
a {.noInit.}: array [0..1023, char] a {.noInit.}: array [0..1023, char]
If a proc is annotated with the ``noinit`` pragma this refers to its implicit
``result`` variable:
.. code-block:: nimrod
proc returnUndefinedValue: int {.noinit.} = nil
let statement let statement
@ -2849,6 +2854,7 @@ exported.
The algorithm for compiling modules is: The algorithm for compiling modules is:
- compile the whole module as usual, following import statements recursively - compile the whole module as usual, following import statements recursively
- if there is a cycle only import the already parsed symbols (that are - if there is a cycle only import the already parsed symbols (that are
exported); if an unknown identifier occurs then abort exported); if an unknown identifier occurs then abort

View file

@ -11,6 +11,8 @@
## a channel as its inbox. This module requires the ``--threads:on`` ## a channel as its inbox. This module requires the ``--threads:on``
## command line switch. ## command line switch.
from os import sleep
type type
TTask*[TIn, TOut] = object{.pure, final.} TTask*[TIn, TOut] = object{.pure, final.}
when TOut isnot void: when TOut isnot void:
@ -29,7 +31,7 @@ type
proc spawn*[TIn, TOut](action: proc( proc spawn*[TIn, TOut](action: proc(
self: PActor[TIn, TOut]){.thread.}): PActor[TIn, TOut] = self: PActor[TIn, TOut]){.thread.}): PActor[TIn, TOut] =
## creates an actor; that is a thread with an inbox. The caller MUST call ## creates an actor; that is a thread with an inbox. The caller MUST call
## ``join`` because that also frees the associated resources with the actor. ## ``join`` because that also frees the actor's associated resources.
result = cast[PActor[TIn, TOut]](allocShared0(sizeof(result[]))) result = cast[PActor[TIn, TOut]](allocShared0(sizeof(result[])))
open(result.i) open(result.i)
createThread(result.t, action, result) createThread(result.t, action, result)
@ -42,6 +44,10 @@ proc running*[TIn, TOut](a: PActor[TIn, TOut]) =
## returns true if the actor `a` is running. ## returns true if the actor `a` is running.
result = running(a.t) result = running(a.t)
proc ready*[TIn, TOut](a: PActor[TIn, TOut]): bool =
## returns true if the actor `a` is ready to process new messages.
result = ready(a.i)
proc join*[TIn, TOut](a: PActor[TIn, TOut]) = proc join*[TIn, TOut](a: PActor[TIn, TOut]) =
## joins an actor. ## joins an actor.
joinThread(a.t) joinThread(a.t)
@ -111,17 +117,35 @@ proc createActorPool*[TIn, TOut](a: var TActorPool[TIn, TOut], poolSize = 4) =
for i in 0 .. < a.actors.len: for i in 0 .. < a.actors.len:
a.actors[i] = spawn(poolWorker[TIn, TOut]) a.actors[i] = spawn(poolWorker[TIn, TOut])
proc join*[TIn, TOut](a: var TActorPool[TIn, TOut]) = proc sync*[TIn, TOut](a: var TActorPool[TIn, TOut], polling=50) =
## waits for each actor in the actor pool `a` to finish and frees the ## waits for every actor of `a` to finish with its work. Currently this is
## implemented as polling every `polling` ms. This will change in a later
## version, however.
while true:
var wait = false
for i in 0..high(a.actors):
if not a.actors[i].i.ready:
wait = true
break
if not wait: break
sleep(polling)
proc terminate*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
## terminates each actor in the actor pool `a` and frees the
## resources attached to `a`. ## resources attached to `a`.
var t: TTask[TIn, TOut] var t: TTask[TIn, TOut]
t.shutdown = true t.shutdown = true
for i in 0 .. < a.actors.len: send(a.actors[i].i, t) for i in 0.. <a.actors.len: send(a.actors[i].i, t)
for i in 0 .. < a.actors.len: join(a.actors[i]) for i in 0.. <a.actors.len: join(a.actors[i])
when TOut isnot void: when TOut isnot void:
close(a.outputs) close(a.outputs)
a.actors = nil a.actors = nil
proc join*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
## short-cut for `sync` and then `terminate`.
sync(a)
terminate(a)
template setupTask = template setupTask =
t.action = action t.action = action
shallowCopy(t.data, input) shallowCopy(t.data, input)
@ -135,7 +159,7 @@ template schedule =
return return
# no thread ready :-( --> send message to the thread which has the least # no thread ready :-( --> send message to the thread which has the least
# messages pending: # messages pending:
var minIdx = 0 var minIdx = -1
var minVal = high(int) var minVal = high(int)
for i in 0..high(p.actors): for i in 0..high(p.actors):
var curr = p.actors[i].i.peek var curr = p.actors[i].i.peek
@ -143,10 +167,13 @@ template schedule =
# ok, is ready now: # ok, is ready now:
p.actors[i].i.send(t) p.actors[i].i.send(t)
return return
if curr < minVal: if curr < minVal and curr >= 0:
minVal = curr minVal = curr
minIdx = i minIdx = i
if minIdx >= 0:
p.actors[minIdx].i.send(t) p.actors[minIdx].i.send(t)
else:
raise newException(EDeadThread, "cannot send message; thread died")
proc spawn*[TIn, TOut](p: var TActorPool[TIn, TOut], input: TIn, proc spawn*[TIn, TOut](p: var TActorPool[TIn, TOut], input: TIn,
action: proc (input: TIn): TOut {.thread.} action: proc (input: TIn): TOut {.thread.}

View file

@ -2071,14 +2071,24 @@ proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
## converts the AST of `x` into a string representation. This is very useful ## converts the AST of `x` into a string representation. This is very useful
## for debugging. ## for debugging.
proc raiseAssert(msg: string) {.noinline.} =
raise newException(EAssertionFailed, msg)
template assert*(cond: expr, msg = "") = template assert*(cond: expr, msg = "") =
## provides a means to implement `programming by contracts`:idx: in Nimrod. ## provides a means to implement `programming by contracts`:idx: in Nimrod.
## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it ## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it
## raises an ``EAssertionFailure`` exception. However, the compiler may ## raises an ``EAssertionFailure`` exception. However, the compiler may
## not generate any code at all for ``assert`` if it is advised to do so. ## not generate any code at all for ``assert`` if it is advised to do so.
## Use ``assert`` for debugging purposes only. ## Use ``assert`` for debugging purposes only.
bind raiseAssert
when compileOption("assertions"): when compileOption("assertions"):
if not cond: if not cond:
raise newException(EAssertionFailed, astToStr(cond) & ' ' & msg) raiseAssert(astToStr(cond) & ' ' & msg)
template doAssert*(cond: expr, msg = "") =
## same as `assert' but is always turned on and not affected by the
## ``--assertions`` command line switch.
bind raiseAssert
if not cond:
raiseAssert(astToStr(cond) & ' ' & msg)

View file

@ -222,11 +222,14 @@ proc recv*[TMsg](c: var TChannel[TMsg]): TMsg =
llRecv(q, addr(result), cast[PNimType](getTypeInfo(result))) llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
proc peek*[TMsg](c: var TChannel[TMsg]): int = proc peek*[TMsg](c: var TChannel[TMsg]): int =
## returns the current number of messages in the channel `c`. ## returns the current number of messages in the channel `c`. Returns -1
## if the channel has been closed.
var q = cast[PRawChannel](addr(c)) var q = cast[PRawChannel](addr(c))
if q.mask != ChannelDeadMask: if q.mask != ChannelDeadMask:
lockChannel(q): lockChannel(q):
result = q.count result = q.count
else:
result = -1
proc open*[TMsg](c: var TChannel[TMsg]) = proc open*[TMsg](c: var TChannel[TMsg]) =
## opens a channel `c` for inter thread communication. ## opens a channel `c` for inter thread communication.

View file

@ -1669,25 +1669,25 @@ proc escapePeg*(s: string): string =
if inQuote: result.add('\'') if inQuote: result.add('\'')
when isMainModule: when isMainModule:
assert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27" doAssert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27"
#assert match("(a b c)", peg"'(' @ ')'") #doAssert match("(a b c)", peg"'(' @ ')'")
assert match("W_HI_Le", peg"\y 'while'") doAssert match("W_HI_Le", peg"\y 'while'")
assert(not match("W_HI_L", peg"\y 'while'")) doAssert(not match("W_HI_L", peg"\y 'while'"))
assert(not match("W_HI_Le", peg"\y v'while'")) doAssert(not match("W_HI_Le", peg"\y v'while'"))
assert match("W_HI_Le", peg"y'while'") doAssert match("W_HI_Le", peg"y'while'")
assert($ +digits == $peg"\d+") doAssert($ +digits == $peg"\d+")
assert "0158787".match(peg"\d+") doAssert "0158787".match(peg"\d+")
assert "ABC 0232".match(peg"\w+\s+\d+") doAssert "ABC 0232".match(peg"\w+\s+\d+")
assert "ABC".match(peg"\d+ / \w+") doAssert "ABC".match(peg"\d+ / \w+")
for word in split("00232this02939is39an22example111", peg"\d+"): for word in split("00232this02939is39an22example111", peg"\d+"):
writeln(stdout, word) writeln(stdout, word)
assert matchLen("key", ident) == 3 doAssert matchLen("key", ident) == 3
var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident) var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident)
assert matchLen("key1= cal9", pattern) == 11 doAssert matchLen("key1= cal9", pattern) == 11
var ws = newNonTerminal("ws", 1, 1) var ws = newNonTerminal("ws", 1, 1)
ws.rule = *whitespace ws.rule = *whitespace
@ -1698,24 +1698,24 @@ when isMainModule:
var c: TCaptures var c: TCaptures
var s = "a+b + c +d+e+f" var s = "a+b + c +d+e+f"
assert rawMatch(s, expr.rule, 0, c) == len(s) doAssert rawMatch(s, expr.rule, 0, c) == len(s)
var a = "" var a = ""
for i in 0..c.ml-1: for i in 0..c.ml-1:
a.add(substr(s, c.matches[i][0], c.matches[i][1])) a.add(substr(s, c.matches[i][0], c.matches[i][1]))
assert a == "abcdef" doAssert a == "abcdef"
#echo expr.rule #echo expr.rule
#const filename = "lib/devel/peg/grammar.txt" #const filename = "lib/devel/peg/grammar.txt"
#var grammar = parsePeg(newFileStream(filename, fmRead), filename) #var grammar = parsePeg(newFileStream(filename, fmRead), filename)
#echo "a <- [abc]*?".match(grammar) #echo "a <- [abc]*?".match(grammar)
assert find("_____abc_______", term("abc"), 2) == 5 doAssert find("_____abc_______", term("abc"), 2) == 5
assert match("_______ana", peg"A <- 'ana' / . A") doAssert match("_______ana", peg"A <- 'ana' / . A")
assert match("abcs%%%", peg"A <- ..A / .A / '%'") doAssert match("abcs%%%", peg"A <- ..A / .A / '%'")
if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}": if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}":
assert matches[0] == "abc" doAssert matches[0] == "abc"
else: else:
assert false doAssert false
var g2 = peg"""S <- A B / C D var g2 = peg"""S <- A B / C D
A <- 'a'+ A <- 'a'+
@ -1723,44 +1723,44 @@ when isMainModule:
C <- 'c'+ C <- 'c'+
D <- 'd'+ D <- 'd'+
""" """
assert($g2 == "((A B) / (C D))") doAssert($g2 == "((A B) / (C D))")
assert match("cccccdddddd", g2) doAssert match("cccccdddddd", g2)
assert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") == doAssert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
"var1<-keykey; var2<-key2key2") "var1<-keykey; var2<-key2key2")
assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}") doAssert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
if "aaaaaa" =~ peg"'aa' !. / ({'a'})+": if "aaaaaa" =~ peg"'aa' !. / ({'a'})+":
assert matches[0] == "a" doAssert matches[0] == "a"
else: else:
assert false doAssert false
var matches: array[0..5, string] var matches: array[0..5, string]
if match("abcdefg", peg"c {d} ef {g}", matches, 2): if match("abcdefg", peg"c {d} ef {g}", matches, 2):
assert matches[0] == "d" doAssert matches[0] == "d"
assert matches[1] == "g" doAssert matches[1] == "g"
else: else:
assert false doAssert false
for x in findAll("abcdef", peg"{.}", 3): for x in findAll("abcdef", peg"{.}", 3):
echo x echo x
if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')": if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')":
assert matches[0] == "f" doAssert matches[0] == "f"
assert matches[1] == "a, b" doAssert matches[1] == "a, b"
else: else:
assert false doAssert false
assert match("eine übersicht und außerdem", peg"(\letter \white*)+") doAssert match("eine übersicht und außerdem", peg"(\letter \white*)+")
# ß is not a lower cased letter?! # ß is not a lower cased letter?!
assert match("eine übersicht und auerdem", peg"(\lower \white*)+") doAssert match("eine übersicht und auerdem", peg"(\lower \white*)+")
assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+") doAssert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
assert(not match("456678", peg"(\letter)+")) doAssert(not match("456678", peg"(\letter)+"))
assert("var1 = key; var2 = key2".replacef( doAssert("var1 = key; var2 = key2".replacef(
peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") == peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") ==
"var1<-keykey;var2<-key2key2") "var1<-keykey;var2<-key2key2")
assert match("prefix/start", peg"^start$", 7) doAssert match("prefix/start", peg"^start$", 7)
# tricky test to check for false aliasing: # tricky test to check for false aliasing:
block: block:

View file

@ -5,7 +5,6 @@ discard """
template myAssert(cond: expr) = template myAssert(cond: expr) =
when rand(3) < 2: when rand(3) < 2:
let c = cond.astToStr let c = cond.astToStr
{.warning: "code activated: " & c.}
if not cond: if not cond:
echo c, "ugh" echo c, "ugh"

View file

@ -118,6 +118,7 @@ proc runThreadTests(r: var TResults, options: string) =
test "tactors" test "tactors"
test "threadex" test "threadex"
test "trecursive_actor"
#test "threadring" #test "threadring"
#test "tthreadanalysis" #test "tthreadanalysis"
#test "tthreadsort" #test "tthreadsort"

View file

@ -0,0 +1,19 @@
discard """
outputsub: "0"
"""
import actors
var
a: TActorPool[int, void]
createActorPool(a)
proc task(i: int) {.thread.} =
echo i
if i != 0: a.spawn (i-1, task)
# count from 9 till 0 and check 0 is somewhere in the output
a.spawn(9, task)
a.join()

View file

@ -1,7 +1,9 @@
version 0.8.14 version 0.8.14
============== ==============
- find a proper bugfix for C compilers optimizing away stack roots - implement a proper bugfix for C compilers optimizing away stack roots
- cleanup file path handling in the compiler
- warning for implicit openArray -> varargs convention - warning for implicit openArray -> varargs convention
- 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
@ -36,9 +38,6 @@ version 0.9.0
Bugs Bugs
---- ----
- bug: generic assign still buggy - bug: generic assign still buggy
- Optimization: If we use a temporary for the result anyway the code gen
should make use of this fact to generate better code...
- bug: memset() without type field initialization?
- special case the generic assign that needs to care about case objects - special case the generic assign that needs to care about case objects
- bug: returning a tyVar does not mean it is save to return it: - bug: returning a tyVar does not mean it is save to return it:
@ -124,7 +123,6 @@ Low priority
need to recompile clients; er ... what about templates, macros or anything need to recompile clients; er ... what about templates, macros or anything
that has inlining semantics? that has inlining semantics?
- codegen should use "NIM_CAST" macro and respect aliasing rules for GCC - codegen should use "NIM_CAST" macro and respect aliasing rules for GCC
- remove unused mAssert magic
Version 2 Version 2
========= =========

View file

@ -53,8 +53,8 @@ Changes affecting backwards compatibility
because they should not be used directly anymore. because they should not be used directly anymore.
Wrapper procs have been created that should be used instead. Wrapper procs have been created that should be used instead.
- ``export`` is now a keyword. - ``export`` is now a keyword.
- ``assert`` is now implemented in pure Nimrod; it's easy to implement your - ``assert`` is now implemented in pure Nimrod as a template; it's easy
own assertion schemes now. to implement your own assertion templates with ``system.astToStr``.
Language Additions Language Additions
@ -112,6 +112,7 @@ Compiler Additions
- Added ``--import:file`` and ``--include:file`` configuration options - Added ``--import:file`` and ``--include:file`` configuration options
for specifying modules that will be automatically imported/incluced. for specifying modules that will be automatically imported/incluced.
- ``nimrod i`` can now optionally be given a module to execute. - ``nimrod i`` can now optionally be given a module to execute.
- The compiler now performs a simple aliases analysis to generate better code.
Library Additions Library Additions
@ -142,7 +143,7 @@ Library Additions
and ``exec`` on Posix systems. Define the symbol ``useFork`` to revert to and ``exec`` on Posix systems. Define the symbol ``useFork`` to revert to
the old implementation. the old implementation.
- Added ``intsets.assign``. - Added ``intsets.assign``.
- Added ``system.astToStr`` and ``system.rand``. - Added ``system.astToStr`` and ``system.rand``, ``system.doAssert``.
2011-07-10 Version 0.8.12 released 2011-07-10 Version 0.8.12 released