progress on deepCopy

This commit is contained in:
Araq 2014-08-01 23:40:48 +02:00
commit 9673e4f2df
19 changed files with 419 additions and 135 deletions

View file

@ -263,7 +263,7 @@ type
sfNamedParamCall, # symbol needs named parameter call syntax in target sfNamedParamCall, # symbol needs named parameter call syntax in target
# language; for interfacing with Objective C # language; for interfacing with Objective C
sfDiscardable, # returned value may be discarded implicitly sfDiscardable, # returned value may be discarded implicitly
sfDestructor, # proc is destructor sfOverriden, # proc is overriden
sfGenSym # symbol is 'gensym'ed; do not add to symbol table sfGenSym # symbol is 'gensym'ed; do not add to symbol table
TSymFlags* = set[TSymFlag] TSymFlags* = set[TSymFlag]
@ -785,12 +785,13 @@ type
# the body of the user-defined type class # the body of the user-defined type class
# formal param list # formal param list
# else: unused # else: unused
destructor*: PSym # destructor. warning: nil here may not necessary
# mean that there is no destructor.
# see instantiateDestructor in types.nim
owner*: PSym # the 'owner' of the type owner*: PSym # the 'owner' of the type
sym*: PSym # types have the sym associated with them sym*: PSym # types have the sym associated with them
# it is used for converting types to strings # it is used for converting types to strings
destructor*: PSym # destructor. warning: nil here may not necessary
# mean that there is no destructor.
# see instantiateDestructor in semdestruct.nim
deepCopy*: PSym # overriden 'deepCopy' operation
size*: BiggestInt # the size of the type in bytes size*: BiggestInt # the size of the type in bytes
# -1 means that the size is unkwown # -1 means that the size is unkwown
align*: int # the type's alignment requirements align*: int # the type's alignment requirements
@ -1190,6 +1191,7 @@ proc assignType(dest, src: PType) =
dest.size = src.size dest.size = src.size
dest.align = src.align dest.align = src.align
dest.destructor = src.destructor dest.destructor = src.destructor
dest.deepCopy = src.deepCopy
# this fixes 'type TLock = TSysLock': # this fixes 'type TLock = TSysLock':
if src.sym != nil: if src.sym != nil:
if dest.sym != nil: if dest.sym != nil:

View file

@ -359,6 +359,32 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
else: internalError("genAssignment: " & $ty.kind) else: internalError("genAssignment: " & $ty.kind)
proc genDeepCopy(p: BProc; dest, src: TLoc) =
var ty = skipTypes(dest.t, abstractRange)
case ty.kind
of tyPtr, tyRef, tyString, tyProc, tyTuple, tyObject, tyArray, tyArrayConstr:
# XXX optimize this
linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n",
addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t))
of tySequence:
linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n",
addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t))
of tyOpenArray, tyVarargs:
linefmt(p, cpsStmts,
"#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len0, $3);$n",
addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t))
of tySet:
if mapType(ty) == ctArray:
useStringh(p.module)
linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
rdLoc(dest), rdLoc(src), toRope(getSize(dest.t)))
else:
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
of tyPointer, tyChar, tyBool, tyEnum, tyCString,
tyInt..tyUInt64, tyRange, tyVar:
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
else: internalError("genDeepCopy: " & $ty.kind)
proc getDestLoc(p: BProc, d: var TLoc, typ: PType) = proc getDestLoc(p: BProc, d: var TLoc, typ: PType) =
if d.k == locNone: getTemp(p, typ, d) if d.k == locNone: getTemp(p, typ, d)
@ -1578,25 +1604,25 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mGetTypeInfo: genGetTypeInfo(p, e, d) of mGetTypeInfo: genGetTypeInfo(p, e, d)
of mSwap: genSwap(p, e, d) of mSwap: genSwap(p, e, d)
of mUnaryLt: of mUnaryLt:
if not (optOverflowCheck in p.options): unaryExpr(p, e, d, "$1 - 1") if optOverflowCheck notin p.options: unaryExpr(p, e, d, "$1 - 1")
else: unaryExpr(p, e, d, "#subInt($1, 1)") else: unaryExpr(p, e, d, "#subInt($1, 1)")
of mPred: of mPred:
# XXX: range checking? # XXX: range checking?
if not (optOverflowCheck in p.options): binaryExpr(p, e, d, "$1 - $2") if optOverflowCheck notin p.options: binaryExpr(p, e, d, "$1 - $2")
else: binaryExpr(p, e, d, "#subInt($1, $2)") else: binaryExpr(p, e, d, "#subInt($1, $2)")
of mSucc: of mSucc:
# XXX: range checking? # XXX: range checking?
if not (optOverflowCheck in p.options): binaryExpr(p, e, d, "$1 + $2") if optOverflowCheck notin p.options: binaryExpr(p, e, d, "$1 + $2")
else: binaryExpr(p, e, d, "#addInt($1, $2)") else: binaryExpr(p, e, d, "#addInt($1, $2)")
of mInc: of mInc:
if not (optOverflowCheck in p.options): if optOverflowCheck notin p.options:
binaryStmt(p, e, d, "$1 += $2;$n") binaryStmt(p, e, d, "$1 += $2;$n")
elif skipTypes(e.sons[1].typ, abstractVar).kind == tyInt64: elif skipTypes(e.sons[1].typ, abstractVar).kind == tyInt64:
binaryStmt(p, e, d, "$1 = #addInt64($1, $2);$n") binaryStmt(p, e, d, "$1 = #addInt64($1, $2);$n")
else: else:
binaryStmt(p, e, d, "$1 = #addInt($1, $2);$n") binaryStmt(p, e, d, "$1 = #addInt($1, $2);$n")
of ast.mDec: of ast.mDec:
if not (optOverflowCheck in p.options): if optOverflowCheck notin p.options:
binaryStmt(p, e, d, "$1 -= $2;$n") binaryStmt(p, e, d, "$1 -= $2;$n")
elif skipTypes(e.sons[1].typ, abstractVar).kind == tyInt64: elif skipTypes(e.sons[1].typ, abstractVar).kind == tyInt64:
binaryStmt(p, e, d, "$1 = #subInt64($1, $2);$n") binaryStmt(p, e, d, "$1 = #subInt64($1, $2);$n")
@ -1659,6 +1685,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mParallel: of mParallel:
let n = semparallel.liftParallel(p.module.module, e) let n = semparallel.liftParallel(p.module.module, e)
expr(p, n, d) expr(p, n, d)
of mDeepCopy:
var a, b: TLoc
initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b)
genDeepCopy(p, a, b)
else: internalError(e.info, "genMagicExpr: " & $op) else: internalError(e.info, "genMagicExpr: " & $op)
proc genConstExpr(p: BProc, n: PNode): PRope proc genConstExpr(p: BProc, n: PNode): PRope
@ -1878,7 +1909,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of skVar, skForVar, skResult, skLet: of skVar, skForVar, skResult, skLet:
if sfGlobal in sym.flags: genVarPrototype(p.module, sym) if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
if sym.loc.r == nil or sym.loc.t == nil: if sym.loc.r == nil or sym.loc.t == nil:
internalError(n.info, "expr: var not init " & sym.name.s) internalError n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
if sfThread in sym.flags: if sfThread in sym.flags:
accessThreadLocalVar(p, sym) accessThreadLocalVar(p, sym)
if emulatedThreadVars(): if emulatedThreadVars():
@ -1893,7 +1924,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
of skParam: of skParam:
if sym.loc.r == nil or sym.loc.t == nil: if sym.loc.r == nil or sym.loc.t == nil:
internalError(n.info, "expr: param not init " & sym.name.s) #echo "FAILED FOR PRCO ", p.prc.name.s
internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id)
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol") else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
of nkNilLit: of nkNilLit:

View file

@ -453,7 +453,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
appf(result, " {$n", [name]) appf(result, " {$n", [name])
var desc = getRecordFields(m, typ, check) var desc = getRecordFields(m, typ, check)
if (desc == nil) and not hasField: if desc == nil and not hasField:
appf(result, "char dummy;$n", []) appf(result, "char dummy;$n", [])
else: else:
app(result, desc) app(result, desc)
@ -895,11 +895,20 @@ type
include ccgtrav include ccgtrav
proc genDeepCopyProc(m: BModule; s: PSym; result: PRope) =
genProc(m, s)
appf(m.s[cfsTypeInit3], "$1.deepcopy = (N_NIMCALL_PTR(void*, void*)) $2;$n",
[result, s.loc.r])
proc genTypeInfo(m: BModule, t: PType): PRope = proc genTypeInfo(m: BModule, t: PType): PRope =
let origType = t
var t = getUniqueType(t) var t = getUniqueType(t)
result = ropef("NTI$1", [toRope(t.id)]) result = ropef("NTI$1", [toRope(t.id)])
if containsOrIncl(m.typeInfoMarker, t.id): if containsOrIncl(m.typeInfoMarker, t.id):
return con("(&".toRope, result, ")".toRope) return con("(&".toRope, result, ")".toRope)
# getUniqueType doesn't skip tyDistinct when that has an overriden operation:
while t.kind == tyDistinct: t = t.lastSon
let owner = t.skipTypes(typedescPtrs).owner.getModule let owner = t.skipTypes(typedescPtrs).owner.getModule
if owner != m.module: if owner != m.module:
# make sure the type info is created in the owner module # make sure the type info is created in the owner module
@ -936,6 +945,10 @@ proc genTypeInfo(m: BModule, t: PType): PRope =
# results are not deterministic! # results are not deterministic!
genTupleInfo(m, t, result) genTupleInfo(m, t, result)
else: internalError("genTypeInfo(" & $t.kind & ')') else: internalError("genTypeInfo(" & $t.kind & ')')
if t.deepCopy != nil:
genDeepCopyProc(m, t.deepCopy, result)
elif origType.deepCopy != nil:
genDeepCopyProc(m, origType.deepCopy, result)
result = con("(&".toRope, result, ")".toRope) result = con("(&".toRope, result, ")".toRope)
proc genTypeSection(m: BModule, n: PNode) = proc genTypeSection(m: BModule, n: PNode) =

View file

@ -89,8 +89,10 @@ proc getUniqueType*(key: PType): PType =
of tyTypeDesc, tyTypeClasses, tyGenericParam, of tyTypeDesc, tyTypeClasses, tyGenericParam,
tyFromExpr, tyFieldAccessor: tyFromExpr, tyFieldAccessor:
internalError("GetUniqueType") internalError("GetUniqueType")
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, of tyDistinct:
tyConst, tyIter, tyStatic: if key.deepCopy != nil: result = key
else: result = getUniqueType(lastSon(key))
of tyGenericInst, tyOrdinal, tyMutable, tyConst, tyIter, tyStatic:
result = getUniqueType(lastSon(key)) result = getUniqueType(lastSon(key))
of tyArrayConstr, tyGenericInvokation, tyGenericBody, of tyArrayConstr, tyGenericInvokation, tyGenericBody,
tyOpenArray, tyArray, tySet, tyRange, tyTuple, tyOpenArray, tyArray, tySet, tyRange, tyTuple,

View file

@ -295,6 +295,7 @@ proc postStmtActions(p: BProc) {.inline.} =
proc accessThreadLocalVar(p: BProc, s: PSym) proc accessThreadLocalVar(p: BProc, s: PSym)
proc emulatedThreadVars(): bool {.inline.} proc emulatedThreadVars(): bool {.inline.}
proc genProc(m: BModule, prc: PSym)
include "ccgtypes.nim" include "ccgtypes.nim"
@ -574,7 +575,6 @@ proc fixLabel(p: BProc, labl: TLabel) =
proc genVarPrototype(m: BModule, sym: PSym) proc genVarPrototype(m: BModule, sym: PSym)
proc requestConstImpl(p: BProc, sym: PSym) proc requestConstImpl(p: BProc, sym: PSym)
proc genProc(m: BModule, prc: PSym)
proc genStmts(p: BProc, t: PNode) proc genStmts(p: BProc, t: PNode)
proc expr(p: BProc, n: PNode, d: var TLoc) proc expr(p: BProc, n: PNode, d: var TLoc)
proc genProcPrototype(m: BModule, sym: PSym) proc genProcPrototype(m: BModule, sym: PSym)

View file

@ -122,8 +122,8 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode =
if t == nil: break if t == nil: break
t = t.skipTypes(abstractInst) t = t.skipTypes(abstractInst)
#if field == nil: #if field == nil:
# echo "FIELD ", b
# debug deref.typ # debug deref.typ
# echo deref.typ.id
internalAssert field != nil internalAssert field != nil
addSon(deref, a) addSon(deref, a)
result = newNodeI(nkDotExpr, info) result = newNodeI(nkDotExpr, info)
@ -202,7 +202,8 @@ proc flowVarKind(t: PType): TFlowVarKind =
elif containsGarbageCollectedRef(t): fvInvalid elif containsGarbageCollectedRef(t): fvInvalid
else: fvBlob else: fvBlob
proc addLocalVar(varSection: PNode; owner: PSym; typ: PType; v: PNode): PSym = proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
v: PNode): PSym =
result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info) result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info)
result.typ = typ result.typ = typ
incl(result.flags, sfFromGeneric) incl(result.flags, sfFromGeneric)
@ -210,8 +211,15 @@ proc addLocalVar(varSection: PNode; owner: PSym; typ: PType; v: PNode): PSym =
var vpart = newNodeI(nkIdentDefs, varSection.info, 3) var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
vpart.sons[0] = newSymNode(result) vpart.sons[0] = newSymNode(result)
vpart.sons[1] = ast.emptyNode vpart.sons[1] = ast.emptyNode
vpart.sons[2] = v vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode
varSection.add vpart varSection.add vpart
if varInit != nil:
let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
deepCopyCall.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy))
deepCopyCall.sons[1] = newSymNode(result)
deepCopyCall.sons[2] = v
deepCopyCall.typ = typ
varInit.add deepCopyCall
discard """ discard """
We generate roughly this: We generate roughly this:
@ -244,24 +252,25 @@ stmtList:
""" """
proc createWrapperProc(f: PNode; threadParam, argsParam: PSym; proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
varSection, call, barrier, fv: PNode; varSection, varInit, call, barrier, fv: PNode;
spawnKind: TSpawnResult): PSym = spawnKind: TSpawnResult): PSym =
var body = newNodeI(nkStmtList, f.info) var body = newNodeI(nkStmtList, f.info)
var threadLocalBarrier: PSym var threadLocalBarrier: PSym
if barrier != nil: if barrier != nil:
var varSection = newNodeI(nkVarSection, barrier.info) var varSection = newNodeI(nkVarSection, barrier.info)
threadLocalBarrier = addLocalVar(varSection, argsParam.owner, threadLocalBarrier = addLocalVar(varSection, nil, argsParam.owner,
barrier.typ, barrier) barrier.typ, barrier)
body.add varSection body.add varSection
body.add callCodeGenProc("barrierEnter", threadLocalBarrier.newSymNode) body.add callCodeGenProc("barrierEnter", threadLocalBarrier.newSymNode)
var threadLocalProm: PSym var threadLocalProm: PSym
if spawnKind == srByVar: if spawnKind == srByVar:
threadLocalProm = addLocalVar(varSection, argsParam.owner, fv.typ, fv) threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
elif fv != nil: elif fv != nil:
internalAssert fv.typ.kind == tyGenericInst internalAssert fv.typ.kind == tyGenericInst
threadLocalProm = addLocalVar(varSection, argsParam.owner, fv.typ, fv) threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
if barrier == nil:
body.add varSection body.add varSection
body.add varInit
if fv != nil and spawnKind != srByVar: if fv != nil and spawnKind != srByVar:
# generate: # generate:
# fv.owner = threadParam # fv.owner = threadParam
@ -314,7 +323,8 @@ proc createCastExpr(argsParam: PSym; objType: PType): PNode =
result.typ.rawAddSon(objType) result.typ.rawAddSon(objType)
proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym, proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
castExpr, call, varSection, result: PNode) = castExpr, call,
varSection, varInit, result: PNode) =
let formals = n[0].typ.n let formals = n[0].typ.n
let tmpName = getIdent(genPrefix) let tmpName = getIdent(genPrefix)
for i in 1 .. <n.len: for i in 1 .. <n.len:
@ -323,8 +333,8 @@ proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
var argType = n[i].typ.skipTypes(abstractInst) var argType = n[i].typ.skipTypes(abstractInst)
if i < formals.len and formals[i].typ.kind == tyVar: if i < formals.len and formals[i].typ.kind == tyVar:
localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter") localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter")
elif containsTyRef(argType): #elif containsTyRef(argType):
localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure") # localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
let fieldname = if i < formals.len: formals[i].sym.name else: tmpName let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
var field = newSym(skField, fieldname, objType.owner, n.info) var field = newSym(skField, fieldname, objType.owner, n.info)
@ -332,7 +342,7 @@ proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
objType.addField(field) objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i]) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
let temp = addLocalVar(varSection, objType.owner, argType, let temp = addLocalVar(varSection, varInit, objType.owner, argType,
indirectAccess(castExpr, field, n.info)) indirectAccess(castExpr, field, n.info))
call.add(newSymNode(temp)) call.add(newSymNode(temp))
@ -367,7 +377,8 @@ proc genHigh(n: PNode): PNode =
result.sons[1] = n result.sons[1] = n
proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
castExpr, call, varSection, result: PNode) = castExpr, call,
varSection, varInit, result: PNode) =
let formals = n[0].typ.n let formals = n[0].typ.n
let tmpName = getIdent(genPrefix) let tmpName = getIdent(genPrefix)
# we need to copy the foreign scratch object fields into local variables # we need to copy the foreign scratch object fields into local variables
@ -376,8 +387,8 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
let n = n[i] let n = n[i]
let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ, let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,
abstractInst) abstractInst)
if containsTyRef(argType): #if containsTyRef(argType):
localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure") # localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
let fieldname = if i < formals.len: formals[i].sym.name else: tmpName let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
var field = newSym(skField, fieldname, objType.owner, n.info) var field = newSym(skField, fieldname, objType.owner, n.info)
@ -403,7 +414,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2]) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3]) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
let threadLocal = addLocalVar(varSection, objType.owner, fieldA.typ, let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldA.typ,
indirectAccess(castExpr, fieldA, n.info)) indirectAccess(castExpr, fieldA, n.info))
slice.sons[2] = threadLocal.newSymNode slice.sons[2] = threadLocal.newSymNode
else: else:
@ -417,7 +428,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
# the array itself does not need to go through a thread local variable: # the array itself does not need to go through a thread local variable:
slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info)) slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
let threadLocal = addLocalVar(varSection, objType.owner, fieldB.typ, let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldB.typ,
indirectAccess(castExpr, fieldB, n.info)) indirectAccess(castExpr, fieldB, n.info))
slice.sons[3] = threadLocal.newSymNode slice.sons[3] = threadLocal.newSymNode
call.add slice call.add slice
@ -428,7 +439,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
field.typ = a.typ field.typ = a.typ
objType.addField(field) objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
let threadLocal = addLocalVar(varSection, objType.owner, field.typ, let threadLocal = addLocalVar(varSection,nil, objType.owner, field.typ,
indirectAccess(castExpr, field, n.info)) indirectAccess(castExpr, field, n.info))
call.add(genDeref(threadLocal.newSymNode)) call.add(genDeref(threadLocal.newSymNode))
else: else:
@ -436,7 +447,8 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
field.typ = argType field.typ = argType
objType.addField(field) objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
let threadLocal = addLocalVar(varSection, objType.owner, field.typ, let threadLocal = addLocalVar(varSection, varInit,
objType.owner, field.typ,
indirectAccess(castExpr, field, n.info)) indirectAccess(castExpr, field, n.info))
call.add(threadLocal.newSymNode) call.add(threadLocal.newSymNode)
@ -504,10 +516,13 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
call.add(fn) call.add(fn)
var varSection = newNodeI(nkVarSection, n.info) var varSection = newNodeI(nkVarSection, n.info)
var varInit = newNodeI(nkStmtList, n.info)
if barrier.isNil: if barrier.isNil:
setupArgsForConcurrency(n, objType, scratchObj, castExpr, call, varSection, result) setupArgsForConcurrency(n, objType, scratchObj, castExpr, call,
varSection, varInit, result)
else: else:
setupArgsForParallelism(n, objType, scratchObj, castExpr, call, varSection, result) setupArgsForParallelism(n, objType, scratchObj, castExpr, call,
varSection, varInit, result)
var barrierAsExpr: PNode = nil var barrierAsExpr: PNode = nil
if barrier != nil: if barrier != nil:
@ -539,7 +554,8 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
fvAsExpr = indirectAccess(castExpr, field, n.info) fvAsExpr = indirectAccess(castExpr, field, n.info)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest)) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, call, let wrapper = createWrapperProc(fn, threadParam, argsParam,
varSection, varInit, call,
barrierAsExpr, fvAsExpr, spawnKind) barrierAsExpr, fvAsExpr, spawnKind)
result.add callCodeGenProc("nimSpawn", wrapper.newSymNode, result.add callCodeGenProc("nimSpawn", wrapper.newSymNode,
genAddrOf(scratchObj.newSymNode)) genAddrOf(scratchObj.newSymNode))

View file

@ -24,7 +24,8 @@ const
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
wGensym, wInject, wRaises, wTags, wUses, wOperator, wDelegator, wGcSafe} wGensym, wInject, wRaises, wTags, wUses, wOperator, wDelegator, wGcSafe,
wOverride}
converterPragmas* = procPragmas converterPragmas* = procPragmas
methodPragmas* = procPragmas methodPragmas* = procPragmas
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
@ -628,10 +629,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
# implies nodecl, because otherwise header would not make sense # implies nodecl, because otherwise header would not make sense
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s) if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)
of wDestructor: of wDestructor:
if sym.typ.sons.len == 2: sym.flags.incl sfOverriden
sym.flags.incl sfDestructor if sym.name.s.normalize != "destroy":
else: localError(n.info, errGenerated, "destructor has to be named 'destroy'")
invalidPragma(it) of wOverride:
sym.flags.incl sfOverriden
of wNosideeffect: of wNosideeffect:
noVal(it) noVal(it)
incl(sym.flags, sfNoSideEffect) incl(sym.flags, sfNoSideEffect)

View file

@ -125,8 +125,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
# The destructor is either user-defined or automatically # The destructor is either user-defined or automatically
# generated by the compiler in a member-wise fashion. # generated by the compiler in a member-wise fashion.
var t = skipTypes(typ, {tyConst, tyMutable}).skipGenericAlias var t = skipTypes(typ, {tyConst, tyMutable}).skipGenericAlias
let typeHoldingUserDefinition = if t.kind == tyGenericInst: t.base let typeHoldingUserDefinition = if t.kind == tyGenericInst: t.base else: t
else: t
if typeHoldingUserDefinition.destructor != nil: if typeHoldingUserDefinition.destructor != nil:
# XXX: This is not entirely correct for recursive types, but we need # XXX: This is not entirely correct for recursive types, but we need

View file

@ -1009,6 +1009,28 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
addResult(c, s.typ.sons[0], n.info, s.kind) addResult(c, s.typ.sons[0], n.info, s.kind)
addResultNode(c, n) addResultNode(c, n)
proc semOverride(c: PContext, s: PSym, n: PNode) =
case s.name.s.normalize
of "destroy": doDestructorStuff(c, s, n)
of "deepcopy":
if s.typ.len == 2 and
s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
sameType(s.typ.sons[1], s.typ.sons[0]):
# Note: we store the deepCopy in the base of the pointer to mitigate
# the problem that pointers are structural types:
let t = s.typ.sons[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst)
if t.kind in {tyObject, tyDistinct, tyEnum}:
t.deepCopy = s
else:
localError(n.info, errGenerated,
"cannot bind 'deepCopy' to: " & typeToString(t))
else:
localError(n.info, errGenerated,
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
of "=": discard
else: localError(n.info, errGenerated,
"'destroy' or 'deepCopy' expected for 'override'")
type type
TProcCompilationSteps = enum TProcCompilationSteps = enum
stepRegisterSymbol, stepRegisterSymbol,
@ -1125,7 +1147,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
popOwner() popOwner()
pushOwner(s) pushOwner(s)
s.options = gOptions s.options = gOptions
if sfDestructor in s.flags: doDestructorStuff(c, s, n) if sfOverriden in s.flags: semOverride(c, s, n)
if n.sons[bodyPos].kind != nkEmpty: if n.sons[bodyPos].kind != nkEmpty:
# for DLL generation it is annoying to check for sfImportc! # for DLL generation it is annoying to check for sfImportc!
if sfBorrow in s.flags: if sfBorrow in s.flags:

View file

@ -137,7 +137,7 @@ proc hasGenericArguments*(n: PNode): bool =
return false return false
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode = proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
# This is needed fo tgenericshardcases # This is needed for tgenericshardcases
# It's possible that a generic param will be used in a proc call to a # It's possible that a generic param will be used in a proc call to a
# typedesc accepting proc. After generic param substitution, such procs # typedesc accepting proc. After generic param substitution, such procs
# should be optionally instantiated with the correct type. In order to # should be optionally instantiated with the correct type. In order to

View file

@ -39,7 +39,7 @@ type
wDestroy, wDestroy,
wImmediate, wDestructor, wDelegator, wImmediate, wDestructor, wDelegator, wOverride,
wImportCpp, wImportObjC, wImportCpp, wImportObjC,
wImportCompilerProc, wImportCompilerProc,
wImportc, wExportc, wIncompleteStruct, wRequiresInit, wImportc, wExportc, wIncompleteStruct, wRequiresInit,
@ -122,7 +122,7 @@ const
"destroy", "destroy",
"immediate", "destructor", "delegator", "immediate", "destructor", "delegator", "override",
"importcpp", "importobjc", "importcpp", "importobjc",
"importcompilerproc", "importc", "exportc", "incompletestruct", "importcompilerproc", "importc", "exportc", "incompletestruct",
"requiresinit", "align", "nodecl", "pure", "sideeffect", "requiresinit", "align", "nodecl", "pure", "sideeffect",

View file

@ -2497,7 +2497,7 @@ variable to use:
When the compiler reaches the second ``add`` call, both ``a`` and ``b`` could When the compiler reaches the second ``add`` call, both ``a`` and ``b`` could
be used with the proc, so one gets ``Error: expression '(a|b)' has no type (or be used with the proc, so one gets ``Error: expression '(a|b)' has no type (or
is ambiguous)``. To solve this you would need to nest ``using`` with a is ambiguous)``. To solve this one would need to nest ``using`` with a
``block`` statement so as to control the reach of the ``using`` statement. ``block`` statement so as to control the reach of the ``using`` statement.
If expression If expression
@ -4347,7 +4347,7 @@ Nimrod offers a special family of dot operators that can be used to
intercept and rewrite proc call and field access attempts, referring intercept and rewrite proc call and field access attempts, referring
to previously undeclared symbol names. They can be used to provide a to previously undeclared symbol names. They can be used to provide a
fluent interface to objects lying outside the static confines of the fluent interface to objects lying outside the static confines of the
Nimrod's type system such as values from dynamic scripting languages type system such as values from dynamic scripting languages
or dynamic file formats such as JSON or XML. or dynamic file formats such as JSON or XML.
When Nimrod encounters an expression that cannot be resolved by the When Nimrod encounters an expression that cannot be resolved by the
@ -4379,8 +4379,8 @@ This operator will be matched against both field accesses and method calls.
operator `.()` operator `.()`
--------------- ---------------
This operator will be matched exclusively against method calls. It has higher This operator will be matched exclusively against method calls. It has higher
precedence than the `.` operator and this allows you to handle expressions like precedence than the `.` operator and this allows one to handle expressions like
`x.y` and `x.y()` differently if you are interfacing with a scripting language `x.y` and `x.y()` differently if one is interfacing with a scripting language
for example. for example.
operator `.=` operator `.=`
@ -4391,6 +4391,115 @@ This operator will be matched against assignments to missing fields.
a.b = c # becomes `.=`(a, "b", c) a.b = c # becomes `.=`(a, "b", c)
Type bound operations
=====================
There are 3 operations that are bound to a type:
1. Assignment
2. Destruction
3. Deep copying for communication between threads
These operations can be *overriden* instead of *overloaded*. This means the
implementation is automatically lifted to structured types. For instance if type
``T`` has an overriden assignment operator ``=`` this operator is also used
for assignments of the type ``seq[T]``. Since these operations are bound to a
type they have to be bound to a nominal type for reasons of simplicity of
implementation: This means an overriden ``deepCopy`` for ``ref T`` is really
bound to ``T`` and not to ``ref T``. This also means that one cannot override
``deepCopy`` for both ``ptr T`` and ``ref T`` at the same time; instead a
helper distinct or object type has to be used for one pointer type.
operator `=`
------------
This operator is the assignment operator. Note that in the contexts
like ``let v = expr``, ``var v = expr``, ``parameter = defaultValue`` or for
parameter passing no assignment is performed. The ``override`` pragma is
optional for overriding ``=``.
destructors
-----------
A destructor must have a single parameter with a concrete type (the name of a
generic type is allowed too). The name of the destructor has to be ``destroy``
and it need to be annotated with the ``override`` pragma.
``destroy(v)`` will be automatically invoked for every local stack
variable ``v`` that goes out of scope.
If a structured type features a field with destructable type and
the user has not provided an explicit implementation, a destructor for the
structured type will be automatically generated. Calls to any base class
destructors in both user-defined and generated destructors will be inserted.
A destructor is attached to the type it destructs; expressions of this type
can then only be used in *destructible contexts* and as parameters:
.. code-block:: nimrod
type
TMyObj = object
x, y: int
p: pointer
proc destroy(o: var TMyObj) {.override.} =
if o.p != nil: dealloc o.p
proc open: TMyObj =
result = TMyObj(x: 1, y: 2, p: alloc(3))
proc work(o: TMyObj) =
echo o.x
# No destructor invoked here for 'o' as 'o' is a parameter.
proc main() =
# destructor automatically invoked at the end of the scope:
var x = open()
# valid: pass 'x' to some other proc:
work(x)
# Error: usage of a type with a destructor in a non destructible context
echo open()
A destructible context is currently only the following:
1. The ``expr`` in ``var x = expr``.
2. The ``expr`` in ``let x = expr``.
3. The ``expr`` in ``return expr``.
4. The ``expr`` in ``result = expr`` where ``result`` is the special symbol
introduced by the compiler.
These rules ensure that the construction is tied to a variable and can easily
be destructed at its scope exit. Later versions of the language will improve
the support of destructors.
Be aware that destructors are not called for objects allocated with ``new``.
This may change in future versions of language, but for now the ``finalizer``
parameter to ``new`` has to be used.
deepCopy
--------
``deepCopy`` is a builtin that is invoked whenever data is passed to
a ``spawn``'ed proc to ensure memory safety. The programmer can override its
behaviour for a specific ``ref`` or ``ptr`` type ``T``. (Later versions of the
language may weaken this restriction.)
The signature has to be:
.. code-block:: nimrod
proc deepCopy(x: T): T {.override.}
This mechanism is used by most data structures that support shared memory like
channels to implement thread safe automatic memory management.
The builtin ``deepCopy`` can even clone closures and their environments. See
the documentation of `spawn`_ for details.
Term rewriting macros Term rewriting macros
===================== =====================
@ -4985,62 +5094,14 @@ destructor pragma
----------------- -----------------
The ``destructor`` pragma is used to mark a proc to act as a type destructor. The ``destructor`` pragma is used to mark a proc to act as a type destructor.
The proc must have a single parameter with a concrete type (the name of a Its usage is deprecated, use the ``override`` pragma instead.
generic type is allowed too). See `type bound operations`_.
Destructors will be automatically invoked when a local stack variable goes
out of scope.
If a record type features a field with destructable type and override pragma
the user have not provided explicit implementation, Nimrod will automatically ---------------
generate a destructor for the record type. Nimrod will automatically insert
calls to any base class destructors in both user-defined and generated
destructors.
A destructor is attached to the type it destructs; expressions of this type
can then only be used in *destructible contexts* and as parameters:
.. code-block:: nimrod
type
TMyObj = object
x, y: int
p: pointer
proc destruct(o: var TMyObj) {.destructor.} =
if o.p != nil: dealloc o.p
proc open: TMyObj =
result = TMyObj(x: 1, y: 2, p: alloc(3))
proc work(o: TMyObj) =
echo o.x
# No destructor invoked here for 'o' as 'o' is a parameter.
proc main() =
# destructor automatically invoked at the end of the scope:
var x = open()
# valid: pass 'x' to some other proc:
work(x)
# Error: usage of a type with a destructor in a non destructible context
echo open()
A destructible context is currently only the following:
1. The ``expr`` in ``var x = expr``.
2. The ``expr`` in ``let x = expr``.
3. The ``expr`` in ``return expr``.
4. The ``expr`` in ``result = expr`` where ``result`` is the special symbol
introduced by the compiler.
These rules ensure that the construction is tied to a variable and can easily
be destructed at its scope exit. Later versions of the language will improve
the support of destructors.
Be aware that destructors are not called for objects allocated with ``new``.
This may change in future versions of language, but for now use
the ``finalizer`` parameter to ``new``.
See `type bound operations`_ instead.
procvar pragma procvar pragma
-------------- --------------
@ -5490,10 +5551,10 @@ spelled*:
proc printf(formatstr: cstring) {.header: "<stdio.h>", importc: "printf", varargs.} proc printf(formatstr: cstring) {.header: "<stdio.h>", importc: "printf", varargs.}
Note that this pragma is somewhat of a misnomer: Other backends will provide Note that this pragma is somewhat of a misnomer: Other backends will provide
the same feature under the same name. Also, if you are interfacing with C++ the same feature under the same name. Also, if one is interfacing with C++
you can use the `ImportCpp pragma <nimrodc.html#importcpp-pragma>`_ and the `ImportCpp pragma <nimrodc.html#importcpp-pragma>`_ and
interfacing with Objective-C the `ImportObjC pragma interfacing with Objective-C the `ImportObjC pragma
<nimrodc.html#importobjc-pragma>`_. <nimrodc.html#importobjc-pragma>`_ can be used.
Exportc pragma Exportc pragma
@ -5781,12 +5842,14 @@ used instead. `spawn`:idx: is used to pass a task to the thread pool:
Currently the expression that ``spawn`` takes is however quite restricted: Currently the expression that ``spawn`` takes is however quite restricted:
* It must be a call expresion ``f(a, ...)``. * It must be a call expression ``f(a, ...)``.
* ``f`` must be ``gcsafe``. * ``f`` must be ``gcsafe``.
* ``f`` must not have the calling convention ``closure``. * ``f`` must not have the calling convention ``closure``.
* ``f``'s parameters may not be of type ``var`` nor may they contain ``ref``. * ``f``'s parameters may not be of type ``var``.
This means you have to use raw ``ptr``'s for data passing reminding the This means one has to use raw ``ptr``'s for data passing reminding the
programmer to be careful. programmer to be careful.
* ``ref`` parameters are deeply copied which is a subtle semantic change and
can cause performance problems but ensures memory safety.
* For *safe* data exchange between ``f`` and the caller a global ``TChannel`` * For *safe* data exchange between ``f`` and the caller a global ``TChannel``
needs to be used. Other means will be provided soon. needs to be used. Other means will be provided soon.

View file

@ -2997,11 +2997,14 @@ proc locals*(): TObject {.magic: "Locals", noSideEffect.} =
## # -> B is 1 ## # -> B is 1
discard discard
proc deepCopy*[T](x: T): T {.magic: "DeepCopy", noSideEffect.} = when hostOS != "standalone" and not defined(NimrodVM) and not defined(JS):
proc deepCopy*[T](x: var T, y: T) {.noSideEffect, magic: "DeepCopy".} =
## performs a deep copy of `x`. This is also used by the code generator ## performs a deep copy of `x`. This is also used by the code generator
## for the implementation of ``spawn``. ## for the implementation of ``spawn``.
discard discard
include "system/deepcopy"
{.pop.} #{.push warning[GcMem]: off.} {.pop.} #{.push warning[GcMem]: off.}
when not defined(booting): when not defined(booting):

121
lib/system/deepcopy.nim Normal file
View file

@ -0,0 +1,121 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) {.gcsafe.}
proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.gcsafe.} =
var
d = cast[TAddress](dest)
s = cast[TAddress](src)
case n.kind
of nkSlot:
genericDeepCopyAux(cast[pointer](d +% n.offset),
cast[pointer](s +% n.offset), n.typ)
of nkList:
for i in 0..n.len-1:
genericDeepCopyAux(dest, src, n.sons[i])
of nkCase:
var dd = selectBranch(dest, n)
var m = selectBranch(src, n)
# reset if different branches are in use; note different branches also
# imply that's not self-assignment (``x = x``)!
if m != dd and dd != nil:
genericResetAux(dest, dd)
copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
n.typ.size)
if m != nil:
genericDeepCopyAux(dest, src, m)
of nkNone: sysAssert(false, "genericDeepCopyAux")
proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
var
d = cast[TAddress](dest)
s = cast[TAddress](src)
sysAssert(mt != nil, "genericDeepCopyAux 2")
case mt.kind
of tyString:
var x = cast[PPointer](dest)
var s2 = cast[PPointer](s)[]
if s2 == nil:
unsureAsgnRef(x, s2)
else:
unsureAsgnRef(x, copyString(cast[NimString](s2)))
of tySequence:
var s2 = cast[PPointer](src)[]
var seq = cast[PGenericSeq](s2)
var x = cast[PPointer](dest)
if s2 == nil:
unsureAsgnRef(x, s2)
return
sysAssert(dest != nil, "genericDeepCopyAux 3")
unsureAsgnRef(x, newSeq(mt, seq.len))
var dst = cast[TAddress](cast[PPointer](dest)[])
for i in 0..seq.len-1:
genericDeepCopyAux(
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
cast[pointer](cast[TAddress](s2) +% i *% mt.base.size +%
GenericSeqSize),
mt.base)
of tyObject:
# we need to copy m_type field for tyObject, as it could be empty for
# sequence reallocations:
var pint = cast[ptr PNimType](dest)
pint[] = cast[ptr PNimType](src)[]
if mt.base != nil:
genericDeepCopyAux(dest, src, mt.base)
genericDeepCopyAux(dest, src, mt.node)
of tyTuple:
genericDeepCopyAux(dest, src, mt.node)
of tyArray, tyArrayConstr:
for i in 0..(mt.size div mt.base.size)-1:
genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size),
cast[pointer](s +% i*% mt.base.size), mt.base)
of tyRef:
var z: pointer
if mt.base.deepCopy != nil:
z = mt.base.deepCopy(cast[PPointer](s)[])
else:
# we modify the header of the cell temporarily; instead of the type
# field we store a forwarding pointer. XXX This is bad when the cloning
# fails due to OOM etc.
let x = usrToCell(cast[PPointer](s)[])
let forw = cast[int](x.typ)
if (forw and 1) == 1:
# we stored a forwarding pointer, so let's use that:
z = cast[pointer](forw and not 1)
else:
let realType = x.typ
z = newObj(realType, realType.base.size)
x.typ = cast[PNimType](cast[int](z) or 1)
genericDeepCopyAux(dest, addr(z), realType)
x.typ = realType
unsureAsgnRef(cast[PPointer](dest), z)
of tyPtr:
# no cycle check here, but also not really required
if mt.base.deepCopy != nil:
cast[PPointer](dest)[] = mt.base.deepCopy(cast[PPointer](s)[])
else:
cast[PPointer](dest)[] = cast[PPointer](s)[]
else:
copyMem(dest, src, mt.size) # copy raw bits
proc genericDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} =
genericDeepCopyAux(dest, src, mt)
proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} =
var src = src
genericDeepCopy(dest, addr(src), mt)
proc genericDeepCopyOpenArray(dest, src: pointer, len: int,
mt: PNimType) {.compilerproc.} =
var
d = cast[TAddress](dest)
s = cast[TAddress](src)
for i in 0..len-1:
genericDeepCopy(cast[pointer](d +% i*% mt.base.size),
cast[pointer](s +% i*% mt.base.size), mt.base)

View file

@ -86,6 +86,7 @@ type
node: ptr TNimNode # valid for tyRecord, tyObject, tyTuple, tyEnum node: ptr TNimNode # valid for tyRecord, tyObject, tyTuple, tyEnum
finalizer: pointer # the finalizer for the type finalizer: pointer # the finalizer for the type
marker: proc (p: pointer, op: int) {.nimcall, gcsafe.} # marker proc for GC marker: proc (p: pointer, op: int) {.nimcall, gcsafe.} # marker proc for GC
deepcopy: proc (p: pointer): pointer {.nimcall, gcsafe.}
PNimType = ptr TNimType PNimType = ptr TNimType
# node.len may be the ``first`` element of a set # node.len may be the ``first`` element of a set

View file

@ -55,14 +55,14 @@ type
q: TMyGeneric3[TMyObj, int, int] q: TMyGeneric3[TMyObj, int, int]
r: string r: string
proc destruct(o: var TMyObj) {.destructor.} = proc destroy(o: var TMyObj) {.override.} =
if o.p != nil: dealloc o.p if o.p != nil: dealloc o.p
echo "myobj destroyed" echo "myobj destroyed"
proc destroy(o: var TMyGeneric1) {.destructor.} = proc destroy(o: var TMyGeneric1) {.override.} =
echo "mygeneric1 destroyed" echo "mygeneric1 destroyed"
proc destroy[A, B](o: var TMyGeneric2[A, B]) {.destructor.} = proc destroy[A, B](o: var TMyGeneric2[A, B]) {.override.} =
echo "mygeneric2 destroyed" echo "mygeneric2 destroyed"
proc open: TMyObj = proc open: TMyObj =

View file

@ -8,7 +8,7 @@ type
x, y: int x, y: int
p: pointer p: pointer
proc destruct(o: var TMyObj) {.destructor.} = proc destroy(o: var TMyObj) {.override.} =
if o.p != nil: dealloc o.p if o.p != nil: dealloc o.p
proc open: TMyObj = proc open: TMyObj =

View file

@ -3,20 +3,26 @@ version 0.9.6
- scopes are still broken for generic instantiation! - scopes are still broken for generic instantiation!
- start experimental branch - start experimental branch
- overloading of '='; general lift mechanism - implicit deref for parameter matching
Concurrency Concurrency
----------- -----------
- 'gcsafe' inferrence needs to be fixed - 'gcsafe' inferrence needs to be fixed
- 'deepCopy' needs to be instantiated for
generics *when the type is constructed*
- test 'deepCopy'
- overloading of '='; general lift mechanism
- the disjoint checker needs to deal with 'a = spawn f(); g = spawn f()' - the disjoint checker needs to deal with 'a = spawn f(); g = spawn f()'
- implement 'deepCopy' builtin
- implement 'foo[1..4] = spawn(f[4..7])' - implement 'foo[1..4] = spawn(f[4..7])'
- support for exception propagation - document the new 'spawn' and 'parallel' statements
- Minor: The copying of the 'ref Promise' into the thead local storage only
Low priority:
- support for exception propagation? (hard to implement)
- the copying of the 'ref Promise' into the thead local storage only
happens to work due to the write barrier's implementation happens to work due to the write barrier's implementation
- implement lock levels --> first without the more complex race avoidance - implement lock levels --> first without the more complex race avoidance
- document the new 'spawn' and 'parallel' statements
Misc Misc
@ -27,8 +33,6 @@ Misc
- fix the tuple unpacking in lambda bug - fix the tuple unpacking in lambda bug
- make tuple unpacking work in a non-var/let context - make tuple unpacking work in a non-var/let context
- special rule for ``[]=`` - special rule for ``[]=``
- ``=`` should be overloadable; requires specialization for ``=``; general
lift mechanism in the compiler is already implemented for 'fields'
- built-in 'getImpl' - built-in 'getImpl'
- type API for macros; make 'spawn' a macro - type API for macros; make 'spawn' a macro
- markAndSweepGC should expose an API for fibers - markAndSweepGC should expose an API for fibers
@ -57,13 +61,9 @@ version 0.9.x
- implement 'bits' pragmas - implement 'bits' pragmas
- we need a magic thisModule symbol - we need a magic thisModule symbol
- provide --nilChecks:on|off - provide --nilChecks:on|off
- fix closures/lambdalifting
- ensure (ref T)(a, b) works as a type conversion and type constructor - ensure (ref T)(a, b) works as a type conversion and type constructor
- optimize 'genericReset'; 'newException' leads to code bloat - optimize 'genericReset'; 'newException' leads to code bloat
- stack-less GC - stack-less GC
- implicit deref for parameter matching
- VM: optimize opcAsgnStr
version 0.9.X version 0.9.X

View file

@ -20,6 +20,14 @@ News
representation. representation.
- ``uri.TUrl`` as well as the ``parseurl`` module are now deprecated in favour - ``uri.TUrl`` as well as the ``parseurl`` module are now deprecated in favour
of the new ``TUri`` type in the ``uri`` module. of the new ``TUri`` type in the ``uri`` module.
- The ``destructor`` pragma has been deprecated. Use the ``override`` pragma
instead. The destructor's name has to be ``destroy`` now.
Language Additions
------------------
- There is a new ``parallel`` statement for safe fork&join parallel computing.
Library Additions Library Additions
----------------- -----------------