summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2020-10-06 16:47:15 +0200
committerGitHub <noreply@github.com>2020-10-06 16:47:15 +0200
commit92163fa3304e5b6768a50d36a5243639ce4a2f69 (patch)
treeb7aa2705a4772e8f82d61e765ff9158f7b21b441
parentacd71dd6bb745eb08f81ab489d635951f8edfcfa (diff)
downloadNim-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.md5
-rw-r--r--compiler/condsyms.nim2
-rw-r--r--compiler/parser.nim25
-rw-r--r--compiler/pragmas.nim9
-rw-r--r--compiler/renderer.nim7
-rw-r--r--compiler/sempass2.nim108
-rw-r--r--compiler/semstmts.nim2
-rw-r--r--compiler/transf.nim1
-rw-r--r--compiler/trees.nim14
-rw-r--r--compiler/varpartitions.nim5
-rw-r--r--compiler/vmops.nim20
-rw-r--r--doc/grammar.txt22
-rw-r--r--doc/manual.rst8
-rw-r--r--lib/std/effecttraits.nim43
-rw-r--r--tests/effects/tcast_as_pragma.nim18
-rw-r--r--tests/macros/tgetraiseslist.nim11
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)