implements https://github.com/nim-lang/RFCs/issues/258 (#15503)
* implements https://github.com/nim-lang/RFCs/issues/258 * don't be too strict with custom pragma blocks * cast pragmas: documentation * added most missing inference query procs to effecttraits.nim
This commit is contained in:
parent
acd71dd6bb
commit
92163fa330
16 changed files with 232 additions and 68 deletions
|
|
@ -121,3 +121,5 @@ proc initDefines*(symbols: StringTableRef) =
|
|||
defineSymbol("nimHasLentIterators")
|
||||
defineSymbol("nimHasDeclaredMagic")
|
||||
defineSymbol("nimHasStacktracesModule")
|
||||
defineSymbol("nimHasEffectTraitsModule")
|
||||
defineSymbol("nimHasCastPragmaBlocks")
|
||||
|
|
|
|||
|
|
@ -485,17 +485,24 @@ proc setOrTableConstr(p: var Parser): PNode =
|
|||
eat(p, tkCurlyRi) # skip '}'
|
||||
|
||||
proc parseCast(p: var Parser): PNode =
|
||||
#| castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
|
||||
#| castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') /
|
||||
# ('(' optInd exprColonEqExpr optPar ')')
|
||||
result = newNodeP(nkCast, p)
|
||||
getTok(p)
|
||||
eat(p, tkBracketLe)
|
||||
optInd(p, result)
|
||||
result.add(parseTypeDesc(p))
|
||||
optPar(p)
|
||||
eat(p, tkBracketRi)
|
||||
eat(p, tkParLe)
|
||||
optInd(p, result)
|
||||
result.add(parseExpr(p))
|
||||
if p.tok.tokType == tkBracketLe:
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
result.add(parseTypeDesc(p))
|
||||
optPar(p)
|
||||
eat(p, tkBracketRi)
|
||||
eat(p, tkParLe)
|
||||
optInd(p, result)
|
||||
result.add(parseExpr(p))
|
||||
else:
|
||||
result.add p.emptyNode
|
||||
eat(p, tkParLe)
|
||||
optInd(p, result)
|
||||
result.add(exprColonEqExpr(p))
|
||||
optPar(p)
|
||||
eat(p, tkParRi)
|
||||
|
||||
|
|
|
|||
|
|
@ -771,6 +771,15 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
|||
if key.kind == nkBracketExpr:
|
||||
processNote(c, it)
|
||||
return
|
||||
elif key.kind == nkCast:
|
||||
if comesFromPush:
|
||||
localError(c.config, n.info, "a 'cast' pragma cannot be pushed")
|
||||
elif not isStatement:
|
||||
localError(c.config, n.info, "'cast' pragma only allowed in a statement context")
|
||||
case whichPragma(key[1])
|
||||
of wRaises, wTags: pragmaRaisesOrTags(c, key[1])
|
||||
else: discard
|
||||
return
|
||||
elif key.kind notin nkIdentKinds:
|
||||
n[i] = semCustomPragma(c, it)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1057,9 +1057,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
|||
put(g, tkSymbol, "(wrong conv)")
|
||||
of nkCast:
|
||||
put(g, tkCast, "cast")
|
||||
put(g, tkBracketLe, "[")
|
||||
gsub(g, n, 0)
|
||||
put(g, tkBracketRi, "]")
|
||||
if n.len > 0 and n[0].kind != nkEmpty:
|
||||
put(g, tkBracketLe, "[")
|
||||
gsub(g, n, 0)
|
||||
put(g, tkBracketRi, "]")
|
||||
put(g, tkParLe, "(")
|
||||
gsub(g, n, 1)
|
||||
put(g, tkParRi, ")")
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ proc createTag(g: ModuleGraph; n: PNode): PNode =
|
|||
result.typ = g.sysTypeFromName(n.info, "RootEffect")
|
||||
if not n.isNil: result.info = n.info
|
||||
|
||||
proc addEffect(a: PEffects, e, comesFrom: PNode) =
|
||||
proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) =
|
||||
assert e.kind != nkRaiseStmt
|
||||
var aa = a.exc
|
||||
for i in a.bottom..<aa.len:
|
||||
|
|
@ -345,11 +345,11 @@ proc addTag(a: PEffects, e, comesFrom: PNode) =
|
|||
if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)): return
|
||||
throws(a.tags, e, comesFrom)
|
||||
|
||||
proc mergeEffects(a: PEffects, b, comesFrom: PNode) =
|
||||
proc mergeRaises(a: PEffects, b, comesFrom: PNode) =
|
||||
if b.isNil:
|
||||
addEffect(a, createRaise(a.graph, comesFrom), comesFrom)
|
||||
addRaiseEffect(a, createRaise(a.graph, comesFrom), comesFrom)
|
||||
else:
|
||||
for effect in items(b): addEffect(a, effect, comesFrom)
|
||||
for effect in items(b): addRaiseEffect(a, effect, comesFrom)
|
||||
|
||||
proc mergeTags(a: PEffects, b, comesFrom: PNode) =
|
||||
if b.isNil:
|
||||
|
|
@ -489,7 +489,7 @@ proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) =
|
|||
proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
||||
let pragma = s.ast[pragmasPos]
|
||||
let spec = effectSpec(pragma, wRaises)
|
||||
mergeEffects(tracked, spec, n)
|
||||
mergeRaises(tracked, spec, n)
|
||||
|
||||
let tagSpec = effectSpec(pragma, wTags)
|
||||
mergeTags(tracked, tagSpec, n)
|
||||
|
|
@ -535,7 +535,7 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
|||
of impYes: discard
|
||||
|
||||
proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
|
||||
addEffect(tracked, createRaise(tracked.graph, n), nil)
|
||||
addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
|
||||
addTag(tracked, createTag(tracked.graph, n), nil)
|
||||
let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
|
||||
else: op.lockLevel
|
||||
|
|
@ -581,7 +581,7 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType;
|
|||
elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner):
|
||||
markSideEffect(tracked, a)
|
||||
else:
|
||||
mergeEffects(tracked, effectList[exceptionEffects], n)
|
||||
mergeRaises(tracked, effectList[exceptionEffects], n)
|
||||
mergeTags(tracked, effectList[tagEffects], n)
|
||||
if notGcSafe(op):
|
||||
if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
|
||||
|
|
@ -780,7 +780,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
|||
assumeTheWorst(tracked, n, op)
|
||||
gcsafeAndSideeffectCheck()
|
||||
else:
|
||||
mergeEffects(tracked, effectList[exceptionEffects], n)
|
||||
mergeRaises(tracked, effectList[exceptionEffects], n)
|
||||
mergeTags(tracked, effectList[tagEffects], n)
|
||||
gcsafeAndSideeffectCheck()
|
||||
if a.kind != nkSym or a.sym.magic != mNBindSym:
|
||||
|
|
@ -837,6 +837,64 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
|||
# initVar(tracked, n[i].skipAddr, false)
|
||||
else: discard
|
||||
|
||||
type
|
||||
PragmaBlockContext = object
|
||||
oldLocked: int
|
||||
oldLockLevel: TLockLevel
|
||||
enforcedGcSafety, enforceNoSideEffects: bool
|
||||
oldExc, oldTags: int
|
||||
exc, tags: PNode
|
||||
|
||||
proc createBlockContext(tracked: PEffects): PragmaBlockContext =
|
||||
result = PragmaBlockContext(oldLocked: tracked.locked.len,
|
||||
oldLockLevel: tracked.currLockLevel,
|
||||
enforcedGcSafety: false, enforceNoSideEffects: false,
|
||||
oldExc: tracked.exc.len, oldTags: tracked.tags.len)
|
||||
|
||||
proc applyBlockContext(tracked: PEffects, bc: PragmaBlockContext) =
|
||||
if bc.enforcedGcSafety: tracked.inEnforcedGcSafe = true
|
||||
if bc.enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
|
||||
|
||||
proc unapplyBlockContext(tracked: PEffects; bc: PragmaBlockContext) =
|
||||
if bc.enforcedGcSafety: tracked.inEnforcedGcSafe = false
|
||||
if bc.enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false
|
||||
setLen(tracked.locked, bc.oldLocked)
|
||||
tracked.currLockLevel = bc.oldLockLevel
|
||||
if bc.exc != nil:
|
||||
# beware that 'raises: []' is very different from not saying
|
||||
# anything about 'raises' in the 'cast' at all. Same applies for 'tags'.
|
||||
setLen(tracked.exc.sons, bc.oldExc)
|
||||
for e in bc.exc:
|
||||
addRaiseEffect(tracked, e, e)
|
||||
if bc.tags != nil:
|
||||
setLen(tracked.tags.sons, bc.oldTags)
|
||||
for t in bc.tags:
|
||||
addTag(tracked, t, t)
|
||||
|
||||
proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
|
||||
case whichPragma(pragma)
|
||||
of wGcSafe:
|
||||
bc.enforcedGcSafety = true
|
||||
of wNoSideEffect:
|
||||
bc.enforceNoSideEffects = true
|
||||
of wTags:
|
||||
let n = pragma[1]
|
||||
if n.kind in {nkCurly, nkBracket}:
|
||||
bc.tags = n
|
||||
else:
|
||||
bc.tags = newNodeI(nkArgList, pragma.info)
|
||||
bc.tags.add n
|
||||
of wRaises:
|
||||
let n = pragma[1]
|
||||
if n.kind in {nkCurly, nkBracket}:
|
||||
bc.exc = n
|
||||
else:
|
||||
bc.exc = newNodeI(nkArgList, pragma.info)
|
||||
bc.exc.add n
|
||||
else:
|
||||
localError(tracked.config, pragma.info,
|
||||
"invalid pragma block: " & $pragma)
|
||||
|
||||
proc track(tracked: PEffects, n: PNode) =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
|
|
@ -854,7 +912,7 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
if n[0].kind != nkEmpty:
|
||||
n[0].info = n.info
|
||||
#throws(tracked.exc, n[0])
|
||||
addEffect(tracked, n[0], nil)
|
||||
addRaiseEffect(tracked, n[0], nil)
|
||||
for i in 0..<n.safeLen:
|
||||
track(tracked, n[i])
|
||||
createTypeBoundOps(tracked, n[0].typ, n.info)
|
||||
|
|
@ -862,7 +920,7 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
# A `raise` with no arguments means we're going to re-raise the exception
|
||||
# being handled or, if outside of an `except` block, a `ReraiseDefect`.
|
||||
# Here we add a `Exception` tag in order to cover both the cases.
|
||||
addEffect(tracked, createRaise(tracked.graph, n), nil)
|
||||
addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
|
||||
of nkCallKinds:
|
||||
trackCall(tracked, n)
|
||||
of nkDotExpr:
|
||||
|
|
@ -1011,26 +1069,24 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
checkForSink(tracked.config, tracked.owner, n[i])
|
||||
of nkPragmaBlock:
|
||||
let pragmaList = n[0]
|
||||
let oldLocked = tracked.locked.len
|
||||
let oldLockLevel = tracked.currLockLevel
|
||||
var enforcedGcSafety = false
|
||||
var enforceNoSideEffects = false
|
||||
var bc = createBlockContext(tracked)
|
||||
for i in 0..<pragmaList.len:
|
||||
let pragma = whichPragma(pragmaList[i])
|
||||
if pragma == wLocks:
|
||||
case pragma
|
||||
of wLocks:
|
||||
lockLocations(tracked, pragmaList[i])
|
||||
elif pragma == wGcSafe:
|
||||
enforcedGcSafety = true
|
||||
elif pragma == wNoSideEffect:
|
||||
enforceNoSideEffects = true
|
||||
|
||||
if enforcedGcSafety: tracked.inEnforcedGcSafe = true
|
||||
if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
|
||||
of wGcSafe:
|
||||
bc.enforcedGcSafety = true
|
||||
of wNoSideEffect:
|
||||
bc.enforceNoSideEffects = true
|
||||
of wCast:
|
||||
castBlock(tracked, pragmaList[i][1], bc)
|
||||
else:
|
||||
discard
|
||||
applyBlockContext(tracked, bc)
|
||||
track(tracked, n.lastSon)
|
||||
if enforcedGcSafety: tracked.inEnforcedGcSafe = false
|
||||
if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false
|
||||
setLen(tracked.locked, oldLocked)
|
||||
tracked.currLockLevel = oldLockLevel
|
||||
unapplyBlockContext(tracked, bc)
|
||||
|
||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||
nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
|
||||
discard
|
||||
|
|
|
|||
|
|
@ -2169,7 +2169,7 @@ proc setLine(n: PNode, info: TLineInfo) =
|
|||
proc semPragmaBlock(c: PContext, n: PNode): PNode =
|
||||
checkSonsLen(n, 2, c.config)
|
||||
let pragmaList = n[0]
|
||||
pragma(c, nil, pragmaList, exprPragmas)
|
||||
pragma(c, nil, pragmaList, exprPragmas, isStatement = true)
|
||||
n[1] = semExpr(c, n[1])
|
||||
result = n
|
||||
result.typ = n[1].typ
|
||||
|
|
|
|||
|
|
@ -1009,6 +1009,7 @@ proc transform(c: PTransf, n: PNode): PNode =
|
|||
# Constants can be inlined here, but only if they cannot result in a cast
|
||||
# in the back-end (e.g. var p: pointer = someProc)
|
||||
let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and
|
||||
n.typ != nil and
|
||||
n.typ.kind == tyPointer
|
||||
if not exprIsPointerCast:
|
||||
var cnst = getConstExpr(c.module, result, c.graph)
|
||||
|
|
|
|||
|
|
@ -130,7 +130,19 @@ proc isRange*(n: PNode): bool {.inline.} =
|
|||
|
||||
proc whichPragma*(n: PNode): TSpecialWord =
|
||||
let key = if n.kind in nkPragmaCallKinds and n.len > 0: n[0] else: n
|
||||
if key.kind == nkIdent: result = whichKeyword(key.ident)
|
||||
case key.kind
|
||||
of nkIdent: result = whichKeyword(key.ident)
|
||||
of nkSym: result = whichKeyword(key.sym.name)
|
||||
of nkCast: result = wCast
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
result = whichPragma(key[0])
|
||||
else: result = wInvalid
|
||||
|
||||
proc isNoSideEffectPragma*(n: PNode): bool =
|
||||
var k = whichPragma(n)
|
||||
if k == wCast:
|
||||
k = whichPragma(n[1])
|
||||
result = k == wNoSideEffect
|
||||
|
||||
proc findPragma*(n: PNode, which: TSpecialWord): PNode =
|
||||
if n.kind == nkPragma:
|
||||
|
|
|
|||
|
|
@ -29,8 +29,7 @@
|
|||
## for a high-level description of how borrow checking works.
|
||||
|
||||
import ast, types, lineinfos, options, msgs, renderer, typeallowed
|
||||
from trees import getMagic, whichPragma, stupidStmtListExpr
|
||||
from wordrecg import wNoSideEffect
|
||||
from trees import getMagic, isNoSideEffectPragma, stupidStmtListExpr
|
||||
from isolation_check import canAlias
|
||||
|
||||
type
|
||||
|
|
@ -713,7 +712,7 @@ proc traverse(c: var Partitions; n: PNode) =
|
|||
let pragmaList = n[0]
|
||||
var enforceNoSideEffects = 0
|
||||
for i in 0..<pragmaList.len:
|
||||
if whichPragma(pragmaList[i]) == wNoSideEffect:
|
||||
if isNoSideEffectPragma(pragmaList[i]):
|
||||
enforceNoSideEffects = 1
|
||||
break
|
||||
|
||||
|
|
|
|||
|
|
@ -262,11 +262,25 @@ proc registerAdditionalOps*(c: PCtx) =
|
|||
registerCallback c, "stdlib.times.getTime", proc (a: VmArgs) {.nimcall.} =
|
||||
setResult(a, times.getTime().toLit)
|
||||
|
||||
registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) =
|
||||
proc getEffectList(c: PCtx; a: VmArgs; effectIndex: int) =
|
||||
let fn = getNode(a, 0)
|
||||
if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].len >= effectListLen and
|
||||
fn.typ.n[0][exceptionEffects] != nil:
|
||||
fn.typ.n[0][effectIndex] != nil:
|
||||
var list = newNodeI(nkBracket, fn.info)
|
||||
for e in fn.typ.n[0][exceptionEffects]:
|
||||
for e in fn.typ.n[0][effectIndex]:
|
||||
list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info)
|
||||
setResult(a, list)
|
||||
|
||||
registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) =
|
||||
getEffectList(c, a, exceptionEffects)
|
||||
registerCallback c, "stdlib.effecttraits.getTagsListImpl", proc (a: VmArgs) =
|
||||
getEffectList(c, a, tagEffects)
|
||||
|
||||
registerCallback c, "stdlib.effecttraits.isGcSafeImpl", proc (a: VmArgs) =
|
||||
let fn = getNode(a, 0)
|
||||
setResult(a, fn.typ != nil and tfGcSafe in fn.typ.flags)
|
||||
|
||||
registerCallback c, "stdlib.effecttraits.hasNoSideEffectsImpl", proc (a: VmArgs) =
|
||||
let fn = getNode(a, 0)
|
||||
setResult(a, (fn.typ != nil and tfNoSideEffect in fn.typ.flags) or
|
||||
(fn.kind == nkSym and fn.sym.kind == skFunc))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue