diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2020-10-06 16:47:15 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-06 16:47:15 +0200 |
commit | 92163fa3304e5b6768a50d36a5243639ce4a2f69 (patch) | |
tree | b7aa2705a4772e8f82d61e765ff9158f7b21b441 | |
parent | acd71dd6bb745eb08f81ab489d635951f8edfcfa (diff) | |
download | Nim-92163fa3304e5b6768a50d36a5243639ce4a2f69.tar.gz |
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
-rw-r--r-- | changelog.md | 5 | ||||
-rw-r--r-- | compiler/condsyms.nim | 2 | ||||
-rw-r--r-- | compiler/parser.nim | 25 | ||||
-rw-r--r-- | compiler/pragmas.nim | 9 | ||||
-rw-r--r-- | compiler/renderer.nim | 7 | ||||
-rw-r--r-- | compiler/sempass2.nim | 108 | ||||
-rw-r--r-- | compiler/semstmts.nim | 2 | ||||
-rw-r--r-- | compiler/transf.nim | 1 | ||||
-rw-r--r-- | compiler/trees.nim | 14 | ||||
-rw-r--r-- | compiler/varpartitions.nim | 5 | ||||
-rw-r--r-- | compiler/vmops.nim | 20 | ||||
-rw-r--r-- | doc/grammar.txt | 22 | ||||
-rw-r--r-- | doc/manual.rst | 8 | ||||
-rw-r--r-- | lib/std/effecttraits.nim | 43 | ||||
-rw-r--r-- | tests/effects/tcast_as_pragma.nim | 18 | ||||
-rw-r--r-- | tests/macros/tgetraiseslist.nim | 11 |
16 files changed, 232 insertions, 68 deletions
diff --git a/changelog.md b/changelog.md index e905dd941..3144c7cf5 100644 --- a/changelog.md +++ b/changelog.md @@ -304,10 +304,13 @@ proc mydiv(a, b): int {.raises: [].} = - `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via `--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 can be raised. - 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 diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 7dc1d183e..9771a48d3 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -121,3 +121,5 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasLentIterators") defineSymbol("nimHasDeclaredMagic") defineSymbol("nimHasStacktracesModule") + defineSymbol("nimHasEffectTraitsModule") + defineSymbol("nimHasCastPragmaBlocks") diff --git a/compiler/parser.nim b/compiler/parser.nim index 1ec708057..e3cf54b38 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -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) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 67e574d52..fe8cd3cfe 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -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 diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 88b9adf27..08012bb55 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -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, ")") diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index cda156224..19223b88f 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -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 diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index abf2cc0b6..44a31c2ea 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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 diff --git a/compiler/transf.nim b/compiler/transf.nim index c8dbf5e04..5922ca595 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -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) diff --git a/compiler/trees.nim b/compiler/trees.nim index c5e2cb7b4..63459babe 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -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: diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index e59c5cb75..a172a906e 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -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 diff --git a/compiler/vmops.nim b/compiler/vmops.nim index de6229ec7..a3d4d74db 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -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)) diff --git a/doc/grammar.txt b/doc/grammar.txt index 9d952d372..f5e619ebc 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -6,7 +6,7 @@ colon = ':' COMMENT? colcom = ':' COMMENT? operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 | 'or' | 'xor' | 'and' - | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from' | + | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from' | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..' prefixOperator = operator optInd = COMMENT? IND? @@ -31,15 +31,15 @@ dotExpr = expr '.' optInd (symbol | '[:' exprList ']') explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )? qualifiedIdent = symbol ('.' optInd symbol)? 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' | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' | 'when' | 'var' | 'mixin' par = '(' optInd - ( &parKeyw complexOrSimpleStmt ^+ ';' - | ';' complexOrSimpleStmt ^+ ';' + ( &parKeyw (ifExpr \ complexOrSimpleStmt) ^+ ';' + | ';' (ifExpr \ complexOrSimpleStmt) ^+ ';' | pragmaStmt - | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? ) + | simpleExpr ( ('=' expr (';' (ifExpr \ complexOrSimpleStmt) ^+ ';' )? ) | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) optPar ')' literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT @@ -59,11 +59,6 @@ primarySuffix = '(' (exprColonEqExpr comma?)* ')' | '[' optInd exprColonEqExprList optPar ']' | '{' optInd exprColonEqExprList optPar '}' | &( '`'|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 ('.}' | '}') identVis = symbol OPR? # postfix position identVisDot = symbol '.' optInd symbol OPR? @@ -130,6 +125,11 @@ condStmt = expr colcom stmt COMMENT? (IND{=} 'else' colcom stmt)? ifStmt = 'if' 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 ofBranch = 'of' exprList colcom stmt ofBranches = ofBranch (IND{=} ofBranch)* @@ -193,8 +193,8 @@ complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt | tryStmt | forStmt | blockStmt | staticStmt | deferStmt | asmStmt | 'proc' routine - | 'func' routine | 'method' routine + | 'func' routine | 'iterator' routine | 'macro' routine | 'template' routine diff --git a/doc/manual.rst b/doc/manual.rst index 11ea6b99c..494a009cb 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -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.}`` -pragma block can be used: +``cast`` pragma block can be used: .. code-block:: nim func f() = - {.noSideEffect.}: + {.cast(noSideEffect).}: 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 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: .. code-block:: nim @@ -7511,7 +7511,7 @@ be used: perThread {.threadvar.}: string proc setPerThread() = - {.gcsafe.}: + {.cast(gcsafe).}: deepCopy(perThread, someGlobal) diff --git a/lib/std/effecttraits.nim b/lib/std/effecttraits.nim index a5d799786..0f0a24492 100644 --- a/lib/std/effecttraits.nim +++ b/lib/std/effecttraits.nim @@ -1,7 +1,7 @@ # # # Nim's Runtime Library -# (c) Copyright 2018 Nim contributors +# (c) Copyright 2020 Nim contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -9,11 +9,46 @@ ## This module provides access to the inferred .raises effects ## for Nim's macro system. +## **Since**: Version 1.4. +## +## One can test for the existance of this standard module +## via ``defined(nimHasEffectTraitsModule)``. import macros 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 = - expectKind call, nnkCallKinds - result = getRaisesListImpl(call[0]) +proc getRaisesList*(fn: NimNode): NimNode = + ## Extracts the ``.raises`` 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 = 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) diff --git a/tests/effects/tcast_as_pragma.nim b/tests/effects/tcast_as_pragma.nim new file mode 100644 index 000000000..1de61333e --- /dev/null +++ b/tests/effects/tcast_as_pragma.nim @@ -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() diff --git a/tests/macros/tgetraiseslist.nim b/tests/macros/tgetraiseslist.nim index de7ba06c8..79694a66f 100644 --- a/tests/macros/tgetraiseslist.nim +++ b/tests/macros/tgetraiseslist.nim @@ -1,5 +1,7 @@ discard """ - nimout: '''##[ValueError, Gen[string]]##''' + nimout: '''##[ValueError, Gen[string]]## +%%[RootEffect]%% +true true''' """ import macros @@ -10,13 +12,18 @@ type x: T 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 +proc gutenTag() {.tags: RootEffect.} = discard + proc r(inp: int) = if inp == 0: raise newException(ValueError, "bah") elif inp == 1: raise newException(Gen[string], "bahB") + gutenTag() m r(2) |