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
|
|
@ -304,10 +304,13 @@ proc mydiv(a, b): int {.raises: [].} =
|
||||||
- `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via
|
- `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via
|
||||||
`--deepcopy:on`.
|
`--deepcopy:on`.
|
||||||
|
|
||||||
- Added a `std/effecttraits` module for introspection of the inferred `.raise` effects.
|
- Added a `std/effecttraits` module for introspection of the inferred effects.
|
||||||
We hope this enables `async` macros that are precise about the possible exceptions that
|
We hope this enables `async` macros that are precise about the possible exceptions that
|
||||||
can be raised.
|
can be raised.
|
||||||
- Added `critbits.toCritBitTree`, similar to `tables.toTable`, creates a new `CritBitTree` with given arguments.
|
- Added `critbits.toCritBitTree`, similar to `tables.toTable`, creates a new `CritBitTree` with given arguments.
|
||||||
|
- The pragma blocks `{.gcsafe.}: ...` and `{.noSideEffect.}: ...` can now also be
|
||||||
|
written as `{.cast(gcsafe).}: ...` and `{.cast(noSideEffect).}: ...`. This is the new
|
||||||
|
preferred way of writing these, emphasizing their unsafe nature.
|
||||||
|
|
||||||
|
|
||||||
## Compiler changes
|
## Compiler changes
|
||||||
|
|
|
||||||
|
|
@ -121,3 +121,5 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
defineSymbol("nimHasLentIterators")
|
defineSymbol("nimHasLentIterators")
|
||||||
defineSymbol("nimHasDeclaredMagic")
|
defineSymbol("nimHasDeclaredMagic")
|
||||||
defineSymbol("nimHasStacktracesModule")
|
defineSymbol("nimHasStacktracesModule")
|
||||||
|
defineSymbol("nimHasEffectTraitsModule")
|
||||||
|
defineSymbol("nimHasCastPragmaBlocks")
|
||||||
|
|
|
||||||
|
|
@ -485,10 +485,12 @@ proc setOrTableConstr(p: var Parser): PNode =
|
||||||
eat(p, tkCurlyRi) # skip '}'
|
eat(p, tkCurlyRi) # skip '}'
|
||||||
|
|
||||||
proc parseCast(p: var Parser): PNode =
|
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)
|
result = newNodeP(nkCast, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
eat(p, tkBracketLe)
|
if p.tok.tokType == tkBracketLe:
|
||||||
|
getTok(p)
|
||||||
optInd(p, result)
|
optInd(p, result)
|
||||||
result.add(parseTypeDesc(p))
|
result.add(parseTypeDesc(p))
|
||||||
optPar(p)
|
optPar(p)
|
||||||
|
|
@ -496,6 +498,11 @@ proc parseCast(p: var Parser): PNode =
|
||||||
eat(p, tkParLe)
|
eat(p, tkParLe)
|
||||||
optInd(p, result)
|
optInd(p, result)
|
||||||
result.add(parseExpr(p))
|
result.add(parseExpr(p))
|
||||||
|
else:
|
||||||
|
result.add p.emptyNode
|
||||||
|
eat(p, tkParLe)
|
||||||
|
optInd(p, result)
|
||||||
|
result.add(exprColonEqExpr(p))
|
||||||
optPar(p)
|
optPar(p)
|
||||||
eat(p, tkParRi)
|
eat(p, tkParRi)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -771,6 +771,15 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
if key.kind == nkBracketExpr:
|
if key.kind == nkBracketExpr:
|
||||||
processNote(c, it)
|
processNote(c, it)
|
||||||
return
|
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:
|
elif key.kind notin nkIdentKinds:
|
||||||
n[i] = semCustomPragma(c, it)
|
n[i] = semCustomPragma(c, it)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -1057,6 +1057,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
put(g, tkSymbol, "(wrong conv)")
|
put(g, tkSymbol, "(wrong conv)")
|
||||||
of nkCast:
|
of nkCast:
|
||||||
put(g, tkCast, "cast")
|
put(g, tkCast, "cast")
|
||||||
|
if n.len > 0 and n[0].kind != nkEmpty:
|
||||||
put(g, tkBracketLe, "[")
|
put(g, tkBracketLe, "[")
|
||||||
gsub(g, n, 0)
|
gsub(g, n, 0)
|
||||||
put(g, tkBracketRi, "]")
|
put(g, tkBracketRi, "]")
|
||||||
|
|
|
||||||
|
|
@ -325,7 +325,7 @@ proc createTag(g: ModuleGraph; n: PNode): PNode =
|
||||||
result.typ = g.sysTypeFromName(n.info, "RootEffect")
|
result.typ = g.sysTypeFromName(n.info, "RootEffect")
|
||||||
if not n.isNil: result.info = n.info
|
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
|
assert e.kind != nkRaiseStmt
|
||||||
var aa = a.exc
|
var aa = a.exc
|
||||||
for i in a.bottom..<aa.len:
|
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
|
if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)): return
|
||||||
throws(a.tags, e, comesFrom)
|
throws(a.tags, e, comesFrom)
|
||||||
|
|
||||||
proc mergeEffects(a: PEffects, b, comesFrom: PNode) =
|
proc mergeRaises(a: PEffects, b, comesFrom: PNode) =
|
||||||
if b.isNil:
|
if b.isNil:
|
||||||
addEffect(a, createRaise(a.graph, comesFrom), comesFrom)
|
addRaiseEffect(a, createRaise(a.graph, comesFrom), comesFrom)
|
||||||
else:
|
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) =
|
proc mergeTags(a: PEffects, b, comesFrom: PNode) =
|
||||||
if b.isNil:
|
if b.isNil:
|
||||||
|
|
@ -489,7 +489,7 @@ proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) =
|
||||||
proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
||||||
let pragma = s.ast[pragmasPos]
|
let pragma = s.ast[pragmasPos]
|
||||||
let spec = effectSpec(pragma, wRaises)
|
let spec = effectSpec(pragma, wRaises)
|
||||||
mergeEffects(tracked, spec, n)
|
mergeRaises(tracked, spec, n)
|
||||||
|
|
||||||
let tagSpec = effectSpec(pragma, wTags)
|
let tagSpec = effectSpec(pragma, wTags)
|
||||||
mergeTags(tracked, tagSpec, n)
|
mergeTags(tracked, tagSpec, n)
|
||||||
|
|
@ -535,7 +535,7 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
||||||
of impYes: discard
|
of impYes: discard
|
||||||
|
|
||||||
proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
|
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)
|
addTag(tracked, createTag(tracked.graph, n), nil)
|
||||||
let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
|
let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
|
||||||
else: op.lockLevel
|
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):
|
elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner):
|
||||||
markSideEffect(tracked, a)
|
markSideEffect(tracked, a)
|
||||||
else:
|
else:
|
||||||
mergeEffects(tracked, effectList[exceptionEffects], n)
|
mergeRaises(tracked, effectList[exceptionEffects], n)
|
||||||
mergeTags(tracked, effectList[tagEffects], n)
|
mergeTags(tracked, effectList[tagEffects], n)
|
||||||
if notGcSafe(op):
|
if notGcSafe(op):
|
||||||
if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
|
if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
|
||||||
|
|
@ -780,7 +780,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||||
assumeTheWorst(tracked, n, op)
|
assumeTheWorst(tracked, n, op)
|
||||||
gcsafeAndSideeffectCheck()
|
gcsafeAndSideeffectCheck()
|
||||||
else:
|
else:
|
||||||
mergeEffects(tracked, effectList[exceptionEffects], n)
|
mergeRaises(tracked, effectList[exceptionEffects], n)
|
||||||
mergeTags(tracked, effectList[tagEffects], n)
|
mergeTags(tracked, effectList[tagEffects], n)
|
||||||
gcsafeAndSideeffectCheck()
|
gcsafeAndSideeffectCheck()
|
||||||
if a.kind != nkSym or a.sym.magic != mNBindSym:
|
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)
|
# initVar(tracked, n[i].skipAddr, false)
|
||||||
else: discard
|
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) =
|
proc track(tracked: PEffects, n: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
|
|
@ -854,7 +912,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
if n[0].kind != nkEmpty:
|
if n[0].kind != nkEmpty:
|
||||||
n[0].info = n.info
|
n[0].info = n.info
|
||||||
#throws(tracked.exc, n[0])
|
#throws(tracked.exc, n[0])
|
||||||
addEffect(tracked, n[0], nil)
|
addRaiseEffect(tracked, n[0], nil)
|
||||||
for i in 0..<n.safeLen:
|
for i in 0..<n.safeLen:
|
||||||
track(tracked, n[i])
|
track(tracked, n[i])
|
||||||
createTypeBoundOps(tracked, n[0].typ, n.info)
|
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
|
# 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`.
|
# being handled or, if outside of an `except` block, a `ReraiseDefect`.
|
||||||
# Here we add a `Exception` tag in order to cover both the cases.
|
# 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:
|
of nkCallKinds:
|
||||||
trackCall(tracked, n)
|
trackCall(tracked, n)
|
||||||
of nkDotExpr:
|
of nkDotExpr:
|
||||||
|
|
@ -1011,26 +1069,24 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
checkForSink(tracked.config, tracked.owner, n[i])
|
checkForSink(tracked.config, tracked.owner, n[i])
|
||||||
of nkPragmaBlock:
|
of nkPragmaBlock:
|
||||||
let pragmaList = n[0]
|
let pragmaList = n[0]
|
||||||
let oldLocked = tracked.locked.len
|
var bc = createBlockContext(tracked)
|
||||||
let oldLockLevel = tracked.currLockLevel
|
|
||||||
var enforcedGcSafety = false
|
|
||||||
var enforceNoSideEffects = false
|
|
||||||
for i in 0..<pragmaList.len:
|
for i in 0..<pragmaList.len:
|
||||||
let pragma = whichPragma(pragmaList[i])
|
let pragma = whichPragma(pragmaList[i])
|
||||||
if pragma == wLocks:
|
case pragma
|
||||||
|
of wLocks:
|
||||||
lockLocations(tracked, pragmaList[i])
|
lockLocations(tracked, pragmaList[i])
|
||||||
elif pragma == wGcSafe:
|
of wGcSafe:
|
||||||
enforcedGcSafety = true
|
bc.enforcedGcSafety = true
|
||||||
elif pragma == wNoSideEffect:
|
of wNoSideEffect:
|
||||||
enforceNoSideEffects = true
|
bc.enforceNoSideEffects = true
|
||||||
|
of wCast:
|
||||||
if enforcedGcSafety: tracked.inEnforcedGcSafe = true
|
castBlock(tracked, pragmaList[i][1], bc)
|
||||||
if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
|
else:
|
||||||
|
discard
|
||||||
|
applyBlockContext(tracked, bc)
|
||||||
track(tracked, n.lastSon)
|
track(tracked, n.lastSon)
|
||||||
if enforcedGcSafety: tracked.inEnforcedGcSafe = false
|
unapplyBlockContext(tracked, bc)
|
||||||
if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false
|
|
||||||
setLen(tracked.locked, oldLocked)
|
|
||||||
tracked.currLockLevel = oldLockLevel
|
|
||||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||||
nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
|
nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
|
||||||
discard
|
discard
|
||||||
|
|
|
||||||
|
|
@ -2169,7 +2169,7 @@ proc setLine(n: PNode, info: TLineInfo) =
|
||||||
proc semPragmaBlock(c: PContext, n: PNode): PNode =
|
proc semPragmaBlock(c: PContext, n: PNode): PNode =
|
||||||
checkSonsLen(n, 2, c.config)
|
checkSonsLen(n, 2, c.config)
|
||||||
let pragmaList = n[0]
|
let pragmaList = n[0]
|
||||||
pragma(c, nil, pragmaList, exprPragmas)
|
pragma(c, nil, pragmaList, exprPragmas, isStatement = true)
|
||||||
n[1] = semExpr(c, n[1])
|
n[1] = semExpr(c, n[1])
|
||||||
result = n
|
result = n
|
||||||
result.typ = n[1].typ
|
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
|
# Constants can be inlined here, but only if they cannot result in a cast
|
||||||
# in the back-end (e.g. var p: pointer = someProc)
|
# in the back-end (e.g. var p: pointer = someProc)
|
||||||
let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and
|
let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and
|
||||||
|
n.typ != nil and
|
||||||
n.typ.kind == tyPointer
|
n.typ.kind == tyPointer
|
||||||
if not exprIsPointerCast:
|
if not exprIsPointerCast:
|
||||||
var cnst = getConstExpr(c.module, result, c.graph)
|
var cnst = getConstExpr(c.module, result, c.graph)
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,19 @@ proc isRange*(n: PNode): bool {.inline.} =
|
||||||
|
|
||||||
proc whichPragma*(n: PNode): TSpecialWord =
|
proc whichPragma*(n: PNode): TSpecialWord =
|
||||||
let key = if n.kind in nkPragmaCallKinds and n.len > 0: n[0] else: n
|
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 =
|
proc findPragma*(n: PNode, which: TSpecialWord): PNode =
|
||||||
if n.kind == nkPragma:
|
if n.kind == nkPragma:
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,7 @@
|
||||||
## for a high-level description of how borrow checking works.
|
## for a high-level description of how borrow checking works.
|
||||||
|
|
||||||
import ast, types, lineinfos, options, msgs, renderer, typeallowed
|
import ast, types, lineinfos, options, msgs, renderer, typeallowed
|
||||||
from trees import getMagic, whichPragma, stupidStmtListExpr
|
from trees import getMagic, isNoSideEffectPragma, stupidStmtListExpr
|
||||||
from wordrecg import wNoSideEffect
|
|
||||||
from isolation_check import canAlias
|
from isolation_check import canAlias
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -713,7 +712,7 @@ proc traverse(c: var Partitions; n: PNode) =
|
||||||
let pragmaList = n[0]
|
let pragmaList = n[0]
|
||||||
var enforceNoSideEffects = 0
|
var enforceNoSideEffects = 0
|
||||||
for i in 0..<pragmaList.len:
|
for i in 0..<pragmaList.len:
|
||||||
if whichPragma(pragmaList[i]) == wNoSideEffect:
|
if isNoSideEffectPragma(pragmaList[i]):
|
||||||
enforceNoSideEffects = 1
|
enforceNoSideEffects = 1
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,11 +262,25 @@ proc registerAdditionalOps*(c: PCtx) =
|
||||||
registerCallback c, "stdlib.times.getTime", proc (a: VmArgs) {.nimcall.} =
|
registerCallback c, "stdlib.times.getTime", proc (a: VmArgs) {.nimcall.} =
|
||||||
setResult(a, times.getTime().toLit)
|
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)
|
let fn = getNode(a, 0)
|
||||||
if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].len >= effectListLen and
|
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)
|
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)
|
list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info)
|
||||||
setResult(a, list)
|
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))
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ colon = ':' COMMENT?
|
||||||
colcom = ':' COMMENT?
|
colcom = ':' COMMENT?
|
||||||
operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
|
operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
|
||||||
| 'or' | 'xor' | 'and'
|
| 'or' | 'xor' | 'and'
|
||||||
| 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from' |
|
| 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from'
|
||||||
| 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
|
| 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
|
||||||
prefixOperator = operator
|
prefixOperator = operator
|
||||||
optInd = COMMENT? IND?
|
optInd = COMMENT? IND?
|
||||||
|
|
@ -31,15 +31,15 @@ dotExpr = expr '.' optInd (symbol | '[:' exprList ']')
|
||||||
explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )?
|
explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )?
|
||||||
qualifiedIdent = symbol ('.' optInd symbol)?
|
qualifiedIdent = symbol ('.' optInd symbol)?
|
||||||
setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
|
setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
|
||||||
castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
|
castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') /
|
||||||
parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
|
parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
|
||||||
| 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
|
| 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
|
||||||
| 'when' | 'var' | 'mixin'
|
| 'when' | 'var' | 'mixin'
|
||||||
par = '(' optInd
|
par = '(' optInd
|
||||||
( &parKeyw complexOrSimpleStmt ^+ ';'
|
( &parKeyw (ifExpr \ complexOrSimpleStmt) ^+ ';'
|
||||||
| ';' complexOrSimpleStmt ^+ ';'
|
| ';' (ifExpr \ complexOrSimpleStmt) ^+ ';'
|
||||||
| pragmaStmt
|
| pragmaStmt
|
||||||
| simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
|
| simpleExpr ( ('=' expr (';' (ifExpr \ complexOrSimpleStmt) ^+ ';' )? )
|
||||||
| (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) )
|
| (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) )
|
||||||
optPar ')'
|
optPar ')'
|
||||||
literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
|
literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
|
||||||
|
|
@ -59,11 +59,6 @@ primarySuffix = '(' (exprColonEqExpr comma?)* ')'
|
||||||
| '[' optInd exprColonEqExprList optPar ']'
|
| '[' optInd exprColonEqExprList optPar ']'
|
||||||
| '{' optInd exprColonEqExprList optPar '}'
|
| '{' optInd exprColonEqExprList optPar '}'
|
||||||
| &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
|
| &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
|
||||||
condExpr = expr colcom expr optInd
|
|
||||||
('elif' expr colcom expr optInd)*
|
|
||||||
'else' colcom expr
|
|
||||||
ifExpr = 'if' condExpr
|
|
||||||
whenExpr = 'when' condExpr
|
|
||||||
pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}')
|
pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}')
|
||||||
identVis = symbol OPR? # postfix position
|
identVis = symbol OPR? # postfix position
|
||||||
identVisDot = symbol '.' optInd symbol OPR?
|
identVisDot = symbol '.' optInd symbol OPR?
|
||||||
|
|
@ -130,6 +125,11 @@ condStmt = expr colcom stmt COMMENT?
|
||||||
(IND{=} 'else' colcom stmt)?
|
(IND{=} 'else' colcom stmt)?
|
||||||
ifStmt = 'if' condStmt
|
ifStmt = 'if' condStmt
|
||||||
whenStmt = 'when' condStmt
|
whenStmt = 'when' condStmt
|
||||||
|
condExpr = expr colcom expr optInd
|
||||||
|
('elif' expr colcom expr optInd)*
|
||||||
|
'else' colcom expr
|
||||||
|
ifExpr = 'if' condExpr
|
||||||
|
whenExpr = 'when' condExpr
|
||||||
whileStmt = 'while' expr colcom stmt
|
whileStmt = 'while' expr colcom stmt
|
||||||
ofBranch = 'of' exprList colcom stmt
|
ofBranch = 'of' exprList colcom stmt
|
||||||
ofBranches = ofBranch (IND{=} ofBranch)*
|
ofBranches = ofBranch (IND{=} ofBranch)*
|
||||||
|
|
@ -193,8 +193,8 @@ complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
|
||||||
| tryStmt | forStmt
|
| tryStmt | forStmt
|
||||||
| blockStmt | staticStmt | deferStmt | asmStmt
|
| blockStmt | staticStmt | deferStmt | asmStmt
|
||||||
| 'proc' routine
|
| 'proc' routine
|
||||||
| 'func' routine
|
|
||||||
| 'method' routine
|
| 'method' routine
|
||||||
|
| 'func' routine
|
||||||
| 'iterator' routine
|
| 'iterator' routine
|
||||||
| 'macro' routine
|
| 'macro' routine
|
||||||
| 'template' routine
|
| 'template' routine
|
||||||
|
|
|
||||||
|
|
@ -6031,12 +6031,12 @@ so that it can be used for debugging routines marked as ``noSideEffect``.
|
||||||
|
|
||||||
|
|
||||||
To override the compiler's side effect analysis a ``{.noSideEffect.}``
|
To override the compiler's side effect analysis a ``{.noSideEffect.}``
|
||||||
pragma block can be used:
|
``cast`` pragma block can be used:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
func f() =
|
func f() =
|
||||||
{.noSideEffect.}:
|
{.cast(noSideEffect).}:
|
||||||
echo "test"
|
echo "test"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -7501,7 +7501,7 @@ To disable the GC-safety checking the ``--threadAnalysis:off`` command line
|
||||||
switch can be used. This is a temporary workaround to ease the porting effort
|
switch can be used. This is a temporary workaround to ease the porting effort
|
||||||
from old code to the new threading model.
|
from old code to the new threading model.
|
||||||
|
|
||||||
To override the compiler's gcsafety analysis a ``{.gcsafe.}`` pragma block can
|
To override the compiler's gcsafety analysis a ``{.cast(gcsafe).}`` pragma block can
|
||||||
be used:
|
be used:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -7511,7 +7511,7 @@ be used:
|
||||||
perThread {.threadvar.}: string
|
perThread {.threadvar.}: string
|
||||||
|
|
||||||
proc setPerThread() =
|
proc setPerThread() =
|
||||||
{.gcsafe.}:
|
{.cast(gcsafe).}:
|
||||||
deepCopy(perThread, someGlobal)
|
deepCopy(perThread, someGlobal)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nim's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2018 Nim contributors
|
# (c) Copyright 2020 Nim contributors
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -9,11 +9,46 @@
|
||||||
|
|
||||||
## This module provides access to the inferred .raises effects
|
## This module provides access to the inferred .raises effects
|
||||||
## for Nim's macro system.
|
## for Nim's macro system.
|
||||||
|
## **Since**: Version 1.4.
|
||||||
|
##
|
||||||
|
## One can test for the existance of this standard module
|
||||||
|
## via ``defined(nimHasEffectTraitsModule)``.
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
proc getRaisesListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim"
|
proc getRaisesListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim"
|
||||||
|
proc getTagsListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim"
|
||||||
|
proc isGcSafeImpl(n: NimNode): bool = discard "see compiler/vmops.nim"
|
||||||
|
proc hasNoSideEffectsImpl(n: NimNode): bool = discard "see compiler/vmops.nim"
|
||||||
|
|
||||||
proc getRaisesList*(call: NimNode): NimNode =
|
proc getRaisesList*(fn: NimNode): NimNode =
|
||||||
expectKind call, nnkCallKinds
|
## Extracts the ``.raises`` list of the func/proc/etc ``fn``.
|
||||||
result = getRaisesListImpl(call[0])
|
## ``fn`` has to be a resolved symbol of kind ``nnkSym``. This
|
||||||
|
## implies that the macro that calls this proc should accept ``typed``
|
||||||
|
## arguments and not ``untyped`` arguments.
|
||||||
|
expectKind fn, nnkSym
|
||||||
|
result = getRaisesListImpl(fn)
|
||||||
|
|
||||||
|
proc getTagsList*(fn: NimNode): NimNode =
|
||||||
|
## Extracts the ``.tags`` list of the func/proc/etc ``fn``.
|
||||||
|
## ``fn`` has to be a resolved symbol of kind ``nnkSym``. This
|
||||||
|
## implies that the macro that calls this proc should accept ``typed``
|
||||||
|
## arguments and not ``untyped`` arguments.
|
||||||
|
expectKind fn, nnkSym
|
||||||
|
result = getTagsListImpl(fn)
|
||||||
|
|
||||||
|
proc isGcSafe*(fn: NimNode): bool =
|
||||||
|
## Return true if the func/proc/etc ``fn`` is `gcsafe`.
|
||||||
|
## ``fn`` has to be a resolved symbol of kind ``nnkSym``. This
|
||||||
|
## implies that the macro that calls this proc should accept ``typed``
|
||||||
|
## arguments and not ``untyped`` arguments.
|
||||||
|
expectKind fn, nnkSym
|
||||||
|
result = isGcSafeImpl(fn)
|
||||||
|
|
||||||
|
proc hasNoSideEffects*(fn: NimNode): bool =
|
||||||
|
## Return true if the func/proc/etc ``fn`` has `noSideEffect`.
|
||||||
|
## ``fn`` has to be a resolved symbol of kind ``nnkSym``. This
|
||||||
|
## implies that the macro that calls this proc should accept ``typed``
|
||||||
|
## arguments and not ``untyped`` arguments.
|
||||||
|
expectKind fn, nnkSym
|
||||||
|
result = hasNoSideEffectsImpl(fn)
|
||||||
|
|
|
||||||
18
tests/effects/tcast_as_pragma.nim
Normal file
18
tests/effects/tcast_as_pragma.nim
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim c $file"
|
||||||
|
action: "compile"
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc taggy() {.tags: RootEffect.} = discard
|
||||||
|
|
||||||
|
proc m {.raises: [], tags: [].} =
|
||||||
|
{.cast(noSideEffect).}:
|
||||||
|
echo "hi"
|
||||||
|
|
||||||
|
{.cast(raises: []).}:
|
||||||
|
raise newException(ValueError, "bah")
|
||||||
|
|
||||||
|
{.cast(tags: []).}:
|
||||||
|
taggy()
|
||||||
|
|
||||||
|
m()
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
nimout: '''##[ValueError, Gen[string]]##'''
|
nimout: '''##[ValueError, Gen[string]]##
|
||||||
|
%%[RootEffect]%%
|
||||||
|
true true'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
|
|
@ -10,13 +12,18 @@ type
|
||||||
x: T
|
x: T
|
||||||
|
|
||||||
macro m(call: typed): untyped =
|
macro m(call: typed): untyped =
|
||||||
echo "##", repr getRaisesList(call), "##"
|
echo "##", repr getRaisesList(call[0]), "##"
|
||||||
|
echo "%%", repr getTagsList(call[0]), "%%"
|
||||||
|
echo isGcSafe(call[0]), " ", hasNoSideEffects(call[0])
|
||||||
result = call
|
result = call
|
||||||
|
|
||||||
|
proc gutenTag() {.tags: RootEffect.} = discard
|
||||||
|
|
||||||
proc r(inp: int) =
|
proc r(inp: int) =
|
||||||
if inp == 0:
|
if inp == 0:
|
||||||
raise newException(ValueError, "bah")
|
raise newException(ValueError, "bah")
|
||||||
elif inp == 1:
|
elif inp == 1:
|
||||||
raise newException(Gen[string], "bahB")
|
raise newException(Gen[string], "bahB")
|
||||||
|
gutenTag()
|
||||||
|
|
||||||
m r(2)
|
m r(2)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue