implemented 'guard' annotation
This commit is contained in:
parent
4800acf6ab
commit
7916b1f9aa
21 changed files with 267 additions and 123 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 = {}
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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}:
|
|
||||||
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])
|
|
||||||
else:
|
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) =
|
proc typeBorrow(sym: PSym, n: PNode) =
|
||||||
if n.kind == nkExprColonExpr:
|
if n.kind == nkExprColonExpr:
|
||||||
|
|
@ -563,11 +564,25 @@ 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]
|
||||||
var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
|
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)
|
var userPragma = strTableGet(c.userPragmas, key.ident)
|
||||||
if userPragma != nil:
|
if userPragma != nil:
|
||||||
inc c.instCounter
|
inc c.instCounter
|
||||||
|
|
@ -599,11 +614,11 @@ 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)
|
||||||
if not isPowerOfTwo(size) or size <= 0 or size > 8:
|
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)
|
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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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) =
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
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
|
- 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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue