diff options
-rw-r--r-- | compiler/ast.nim | 3 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 2 | ||||
-rw-r--r-- | compiler/pragmas.nim | 12 | ||||
-rw-r--r-- | compiler/sempass2.nim | 49 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 7 | ||||
-rw-r--r-- | lib/nimbase.h | 2 | ||||
-rw-r--r-- | todo.txt | 2 |
7 files changed, 58 insertions, 19 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index e3e8fd49f..919a1a3fd 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1165,6 +1165,7 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, result.sons = @[name, pattern, genericParams, params, pragmas, exceptions, body] +const UnspecifiedLockLevel* = -1 proc newType(kind: TTypeKind, owner: PSym): PType = new(result) @@ -1173,6 +1174,7 @@ proc newType(kind: TTypeKind, owner: PSym): PType = result.size = - 1 result.align = 2 # default alignment result.id = getID() + result.lockLevel = UnspecifiedLockLevel when debugIds: registerId(result) #if result.id < 2000: @@ -1195,6 +1197,7 @@ proc assignType(dest, src: PType) = dest.align = src.align dest.destructor = src.destructor dest.deepCopy = src.deepCopy + dest.lockLevel = src.lockLevel # this fixes 'type TLock = TSysLock': if src.sym != nil: if dest.sym != nil: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 1b0b9e103..4b52d3956 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -898,7 +898,7 @@ include ccgtrav proc genDeepCopyProc(m: BModule; s: PSym; result: PRope) = genProc(m, s) - appf(m.s[cfsTypeInit3], "$1.deepcopy =(N_NIMCALL_PTR(void*,)(void*))$2;$n", + appf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n", [result, s.loc.r]) proc genTypeInfo(m: BModule, t: PType): PRope = diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 815369066..5f9ee218f 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -529,12 +529,12 @@ 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) + if it[1].kind != nkNilLit: + let x = expectIntLit(c, it) + if x < 0 or x > high(int16): + localError(it[1].info, "integer must be within 0..high(int16)") + else: + result = int16(x) proc typeBorrow(sym: PSym, n: PNode) = if n.kind == nkExprColonExpr: diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index a330e0ecd..981e69900 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -69,24 +69,44 @@ type TEffects = object exc: PNode # stack of exceptions tags: PNode # list of tags - uses: PNode # list of used global variables bottom: int owner: PSym init: seq[int] # list of initialized variables guards: TModel # nested guards locked: seq[PNode] # locked locations gcUnsafe, isRecursive, isToplevel: bool + maxLockLevel, currLockLevel: int16 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 getLockLevel(t: PType): int16 = + var t = t + # tyGenericInst(TLock {tyGenericBody}, tyStatic, tyObject): + if t.kind == tyGenericInst and t.len == 3: t = t.sons[1] + if t.kind == tyStatic and t.n != nil and t.n.kind in {nkCharLit..nkInt64Lit}: + result = t.n.intVal.int16 + proc lockLocations(a: PEffects; pragma: PNode) = if pragma.kind != nkExprColonExpr: internalError(pragma.info, "no colon") return + var firstLL = -1'i16 for x in pragma[1]: + let thisLL = getLockLevel(x.typ) + if thisLL != 0: + if firstLL < 0: firstLL = thisLL + elif firstLL != thisLL: + localError(x.info, errGenerated, + "multi-lock requires the same static lock level for every operand") + a.maxLockLevel = max(a.maxLockLevel, firstLL) a.locked.add x + if firstLL >= 0 and firstLL != a.currLockLevel: + if a.currLockLevel > 0 and a.currLockLevel < firstLL: + localError(pragma.info, errGenerated, + "invalid nested locking") + a.currLockLevel = firstLL proc guardGlobal(a: PEffects; n: PNode; guard: PSym) = # check whether the corresponding lock is held: @@ -242,7 +262,8 @@ proc mergeTags(a: PEffects, b, comesFrom: PNode) = 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)) - for e in items(a.uses): message(e.info, hintUser, e.sym.name.s) + #if a.maxLockLevel != 0: + # message(e.info, hintUser, "lockLevel: " & a.maxLockLevel) proc catches(tracked: PEffects, e: PType) = let e = skipTypes(e, skipPtrs) @@ -368,7 +389,6 @@ 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) template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {} @@ -376,6 +396,15 @@ proc importedFromC(n: PNode): bool = # when imported from C, we assume GC-safety. result = n.kind == nkSym and sfImportc in n.sym.flags +proc mergeLockLevels(tracked: PEffects, n: PNode, t: PType) = + if t.lockLevel > tracked.currLockLevel: + # if in lock section: + if tracked.currLockLevel > 0: + localError n.info, errGenerated, + "expected lock level <= " & $tracked.currLockLevel & + " but got lock level " & $t.lockLevel + tracked.maxLockLevel = max(tracked.maxLockLevel, t.lockLevel) + proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = let pragma = s.ast.sons[pragmasPos] let spec = effectSpec(pragma, wRaises) @@ -387,6 +416,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = if notGcSafe(s.typ) and sfImportc notin s.flags: if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n)) tracked.gcUnsafe = true + mergeLockLevels(tracked, n, s.typ) proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = let n = n.skipConv @@ -577,6 +607,7 @@ proc track(tracked: PEffects, n: PNode) = if not (a.kind == nkSym and a.sym == tracked.owner): message(n.info, warnGcUnsafe, renderTree(n)) tracked.gcUnsafe = true + mergeLockLevels(tracked, n, op) for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}: # may not look like an assignment, but it is: @@ -644,11 +675,13 @@ proc track(tracked: PEffects, n: PNode) = of nkPragmaBlock: let pragmaList = n.sons[0] let oldLocked = tracked.locked.len + let oldLockLevel = tracked.currLockLevel 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) + tracked.currLockLevel = oldLockLevel of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef: discard @@ -699,6 +732,10 @@ proc checkMethodEffects*(disp, branch: PSym) = if sfThread in disp.flags and notGcSafe(branch.typ): localError(branch.info, "base method is GC-safe, but '$1' is not" % branch.name.s) + if branch.typ.lockLevel > disp.typ.lockLevel: + localError(branch.info, + "base method has lock level $1, but dispatcher has $2" % + [$branch.typ.lockLevel, $disp.typ.lockLevel]) proc setEffectsForProcType*(t: PType, n: PNode) = var effects = t.n.sons[0] @@ -764,6 +801,12 @@ proc trackProc*(s: PSym, body: PNode) = else: localError(s.info, warnGcUnsafe2, s.name.s) if not t.gcUnsafe: s.typ.flags.incl tfGcSafe + if s.typ.lockLevel == UnspecifiedLockLevel: + s.typ.lockLevel = t.maxLockLevel + elif t.maxLockLevel > s.typ.lockLevel: + localError(s.info, + "declared lock level is $1, but real lock level is $2" % + [$s.typ.lockLevel, $t.maxLockLevel]) proc trackTopLevelStmt*(module: PSym; n: PNode) = if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index fa4d2cc0b..9c15be635 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -304,13 +304,6 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = # 'deepCopy' needs to be instantiated for # generics *when the type is constructed*: newbody.deepCopy = cl.c.instDeepCopy(cl.c, dc, result, cl.info) - when false: - var bindings: TIdTable - initIdTable(bindings) - debug newbody - bindings.idTablePut(dc.ast[genericParamsPos].sons[0].typ, newbody) - newbody.deepCopy = cl.c.semGenerateInstance(cl.c, dc, bindings, cl.info) - assert sfFromGeneric in newbody.deepCopy.flags proc eraseVoidParams*(t: PType) = if t.sons[0] != nil and t.sons[0].kind == tyEmpty: diff --git a/lib/nimbase.h b/lib/nimbase.h index f49855076..2ca3bd886 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -141,9 +141,11 @@ __clang__ /* these compilers have a fastcall so use it: */ # define N_NIMCALL(rettype, name) rettype __fastcall name # define N_NIMCALL_PTR(rettype, name) rettype (__fastcall *name) +# define N_RAW_NIMCALL __fastcall #else # define N_NIMCALL(rettype, name) rettype name /* no modifier */ # define N_NIMCALL_PTR(rettype, name) rettype (*name) +# define N_RAW_NIMCALL #endif #define N_CLOSURE(rettype, name) N_NIMCALL(rettype, name) diff --git a/todo.txt b/todo.txt index 7f89678e2..c43132cd5 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,6 @@ version 0.10 ============ -- use the effect system for static deadlock prevention - Test nimfix on various babel packages - deprecate recursive tuples; tuple needs laxer type checking - string case should require an 'else' @@ -30,7 +29,6 @@ Low priority: - support for exception propagation? (hard to implement) - the copying of the 'ref Promise' into the thead local storage only happens to work due to the write barrier's implementation -- implement lock levels Misc |