mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 14:00:35 +00:00
implemented 'guard' annotation
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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) =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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",
|
||||
|
||||
37
tests/parallel/tguard1.nim
Normal file
37
tests/parallel/tguard1.nim
Normal 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()
|
||||
27
tests/parallel/tguard2.nim
Normal file
27
tests/parallel/tguard2.nim
Normal 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()
|
||||
7
todo.txt
7
todo.txt
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user