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:
Andreas Rumpf 2019-03-05 19:54:44 +01:00 • committed by GitHub
commit c86b1fbcac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 65 additions and 34 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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] = [

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