ARC: implemented a simple cycle detector
This commit is contained in:
parent
7e747d11c6
commit
2dea920379
4 changed files with 44 additions and 6 deletions
|
|
@ -19,6 +19,8 @@ import
|
||||||
strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
|
strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
|
||||||
lineinfos, parampatterns, sighashes
|
lineinfos, parampatterns, sighashes
|
||||||
|
|
||||||
|
from trees import exprStructuralEquivalent
|
||||||
|
|
||||||
type
|
type
|
||||||
Con = object
|
Con = object
|
||||||
owner: PSym
|
owner: PSym
|
||||||
|
|
@ -461,6 +463,35 @@ proc isCursor(n: PNode): bool =
|
||||||
else:
|
else:
|
||||||
result = false
|
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 =
|
proc p(n: PNode; c: var Con; consumed = false): PNode =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkCallKinds:
|
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:
|
if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym:
|
||||||
result = newNodeI(nkEmpty, n.info)
|
result = newNodeI(nkEmpty, n.info)
|
||||||
else:
|
else:
|
||||||
|
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
|
||||||
|
cycleCheck(n, c)
|
||||||
result = moveOrCopy(n[0], n[1], c)
|
result = moveOrCopy(n[0], n[1], c)
|
||||||
else:
|
else:
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
|
|
|
||||||
|
|
@ -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}):
|
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")
|
localError(c.graph.config, dep.info, "internal error: up references do not agree")
|
||||||
|
|
||||||
if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
|
when false:
|
||||||
localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
|
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:
|
else:
|
||||||
let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
|
let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
|
||||||
result.typ = fieldType
|
result.typ = fieldType
|
||||||
if c.graph.config.selectedGC == gcDestructors:
|
when false:
|
||||||
result.flags.incl sfCursor
|
if c.graph.config.selectedGC == gcDestructors:
|
||||||
|
result.flags.incl sfCursor
|
||||||
rawAddField(obj, result)
|
rawAddField(obj, result)
|
||||||
|
|
||||||
discard """
|
discard """
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ type
|
||||||
warnEachIdentIsTuple,
|
warnEachIdentIsTuple,
|
||||||
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
|
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
|
||||||
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
|
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
|
||||||
warnInconsistentSpacing, warnCaseTransition, warnUser,
|
warnInconsistentSpacing, warnCaseTransition, warnCycleCreated, warnUser,
|
||||||
hintSuccess, hintSuccessX, hintCC,
|
hintSuccess, hintSuccessX, hintCC,
|
||||||
hintLineTooLong, hintXDeclaredButNotUsed,
|
hintLineTooLong, hintXDeclaredButNotUsed,
|
||||||
hintConvToBaseNotNeeded,
|
hintConvToBaseNotNeeded,
|
||||||
|
|
@ -94,6 +94,7 @@ const
|
||||||
warnResultShadowed: "Special variable 'result' is shadowed.",
|
warnResultShadowed: "Special variable 'result' is shadowed.",
|
||||||
warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
|
warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
|
||||||
warnCaseTransition: "Potential object case transition, instantiate new object instead",
|
warnCaseTransition: "Potential object case transition, instantiate new object instead",
|
||||||
|
warnCycleCreated: "$1",
|
||||||
warnUser: "$1",
|
warnUser: "$1",
|
||||||
hintSuccess: "operation successful: $#",
|
hintSuccess: "operation successful: $#",
|
||||||
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
|
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
|
||||||
|
|
@ -139,7 +140,7 @@ const
|
||||||
"UnsafeCode", "UnusedImport", "EachIdentIsTuple",
|
"UnsafeCode", "UnusedImport", "EachIdentIsTuple",
|
||||||
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
|
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
|
||||||
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
|
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
|
||||||
"Spacing", "CaseTransition", "User"]
|
"Spacing", "CaseTransition", "CycleCreated", "User"]
|
||||||
|
|
||||||
HintsToStr* = [
|
HintsToStr* = [
|
||||||
"Success", "SuccessX", "CC", "LineTooLong",
|
"Success", "SuccessX", "CC", "LineTooLong",
|
||||||
|
|
|
||||||
|
|
@ -341,6 +341,8 @@ when defined(windows) or defined(nimdoc):
|
||||||
addr lpNumberOfBytesTransferred, addr lpCompletionKey,
|
addr lpNumberOfBytesTransferred, addr lpCompletionKey,
|
||||||
cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
|
cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
|
||||||
result = true
|
result = true
|
||||||
|
when defined(gcDestructors):
|
||||||
|
GC_ref(customOverlapped)
|
||||||
|
|
||||||
# http://stackoverflow.com/a/12277264/492186
|
# http://stackoverflow.com/a/12277264/492186
|
||||||
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
|
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue