diff options
author | Araq <rumpf_a@web.de> | 2019-04-12 13:05:57 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2019-04-12 13:28:12 +0200 |
commit | 572735bbfab2ea13fac0dde21d4e61a3fcfd8309 (patch) | |
tree | 482ff92b58433ffa579296f38d7dbf911f85a58b | |
parent | 041d15392aaf732665abab290f0cf5993d909efc (diff) | |
download | Nim-572735bbfab2ea13fac0dde21d4e61a3fcfd8309.tar.gz |
fixes #11004
-rw-r--r-- | compiler/liftdestructors.nim | 132 | ||||
-rw-r--r-- | tests/destructor/tnewruntime_strutils.nim | 7 |
2 files changed, 79 insertions, 60 deletions
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 9ab6be52d..8c89b2dad 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -27,9 +27,11 @@ type recurse: bool c: PContext -proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) -proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; - info: TLineInfo): PSym {.discardable.} +proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) +proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp; + info: TLineInfo): PSym + +proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo) proc at(a, i: PNode, elemType: PType): PNode = result = newNodeI(nkBracketExpr, a.info, 2) @@ -37,10 +39,10 @@ proc at(a, i: PNode, elemType: PType): PNode = result.sons[1] = i result.typ = elemType -proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) = +proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) = for i in 0 ..< t.len: let lit = lowerings.newIntLit(c.graph, x.info, i) - liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i])) + fillBody(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i])) proc dotField(x: PNode, f: PSym): PNode = result = newNodeI(nkDotExpr, x.info, 2) @@ -48,11 +50,11 @@ proc dotField(x: PNode, f: PSym): PNode = result.sons[1] = newSymNode(f, x.info) result.typ = f.typ -proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) = +proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode) = case n.kind of nkSym: let f = n.sym - liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f)) + fillBody(c, f.typ, body, x.dotField(f), y.dotField(f)) of nkNilLit: discard of nkRecCase: if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}: @@ -60,11 +62,11 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) = ## or the value is lost let prevKind = c.kind c.kind = attachedDestructor - liftBodyObj(c, n, body, x, y) + fillBodyObj(c, n, body, x, y) c.kind = prevKind # copy the selector: - liftBodyObj(c, n[0], body, x, y) + fillBodyObj(c, n[0], body, x, y) # we need to generate a case statement: var caseStmt = newNodeI(nkCaseStmt, c.info) # XXX generate 'if' that checks same branches @@ -78,13 +80,13 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) = let L = branch.len branch.sons[L-1] = newNodeI(nkStmtList, c.info) - liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y) + fillBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y) if branch.sons[L-1].len == 0: inc emptyBranches caseStmt.add(branch) if emptyBranches != n.len-1: body.add(caseStmt) of nkRecList: - for t in items(n): liftBodyObj(c, t, body, x, y) + for t in items(n): fillBodyObj(c, t, body, x, y) else: illFormedAstLocal(n, c.graph.config) @@ -150,7 +152,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; else: op = field if op == nil: - op = liftBody(c.c, t, c.kind, c.info) + op = produceSym(c.c, t, c.kind, c.info) if sfError in op.flags: incl c.fn.flags, sfError else: @@ -172,7 +174,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool = var op = t.destructor if op == nil and useNoGc(c, t): - op = liftBody(c.c, t, attachedDestructor, c.info) + op = produceSym(c.c, t, attachedDestructor, c.info) doAssert op != nil doAssert op == t.destructor @@ -280,12 +282,12 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) = let i = declareCounter(c, body, firstOrd(c.graph.config, t)) let whileLoop = genWhileLoop(c, i, x) let elemType = t.lastSon - liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType), + fillBody(c, elemType, whileLoop.sons[1], x.at(i, elemType), y.at(i, elemType)) addIncStmt(c, whileLoop.sons[1], i) body.add whileLoop -proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = +proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind of attachedAsgn, attachedDeepCopy: # we generate: @@ -306,17 +308,31 @@ proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = forallElements(c, t, body, x, y) body.add genBuiltin(c.graph, mDestroy, "destroy", x) -proc strOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = +proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = + createTypeBoundOps(c.c, t, body.info) + case c.kind + of attachedAsgn, attachedDeepCopy: + doAssert t.assignment != nil + body.add newAsgnCall(c.graph, t.assignment, x, y) + of attachedSink: + # we always inline the move for better performance: + let moveCall = genBuiltin(c.graph, mMove, "move", x) + moveCall.add y + doAssert t.destructor != nil + moveCall.add destructorCall(c.graph, t.destructor, x) + body.add moveCall + # alternatively we could do this: + when false: + doAssert t.sink != nil + body.add newAsgnCall(c.graph, t.sink, x, y) + of attachedDestructor: + doAssert t.destructor != nil + body.add destructorCall(c.graph, t.destructor, x) + +proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind of attachedAsgn, attachedDeepCopy: body.add callCodegenProc(c.graph, "nimAsgnStrV2", c.info, genAddr(c.graph, x), y) - # we generate: - # setLen(dest, y.len) - # var i = 0 - # while i < y.len: dest[i] = y[i]; inc(i) - # This is usually more efficient than a destroy/create pair. - #body.add setLenStrCall(c.graph, x, y) - #forallElements(c, t, body, x, y) of attachedSink: let moveCall = genBuiltin(c.graph, mMove, "move", x) moveCall.add y @@ -345,7 +361,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = var actions = newNodeI(nkStmtList, c.info) let elemType = t.lastSon - #liftBodyAux(c, elemType, actions, genDeref(x), genDeref(y)) + #fillBody(c, elemType, actions, genDeref(x), genDeref(y)) #var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x) if isFinal(elemType): @@ -406,7 +422,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, xx, actions) of attachedDeepCopy: assert(false, "cannot happen") -proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = +proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind of tyNone, tyEmpty, tyVoid: discard of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, @@ -442,7 +458,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = defaultOp(c, t, body, x, y) of tySequence: if useNoGc(c, t): - seqOp(c, t, body, x, y) + useSeqOrStrOp(c, t, body, x, y) elif c.graph.config.selectedGC == gcDestructors: # note that tfHasAsgn is propagated so we need the check on # 'selectedGC' here to determine if we have the new runtime. @@ -455,19 +471,19 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = defaultOp(c, t, body, x, y) of tyString: if useNoGc(c, t): - strOp(c, t, body, x, y) + useSeqOrStrOp(c, t, body, x, y) elif tfHasAsgn in t.flags: discard considerUserDefinedOp(c, t, body, x, y) else: defaultOp(c, t, body, x, y) of tyObject: if not considerUserDefinedOp(c, t, body, x, y): - liftBodyObj(c, t.n, body, x, y) + fillBodyObj(c, t.n, body, x, y) of tyDistinct: if not considerUserDefinedOp(c, t, body, x, y): - liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y) + fillBody(c, t.sons[0].skipTypes(skipPtrs), body, x, y) of tyTuple: - liftBodyTup(c, t, body, x, y) + fillBodyTup(c, t, body, x, y) of tyVarargs, tyOpenArray: localError(c.graph.config, c.info, "cannot copy openArray") of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, @@ -478,45 +494,40 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = discard of tyVar, tyLent: if c.kind != attachedDestructor: - liftBodyAux(c, lastSon(t), body, x, y) + fillBody(c, lastSon(t), body, x, y) of tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink: - liftBodyAux(c, lastSon(t), body, x, y) + fillBody(c, lastSon(t), body, x, y) -proc liftBodyDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = +proc produceSymDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = assert typ.kind == tyDistinct let baseType = typ[0] case kind of attachedAsgn: if baseType.assignment == nil: - discard liftBody(c, baseType, kind, info) + discard produceSym(c, baseType, kind, info) typ.assignment = baseType.assignment result = typ.assignment of attachedSink: if baseType.sink == nil: - discard liftBody(c, baseType, kind, info) + discard produceSym(c, baseType, kind, info) typ.sink = baseType.sink result = typ.sink of attachedDeepCopy: if baseType.deepCopy == nil: - discard liftBody(c, baseType, kind, info) + discard produceSym(c, baseType, kind, info) typ.deepCopy = baseType.deepCopy result = typ.deepCopy of attachedDestructor: if baseType.destructor == nil: - discard liftBody(c, baseType, kind, info) + discard produceSym(c, baseType, kind, info) typ.destructor = baseType.destructor result = typ.destructor -proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; +proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = if typ.kind == tyDistinct: - return liftBodyDistinctType(c, typ, kind, info) - when false: - var typ = typ - if c.config.selectedGC == gcDestructors and typ.kind == tySequence: - # use the canonical type to access the =sink and =destroy etc. - typ = c.graph.sysTypes[tySequence] + return produceSymDistinctType(c, typ, kind, info) var a: TLiftCtx a.info = info @@ -552,7 +563,18 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; of attachedDeepCopy: typ.deepCopy = result of attachedDestructor: typ.destructor = result - liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src)) + var tk: TTypeKind + if optNimV2 in c.graph.config.globalOptions: + tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind + else: + tk = tyNone # no special casing for strings and seqs + case tk + of tySequence: + fillSeqOp(a, typ, body, newSymNode(dest).newDeref, newSymNode(src)) + of tyString: + fillStrOp(a, typ, body, newSymNode(dest).newDeref, newSymNode(src)) + else: + fillBody(a, typ, body, newSymNode(dest).newDeref, newSymNode(src)) var n = newNodeI(nkProcDef, info, bodyPos+1) for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info) @@ -563,16 +585,6 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; incl result.flags, sfFromGeneric incl result.flags, sfGeneratedOp -proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym = - let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) - result = t.assignment - if result.isNil: - result = liftBody(c, t, attachedAsgn, info) - -proc overloadedAsgn(c: PContext; dest, src: PNode): PNode = - let a = getAsgnOrLiftBody(c, dest.typ, dest.info) - result = newAsgnCall(c.graph, a, dest, src) - template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = discard "now a nop" @@ -581,7 +593,7 @@ proc patchBody(c: PContext; n: PNode; info: TLineInfo) = if n[0].kind == nkSym and n[0].sym.magic == mDestroy: let t = n[1].typ.skipTypes(abstractVar) if t.destructor == nil: - liftBody(c, t, attachedDestructor, info) + discard produceSym(c, t, attachedDestructor, info) if t.destructor != nil: if t.destructor.ast[genericParamsPos].kind != nkEmpty: @@ -600,7 +612,7 @@ template inst(field, t) = proc isTrival(s: PSym): bool {.inline.} = s == nil or s.ast[bodyPos].len == 0 -proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo) = +proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) = ## In the semantic pass this is called in strategic places ## to ensure we lift assignment, destructors and moves properly. ## The later 'injectdestructors' pass depends on it. @@ -625,15 +637,15 @@ proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo) = let typ = canon.skipTypes({tyGenericInst, tyAlias}) # we generate the destructor first so that other operators can depend on it: if typ.destructor == nil: - liftBody(c, typ, attachedDestructor, info) + discard produceSym(c, typ, attachedDestructor, info) else: inst(typ.destructor, typ) if typ.assignment == nil: - liftBody(c, typ, attachedAsgn, info) + discard produceSym(c, typ, attachedAsgn, info) else: inst(typ.assignment, typ) if typ.sink == nil: - liftBody(c, typ, attachedSink, info) + discard produceSym(c, typ, attachedSink, info) else: inst(typ.sink, typ) diff --git a/tests/destructor/tnewruntime_strutils.nim b/tests/destructor/tnewruntime_strutils.nim index 80ed1757e..5b8684354 100644 --- a/tests/destructor/tnewruntime_strutils.nim +++ b/tests/destructor/tnewruntime_strutils.nim @@ -8,6 +8,11 @@ import strutils, os import core / allocators import system / ansi_c +# bug #11004 +proc retTuple(): (seq[int], int) = + # XXX this doesn't allocate yet but probably it should + return (@[1], 1) + proc nonStaticTests = doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000" when not defined(js): @@ -177,6 +182,8 @@ proc staticTests = doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "] doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"] + discard retTuple() + nonStaticTests() staticTests() |