ARC: implemented a simple cycle detector

This commit is contained in:
Araq 2019-11-28 09:32:14 +01:00 • committed by Andreas Rumpf
commit 2dea920379
4 changed files with 44 additions and 6 deletions

View file

@ -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)

View file

@ -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 """

View file

@ -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",

View file

@ -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