summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2012-09-08 15:43:21 +0200
committerAraq <rumpf_a@web.de>2012-09-08 15:43:21 +0200
commitb64eeeb4303953f9fe1135cb9c3c61e23ec55afa (patch)
tree0f994de4a02afd9bed14dafcac24a3dd2df99d0d /compiler
parent355ae07b8f3362af4e90770477d344dcd2fef594 (diff)
downloadNim-b64eeeb4303953f9fe1135cb9c3c61e23ec55afa.tar.gz
term rewriting improvements
Diffstat (limited to 'compiler')
-rw-r--r--compiler/hlo.nim91
-rw-r--r--compiler/parampatterns.nim59
-rw-r--r--compiler/patterns.nim84
-rwxr-xr-xcompiler/sem.nim51
-rwxr-xr-xcompiler/semexprs.nim45
-rwxr-xr-xcompiler/seminst.nim1
-rwxr-xr-xcompiler/semstmts.nim8
-rwxr-xr-xcompiler/semtempl.nim22
-rwxr-xr-xcompiler/semtypes.nim13
9 files changed, 238 insertions, 136 deletions
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
new file mode 100644
index 000000000..152fd4414
--- /dev/null
+++ b/compiler/hlo.nim
@@ -0,0 +1,91 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# This include implements the high level optimization pass.
+
+proc hlo(c: PContext, n: PNode): PNode
+
+proc evalPattern(c: PContext, n, orig: PNode): PNode =
+  InternalAssert n.kind == nkCall and n.sons[0].kind == nkSym
+  # we need to ensure that the resulting AST is semchecked. However, it's
+  # aweful to semcheck before macro invocation, so we don't and treat
+  # templates and macros as immediate in this context.
+  var rule: string
+  if optHints in gOptions and hintPattern in gNotes:
+    rule = renderTree(n, {renderNoComments})
+  let s = n.sons[0].sym
+  case s.kind
+  of skMacro:
+    result = semMacroExpr(c, n, orig, s)
+  of skTemplate:
+    result = semTemplateExpr(c, n, s)
+  else:
+    result = semDirectOp(c, n, {})
+  if optHints in gOptions and hintPattern in gNotes:
+    Message(orig.info, hintPattern, rule & " --> '" & 
+      renderTree(result, {renderNoComments}) & "'")
+  # check the resulting AST for optimization rules again:
+  result = hlo(c, result)
+
+proc applyPatterns(c: PContext, n: PNode): PNode =
+  result = n
+  # we apply the last pattern first, so that pattern overriding is possible;
+  # however the resulting AST would better not trigger the old rule then
+  # anymore ;-)
+  for i in countdown(<c.patterns.len, 0):
+    let pattern = c.patterns[i]
+    if not isNil(pattern):
+      let x = applyRule(c, pattern, result)
+      if not isNil(x):
+        assert x.kind in {nkStmtList, nkCall}
+        inc(evalTemplateCounter)
+        if evalTemplateCounter > 100:
+          GlobalError(n.info, errTemplateInstantiationTooNested)
+        # deactivate this pattern:
+        c.patterns[i] = nil
+        if x.kind == nkStmtList:
+          assert x.len == 3
+          x.sons[1] = evalPattern(c, x.sons[1], result)
+          result = flattenStmts(x)
+        else:
+          result = evalPattern(c, x, result)
+        dec(evalTemplateCounter)
+        # activate this pattern again:
+        c.patterns[i] = pattern
+
+proc hlo(c: PContext, n: PNode): PNode =
+  case n.kind
+  of nkMacroDef, nkTemplateDef, procDefs:
+    # already processed (special cases in semstmts.nim)
+    result = n
+  else:
+    result = applyPatterns(c, n)
+    if result == n:
+      # no optimization applied, try subtrees:
+      for i in 0 .. < safeLen(result):
+        let a = result.sons[i]
+        let h = hlo(c, a)
+        if h != a: result.sons[i] = h
+    else:
+      # perform type checking, so that the replacement still fits:
+      if n.typ == nil and (result.typ == nil or 
+          result.typ.kind in {tyStmt, tyEmpty}):
+        nil
+      else:
+        result = fitNode(c, n.typ, result)
+
+proc hloBody(c: PContext, n: PNode): PNode =
+  # fast exit:
+  if c.patterns.len == 0 or optPatterns notin gOptions: return n
+  result = hlo(c, n)
+
+proc hloStmt(c: PContext, n: PNode): PNode =
+  # fast exit:
+  if c.patterns.len == 0 or optPatterns notin gOptions: return n
+  result = hlo(c, n)
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index 44e41f7a0..ee1f69818 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -18,7 +18,7 @@ import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg
 type
   TAliasRequest* = enum # first byte of the bytecode determines alias checking
     aqNone = 1,         # no alias analysis requested
-    aqShouldAlias,      # with what?
+    aqShouldAlias,      # with some other param
     aqNoAlias           # request noalias
   TOpcode = enum
     ppEof = 1, # end of compiled pattern
@@ -32,6 +32,8 @@ type
     ppCall,
     ppSymKind,
     ppNodeKind,
+    ppLValue,
+    ppLocal,
     ppSideEffect,
     ppNoSideEffect
   TPatternCode = string
@@ -87,6 +89,8 @@ proc compileConstraints(p: PNode, result: var TPatternCode) =
     of "call":  result.add(ppCall)
     of "alias": result[0] = chr(aqShouldAlias.ord)
     of "noalias": result[0] = chr(aqNoAlias.ord)
+    of "lvalue": result.add(ppLValue)
+    of "local": result.add(ppLocal)
     of "sideeffect": result.add(ppSideEffect)
     of "nosideeffect": result.add(ppNoSideEffect)
     else:
@@ -144,8 +148,8 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
       # indirect call without side effects:
       result = seNoSideEffect
     else:
-      # indirect call: we don't know
-      result = seUnknown
+      # indirect call: assume side effect:
+      return seSideEffect
     # we need to check n[0] too: (FwithSideEffectButReturnsProcWithout)(args)
     for i in 0 .. <n.len:
       let ret = checkForSideEffects(n.sons[i])
@@ -162,6 +166,53 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
       elif ret == seUnknown and result == seNoSideEffect:
         result = seUnknown
 
+type 
+  TAssignableResult* = enum 
+    arNone,                   # no l-value and no discriminant
+    arLValue,                 # is an l-value
+    arLocalLValue,            # is an l-value, but local var; must not escape
+                              # its stack frame!
+    arDiscriminant            # is a discriminant
+
+proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
+  ## 'owner' can be nil!
+  result = arNone
+  case n.kind
+  of nkSym:
+    # don't list 'skLet' here:
+    if n.sym.kind in {skVar, skResult, skTemp}:
+      if owner != nil and owner.id == n.sym.owner.id and 
+          sfGlobal notin n.sym.flags:
+        result = arLocalLValue
+      else:
+        result = arLValue
+  of nkDotExpr: 
+    if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}: 
+      result = arLValue
+    else: 
+      result = isAssignable(owner, n.sons[0])
+    if result != arNone and sfDiscriminant in n.sons[1].sym.flags: 
+      result = arDiscriminant
+  of nkBracketExpr: 
+    if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}: 
+      result = arLValue
+    else:
+      result = isAssignable(owner, n.sons[0])
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv: 
+    # Object and tuple conversions are still addressable, so we skip them
+    # XXX why is 'tyOpenArray' allowed here?
+    if skipTypes(n.typ, abstractPtrs).kind in {tyOpenArray, tyTuple, tyObject}: 
+      result = isAssignable(owner, n.sons[1])
+    elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
+      # types that are equal modulo distinction preserve l-value:
+      result = isAssignable(owner, n.sons[1])
+  of nkHiddenDeref, nkDerefExpr: 
+    result = arLValue
+  of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: 
+    result = isAssignable(owner, n.sons[0])
+  else:
+    nil
+
 proc matchNodeKinds*(p, n: PNode): bool =
   # matches the parameter constraint 'p' against the concrete AST 'n'. 
   # Efficiency matters here.
@@ -199,6 +250,8 @@ proc matchNodeKinds*(p, n: PNode): bool =
       let kind = TNodeKind(code[pc+1])
       push n.kind == kind
       inc pc
+    of ppLValue: push isAssignable(nil, n) in {arLValue, arLocalLValue}
+    of ppLocal: push isAssignable(nil, n) == arLocalLValue
     of ppSideEffect: push checkForSideEffects(n) == seSideEffect
     of ppNoSideEffect: push checkForSideEffects(n) != seSideEffect
     inc pc
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 402283b76..1a2f9b761 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -86,24 +86,38 @@ proc bindOrCheck(c: PPatternContext, param: PSym, n: PNode): bool =
     IdNodeTablePutLazy(c.mapping, param, n)
     result = true
 
-proc matchNested(c: PPatternContext, p, n: PNode): bool =
-  # match ``op*param``
+proc gather(c: PPatternContext, param: PSym, n: PNode) =
+  var pp = IdNodeTableGetLazy(c.mapping, param)
+  if pp != nil and pp.kind == nkArgList:
+    pp.add(n)
+  else:
+    pp = newNodeI(nkArgList, n.info, 1)
+    pp.sons[0] = n
+    IdNodeTablePutLazy(c.mapping, param, pp)
 
-  proc matchStarAux(c: PPatternContext, op, n, arglist: PNode) =
-    if n.kind in nkCallKinds and matches(c, op, n.sons[0]):
+proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
+  # match ``op * param`` or ``op *| param``
+  proc matchStarAux(c: PPatternContext, op, n, arglist: PNode,
+                    rpn: bool): bool =
+    result = true
+    if n.kind in nkCallKinds and matches(c, op.sons[1], n.sons[0]):
       for i in 1..sonsLen(n)-1:
-        matchStarAux(c, op, n[i], arglist)    
+        if not matchStarAux(c, op, n[i], arglist, rpn): return false
+      if rpn: arglist.add(n.sons[0])
     elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket:
       let n = n.sons[1]
-      for i in 0.. <n.len: matchStarAux(c, op, n[i], arglist)
-    else:
+      for i in 0.. <n.len: 
+        if not matchStarAux(c, op, n[i], arglist, rpn): return false
+    elif checkTypes(c, p.sons[2].sym, n):
       add(arglist, n)
+    else:
+      result = false
     
   if n.kind notin nkCallKinds: return false
   if matches(c, p.sons[1], n.sons[0]):
     var arglist = newNodeI(nkArgList, n.info)
-    matchStarAux(c, p.sons[1], n, arglist)
-    result = bindOrCheck(c, p.sons[2].sym, arglist)
+    if matchStarAux(c, p, n, arglist, rpn):
+      result = bindOrCheck(c, p.sons[2].sym, arglist)
 
 proc matches(c: PPatternContext, p, n: PNode): bool =
   # hidden conversions (?)
@@ -122,15 +136,21 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
     let opr = p.sons[0].ident.s
     case opr
     of "|": result = matchChoice(c, p, n)
-    of "*": result = matchNested(c, p, n)
+    of "*": result = matchNested(c, p, n, rpn=false)
+    of "*|": result = matchNested(c, p, n, rpn=true)
     of "~": result = not matches(c, p.sons[1], n)
     else: InternalError(p.info, "invalid pattern")
     # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) = 
     #   add(a, b)
   elif p.kind == nkCurlyExpr:
-    assert isPatternParam(c, p.sons[1])
-    if matches(c, p.sons[0], n):
-      result = bindOrCheck(c, p.sons[1].sym, n)
+    if p.sons[1].kind == nkPrefix:
+      if matches(c, p.sons[0], n):
+        gather(c, p.sons[1].sons[1].sym, n)
+        result = true
+    else:
+      assert isPatternParam(c, p.sons[1])
+      if matches(c, p.sons[0], n):
+        result = bindOrCheck(c, p.sons[1].sym, n)
   elif sameKinds(p, n):
     case p.kind
     of nkSym: result = p.sym == n.sym
@@ -196,7 +216,17 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
   elif matches(c, p, n):
     result = n
 
-# writeln(X, a); writeln(X, b); --> writeln(X, a, b)
+proc aliasAnalysisRequested(params: PNode): bool =
+  if params.len >= 2:
+    for i in 1 .. < params.len:
+      let param = params.sons[i].sym
+      if whichAlias(param) != aqNone: return true
+
+proc addToArgList(result, n: PNode) =
+  if n.typ != nil and n.typ.kind != tyStmt:
+    if n.kind != nkArgList: result.add(n)
+    else:
+      for i in 0 .. <n.len: result.add(n.sons[i])
 
 proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
   ## returns a tree to semcheck if the rule triggered; nil otherwise
@@ -211,36 +241,40 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
   result = newNodeI(nkCall, n.info)
   result.add(newSymNode(s, n.info))
   let params = s.typ.n
+  let requiresAA = aliasAnalysisRequested(params)
+  var args: PNode
+  if requiresAA:
+    args = newNodeI(nkArgList, n.info)
   for i in 1 .. < params.len:
     let param = params.sons[i].sym
     let x = IdNodeTableGetLazy(ctx.mapping, param)
     # couldn't bind parameter:
     if isNil(x): return nil
     result.add(x)
+    if requiresAA: addToArgList(args, n)
   # perform alias analysis here:
-  if params.len >= 2:
+  if requiresAA:
     for i in 1 .. < params.len:
+      var rs = result.sons[i]
       let param = params.sons[i].sym
       case whichAlias(param)
       of aqNone: nil
       of aqShouldAlias:
         # it suffices that it aliases for sure with *some* other param:
         var ok = false
-        for j in 1 .. < result.len:
-          if j != i and result.sons[j].typ != nil:
-            if aliases.isPartOf(result[i], result[j]) == arYes:
-              ok = true
-              break
+        for arg in items(args):
+          if arg != rs and aliases.isPartOf(rs, arg) == arYes:
+            ok = true
+            break
         # constraint not fullfilled:
         if not ok: return nil
       of aqNoAlias:
         # it MUST not alias with any other param:
         var ok = true
-        for j in 1 .. < result.len:
-          if j != i and result.sons[j].typ != nil:
-            if aliases.isPartOf(result[i], result[j]) != arNo:
-              ok = false
-              break
+        for arg in items(args):
+          if arg != rs and aliases.isPartOf(rs, arg) != arNo:
+            ok = false
+            break
         # constraint not fullfilled:
         if not ok: return nil
 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 24bee4a22..0f2627998 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -103,55 +103,7 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
     return n
   result = evalTypedExpr(c, e)
 
-proc evalPattern(c: PContext, n: PNode, info: TLineInfo): PNode =
-  InternalAssert n.kind == nkCall and n.sons[0].kind == nkSym
-  # we need to ensure that the resulting AST is semchecked. However, it's
-  # aweful to semcheck before macro invocation, so we don't and treat
-  # templates and macros as immediate in this context.
-  var rule: string
-  if optHints in gOptions and hintPattern in gNotes:
-    rule = renderTree(n, {renderNoComments})
-  let s = n.sons[0].sym
-  case s.kind
-  of skMacro:
-    result = semMacroExpr(c, n, n, s)
-  of skTemplate:
-    result = semTemplateExpr(c, n, s)
-  else:
-    result = semDirectOp(c, n, {})
-  if optHints in gOptions and hintPattern in gNotes:
-    Message(info, hintPattern, rule & " --> '" & 
-      renderTree(result, {renderNoComments}) & "'")
-
-proc applyPatterns(c: PContext, n: PNode): PNode =
-  # fast exit:
-  if c.patterns.len == 0 or optPatterns notin gOptions: return n
-  result = n
-  # we apply the last pattern first, so that pattern overriding is possible;
-  # however the resulting AST would better not trigger the old rule then
-  # anymore ;-)
-  for i in countdown(<c.patterns.len, 0):
-    let pattern = c.patterns[i]
-    if not isNil(pattern):
-      let x = applyRule(c, pattern, result)
-      if not isNil(x):
-        assert x.kind in {nkStmtList, nkCall}
-        inc(evalTemplateCounter)
-        if evalTemplateCounter > 100:
-          GlobalError(n.info, errTemplateInstantiationTooNested)
-        # deactivate this pattern:
-        c.patterns[i] = nil
-        if x.kind == nkStmtList:
-          assert x.len == 3
-          x.sons[1] = evalPattern(c, x.sons[1], n.info)
-          result = flattenStmts(x)
-        else:
-          result = evalPattern(c, x, n.info)
-        dec(evalTemplateCounter)
-        # activate this pattern again:
-        c.patterns[i] = pattern
-
-include seminst, semcall
+include hlo, seminst, semcall
 
 proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = 
   inc(evalTemplateCounter)
@@ -251,6 +203,7 @@ proc SemStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
       # a generic has been added to `a`:
       if result.kind != nkEmpty: addSon(a, result)
       result = a
+  result = hloStmt(c, result)
   result = transformStmt(c.module, result)
 
 proc RecoverContext(c: PContext) = 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index fe698bffb..fc3ea1820 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -438,51 +438,9 @@ proc skipObjConv(n: PNode): PNode =
       result = n
   of nkObjUpConv, nkObjDownConv: result = n.sons[0]
   else: result = n
-  
-type 
-  TAssignableResult = enum 
-    arNone,                   # no l-value and no discriminant
-    arLValue,                 # is an l-value
-    arLocalLValue,            # is an l-value, but local var; must not escape
-                              # its stack frame!
-    arDiscriminant            # is a discriminant
 
 proc isAssignable(c: PContext, n: PNode): TAssignableResult = 
-  result = arNone
-  case n.kind
-  of nkSym:
-    # don't list 'skLet' here:
-    if n.sym.kind in {skVar, skResult, skTemp}:
-      if c.p.owner.id == n.sym.owner.id and sfGlobal notin n.sym.flags:
-        result = arLocalLValue
-      else:
-        result = arLValue
-  of nkDotExpr: 
-    if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}: 
-      result = arLValue
-    else: 
-      result = isAssignable(c, n.sons[0])
-    if result != arNone and sfDiscriminant in n.sons[1].sym.flags: 
-      result = arDiscriminant
-  of nkBracketExpr: 
-    if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}: 
-      result = arLValue
-    else:
-      result = isAssignable(c, n.sons[0])
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv: 
-    # Object and tuple conversions are still addressable, so we skip them
-    # XXX why is 'tyOpenArray' allowed here?
-    if skipTypes(n.typ, abstractPtrs).kind in {tyOpenArray, tyTuple, tyObject}: 
-      result = isAssignable(c, n.sons[1])
-    elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
-      # types that are equal modulo distinction preserve l-value:
-      result = isAssignable(c, n.sons[1])
-  of nkHiddenDeref, nkDerefExpr: 
-    result = arLValue
-  of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: 
-    result = isAssignable(c, n.sons[0])
-  else: 
-    nil
+  result = parampatterns.isAssignable(c.p.owner, n)
 
 proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = 
   if n.kind == nkHiddenDeref: 
@@ -1598,4 +1556,3 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     LocalError(n.info, errInvalidExpressionX,
                renderTree(n, {renderNoComments}))
   incl(result.flags, nfSem)
-  result = applyPatterns(c, result)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 2d1f68d5d..a03f62075 100755
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -78,6 +78,7 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
       addResult(c, result.typ.sons[0], n.info, result.kind)
       addResultNode(c, n)
     var b = semStmtScope(c, n.sons[bodyPos])
+    b = hloBody(c, b)
     n.sons[bodyPos] = transformBody(c.module, b, result)
     #echo "code instantiated ", result.name.s
     excl(result.flags, sfForward)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 04766ae58..4b79291e6 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -714,7 +714,7 @@ proc semLambda(c: PContext, n: PNode): PNode =
       LocalError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
     pushProcCon(c, s)
     addResult(c, s.typ.sons[0], n.info, skProc)
-    let semBody = semStmtScope(c, n.sons[bodyPos])
+    let semBody = hloBody(c, semStmtScope(c, n.sons[bodyPos]))
     n.sons[bodyPos] = transformBody(c.module, semBody, s)
     addResultNode(c, n)
     popProcCon(c)
@@ -772,7 +772,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     rawAddSon(s.typ, nil)
   if n.sons[patternPos].kind != nkEmpty:
     n.sons[patternPos] = semPattern(c, n.sons[patternPos])
-    c.patterns.add(s)
   
   var proto = SearchForProc(c, s, c.tab.tos-2) # -2 because we have a scope
                                                # open for parameters
@@ -821,7 +820,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         addResult(c, s.typ.sons[0], n.info, kind)
       if sfImportc notin s.flags:
         # no semantic checking for importc:
-        let semBody = semStmtScope(c, n.sons[bodyPos])
+        let semBody = hloBody(c, semStmtScope(c, n.sons[bodyPos]))
         # unfortunately we cannot skip this step when in 'system.compiles'
         # context as it may even be evaluated in 'system.compiles':
         n.sons[bodyPos] = transformBody(c.module, semBody, s)
@@ -844,6 +843,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   sideEffectsCheck(c, s)
   closeScope(c.tab)           # close scope for parameters
   popOwner()
+  if n.sons[patternPos].kind != nkEmpty:
+    c.patterns.add(s)
   
 proc semIterator(c: PContext, n: PNode): PNode =
   result = semProcAux(c, n, skIterator, iteratorPragmas)
@@ -1222,7 +1223,6 @@ proc SemStmt(c: PContext, n: PNode): PNode =
     result = emptyNode
   else:
     incl(result.flags, nfSem)
-  result = applyPatterns(c, result)
 
 proc semStmtScope(c: PContext, n: PNode): PNode =
   openScope(c.tab)
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 0887e1789..fa15f8ede 100755
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -392,8 +392,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0]))
   if n.sons[patternPos].kind != nkEmpty:
     n.sons[patternPos] = semPattern(c, n.sons[patternPos])
-    c.patterns.add(s)
-
   var ctx: TemplCtx
   ctx.toBind = initIntSet()
   ctx.c = c
@@ -417,6 +415,8 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     addInterfaceOverloadableSymAt(c, s, curScope)
   else:
     SymTabReplace(c.tab.stack[curScope], proto, s)
+  if n.sons[patternPos].kind != nkEmpty:
+    c.patterns.add(s)
 
 proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
   template templToExpand(s: expr): expr =
@@ -455,12 +455,22 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
     result = semBindStmt(c.c, n, c.toBind)
   of nkEmpty, nkSym..nkNilLit: nil
   of nkCurlyExpr:
-    # we support '(pattern){x}' to bind a subpattern to a parameter 'x':
-    if n.len != 2 or n.sons[1].kind != nkIdent:
+    # we support '(pattern){x}' to bind a subpattern to a parameter 'x'; 
+    # '(pattern){|x}' does the same but the matches will be gathered in 'x'
+    if n.len != 2:
       localError(n.info, errInvalidExpression)
-    else:
+    elif n.sons[1].kind == nkIdent:
       n.sons[0] = semPatternBody(c, n.sons[0])
       n.sons[1] = expectParam(c, n.sons[1])
+    elif n.sons[1].kind == nkPrefix and n.sons[1].sons[0].kind == nkIdent:
+      let opr = n.sons[1].sons[0]
+      if opr.ident.s == "|":
+        n.sons[0] = semPatternBody(c, n.sons[0])
+        n.sons[1].sons[1] = expectParam(c, n.sons[1].sons[1])
+      else:
+        localError(n.info, errInvalidExpression)
+    else:
+      localError(n.info, errInvalidExpression)
   of nkCallKinds:
     let s = QualifiedLookUp(c.c, n.sons[0], {})
     if s != nil:
@@ -473,7 +483,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
       # we interpret `*` and `|` only as pattern operators if they occur in
       # infix notation, so that '`*`(a, b)' can be used for verbatim matching:
       let opr = n.sons[0]
-      if opr.ident.s == "*":
+      if opr.ident.s == "*" or opr.ident.s == "*|":
         result = newNodeI(nkPattern, n.info, n.len)
         result.sons[0] = opr
         result.sons[1] = semPatternBody(c, n.sons[1])
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 7c1318ada..ff745dc36 100755
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -554,12 +554,15 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
   if kind == skMacro:
     # within a macro, every param has the type PNimrodNode!
     # and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}:
+    # We used to copySym(param) here, but this is not possible for term
+    # rewriting macros; so we simply overwrite param.typ here and hope for
+    # the best ...
     let nn = getSysSym"PNimrodNode"
-    var a = copySym(param)
-    a.typ = nn.typ
-    if sfGenSym notin a.flags: addDecl(c, a)
-  else:
-    if sfGenSym notin param.flags: addDecl(c, param)
+    #var a = copySym(param)
+    #a.typ = nn.typ
+    param.typ = nn.typ
+    #if sfGenSym notin a.flags: addDecl(c, a)
+  if sfGenSym notin param.flags: addDecl(c, param)
 
 proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
   tuple[typ: PType, id: PIdent] =