implemented 'guard' annotation

This commit is contained in:
Araq 2014-09-21 18:39:00 +02:00
commit 7916b1f9aa
21 changed files with 267 additions and 123 deletions

View file

@ -679,7 +679,7 @@ type
heapRoot*: PRope # keeps track of the enclosing heap object that heapRoot*: PRope # keeps track of the enclosing heap object that
# owns this location (required by GC algorithms # owns this location (required by GC algorithms
# employing heap snapshots or sliding views) # employing heap snapshots or sliding views)
a*: int # location's "address", i.e. slot for temporaries #a*: int # location's "address", i.e. slot for temporaries
# ---------------- end of backend information ------------------------------ # ---------------- end of backend information ------------------------------
@ -732,8 +732,9 @@ type
# check for the owner when touching 'usedGenerics'. # check for the owner when touching 'usedGenerics'.
usedGenerics*: seq[PInstantiation] usedGenerics*: seq[PInstantiation]
tab*: TStrTable # interface table for modules tab*: TStrTable # interface table for modules
of skLet, skVar, skField:
guard*: PSym
else: nil else: nil
magic*: TMagic magic*: TMagic
typ*: PType typ*: PType
name*: PIdent name*: PIdent
@ -795,7 +796,8 @@ type
deepCopy*: PSym # overriden 'deepCopy' operation deepCopy*: PSym # overriden 'deepCopy' operation
size*: BiggestInt # the size of the type in bytes size*: BiggestInt # the size of the type in bytes
# -1 means that the size is unkwown # -1 means that the size is unkwown
align*: int # the type's alignment requirements align*: int16 # the type's alignment requirements
lockLevel*: int16 # lock level as required for deadlock checking
loc*: TLoc loc*: TLoc
TPair*{.final.} = object TPair*{.final.} = object
@ -1173,8 +1175,8 @@ proc newType(kind: TTypeKind, owner: PSym): PType =
result.id = getID() result.id = getID()
when debugIds: when debugIds:
registerId(result) registerId(result)
#if result.id < 2000 then #if result.id < 2000:
# MessageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id)) # messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
proc mergeLoc(a: var TLoc, b: TLoc) = proc mergeLoc(a: var TLoc, b: TLoc) =
if a.k == low(a.k): a.k = b.k if a.k == low(a.k): a.k = b.k
@ -1182,7 +1184,7 @@ proc mergeLoc(a: var TLoc, b: TLoc) =
a.flags = a.flags + b.flags a.flags = a.flags + b.flags
if a.t == nil: a.t = b.t if a.t == nil: a.t = b.t
if a.r == nil: a.r = b.r if a.r == nil: a.r = b.r
if a.a == 0: a.a = b.a #if a.a == 0: a.a = b.a
proc assignType(dest, src: PType) = proc assignType(dest, src: PType) =
dest.kind = src.kind dest.kind = src.kind
@ -1230,6 +1232,8 @@ proc copySym(s: PSym, keepId: bool = false): PSym =
result.position = s.position result.position = s.position
result.loc = s.loc result.loc = s.loc
result.annex = s.annex # BUGFIX result.annex = s.annex # BUGFIX
if result.kind in {skVar, skLet, skField}:
result.guard = s.guard
proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym = proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym =
result = newSym(s.kind, newIdent, s.owner, info) result = newSym(s.kind, newIdent, s.owner, info)

View file

@ -409,7 +409,6 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) =
d.k = locData d.k = locData
d.t = getUniqueType(t) d.t = getUniqueType(t)
d.r = r d.r = r
d.a = -1
proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) = proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) =
var a: TLoc var a: TLoc
@ -425,7 +424,6 @@ proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) =
d.k = locExpr d.k = locExpr
d.t = getUniqueType(t) d.t = getUniqueType(t)
d.r = r d.r = r
d.a = -1
proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
var a, b: TLoc var a, b: TLoc
@ -1508,7 +1506,6 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n", linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
getTypeDesc(p.module, srct), getTypeDesc(p.module, destt), lbl) getTypeDesc(p.module, srct), getTypeDesc(p.module, destt), lbl)
tmp.k = locExpr tmp.k = locExpr
tmp.a = -1
tmp.t = srct tmp.t = srct
tmp.s = OnStack tmp.s = OnStack
tmp.flags = {} tmp.flags = {}
@ -2043,6 +2040,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
nkFromStmt, nkTemplateDef, nkMacroDef: nkFromStmt, nkTemplateDef, nkMacroDef:
discard discard
of nkPragma: genPragma(p, n) of nkPragma: genPragma(p, n)
of nkPragmaBlock: expr(p, n.lastSon, d)
of nkProcDef, nkMethodDef, nkConverterDef: of nkProcDef, nkMethodDef, nkConverterDef:
if (n.sons[genericParamsPos].kind == nkEmpty): if (n.sons[genericParamsPos].kind == nkEmpty):
var prc = n.sons[namePos].sym var prc = n.sons[namePos].sym

View file

@ -443,7 +443,7 @@ proc genBlock(p: BProc, t: PNode, d: var TLoc) =
assert(t.sons[0].kind == nkSym) assert(t.sons[0].kind == nkSym)
var sym = t.sons[0].sym var sym = t.sons[0].sym
sym.loc.k = locOther sym.loc.k = locOther
sym.loc.a = p.breakIdx sym.position = p.breakIdx+1
expr(p, t.sons[1], d) expr(p, t.sons[1], d)
endBlock(p) endBlock(p)
@ -482,7 +482,7 @@ proc genBreakStmt(p: BProc, t: PNode) =
assert(t.sons[0].kind == nkSym) assert(t.sons[0].kind == nkSym)
var sym = t.sons[0].sym var sym = t.sons[0].sym
assert(sym.loc.k == locOther) assert(sym.loc.k == locOther)
idx = sym.loc.a idx = sym.position-1
else: else:
# an unnamed 'break' can only break a loop after 'transf' pass: # an unnamed 'break' can only break a loop after 'transf' pass:
while idx >= 0 and not p.blocks[idx].isLoop: dec idx while idx >= 0 and not p.blocks[idx].isLoop: dec idx

View file

@ -55,7 +55,7 @@ proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) =
result.s = s result.s = s
result.t = getUniqueType(typ) result.t = getUniqueType(typ)
result.r = nil result.r = nil
result.a = - 1 #result.a = - 1
result.flags = {} result.flags = {}
proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) = proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) =
@ -63,7 +63,7 @@ proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) =
if a.k == locNone: if a.k == locNone:
a.k = k a.k = k
a.t = getUniqueType(typ) a.t = getUniqueType(typ)
a.a = - 1 #a.a = - 1
a.s = s a.s = s
if a.r == nil: a.r = r if a.r == nil: a.r = r
@ -407,7 +407,7 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
result.r = con("LOC", toRope(p.labels)) result.r = con("LOC", toRope(p.labels))
linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r) linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
result.k = locTemp result.k = locTemp
result.a = - 1 #result.a = - 1
result.t = getUniqueType(t) result.t = getUniqueType(t)
result.s = OnStack result.s = OnStack
result.flags = {} result.flags = {}
@ -425,7 +425,7 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) =
[getTypeDesc(p.module, toKeepAlive.t), fid]) [getTypeDesc(p.module, toKeepAlive.t), fid])
inc(p.gcFrameId) inc(p.gcFrameId)
result.k = locTemp result.k = locTemp
result.a = -1 #result.a = -1
result.t = toKeepAlive.t result.t = toKeepAlive.t
result.s = OnStack result.s = OnStack
result.flags = {} result.flags = {}

View file

@ -838,6 +838,22 @@ proc addAsgnFact*(m: var TModel, key, value: PNode) =
fact.sons[2] = value fact.sons[2] = value
m.add fact m.add fact
proc sameSubexprs*(m: TModel; a, b: PNode): bool =
# This should be used to check whether two *path expressions* refer to the
# same memory location according to 'm'. This is tricky:
# lock a[i].guard:
# ...
# access a[i].guarded
#
# Here a[i] is the same as a[i] iff 'i' and 'a' are not changed via '...'.
# However, nil checking requires exactly the same mechanism! But for now
# we simply use sameTree and live with the unsoundness of the analysis.
var check = newNodeI(nkCall, a.info, 3)
check.sons[0] = newSymNode(opEq)
check.sons[1] = a
check.sons[2] = b
result = m.doesImply(check) == impYes
proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) = proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
let branch = n.sons[i] let branch = n.sons[i]
if branch.kind == nkOfBranch: if branch.kind == nkOfBranch:

View file

@ -724,7 +724,7 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock") if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock")
var sym = n.sons[0].sym var sym = n.sons[0].sym
sym.loc.k = locOther sym.loc.k = locOther
sym.loc.a = idx sym.position = idx+1
setLen(p.blocks, idx + 1) setLen(p.blocks, idx + 1)
p.blocks[idx].id = - p.unique # negative because it isn't used yet p.blocks[idx].id = - p.unique # negative because it isn't used yet
let labl = p.unique let labl = p.unique
@ -741,7 +741,7 @@ proc genBreakStmt(p: PProc, n: PNode) =
assert(n.sons[0].kind == nkSym) assert(n.sons[0].kind == nkSym)
let sym = n.sons[0].sym let sym = n.sons[0].sym
assert(sym.loc.k == locOther) assert(sym.loc.k == locOther)
idx = sym.loc.a idx = sym.position-1
else: else:
# an unnamed 'break' can only break a loop after 'transf' pass: # an unnamed 'break' can only break a loop after 'transf' pass:
idx = len(p.blocks) - 1 idx = len(p.blocks) - 1
@ -1654,6 +1654,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
r.res = nil r.res = nil
of nkGotoState, nkState: of nkGotoState, nkState:
internalError(n.info, "first class iterators not implemented") internalError(n.info, "first class iterators not implemented")
of nkPragmaBlock: gen(p, n.lastSon, r)
else: internalError(n.info, "gen: unknown node type: " & $n.kind) else: internalError(n.info, "gen: unknown node type: " & $n.kind)
var globals: PGlobals var globals: PGlobals

View file

@ -35,7 +35,7 @@ proc registerSysType(t: PType) =
proc newSysType(kind: TTypeKind, size: int): PType = proc newSysType(kind: TTypeKind, size: int): PType =
result = newType(kind, systemModule) result = newType(kind, systemModule)
result.size = size result.size = size
result.align = size result.align = size.int16
proc getSysSym(name: string): PSym = proc getSysSym(name: string): PSym =
result = strTableGet(systemModule.tab, getIdent(name)) result = strTableGet(systemModule.tab, getIdent(name))

View file

@ -119,7 +119,7 @@ type
warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent, warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnUser, warnUninit, warnGcMem, warnUnguardedAccess, warnUser,
hintSuccess, hintSuccessX, hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
@ -387,9 +387,10 @@ const
warnProveField: "cannot prove that field '$1' is accessible [ProveField]", warnProveField: "cannot prove that field '$1' is accessible [ProveField]",
warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]", warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]",
warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]", warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]",
warnGcUnsafe2: "cannot prove '$1' is GC-safe. This will become a compile time error in the future.", warnGcUnsafe2: "cannot prove '$1' is GC-safe. Does not compile with --threads:on.",
warnUninit: "'$1' might not have been initialized [Uninit]", warnUninit: "'$1' might not have been initialized [Uninit]",
warnGcMem: "'$1' uses GC'ed memory [GcMem]", warnGcMem: "'$1' uses GC'ed memory [GcMem]",
warnUnguardedAccess: "'$1' is accessed without its guard [UnguardedAccess]",
warnUser: "$1 [User]", warnUser: "$1 [User]",
hintSuccess: "operation successful [Success]", hintSuccess: "operation successful [Success]",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]",
@ -410,7 +411,7 @@ const
hintUser: "$1 [User]"] hintUser: "$1 [User]"]
const const
WarningsToStr*: array[0..26, string] = ["CannotOpenFile", "OctalEscape", WarningsToStr*: array[0..27, string] = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit", "XIsNeverRead", "XmightNotBeenInit",
"Deprecated", "ConfigDeprecated", "Deprecated", "ConfigDeprecated",
"SmallLshouldNotBeUsed", "UnknownMagic", "SmallLshouldNotBeUsed", "UnknownMagic",
@ -419,7 +420,7 @@ const
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "User"] "GcMem", "UnguardedAccess", "User"]
HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong", HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",

View file

@ -24,7 +24,7 @@ const
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
wGensym, wInject, wRaises, wTags, wUses, wOperator, wDelegator, wGcSafe, wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
wOverride} wOverride}
converterPragmas* = procPragmas converterPragmas* = procPragmas
methodPragmas* = procPragmas methodPragmas* = procPragmas
@ -36,8 +36,8 @@ const
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect, iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
wTags, wUses, wOperator, wGcSafe} wTags, wLocks, wGcSafe}
exprPragmas* = {wLine} exprPragmas* = {wLine, wLocks}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
@ -49,23 +49,23 @@ const
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
wRaises, wUses, wTags, wGcSafe} wRaises, wLocks, wTags, wGcSafe}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
wBorrow, wGcSafe} wBorrow, wGcSafe}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError} wImportCpp, wImportObjC, wError, wGuard}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern, wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
wGensym, wInject, wCodegenDecl} wGensym, wInject, wCodegenDecl, wGuard}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject} wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
letPragmas* = varPragmas letPragmas* = varPragmas
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
wThread, wRaises, wUses, wTags, wGcSafe} wThread, wRaises, wLocks, wTags, wGcSafe}
allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
@ -514,26 +514,27 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) =
else: else:
invalidPragma(n) invalidPragma(n)
proc pragmaUses(c: PContext, n: PNode) = proc pragmaLockStmt(c: PContext; it: PNode) =
proc processExc(c: PContext, x: PNode): PNode = if it.kind != nkExprColonExpr:
if x.kind in {nkAccQuoted, nkIdent, nkSym, invalidPragma(it)
nkOpenSymChoice, nkClosedSymChoice}: else:
if considerQuotedIdent(x).s == "*": let n = it[1]
return newSymNode(ast.anyGlobal) if n.kind != nkBracket:
result = c.semExpr(c, x) localError(n.info, errGenerated, "locks pragma takes a list of expressions")
if result.kind != nkSym or sfGlobal notin result.sym.flags: else:
localError(x.info, "'$1' is not a global variable" % result.renderTree) for i in 0 .. <n.len:
result = newSymNode(ast.anyGlobal) n.sons[i] = c.semExpr(c, n.sons[i])
if n.kind == nkExprColonExpr: proc pragmaLocks(c: PContext, it: PNode): int16 =
let it = n.sons[1] if it.kind != nkExprColonExpr:
if it.kind notin {nkCurly, nkBracket}: invalidPragma(it)
n.sons[1] = processExc(c, it)
else: else:
for i in 0 .. <it.len: let n = it[1]
it.sons[i] = processExc(c, it.sons[i]) let x = expectIntLit(c, n)
if x < low(int16) or x > high(int16):
localError(n.info, "integer must be in the valid range of int16")
else: else:
invalidPragma(n) result = int16(x)
proc typeBorrow(sym: PSym, n: PNode) = proc typeBorrow(sym: PSym, n: PNode) =
if n.kind == nkExprColonExpr: if n.kind == nkExprColonExpr:
@ -563,6 +564,20 @@ proc deprecatedStmt(c: PContext; pragma: PNode) =
else: else:
localError(n.info, "key:value pair expected") localError(n.info, "key:value pair expected")
proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
if it.kind != nkExprColonExpr:
invalidPragma(it); return
let n = it[1]
if n.kind == nkSym:
result = n.sym
elif kind == skField:
# we return a dummy symbol; later passes over the type will repair it. Note
# that generic instantiation needs to know about this too. But we're lazy
# and perform the lookup on demand instead.
result = newSym(skUnknown, considerQuotedIdent(n), nil, n.info)
else:
result = qualifiedLookUp(c, n)
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
validPragmas: TSpecialWords): bool = validPragmas: TSpecialWords): bool =
var it = n.sons[i] var it = n.sons[i]
@ -599,10 +614,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wAlign: of wAlign:
if sym.typ == nil: invalidPragma(it) if sym.typ == nil: invalidPragma(it)
var align = expectIntLit(c, it) var align = expectIntLit(c, it)
if not isPowerOfTwo(align) and align != 0: if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
localError(it.info, errPowerOfTwoExpected) localError(it.info, errPowerOfTwoExpected)
else: else:
sym.typ.align = align sym.typ.align = align.int16
of wSize: of wSize:
if sym.typ == nil: invalidPragma(it) if sym.typ == nil: invalidPragma(it)
var size = expectIntLit(c, it) var size = expectIntLit(c, it)
@ -806,10 +821,15 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
if sym == nil: invalidPragma(it) if sym == nil: invalidPragma(it)
of wLine: pragmaLine(c, it) of wLine: pragmaLine(c, it)
of wRaises, wTags: pragmaRaisesOrTags(c, it) of wRaises, wTags: pragmaRaisesOrTags(c, it)
of wUses: pragmaUses(c, it) of wLocks:
of wOperator: if sym == nil: pragmaLockStmt(c, it)
if sym == nil: invalidPragma(it) elif sym.typ == nil: invalidPragma(it)
else: sym.position = expectIntLit(c, it) else: sym.typ.lockLevel = pragmaLocks(c, it)
of wGuard:
if sym == nil or sym.kind notin {skVar, skLet, skField}:
invalidPragma(it)
else:
sym.guard = pragmaGuard(c, it, sym.kind)
of wInjectStmt: of wInjectStmt:
if it.kind != nkExprColonExpr: if it.kind != nkExprColonExpr:
localError(it.info, errExprExpected) localError(it.info, errExprExpected)

View file

@ -280,11 +280,6 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
loc.r = toRope(decodeStr(r.s, r.pos)) loc.r = toRope(decodeStr(r.s, r.pos))
else: else:
loc.r = nil loc.r = nil
if r.s[r.pos] == '?':
inc(r.pos)
loc.a = decodeVInt(r.s, r.pos)
else:
loc.a = 0
if r.s[r.pos] == '>': inc(r.pos) if r.s[r.pos] == '>': inc(r.pos)
else: internalError(info, "decodeLoc " & r.s[r.pos]) else: internalError(info, "decodeLoc " & r.s[r.pos])
@ -326,7 +321,7 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
result.size = - 1 result.size = - 1
if r.s[r.pos] == '=': if r.s[r.pos] == '=':
inc(r.pos) inc(r.pos)
result.align = decodeVInt(r.s, r.pos) result.align = decodeVInt(r.s, r.pos).int16
else: else:
result.align = 2 result.align = 2
decodeLoc(r, result.loc, info) decodeLoc(r, result.loc, info)

View file

@ -187,9 +187,6 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
if loc.r != nil: if loc.r != nil:
add(result, '!') add(result, '!')
encodeStr(ropeToStr(loc.r), result) encodeStr(ropeToStr(loc.r), result)
if loc.a != 0:
add(result, '?')
encodeVInt(loc.a, result)
if oldLen + 1 == result.len: if oldLen + 1 == result.len:
# no data was necessary, so remove the '<' again: # no data was necessary, so remove the '<' again:
setLen(result, oldLen) setLen(result, oldLen)

View file

@ -1626,6 +1626,10 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType;
initIdTable(bindings) initIdTable(bindings)
bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t) bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t)
result = c.semGenerateInstance(c, sym, bindings, info) result = c.semGenerateInstance(c, sym, bindings, info)
# since it's an instantiation, we unmark it as a compilerproc. Otherwise
# codegen would fail:
result.flags = result.flags - {sfCompilerProc, sfExportC, sfImportC}
result.loc.r = nil
proc setMs(n: PNode, s: PSym): PNode = proc setMs(n: PNode, s: PSym): PNode =
result = n result = n

View file

@ -57,9 +57,13 @@ discard """
c() c()
--> we need a stack of scopes for this analysis --> we need a stack of scopes for this analysis
"""
const trackGlobals = false ## we don't need it for now # XXX enhance the algorithm to care about 'dirty' expressions:
lock a[i].L:
inc i # mark 'i' dirty
lock a[j].L:
access a[i], a[j] # --> reject a[i]
"""
type type
TEffects = object TEffects = object
@ -71,12 +75,69 @@ type
init: seq[int] # list of initialized variables init: seq[int] # list of initialized variables
guards: TModel # nested guards guards: TModel # nested guards
locked: seq[PNode] # locked locations locked: seq[PNode] # locked locations
gcUnsafe, isRecursive: bool gcUnsafe, isRecursive, isToplevel: bool
PEffects = var TEffects PEffects = var TEffects
proc isLocalVar(a: PEffects, s: PSym): bool = proc isLocalVar(a: PEffects, s: PSym): bool =
s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.owner == a.owner s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.owner == a.owner
proc lockLocations(a: PEffects; pragma: PNode) =
if pragma.kind != nkExprColonExpr:
internalError(pragma.info, "no colon")
return
for x in pragma[1]:
a.locked.add x
proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
# check whether the corresponding lock is held:
for L in a.locked:
if L.kind == nkSym and L.sym == guard: return
# we allow accesses nevertheless in top level statements for
# easier initialization:
#if a.isTopLevel:
# message(n.info, warnUnguardedAccess, renderTree(n))
#else:
if not a.isTopLevel:
localError(n.info, errGenerated, "unguarded access: " & renderTree(n))
# 'guard*' are checks which are concerned with 'guard' annotations
# (var x{.guard: y.}: int)
proc guardDotAccess(a: PEffects; n: PNode) =
let ri = n.sons[1]
internalAssert ri.kind == nkSym and ri.sym.kind == skField
var g = ri.sym.guard
if g.isNil or a.isTopLevel: return
# fixup guard:
if g.kind == skUnknown:
var field: PSym = nil
var ty = n.sons[0].typ.skipTypes(abstractPtrs)
if ty.kind == tyTuple:
field = lookupInRecord(ty.n, g.name)
else:
while ty != nil and ty.kind == tyObject:
field = lookupInRecord(ty.n, g.name)
if field != nil: break
ty = ty.sons[0]
if ty == nil: break
ty = ty.skipTypes(abstractPtrs)
if field == nil:
localError(n.info, errGenerated, "invalid guard field: " & g.name.s)
return
g = field
#ri.sym.guard = field
# XXX unfortunately this is not correct for generic instantiations!
if g.kind == skField:
let dot = newNodeI(nkDotExpr, n.info, 2)
dot.sons[0] = n.sons[0]
dot.sons[1] = newSymNode(g)
dot.typ = g.typ
for L in a.locked:
#if a.guards.sameSubexprs(dot, L): return
if guards.sameTree(dot, L): return
localError(n.info, errGenerated, "unguarded access: " & renderTree(n))
else:
guardGlobal(a, n, g)
proc initVar(a: PEffects, n: PNode) = proc initVar(a: PEffects, n: PNode) =
if n.kind != nkSym: return if n.kind != nkSym: return
let s = n.sym let s = n.sym
@ -93,13 +154,6 @@ proc initVarViaNew(a: PEffects, n: PNode) =
# are initialized: # are initialized:
initVar(a, n) initVar(a, n)
when trackGlobals:
proc addUse(a: PEffects, e: PNode) =
var aa = a.uses
for i in 0 .. <aa.len:
if aa[i].sym.id == e.sym.id: return
a.uses.add(e)
proc useVar(a: PEffects, n: PNode) = proc useVar(a: PEffects, n: PNode) =
let s = n.sym let s = n.sym
if isLocalVar(a, s): if isLocalVar(a, s):
@ -110,9 +164,8 @@ proc useVar(a: PEffects, n: PNode) =
message(n.info, warnUninit, s.name.s) message(n.info, warnUninit, s.name.s)
# prevent superfluous warnings about the same variable: # prevent superfluous warnings about the same variable:
a.init.add s.id a.init.add s.id
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind == skVar: if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}:
when trackGlobals: if s.guard != nil: guardGlobal(a, n, s.guard)
a.addUse(copyNode(n))
if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and
tfGcSafe notin s.typ.flags: tfGcSafe notin s.typ.flags:
if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n)) if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
@ -186,13 +239,6 @@ proc mergeTags(a: PEffects, b, comesFrom: PNode) =
else: else:
for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil) for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil)
when trackGlobals:
proc mergeUses(a: PEffects, b, comesFrom: PNode) =
if b.isNil:
addUse(a, createAnyGlobal(comesFrom))
else:
for effect in items(b): addUse(a, effect)
proc listEffects(a: PEffects) = proc listEffects(a: PEffects) =
for e in items(a.exc): message(e.info, hintUser, typeToString(e.typ)) for e in items(a.exc): message(e.info, hintUser, typeToString(e.typ))
for e in items(a.tags): message(e.info, hintUser, typeToString(e.typ)) for e in items(a.tags): message(e.info, hintUser, typeToString(e.typ))
@ -322,7 +368,7 @@ proc documentRaises*(n: PNode) =
if n.sons[namePos].kind != nkSym: return if n.sons[namePos].kind != nkSym: return
documentEffect(n, n.sons[pragmasPos], wRaises, exceptionEffects) documentEffect(n, n.sons[pragmasPos], wRaises, exceptionEffects)
documentEffect(n, n.sons[pragmasPos], wTags, tagEffects) documentEffect(n, n.sons[pragmasPos], wTags, tagEffects)
documentEffect(n, n.sons[pragmasPos], wUses, usesEffects) #documentEffect(n, n.sons[pragmasPos], wUses, usesEffects)
template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {} template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {}
@ -342,10 +388,6 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n)) if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
tracked.gcUnsafe = true tracked.gcUnsafe = true
when trackGlobals:
let usesSpec = effectSpec(pragma, wUses)
mergeUses(tracked, usesSpec, n)
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
let n = n.skipConv let n = n.skipConv
if paramType != nil and tfNotNil in paramType.flags and if paramType != nil and tfNotNil in paramType.flags and
@ -381,7 +423,6 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
# we have no explicit effects so assume the worst: # we have no explicit effects so assume the worst:
addEffect(tracked, createRaise(n)) addEffect(tracked, createRaise(n))
addTag(tracked, createTag(n)) addTag(tracked, createTag(n))
when trackGlobals: addUse(tracked, createAnyGlobal(n))
# assume GcUnsafe unless in its type; 'forward' does not matter: # assume GcUnsafe unless in its type; 'forward' does not matter:
if notGcSafe(op): if notGcSafe(op):
if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n)) if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
@ -389,7 +430,6 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
else: else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n)
when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n)
if notGcSafe(op): if notGcSafe(op):
if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n)) if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
tracked.gcUnsafe = true tracked.gcUnsafe = true
@ -529,12 +569,9 @@ proc track(tracked: PEffects, n: PNode) =
elif isIndirectCall(a, tracked.owner): elif isIndirectCall(a, tracked.owner):
addEffect(tracked, createRaise(n)) addEffect(tracked, createRaise(n))
addTag(tracked, createTag(n)) addTag(tracked, createTag(n))
when trackGlobals: addUse(tracked, createAnyGlobal(n))
# XXX handle 'gcsafe' properly for callbacks!
else: else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n)
when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n)
if notGcSafe(op) and not importedFromC(a): if notGcSafe(op) and not importedFromC(a):
# and it's not a recursive call: # and it's not a recursive call:
if not (a.kind == nkSym and a.sym == tracked.owner): if not (a.kind == nkSym and a.sym == tracked.owner):
@ -546,6 +583,9 @@ proc track(tracked: PEffects, n: PNode) =
initVarViaNew(tracked, n.sons[1]) initVarViaNew(tracked, n.sons[1])
for i in 0 .. <safeLen(n): for i in 0 .. <safeLen(n):
track(tracked, n.sons[i]) track(tracked, n.sons[i])
of nkDotExpr:
guardDotAccess(tracked, n)
for i in 0 .. <len(n): track(tracked, n.sons[i])
of nkCheckedFieldExpr: of nkCheckedFieldExpr:
track(tracked, n.sons[0]) track(tracked, n.sons[0])
if warnProveField in gNotes: checkFieldAccess(tracked.guards, n) if warnProveField in gNotes: checkFieldAccess(tracked.guards, n)
@ -601,6 +641,14 @@ proc track(tracked: PEffects, n: PNode) =
if sfDiscriminant in x.sons[0].sym.flags: if sfDiscriminant in x.sons[0].sym.flags:
addDiscriminantFact(tracked.guards, x) addDiscriminantFact(tracked.guards, x)
setLen(tracked.guards, oldFacts) setLen(tracked.guards, oldFacts)
of nkPragmaBlock:
let pragmaList = n.sons[0]
let oldLocked = tracked.locked.len
for i in 0 .. <pragmaList.len:
if whichPragma(pragmaList.sons[i]) == wLocks:
lockLocations(tracked, pragmaList.sons[i])
track(tracked, n.lastSon)
setLen(tracked.locked, oldLocked)
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
nkMacroDef, nkTemplateDef: nkMacroDef, nkTemplateDef:
discard discard
@ -648,10 +696,6 @@ proc checkMethodEffects*(disp, branch: PSym) =
if not isNil(tagsSpec): if not isNil(tagsSpec):
checkRaisesSpec(tagsSpec, actual.sons[tagEffects], checkRaisesSpec(tagsSpec, actual.sons[tagEffects],
"can have an unlisted effect: ", hints=off, subtypeRelation) "can have an unlisted effect: ", hints=off, subtypeRelation)
let usesSpec = effectSpec(p, wUses)
if not isNil(usesSpec):
checkRaisesSpec(usesSpec, actual.sons[usesEffects],
"may use an unlisted global variable: ", hints=off, symbolPredicate)
if sfThread in disp.flags and notGcSafe(branch.typ): if sfThread in disp.flags and notGcSafe(branch.typ):
localError(branch.info, "base method is GC-safe, but '$1' is not" % localError(branch.info, "base method is GC-safe, but '$1' is not" %
branch.name.s) branch.name.s)
@ -663,29 +707,25 @@ proc setEffectsForProcType*(t: PType, n: PNode) =
let let
raisesSpec = effectSpec(n, wRaises) raisesSpec = effectSpec(n, wRaises)
tagsSpec = effectSpec(n, wTags) tagsSpec = effectSpec(n, wTags)
usesSpec = effectSpec(n, wUses) if not isNil(raisesSpec) or not isNil(tagsSpec):
if not isNil(raisesSpec) or not isNil(tagsSpec) or not isNil(usesSpec):
internalAssert effects.len == 0 internalAssert effects.len == 0
newSeq(effects.sons, effectListLen) newSeq(effects.sons, effectListLen)
if not isNil(raisesSpec): if not isNil(raisesSpec):
effects.sons[exceptionEffects] = raisesSpec effects.sons[exceptionEffects] = raisesSpec
if not isNil(tagsSpec): if not isNil(tagsSpec):
effects.sons[tagEffects] = tagsSpec effects.sons[tagEffects] = tagsSpec
if not isNil(usesSpec):
effects.sons[usesEffects] = usesSpec
proc initEffects(effects: PNode; s: PSym; t: var TEffects) = proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
newSeq(effects.sons, effectListLen) newSeq(effects.sons, effectListLen)
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info) effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
effects.sons[tagEffects] = newNodeI(nkArgList, s.info) effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
effects.sons[usesEffects] = newNodeI(nkArgList, s.info)
t.exc = effects.sons[exceptionEffects] t.exc = effects.sons[exceptionEffects]
t.tags = effects.sons[tagEffects] t.tags = effects.sons[tagEffects]
t.uses = effects.sons[usesEffects]
t.owner = s t.owner = s
t.init = @[] t.init = @[]
t.guards = @[] t.guards = @[]
t.locked = @[]
proc trackProc*(s: PSym, body: PNode) = proc trackProc*(s: PSym, body: PNode) =
var effects = s.typ.n.sons[0] var effects = s.typ.n.sons[0]
@ -717,16 +757,12 @@ proc trackProc*(s: PSym, body: PNode) =
# after the check, use the formal spec: # after the check, use the formal spec:
effects.sons[tagEffects] = tagsSpec effects.sons[tagEffects] = tagsSpec
when trackGlobals:
let usesSpec = effectSpec(p, wUses)
if not isNil(usesSpec):
checkRaisesSpec(usesSpec, t.uses,
"uses an unlisted global variable: ", hints=on, symbolPredicate)
effects.sons[usesEffects] = usesSpec
if optThreadAnalysis in gGlobalOptions: if optThreadAnalysis in gGlobalOptions:
if sfThread in s.flags and t.gcUnsafe: if sfThread in s.flags and t.gcUnsafe:
#localError(s.info, warnGcUnsafe2, s.name.s) if optThreads in gGlobalOptions:
localError(s.info, "'$1' is not GC-safe" % s.name.s) localError(s.info, "'$1' is not GC-safe" % s.name.s)
else:
localError(s.info, warnGcUnsafe2, s.name.s)
if not t.gcUnsafe: s.typ.flags.incl tfGcSafe if not t.gcUnsafe: s.typ.flags.incl tfGcSafe
proc trackTopLevelStmt*(module: PSym; n: PNode) = proc trackTopLevelStmt*(module: PSym; n: PNode) =
@ -736,5 +772,5 @@ proc trackTopLevelStmt*(module: PSym; n: PNode) =
var effects = newNode(nkEffectList, n.info) var effects = newNode(nkEffectList, n.info)
var t: TEffects var t: TEffects
initEffects(effects, module, t) initEffects(effects, module, t)
t.isToplevel = true
track(t, n) track(t, n)

View file

@ -1300,9 +1300,14 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
let pragmaList = n.sons[0] let pragmaList = n.sons[0]
pragma(c, nil, pragmaList, exprPragmas) pragma(c, nil, pragmaList, exprPragmas)
result = semExpr(c, n.sons[1]) result = semExpr(c, n.sons[1])
n.sons[1] = result
for i in 0 .. <pragmaList.len: for i in 0 .. <pragmaList.len:
if whichPragma(pragmaList.sons[i]) == wLine: case whichPragma(pragmaList.sons[i])
setLine(result, pragmaList.sons[i].info) of wLine: setLine(result, pragmaList.sons[i].info)
of wLocks:
result = n
result.typ = n.sons[1].typ
else: discard
proc semStaticStmt(c: PContext, n: PNode): PNode = proc semStaticStmt(c: PContext, n: PNode): PNode =
let a = semStmt(c, n.sons[0]) let a = semStmt(c, n.sons[0])

View file

@ -1244,7 +1244,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
proc setMagicType(m: PSym, kind: TTypeKind, size: int) = proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
m.typ.kind = kind m.typ.kind = kind
m.typ.align = size m.typ.align = size.int16
m.typ.size = size m.typ.size = size
proc processMagicType(c: PContext, m: PSym) = proc processMagicType(c: PContext, m: PSym) =

View file

@ -1271,7 +1271,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
#internalError("computeSizeAux()") #internalError("computeSizeAux()")
result = szUnknownSize result = szUnknownSize
typ.size = result typ.size = result
typ.align = int(a) typ.align = int16(a)
proc computeSize(typ: PType): BiggestInt = proc computeSize(typ: PType): BiggestInt =
var a: BiggestInt = 1 var a: BiggestInt = 1

View file

@ -1595,6 +1595,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
let L = n.len-1 let L = n.len-1
for i in 0 .. <L: gen(c, n.sons[i]) for i in 0 .. <L: gen(c, n.sons[i])
gen(c, n.sons[L], dest, flags) gen(c, n.sons[L], dest, flags)
of nkPragmaBlock:
gen(c, n.lastSon, dest, flags)
of nkDiscardStmt: of nkDiscardStmt:
unused(n, dest) unused(n, dest)
gen(c, n.sons[0]) gen(c, n.sons[0])

View file

@ -64,7 +64,7 @@ type
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt, wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wAsmNoStackFrame, wAsmNoStackFrame,
wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wUses, wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks,
wAuto, wBool, wCatch, wChar, wClass, wAuto, wBool, wCatch, wChar, wClass,
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@ -147,7 +147,7 @@ const
"computedgoto", "injectstmt", "computedgoto", "injectstmt",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
"asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked", "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
"guard", "uses", "guard", "locks",
"auto", "bool", "catch", "char", "class", "auto", "bool", "catch", "char", "class",
"const_cast", "default", "delete", "double", "const_cast", "default", "delete", "double",

View file

@ -0,0 +1,37 @@
when false:
template lock(a, b: ptr TLock; body: stmt) =
if cast[ByteAddress](a) < cast[ByteAddress](b):
pthread_mutex_lock(a)
pthread_mutex_lock(b)
else:
pthread_mutex_lock(b)
pthread_mutex_lock(a)
{.locks: [a, b].}:
try:
body
finally:
pthread_mutex_unlock(a)
pthread_mutex_unlock(b)
type
ProtectedCounter[T] = object
i {.guard: L.}: T
L: int
var
c: ProtectedCounter[int]
c.i = 89
template atomicRead(L, x): expr =
{.locks: [L].}:
x
proc main =
{.locks: [c.L].}:
inc c.i
discard
echo(atomicRead(c.L, c.i))
main()

View file

@ -0,0 +1,27 @@
discard """
errormsg: "unguarded access: c.i"
line: 25
"""
type
ProtectedCounter[T] = object
i {.guard: L.}: T
L: int
var
c: ProtectedCounter[int]
c.i = 89
template atomicRead(L, x): expr =
{.locks: [L].}:
x
proc main =
{.locks: [c.L].}:
inc c.i
discard
echo(atomicRead(c.L, c.i))
echo c.i
main()

View file

@ -3,7 +3,7 @@ version 0.10
- Test nimfix on various babel packages - Test nimfix on various babel packages
- Pegs do not work at compile-time - Pegs do not work at compile-time
- # echo type.int
version 0.9.6 version 0.9.6
============= =============
@ -15,7 +15,6 @@ version 0.9.6
- split docgen into separate tool - split docgen into separate tool
- .benign pragma - .benign pragma
- scopes are still broken for generic instantiation! - scopes are still broken for generic instantiation!
- implicit deref for parameter matching
Concurrency Concurrency
----------- -----------
@ -23,7 +22,6 @@ Concurrency
- 'deepCopy' needs to be instantiated for - 'deepCopy' needs to be instantiated for
generics *when the type is constructed* generics *when the type is constructed*
- test 'deepCopy' - test 'deepCopy'
- overloading of '='; general lift mechanism
- the disjoint checker needs to deal with 'a = spawn f(); g = spawn f()' - the disjoint checker needs to deal with 'a = spawn f(); g = spawn f()'
- implement 'foo[1..4] = spawn(f[4..7])' - implement 'foo[1..4] = spawn(f[4..7])'
@ -63,6 +61,9 @@ Bugs
version 0.9.x version 0.9.x
============= =============
- implicit deref for parameter matching
- overloading of '='; general lift mechanism
- pragmas need 'bindSym' support - pragmas need 'bindSym' support
- pragmas need re-work: 'push' is dangerous, 'hasPragma' does not work - pragmas need re-work: 'push' is dangerous, 'hasPragma' does not work
reliably with user-defined pragmas reliably with user-defined pragmas