Skip to content

Commit

Permalink
fixes a critical GC safety inference bug (#10615)
Browse files Browse the repository at this point in the history
* fixes a critical GC safety inference bug
* make nimsuggest compile again
* make Nimble compile again
  • Loading branch information
Araq committed Mar 5, 2019
1 parent aed8766 commit c86b1fb
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 34 deletions.
7 changes: 4 additions & 3 deletions compiler/docgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ proc attachToType(d: PDoc; p: PSym): PSym =

template declareClosures =
proc compilerMsgHandler(filename: string, line, col: int,
msgKind: rst.MsgKind, arg: string) {.procvar.} =
msgKind: rst.MsgKind, arg: string) {.procvar, gcsafe.} =
# translate msg kind:
var k: TMsgKind
case msgKind
Expand All @@ -81,9 +81,10 @@ template declareClosures =
of mwUnknownSubstitution: k = warnUnknownSubstitutionX
of mwUnsupportedLanguage: k = warnLanguageXNotSupported
of mwUnsupportedField: k = warnFieldXNotSupported
globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
{.gcsafe.}:
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
if result.len == 0:
result = getCurrentDir() / s
Expand Down
15 changes: 8 additions & 7 deletions compiler/msgs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -324,14 +324,15 @@ proc log*(s: string) {.procvar.} =
f.writeLine(s)
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 stackTraceAvailable() and isNil(conf.writelnHook):
writeStackTrace()
else:
styledMsgWriteln(fgRed, "No stack traceback available\n" &
"To create a stacktrace, rerun compilation with ./koch temp " &
conf.command & " <file>")
{.gcsafe.}:
if stackTraceAvailable() and isNil(conf.writelnHook):
writeStackTrace()
else:
styledMsgWriteln(fgRed, "No stack traceback available\n" &
"To create a stacktrace, rerun compilation with ./koch temp " &
conf.command & " <file>")
quit 1

proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) =
Expand Down
4 changes: 2 additions & 2 deletions compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,9 @@ type
suggestVersion*: int
suggestMaxResults*: int
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;
severity: Severity) {.closure.}
severity: Severity) {.closure, gcsafe.}
cppCustomNamespace*: string

proc hcrOn*(conf: ConfigRef): bool = return optHotCodeReloading in conf.globalOptions
Expand Down
26 changes: 15 additions & 11 deletions compiler/ropes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,13 @@ proc newRope(data: string = ""): Rope =
result.L = -len(data)
result.data = data

var
cache: array[0..2048*2 - 1, Rope] # XXX Global here!
when not compileOption("threads"):
var
cache: array[0..2048*2 - 1, Rope]

proc resetRopeCache* =
for i in low(cache)..high(cache):
cache[i] = nil
proc resetRopeCache* =
for i in low(cache)..high(cache):
cache[i] = nil

proc ropeInvariant(r: Rope): bool =
if r == nil:
Expand All @@ -107,13 +108,16 @@ var gCacheMisses* = 0
var gCacheIntTries* = 0

proc insertInCache(s: string): Rope =
inc gCacheTries
var h = hash(s) and high(cache)
result = cache[h]
if isNil(result) or result.data != s:
inc gCacheMisses
when declared(cache):
inc gCacheTries
var h = hash(s) and high(cache)
result = cache[h]
if isNil(result) or result.data != s:
inc gCacheMisses
result = newRope(s)
cache[h] = result
else:
result = newRope(s)
cache[h] = result

proc rope*(s: string): Rope =
## Converts a string to a rope.
Expand Down
22 changes: 13 additions & 9 deletions compiler/sempass2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,17 @@ proc cstringCheck(tracked: PEffects; n: PNode) =
message(tracked.config, n.info, warnUnsafeCode, renderTree(n))

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
of nkSym:
useVar(tracked, n)
Expand Down Expand Up @@ -764,18 +775,11 @@ proc track(tracked: PEffects, n: PNode) =
propagateEffects(tracked, n, a.sym)
elif isIndirectCall(a, tracked.owner):
assumeTheWorst(tracked, n, op)
gcsafeAndSideeffectCheck()
else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n)
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)
gcsafeAndSideeffectCheck()
if a.kind != nkSym or a.sym.magic != mNBindSym:
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}:
Expand Down
4 changes: 2 additions & 2 deletions lib/packages/docutils/rst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ type
mwUnsupportedField

MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
arg: string) {.closure.} ## what to do in case of an error
FindFileHandler* = proc (filename: string): string {.closure.}
arg: string) {.closure, gcsafe.} ## what to do in case of an error
FindFileHandler* = proc (filename: string): string {.closure, gcsafe.}

const
messages: array[MsgKind, string] = [
Expand Down
21 changes: 21 additions & 0 deletions tests/effects/tgcsafe3.nim
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit c86b1fb

Please sign in to comment.