diff options
-rw-r--r-- | compiler/ast.nim | 13 | ||||
-rw-r--r-- | compiler/pragmas.nim | 20 | ||||
-rw-r--r-- | compiler/sempass2.nim | 70 | ||||
-rw-r--r-- | compiler/types.nim | 15 |
4 files changed, 78 insertions, 40 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 919a1a3fd..1fbbd56d4 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -770,6 +770,7 @@ type # it won't cause problems TTypeSeq* = seq[PType] + TLockLevel* = distinct int16 TType* {.acyclic.} = object of TIdObj # \ # types are identical iff they have the # same id; there may be multiple copies of a type @@ -797,7 +798,7 @@ type size*: BiggestInt # the size of the type in bytes # -1 means that the size is unkwown align*: int16 # the type's alignment requirements - lockLevel*: int16 # lock level as required for deadlock checking + lockLevel*: TLockLevel # lock level as required for deadlock checking loc*: TLoc TPair*{.final.} = object @@ -1165,7 +1166,15 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, result.sons = @[name, pattern, genericParams, params, pragmas, exceptions, body] -const UnspecifiedLockLevel* = -1 +const + UnspecifiedLockLevel* = TLockLevel(-1'i16) + MaxLockLevel* = 1000'i16 + UnknownLockLevel* = TLockLevel(1001'i16) + +proc `$`*(x: TLockLevel): string = + if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>" + elif x.ord == UnknownLockLevel.ord: result = "<unknown>" + else: result = $int16(x) proc newType(kind: TTypeKind, owner: PSym): PType = new(result) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 5f9ee218f..510023bb8 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -525,16 +525,16 @@ proc pragmaLockStmt(c: PContext; it: PNode) = for i in 0 .. <n.len: n.sons[i] = c.semExpr(c, n.sons[i]) -proc pragmaLocks(c: PContext, it: PNode): int16 = +proc pragmaLocks(c: PContext, it: PNode): TLockLevel = if it.kind != nkExprColonExpr: invalidPragma(it) else: 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)") + if x < 0 or x > MaxLockLevel: + localError(it[1].info, "integer must be within 0.." & $MaxLockLevel) else: - result = int16(x) + result = TLockLevel(x) proc typeBorrow(sym: PSym, n: PNode) = if n.kind == nkExprColonExpr: @@ -571,10 +571,14 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = 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) + # First check if the guard is a global variable: + result = qualifiedLookUp(c, n, {}) + if result.isNil or result.kind notin {skLet, skVar} or + sfGlobal notin result.flags: + # We return a dummy symbol; later passes over the type will repair it. + # 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) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 981e69900..94a3b49e3 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -75,35 +75,42 @@ type guards: TModel # nested guards locked: seq[PNode] # locked locations gcUnsafe, isRecursive, isToplevel: bool - maxLockLevel, currLockLevel: int16 + maxLockLevel, currLockLevel: TLockLevel PEffects = var TEffects +proc `<`(a, b: TLockLevel): bool {.borrow.} +proc `<=`(a, b: TLockLevel): bool {.borrow.} +proc `==`(a, b: TLockLevel): bool {.borrow.} +proc max(a, b: TLockLevel): TLockLevel {.borrow.} + 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 = +proc getLockLevel(t: PType): TLockLevel = 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 + result = t.n.intVal.TLockLevel proc lockLocations(a: PEffects; pragma: PNode) = if pragma.kind != nkExprColonExpr: - internalError(pragma.info, "no colon") + localError(pragma.info, errGenerated, "locks pragma without argument") return - var firstLL = -1'i16 + var firstLL = TLockLevel(-1'i16) for x in pragma[1]: let thisLL = getLockLevel(x.typ) - if thisLL != 0: - if firstLL < 0: firstLL = thisLL + if thisLL != 0.TLockLevel: + if thisLL < 0.TLockLevel or thisLL > MaxLockLevel.TLockLevel: + localError(x.info, "invalid lock level: " & $thisLL) + elif firstLL < 0.TLockLevel: 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: + if firstLL >= 0.TLockLevel and firstLL != a.currLockLevel: + if a.currLockLevel > 0.TLockLevel and a.currLockLevel <= firstLL: localError(pragma.info, errGenerated, "invalid nested locking") a.currLockLevel = firstLL @@ -396,14 +403,23 @@ 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: +proc getLockLevel(s: PSym): TLockLevel = + result = s.typ.lockLevel + if result == UnspecifiedLockLevel: + if {sfImportc, sfNoSideEffect} * s.flags != {} or + tfNoSideEffect in s.typ.flags: + result = 0.TLockLevel + else: + result = UnknownLockLevel + +proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) = + if lockLevel >= tracked.currLockLevel: # if in lock section: - if tracked.currLockLevel > 0: + if tracked.currLockLevel > 0.TLockLevel: localError n.info, errGenerated, - "expected lock level <= " & $tracked.currLockLevel & - " but got lock level " & $t.lockLevel - tracked.maxLockLevel = max(tracked.maxLockLevel, t.lockLevel) + "expected lock level < " & $tracked.currLockLevel & + " but got lock level " & $lockLevel + tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel) proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = let pragma = s.ast.sons[pragmasPos] @@ -416,7 +432,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) + mergeLockLevels(tracked, n, s.getLockLevel) proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = let n = n.skipConv @@ -436,6 +452,13 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree) of impYes: discard +proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) = + addEffect(tracked, createRaise(n)) + addTag(tracked, createTag(n)) + let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel + else: op.lockLevel + mergeLockLevels(tracked, n, lockLevel) + proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = let op = skipConvAndClosure(n).typ if op != nil and op.kind == tyProc and n.kind != nkNilLit: @@ -451,8 +474,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = propagateEffects(tracked, n, n.sym) else: # we have no explicit effects so assume the worst: - addEffect(tracked, createRaise(n)) - addTag(tracked, createTag(n)) + assumeTheWorst(tracked, n, op) # assume GcUnsafe unless in its type; 'forward' does not matter: if notGcSafe(op): if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n)) @@ -588,8 +610,12 @@ proc track(tracked: PEffects, n: PNode) = # are indistinguishable from normal procs (both have tyProc type) and # we can detect them only by checking for attached nkEffectList. if op != nil and op.kind == tyProc and op.n.sons[0].kind == nkEffectList: - if a.kind == nkSym and a.sym == tracked.owner: - tracked.isRecursive = true + if a.kind == nkSym: + if a.sym == tracked.owner: tracked.isRecursive = true + # even for recursive calls we need to check the lock levels (!): + mergeLockLevels(tracked, n, a.sym.getLockLevel) + else: + mergeLockLevels(tracked, n, op.lockLevel) var effectList = op.n.sons[0] if a.kind == nkSym and a.sym.kind == skMethod: propagateEffects(tracked, n, a.sym) @@ -597,8 +623,7 @@ proc track(tracked: PEffects, n: PNode) = if isForwardedProc(a): propagateEffects(tracked, n, a.sym) elif isIndirectCall(a, tracked.owner): - addEffect(tracked, createRaise(n)) - addTag(tracked, createTag(n)) + assumeTheWorst(tracked, n, op) else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) @@ -607,7 +632,6 @@ 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: diff --git a/compiler/types.nim b/compiler/types.nim index 27282e684..5ef2d627b 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -530,15 +530,16 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if i < sonsLen(t) - 1: add(result, ", ") add(result, ')') if t.sons[0] != nil: add(result, ": " & typeToString(t.sons[0])) - var prag: string - if t.callConv != ccDefault: prag = CallingConvToStr[t.callConv] - else: prag = "" - if tfNoSideEffect in t.flags: + var prag = if t.callConv == ccDefault: "" else: CallingConvToStr[t.callConv] + if tfNoSideEffect in t.flags: addSep(prag) add(prag, "noSideEffect") if tfThread in t.flags: addSep(prag) add(prag, "gcsafe") + if t.lockLevel.ord != UnspecifiedLockLevel.ord: + addSep(prag) + add(prag, "locks: " & $t.lockLevel) if len(prag) != 0: add(result, "{." & prag & ".}") of tyVarargs, tyIter: result = typeToStr[t.kind] % typeToString(t.sons[0]) @@ -579,8 +580,7 @@ proc firstOrd(t: PType): BiggestInt = else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position - of tyGenericInst, tyDistinct, tyConst, tyMutable, - tyTypeDesc, tyFieldAccessor: + of tyGenericInst, tyDistinct, tyConst, tyMutable, tyTypeDesc, tyFieldAccessor: result = firstOrd(lastSon(t)) of tyOrdinal: if t.len > 0: result = firstOrd(lastSon(t)) @@ -1360,7 +1360,8 @@ proc compatibleEffects*(formal, actual: PType): bool = if real.len == 0: return false result = compatibleEffectsAux(st, real.sons[tagEffects]) if not result: return - result = true + result = formal.lockLevel.ord < 0 or + actual.lockLevel.ord <= formal.lockLevel.ord proc isCompileTimeOnly*(t: PType): bool {.inline.} = result = t.kind in {tyTypeDesc, tyStatic} |