bugfix: 'set' overloadable; further steps for multi threading support
This commit is contained in:
parent
170573a87f
commit
99bcc233cd
34 changed files with 934 additions and 459 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue