compiler can emulate thread local variables
This commit is contained in:
parent
adbb48fbce
commit
4fa80956b8
10 changed files with 255 additions and 250 deletions
|
|
@ -1482,7 +1482,6 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
|||
# incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
|
||||
var
|
||||
a, b, idx: TLoc
|
||||
ts: string
|
||||
if nfAllConst in e.flags:
|
||||
putIntoDest(p, d, e.typ, genSetNode(p, e))
|
||||
else:
|
||||
|
|
@ -1504,7 +1503,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
|||
[rdLoc(d), rdSetElemLoc(a, e.typ)])
|
||||
else:
|
||||
# small set
|
||||
ts = "NI" & $(getSize(e.typ) * 8)
|
||||
var ts = "NI" & $(getSize(e.typ) * 8)
|
||||
appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(d)])
|
||||
for i in countup(0, sonsLen(e) - 1):
|
||||
if e.sons[i].kind == nkRange:
|
||||
|
|
@ -1645,10 +1644,17 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
|
|||
of skEnumField:
|
||||
putIntoDest(p, d, e.typ, toRope(sym.position))
|
||||
of skVar:
|
||||
if (sfGlobal in sym.flags): genVarPrototype(p.module, sym)
|
||||
if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
|
||||
if ((sym.loc.r == nil) or (sym.loc.t == nil)):
|
||||
InternalError(e.info, "expr: var not init " & sym.name.s)
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
if sfThreadVar in sym.flags:
|
||||
AccessThreadLocalVar(p, sym)
|
||||
if emulatedThreadVars():
|
||||
putIntoDest(p, d, sym.loc.t, con("NimTV->", sym.loc.r))
|
||||
else:
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
else:
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
of skForVar, skTemp:
|
||||
if ((sym.loc.r == nil) or (sym.loc.t == nil)):
|
||||
InternalError(e.info, "expr: temp not init " & sym.name.s)
|
||||
|
|
@ -1727,7 +1733,6 @@ proc genConstExpr(p: BProc, n: PNode): PRope =
|
|||
# XXX: tySequence!
|
||||
result = genConstSimpleList(p, n)
|
||||
else:
|
||||
# result := genLiteral(p, n)
|
||||
var d: TLoc
|
||||
initLocExpr(p, n, d)
|
||||
result = rdLoc(d)
|
||||
|
|
|
|||
54
compiler/ccgthreadvars.nim
Normal file
54
compiler/ccgthreadvars.nim
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2011 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Thread var support for crappy architectures that lack native support for
|
||||
## thread local storage.
|
||||
|
||||
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")
|
||||
|
||||
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
|
||||
# storage for that somehow, can't use the thread local storage
|
||||
# allocator for it :-(
|
||||
if not containsOrIncl(nimtvDeclared, s.id):
|
||||
nimtvDeps.add(s.loc.t)
|
||||
appf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
|
||||
else:
|
||||
if isExtern: app(m.s[cfsVars], "extern ")
|
||||
if optThreads in gGlobalOptions: app(m.s[cfsVars], "NIM_THREADVAR ")
|
||||
app(m.s[cfsVars], getTypeDesc(m, s.loc.t))
|
||||
appf(m.s[cfsVars], " $1;$n", [s.loc.r])
|
||||
|
||||
proc generateThreadLocalStorage(m: BModule) =
|
||||
if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags):
|
||||
for t in items(nimtvDeps): discard getTypeDesc(m, t)
|
||||
appf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
|
||||
|
||||
proc GenerateThreadVarsSize(m: BModule) =
|
||||
if nimtv != nil:
|
||||
app(m.s[cfsProcs],
|
||||
"NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}" & tnl)
|
||||
|
||||
|
|
@ -66,6 +66,7 @@ type
|
|||
s: TCProcSections # the procs sections; short name for readability
|
||||
prc: PSym # the Nimrod proc that this C proc belongs to
|
||||
BeforeRetNeeded: bool # true iff 'BeforeRet' label for proc is needed
|
||||
ThreadVarAccessed: bool # true if the proc already accessed some threadvar
|
||||
nestedTryStmts: seq[PNode] # in how many nested try statements we are
|
||||
# (the vars must be volatile then)
|
||||
labels: Natural # for generating unique labels in the C proc
|
||||
|
|
@ -85,6 +86,7 @@ type
|
|||
filename*: string
|
||||
s*: TCFileSections # sections of the C file
|
||||
PreventStackTrace: bool # true if stack traces need to be prevented
|
||||
usesThreadVars: bool # true if the module uses a thread var
|
||||
cfilename*: string # filename of the module (including path,
|
||||
# without extension)
|
||||
typeCache*: TIdTable # cache the generated types
|
||||
|
|
@ -386,29 +388,12 @@ proc localDebugInfo(p: BProc, s: PSym) =
|
|||
if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
|
||||
# XXX work around a bug: No type information for open arrays possible:
|
||||
if skipTypes(s.typ, abstractVar).kind == tyOpenArray: return
|
||||
if gCmd == cmdCompileToLLVM:
|
||||
# "address" is the 0th field
|
||||
# "typ" is the 1rst field
|
||||
# "name" is the 2nd field
|
||||
var name = cstringLit(p, p.s[cpsInit], normalize(s.name.s))
|
||||
if (s.kind == skParam) and not ccgIntroducedPtr(s): allocParam(p, s)
|
||||
inc(p.labels, 3)
|
||||
appf(p.s[cpsInit], "%LOC$6 = getelementptr %TF* %F, %NI 0, $1, %NI 0$n" &
|
||||
"%LOC$7 = getelementptr %TF* %F, %NI 0, $1, %NI 1$n" &
|
||||
"%LOC$8 = getelementptr %TF* %F, %NI 0, $1, %NI 2$n" &
|
||||
"store i8* $2, i8** %LOC$6$n" & "store $3* $4, $3** %LOC$7$n" &
|
||||
"store i8* $5, i8** %LOC$8$n", [toRope(p.frameLen), s.loc.r,
|
||||
getTypeDesc(p.module, "TNimType"),
|
||||
genTypeInfo(p.module, s.loc.t), name,
|
||||
toRope(p.labels), toRope(p.labels - 1),
|
||||
toRope(p.labels - 2)])
|
||||
else:
|
||||
var a = con("&", s.loc.r)
|
||||
if (s.kind == skParam) and ccgIntroducedPtr(s): a = s.loc.r
|
||||
appf(p.s[cpsInit],
|
||||
"F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n",
|
||||
[toRope(p.frameLen), makeCString(normalize(s.name.s)), a,
|
||||
genTypeInfo(p.module, s.loc.t)])
|
||||
var a = con("&", s.loc.r)
|
||||
if (s.kind == skParam) and ccgIntroducedPtr(s): a = s.loc.r
|
||||
appf(p.s[cpsInit],
|
||||
"F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n",
|
||||
[toRope(p.frameLen), makeCString(normalize(s.name.s)), a,
|
||||
genTypeInfo(p.module, s.loc.t)])
|
||||
inc(p.frameLen)
|
||||
|
||||
proc assignLocalVar(p: BProc, s: PSym) =
|
||||
|
|
@ -417,55 +402,35 @@ proc assignLocalVar(p: BProc, s: PSym) =
|
|||
# for each module that uses them!
|
||||
if s.loc.k == locNone:
|
||||
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack)
|
||||
if gCmd == cmdCompileToLLVM:
|
||||
appf(p.s[cpsLocals], "$1 = alloca $2$n",
|
||||
[s.loc.r, getTypeDesc(p.module, s.loc.t)])
|
||||
incl(s.loc.flags, lfIndirect)
|
||||
else:
|
||||
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t))
|
||||
if sfRegister in s.flags: app(p.s[cpsLocals], " register")
|
||||
if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0):
|
||||
app(p.s[cpsLocals], " volatile")
|
||||
appf(p.s[cpsLocals], " $1;$n", [s.loc.r])
|
||||
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t))
|
||||
if sfRegister in s.flags: app(p.s[cpsLocals], " register")
|
||||
if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0):
|
||||
app(p.s[cpsLocals], " volatile")
|
||||
appf(p.s[cpsLocals], " $1;$n", [s.loc.r])
|
||||
localDebugInfo(p, s)
|
||||
|
||||
proc declareThreadVar(m: BModule, s: PSym) =
|
||||
if optThreads in gGlobalOptions:
|
||||
if platform.OS[targetOS].props.contains(ospLacksThreadVars):
|
||||
# we gather all thread locals var into a struct and put that into
|
||||
# nim__dat.c; we need to allocate storage for that somehow, can't use
|
||||
# the thread local storage allocator for it :-(
|
||||
# XXX we need to adapt expr() too, every reference to a thread local var
|
||||
# generates quite some code ...
|
||||
InternalError("no workaround for lack of thread local vars implemented")
|
||||
else:
|
||||
app(m.s[cfsVars], "NIM_THREADVAR ")
|
||||
app(m.s[cfsVars], getTypeDesc(m, s.loc.t))
|
||||
else:
|
||||
app(m.s[cfsVars], getTypeDesc(m, s.loc.t))
|
||||
include ccgthreadvars
|
||||
|
||||
proc assignGlobalVar(p: BProc, s: PSym) =
|
||||
if s.loc.k == locNone:
|
||||
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap)
|
||||
useHeader(p.module, s)
|
||||
if lfNoDecl in s.loc.flags: return
|
||||
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
|
||||
if sfThreadVar in s.flags: declareThreadVar(p.module, s)
|
||||
else: app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
|
||||
if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
|
||||
if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile")
|
||||
appf(p.module.s[cfsVars], " $1;$n", [s.loc.r])
|
||||
if {optStackTrace, optEndb} * p.module.module.options ==
|
||||
{optStackTrace, optEndb}:
|
||||
if lfNoDecl in s.loc.flags: return
|
||||
if sfThreadVar in s.flags:
|
||||
declareThreadVar(p.module, s, sfImportc in s.flags)
|
||||
else:
|
||||
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
|
||||
app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
|
||||
if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
|
||||
if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile")
|
||||
appf(p.module.s[cfsVars], " $1;$n", [s.loc.r])
|
||||
if p.module.module.options * {optStackTrace, optEndb} ==
|
||||
{optStackTrace, optEndb}:
|
||||
appcg(p.module, p.module.s[cfsDebugInit],
|
||||
"#dbgRegisterGlobal($1, &$2, $3);$n",
|
||||
[cstringLit(p, p.module.s[cfsDebugInit],
|
||||
normalize(s.owner.name.s & '.' & s.name.s)),
|
||||
s.loc.r, genTypeInfo(p.module, s.typ)])
|
||||
|
||||
proc iff(cond: bool, the, els: PRope): PRope =
|
||||
if cond: result = the
|
||||
else: result = els
|
||||
|
||||
proc assignParam(p: BProc, s: PSym) =
|
||||
assert(s.loc.r != nil)
|
||||
|
|
@ -691,9 +656,8 @@ proc genProcPrototype(m: BModule, sym: PSym) =
|
|||
if lfDynamicLib in sym.loc.Flags:
|
||||
if sym.owner.id != m.module.id and
|
||||
not ContainsOrIncl(m.declaredThings, sym.id):
|
||||
appff(m.s[cfsVars], "extern $1 $2;$n",
|
||||
"@$2 = linkonce global $1 zeroinitializer$n",
|
||||
[getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)])
|
||||
appf(m.s[cfsVars], "extern $1 $2;$n",
|
||||
[getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)])
|
||||
if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect)
|
||||
elif not ContainsOrIncl(m.declaredProtos, sym.id):
|
||||
appf(m.s[cfsProcHeaders], "$1;$n", [genProcHeader(m, sym)])
|
||||
|
|
@ -725,20 +689,16 @@ proc genVarPrototype(m: BModule, sym: PSym) =
|
|||
assert(sfGlobal in sym.flags)
|
||||
useHeader(m, sym)
|
||||
fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(sym), OnHeap)
|
||||
if (lfNoDecl in sym.loc.Flags) or
|
||||
ContainsOrIncl(m.declaredThings, sym.id):
|
||||
if (lfNoDecl in sym.loc.Flags) or ContainsOrIncl(m.declaredThings, sym.id):
|
||||
return
|
||||
if sym.owner.id != m.module.id:
|
||||
# else we already have the symbol generated!
|
||||
assert(sym.loc.r != nil)
|
||||
if gCmd == cmdCompileToLLVM:
|
||||
incl(sym.loc.flags, lfIndirect)
|
||||
appf(m.s[cfsVars], "$1 = linkonce global $2 zeroinitializer$n",
|
||||
[sym.loc.r, getTypeDesc(m, sym.loc.t)])
|
||||
else:
|
||||
if sfThreadVar in sym.flags:
|
||||
declareThreadVar(m, sym, true)
|
||||
else:
|
||||
app(m.s[cfsVars], "extern ")
|
||||
if sfThreadVar in sym.flags: declareThreadVar(m, sym)
|
||||
else: app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
|
||||
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
|
||||
if sfRegister in sym.flags: app(m.s[cfsVars], " register")
|
||||
if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile")
|
||||
appf(m.s[cfsVars], " $1;$n", [sym.loc.r])
|
||||
|
|
@ -898,6 +858,7 @@ proc genInitCode(m: BModule) =
|
|||
proc genModule(m: BModule, cfilenoext: string): PRope =
|
||||
result = getFileHeader(cfilenoext)
|
||||
generateHeaders(m)
|
||||
generateThreadLocalStorage(m)
|
||||
for i in countup(low(TCFileSection), cfsProcs): app(result, m.s[i])
|
||||
|
||||
proc rawNewModule(module: PSym, filename: string): BModule =
|
||||
|
|
@ -990,6 +951,7 @@ proc writeModule(m: BModule) =
|
|||
if sfMainModule in m.module.flags:
|
||||
# generate main file:
|
||||
app(m.s[cfsProcHeaders], mainModProcs)
|
||||
GenerateThreadVarsSize(m)
|
||||
var code = genModule(m, cfilenoext)
|
||||
|
||||
when hasTinyCBackend:
|
||||
|
|
|
|||
|
|
@ -86,14 +86,15 @@ type
|
|||
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
|
||||
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
|
||||
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
|
||||
errXhasSideEffects, errIteratorExpected, errDifferentHeaps,
|
||||
errUser,
|
||||
errXhasSideEffects, errIteratorExpected,
|
||||
errUser,
|
||||
warnCannotOpenFile,
|
||||
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
||||
warnCannotWriteMO2, warnCannotReadMO2, warnDeprecated,
|
||||
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
|
||||
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored,
|
||||
warnXisPassedToProcVar, warnDerefDeprecated, warnAnalysisLoophole,
|
||||
warnDifferentHeaps,
|
||||
warnUser,
|
||||
hintSuccess, hintSuccessX,
|
||||
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
||||
|
|
@ -310,7 +311,6 @@ const
|
|||
errXisNoMacroOrTemplate: "\'$1\' is no macro or template",
|
||||
errXhasSideEffects: "\'$1\' can have side effects",
|
||||
errIteratorExpected: "iterator within for loop context expected",
|
||||
errDifferentHeaps: "possible inconsistency of thread local heaps",
|
||||
errUser: "$1",
|
||||
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
||||
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
||||
|
|
@ -327,7 +327,8 @@ const
|
|||
warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]",
|
||||
warnXisPassedToProcVar: "\'$1\' is passed to a procvar; deprecated [XisPassedToProcVar]",
|
||||
warnDerefDeprecated: "p^ is deprecated; use p[] instead [DerefDeprecated]",
|
||||
warnAnalysisLoophole: "thread analysis incomplete due to indirect call '$1' [AnalysisLoophole]",
|
||||
warnAnalysisLoophole: "thread analysis incomplete due to unkown call '$1' [AnalysisLoophole]",
|
||||
warnDifferentHeaps: "possible inconsistency of thread local heaps",
|
||||
warnUser: "$1 [User]",
|
||||
hintSuccess: "operation successful [Success]",
|
||||
hintSuccessX: "operation successful ($1 lines compiled; $2 sec total) [SuccessX]",
|
||||
|
|
@ -345,12 +346,12 @@ const
|
|||
hintUser: "$1 [User]"]
|
||||
|
||||
const
|
||||
WarningsToStr*: array[0..16, string] = ["CannotOpenFile", "OctalEscape",
|
||||
WarningsToStr*: array[0..17, string] = ["CannotOpenFile", "OctalEscape",
|
||||
"XIsNeverRead", "XmightNotBeenInit", "CannotWriteMO2", "CannotReadMO2",
|
||||
"Deprecated", "SmallLshouldNotBeUsed", "UnknownMagic",
|
||||
"RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported",
|
||||
"CommentXIgnored", "XisPassedToProcVar", "DerefDeprecated",
|
||||
"AnalysisLoophole", "User"]
|
||||
"AnalysisLoophole", "DifferentHeaps", "User"]
|
||||
|
||||
HintsToStr*: array[0..13, string] = ["Success", "SuccessX", "LineTooLong",
|
||||
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) =
|
|||
of toVoid, toUndefined: InternalError(n.info, "writeAccess")
|
||||
of toTheirs, toMine:
|
||||
if lastOwner != owner and owner != toNil:
|
||||
LocalError(n.info, errDifferentHeaps)
|
||||
Message(n.info, warnDifferentHeaps)
|
||||
else:
|
||||
# we could not backtrack to a concrete symbol, but that's fine:
|
||||
var lastOwner = analyseSym(c, n)
|
||||
|
|
@ -147,7 +147,7 @@ proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) =
|
|||
of toVoid, toUndefined: InternalError(n.info, "writeAccess")
|
||||
of toTheirs, toMine:
|
||||
if lastOwner != owner and owner != toNil:
|
||||
LocalError(n.info, errDifferentHeaps)
|
||||
Message(n.info, warnDifferentHeaps)
|
||||
|
||||
proc analyseAssign(c: PProcCtx, le, ri: PNode) =
|
||||
var y = analyse(c, ri) # read access; ok
|
||||
|
|
@ -171,7 +171,7 @@ 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:
|
||||
if prc.ast.sons[codePos].kind == nkEmpty and sfNoSideEffect notin prc.flags:
|
||||
Message(n.info, warnAnalysisLoophole, renderTree(n))
|
||||
if prc.typ.sons[0] != nil:
|
||||
if prc.ast.len > resultPos:
|
||||
|
|
@ -221,14 +221,15 @@ template aggregateOwner(result, ana: expr) =
|
|||
var a = ana # eval once
|
||||
if result != a:
|
||||
if result == toNil: result = a
|
||||
else: localError(n.info, errDifferentHeaps)
|
||||
else: 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:
|
||||
Message(n.info, warnAnalysisLoophole, renderTree(n))
|
||||
if tfNoSideEffect notin n[0].typ.flags:
|
||||
Message(n.info, warnAnalysisLoophole, renderTree(n))
|
||||
result = toNil
|
||||
else:
|
||||
var prc = n[0].sym
|
||||
|
|
@ -316,7 +317,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
|
|||
result = analyse(c, n.sons[0])
|
||||
of nkRaiseStmt:
|
||||
var a = analyse(c, n.sons[0])
|
||||
if a != toMine: LocalError(n.info, errDifferentHeaps)
|
||||
if a != toMine: Message(n.info, warnDifferentHeaps)
|
||||
result = toVoid
|
||||
of nkVarSection: result = analyseVarSection(c, n)
|
||||
of nkConstSection: result = analyseConstSection(c, n)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue