bugfix: 'set' overloadable; further steps for multi threading support

This commit is contained in:
Araq 2011-07-08 01:29:15 +02:00
commit 99bcc233cd
34 changed files with 934 additions and 459 deletions

View file

@ -57,7 +57,7 @@ type
nkStrLit, # a string literal ""
nkRStrLit, # a raw string literal r""
nkTripleStrLit, # a triple string literal """
nkMetaNode, # difficult to explan; represents itself
nkMetaNode, # difficult to explain; represents itself
# (used for macros)
nkNilLit, # the nil literal
# end of atoms
@ -217,7 +217,7 @@ type
sfDiscriminant, # field is a discriminant in a record/object
sfDeprecated, # symbol is deprecated
sfInClosure, # variable is accessed by a closure
sfTypeCheck, # wether macro parameters should be type checked
sfThread, # proc will run as a thread
sfCompileTime, # proc can be evaluated at compile time
sfThreadVar, # variable is a thread variable
sfMerge, # proc can be merged with itself
@ -274,7 +274,8 @@ type
tfFinal, # is the object final?
tfAcyclic, # type is acyclic (for GC optimization)
tfEnumHasHoles, # enum cannot be mapped into a range
tfShallow # type can be shallow copied on assignment
tfShallow, # type can be shallow copied on assignment
tfThread # proc type is marked as ``thread``
TTypeFlags* = set[TTypeFlag]
@ -310,7 +311,7 @@ type
TMagic* = enum # symbols that require compiler magic:
mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mIs,
mEcho, mCreateThread, mShallowCopy,
mEcho, mShallowCopy,
mUnaryLt, mSucc,
mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,

View file

@ -1452,7 +1452,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
mInSet:
genSetOp(p, e, d, op)
of mCreateThread: genCall(p, e, d)
of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit:
var opr = e.sons[0].sym
if lfNoDecl notin opr.loc.flags:

View file

@ -10,24 +10,23 @@
## Thread var support for crappy architectures that lack native support for
## thread local storage.
proc emulatedThreadVars(): bool {.inline.} =
result = optThreads in gGlobalOptions
# NOW: Use the work-around everywhere, because it should be faster anyway.
#platform.OS[targetOS].props.contains(ospLacksThreadVars)
proc AccessThreadLocalVar(p: BProc, s: PSym) =
if optThreads in gGlobalOptions:
if platform.OS[targetOS].props.contains(ospLacksThreadVars):
if not p.ThreadVarAccessed:
p.ThreadVarAccessed = true
p.module.usesThreadVars = true
appf(p.s[cpsLocals], "NimThreadVars* NimTV;$n")
appcg(p, cpsInit, "NimTV=(NimThreadVars*)#GetThreadLocalVars();$n")
if emulatedThreadVars() and not p.ThreadVarAccessed:
p.ThreadVarAccessed = true
p.module.usesThreadVars = true
appf(p.s[cpsLocals], "NimThreadVars* NimTV;$n")
appcg(p, cpsInit, "NimTV=(NimThreadVars*)#GetThreadLocalVars();$n")
var
nimtv: PRope # nimrod thread vars
nimtvDeps: seq[PType] = @[]
nimtvDeclared = initIntSet()
proc emulatedThreadVars(): bool {.inline.} =
result = optThreads in gGlobalOptions and
platform.OS[targetOS].props.contains(ospLacksThreadVars)
proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
if emulatedThreadVars():
# we gather all thread locals var into a struct; we need to allocate

View file

@ -94,7 +94,7 @@ type
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored,
warnXisPassedToProcVar, warnDerefDeprecated, warnAnalysisLoophole,
warnDifferentHeaps,
warnDifferentHeaps, warnWriteToForeignHeap,
warnUser,
hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
@ -328,7 +328,8 @@ const
warnXisPassedToProcVar: "\'$1\' is passed to a procvar; deprecated [XisPassedToProcVar]",
warnDerefDeprecated: "p^ is deprecated; use p[] instead [DerefDeprecated]",
warnAnalysisLoophole: "thread analysis incomplete due to unkown call '$1' [AnalysisLoophole]",
warnDifferentHeaps: "possible inconsistency of thread local heaps",
warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]",
warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]",
warnUser: "$1 [User]",
hintSuccess: "operation successful [Success]",
hintSuccessX: "operation successful ($1 lines compiled; $2 sec total) [SuccessX]",
@ -345,13 +346,13 @@ const
hintPath: "added path: '$1' [Path]",
hintUser: "$1 [User]"]
const
WarningsToStr*: array[0..17, string] = ["CannotOpenFile", "OctalEscape",
const
WarningsToStr*: array[0..18, string] = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit", "CannotWriteMO2", "CannotReadMO2",
"Deprecated", "SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported",
"CommentXIgnored", "XisPassedToProcVar", "DerefDeprecated",
"AnalysisLoophole", "DifferentHeaps", "User"]
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", "User"]
HintsToStr*: array[0..13, string] = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",

View file

@ -22,11 +22,11 @@ const
procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wCompilerProc, wPure, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc}
wBorrow, wExtern, wImportCompilerProc, wThread}
converterPragmas* = procPragmas
methodPragmas* = procPragmas
macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wMagic, wNosideEffect, wCompilerProc, wDeprecated, wTypeCheck, wExtern}
wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern}
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
@ -37,7 +37,7 @@ const
wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wLinearScanEnd}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wPure,
wDeprecated, wExtern}
wDeprecated, wExtern, wThread}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern}
@ -45,7 +45,8 @@ const
wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib, wExtern}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern}
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect}
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect,
wThread}
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
proc pragmaAsm*(c: PContext, n: PNode): char
@ -125,10 +126,9 @@ proc processMagic(c: PContext, n: PNode, s: PSym) =
s.magic = m
break
if s.magic == mNone: Message(n.info, warnUnknownMagic, v)
if s.magic != mCreateThread:
# magics don't need an implementation, so we
# treat them as imported, instead of modifing a lot of working code:
incl(s.flags, sfImportc)
# magics don't need an implementation, so we
# treat them as imported, instead of modifing a lot of working code:
incl(s.flags, sfImportc)
proc wordToCallConv(sw: TSpecialWord): TCallingConvention =
# this assumes that the order of special words and calling conventions is
@ -500,9 +500,11 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfShallow)
of wTypeCheck:
of wThread:
noVal(it)
incl(sym.flags, sfTypeCheck)
incl(sym.flags, sfThread)
incl(sym.flags, sfProcVar)
if sym.typ != nil: incl(sym.typ.flags, tfThread)
of wHint: Message(it.info, hintUser, expectStrLit(c, it))
of wWarning: Message(it.info, warnUser, expectStrLit(c, it))
of wError: LocalError(it.info, errUser, expectStrLit(c, it))

View file

@ -203,8 +203,9 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
result = ast.emptyNode
proc checkThreads(c: PContext) =
if not needsGlobalAnalysis(): return
for i in 0 .. c.threadEntries.len-1:
semthreads.AnalyseThread(c.threadEntries[i])
semthreads.AnalyseThreadProc(c.threadEntries[i])
proc myClose(context: PPassContext, n: PNode): PNode =
var c = PContext(context)

View file

@ -41,7 +41,7 @@ type
generics*: PNode # a list of the things to compile; list of
# nkExprEqExpr nodes which contain the
# generic symbol and the instantiated symbol
threadEntries*: PNode # list of thread entries to check
threadEntries*: TSymSeq # list of thread entries to check
lastGenericIdx*: int # used for the generics stack
tab*: TSymTab # each module has its own symbol table
AmbiguousSymbols*: TIntSet # ids of all ambiguous symbols (cannot
@ -125,7 +125,7 @@ proc newContext(module: PSym, nimfile: string): PContext =
append(result.optionStack, newOptionEntry())
result.module = module
result.generics = newNode(nkStmtList)
result.threadEntries = newNode(nkStmtList)
result.threadEntries = @[]
result.converters = @[]
result.filename = nimfile
result.includedFiles = initIntSet()

View file

@ -41,8 +41,8 @@ proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
case s.kind
of skProc, skMethod, skIterator, skConverter:
if not (sfProcVar in s.flags) and (s.typ.callConv == ccDefault) and
(getModule(s).id != c.module.id):
if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
getModule(s).id != c.module.id:
LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
result = symChoice(c, n, s)
of skConst:
@ -103,8 +103,8 @@ proc checkConvertible(info: TLineInfo, castDest, src: PType) =
d = base(d)
s = base(s)
if d == nil:
GlobalError(info, errGenerated, `%`(msgKindToString(errIllegalConvFromXtoY), [
typeToString(src), typeToString(castDest)]))
GlobalError(info, errGenerated, msgKindToString(errIllegalConvFromXtoY) % [
src.typeToString, castDest.typeToString])
elif d.Kind == tyObject and s.Kind == tyObject:
checkConversionBetweenObjects(info, d, s)
elif (skipTypes(castDest, abstractVarRange).Kind in IntegralTypes) and
@ -195,15 +195,13 @@ proc semIs(c: PContext, n: PNode): PNode =
if sonsLen(n) == 3:
n.sons[1] = semExprWithType(c, n.sons[1], {efAllowType})
n.sons[2] = semExprWithType(c, n.sons[2], {efAllowType})
var a = n.sons[1].typ
var b = n.sons[2].typ
# a and b can be nil in case of an error:
if a != nil and b != nil:
if (b.kind != tyObject) or (a.kind != tyObject):
GlobalError(n.info, errIsExpectsObjectTypes)
while (b != nil) and (b.id != a.id): b = b.sons[0]
if b == nil:
GlobalError(n.info, errXcanNeverBeOfThisSubtype, typeToString(a))
var a = skipTypes(n.sons[1].typ, abstractPtrs)
var b = skipTypes(n.sons[2].typ, abstractPtrs)
if b.kind != tyObject or a.kind != tyObject:
GlobalError(n.info, errIsExpectsObjectTypes)
while b != nil and b.id != a.id: b = b.sons[0]
if b == nil:
GlobalError(n.info, errXcanNeverBeOfThisSubtype, typeToString(a))
n.typ = getSysType(tyBool)
else:
GlobalError(n.info, errIsExpectsTwoArguments)
@ -338,13 +336,13 @@ proc isAssignable(n: PNode): TAssignableResult =
result = arNone
case n.kind
of nkSym:
if (n.sym.kind in {skVar, skTemp}): result = arLValue
if n.sym.kind in {skVar, skTemp}: result = arLValue
of nkDotExpr:
if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}:
result = arLValue
else:
result = isAssignable(n.sons[0])
if (result == arLValue) and (sfDiscriminant in n.sons[1].sym.flags):
if result == arLValue and sfDiscriminant in n.sons[1].sym.flags:
result = arDiscriminant
of nkBracketExpr:
if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}:
@ -400,7 +398,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
mAppendSeqElem, mNewSeq, mReset, mShallowCopy}
checkMinSonsLen(n, 1)
var t = n.sons[0].typ
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic in FakeVarParams):
if n.sons[0].kind == nkSym and n.sons[0].sym.magic in FakeVarParams:
# BUGFIX: check for L-Value still needs to be done for the arguments!
for i in countup(1, sonsLen(n) - 1):
if i < sonsLen(t) and t.sons[i] != nil and
@ -409,8 +407,8 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
LocalError(n.sons[i].info, errVarForOutParamNeeded)
return
for i in countup(1, sonsLen(n) - 1):
if (i < sonsLen(t)) and
(skipTypes(t.sons[i], abstractInst).kind == tyVar):
if i < sonsLen(t) and
skipTypes(t.sons[i], abstractInst).kind == tyVar:
n.sons[i] = analyseIfAddressTaken(c, n.sons[i])
proc semDirectCallAnalyseEffects(c: PContext, n: PNode,
@ -466,7 +464,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = m.call
# we assume that a procedure that calls something indirectly
# has side-effects:
if not (tfNoSideEffect in t.flags): incl(c.p.owner.flags, sfSideEffect)
if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect)
else:
result = overloadedCallOpr(c, n)
# Now that nkSym does not imply an iteration over the proc/iterator space,
@ -845,10 +843,6 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
of mSizeOf: result = semSizeof(c, setMs(n, s))
of mIs: result = semIs(c, setMs(n, s))
of mEcho: result = semEcho(c, setMs(n, s))
of mCreateThread:
result = semDirectOp(c, n, flags)
if semthreads.needsGlobalAnalysis():
c.threadEntries.add(result)
of mShallowCopy:
if sonsLen(n) == 3:
# XXX ugh this is really a hack: shallowCopy() can be overloaded only
@ -1103,12 +1097,17 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of skTemplate: result = semTemplateExpr(c, n, s)
of skType:
if n.kind != nkCall: GlobalError(n.info, errXisNotCallable, s.name.s)
# XXX does this check make any sense?
result = semConv(c, n, s)
# XXX think about this more (``set`` procs)
if n.len == 2:
result = semConv(c, n, s)
elif Contains(c.AmbiguousSymbols, s.id):
LocalError(n.info, errUseQualifier, s.name.s)
elif s.magic == mNone: result = semDirectOp(c, n, flags)
else: result = semMagic(c, n, s, flags)
of skProc, skMethod, skConverter, skIterator:
if s.magic == mNone: result = semDirectOp(c, n, flags)
else: result = semMagic(c, n, s, flags)
else:
else:
#liMessage(n.info, warnUser, renderTree(n));
result = semIndirectOp(c, n, flags)
elif n.sons[0].kind == nkSymChoice:

View file

@ -549,10 +549,12 @@ 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():
c.threadEntries.add(s)
proc addResult(c: PContext, t: PType, info: TLineInfo) =
if t != nil:
var s = newSym(skVar, getIdent("result"), getCurrOwner())
var s = newSym(skVar, getIdent"result", getCurrOwner())
s.info = info
s.typ = t
incl(s.flags, sfResult)
@ -566,7 +568,7 @@ proc addResultNode(c: PContext, n: PNode) =
proc semLambda(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, codePos + 1)
var s = newSym(skProc, getIdent(":anonymous"), getCurrOwner())
var s = newSym(skProc, getIdent":anonymous", getCurrOwner())
s.info = n.info
s.ast = n
n.sons[namePos] = newSymNode(s)
@ -594,10 +596,11 @@ proc semLambda(c: PContext, n: PNode): PNode =
popProcCon(c)
else:
LocalError(n.info, errImplOfXexpected, s.name.s)
sideEffectsCheck(c, s)
closeScope(c.tab) # close scope for parameters
popOwner()
result.typ = s.typ
proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
validPragmas: TSpecialWords): PNode =
var

View file

@ -18,7 +18,7 @@
## 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 <- mine' but not 'mine <- theirs'. Since
## invalid, and so are 'theirs <- whatever' but not 'mine <- theirs'. Since
## strings and sequences are heap allocated they are affected too:
##
## .. code-block:: nimrod
@ -30,8 +30,9 @@
## 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.
## However, '!ref' is not implemented yet and this scheme would be too
## restrictive anyway.
## 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
@ -42,7 +43,8 @@
##
## var x = globalVar # 'x' points to 'theirs'
## while true:
## globalVar = x # OK: 'theirs <- theirs'
## globalVar = x # NOT OK: 'theirs <- theirs' invalid due to
## # write barrier!
## x = "new string" # ugh: 'x is toUnknown'!
##
## --> Solution: toUnknown is never allowed anywhere!
@ -106,10 +108,12 @@ proc analyseSym(c: PProcCtx, n: PNode): TThreadOwner =
if result != toUndefined: return
case v.kind
of skVar:
result = toNil
if sfGlobal in v.flags:
result = if sfThreadVar in v.flags: toMine else: toTheirs
else:
result = toNil
if sfThreadVar in v.flags:
result = toMine
elif containsTyRef(v.typ):
result = toTheirs
of skTemp, skForVar: result = toNil
of skConst: result = toMine
of skParam:
@ -136,7 +140,8 @@ proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) =
of toNil:
c.mapping[v.id] = owner # fine, toNil can be overwritten
of toVoid, toUndefined: InternalError(n.info, "writeAccess")
of toTheirs, toMine:
of toTheirs: Message(n.info, warnWriteToForeignHeap)
of toMine:
if lastOwner != owner and owner != toNil:
Message(n.info, warnDifferentHeaps)
else:
@ -145,7 +150,8 @@ proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) =
case lastOwner
of toNil: nil # fine, toNil can be overwritten
of toVoid, toUndefined: InternalError(n.info, "writeAccess")
of toTheirs, toMine:
of toTheirs: Message(n.info, warnWriteToForeignHeap)
of toMine:
if lastOwner != owner and owner != toNil:
Message(n.info, warnDifferentHeaps)
@ -171,7 +177,8 @@ proc analyseCall(c: PProcCtx, n: PNode): TThreadOwner =
newCtx.mapping[formal.id] = call.args[i-1]
pushInfoContext(n.info)
result = analyse(newCtx, prc.ast.sons[codePos])
if prc.ast.sons[codePos].kind == nkEmpty and sfNoSideEffect notin prc.flags:
if prc.ast.sons[codePos].kind == nkEmpty and
{sfNoSideEffect, sfThread} * prc.flags == {}:
Message(n.info, warnAnalysisLoophole, renderTree(n))
if prc.typ.sons[0] != nil:
if prc.ast.len > resultPos:
@ -228,7 +235,7 @@ proc analyseArgs(c: PProcCtx, n: PNode, start = 1) =
proc analyseOp(c: PProcCtx, n: PNode): TThreadOwner =
if n[0].kind != nkSym or n[0].sym.kind != skProc:
if tfNoSideEffect notin n[0].typ.flags:
if {tfNoSideEffect, tfThread} * n[0].typ.flags == {}:
Message(n.info, warnAnalysisLoophole, renderTree(n))
result = toNil
else:
@ -335,22 +342,26 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
result = toVoid
else: InternalError(n.info, "analysis not implemented for: " & $n.kind)
proc analyseThreadCreationCall(n: PNode) =
# thread proc is second param of ``createThread``:
if n[2].kind != nkSym or n[2].sym.kind != skProc:
Message(n.info, warnAnalysisLoophole, renderTree(n))
return
var prc = n[2].sym
proc analyseThreadProc*(prc: PSym) =
var c = newProcCtx(prc)
var formal = skipTypes(prc.typ, abstractInst).n.sons[1].sym
c.mapping[formal.id] = toTheirs # thread receives foreign data!
var formals = skipTypes(prc.typ, abstractInst).n
for i in 1 .. formals.len-1:
var formal = formals.sons[i].sym
c.mapping[formal.id] = toTheirs # thread receives foreign data!
discard analyse(c, prc.ast.sons[codePos])
when false:
proc analyseThreadCreationCall(n: PNode) =
# thread proc is second param of ``createThread``:
if n[2].kind != nkSym or n[2].sym.kind != skProc:
Message(n.info, warnAnalysisLoophole, renderTree(n))
return
analyseProc(n[2].sym)
proc AnalyseThread*(threadCreation: PNode) =
analyseThreadCreationCall(threadCreation)
proc needsGlobalAnalysis*: bool =
result = gGlobalOptions * {optThreads, optThreadAnalysis} ==
{optThreads, optThreadAnalysis}
proc AnalyseThread*(threadCreation: PNode) =
if needsGlobalAnalysis():
analyseThreadCreationCall(threadCreation)

View file

@ -220,6 +220,9 @@ proc procTypeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
result = isNone
if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
result = isNone
elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}:
# noSideEffect implies ``tfThread``!
result = isNone
else: nil
proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =

View file

@ -331,6 +331,13 @@ proc containsGarbageCollectedRef(typ: PType): bool =
# that are garbage-collected)
result = searchTypeFor(typ, isGBCRef)
proc isTyRef(t: PType): bool =
result = t.kind == tyRef
proc containsTyRef*(typ: PType): bool =
# returns true if typ contains a 'ref'
result = searchTypeFor(typ, isTyRef)
proc isHiddenPointer(t: PType): bool =
result = t.kind in {tyString, tySequence}
@ -484,6 +491,9 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
if tfNoSideEffect in t.flags:
addSep(prag)
add(prag, "noSideEffect")
if tfThread in t.flags:
addSep(prag)
add(prag, "thread")
if len(prag) != 0: add(result, "{." & prag & ".}")
else:
result = typeToStr[t.kind]

View file

@ -33,7 +33,7 @@ type
wWithout, wXor, wYield,
wColon, wColonColon, wEquals, wDot, wDotDot, wHat, wStar, wMinus,
wMagic, wTypeCheck, wFinal, wProfiler, wObjChecks,
wMagic, wThread, wFinal, wProfiler, wObjChecks,
wImportCompilerProc,
wImportc, wExportc, wExtern,
wAlign, wNodecl, wPure, wVolatile, wRegister, wSideeffect, wHeader,
@ -80,7 +80,7 @@ const
"yield",
":", "::", "=", ".", "..", "^", "*", "-",
"magic", "typecheck", "final", "profiler", "objchecks",
"magic", "thread", "final", "profiler", "objchecks",
"importcompilerproc", "importc", "exportc", "extern",
"align", "nodecl", "pure", "volatile", "register", "sideeffect",
"header", "nosideeffect", "noreturn", "merge", "lib", "dynlib",