From 1088814e56811f3bca77f191d8c492405712fcdb Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 26 Sep 2014 09:36:09 +0200 Subject: [PATCH] deepCopy is instantiated when its corresponding type is instantiated --- compiler/ccgtypes.nim | 2 +- compiler/sem.nim | 2 ++ compiler/semdata.nim | 3 +++ compiler/semstmts.nim | 7 ++++++- compiler/semtypinst.nim | 12 ++++++++++++ compiler/sigmatch.nim | 12 ++++++++++++ doc/manual.txt | 5 +++++ lib/system/deepcopy.nim | 18 +++++++++--------- tests/parallel/tdeepcopy2.nim | 35 +++++++++++++++++++++++++++++++++++ todo.txt | 6 ++---- 10 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 tests/parallel/tdeepcopy2.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 4cb6a99774..1b0b9e1038 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -898,7 +898,7 @@ 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", + appf(m.s[cfsTypeInit3], "$1.deepcopy =(N_NIMCALL_PTR(void*,)(void*))$2;$n", [result, s.loc.r]) proc genTypeInfo(m: BModule, t: PType): PRope = diff --git a/compiler/sem.nim b/compiler/sem.nim index 1b6e6fe2ef..7446384d2a 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -371,6 +371,8 @@ proc myOpen(module: PSym): PPassContext = c.semInferredLambda = semInferredLambda c.semGenerateInstance = generateInstance c.semTypeNode = semTypeNode + c.instDeepCopy = sigmatch.instDeepCopy + pushProcCon(c, module) pushOwner(c.module) c.importTable = openScope(c) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 6675562221..bc7b8cdc25 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -92,6 +92,9 @@ type lastGenericIdx*: int # used for the generics stack hloLoopDetector*: int # used to prevent endless loops in the HLO inParallelStmt*: int + instDeepCopy*: proc (c: PContext; dc: PSym; t: PType; + info: TLineInfo): PSym {.nimcall.} + proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = result.genericSym = s diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 524a8fd99f..c386785be1 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1029,7 +1029,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = 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) + var t = s.typ.sons[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst) + while true: + if t.kind == tyGenericBody: t = t.lastSon + elif t.kind == tyGenericInvokation: t = t.sons[0] + else: break if t.kind in {tyObject, tyDistinct, tyEnum}: if t.deepCopy.isNil: t.deepCopy = s else: @@ -1044,6 +1048,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = of "=": discard else: localError(n.info, errGenerated, "'destroy' or 'deepCopy' expected for 'override'") + incl(s.flags, sfUsed) type TProcCompilationSteps = enum diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 4563dc8d40..fa4d2cc0b3 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -299,6 +299,18 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = if newbody.isGenericAlias: newbody = newbody.skipGenericAlias rawAddSon(result, newbody) checkPartialConstructedType(cl.info, newbody) + let dc = newbody.deepCopy + if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: + # 'deepCopy' needs to be instantiated for + # generics *when the type is constructed*: + newbody.deepCopy = cl.c.instDeepCopy(cl.c, dc, result, cl.info) + when false: + var bindings: TIdTable + initIdTable(bindings) + debug newbody + bindings.idTablePut(dc.ast[genericParamsPos].sons[0].typ, newbody) + newbody.deepCopy = cl.c.semGenerateInstance(cl.c, dc, bindings, cl.info) + assert sfFromGeneric in newbody.deepCopy.flags proc eraseVoidParams*(t: PType) = if t.sons[0] != nil and t.sons[0].kind == tyEmpty: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 51ae4e8f53..59f954ff0e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1482,6 +1482,18 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool = # instantiate generic converters for that result = res != nil +proc instDeepCopy*(c: PContext; dc: PSym; t: PType; info: TLineInfo): PSym {. + procvar.} = + var m: TCandidate + initCandidate(c, m, dc.typ) + var f = dc.typ.sons[1] + if f.kind in {tyRef, tyPtr}: f = f.lastSon + if typeRel(m, f, t) == isNone: + localError(info, errGenerated, "cannot instantiate 'deepCopy'") + else: + result = c.semGenerateInstance(c, dc, m.bindings, info) + assert sfFromGeneric in result.flags + include suggest when not declared(tests): diff --git a/doc/manual.txt b/doc/manual.txt index 3d44110f5b..8b0f5a5e68 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -4386,6 +4386,8 @@ like ``let v = expr``, ``var v = expr``, ``parameter = defaultValue`` or for parameter passing no assignment is performed. The ``override`` pragma is optional for overriding ``=``. +**Note**: Overriding of operator ``=`` is not yet implemented. + destructors ----------- @@ -4446,6 +4448,9 @@ 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. +**Note**: Destructors are still experimental and the spec might change +significantly in order to incorporate an escape analysis. + deepCopy -------- diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index 902999970e..e4356a25dd 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -82,17 +82,16 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) = genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size), cast[pointer](s +% i*% mt.base.size), mt.base) of tyRef: - if mt.base.deepcopy != nil: - let z = mt.base.deepcopy(cast[PPointer](src)[]) + let s2 = cast[PPointer](src)[] + if s2 == nil: + unsureAsgnRef(cast[PPointer](dest), s2) + elif mt.base.deepcopy != nil: + let z = mt.base.deepcopy(s2) unsureAsgnRef(cast[PPointer](dest), z) 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 s2 = cast[PPointer](src)[] - if s2 == nil: - unsureAsgnRef(cast[PPointer](dest), s2) - return when declared(usrToCell): # unfortunately we only have cycle detection for our native GCs. let x = usrToCell(s2) @@ -116,10 +115,11 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) = genericDeepCopyAux(z, s2, realType.base) 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)[]) + let s2 = cast[PPointer](src)[] + if s2 != nil and mt.base.deepcopy != nil: + cast[PPointer](dest)[] = mt.base.deepcopy(s2) else: - cast[PPointer](dest)[] = cast[PPointer](s)[] + cast[PPointer](dest)[] = s2 else: copyMem(dest, src, mt.size) diff --git a/tests/parallel/tdeepcopy2.nim b/tests/parallel/tdeepcopy2.nim new file mode 100644 index 0000000000..748ef4d9b7 --- /dev/null +++ b/tests/parallel/tdeepcopy2.nim @@ -0,0 +1,35 @@ +discard """ + output: '''called deepCopy for int +called deepCopy for int +done999 999 +""" + +import threadpool + + +type + Bar[T] = object + x: T + +proc deepCopy[T](b: ref Bar[T]): ref Bar[T] {.override.} = + result.new + result.x = b.x + when T is int: + echo "called deepCopy for int" + else: + echo "called deepCopy for something else" + +proc foo(b: ref Bar[int]): int = 999 + +# test that the disjoint checker deals with 'a = spawn f(); g = spawn f()': + +proc main = + var dummy: ref Bar[int] + new(dummy) + dummy.x = 44 + parallel: + let f = spawn foo(dummy) + let b = spawn foo(dummy) + echo "done", f, " ", b + +main() diff --git a/todo.txt b/todo.txt index 70173d50eb..7f89678e25 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,7 @@ version 0.10 ============ +- use the effect system for static deadlock prevention - Test nimfix on various babel packages - deprecate recursive tuples; tuple needs laxer type checking - string case should require an 'else' @@ -20,9 +21,7 @@ version 0.9.6 Concurrency ----------- -- 'deepCopy' needs to be instantiated for - generics *when the type is constructed* -- test 'deepCopy' +- test 'deepCopy' for closures - implement 'foo[1..4] = spawn(f[4..7])' - document the new 'spawn' and 'parallel' statements @@ -117,7 +116,6 @@ Concurrency/Effect system provide a ``syncgc`` pragma to trigger compiler injection --> more general: an ``injectLoop`` pragma - 'writes: []' effect; track reads/writes for shared types -- use the effect system for static deadlock prevention and race detection - ``~`` operator for effects - introduce 'noaddr' pragma to prevent taking the address of a location; this is very handy to prevent aliasing of global data