implemented 'guard' annotation

This commit is contained in:
Araq
2014-09-21 18:39:00 +02:00
parent 4800acf6ab
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
# owns this location (required by GC algorithms
# 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 ------------------------------
@@ -732,8 +732,9 @@ type
# check for the owner when touching 'usedGenerics'.
usedGenerics*: seq[PInstantiation]
tab*: TStrTable # interface table for modules
of skLet, skVar, skField:
guard*: PSym
else: nil
magic*: TMagic
typ*: PType
name*: PIdent
@@ -795,7 +796,8 @@ type
deepCopy*: PSym # overriden 'deepCopy' operation
size*: BiggestInt # the size of the type in bytes
# -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
TPair*{.final.} = object
@@ -1173,8 +1175,8 @@ proc newType(kind: TTypeKind, owner: PSym): PType =
result.id = getID()
when debugIds:
registerId(result)
#if result.id < 2000 then
# MessageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
#if result.id < 2000:
# messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
proc mergeLoc(a: var TLoc, b: TLoc) =
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
if a.t == nil: a.t = b.t
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) =
dest.kind = src.kind
@@ -1230,6 +1232,8 @@ proc copySym(s: PSym, keepId: bool = false): PSym =
result.position = s.position
result.loc = s.loc
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 =
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.t = getUniqueType(t)
d.r = r
d.a = -1
proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) =
var a: TLoc
@@ -425,7 +424,6 @@ proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) =
d.k = locExpr
d.t = getUniqueType(t)
d.r = r
d.a = -1
proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
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",
getTypeDesc(p.module, srct), getTypeDesc(p.module, destt), lbl)
tmp.k = locExpr
tmp.a = -1
tmp.t = srct
tmp.s = OnStack
tmp.flags = {}
@@ -2043,6 +2040,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
nkFromStmt, nkTemplateDef, nkMacroDef:
discard
of nkPragma: genPragma(p, n)
of nkPragmaBlock: expr(p, n.lastSon, d)
of nkProcDef, nkMethodDef, nkConverterDef:
if (n.sons[genericParamsPos].kind == nkEmpty):
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)
var sym = t.sons[0].sym
sym.loc.k = locOther
sym.loc.a = p.breakIdx
sym.position = p.breakIdx+1
expr(p, t.sons[1], d)
endBlock(p)
@@ -482,7 +482,7 @@ proc genBreakStmt(p: BProc, t: PNode) =
assert(t.sons[0].kind == nkSym)
var sym = t.sons[0].sym
assert(sym.loc.k == locOther)
idx = sym.loc.a
idx = sym.position-1
else:
# an unnamed 'break' can only break a loop after 'transf' pass:
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.t = getUniqueType(typ)
result.r = nil
result.a = - 1
#result.a = - 1
result.flags = {}
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:
a.k = k
a.t = getUniqueType(typ)
a.a = - 1
#a.a = - 1
a.s = s
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))
linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
result.k = locTemp
result.a = - 1
#result.a = - 1
result.t = getUniqueType(t)
result.s = OnStack
result.flags = {}
@@ -425,7 +425,7 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) =
[getTypeDesc(p.module, toKeepAlive.t), fid])
inc(p.gcFrameId)
result.k = locTemp
result.a = -1
#result.a = -1
result.t = toKeepAlive.t
result.s = OnStack
result.flags = {}

View File

@@ -838,6 +838,22 @@ proc addAsgnFact*(m: var TModel, key, value: PNode) =
fact.sons[2] = value
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) =
let branch = n.sons[i]
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")
var sym = n.sons[0].sym
sym.loc.k = locOther
sym.loc.a = idx
sym.position = idx+1
setLen(p.blocks, idx + 1)
p.blocks[idx].id = - p.unique # negative because it isn't used yet
let labl = p.unique
@@ -741,7 +741,7 @@ proc genBreakStmt(p: PProc, n: PNode) =
assert(n.sons[0].kind == nkSym)
let sym = n.sons[0].sym
assert(sym.loc.k == locOther)
idx = sym.loc.a
idx = sym.position-1
else:
# an unnamed 'break' can only break a loop after 'transf' pass:
idx = len(p.blocks) - 1
@@ -1654,6 +1654,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
r.res = nil
of nkGotoState, nkState:
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)
var globals: PGlobals

View File

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

View File

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

View File

@@ -24,7 +24,7 @@ const
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
wGensym, wInject, wRaises, wTags, wUses, wOperator, wDelegator, wGcSafe,
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
wOverride}
converterPragmas* = procPragmas
methodPragmas* = procPragmas
@@ -36,8 +36,8 @@ const
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
wTags, wUses, wOperator, wGcSafe}
exprPragmas* = {wLine}
wTags, wLocks, wGcSafe}
exprPragmas* = {wLine, wLocks}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
@@ -49,23 +49,23 @@ const
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
wRaises, wUses, wTags, wGcSafe}
wRaises, wLocks, wTags, wGcSafe}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
wBorrow, wGcSafe}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError}
wImportCpp, wImportObjC, wError, wGuard}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
wGensym, wInject, wCodegenDecl}
wGensym, wInject, wCodegenDecl, wGuard}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
letPragmas* = varPragmas
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
wThread, wRaises, wUses, wTags, wGcSafe}
wThread, wRaises, wLocks, wTags, wGcSafe}
allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
@@ -514,26 +514,27 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) =
else:
invalidPragma(n)
proc pragmaUses(c: PContext, n: PNode) =
proc processExc(c: PContext, x: PNode): PNode =
if x.kind in {nkAccQuoted, nkIdent, nkSym,
nkOpenSymChoice, nkClosedSymChoice}:
if considerQuotedIdent(x).s == "*":
return newSymNode(ast.anyGlobal)
result = c.semExpr(c, x)
if result.kind != nkSym or sfGlobal notin result.sym.flags:
localError(x.info, "'$1' is not a global variable" % result.renderTree)
result = newSymNode(ast.anyGlobal)
if n.kind == nkExprColonExpr:
let it = n.sons[1]
if it.kind notin {nkCurly, nkBracket}:
n.sons[1] = processExc(c, it)
else:
for i in 0 .. <it.len:
it.sons[i] = processExc(c, it.sons[i])
proc pragmaLockStmt(c: PContext; it: PNode) =
if it.kind != nkExprColonExpr:
invalidPragma(it)
else:
invalidPragma(n)
let n = it[1]
if n.kind != nkBracket:
localError(n.info, errGenerated, "locks pragma takes a list of expressions")
else:
for i in 0 .. <n.len:
n.sons[i] = c.semExpr(c, n.sons[i])
proc pragmaLocks(c: PContext, it: PNode): int16 =
if it.kind != nkExprColonExpr:
invalidPragma(it)
else:
let n = it[1]
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:
result = int16(x)
proc typeBorrow(sym: PSym, n: PNode) =
if n.kind == nkExprColonExpr:
@@ -563,11 +564,25 @@ proc deprecatedStmt(c: PContext; pragma: PNode) =
else:
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,
validPragmas: TSpecialWords): bool =
var it = n.sons[i]
var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
if key.kind == nkIdent:
if key.kind == nkIdent:
var userPragma = strTableGet(c.userPragmas, key.ident)
if userPragma != nil:
inc c.instCounter
@@ -599,11 +614,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wAlign:
if sym.typ == nil: invalidPragma(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)
else:
sym.typ.align = align
of wSize:
sym.typ.align = align.int16
of wSize:
if sym.typ == nil: invalidPragma(it)
var size = expectIntLit(c, it)
if not isPowerOfTwo(size) or size <= 0 or size > 8:
@@ -806,10 +821,15 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
if sym == nil: invalidPragma(it)
of wLine: pragmaLine(c, it)
of wRaises, wTags: pragmaRaisesOrTags(c, it)
of wUses: pragmaUses(c, it)
of wOperator:
if sym == nil: invalidPragma(it)
else: sym.position = expectIntLit(c, it)
of wLocks:
if sym == nil: pragmaLockStmt(c, it)
elif sym.typ == nil: invalidPragma(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:
if it.kind != nkExprColonExpr:
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))
else:
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)
else: internalError(info, "decodeLoc " & r.s[r.pos])
@@ -326,7 +321,7 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
result.size = - 1
if r.s[r.pos] == '=':
inc(r.pos)
result.align = decodeVInt(r.s, r.pos)
result.align = decodeVInt(r.s, r.pos).int16
else:
result.align = 2
decodeLoc(r, result.loc, info)

View File

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

View File

@@ -1626,6 +1626,10 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType;
initIdTable(bindings)
bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t)
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 =
result = n

View File

@@ -57,9 +57,13 @@ discard """
c()
--> 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
TEffects = object
@@ -71,12 +75,69 @@ type
init: seq[int] # list of initialized variables
guards: TModel # nested guards
locked: seq[PNode] # locked locations
gcUnsafe, isRecursive: bool
gcUnsafe, isRecursive, isToplevel: bool
PEffects = var TEffects
proc isLocalVar(a: PEffects, s: PSym): bool =
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) =
if n.kind != nkSym: return
let s = n.sym
@@ -93,13 +154,6 @@ proc initVarViaNew(a: PEffects, n: PNode) =
# are initialized:
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) =
let s = n.sym
if isLocalVar(a, s):
@@ -110,9 +164,8 @@ proc useVar(a: PEffects, n: PNode) =
message(n.info, warnUninit, s.name.s)
# prevent superfluous warnings about the same variable:
a.init.add s.id
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind == skVar:
when trackGlobals:
a.addUse(copyNode(n))
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}:
if s.guard != nil: guardGlobal(a, n, s.guard)
if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and
tfGcSafe notin s.typ.flags:
if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
@@ -186,13 +239,6 @@ proc mergeTags(a: PEffects, b, comesFrom: PNode) =
else:
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) =
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))
@@ -322,7 +368,7 @@ proc documentRaises*(n: PNode) =
if n.sons[namePos].kind != nkSym: return
documentEffect(n, n.sons[pragmasPos], wRaises, exceptionEffects)
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 == {}
@@ -342,10 +388,6 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
tracked.gcUnsafe = true
when trackGlobals:
let usesSpec = effectSpec(pragma, wUses)
mergeUses(tracked, usesSpec, n)
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
let n = n.skipConv
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:
addEffect(tracked, createRaise(n))
addTag(tracked, createTag(n))
when trackGlobals: addUse(tracked, createAnyGlobal(n))
# assume GcUnsafe unless in its type; 'forward' does not matter:
if notGcSafe(op):
if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
@@ -389,7 +430,6 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n)
when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n)
if notGcSafe(op):
if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
tracked.gcUnsafe = true
@@ -529,12 +569,9 @@ proc track(tracked: PEffects, n: PNode) =
elif isIndirectCall(a, tracked.owner):
addEffect(tracked, createRaise(n))
addTag(tracked, createTag(n))
when trackGlobals: addUse(tracked, createAnyGlobal(n))
# XXX handle 'gcsafe' properly for callbacks!
else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n)
when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n)
if notGcSafe(op) and not importedFromC(a):
# and it's not a recursive call:
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])
for i in 0 .. <safeLen(n):
track(tracked, n.sons[i])
of nkDotExpr:
guardDotAccess(tracked, n)
for i in 0 .. <len(n): track(tracked, n.sons[i])
of nkCheckedFieldExpr:
track(tracked, n.sons[0])
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:
addDiscriminantFact(tracked.guards, x)
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,
nkMacroDef, nkTemplateDef:
discard
@@ -648,10 +696,6 @@ proc checkMethodEffects*(disp, branch: PSym) =
if not isNil(tagsSpec):
checkRaisesSpec(tagsSpec, actual.sons[tagEffects],
"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):
localError(branch.info, "base method is GC-safe, but '$1' is not" %
branch.name.s)
@@ -663,29 +707,25 @@ proc setEffectsForProcType*(t: PType, n: PNode) =
let
raisesSpec = effectSpec(n, wRaises)
tagsSpec = effectSpec(n, wTags)
usesSpec = effectSpec(n, wUses)
if not isNil(raisesSpec) or not isNil(tagsSpec) or not isNil(usesSpec):
if not isNil(raisesSpec) or not isNil(tagsSpec):
internalAssert effects.len == 0
newSeq(effects.sons, effectListLen)
if not isNil(raisesSpec):
effects.sons[exceptionEffects] = raisesSpec
if not isNil(tagsSpec):
effects.sons[tagEffects] = tagsSpec
if not isNil(usesSpec):
effects.sons[usesEffects] = usesSpec
proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
newSeq(effects.sons, effectListLen)
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
effects.sons[usesEffects] = newNodeI(nkArgList, s.info)
t.exc = effects.sons[exceptionEffects]
t.tags = effects.sons[tagEffects]
t.uses = effects.sons[usesEffects]
t.owner = s
t.init = @[]
t.guards = @[]
t.locked = @[]
proc trackProc*(s: PSym, body: PNode) =
var effects = s.typ.n.sons[0]
@@ -717,16 +757,12 @@ proc trackProc*(s: PSym, body: PNode) =
# after the check, use the formal spec:
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 sfThread in s.flags and t.gcUnsafe:
#localError(s.info, warnGcUnsafe2, s.name.s)
localError(s.info, "'$1' is not GC-safe" % s.name.s)
if optThreads in gGlobalOptions:
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
proc trackTopLevelStmt*(module: PSym; n: PNode) =
@@ -736,5 +772,5 @@ proc trackTopLevelStmt*(module: PSym; n: PNode) =
var effects = newNode(nkEffectList, n.info)
var t: TEffects
initEffects(effects, module, t)
t.isToplevel = true
track(t, n)

View File

@@ -1300,9 +1300,14 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
let pragmaList = n.sons[0]
pragma(c, nil, pragmaList, exprPragmas)
result = semExpr(c, n.sons[1])
n.sons[1] = result
for i in 0 .. <pragmaList.len:
if whichPragma(pragmaList.sons[i]) == wLine:
setLine(result, pragmaList.sons[i].info)
case whichPragma(pragmaList.sons[i])
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 =
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) =
m.typ.kind = kind
m.typ.align = size
m.typ.align = size.int16
m.typ.size = size
proc processMagicType(c: PContext, m: PSym) =

View File

@@ -1271,7 +1271,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
#internalError("computeSizeAux()")
result = szUnknownSize
typ.size = result
typ.align = int(a)
typ.align = int16(a)
proc computeSize(typ: PType): BiggestInt =
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
for i in 0 .. <L: gen(c, n.sons[i])
gen(c, n.sons[L], dest, flags)
of nkPragmaBlock:
gen(c, n.lastSon, dest, flags)
of nkDiscardStmt:
unused(n, dest)
gen(c, n.sons[0])

View File

@@ -64,7 +64,7 @@ type
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wAsmNoStackFrame,
wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wUses,
wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks,
wAuto, wBool, wCatch, wChar, wClass,
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@@ -147,7 +147,7 @@ const
"computedgoto", "injectstmt",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
"asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
"guard", "uses",
"guard", "locks",
"auto", "bool", "catch", "char", "class",
"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
- Pegs do not work at compile-time
- # echo type.int
version 0.9.6
=============
@@ -15,7 +15,6 @@ version 0.9.6
- split docgen into separate tool
- .benign pragma
- scopes are still broken for generic instantiation!
- implicit deref for parameter matching
Concurrency
-----------
@@ -23,7 +22,6 @@ Concurrency
- 'deepCopy' needs to be instantiated for
generics *when the type is constructed*
- test 'deepCopy'
- overloading of '='; general lift mechanism
- the disjoint checker needs to deal with 'a = spawn f(); g = spawn f()'
- implement 'foo[1..4] = spawn(f[4..7])'
@@ -63,6 +61,9 @@ Bugs
version 0.9.x
=============
- implicit deref for parameter matching
- overloading of '='; general lift mechanism
- pragmas need 'bindSym' support
- pragmas need re-work: 'push' is dangerous, 'hasPragma' does not work
reliably with user-defined pragmas