summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim12
-rw-r--r--compiler/astalgo.nim2
-rw-r--r--compiler/ccgexprs.nim8
-rw-r--r--compiler/cgen.nim4
-rw-r--r--compiler/closureiters.nim4
-rw-r--r--compiler/commands.nim6
-rw-r--r--compiler/dfa.nim2
-rw-r--r--compiler/docgen.nim59
-rw-r--r--compiler/hlo.nim4
-rw-r--r--compiler/injectdestructors.nim (renamed from compiler/destroyer.nim)20
-rw-r--r--compiler/jsgen.nim2
-rw-r--r--compiler/liftdestructors.nim211
-rw-r--r--compiler/lowerings.nim2
-rw-r--r--compiler/options.nim4
-rw-r--r--compiler/pragmas.nim6
-rw-r--r--compiler/renderer.nim8
-rw-r--r--compiler/sem.nim2
-rw-r--r--compiler/semdata.nim2
-rw-r--r--compiler/semexprs.nim6
-rw-r--r--compiler/semgnrc.nim2
-rw-r--r--compiler/seminst.nim2
-rw-r--r--compiler/semmagic.nim5
-rw-r--r--compiler/semparallel.nim2
-rw-r--r--compiler/sempass2.nim122
-rw-r--r--compiler/semstmts.nim34
-rw-r--r--compiler/semtempl.nim2
-rw-r--r--compiler/semtypes.nim78
-rw-r--r--compiler/semtypinst.nim4
-rw-r--r--compiler/transf.nim2
-rw-r--r--compiler/trees.nim10
-rw-r--r--compiler/vmgen.nim2
-rw-r--r--compiler/wordrecg.nim4
-rw-r--r--compiler/writetracking.nim2
-rw-r--r--doc/advopt.txt2
-rw-r--r--lib/core/macros.nim2
-rw-r--r--lib/core/runtime_v2.nim32
-rw-r--r--lib/system.nim2
-rw-r--r--tests/destructor/tinvalid_rebind.nim15
-rw-r--r--tests/destructor/topttree.nim1
39 files changed, 445 insertions, 244 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 5cc608c2e..a21d9f738 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -216,7 +216,7 @@ type
     nkEnumFieldDef,       # `ident = expr` in an enumeration
     nkArgList,            # argument list
     nkPattern,            # a special pattern; used for matching
-    nkReturnToken,        # token used for interpretation
+    nkHiddenTryStmt,      # token used for interpretation
     nkClosure,            # (prc, env)-pair (internally used for code gen)
     nkGotoState,          # used for the state machine (for iterators)
     nkState,              # give a label to a code section (for iterators)
@@ -227,7 +227,7 @@ type
   TNodeKinds* = set[TNodeKind]
 
 type
-  TSymFlag* = enum    # already 34 flags!
+  TSymFlag* = enum    # already 36 flags!
     sfUsed,           # read access of sym (for warnings) or simply used
     sfExported,       # symbol is exported from module
     sfFromGeneric,    # symbol is instantiation of a generic; this is needed
@@ -278,6 +278,8 @@ type
     sfGenSym          # symbol is 'gensym'ed; do not add to symbol table
     sfNonReloadable   # symbol will be left as-is when hot code reloading is on -
                       # meaning that it won't be renamed and/or changed in any way
+    sfGeneratedOp     # proc is a generated '='; do not inject destructors in it
+
 
   TSymFlags* = set[TSymFlag]
 
@@ -478,7 +480,7 @@ type
     nfExecuteOnReload  # A top-level statement that will be executed during reloads
 
   TNodeFlags* = set[TNodeFlag]
-  TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: beyond that)
+  TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: ~38)
     tfVarargs,        # procedure has C styled varargs
                       # tyArray type represeting a varargs list
     tfNoSideEffect,   # procedure type does not allow side effects
@@ -535,6 +537,8 @@ type
     tfCovariant       # covariant generic param mimicing a ptr type
     tfWeakCovariant   # covariant generic param mimicing a seq/array type
     tfContravariant   # contravariant generic param
+    tfCheckedForDestructor # type was checked for having a destructor.
+                           # If it has one, t.destructor is not nil.
 
   TTypeFlags* = set[TTypeFlag]
 
@@ -640,7 +644,7 @@ type
     mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
     mNewString, mNewStringOfCap, mParseBiggestFloat,
     mMove, mWasMoved, mDestroy,
-    mDefault, mReset,
+    mDefault, mAccessEnv, mReset,
     mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
     mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
     mOrdinal,
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 839c4645d..4320ffea3 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -1002,7 +1002,7 @@ proc iiTablePut(t: var TIITable, key, val: int) =
     iiTableRawInsert(t.data, key, val)
     inc(t.counter)
 
-proc isAddrNode*(n: PNode): bool = 
+proc isAddrNode*(n: PNode): bool =
   case n.kind
     of nkAddr, nkHiddenAddr: true
     of nkCallKinds:
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 28aa875bc..4026a429e 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1967,11 +1967,14 @@ proc genDestroy(p: BProc; n: PNode) =
         [rdLoc(a), getTypeDesc(p.module, t.lastSon)])
     else: discard "nothing to do"
   else:
+    let t = n[1].typ.skipTypes(abstractVar)
+    if t.destructor != nil and t.destructor.magic != mDestroy:
+      internalError(p.config, n.info, "destructor turned out to be not trivial")
     discard "ignore calls to the default destructor"
 
 proc genDispose(p: BProc; n: PNode) =
   when false:
-    let elemType = n[1].typ.skipTypes(abstractVarInst).lastSon
+    let elemType = n[1].typ.skipTypes(abstractVar).lastSon
 
     var a: TLoc
     initLocExpr(p, n[1].skipAddr, a)
@@ -2146,6 +2149,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mWasMoved: genWasMoved(p, e)
   of mMove: genMove(p, e, d)
   of mDestroy: genDestroy(p, e)
+  of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0")
   of mSlice:
     localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
       " 'toOpenArray' is only valid within a call expression")
@@ -2541,7 +2545,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
       initLocExprSingleUse(p, ex, a)
       line(p, cpsStmts, "(void)(" & a.r & ");\L")
   of nkAsmStmt: genAsmStmt(p, n)
-  of nkTryStmt:
+  of nkTryStmt, nkHiddenTryStmt:
     if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions:
       genTryCpp(p, n, d)
     else:
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index a7c1895f6..1436880d9 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -539,7 +539,7 @@ proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
 
 include ccgcalls, "ccgstmts.nim"
 
-proc initFrame(p: BProc, procname, filename: Rope): Rope =  
+proc initFrame(p: BProc, procname, filename: Rope): Rope =
   const frameDefines = """
   $1  define nimfr_(proc, file) \
       TFrame FR_; \
@@ -868,7 +868,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
   of nkSym:
     # some path reads from 'result' before it was written to!
     if n.sym.kind == skResult: result = InitRequired
-  of nkTryStmt:
+  of nkTryStmt, nkHiddenTryStmt:
     # We need to watch out for the following problem:
     # try:
     #   result = stuffThatRaises()
diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim
index 5ded6d054..6f6bad942 100644
--- a/compiler/closureiters.nim
+++ b/compiler/closureiters.nim
@@ -541,7 +541,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
 
       if isExpr: result.add(ctx.newEnvVarAccess(tmp))
 
-  of nkTryStmt:
+  of nkTryStmt, nkHiddenTryStmt:
     var ns = false
     for i in 0 ..< n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], ns)
@@ -934,7 +934,7 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
       result[1] = ctx.transformBreaksInBlock(result[1], result[0], gotoOut)
       result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut)
 
-    of nkTryStmt:
+    of nkTryStmt, nkHiddenTryStmt:
       # See explanation above about how this works
       ctx.hasExceptions = true
 
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 2bccc4d81..3a18e69ed 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -286,7 +286,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
   of "taintmode": result = contains(conf.globalOptions, optTaintMode)
   of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
   of "implicitstatic": result = contains(conf.options, optImplicitStatic)
-  of "patterns": result = contains(conf.options, optPatterns)
+  of "patterns", "trmacros": result = contains(conf.options, optTrMacros)
   of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
   of "nilseqs": result = contains(conf.options, optNilSeqs)
   of "oldast": result = contains(conf.options, optOldAst)
@@ -532,8 +532,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info)
   of "implicitstatic":
     processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
-  of "patterns":
-    processOnOffSwitch(conf, {optPatterns}, arg, pass, info)
+  of "patterns", "trmacros":
+    processOnOffSwitch(conf, {optTrMacros}, arg, pass, info)
   of "opt":
     expectArg(conf, switch, arg, pass, info)
     case arg.normalize
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index ec69f381d..1a7c39417 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -665,7 +665,7 @@ proc gen(c: var Con; n: PNode) =
   of nkReturnStmt: genReturn(c, n)
   of nkRaiseStmt: genRaise(c, n)
   of nkBreakStmt: genBreak(c, n)
-  of nkTryStmt: genTry(c, n)
+  of nkTryStmt, nkHiddenTryStmt: genTry(c, n)
   of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
      nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr:
     for x in n: gen(c, x)
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 2c7e52c67..93efef526 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -15,7 +15,7 @@ import
   ast, strutils, strtabs, options, msgs, os, ropes, idents,
   wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
   packages/docutils/rst, packages/docutils/rstgen,
-  packages/docutils/highlite, sempass2, json, xmltree, cgi,
+  packages/docutils/highlite, json, xmltree, cgi, trees, types,
   typesrenderer, astalgo, modulepaths, lineinfos, sequtils, intsets,
   pathutils
 
@@ -760,6 +760,63 @@ proc exportSym(d: PDoc; s: PSym) =
             "$1", [rope esc(d.target, s.name.s),
             rope changeFileExt(external, "html")])
 
+proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
+  let s = n.sons[namePos].sym
+  if tfReturnsNew in s.typ.flags:
+    result = newIdentNode(getIdent(cache, "new"), n.info)
+
+proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
+  let spec = effectSpec(x, effectType)
+  if isNil(spec):
+    let s = n.sons[namePos].sym
+
+    let actual = s.typ.n.sons[0]
+    if actual.len != effectListLen: return
+    let real = actual.sons[idx]
+
+    # warning: hack ahead:
+    var effects = newNodeI(nkBracket, n.info, real.len)
+    for i in 0 ..< real.len:
+      var t = typeToString(real[i].typ)
+      if t.startsWith("ref "): t = substr(t, 4)
+      effects.sons[i] = newIdentNode(getIdent(cache, t), n.info)
+      # set the type so that the following analysis doesn't screw up:
+      effects.sons[i].typ = real[i].typ
+
+    result = newNode(nkExprColonExpr, n.info, @[
+      newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects])
+
+proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
+  let s = n.sons[namePos].sym
+  let params = s.typ.n
+
+  var effects = newNodeI(nkBracket, n.info)
+  for i in 1 ..< params.len:
+    if params[i].kind == nkSym and flag in params[i].sym.flags:
+      effects.add params[i]
+
+  if effects.len > 0:
+    result = newNode(nkExprColonExpr, n.info, @[
+      newIdentNode(getIdent(cache, pragmaName), n.info), effects])
+
+proc documentRaises*(cache: IdentCache; n: PNode) =
+  if n.sons[namePos].kind != nkSym: return
+  let pragmas = n.sons[pragmasPos]
+  let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects)
+  let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects)
+  let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes")
+  let p4 = documentNewEffect(cache, n)
+  let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes")
+
+  if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil:
+    if pragmas.kind == nkEmpty:
+      n.sons[pragmasPos] = newNodeI(nkPragma, n.info)
+    if p1 != nil: n.sons[pragmasPos].add p1
+    if p2 != nil: n.sons[pragmasPos].add p2
+    if p3 != nil: n.sons[pragmasPos].add p3
+    if p4 != nil: n.sons[pragmasPos].add p4
+    if p5 != nil: n.sons[pragmasPos].add p5
+
 proc generateDoc*(d: PDoc, n, orig: PNode) =
   case n.kind
   of nkCommentStmt: add(d.modDesc, genComment(d, n))
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
index bbbcb4e56..8ebc1ec35 100644
--- a/compiler/hlo.nim
+++ b/compiler/hlo.nim
@@ -92,12 +92,12 @@ proc hlo(c: PContext, n: PNode): PNode =
 
 proc hloBody(c: PContext, n: PNode): PNode =
   # fast exit:
-  if c.patterns.len == 0 or optPatterns notin c.config.options: return n
+  if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
   c.hloLoopDetector = 0
   result = hlo(c, n)
 
 proc hloStmt(c: PContext, n: PNode): PNode =
   # fast exit:
-  if c.patterns.len == 0 or optPatterns notin c.config.options: return n
+  if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
   c.hloLoopDetector = 0
   result = hlo(c, n)
diff --git a/compiler/destroyer.nim b/compiler/injectdestructors.nim
index 10fc37f32..e9323834e 100644
--- a/compiler/destroyer.nim
+++ b/compiler/injectdestructors.nim
@@ -91,7 +91,7 @@
 ## destroy(tmp.x); destroy(tmp.y)
 ##
 
-##[
+#[
 From https://github.com/nim-lang/Nim/wiki/Destructors
 
 Rule      Pattern                 Transformed into
@@ -131,7 +131,7 @@ copyMem. This is harder than it looks:
 
 And the C++ optimizers don't sweat to optimize it for us, so we don't have
 to do it.
-]##
+]#
 
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
@@ -315,6 +315,10 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
     if c.otherRead != nil:
       m.add "; another read is done here: "
       m.add c.graph.config $ c.otherRead.info
+    elif ri.kind == nkSym and ri.sym.kind == skParam and ri.sym.typ.kind != tySink:
+      m.add "; try to make "
+      m.add renderTree(ri)
+      m.add " a 'sink' parameter"
   localError(c.graph.config, ri.info, errGenerated, m)
 
 proc makePtrType(c: Con, baseType: PType): PType =
@@ -329,7 +333,7 @@ template genOp(opr, opname, ri) =
   elif op.ast[genericParamsPos].kind != nkEmpty:
     globalError(c.graph.config, dest.info, "internal error: '" & opname &
       "' operator is generic")
-  patchHead op
+  #patchHead op
   if sfError in op.flags: checkForErrorPragma(c, t, ri, opname)
   let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
   addrExp.add(dest)
@@ -343,7 +347,14 @@ proc genSink(c: Con; t: PType; dest, ri: PNode): PNode =
       echo t.sink.id, " owner ", t.id
       quit 1
   let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
-  genOp(if t.sink != nil: t.sink else: t.assignment, "=sink", ri)
+  let op = if t.sink != nil: t.sink else: t.assignment
+  if op != nil:
+    genOp(op, "=sink", ri)
+  else:
+    # in rare cases only =destroy exists but no sink or assignment
+    # (see Pony object in tmove_objconstr.nim)
+    # we generate a fast assignment in this case:
+    result = newTree(nkFastAsgn, dest, ri)
 
 proc genCopy(c: Con; t: PType; dest, ri: PNode): PNode =
   if tfHasOwned in t.flags:
@@ -710,6 +721,7 @@ proc p(n: PNode; c: var Con): PNode =
 proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
   when false: # defined(nimDebugDestroys):
     echo "injecting into ", n
+  if sfGeneratedOp in owner.flags: return n
   var c: Con
   c.owner = owner
   c.destroys = newNodeI(nkStmtList, n.info)
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 627cc6cdf..b1a7d12f3 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -2387,7 +2387,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       genLineDir(p, n)
       gen(p, n.sons[0], r)
   of nkAsmStmt: genAsmOrEmitStmt(p, n)
-  of nkTryStmt: genTry(p, n, r)
+  of nkTryStmt, nkHiddenTryStmt: genTry(p, n, r)
   of nkRaiseStmt: genRaiseStmt(p, n)
   of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
      nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 4179c8e5d..4ae438e48 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -10,7 +10,7 @@
 ## This module implements lifting for type-bound operations
 ## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``).
 
-# included from sem.nim
+# included from sempass2.nim
 
 type
   TLiftCtx = object
@@ -20,9 +20,10 @@ type
     fn: PSym
     asgnForType: PType
     recurse: bool
+    c: PContext
 
 proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
-proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
+proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
               info: TLineInfo): PSym {.discardable.}
 
 proc at(a, i: PNode, elemType: PType): PNode =
@@ -118,8 +119,18 @@ proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
     (tfHasGCedMem in t.flags or t.isGCedMem)
 
 proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
-                        field: PSym): bool =
-  if tfHasAsgn in t.flags or useNoGc(c, t):
+                        field: var PSym): bool =
+  if optNimV2 in c.graph.config.globalOptions:
+    let op = field
+    if field != nil and sfOverriden in field.flags:
+      if sfError in op.flags:
+        incl c.fn.flags, sfError
+      else:
+        markUsed(c.graph.config, c.info, op, c.graph.usageSym)
+      onUse(c.info, op)
+      body.add newAsgnCall(c.graph, op, x, y)
+      result = true
+  elif tfHasAsgn in t.flags:
     var op: PSym
     if sameType(t, c.asgnForType):
       # generate recursive call:
@@ -131,19 +142,29 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
     else:
       op = field
       if op == nil:
-        op = liftBody(c.graph, t, c.kind, c.info)
+        op = liftBody(c.c, t, c.kind, c.info)
     if sfError in op.flags:
       incl c.fn.flags, sfError
     else:
       markUsed(c.graph.config, c.info, op, c.graph.usageSym)
     onUse(c.info, op)
+    # We also now do generic instantiations in the destructor lifting pass:
+    if op.ast[genericParamsPos].kind != nkEmpty:
+      assert t.typeInst != nil
+      op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1)
+      field = op
+      #echo "trying to use ", op.ast
+      #echo "for ", op.name.s, " "
+      #debug(t)
+      #return false
+    assert op.ast[genericParamsPos].kind == nkEmpty
     body.add newAsgnCall(c.graph, op, x, y)
     result = true
 
 proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
   var op = t.destructor
   if op == nil and useNoGc(c, t):
-    op = liftBody(c.graph, t, attachedDestructor, c.info)
+    op = liftBody(c.c, t, attachedDestructor, c.info)
     doAssert op != nil
     doAssert op == t.destructor
 
@@ -159,7 +180,20 @@ proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
 proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
   case c.kind
   of attachedDestructor:
-    result = addDestructorCall(c, t, body, x)
+    var op = t.destructor
+    if op != nil and sfOverriden in op.flags:
+
+      if op.ast[genericParamsPos].kind != nkEmpty:
+        assert t.typeInst != nil
+        # patch generic destructor:
+        op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1)
+        t.destructor = op
+
+      markUsed(c.graph.config, c.info, op, c.graph.usageSym)
+      onUse(c.info, op)
+      body.add destructorCall(c.graph, op, x)
+      result = true
+    #result = addDestructorCall(c, t, body, x)
   of attachedAsgn:
     result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
   of attachedSink:
@@ -221,10 +255,10 @@ proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
   lenCall.typ = getSysType(g, x.info, tyInt)
   result.add lenCall
 
-proc setLenCall(g: ModuleGraph; x, y: PNode): PNode =
+proc setLenCall(g: ModuleGraph; x, y: PNode; m: TMagic): PNode =
   let lenCall = genBuiltin(g, mLengthSeq, "len", y)
   lenCall.typ = getSysType(g, x.info, tyInt)
-  result = genBuiltin(g, mSetLengthSeq, "setLen", genAddr(g, x))
+  result = genBuiltin(g, m, "setLen", genAddr(g, x))
   result.add lenCall
 
 proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
@@ -244,7 +278,7 @@ proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     # var i = 0
     # while i < y.len: dest[i] = y[i]; inc(i)
     # This is usually more efficient than a destroy/create pair.
-    body.add setLenCall(c.graph, x, y)
+    body.add setLenCall(c.graph, x, y, mSetLengthSeq)
     forallElements(c, t, body, x, y)
   of attachedSink:
     let moveCall = genBuiltin(c.graph, mMove, "move", x)
@@ -273,7 +307,23 @@ proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       body.add genIf(c, genVerbatim("dest@len != 0 && dest@region", c.info), deallocStmt)
 
 proc strOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
-  seqOp(c, t, body, x, y)
+  case c.kind
+  of attachedAsgn, attachedDeepCopy:
+    # we generate:
+    # setLen(dest, y.len)
+    # var i = 0
+    # while i < y.len: dest[i] = y[i]; inc(i)
+    # This is usually more efficient than a destroy/create pair.
+    body.add setLenCall(c.graph, x, y, mSetLengthStr)
+    forallElements(c, t, body, x, y)
+  of attachedSink:
+    let moveCall = genBuiltin(c.graph, mMove, "move", x)
+    moveCall.add y
+    doAssert t.destructor != nil
+    moveCall.add destructorCall(c.graph, t.destructor, x)
+    body.add moveCall
+  of attachedDestructor:
+    body.add genBuiltin(c.graph, mDestroy, "destroy", x)
 
 proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case c.kind
@@ -322,13 +372,34 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     call.sons[1] = y
     body.add newAsgnStmt(x, call)
   elif optNimV2 in c.graph.config.globalOptions:
+    let xx = genBuiltin(c.graph, mAccessEnv, "accessEnv", x)
     case c.kind
-    of attachedSink, attachedAsgn: discard
-    of attachedDestructor: discard
+    of attachedSink:
+      # we 'nil' y out afterwards so we *need* to take over its reference
+      # count value:
+      body.add genIf(c, xx, callCodegenProc(c.graph, "nimDecWeakRef", c.info, xx))
+      body.add newAsgnStmt(x, y)
+    of attachedAsgn:
+      body.add callCodegenProc(c.graph, "nimIncWeakRef", c.info, y)
+      body.add genIf(c, xx, callCodegenProc(c.graph, "nimDecWeakRef", c.info, xx))
+      body.add newAsgnStmt(x, y)
+    of attachedDestructor:
+      body.add genIf(c, xx, callCodegenProc(c.graph, "nimDecWeakRef", c.info, xx))
     of attachedDeepCopy: assert(false, "cannot happen")
 
 proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
-  discard "to implement"
+  let xx = genBuiltin(c.graph, mAccessEnv, "accessEnv", x)
+  var actions = newNodeI(nkStmtList, c.info)
+  let elemType = t.lastSon
+  discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx))
+  actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, xx)
+  case c.kind
+  of attachedSink, attachedAsgn:
+    body.add genIf(c, xx, actions)
+    body.add newAsgnStmt(x, y)
+  of attachedDestructor:
+    body.add genIf(c, xx, actions)
+  of attachedDeepCopy: assert(false, "cannot happen")
 
 proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case t.kind
@@ -417,35 +488,35 @@ proc addParam(procType: PType; param: PSym) =
   addSon(procType.n, newSymNode(param))
   rawAddSon(procType, param.typ)
 
-proc liftBodyDistinctType(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
+proc liftBodyDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
   assert typ.kind == tyDistinct
   let baseType = typ[0]
   case kind
   of attachedAsgn:
     if baseType.assignment == nil:
-      discard liftBody(g, baseType, kind, info)
+      discard liftBody(c, baseType, kind, info)
     typ.assignment = baseType.assignment
     result = typ.assignment
   of attachedSink:
     if baseType.sink == nil:
-      discard liftBody(g, baseType, kind, info)
+      discard liftBody(c, baseType, kind, info)
     typ.sink = baseType.sink
     result = typ.sink
   of attachedDeepCopy:
     if baseType.deepCopy == nil:
-      discard liftBody(g, baseType, kind, info)
+      discard liftBody(c, baseType, kind, info)
     typ.deepCopy = baseType.deepCopy
     result = typ.deepCopy
   of attachedDestructor:
     if baseType.destructor == nil:
-      discard liftBody(g, baseType, kind, info)
+      discard liftBody(c, baseType, kind, info)
     typ.destructor = baseType.destructor
     result = typ.destructor
 
-proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
+proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
               info: TLineInfo): PSym =
   if typ.kind == tyDistinct:
-    return liftBodyDistinctType(g, typ, kind, info)
+    return liftBodyDistinctType(c, typ, kind, info)
   when false:
     var typ = typ
     if c.config.selectedGC == gcDestructors and typ.kind == tySequence:
@@ -454,8 +525,10 @@ proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
 
   var a: TLiftCtx
   a.info = info
-  a.graph = g
+  a.graph = c.graph
   a.kind = kind
+  a.c = c
+  let g = c.graph
   let body = newNodeI(nkStmtList, info)
   let procname = case kind
                  of attachedAsgn: getIdent(g.cache, "=")
@@ -477,26 +550,14 @@ proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
   if kind != attachedDestructor:
     result.typ.addParam src
 
-  if optNimV2 in g.config.globalOptions:
-    case kind
-    of attachedAsgn: typ.assignment = result
-    of attachedSink: typ.sink = result
-    of attachedDeepCopy: typ.deepCopy = result
-    of attachedDestructor: typ.destructor = result
+  # register this operation already:
+  case kind
+  of attachedAsgn: typ.assignment = result
+  of attachedSink: typ.sink = result
+  of attachedDeepCopy: typ.deepCopy = result
+  of attachedDestructor: typ.destructor = result
 
   liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
-  if optNimV2 notin g.config.globalOptions:
-    # recursion is handled explicitly, do not register the type based operation
-    # before 'liftBodyAux':
-    if g.config.selectedGC == gcDestructors and
-        typ.kind in {tySequence, tyString} and body.len == 0:
-      discard "do not cache it yet"
-    else:
-      case kind
-      of attachedAsgn: typ.assignment = result
-      of attachedSink: typ.sink = result
-      of attachedDeepCopy: typ.deepCopy = result
-      of attachedDestructor: typ.destructor = result
 
   var n = newNodeI(nkProcDef, info, bodyPos+1)
   for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
@@ -505,32 +566,66 @@ proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
   n.sons[bodyPos] = body
   result.ast = n
   incl result.flags, sfFromGeneric
+  incl result.flags, sfGeneratedOp
 
-
-proc getAsgnOrLiftBody(g: ModuleGraph; typ: PType; info: TLineInfo): PSym =
+proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
   let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
   result = t.assignment
   if result.isNil:
-    result = liftBody(g, t, attachedAsgn, info)
-
-proc overloadedAsgn(g: ModuleGraph; dest, src: PNode): PNode =
-  let a = getAsgnOrLiftBody(g, dest.typ, dest.info)
-  result = newAsgnCall(g, a, dest, src)
-
-proc liftTypeBoundOps*(g: ModuleGraph; typ: PType; info: TLineInfo) =
+    result = liftBody(c, t, attachedAsgn, info)
+
+proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
+  let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
+  result = newAsgnCall(c.graph, a, dest, src)
+
+template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
+  discard "now a nop"
+
+proc patchBody(c: PContext; n: PNode; info: TLineInfo) =
+  if n.kind in nkCallKinds:
+    if n[0].kind == nkSym and n[0].sym.magic == mDestroy:
+      let t = n[1].typ.skipTypes(abstractVar)
+      if t.destructor == nil:
+        liftBody(c, t, attachedDestructor, info)
+
+      if t.destructor != nil:
+        if t.destructor.ast[genericParamsPos].kind != nkEmpty:
+          internalError(c.graph.config, info, "resolved destructor is generic")
+        if t.destructor.magic == mDestroy:
+          internalError(c.graph.config, info, "patching mDestroy with mDestroy?")
+        n.sons[0] = newSymNode(t.destructor)
+  for x in n: patchBody(c, x, info)
+
+template inst(field, t) =
+  if field.ast != nil and field.ast[genericParamsPos].kind != nkEmpty:
+    assert t.typeInst != nil
+    field = c.instTypeBoundOp(c, field, t.typeInst, info, attachedAsgn, 1)
+    if field.ast != nil:
+      patchBody(c, field.ast, info)
+
+proc createTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
   ## In the semantic pass this is called in strategic places
   ## to ensure we lift assignment, destructors and moves properly.
-  ## The later 'destroyer' pass depends on it.
-  if not hasDestructor(typ): return
-  when false:
-    # do not produce wrong liftings while we're still instantiating generics:
-    # now disabled; breaks topttree.nim!
-    if c.typesWithOps.len > 0: return
+  ## The later 'injectdestructors' pass depends on it.
+  if typ == nil or {tfCheckedForDestructor, tfHasMeta} * typ.flags != {}: return
+  incl typ.flags, tfCheckedForDestructor
+  # multiple cases are to distinguish here:
+  # 1. we don't know yet if 'typ' has a nontrival destructor.
+  # 2. we have a nop destructor. --> mDestroy
+  # 3. we have a lifted destructor.
+  # 4. We have a custom destructor.
+  # 5. We have a (custom) generic destructor.
   let typ = typ.skipTypes({tyGenericInst, tyAlias})
   # we generate the destructor first so that other operators can depend on it:
   if typ.destructor == nil:
-    liftBody(g, typ, attachedDestructor, info)
+    liftBody(c, typ, attachedDestructor, info)
+  else:
+    inst(typ.destructor, typ)
   if typ.assignment == nil:
-    liftBody(g, typ, attachedAsgn, info)
+    liftBody(c, typ, attachedAsgn, info)
+  else:
+    inst(typ.assignment, typ)
   if typ.sink == nil:
-    liftBody(g, typ, attachedSink, info)
+    liftBody(c, typ, attachedSink, info)
+  else:
+    inst(typ.sink, typ)
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index b78bccceb..e1eff7a70 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -80,7 +80,7 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
   addSon(result, lit)
 
 proc newTryFinally*(body, final: PNode): PNode =
-  result = newTree(nkTryStmt, body, newTree(nkFinally, final))
+  result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
 
 proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   let value = n.lastSon
diff --git a/compiler/options.nim b/compiler/options.nim
index cf89e3488..2ab5309bf 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -36,7 +36,7 @@ type                          # please make sure we have under 32 options
     optProfiler,              # profiler turned on
     optImplicitStatic,        # optimization: implicit at compile time
                               # evaluation
-    optPatterns,              # en/disable pattern matching
+    optTrMacros,              # en/disable pattern matching
     optMemTracker,
     optLaxStrings,
     optNilSeqs,
@@ -273,7 +273,7 @@ const
   DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
     optBoundsCheck, optOverflowCheck, optAssert, optWarns,
     optHints, optStackTrace, optLineTrace,
-    optPatterns, optNilCheck, optMoveCheck}
+    optTrMacros, optNilCheck, optMoveCheck}
   DefaultGlobalOptions* = {optThreadAnalysis}
 
 proc getSrcTimestamp(): DateTime =
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 4ef662f50..8a701d7f3 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -48,7 +48,7 @@ const
     wDeadCodeElimUnused,  # deprecated, always on
     wDeprecated,
     wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
-    wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto,
+    wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto,
     wInjectStmt, wDeprecated, wExperimental, wThis}
   lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
     wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
@@ -350,7 +350,7 @@ proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} =
   of wMemTracker: {optMemTracker}
   of wByRef: {optByRef}
   of wImplicitStatic: {optImplicitStatic}
-  of wPatterns: {optPatterns}
+  of wPatterns, wTrMacros: {optTrMacros}
   else: {}
 
 proc processExperimental(c: PContext; n: PNode) =
@@ -1011,7 +1011,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
          wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
          wLinedir, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler,
-         wFloatchecks, wNanChecks, wInfChecks, wPatterns:
+         wFloatchecks, wNanChecks, wInfChecks, wPatterns, wTrMacros:
         processOption(c, it, c.config.options)
       of wStacktrace, wLinetrace:
         if sym.kind in {skProc, skMethod, skConverter}:
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 832add378..5d65cf6d3 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -503,7 +503,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
   of nkReturnStmt:
     if n.len > 0 and n[0].kind == nkAsgn:
       result = len("return_") + lsub(g, n[0][1])
-    else: 
+    else:
       result = len("return_") + lsub(g, n[0])
   of nkRaiseStmt: result = lsub(g, n.sons[0]) + len("raise_")
   of nkYieldStmt: result = lsub(g, n.sons[0]) + len("yield_")
@@ -1295,7 +1295,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkWhileStmt: gwhile(g, n)
   of nkPragmaBlock: gpragmaBlock(g, n)
   of nkCaseStmt, nkRecCase: gcase(g, n)
-  of nkTryStmt: gtry(g, n)
+  of nkTryStmt, nkHiddenTryStmt: gtry(g, n)
   of nkForStmt, nkParForStmt: gfor(g, n)
   of nkBlockStmt, nkBlockExpr: gblock(g, n)
   of nkStaticStmt: gstaticStmt(g, n)
@@ -1345,9 +1345,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
       gsub(g, n.sons[0])
   of nkReturnStmt:
     putWithSpace(g, tkReturn, "return")
-    if n.len > 0 and n[0].kind == nkAsgn: 
+    if n.len > 0 and n[0].kind == nkAsgn:
       gsub(g, n[0], 1)
-    else: 
+    else:
       gsub(g, n, 0)
   of nkRaiseStmt:
     putWithSpace(g, tkRaise, "raise")
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 0a3b60ab3..66d963e16 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -586,7 +586,7 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
     result = buildEchoStmt(c, result)
   if c.config.cmd == cmdIdeTools:
     appendToModule(c.module, result)
-  trackTopLevelStmt(c.graph, c.module, result)
+  trackTopLevelStmt(c, c.module, result)
 
 proc recoverContext(c: PContext) =
   # clean up in case of a semantic error: We clean up the stacks, etc. This is
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index a05bda32d..bfe7d5f33 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -298,10 +298,12 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
     result = typ
   else:
     result = newTypeS(tyTypeDesc, c)
+    incl result.flags, tfCheckedForDestructor
     result.addSonSkipIntLit(typ)
 
 proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
   let typedesc = newTypeS(tyTypeDesc, c)
+  incl typedesc.flags, tfCheckedForDestructor
   typedesc.addSonSkipIntLit(assertNotNil(c.config, typ))
   let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
                    c.config.options).linkTo(typedesc)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 3e8edc687..5cc2e96a6 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -815,7 +815,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
       result = magicsAfterOverloadResolution(c, result, flags)
     if result.typ != nil and
         not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty):
-      liftTypeBoundOps(c.graph, result.typ, n.info)
+      liftTypeBoundOps(c, result.typ, n.info)
     #result = patchResolvedTypeBoundOp(c, result)
   if c.matchedConcept == nil:
     result = evalAtCompileTime(c, result)
@@ -1616,7 +1616,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
           typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
 
     n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
-    liftTypeBoundOps(c.graph, lhs.typ, lhs.info)
+    liftTypeBoundOps(c, lhs.typ, lhs.info)
     #liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info)
 
     fixAbstractType(c, n)
@@ -2617,7 +2617,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkTypeSection: result = semTypeSection(c, n)
   of nkDiscardStmt: result = semDiscard(c, n)
   of nkWhileStmt: result = semWhile(c, n, flags)
-  of nkTryStmt: result = semTry(c, n, flags)
+  of nkTryStmt, nkHiddenTryStmt: result = semTry(c, n, flags)
   of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
   of nkForStmt, nkParForStmt: result = semFor(c, n, flags)
   of nkCaseStmt: result = semCase(c, n, flags)
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 2810fca9e..0ba6c302f 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -369,7 +369,7 @@ proc semGenericStmt(c: PContext, n: PNode,
       addTempDecl(c, n.sons[0], skLabel)
     n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
     closeScope(c)
-  of nkTryStmt:
+  of nkTryStmt, nkHiddenTryStmt:
     checkMinSonsLen(n, 2, c.config)
     n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx)
     for i in countup(1, sonsLen(n)-1):
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 51303d1b5..2423a428b 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -147,7 +147,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
     freshGenSyms(b, result, orig, symMap)
     b = semProcBody(c, b)
     result.ast[bodyPos] = hloBody(c, b)
-    trackProc(c.graph, result, result.ast[bodyPos])
+    trackProc(c, result, result.ast[bodyPos])
     excl(result.flags, sfForward)
     dec c.inGenericInst
 
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index a0c35c9ca..f290a08d5 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -428,4 +428,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
     if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}:
       localError(c.config, n.info, "finalizer must be a direct reference to a procedure")
     result = n
+  of mDestroy:
+    result = n
+    let t = n[1].typ.skipTypes(abstractVar)
+    if t.destructor != nil:
+      result.sons[0] = newSymNode(t.destructor)
   else: result = n
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index 137c5d336..a58111955 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -352,7 +352,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
     if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
       c.addSlice(n, n[0], n[1], n[1])
     analyseSons(c, n)
-  of nkReturnStmt, nkRaiseStmt, nkTryStmt:
+  of nkReturnStmt, nkRaiseStmt, nkTryStmt, nkHiddenTryStmt:
     localError(c.graph.config, n.info, "invalid control flow for 'parallel'")
     # 'break' that leaves the 'parallel' section is not valid either
     # or maybe we should generate a 'try' XXX
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 6ac90f617..a3a474828 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -9,8 +9,8 @@
 
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  wordrecg, strutils, options, guards, lineinfos, semfold,
-  modulegraphs
+  wordrecg, strutils, options, guards, lineinfos, semfold, semdata,
+  modulegraphs, lowerings, sigmatch
 
 when not defined(leanCompiler):
   import writetracking
@@ -18,11 +18,30 @@ when not defined(leanCompiler):
 when defined(useDfa):
   import dfa
 
-# Second semantic checking pass over the AST. Necessary because the old
-# way had some inherent problems. Performs:
-#
-# * effect+exception tracking
-# * "usage before definition" checking
+include liftdestructors
+
+#[ Second semantic checking pass over the AST. Necessary because the old
+   way had some inherent problems. Performs:
+
+* effect+exception tracking
+* "usage before definition" checking
+* also now calls the "lift destructor logic" at strategic positions, this
+  is about to be put into the spec:
+
+We treat assignment and sinks and destruction as identical.
+
+In the construct let/var x = expr() x's type is marked.
+
+In x = y the type of x is marked.
+
+For every sink parameter of type T T is marked. TODO!
+
+For every call f() the return type of f() is marked.
+
+
+
+
+]#
 
 # ------------------------ exception and tag tracking -------------------------
 
@@ -60,6 +79,7 @@ type
     maxLockLevel, currLockLevel: TLockLevel
     config: ConfigRef
     graph: ModuleGraph
+    c: PContext
   PEffects = var TEffects
 
 proc `<`(a, b: TLockLevel): bool {.borrow.}
@@ -416,73 +436,6 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) =
       # list the computed effects up to here:
       listEffects(tracked)
 
-proc effectSpec(n: PNode, effectType: TSpecialWord): PNode =
-  for i in countup(0, sonsLen(n) - 1):
-    var it = n.sons[i]
-    if it.kind == nkExprColonExpr and whichPragma(it) == effectType:
-      result = it.sons[1]
-      if result.kind notin {nkCurly, nkBracket}:
-        result = newNodeI(nkCurly, result.info)
-        result.add(it.sons[1])
-      return
-
-proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
-  let spec = effectSpec(x, effectType)
-  if isNil(spec):
-    let s = n.sons[namePos].sym
-
-    let actual = s.typ.n.sons[0]
-    if actual.len != effectListLen: return
-    let real = actual.sons[idx]
-
-    # warning: hack ahead:
-    var effects = newNodeI(nkBracket, n.info, real.len)
-    for i in 0 ..< real.len:
-      var t = typeToString(real[i].typ)
-      if t.startsWith("ref "): t = substr(t, 4)
-      effects.sons[i] = newIdentNode(getIdent(cache, t), n.info)
-      # set the type so that the following analysis doesn't screw up:
-      effects.sons[i].typ = real[i].typ
-
-    result = newNode(nkExprColonExpr, n.info, @[
-      newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects])
-
-proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
-  let s = n.sons[namePos].sym
-  let params = s.typ.n
-
-  var effects = newNodeI(nkBracket, n.info)
-  for i in 1 ..< params.len:
-    if params[i].kind == nkSym and flag in params[i].sym.flags:
-      effects.add params[i]
-
-  if effects.len > 0:
-    result = newNode(nkExprColonExpr, n.info, @[
-      newIdentNode(getIdent(cache, pragmaName), n.info), effects])
-
-proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
-  let s = n.sons[namePos].sym
-  if tfReturnsNew in s.typ.flags:
-    result = newIdentNode(getIdent(cache, "new"), n.info)
-
-proc documentRaises*(cache: IdentCache; n: PNode) =
-  if n.sons[namePos].kind != nkSym: return
-  let pragmas = n.sons[pragmasPos]
-  let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects)
-  let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects)
-  let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes")
-  let p4 = documentNewEffect(cache, n)
-  let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes")
-
-  if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil:
-    if pragmas.kind == nkEmpty:
-      n.sons[pragmasPos] = newNodeI(nkPragma, n.info)
-    if p1 != nil: n.sons[pragmasPos].add p1
-    if p2 != nil: n.sons[pragmasPos].add p2
-    if p3 != nil: n.sons[pragmasPos].add p3
-    if p4 != nil: n.sons[pragmasPos].add p4
-    if p5 != nil: n.sons[pragmasPos].add p5
-
 template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
 
 proc importedFromC(n: PNode): bool =
@@ -749,11 +702,13 @@ proc track(tracked: PEffects, n: PNode) =
       # Here we add a `Exception` tag in order to cover both the cases.
       addEffect(tracked, createRaise(tracked.graph, n))
   of nkCallKinds:
-    if getConstExpr(tracked.owner_module, n, tracked.graph) != nil:
-      return
     # p's effects are ours too:
     var a = n.sons[0]
     let op = a.typ
+    if getConstExpr(tracked.owner_module, n, tracked.graph) != nil:
+      return
+    if op != nil:
+      createTypeBoundOps(tracked.c, op, n.info)
     if a.kind == nkCast and a[1].typ.kind == tyProc:
       a = a[1]
     # XXX: in rare situations, templates and macros will reach here after
@@ -813,10 +768,12 @@ proc track(tracked: PEffects, n: PNode) =
     addAsgnFact(tracked.guards, n.sons[0], n.sons[1])
     notNilCheck(tracked, n.sons[1], n.sons[0].typ)
     when false: cstringCheck(tracked, n)
+    createTypeBoundOps(tracked.c, n[0].typ, n.info)
   of nkVarSection, nkLetSection:
     for child in n:
       let last = lastSon(child)
       if last.kind != nkEmpty: track(tracked, last)
+      createTypeBoundOps(tracked.c, child[0].typ, child.info)
       if child.kind == nkIdentDefs and last.kind != nkEmpty:
         for i in 0 .. child.len-3:
           initVar(tracked, child.sons[i], volatileCheck=false)
@@ -965,7 +922,7 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) =
       effects[tagEffects] = tagsSpec
     effects[pragmasEffects] = n
 
-proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
+proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) =
   newSeq(effects.sons, effectListLen)
   effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
   effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
@@ -983,8 +940,10 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
   t.locked = @[]
   t.graph = g
   t.config = g.config
+  t.c = c
 
-proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
+proc trackProc*(c: PContext; s: PSym, body: PNode) =
+  let g = c.graph
   var effects = s.typ.n.sons[0]
   if effects.kind != nkEffectList: return
   # effects already computed?
@@ -992,7 +951,7 @@ proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
   if effects.len == effectListLen: return
 
   var t: TEffects
-  initEffects(g, effects, s, t)
+  initEffects(g, effects, s, t, c)
   track(t, body)
   if not isEmptyType(s.typ.sons[0]) and
       {tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
@@ -1043,12 +1002,13 @@ proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
       dataflowAnalysis(s, body)
       when false: trackWrites(s, body)
 
-proc trackTopLevelStmt*(g: ModuleGraph; module: PSym; n: PNode) =
+proc trackTopLevelStmt*(c: PContext; module: PSym; n: PNode) =
   if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
                 nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
     return
+  let g = c.graph
   var effects = newNode(nkEffectList, n.info)
   var t: TEffects
-  initEffects(g, effects, module, t)
+  initEffects(g, effects, module, t, c)
   t.isToplevel = true
   track(t, n)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index c3d909824..bc403f1ea 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -339,7 +339,7 @@ proc checkNilable(c: PContext; v: PSym) =
     elif tfNotNil in v.typ.flags and tfNotNil notin v.astdef.typ.flags:
       message(c.config, v.info, warnProveInit, v.name.s)
 
-include liftdestructors
+#include liftdestructors
 
 proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) =
   let L = identDefs.len
@@ -484,7 +484,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
     # this can only happen for errornous var statements:
     if typ == nil: continue
     typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {})
-    liftTypeBoundOps(c.graph, typ, a.info)
+    liftTypeBoundOps(c, typ, a.info)
     instAllTypeBoundOp(c, a.info)
     var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
     if a.kind == nkVarTuple:
@@ -1265,7 +1265,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
         checkConstructedType(c.config, s.info, s.typ)
         if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
           checkForMetaFields(c, s.typ.n)
-  instAllTypeBoundOp(c, n.info)
+  #instAllTypeBoundOp(c, n.info)
 
 
 proc semAllTypeSections(c: PContext; n: PNode): PNode =
@@ -1474,7 +1474,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
       addResult(c, s.typ.sons[0], n.info, skProc)
       addResultNode(c, n)
       s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
-      trackProc(c.graph, s, s.ast[bodyPos])
+      trackProc(c, s, s.ast[bodyPos])
       popProcCon(c)
     elif efOperand notin flags:
       localError(c.config, n.info, errGenericLambdaNotAllowed)
@@ -1515,7 +1515,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   addResult(c, n.typ.sons[0], n.info, skProc)
   addResultNode(c, n)
   s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
-  trackProc(c.graph, s, s.ast[bodyPos])
+  trackProc(c, s, s.ast[bodyPos])
   popProcCon(c)
   popOwner(c)
   closeScope(c)
@@ -1552,6 +1552,14 @@ proc canonType(c: PContext, t: PType): PType =
     result = t
 
 proc semOverride(c: PContext, s: PSym, n: PNode) =
+  proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
+    var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj)
+    if sfOverriden notin prevOp.flags:
+      msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info)
+    else:
+      msg.add "; previous declaration was here: " & (c.config $ prevOp.info)
+    localError(c.config, n.info, errGenerated, msg)
+
   let name = s.name.s.normalize
   case name
   of "=destroy":
@@ -1569,8 +1577,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
         if obj.destructor.isNil:
           obj.destructor = s
         else:
-          localError(c.config, n.info, errGenerated,
-            "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
+          prevDestructor(c, obj.destructor, obj, n.info)
         noError = true
         if obj.owner.getModule != s.getModule:
           localError(c.config, n.info, errGenerated,
@@ -1601,8 +1608,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
                    "cannot bind 'deepCopy' to: " & typeToString(t))
 
       if t.owner.getModule != s.getModule:
-          localError(c.config, n.info, errGenerated,
-            "type bound operation `" & name & "` can be defined only in the same module with its type (" & t.typeToString() & ")")
+        localError(c.config, n.info, errGenerated,
+          "type bound operation `" & name & "` can be defined only in the same module with its type (" & t.typeToString() & ")")
 
     else:
       localError(c.config, n.info, errGenerated,
@@ -1635,11 +1642,10 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
         if opr[].isNil:
           opr[] = s
         else:
-          localError(c.config, n.info, errGenerated,
-                     "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
+          prevDestructor(c, opr[], obj, n.info)
         if obj.owner.getModule != s.getModule:
-            localError(c.config, n.info, errGenerated,
-              "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
+          localError(c.config, n.info, errGenerated,
+            "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
 
         return
     if sfSystemModule notin s.owner.flags:
@@ -1862,7 +1868,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
           s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
           # unfortunately we cannot skip this step when in 'system.compiles'
           # context as it may even be evaluated in 'system.compiles':
-          trackProc(c.graph, s, s.ast[bodyPos])
+          trackProc(c, s, s.ast[bodyPos])
       else:
         if s.typ.sons[0] != nil and kind != skIterator:
           addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index dae2833ec..cbb7a95c9 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -391,7 +391,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         n.sons[0] = newSymNode(s, n.sons[0].info)
     n.sons[1] = semTemplBody(c, n.sons[1])
     closeScope(c)
-  of nkTryStmt:
+  of nkTryStmt, nkHiddenTryStmt:
     checkMinSonsLen(n, 2, c.c.config)
     n.sons[0] = semTemplBodyScope(c, n.sons[0])
     for i in countup(1, sonsLen(n)-1):
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 6996082c3..f215d4a45 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -53,6 +53,7 @@ proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
 
 proc newConstraint(c: PContext, k: TTypeKind): PType =
   result = newTypeS(tyBuiltInTypeClass, c)
+  result.flags.incl tfCheckedForDestructor
   result.addSonSkipIntLit(newTypeS(k, c))
 
 proc semEnum(c: PContext, n: PNode, prev: PType): PType =
@@ -951,8 +952,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
           paramTypId.id == getIdent(c.cache, "typedesc").id:
         # XXX Why doesn't this check for tyTypeDesc instead?
         paramTypId = nil
-      result = addImplicitGeneric(
-        c.newTypeWithSons(tyTypeDesc, @[paramType.base]))
+      let t = c.newTypeWithSons(tyTypeDesc, @[paramType.base])
+      incl t.flags, tfCheckedForDestructor
+      result = addImplicitGeneric(t)
 
   of tyDistinct:
     if paramType.sonsLen == 1:
@@ -1138,6 +1140,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
           # surprising behavior. We must instead fix the expected type of
           # the proc to be the unbound typedesc type:
           typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
+          typ.flags.incl tfCheckedForDestructor
 
       else:
         # if def.typ != nil and def.typ.kind != tyNone:
@@ -1411,6 +1414,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
     inherited = n[2]
 
   result = newOrPrevType(tyUserTypeClass, prev, c)
+  result.flags.incl tfCheckedForDestructor
   var owner = getCurrOwner(c)
   var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType])
   result.sons = @[candidateTypeSlot]
@@ -1432,7 +1436,9 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
     if modifier != tyNone:
       dummyName = param[0]
       dummyType = c.makeTypeWithModifier(modifier, candidateTypeSlot)
-      if modifier == tyTypeDesc: dummyType.flags.incl tfConceptMatchedTypeSym
+      if modifier == tyTypeDesc:
+        dummyType.flags.incl tfConceptMatchedTypeSym
+        dummyType.flags.incl tfCheckedForDestructor
     else:
       dummyName = param
       dummyType = candidateTypeSlot
@@ -1752,7 +1758,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     result = newOrPrevType(tyError, prev, c)
   n.typ = result
   dec c.inTypeContext
-  if c.inTypeContext == 0:
+  if false: # c.inTypeContext == 0:
     #if $n == "var seq[StackTraceEntry]":
     #  echo "begin ", n
     instAllTypeBoundOp(c, n.info)
@@ -1779,24 +1785,28 @@ proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
   else:
     discard
 
+proc setMagicIntegral(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
+  setMagicType(conf, m, kind, size)
+  incl m.typ.flags, tfCheckedForDestructor
+
 proc processMagicType(c: PContext, m: PSym) =
   case m.magic
-  of mInt: setMagicType(c.config, m, tyInt, c.config.target.intSize)
-  of mInt8: setMagicType(c.config, m, tyInt8, 1)
-  of mInt16: setMagicType(c.config, m, tyInt16, 2)
-  of mInt32: setMagicType(c.config, m, tyInt32, 4)
-  of mInt64: setMagicType(c.config, m, tyInt64, 8)
-  of mUInt: setMagicType(c.config, m, tyUInt, c.config.target.intSize)
-  of mUInt8: setMagicType(c.config, m, tyUInt8, 1)
-  of mUInt16: setMagicType(c.config, m, tyUInt16, 2)
-  of mUInt32: setMagicType(c.config, m, tyUInt32, 4)
-  of mUInt64: setMagicType(c.config, m, tyUInt64, 8)
-  of mFloat: setMagicType(c.config, m, tyFloat, c.config.target.floatSize)
-  of mFloat32: setMagicType(c.config, m, tyFloat32, 4)
-  of mFloat64: setMagicType(c.config, m, tyFloat64, 8)
-  of mFloat128: setMagicType(c.config, m, tyFloat128, 16)
-  of mBool: setMagicType(c.config, m, tyBool, 1)
-  of mChar: setMagicType(c.config, m, tyChar, 1)
+  of mInt: setMagicIntegral(c.config, m, tyInt, c.config.target.intSize)
+  of mInt8: setMagicIntegral(c.config, m, tyInt8, 1)
+  of mInt16: setMagicIntegral(c.config, m, tyInt16, 2)
+  of mInt32: setMagicIntegral(c.config, m, tyInt32, 4)
+  of mInt64: setMagicIntegral(c.config, m, tyInt64, 8)
+  of mUInt: setMagicIntegral(c.config, m, tyUInt, c.config.target.intSize)
+  of mUInt8: setMagicIntegral(c.config, m, tyUInt8, 1)
+  of mUInt16: setMagicIntegral(c.config, m, tyUInt16, 2)
+  of mUInt32: setMagicIntegral(c.config, m, tyUInt32, 4)
+  of mUInt64: setMagicIntegral(c.config, m, tyUInt64, 8)
+  of mFloat: setMagicIntegral(c.config, m, tyFloat, c.config.target.floatSize)
+  of mFloat32: setMagicIntegral(c.config, m, tyFloat32, 4)
+  of mFloat64: setMagicIntegral(c.config, m, tyFloat64, 8)
+  of mFloat128: setMagicIntegral(c.config, m, tyFloat128, 16)
+  of mBool: setMagicIntegral(c.config, m, tyBool, 1)
+  of mChar: setMagicIntegral(c.config, m, tyChar, 1)
   of mString:
     setMagicType(c.config, m, tyString, szUncomputedSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
@@ -1804,29 +1814,29 @@ proc processMagicType(c: PContext, m: PSym) =
       if c.config.selectedGc == gcDestructors:
         incl m.typ.flags, tfHasAsgn
   of mCstring:
-    setMagicType(c.config, m, tyCString, c.config.target.ptrSize)
+    setMagicIntegral(c.config, m, tyCString, c.config.target.ptrSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
-  of mPointer: setMagicType(c.config, m, tyPointer, c.config.target.ptrSize)
+  of mPointer: setMagicIntegral(c.config, m, tyPointer, c.config.target.ptrSize)
   of mEmptySet:
-    setMagicType(c.config, m, tySet, 1)
+    setMagicIntegral(c.config, m, tySet, 1)
     rawAddSon(m.typ, newTypeS(tyEmpty, c))
-  of mIntSetBaseType: setMagicType(c.config, m, tyRange, c.config.target.intSize)
+  of mIntSetBaseType: setMagicIntegral(c.config, m, tyRange, c.config.target.intSize)
   of mNil: setMagicType(c.config, m, tyNil, c.config.target.ptrSize)
   of mExpr:
     if m.name.s == "auto":
-      setMagicType(c.config, m, tyAnything, 0)
+      setMagicIntegral(c.config, m, tyAnything, 0)
     else:
-      setMagicType(c.config, m, tyExpr, 0)
+      setMagicIntegral(c.config, m, tyExpr, 0)
   of mStmt:
-    setMagicType(c.config, m, tyStmt, 0)
+    setMagicIntegral(c.config, m, tyStmt, 0)
   of mTypeDesc, mType:
-    setMagicType(c.config, m, tyTypeDesc, 0)
+    setMagicIntegral(c.config, m, tyTypeDesc, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mStatic:
     setMagicType(c.config, m, tyStatic, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mVoidType:
-    setMagicType(c.config, m, tyVoid, 0)
+    setMagicIntegral(c.config, m, tyVoid, 0)
   of mArray:
     setMagicType(c.config, m, tyArray, szUncomputedSize)
   of mOpenArray:
@@ -1834,12 +1844,12 @@ proc processMagicType(c: PContext, m: PSym) =
   of mVarargs:
     setMagicType(c.config, m, tyVarargs, szUncomputedSize)
   of mRange:
-    setMagicType(c.config, m, tyRange, szUncomputedSize)
+    setMagicIntegral(c.config, m, tyRange, szUncomputedSize)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mSet:
-    setMagicType(c.config, m, tySet, szUncomputedSize)
+    setMagicIntegral(c.config, m, tySet, szUncomputedSize)
   of mUncheckedArray:
-    setMagicType(c.config, m, tyUncheckedArray, szUncomputedSize)
+    setMagicIntegral(c.config, m, tyUncheckedArray, szUncomputedSize)
   of mSeq:
     setMagicType(c.config, m, tySequence, szUncomputedSize)
     if c.config.selectedGc == gcDestructors:
@@ -1849,10 +1859,11 @@ proc processMagicType(c: PContext, m: PSym) =
   of mOpt:
     setMagicType(c.config, m, tyOpt, szUncomputedSize)
   of mOrdinal:
-    setMagicType(c.config, m, tyOrdinal, szUncomputedSize)
+    setMagicIntegral(c.config, m, tyOrdinal, szUncomputedSize)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mPNimrodNode:
     incl m.typ.flags, tfTriggersCompileTime
+    incl m.typ.flags, tfCheckedForDestructor
   of mException: discard
   of mBuiltinType:
     case m.name.s
@@ -1886,6 +1897,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
         if typ.kind == tyTypeDesc:
           if typ.sons[0].kind == tyNone:
             typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
+            incl typ.flags, tfCheckedForDestructor
         else:
           typ = semGenericConstraints(c, typ)
 
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index dcb6ea5a4..931a54f96 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -291,7 +291,7 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
   when false:
     if newDestructors:
       result.assignment = nil
-      #result.destructor = nil
+      result.destructor = nil
       result.sink = nil
 
 proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
@@ -404,7 +404,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
     # adding myseq for myseq[system.int]
     # sigmatch: Formal myseq[=destroy.T] real myseq[system.int]
     #echo "DESTROY: adding ", typeToString(newbody), " for ", typeToString(result, preferDesc)
-    cl.c.typesWithOps.add((newbody, result))
+    #cl.c.typesWithOps.add((newbody, result))
     let mm = skipTypes(bbody, abstractPtrs)
     if tfFromGeneric notin mm.flags:
       # bug #5479, prevent endless recursions here:
diff --git a/compiler/transf.nim b/compiler/transf.nim
index cac0ded90..640ed1136 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -21,7 +21,7 @@
 import
   intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups,
   idents, renderer, types, passes, semfold, magicsys, cgmeth,
-  sempass2, lowerings, destroyer, liftlocals,
+  sempass2, lowerings, injectdestructors, liftlocals,
   modulegraphs, lineinfos
 
 proc transformBody*(g: ModuleGraph, prc: PSym, cache = true;
diff --git a/compiler/trees.nim b/compiler/trees.nim
index ca2360e12..55a3c619e 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -120,6 +120,16 @@ proc whichPragma*(n: PNode): TSpecialWord =
   let key = if n.kind in nkPragmaCallKinds and n.len > 0: n.sons[0] else: n
   if key.kind == nkIdent: result = whichKeyword(key.ident)
 
+proc effectSpec*(n: PNode, effectType: TSpecialWord): PNode =
+  for i in countup(0, sonsLen(n) - 1):
+    var it = n.sons[i]
+    if it.kind == nkExprColonExpr and whichPragma(it) == effectType:
+      result = it.sons[1]
+      if result.kind notin {nkCurly, nkBracket}:
+        result = newNodeI(nkCurly, result.info)
+        result.add(it.sons[1])
+      return
+
 proc unnestStmts(n, result: PNode) =
   if n.kind == nkStmtList:
     for x in items(n): unnestStmts(x, result)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 47b2c12e5..93df4081f 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -2031,7 +2031,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
   of nkBreakStmt:
     unused(c, n, dest)
     genBreak(c, n)
-  of nkTryStmt: genTry(c, n, dest)
+  of nkTryStmt, nkHiddenTryStmt: genTry(c, n, dest)
   of nkStmtList:
     #unused(c, n, dest)
     # XXX Fix this bug properly, lexim triggers it
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 56b8f1806..4da19779b 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -54,7 +54,7 @@ type
     wBoundchecks, wOverflowchecks, wNilchecks,
     wFloatchecks, wNanChecks, wInfChecks, wMoveChecks,
     wNonReloadable, wExecuteOnReload,
-    wAssertions, wPatterns, wWarnings,
+    wAssertions, wPatterns, wTrMacros, wWarnings,
     wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
     wDeadCodeElimUnused,  # deprecated, dead code elim always happens
     wSafecode, wPackage, wNoForward, wReorder, wNoRewrite,
@@ -145,7 +145,7 @@ const
     "floatchecks", "nanchecks", "infchecks", "movechecks",
     "nonreloadable", "executeonreload",
 
-    "assertions", "patterns", "warnings", "hints",
+    "assertions", "patterns", "trmacros", "warnings", "hints",
     "optimization", "raises", "writes", "reads", "size", "effects", "tags",
     "deadcodeelim",  # deprecated, dead code elim always happens
     "safecode", "package", "noforward", "reorder", "norewrite",
diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim
index 1ea1deb2d..04d3b7a16 100644
--- a/compiler/writetracking.nim
+++ b/compiler/writetracking.nim
@@ -122,7 +122,7 @@ proc returnsNewExpr*(n: PNode): NewLocation =
       nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast:
     result = returnsNewExpr(n.lastSon)
   of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure,
-      nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt:
+      nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt, nkHiddenTryStmt:
     result = newLit
     for i in ord(n.kind == nkObjConstr) ..< n.len:
       let x = returnsNewExpr(n.sons[i])
diff --git a/doc/advopt.txt b/doc/advopt.txt
index 46b9a12a6..3b5827aed 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -78,7 +78,7 @@ Advanced options:
   --tlsEmulation:on|off     turn thread local storage emulation on|off
   --taintMode:on|off        turn taint mode on|off
   --implicitStatic:on|off   turn implicit compile time evaluation on|off
-  --patterns:on|off         turn term rewriting macros on|off
+  --trmacros:on|off         turn term rewriting macros on|off
   --multimethods:on|off     turn multi-methods on|off
   --memTracker:on|off       turn memory tracker on|off
   --hotCodeReloading:on|off
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index e48df38f5..85eb597b2 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -78,7 +78,7 @@ type
     nnkEnumTy,
     nnkEnumFieldDef,
     nnkArglist, nnkPattern
-    nnkReturnToken,
+    nnkHiddenTryStmt,
     nnkClosure,
     nnkGotoState,
     nnkState,
diff --git a/lib/core/runtime_v2.nim b/lib/core/runtime_v2.nim
index 305a99fd3..6463a5a27 100644
--- a/lib/core/runtime_v2.nim
+++ b/lib/core/runtime_v2.nim
@@ -1,5 +1,5 @@
 #[
-In this new runtime we simply the object layouts a bit: The runtime type
+In this new runtime we simplify the object layouts a bit: The runtime type
 information is only accessed for the objects that have it and it's always
 at offset 0 then. The ``ref`` object header is independent from the
 runtime type and only contains a reference count.
@@ -48,9 +48,18 @@ template `-!`(p: pointer, s: int): pointer =
 template head(p: pointer): ptr RefHeader =
   cast[ptr RefHeader](cast[int](p) -% sizeof(RefHeader))
 
+var allocs*: int
+
 proc nimNewObj(size: int): pointer {.compilerRtl.} =
-  result = alloc0(size + sizeof(RefHeader)) +! sizeof(RefHeader)
-  # XXX Respect   defined(useMalloc)  here!
+  let s = size + sizeof(RefHeader)
+  when defined(nimscript):
+    discard
+  elif defined(useMalloc):
+    result = c_malloc(s) +! sizeof(RefHeader)
+    nimZeroMem(result, s)
+  else:
+    result = alloc0(s) +! sizeof(RefHeader)
+  inc allocs
 
 proc nimDecWeakRef(p: pointer) {.compilerRtl.} =
   dec head(p).rc
@@ -59,10 +68,19 @@ proc nimIncWeakRef(p: pointer) {.compilerRtl.} =
   inc head(p).rc
 
 proc nimRawDispose(p: pointer) {.compilerRtl.} =
-  if head(p).rc != 0:
-    cstderr.rawWrite "[FATAL] dangling references exist\n"
-    quit 1
-  dealloc(p -! sizeof(RefHeader))
+  when not defined(nimscript):
+    if head(p).rc != 0:
+      cstderr.rawWrite "[FATAL] dangling references exist\n"
+      quit 1
+    when defined(useMalloc):
+      c_free(p -! sizeof(RefHeader))
+    else:
+      dealloc(p -! sizeof(RefHeader))
+    if allocs > 0:
+      dec allocs
+    else:
+      cstderr.rawWrite "[FATAL] unpaired dealloc\n"
+      quit 1
 
 proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
   let d = cast[ptr PNimType](p)[].destructor
diff --git a/lib/system.nim b/lib/system.nim
index 016675d55..88521a026 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2950,7 +2950,7 @@ when not defined(js) and not defined(nimscript):
 when not declared(sysFatal):
   include "system/fatal"
 
-when defined(nimV2) and not defined(nimscript):
+when defined(nimV2):
   include core/runtime_v2
 
 import system/assertions
diff --git a/tests/destructor/tinvalid_rebind.nim b/tests/destructor/tinvalid_rebind.nim
new file mode 100644
index 000000000..0f15c8f9e
--- /dev/null
+++ b/tests/destructor/tinvalid_rebind.nim
@@ -0,0 +1,15 @@
+discard """
+joinable: false
+cmd: "nim check $file"
+errormsg: "cannot bind another '=destroy' to: Foo; previous declaration was constructed here implicitly: tinvalid_rebind.nim(12, 7)"
+line: 14
+"""
+
+type
+  Foo[T] = object
+
+proc main =
+  var f: Foo[int]
+
+proc `=destroy`[T](f: var Foo[T]) =
+  discard
diff --git a/tests/destructor/topttree.nim b/tests/destructor/topttree.nim
index 90ac6fa27..fa5495689 100644
--- a/tests/destructor/topttree.nim
+++ b/tests/destructor/topttree.nim
@@ -22,6 +22,7 @@ var
 
 proc `=destroy`*[T](x: var opt[T]) =
   if x.data != nil:
+    mixin `=destroy`
     when not supportsCopyMem(T):
       `=destroy`(x.data[])
     dealloc(x.data)