diff options
author | Araq <rumpf_a@web.de> | 2019-11-28 09:32:14 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2019-11-28 23:00:34 +0100 |
commit | 2dea9203791996f1c946c8f4708dc8ca5342180b (patch) | |
tree | a611a979b3b7f323fb1713a117a9afdda16b4f19 /compiler | |
parent | 7e747d11c66405f08cc7c69e5afc18348663275e (diff) | |
download | Nim-2dea9203791996f1c946c8f4708dc8ca5342180b.tar.gz |
ARC: implemented a simple cycle detector
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/injectdestructors.nim | 33 | ||||
-rw-r--r-- | compiler/lambdalifting.nim | 10 | ||||
-rw-r--r-- | compiler/lineinfos.nim | 5 |
3 files changed, 42 insertions, 6 deletions
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 8c7170577..7fc2ccd78 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -19,6 +19,8 @@ import strutils, options, dfa, lowerings, tables, modulegraphs, msgs, lineinfos, parampatterns, sighashes +from trees import exprStructuralEquivalent + type Con = object owner: PSym @@ -461,6 +463,35 @@ proc isCursor(n: PNode): bool = else: result = false +proc cycleCheck(n: PNode; c: var Con) = + if c.graph.config.selectedGC != gcDestructors: return + var value = n[1] + if value.kind == nkClosure: + value = value[1] + if value.kind == nkNilLit: return + let destTyp = n[0].typ.skipTypes(abstractInst) + if destTyp.kind != tyRef and not (destTyp.kind == tyProc and destTyp.callConv == ccClosure): + return + + var x = n[0] + var field: PNode = nil + while true: + if x.kind == nkDotExpr: + if field == nil: field = x[1] + x = x[0] + elif x.kind in {nkBracketExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref}: + x = x[0] + else: + break + if exprStructuralEquivalent(x, value, strictSymEquality = true): + let msg = + if field != nil: + "'$#' creates an uncollectable ref cycle; annotate '$#' with .cursor" % [$n, $field] + else: + "'$#' creates an uncollectable ref cycle" % [$n] + message(c.graph.config, n.info, warnCycleCreated, msg) + break + proc p(n: PNode; c: var Con; consumed = false): PNode = case n.kind of nkCallKinds: @@ -544,6 +575,8 @@ proc p(n: PNode; c: var Con; consumed = false): PNode = if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym: result = newNodeI(nkEmpty, n.info) else: + if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}: + cycleCheck(n, c) result = moveOrCopy(n[0], n[1], c) else: result = copyNode(n) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 336d6c314..ea19097b8 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -366,13 +366,15 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = if upField.typ.skipTypes({tyOwned, tyRef, tyPtr}) != fieldType.skipTypes({tyOwned, tyRef, tyPtr}): localError(c.graph.config, dep.info, "internal error: up references do not agree") - if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags: - localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor") + when false: + if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags: + localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor") else: let result = newSym(skField, upIdent, obj.owner, obj.owner.info) result.typ = fieldType - if c.graph.config.selectedGC == gcDestructors: - result.flags.incl sfCursor + when false: + if c.graph.config.selectedGC == gcDestructors: + result.flags.incl sfCursor rawAddField(obj, result) discard """ diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index d315f4d33..479ed2f75 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -37,7 +37,7 @@ type warnEachIdentIsTuple, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, - warnInconsistentSpacing, warnCaseTransition, warnUser, + warnInconsistentSpacing, warnCaseTransition, warnCycleCreated, warnUser, hintSuccess, hintSuccessX, hintCC, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, @@ -94,6 +94,7 @@ const warnResultShadowed: "Special variable 'result' is shadowed.", warnInconsistentSpacing: "Number of spaces around '$#' is not consistent", warnCaseTransition: "Potential object case transition, instantiate new object instead", + warnCycleCreated: "$1", warnUser: "$1", hintSuccess: "operation successful: $#", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)", @@ -139,7 +140,7 @@ const "UnsafeCode", "UnusedImport", "EachIdentIsTuple", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "GcMem", "Destructor", "LockLevel", "ResultShadowed", - "Spacing", "CaseTransition", "User"] + "Spacing", "CaseTransition", "CycleCreated", "User"] HintsToStr* = [ "Success", "SuccessX", "CC", "LineTooLong", |