diff options
author | Araq <rumpf_a@web.de> | 2014-09-21 18:39:00 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2014-09-21 18:39:00 +0200 |
commit | 7916b1f9aa93fca368afda8b18396230ac7f338c (patch) | |
tree | 04072aeee4ae211b91c088a7267455b82daf9787 /compiler | |
parent | 4800acf6ab92799316b270c991b6e8c01454608f (diff) | |
download | Nim-7916b1f9aa93fca368afda8b18396230ac7f338c.tar.gz |
implemented 'guard' annotation
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 16 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 4 | ||||
-rw-r--r-- | compiler/ccgstmts.nim | 4 | ||||
-rw-r--r-- | compiler/cgen.nim | 8 | ||||
-rw-r--r-- | compiler/guards.nim | 16 | ||||
-rw-r--r-- | compiler/jsgen.nim | 5 | ||||
-rw-r--r-- | compiler/magicsys.nim | 2 | ||||
-rw-r--r-- | compiler/msgs.nim | 9 | ||||
-rw-r--r-- | compiler/pragmas.nim | 86 | ||||
-rw-r--r-- | compiler/rodread.nim | 7 | ||||
-rw-r--r-- | compiler/rodwrite.nim | 3 | ||||
-rw-r--r-- | compiler/semexprs.nim | 4 | ||||
-rw-r--r-- | compiler/sempass2.nim | 134 | ||||
-rw-r--r-- | compiler/semstmts.nim | 9 | ||||
-rw-r--r-- | compiler/semtypes.nim | 2 | ||||
-rw-r--r-- | compiler/types.nim | 2 | ||||
-rw-r--r-- | compiler/vmgen.nim | 2 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 4 |
18 files changed, 198 insertions, 119 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 2ba9cdb2e..e3e8fd49f 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 8914ad0de..ca34cfa3a 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -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 diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index efa8b0adc..e30b2c561 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -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 diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 490aeec4b..743e877a1 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -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 = {} diff --git a/compiler/guards.nim b/compiler/guards.nim index 05128f409..cd0aaf296 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -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: diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 652f439a9..4772aecb5 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -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 diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index a794253df..6d4c65268 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -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)) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index da2fe122d..8368fe5fd 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -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", diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 24b2f6750..815369066 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -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) +proc pragmaLockStmt(c: PContext; it: PNode) = + if it.kind != nkExprColonExpr: + invalidPragma(it) + else: + let n = it[1] + if n.kind != nkBracket: + localError(n.info, errGenerated, "locks pragma takes a list of expressions") else: - for i in 0 .. <it.len: - it.sons[i] = processExc(c, it.sons[i]) + 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: - invalidPragma(n) + 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) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 3cc37fb98..3b3538e5d 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -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) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 7eacb0883..9fed7ac52 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -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) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 11215ae68..64914cb25 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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 diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index fee70af15..794b7ec80 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -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) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5cb2eabf8..bd9ca6a0e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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]) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 559bf2e06..765ab5be8 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -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) = diff --git a/compiler/types.nim b/compiler/types.nim index 7a9b6bb85..27282e684 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -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 diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index e7f7ad742..fac3cf1bf 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -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]) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index e3b4b3062..068f59c71 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -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", |