fixes a critical GC safety inference bug (#10615)
* fixes a critical GC safety inference bug * make nimsuggest compile again * make Nimble compile again
This commit is contained in:
parent
aed8766e84
commit
c86b1fbcac
7 changed files with 65 additions and 34 deletions
|
|
@ -67,7 +67,7 @@ proc attachToType(d: PDoc; p: PSym): PSym =
|
||||||
|
|
||||||
template declareClosures =
|
template declareClosures =
|
||||||
proc compilerMsgHandler(filename: string, line, col: int,
|
proc compilerMsgHandler(filename: string, line, col: int,
|
||||||
msgKind: rst.MsgKind, arg: string) {.procvar.} =
|
msgKind: rst.MsgKind, arg: string) {.procvar, gcsafe.} =
|
||||||
# translate msg kind:
|
# translate msg kind:
|
||||||
var k: TMsgKind
|
var k: TMsgKind
|
||||||
case msgKind
|
case msgKind
|
||||||
|
|
@ -81,9 +81,10 @@ template declareClosures =
|
||||||
of mwUnknownSubstitution: k = warnUnknownSubstitutionX
|
of mwUnknownSubstitution: k = warnUnknownSubstitutionX
|
||||||
of mwUnsupportedLanguage: k = warnLanguageXNotSupported
|
of mwUnsupportedLanguage: k = warnLanguageXNotSupported
|
||||||
of mwUnsupportedField: k = warnFieldXNotSupported
|
of mwUnsupportedField: k = warnFieldXNotSupported
|
||||||
|
{.gcsafe.}:
|
||||||
globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
|
globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
|
||||||
|
|
||||||
proc docgenFindFile(s: string): string {.procvar.} =
|
proc docgenFindFile(s: string): string {.procvar, gcsafe.} =
|
||||||
result = options.findFile(conf, s).string
|
result = options.findFile(conf, s).string
|
||||||
if result.len == 0:
|
if result.len == 0:
|
||||||
result = getCurrentDir() / s
|
result = getCurrentDir() / s
|
||||||
|
|
|
||||||
|
|
@ -324,8 +324,9 @@ proc log*(s: string) {.procvar.} =
|
||||||
f.writeLine(s)
|
f.writeLine(s)
|
||||||
close(f)
|
close(f)
|
||||||
|
|
||||||
proc quit(conf: ConfigRef; msg: TMsgKind) =
|
proc quit(conf: ConfigRef; msg: TMsgKind) {.gcsafe.} =
|
||||||
if defined(debug) or msg == errInternal or hintStackTrace in conf.notes:
|
if defined(debug) or msg == errInternal or hintStackTrace in conf.notes:
|
||||||
|
{.gcsafe.}:
|
||||||
if stackTraceAvailable() and isNil(conf.writelnHook):
|
if stackTraceAvailable() and isNil(conf.writelnHook):
|
||||||
writeStackTrace()
|
writeStackTrace()
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -249,9 +249,9 @@ type
|
||||||
suggestVersion*: int
|
suggestVersion*: int
|
||||||
suggestMaxResults*: int
|
suggestMaxResults*: int
|
||||||
lastLineInfo*: TLineInfo
|
lastLineInfo*: TLineInfo
|
||||||
writelnHook*: proc (output: string) {.closure.}
|
writelnHook*: proc (output: string) {.closure.} # cannot make this gcsafe yet because of Nimble
|
||||||
structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string;
|
structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string;
|
||||||
severity: Severity) {.closure.}
|
severity: Severity) {.closure, gcsafe.}
|
||||||
cppCustomNamespace*: string
|
cppCustomNamespace*: string
|
||||||
|
|
||||||
proc hcrOn*(conf: ConfigRef): bool = return optHotCodeReloading in conf.globalOptions
|
proc hcrOn*(conf: ConfigRef): bool = return optHotCodeReloading in conf.globalOptions
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,9 @@ proc newRope(data: string = ""): Rope =
|
||||||
result.L = -len(data)
|
result.L = -len(data)
|
||||||
result.data = data
|
result.data = data
|
||||||
|
|
||||||
|
when not compileOption("threads"):
|
||||||
var
|
var
|
||||||
cache: array[0..2048*2 - 1, Rope] # XXX Global here!
|
cache: array[0..2048*2 - 1, Rope]
|
||||||
|
|
||||||
proc resetRopeCache* =
|
proc resetRopeCache* =
|
||||||
for i in low(cache)..high(cache):
|
for i in low(cache)..high(cache):
|
||||||
|
|
@ -107,6 +108,7 @@ var gCacheMisses* = 0
|
||||||
var gCacheIntTries* = 0
|
var gCacheIntTries* = 0
|
||||||
|
|
||||||
proc insertInCache(s: string): Rope =
|
proc insertInCache(s: string): Rope =
|
||||||
|
when declared(cache):
|
||||||
inc gCacheTries
|
inc gCacheTries
|
||||||
var h = hash(s) and high(cache)
|
var h = hash(s) and high(cache)
|
||||||
result = cache[h]
|
result = cache[h]
|
||||||
|
|
@ -114,6 +116,8 @@ proc insertInCache(s: string): Rope =
|
||||||
inc gCacheMisses
|
inc gCacheMisses
|
||||||
result = newRope(s)
|
result = newRope(s)
|
||||||
cache[h] = result
|
cache[h] = result
|
||||||
|
else:
|
||||||
|
result = newRope(s)
|
||||||
|
|
||||||
proc rope*(s: string): Rope =
|
proc rope*(s: string): Rope =
|
||||||
## Converts a string to a rope.
|
## Converts a string to a rope.
|
||||||
|
|
|
||||||
|
|
@ -721,6 +721,17 @@ proc cstringCheck(tracked: PEffects; n: PNode) =
|
||||||
message(tracked.config, n.info, warnUnsafeCode, renderTree(n))
|
message(tracked.config, n.info, warnUnsafeCode, renderTree(n))
|
||||||
|
|
||||||
proc track(tracked: PEffects, n: PNode) =
|
proc track(tracked: PEffects, n: PNode) =
|
||||||
|
template gcsafeAndSideeffectCheck() =
|
||||||
|
if notGcSafe(op) and not importedFromC(a):
|
||||||
|
# and it's not a recursive call:
|
||||||
|
if not (a.kind == nkSym and a.sym == tracked.owner):
|
||||||
|
if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
|
||||||
|
markGcUnsafe(tracked, a)
|
||||||
|
if tfNoSideEffect notin op.flags and not importedFromC(a):
|
||||||
|
# and it's not a recursive call:
|
||||||
|
if not (a.kind == nkSym and a.sym == tracked.owner):
|
||||||
|
markSideEffect(tracked, a)
|
||||||
|
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
useVar(tracked, n)
|
useVar(tracked, n)
|
||||||
|
|
@ -764,18 +775,11 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
propagateEffects(tracked, n, a.sym)
|
propagateEffects(tracked, n, a.sym)
|
||||||
elif isIndirectCall(a, tracked.owner):
|
elif isIndirectCall(a, tracked.owner):
|
||||||
assumeTheWorst(tracked, n, op)
|
assumeTheWorst(tracked, n, op)
|
||||||
|
gcsafeAndSideeffectCheck()
|
||||||
else:
|
else:
|
||||||
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
||||||
mergeTags(tracked, effectList.sons[tagEffects], n)
|
mergeTags(tracked, effectList.sons[tagEffects], n)
|
||||||
if notGcSafe(op) and not importedFromC(a):
|
gcsafeAndSideeffectCheck()
|
||||||
# and it's not a recursive call:
|
|
||||||
if not (a.kind == nkSym and a.sym == tracked.owner):
|
|
||||||
if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
|
|
||||||
markGcUnsafe(tracked, a)
|
|
||||||
if tfNoSideEffect notin op.flags and not importedFromC(a):
|
|
||||||
# and it's not a recursive call:
|
|
||||||
if not (a.kind == nkSym and a.sym == tracked.owner):
|
|
||||||
markSideEffect(tracked, a)
|
|
||||||
if a.kind != nkSym or a.sym.magic != mNBindSym:
|
if a.kind != nkSym or a.sym.magic != mNBindSym:
|
||||||
for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i), a)
|
for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i), a)
|
||||||
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ type
|
||||||
mwUnsupportedField
|
mwUnsupportedField
|
||||||
|
|
||||||
MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
|
MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
|
||||||
arg: string) {.closure.} ## what to do in case of an error
|
arg: string) {.closure, gcsafe.} ## what to do in case of an error
|
||||||
FindFileHandler* = proc (filename: string): string {.closure.}
|
FindFileHandler* = proc (filename: string): string {.closure, gcsafe.}
|
||||||
|
|
||||||
const
|
const
|
||||||
messages: array[MsgKind, string] = [
|
messages: array[MsgKind, string] = [
|
||||||
|
|
|
||||||
21
tests/effects/tgcsafe3.nim
Normal file
21
tests/effects/tgcsafe3.nim
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
discard """
|
||||||
|
errormsg: "'myproc' is not GC-safe as it accesses 'global_proc' which is a global using GC'ed memory"
|
||||||
|
line: 12
|
||||||
|
cmd: "nim $target --hints:on --threads:on $options $file"
|
||||||
|
"""
|
||||||
|
|
||||||
|
var useGcMem = "string here"
|
||||||
|
|
||||||
|
var global_proc: proc(a: string) {.nimcall.} = proc (a: string) =
|
||||||
|
echo useGcMem
|
||||||
|
|
||||||
|
proc myproc(i: int) {.gcsafe.} =
|
||||||
|
when false:
|
||||||
|
if global_proc != nil:
|
||||||
|
echo "a"
|
||||||
|
if isNil(global_proc):
|
||||||
|
return
|
||||||
|
|
||||||
|
global_proc("ho")
|
||||||
|
|
||||||
|
myproc(0)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue