diff options
author | Araq <rumpf_a@web.de> | 2014-04-20 14:00:04 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2014-04-20 14:00:04 +0200 |
commit | be6474af638b72aabeb70cfc5f477cc5fb7af0ce (patch) | |
tree | f25002ea96fcfbd69997e22208c55eb3930eeaf0 | |
parent | 39e4e3f20570e804e3059574eafe8f78b2d8a9df (diff) | |
download | Nim-be6474af638b72aabeb70cfc5f477cc5fb7af0ce.tar.gz |
removed flawed thread analysis pass
-rw-r--r-- | compiler/ast.nim | 8 | ||||
-rw-r--r-- | compiler/commands.nim | 2 | ||||
-rw-r--r-- | compiler/options.nim | 3 | ||||
-rw-r--r-- | compiler/passes.nim | 5 | ||||
-rw-r--r-- | compiler/sem.nim | 10 | ||||
-rw-r--r-- | compiler/semdata.nim | 2 | ||||
-rw-r--r-- | compiler/seminst.nim | 3 | ||||
-rw-r--r-- | compiler/sempass2.nim | 2 | ||||
-rw-r--r-- | compiler/semthreads.nim | 390 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 2 | ||||
-rw-r--r-- | compiler/types.nim | 2 | ||||
-rw-r--r-- | doc/advopt.txt | 1 | ||||
-rw-r--r-- | tests/actiontable/tactiontable2.nim | 2 | ||||
-rw-r--r-- | tests/bind/tnicerrorforsymchoice.nim | 8 | ||||
-rw-r--r-- | tests/closure/tinvalidclosure.nim | 2 | ||||
-rw-r--r-- | tests/threads/tthreadanalysis2.nim | 6 | ||||
-rw-r--r-- | tests/threads/tthreadanalysis3.nim | 51 |
17 files changed, 23 insertions, 476 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index e575f317d..97f48b253 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1311,6 +1311,10 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType = result = t while result.kind in kinds: result = lastSon(result) +proc isGCedMem*(t: PType): bool {.inline.} = + result = t.kind in {tyString, tyRef, tySequence} or + t.kind == tyProc and t.callConv == ccClosure + proc propagateToOwner*(owner, elem: PType) = const HaveTheirOwnEmpty = {tySequence, tySet} owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta}) @@ -1331,9 +1335,7 @@ proc propagateToOwner*(owner, elem: PType) = owner.flags.incl tfHasMeta if owner.kind != tyProc: - if elem.kind in {tyString, tyRef, tySequence} or - elem.kind == tyProc and elem.callConv == ccClosure or - tfHasGCedMem in elem.flags: + if elem.isGCedMem or tfHasGCedMem in elem.flags: owner.flags.incl tfHasGCedMem proc rawAddSon*(father, son: PType) = diff --git a/compiler/commands.nim b/compiler/commands.nim index 8339219ed..53e05aea1 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -168,7 +168,6 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = of "forcebuild", "f": result = contains(gGlobalOptions, optForceFullMake) of "warnings", "w": result = contains(gOptions, optWarns) of "hints": result = contains(gOptions, optHints) - of "threadanalysis": result = contains(gGlobalOptions, optThreadAnalysis) of "stacktrace": result = contains(gOptions, optStackTrace) of "linetrace": result = contains(gOptions, optLineTrace) of "debugger": result = contains(gOptions, optEndb) @@ -330,7 +329,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "warning": processSpecificNote(arg, wWarning, pass, info) of "hint": processSpecificNote(arg, wHint, pass, info) of "hints": processOnOffSwitch({optHints}, arg, pass, info) - of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info) of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info) of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info) of "debugger": diff --git a/compiler/options.nim b/compiler/options.nim index f05354666..211eee360 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -60,7 +60,6 @@ type # please make sure we have under 32 options optContext, # ideTools: 'context' optDef, # ideTools: 'def' optUsages, # ideTools: 'usages' - optThreadAnalysis, # thread analysis pass optTaintMode, # taint mode turned on optTlsEmulation, # thread var emulation turned on optGenIndex # generate index file for documentation; @@ -95,7 +94,7 @@ var optBoundsCheck, optOverflowCheck, optAssert, optWarns, optHints, optStackTrace, optLineTrace, optPatterns, optNilCheck} - gGlobalOptions*: TGlobalOptions = {optThreadAnalysis} + gGlobalOptions*: TGlobalOptions = {} gExitcode*: int8 gCmd*: TCommands = cmdNone # the command gSelectedGC* = gcRefc # the selected GC diff --git a/compiler/passes.nim b/compiler/passes.nim index 3dc31e7ac..66a1a4954 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -13,7 +13,7 @@ import strutils, lists, options, ast, astalgo, llstream, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, - nimsets, syntaxes, times, rodread, semthreads, idgen + nimsets, syntaxes, times, rodread, idgen type TPassContext* = object of TObject # the pass's context @@ -74,7 +74,8 @@ proc astNeeded*(s: PSym): bool = ({sfCompilerProc, sfCompileTime} * s.flags == {}) and (s.typ.callConv != ccInline) and (s.ast.sons[genericParamsPos].kind == nkEmpty): - result = semthreads.needsGlobalAnalysis() + result = false + # XXX this doesn't really make sense with excessive CTFE else: result = true diff --git a/compiler/sem.nim b/compiler/sem.nim index e7bff0665..7d129caf4 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -14,7 +14,7 @@ import wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math, magicsys, parser, nversion, nimsets, semfold, importer, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, - semthreads, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, + intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, pretty, semmacrosanity # implementation @@ -415,12 +415,7 @@ proc myProcess(context: PPassContext, n: PNode): PNode = if getCurrentException() of ESuggestDone: result = nil else: result = ast.emptyNode #if gCmd == cmdIdeTools: findSuggest(c, n) - -proc checkThreads(c: PContext) = - if not needsGlobalAnalysis(): return - for i in 0 .. c.threadEntries.len-1: - semthreads.analyseThreadProc(c.threadEntries[i]) - + proc myClose(context: PPassContext, n: PNode): PNode = var c = PContext(context) closeScope(c) # close module's scope @@ -431,7 +426,6 @@ proc myClose(context: PPassContext, n: PNode): PNode = addCodeForGenerics(c, result) if c.module.ast != nil: result.add(c.module.ast) - checkThreads(c) popOwner() popProcCon(c) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 4cb5ad38c..987a70a41 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -57,7 +57,6 @@ type # can access private object fields instCounter*: int # to prevent endless instantiations - threadEntries*: TSymSeq # list of thread entries to check ambiguousSymbols*: TIntSet # ids of all ambiguous symbols (cannot # store this info in the syms themselves!) inTypeClass*: int # > 0 if we are in a user-defined type class @@ -170,7 +169,6 @@ proc newContext(module: PSym): PContext = append(result.optionStack, newOptionEntry()) result.module = module result.friendModule = module - result.threadEntries = @[] result.converters = @[] result.patterns = @[] result.includedFiles = initIntSet() diff --git a/compiler/seminst.nim b/compiler/seminst.nim index a5149a842..f7d5fa6f8 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -127,9 +127,6 @@ proc sideEffectsCheck(c: PContext, s: PSym) = if {sfNoSideEffect, sfSideEffect} * s.flags == {sfNoSideEffect, sfSideEffect}: localError(s.info, errXhasSideEffects, s.name.s) - elif sfThread in s.flags and semthreads.needsGlobalAnalysis() and - s.ast.sons[genericParamsPos].kind == nkEmpty: - c.threadEntries.add(s) proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, allowMetaTypes = false): PType = diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 72666e47d..295321f4b 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -113,7 +113,7 @@ proc useVar(a: PEffects, n: PNode) = if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind == skVar: when trackGlobals: a.addUse(copyNode(n)) - if tfHasGCedMem in s.typ.flags: + if tfHasGCedMem in s.typ.flags or s.typ.isGCedMem: message(n.info, warnGcUnsafe, renderTree(n)) a.gcUnsafe = true diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim deleted file mode 100644 index d3426ca3e..000000000 --- a/compiler/semthreads.nim +++ /dev/null @@ -1,390 +0,0 @@ -# -# -# The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Semantic analysis that deals with threads: Possible race conditions should -## be reported some day. -## -## -## ======================== -## No heap sharing analysis -## ======================== -## -## The only crucial operation that can violate the heap invariants is the -## write access. The analysis needs to distinguish between 'unknown', 'mine', -## and 'theirs' memory and pointers. Assignments 'whatever <- unknown' are -## invalid, and so are 'theirs <- whatever' but not 'mine <- theirs'. Since -## strings and sequences are heap allocated they are affected too: -## -## .. code-block:: nimrod -## proc p() = -## global = "alloc this string" # ugh! -## -## Thus the analysis is concerned with any type that contains a GC'ed -## reference... -## If the type system would distinguish between 'ref' and '!ref' and threads -## could not have '!ref' as input parameters the analysis could simply need to -## reject any write access to a global variable which contains GC'ed data. -## Thanks to the write barrier of the GC, this is exactly what needs to be -## done! Every write access to a global that contains GC'ed data needs to -## be prevented! Unfortunately '!ref' is not implemented yet... -## -## The assignment target is essential for the algorithm: only -## write access to heap locations and global variables are critical and need -## to be checked. Access via 'var' parameters is no problem to analyse since -## we need the arguments' locations in the analysis. -## -## However, this is tricky: -## -## var x = globalVar # 'x' points to 'theirs' -## while true: -## globalVar = x # NOT OK: 'theirs <- theirs' invalid due to -## # write barrier! -## x = "new string" # ugh: 'x is toUnknown'! -## -## --> Solution: toUnknown is never allowed anywhere! -## -## -## Beware that the same proc might need to be -## analysed multiple times! Oh and watch out for recursion! Recursion is handled -## by a stack of symbols that we are processing, if we come back to the same -## symbol, we have to skip this check (assume no error in the recursive case). -## However this is wrong. We need to check for the particular combination -## of (procsym, threadOwner(arg1), threadOwner(arg2), ...)! - -import - ast, astalgo, strutils, hashes, options, msgs, idents, types, os, - renderer, tables, rodread - -type - TThreadOwner = enum - toUndefined, # not computed yet - toVoid, # no return type - toNil, # cycle in computation or nil: can be overwritten - toTheirs, # some other heap - toMine # mine heap - - TCall = object {.pure.} - callee: PSym # what if callee is an indirect call? - args: seq[TThreadOwner] - - PProcCtx = ref TProcCtx - TProcCtx = object {.pure.} - nxt: PProcCtx # can be stacked - mapping: tables.TTable[int, TThreadOwner] # int = symbol ID - owner: PSym # current owner - -var - computed = tables.initTable[TCall, TThreadOwner]() - -proc hash(c: TCall): THash = - result = hash(c.callee.id) - for a in items(c.args): result = result !& hash(ord(a)) - result = !$result - -proc `==`(a, b: TCall): bool = - if a.callee != b.callee: return - if a.args.len != b.args.len: return - for i in 0..a.args.len-1: - if a.args[i] != b.args[i]: return - result = true - -proc newProcCtx(owner: PSym): PProcCtx = - assert owner != nil - new(result) - result.mapping = tables.initTable[int, TThreadOwner]() - result.owner = owner - -proc analyse(c: PProcCtx, n: PNode): TThreadOwner - -proc analyseSym(c: PProcCtx, n: PNode): TThreadOwner = - var v = n.sym - result = c.mapping[v.id] - if result != toUndefined: return - case v.kind - of skVar, skForVar, skLet, skResult: - result = toNil - if sfGlobal in v.flags: - if sfThread in v.flags: - result = toMine - elif containsGarbageCollectedRef(v.typ): - result = toTheirs - of skTemp: result = toNil - of skConst: result = toMine - of skParam: - result = c.mapping[v.id] - if result == toUndefined: - internalError(n.info, "param not set: " & v.name.s) - else: - result = toNil - c.mapping[v.id] = result - -proc lvalueSym(n: PNode): PNode = - result = n - while result.kind in {nkDotExpr, nkCheckedFieldExpr, - nkBracketExpr, nkDerefExpr, nkHiddenDeref}: - result = result.sons[0] - -proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) = - if owner notin {toNil, toMine, toTheirs}: - internalError(n.info, "writeAccess: " & $owner) - var a = lvalueSym(n) - if a.kind == nkSym: - var v = a.sym - var lastOwner = analyseSym(c, a) - case lastOwner - of toNil: - # fine, toNil can be overwritten - var newOwner: TThreadOwner - if sfGlobal in v.flags: - newOwner = owner - elif containsTyRef(v.typ): - # ``var local = gNode`` --> ok, but ``local`` is theirs! - newOwner = owner - else: - # ``var local = gString`` --> string copy: ``local`` is mine! - newOwner = toMine - # XXX BUG what if the tuple contains both ``tyRef`` and ``tyString``? - c.mapping[v.id] = newOwner - of toVoid, toUndefined: internalError(n.info, "writeAccess") - of toTheirs: message(n.info, warnWriteToForeignHeap) - of toMine: - if lastOwner != owner and owner != toNil: - message(n.info, warnDifferentHeaps) - else: - # we could not backtrack to a concrete symbol, but that's fine: - var lastOwner = analyse(c, n) - case lastOwner - of toNil: discard # fine, toNil can be overwritten - of toVoid, toUndefined: internalError(n.info, "writeAccess") - of toTheirs: message(n.info, warnWriteToForeignHeap) - of toMine: - if lastOwner != owner and owner != toNil: - message(n.info, warnDifferentHeaps) - -proc analyseAssign(c: PProcCtx, le, ri: PNode) = - var y = analyse(c, ri) # read access; ok - writeAccess(c, le, y) - -proc analyseAssign(c: PProcCtx, n: PNode) = - analyseAssign(c, n.sons[0], n.sons[1]) - -proc analyseCall(c: PProcCtx, n: PNode): TThreadOwner = - var prc = n[0].sym - var newCtx = newProcCtx(prc) - var call: TCall - call.callee = prc - newSeq(call.args, n.len-1) - for i in 1..n.len-1: - call.args[i-1] = analyse(c, n[i]) - if not computed.hasKey(call): - computed[call] = toUndefined # we are computing it - let prctyp = skipTypes(prc.typ, abstractInst).n - for i in 1.. prctyp.len-1: - var formal = prctyp.sons[i].sym - newCtx.mapping[formal.id] = call.args[i-1] - pushInfoContext(n.info) - result = analyse(newCtx, prc.getBody) - if prc.ast.sons[bodyPos].kind == nkEmpty and - {sfNoSideEffect, sfThread, sfImportc} * prc.flags == {}: - message(n.info, warnAnalysisLoophole, renderTree(n)) - if result == toUndefined: result = toNil - if prc.typ.sons[0] != nil: - if prc.ast.len > resultPos: - result = newCtx.mapping[prc.ast.sons[resultPos].sym.id] - # if the proc body does not set 'result', nor 'return's something - # explicitely, it returns a binary zero, so 'toNil' is correct: - if result == toUndefined: result = toNil - else: - result = toNil - else: - result = toVoid - computed[call] = result - popInfoContext() - else: - result = computed[call] - if result == toUndefined: - # ugh, cycle! We are already computing it but don't know the - # outcome yet... - if prc.typ.sons[0] == nil: result = toVoid - else: result = toNil - -proc analyseVarTuple(c: PProcCtx, n: PNode) = - if n.kind != nkVarTuple: internalError(n.info, "analyseVarTuple") - var L = n.len - for i in countup(0, L-3): analyseAssign(c, n.sons[i], n.sons[L-1]) - -proc analyseSingleVar(c: PProcCtx, a: PNode) = - if a.sons[2].kind != nkEmpty: analyseAssign(c, a.sons[0], a.sons[2]) - -proc analyseVarSection(c: PProcCtx, n: PNode): TThreadOwner = - for i in countup(0, sonsLen(n) - 1): - var a = n.sons[i] - if a.kind == nkCommentStmt: continue - if a.kind == nkIdentDefs: - #assert(a.sons[0].kind == nkSym); also valid for after - # closure transformation: - analyseSingleVar(c, a) - else: - analyseVarTuple(c, a) - result = toVoid - -proc analyseConstSection(c: PProcCtx, t: PNode): TThreadOwner = - for i in countup(0, sonsLen(t) - 1): - var it = t.sons[i] - if it.kind == nkCommentStmt: continue - if it.kind != nkConstDef: internalError(t.info, "analyseConstSection") - if sfFakeConst in it.sons[0].sym.flags: analyseSingleVar(c, it) - result = toVoid - -template aggregateOwner(result, ana: expr) = - var a = ana # eval once - if result != a: - if result == toNil: result = a - elif a != toNil: message(n.info, warnDifferentHeaps) - -proc analyseArgs(c: PProcCtx, n: PNode, start = 1) = - for i in start..n.len-1: discard analyse(c, n[i]) - -proc analyseOp(c: PProcCtx, n: PNode): TThreadOwner = - if n[0].kind != nkSym or n[0].sym.kind != skProc: - if {tfNoSideEffect, tfThread} * n[0].typ.flags == {}: - message(n.info, warnAnalysisLoophole, renderTree(n)) - result = toNil - else: - var prc = n[0].sym - case prc.magic - of mNone: - if sfSystemModule in prc.owner.flags: - # System module proc does no harm :-) - analyseArgs(c, n) - if prc.typ.sons[0] == nil: result = toVoid - else: result = toNil - else: - result = analyseCall(c, n) - of mNew, mNewFinalize, mNewSeq, mSetLengthStr, mSetLengthSeq, - mAppendSeqElem, mReset, mAppendStrCh, mAppendStrStr: - writeAccess(c, n[1], toMine) - result = toVoid - of mSwap: - var a = analyse(c, n[2]) - writeAccess(c, n[1], a) - writeAccess(c, n[2], a) - result = toVoid - of mIntToStr, mInt64ToStr, mFloatToStr, mBoolToStr, mCharToStr, - mCStrToStr, mStrToStr, mEnumToStr, - mConStrStr, mConArrArr, mConArrT, - mConTArr, mConTT, mSlice, - mRepr, mArrToSeq, mCopyStr, mCopyStrLast, - mNewString, mNewStringOfCap: - analyseArgs(c, n) - result = toMine - else: - # don't recurse, but check args: - analyseArgs(c, n) - if prc.typ.sons[0] == nil: result = toVoid - else: result = toNil - -proc analyse(c: PProcCtx, n: PNode): TThreadOwner = - case n.kind - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, - nkCallStrLit, nkHiddenCallConv: - result = analyseOp(c, n) - of nkAsgn, nkFastAsgn: - analyseAssign(c, n) - result = toVoid - of nkSym: result = analyseSym(c, n) - of nkEmpty, nkNone: result = toVoid - of nkNilLit, nkCharLit..nkFloat64Lit: result = toNil - of nkStrLit..nkTripleStrLit: result = toMine - of nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref: - # field access: - # pointer deref or array access: - result = analyse(c, n.sons[0]) - of nkBind: result = analyse(c, n.sons[0]) - of nkPar, nkCurly, nkBracket, nkRange: - # container construction: - result = toNil # nothing until later - for i in 0..n.len-1: aggregateOwner(result, analyse(c, n[i])) - of nkObjConstr: - if n.typ != nil and containsGarbageCollectedRef(n.typ): - result = toMine - else: - result = toNil # nothing until later - for i in 1..n.len-1: aggregateOwner(result, analyse(c, n[i])) - of nkAddr, nkHiddenAddr: - var a = lvalueSym(n) - if a.kind == nkSym: - result = analyseSym(c, a) - assert result in {toNil, toMine, toTheirs} - if result == toNil: - # assume toMine here for consistency: - c.mapping[a.sym.id] = toMine - result = toMine - else: - # should never really happen: - result = analyse(c, n.sons[0]) - of nkIfExpr: - result = toNil - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] - if it.len == 2: - discard analyse(c, it.sons[0]) - aggregateOwner(result, analyse(c, it.sons[1])) - else: - aggregateOwner(result, analyse(c, it.sons[0])) - of nkStmtListExpr, nkBlockExpr: - var n = if n.kind == nkBlockExpr: n.sons[1] else: n - var L = sonsLen(n) - for i in countup(0, L-2): discard analyse(c, n.sons[i]) - if L > 0: result = analyse(c, n.sons[L-1]) - else: result = toVoid - of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast: - result = analyse(c, n.sons[1]) - of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, - nkChckRange, nkCheckedFieldExpr, nkObjDownConv, - nkObjUpConv: - result = analyse(c, n.sons[0]) - of nkRaiseStmt: - var a = analyse(c, n.sons[0]) - if a != toMine: message(n.info, warnDifferentHeaps) - result = toVoid - of nkVarSection, nkLetSection: result = analyseVarSection(c, n) - of nkConstSection: result = analyseConstSection(c, n) - of nkTypeSection, nkCommentStmt: result = toVoid - of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt, - nkElifBranch, nkElse, nkExceptBranch, nkOfBranch, nkFinally: - for i in 0 .. <n.len: discard analyse(c, n[i]) - result = toVoid - of nkBreakStmt, nkContinueStmt: result = toVoid - of nkReturnStmt, nkDiscardStmt: - if n.sons[0].kind != nkEmpty: result = analyse(c, n.sons[0]) - else: result = toVoid - of nkLambdaKinds, nkClosure: - result = toMine - of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef, - nkConverterDef, nkMacroDef, nkTemplateDef, - nkGotoState, nkState, nkBreakState, nkType, nkIdent: - result = toVoid - of nkExprColonExpr: - result = analyse(c, n.sons[1]) - else: internalError(n.info, "analysis not implemented for: " & $n.kind) - -proc analyseThreadProc*(prc: PSym) = - var c = newProcCtx(prc) - var formals = skipTypes(prc.typ, abstractInst).n - for i in 1 .. formals.len-1: - var formal = formals.sons[i].sym - # the input is copied and belongs to the thread: - c.mapping[formal.id] = toMine - discard analyse(c, prc.getBody) - -proc needsGlobalAnalysis*: bool = - result = gGlobalOptions * {optThreads, optThreadAnalysis} == - {optThreads, optThreadAnalysis} - diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 8b9b7b13b..271a01266 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -19,7 +19,7 @@ proc sharedPtrCheck(info: TLineInfo, t: PType) = if t.sons[0].sym.magic in {mShared, mGuarded}: incl(t.flags, tfShared) if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded) - if tfHasGCedMem in t.flags: + if tfHasGCedMem in t.flags or t.isGCedMem: localError(info, errGenerated, "shared memory may not refer to GC'ed thread local memory") diff --git a/compiler/types.nim b/compiler/types.nim index e0e0162ec..1f266d64f 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -538,7 +538,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = add(prag, "noSideEffect") if tfThread in t.flags: addSep(prag) - add(prag, "thread") + add(prag, "gcsafe") if len(prag) != 0: add(result, "{." & prag & ".}") of tyVarargs, tyIter: result = typeToStr[t.kind] % typeToString(t.sons[0]) diff --git a/doc/advopt.txt b/doc/advopt.txt index f5ff90791..143c465fa 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -63,7 +63,6 @@ Advanced options: --lineDir:on|off generation of #line directive on|off --embedsrc embeds the original source code as comments in the generated output - --threadanalysis:on|off turn thread analysis on|off --tlsEmulation:on|off turn thread local storage emulation on|off --taintMode:on|off turn taint mode on|off --symbolFiles:on|off turn symbol files on|off (experimental) diff --git a/tests/actiontable/tactiontable2.nim b/tests/actiontable/tactiontable2.nim index 878356321..bfeb1c169 100644 --- a/tests/actiontable/tactiontable2.nim +++ b/tests/actiontable/tactiontable2.nim @@ -1,6 +1,6 @@ discard """ line: 21 - errormsg: "invalid type: 'TTable[string, proc (string)]'" + errormsg: "invalid type: 'TTable[string, proc (string){.gcsafe.}]'" """ import tables diff --git a/tests/bind/tnicerrorforsymchoice.nim b/tests/bind/tnicerrorforsymchoice.nim index bc271dcab..4e7a271b3 100644 --- a/tests/bind/tnicerrorforsymchoice.nim +++ b/tests/bind/tnicerrorforsymchoice.nim @@ -1,18 +1,18 @@ discard """ line: 18 - errormsg: "type mismatch: got (proc (TScgi) | proc (PAsyncSocket, PStringTable, string))" + errormsg: "type mismatch: got (proc (TScgi) | proc (PAsyncSocket, PStringTable, string){.gcsafe.})" """ #bug #442 import scgi, sockets, asyncio, strtabs proc handleSCGIRequest[TScgi: TScgiState | PAsyncScgiState](s: TScgi) = - nil + discard proc handleSCGIRequest(client: PAsyncSocket, headers: PStringTable, input: string) = - nil + discard proc test(handle: proc (client: PAsyncSocket, headers: PStringTable, input: string), b: int) = - nil + discard test(handleSCGIRequest) diff --git a/tests/closure/tinvalidclosure.nim b/tests/closure/tinvalidclosure.nim index c06270bfa..06e19df3c 100644 --- a/tests/closure/tinvalidclosure.nim +++ b/tests/closure/tinvalidclosure.nim @@ -1,6 +1,6 @@ discard """ line: 12 - errormsg: "type mismatch: got (proc (int){.closure.})" + errormsg: "type mismatch: got (proc (int){.closure, gcsafe.})" """ proc ugh[T](x: T) {.closure.} = diff --git a/tests/threads/tthreadanalysis2.nim b/tests/threads/tthreadanalysis2.nim index 697e2cb22..bcc09db98 100644 --- a/tests/threads/tthreadanalysis2.nim +++ b/tests/threads/tthreadanalysis2.nim @@ -1,7 +1,7 @@ discard """ file: "tthreadanalysis2.nim" - line: 42 - errormsg: "write to foreign heap" + line: 37 + errormsg: "'threadFunc' is not GC-safe" cmd: "nimrod $target --hints:on --threads:on $options $file" """ @@ -10,7 +10,7 @@ import os var thr: array [0..5, TThread[tuple[a, b: int]]] -proc doNothing() = nil +proc doNothing() = discard type PNode = ref TNode diff --git a/tests/threads/tthreadanalysis3.nim b/tests/threads/tthreadanalysis3.nim deleted file mode 100644 index 3c17fe7e2..000000000 --- a/tests/threads/tthreadanalysis3.nim +++ /dev/null @@ -1,51 +0,0 @@ -discard """ - file: "tthreadanalysis3.nim" - line: 35 - errormsg: "write to foreign heap" - cmd: "nimrod $target --hints:on --threads:on $options $file" -""" - -import os - -var - thr: array [0..5, TThread[tuple[a, b: int]]] - -proc doNothing() = nil - -type - PNode = ref TNode - TNode = object {.pure.} - le, ri: PNode - data: string - -var - root: PNode - -proc buildTree(depth: int): PNode = - if depth == 3: return nil - new(result) - result.le = buildTree(depth-1) - result.ri = buildTree(depth-1) - result.data = $depth - -proc echoLeTree(n: PNode) = - var it = n - while it != nil: - echo it.data - it = it.le - -proc threadFunc(interval: tuple[a, b: int]) {.thread.} = - doNothing() - for i in interval.a..interval.b: - var r = buildTree(i) - echoLeTree(r) # for local data - echoLeTree(root) # and the same for foreign data :-) - -proc main = - root = buildTree(5) - for i in 0..high(thr): - createThread(thr[i], threadFunc, (i*100, i*100+50)) - joinThreads(thr) - -main() - |