summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/lambdalifting.nim7
-rw-r--r--compiler/liftdestructors.nim201
-rw-r--r--compiler/sempass2.nim16
3 files changed, 120 insertions, 104 deletions
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index e2d1a143f..9ea6d4f02 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -12,7 +12,7 @@
 import
   intsets, strutils, options, ast, astalgo, trees, treetab, msgs,
   idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos,
-  transf
+  transf, liftdestructors
 
 discard """
   The basic approach is that captured vars need to be put on the heap and
@@ -583,6 +583,11 @@ proc rawClosureCreation(owner: PSym;
     #             oldenv, env.info))
     else:
       localError(d.graph.config, env.info, "internal error: cannot create up reference")
+  # we are not in the sem'check phase anymore! so pass 'nil' for the PContext
+  # and hope for the best:
+  when false:
+    if optNimV2 in d.graph.config.globalOptions:
+      createTypeBoundOps(d.graph, nil, env.typ, owner.info)
 
 proc closureCreationForIter(iter: PNode;
                             d: DetectionPass; c: var LiftingPass): PNode =
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 7e4f26e4e..b05e13c0f 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -18,19 +18,19 @@ import modulegraphs, lineinfos, idents, ast, astalgo, renderer, semdata,
 
 type
   TLiftCtx = object
-    graph: ModuleGraph
+    g: ModuleGraph
     info: TLineInfo # for construction
     kind: TTypeAttachedOp
     fn: PSym
     asgnForType: PType
     recurse: bool
-    c: PContext
+    c: PContext # c can be nil, then we are called from lambdalifting!
 
 proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
-proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
+proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
               info: TLineInfo): PSym
 
-proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo)
+proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo)
 
 proc at(a, i: PNode, elemType: PType): PNode =
   result = newNodeI(nkBracketExpr, a.info, 2)
@@ -40,7 +40,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
 
 proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   for i in 0 ..< t.len:
-    let lit = lowerings.newIntLit(c.graph, x.info, i)
+    let lit = lowerings.newIntLit(c.g, x.info, i)
     fillBody(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
 
 proc dotField(x: PNode, f: PSym): PNode =
@@ -87,7 +87,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
   of nkRecList:
     for t in items(n): fillBodyObj(c, t, body, x, y)
   else:
-    illFormedAstLocal(n, c.graph.config)
+    illFormedAstLocal(n, c.g.config)
 
 proc genAddr(g: ModuleGraph; x: PNode): PNode =
   if x.kind == nkHiddenDeref:
@@ -124,20 +124,28 @@ proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
   result = newAsgnStmt(x, newOpCall(op, y))
 
 proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
-  result = optNimV2 in c.graph.config.globalOptions and
+  result = optNimV2 in c.g.config.globalOptions and
     ({tfHasGCedMem, tfHasOwned} * t.flags != {} or t.isGCedMem)
 
+proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
+  if c.c != nil and typeInst != nil:
+    result = c.c.instTypeBoundOp(c.c, op, typeInst, c.info, attachedAsgn, 1)
+  else:
+    localError(c.g.config, c.info,
+      "cannot generate destructor for generic type: " & typeToString(t))
+    result = nil
+
 proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
                         field: var PSym): bool =
-  if optNimV2 in c.graph.config.globalOptions:
+  if optNimV2 in c.g.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)
+      #  markUsed(c.g.config, c.info, op, c.g.usageSym)
       onUse(c.info, op)
-      body.add newAsgnCall(c.graph, op, x, y)
+      body.add newAsgnCall(c.g, op, x, y)
       result = true
   elif tfHasAsgn in t.flags:
     var op: PSym
@@ -151,38 +159,37 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
     else:
       op = field
       if op == nil:
-        op = produceSym(c.c, t, c.kind, c.info)
+        op = produceSym(c.g, 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)
+    #  markUsed(c.g.config, c.info, op, c.g.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)
+      op = instantiateGeneric(c, op, t, t.typeInst)
       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)
+    body.add newAsgnCall(c.g, op, x, y)
     result = true
 
 proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode) =
   var op = t.destructor
   if op == nil and useNoGc(c, t):
-    op = produceSym(c.c, t, attachedDestructor, c.info)
+    op = produceSym(c.g, c.c, t, attachedDestructor, c.info)
     doAssert op != nil
     doAssert op == t.destructor
 
   if op != nil:
-    #markUsed(c.graph.config, c.info, op, c.graph.usageSym)
+    #markUsed(c.g.config, c.info, op, c.g.usageSym)
     onUse(c.info, op)
-    body.add destructorCall(c.graph, op, x)
+    body.add destructorCall(c.g, op, x)
   elif useNoGc(c, t):
-    internalError(c.graph.config, c.info,
+    internalError(c.g.config, c.info,
       "type-bound operator could not be resolved")
 
 proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
@@ -192,14 +199,13 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
     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)
+        op = instantiateGeneric(c, op, t, t.typeInst)
         t.attachedOps[attachedDestructor] = op
 
-      #markUsed(c.graph.config, c.info, op, c.graph.usageSym)
+      #markUsed(c.g.config, c.info, op, c.g.usageSym)
       onUse(c.info, op)
-      body.add destructorCall(c.graph, op, x)
+      body.add destructorCall(c.g, op, x)
       result = true
     #result = addDestructorCall(c, t, body, x)
   of attachedAsgn:
@@ -209,7 +215,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
   of attachedDeepCopy:
     let op = t.attachedOps[attachedDeepCopy]
     if op != nil:
-      #markUsed(c.graph.config, c.info, op, c.graph.usageSym)
+      #markUsed(c.g.config, c.info, op, c.g.usageSym)
       onUse(c.info, op)
       body.add newDeepCopyCall(op, x, y)
       result = true
@@ -226,13 +232,13 @@ proc addVar(father, v, value: PNode) =
   addSon(father, vpart)
 
 proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
-  var temp = newSym(skTemp, getIdent(c.graph.cache, lowerings.genPrefix), c.fn, c.info)
-  temp.typ = getSysType(c.graph, body.info, tyInt)
+  var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.fn, c.info)
+  temp.typ = getSysType(c.g, body.info, tyInt)
   incl(temp.flags, sfFromGeneric)
 
   var v = newNodeI(nkVarSection, c.info)
   result = newSymNode(temp)
-  v.addVar(result, lowerings.newIntLit(c.graph, body.info, first))
+  v.addVar(result, lowerings.newIntLit(c.g, body.info, first))
   body.add v
 
 proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
@@ -242,9 +248,9 @@ proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
 
 proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
   result = newNodeI(nkWhileStmt, c.info, 2)
-  let cmp = genBuiltin(c.graph, mLtI, "<", i)
-  cmp.add genLen(c.graph, dest)
-  cmp.typ = getSysType(c.graph, c.info, tyBool)
+  let cmp = genBuiltin(c.g, mLtI, "<", i)
+  cmp.add genLen(c.g, dest)
+  cmp.typ = getSysType(c.g, c.info, tyBool)
   result.sons[0] = cmp
   result.sons[1] = newNodeI(nkStmtList, c.info)
 
@@ -252,8 +258,8 @@ proc genIf(c: var TLiftCtx; cond, action: PNode): PNode =
   result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action))
 
 proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
-  let incCall = genBuiltin(c.graph, mInc, "inc", i)
-  incCall.add lowerings.newIntLit(c.graph, c.info, 1)
+  let incCall = genBuiltin(c.g, mInc, "inc", i)
+  incCall.add lowerings.newIntLit(c.g, c.info, 1)
   body.add incCall
 
 proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
@@ -270,14 +276,14 @@ proc setLenStrCall(g: ModuleGraph; x, y: PNode): PNode =
   result.add lenCall
 
 proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
-  let lenCall = genBuiltin(c.graph, mLengthSeq, "len", y)
-  lenCall.typ = getSysType(c.graph, x.info, tyInt)
-  var op = getSysMagic(c.graph, x.info, "setLen", mSetLengthSeq)
-  op = c.c.instTypeBoundOp(c.c, op, t, c.info, attachedAsgn, 1)
+  let lenCall = genBuiltin(c.g, mLengthSeq, "len", y)
+  lenCall.typ = getSysType(c.g, x.info, tyInt)
+  var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
+  op = instantiateGeneric(c, op, t, t)
   result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
 
 proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
-  let i = declareCounter(c, body, firstOrd(c.graph.config, t))
+  let i = declareCounter(c, body, firstOrd(c.g.config, t))
   let whileLoop = genWhileLoop(c, i, x)
   let elemType = t.lastSon
   fillBody(c, elemType, whileLoop.sons[1], x.at(i, elemType),
@@ -296,63 +302,63 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     body.add setLenSeqCall(c, t, x, y)
     forallElements(c, t, body, x, y)
   of attachedSink:
-    let moveCall = genBuiltin(c.graph, mMove, "move", x)
+    let moveCall = genBuiltin(c.g, mMove, "move", x)
     moveCall.add y
     doAssert t.destructor != nil
-    moveCall.add destructorCall(c.graph, t.destructor, x)
+    moveCall.add destructorCall(c.g, t.destructor, x)
     body.add moveCall
   of attachedDestructor:
     # destroy all elements:
     forallElements(c, t, body, x, y)
-    body.add genBuiltin(c.graph, mDestroy, "destroy", x)
+    body.add genBuiltin(c.g, mDestroy, "destroy", x)
 
 proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
-  createTypeBoundOps(c.c, t, body.info)
+  createTypeBoundOps(c.g, c.c, t, body.info)
   case c.kind
   of attachedAsgn, attachedDeepCopy:
     doAssert t.assignment != nil
-    body.add newAsgnCall(c.graph, t.assignment, x, y)
+    body.add newAsgnCall(c.g, t.assignment, x, y)
   of attachedSink:
     # we always inline the move for better performance:
-    let moveCall = genBuiltin(c.graph, mMove, "move", x)
+    let moveCall = genBuiltin(c.g, mMove, "move", x)
     moveCall.add y
     doAssert t.destructor != nil
-    moveCall.add destructorCall(c.graph, t.destructor, x)
+    moveCall.add destructorCall(c.g, t.destructor, x)
     body.add moveCall
     # alternatively we could do this:
     when false:
       doAssert t.asink != nil
-      body.add newAsgnCall(c.graph, t.asink, x, y)
+      body.add newAsgnCall(c.g, t.asink, x, y)
   of attachedDestructor:
     doAssert t.destructor != nil
-    body.add destructorCall(c.graph, t.destructor, x)
+    body.add destructorCall(c.g, t.destructor, x)
 
 proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case c.kind
   of attachedAsgn, attachedDeepCopy:
-    body.add callCodegenProc(c.graph, "nimAsgnStrV2", c.info, genAddr(c.graph, x), y)
+    body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c.g, x), y)
   of attachedSink:
-    let moveCall = genBuiltin(c.graph, mMove, "move", x)
+    let moveCall = genBuiltin(c.g, mMove, "move", x)
     moveCall.add y
     doAssert t.destructor != nil
-    moveCall.add destructorCall(c.graph, t.destructor, x)
+    moveCall.add destructorCall(c.g, t.destructor, x)
     body.add moveCall
   of attachedDestructor:
-    body.add genBuiltin(c.graph, mDestroy, "destroy", x)
+    body.add genBuiltin(c.g, mDestroy, "destroy", x)
 
 proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case c.kind
   of attachedSink:
     # we 'nil' y out afterwards so we *need* to take over its reference
     # count value:
-    body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
+    body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
     body.add newAsgnStmt(x, y)
   of attachedAsgn:
-    body.add genIf(c, y, callCodegenProc(c.graph, "nimIncWeakRef", c.info, y))
-    body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
+    body.add genIf(c, y, callCodegenProc(c.g, "nimIncWeakRef", c.info, y))
+    body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
     body.add newAsgnStmt(x, y)
   of attachedDestructor:
-    body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
+    body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
   of attachedDeepCopy: assert(false, "cannot happen")
 
 proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
@@ -360,14 +366,14 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
 
   let elemType = t.lastSon
   #fillBody(c, elemType, actions, genDeref(x), genDeref(y))
-  #var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x)
+  #var disposeCall = genBuiltin(c.g, mDispose, "dispose", x)
 
   if isFinal(elemType):
     addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
-    actions.add callCodegenProc(c.graph, "nimRawDispose", c.info, x)
+    actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x)
   else:
     addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
-    actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, x)
+    actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x)
 
   case c.kind
   of attachedSink, attachedAsgn:
@@ -383,35 +389,35 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     # have to go through some indirection; we delegate this to the codegen:
     let call = newNodeI(nkCall, c.info, 2)
     call.typ = t
-    call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy))
+    call.sons[0] = newSymNode(createMagic(c.g, "deepCopy", mDeepCopy))
     call.sons[1] = y
     body.add newAsgnStmt(x, call)
-  elif optNimV2 in c.graph.config.globalOptions:
-    let xx = genBuiltin(c.graph, mAccessEnv, "accessEnv", x)
-    xx.typ = getSysType(c.graph, c.info, tyPointer)
+  elif optNimV2 in c.g.config.globalOptions:
+    let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
+    xx.typ = getSysType(c.g, c.info, tyPointer)
     case c.kind
     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 genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
       body.add newAsgnStmt(x, y)
     of attachedAsgn:
-      let yy = genBuiltin(c.graph, mAccessEnv, "accessEnv", y)
-      yy.typ = getSysType(c.graph, c.info, tyPointer)
-      body.add genIf(c, yy, callCodegenProc(c.graph, "nimIncWeakRef", c.info, yy))
-      body.add genIf(c, xx, callCodegenProc(c.graph, "nimDecWeakRef", c.info, xx))
+      let yy = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
+      yy.typ = getSysType(c.g, c.info, tyPointer)
+      body.add genIf(c, yy, callCodegenProc(c.g, "nimIncWeakRef", c.info, yy))
+      body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
       body.add newAsgnStmt(x, y)
     of attachedDestructor:
-      body.add genIf(c, xx, callCodegenProc(c.graph, "nimDecWeakRef", c.info, xx))
+      body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
     of attachedDeepCopy: assert(false, "cannot happen")
 
 proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
-  let xx = genBuiltin(c.graph, mAccessEnv, "accessEnv", x)
-  xx.typ = getSysType(c.graph, c.info, tyPointer)
+  let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
+  xx.typ = getSysType(c.g, c.info, tyPointer)
   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)
+  actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xx)
   case c.kind
   of attachedSink, attachedAsgn:
     body.add genIf(c, xx, actions)
@@ -427,7 +433,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       tyPtr, tyOpt, tyUncheckedArray:
     defaultOp(c, t, body, x, y)
   of tyRef:
-    if optNimV2 in c.graph.config.globalOptions:
+    if optNimV2 in c.g.config.globalOptions:
       weakrefOp(c, t, body, x, y)
     else:
       defaultOp(c, t, body, x, y)
@@ -438,7 +444,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       defaultOp(c, t, body, x, y)
   of tyOwned:
     let base = t.skipTypes(abstractInstOwned)
-    if optNimV2 in c.graph.config.globalOptions:
+    if optNimV2 in c.g.config.globalOptions:
       case base.kind
       of tyRef:
         ownedRefOp(c, base, body, x, y)
@@ -457,13 +463,13 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   of tySequence:
     if useNoGc(c, t):
       useSeqOrStrOp(c, t, body, x, y)
-    elif c.graph.config.selectedGC == gcDestructors:
+    elif c.g.config.selectedGC == gcDestructors:
       # note that tfHasAsgn is propagated so we need the check on
       # 'selectedGC' here to determine if we have the new runtime.
       discard considerUserDefinedOp(c, t, body, x, y)
     elif tfHasAsgn in t.flags:
       if c.kind != attachedDestructor:
-        body.add newSeqCall(c.graph, x, y)
+        body.add newSeqCall(c.g, x, y)
       forallElements(c, t, body, x, y)
     else:
       defaultOp(c, t, body, x, y)
@@ -483,12 +489,12 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   of tyTuple:
     fillBodyTup(c, t, body, x, y)
   of tyVarargs, tyOpenArray:
-    localError(c.graph.config, c.info, "cannot copy openArray")
+    localError(c.g.config, c.info, "cannot copy openArray")
   of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
      tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
      tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
      tyTypeDesc, tyGenericInvocation, tyForward:
-    #internalError(c.graph.config, c.info, "assignment requested for type: " & typeToString(t))
+    #internalError(c.g.config, c.info, "assignment requested for type: " & typeToString(t))
     discard
   of tyVar, tyLent:
     if c.kind != attachedDestructor:
@@ -497,25 +503,24 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
      tyGenericInst, tyStatic, tyAlias, tySink:
     fillBody(c, lastSon(t), body, x, y)
 
-proc produceSymDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
+proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
   assert typ.kind == tyDistinct
   let baseType = typ[0]
   if baseType.attachedOps[kind] == nil:
-    discard produceSym(c, baseType, kind, info)
+    discard produceSym(g, c, baseType, kind, info)
   typ.attachedOps[kind] = baseType.attachedOps[kind]
   result = typ.attachedOps[kind]
 
-proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
+proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
               info: TLineInfo): PSym =
   if typ.kind == tyDistinct:
-    return produceSymDistinctType(c, typ, kind, info)
+    return produceSymDistinctType(g, c, typ, kind, info)
 
   var a: TLiftCtx
   a.info = info
-  a.graph = c.graph
+  a.g = g
   a.kind = kind
   a.c = c
-  let g = c.graph
   let body = newNodeI(nkStmtList, info)
   let procname = getIdent(g.cache, AttachedOpToStr[kind])
 
@@ -537,7 +542,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
   typ.attachedOps[kind] = result
 
   var tk: TTypeKind
-  if optNimV2 in c.graph.config.globalOptions:
+  if optNimV2 in g.config.globalOptions:
     tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
   else:
     tk = tyNone # no special casing for strings and seqs
@@ -561,33 +566,39 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
 template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
   discard "now a nop"
 
-proc patchBody(c: PContext; n: PNode; info: TLineInfo) =
+proc patchBody(g: ModuleGraph; 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:
-        discard produceSym(c, t, attachedDestructor, info)
+        discard produceSym(g, c, t, attachedDestructor, info)
 
       if t.destructor != nil:
         if t.destructor.ast[genericParamsPos].kind != nkEmpty:
-          internalError(c.graph.config, info, "resolved destructor is generic")
+          internalError(g.config, info, "resolved destructor is generic")
         if t.destructor.magic == mDestroy:
-          internalError(c.graph.config, info, "patching mDestroy with mDestroy?")
+          internalError(g.config, info, "patching mDestroy with mDestroy?")
         n.sons[0] = newSymNode(t.destructor)
-  for x in n: patchBody(c, x, info)
+  for x in n: patchBody(g, c, x, info)
 
 template inst(field, t) =
   if field.ast != nil and field.ast[genericParamsPos].kind != nkEmpty:
     if t.typeInst != nil:
-      field = c.instTypeBoundOp(c, field, t.typeInst, info, attachedAsgn, 1)
+      var a: TLiftCtx
+      a.info = info
+      a.g = g
+      a.kind = k
+      a.c = c
+
+      field = instantiateGeneric(a, field, t, t.typeInst)
       if field.ast != nil:
-        patchBody(c, field.ast, info)
+        patchBody(g, c, field.ast, info)
     else:
-      localError(c.graph.config, info, "unresolved generic parameter")
+      localError(g.config, info, "unresolved generic parameter")
 
 proc isTrival(s: PSym): bool {.inline.} = s == nil or s.ast[bodyPos].len == 0
 
-proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
+proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo) =
   ## In the semantic pass this is called in strategic places
   ## to ensure we lift assignment, destructors and moves properly.
   ## The later 'injectdestructors' pass depends on it.
@@ -595,11 +606,11 @@ proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
   incl orig.flags, tfCheckedForDestructor
 
   let h = sighashes.hashType(orig, {CoType, CoConsiderOwned, CoDistinct})
-  var canon = c.graph.canonTypes.getOrDefault(h)
+  var canon = g.canonTypes.getOrDefault(h)
   var overwrite = false
   if canon == nil:
     let typ = orig.skipTypes({tyGenericInst, tyAlias})
-    c.graph.canonTypes[h] = typ
+    g.canonTypes[h] = typ
     canon = typ
   elif canon != orig:
     overwrite = true
@@ -614,7 +625,7 @@ proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
   # we generate the destructor first so that other operators can depend on it:
   for k in attachedDestructor..attachedSink:
     if canon.attachedOps[k] == nil:
-      discard produceSym(c, canon, k, info)
+      discard produceSym(g, c, canon, k, info)
     else:
       inst(canon.attachedOps[k], canon)
 
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 32d749bb4..6cde12588 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -709,7 +709,7 @@ proc track(tracked: PEffects, n: PNode) =
       return
     if n.typ != nil:
       if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
-        createTypeBoundOps(tracked.c, n.typ, n.info)
+        createTypeBoundOps(tracked.graph, tracked.c, n.typ, 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
@@ -770,18 +770,18 @@ proc track(tracked: PEffects, n: PNode) =
     notNilCheck(tracked, n.sons[1], n.sons[0].typ)
     when false: cstringCheck(tracked, n)
     if tracked.owner.kind != skMacro:
-      createTypeBoundOps(tracked.c, n[0].typ, n.info)
+      createTypeBoundOps(tracked.graph, 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)
       if tracked.owner.kind != skMacro:
         if child.kind == nkVarTuple:
-          createTypeBoundOps(tracked.c, child[^1].typ, child.info)
+          createTypeBoundOps(tracked.graph, tracked.c, child[^1].typ, child.info)
           for i in 0..child.len-3:
-            createTypeBoundOps(tracked.c, child[i].typ, child.info)
+            createTypeBoundOps(tracked.graph, tracked.c, child[i].typ, child.info)
         else:
-          createTypeBoundOps(tracked.c, child[0].typ, child.info)
+          createTypeBoundOps(tracked.graph, 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)
@@ -827,15 +827,15 @@ proc track(tracked: PEffects, n: PNode) =
       if tracked.owner.kind != skMacro:
         if it.kind == nkVarTuple:
           for x in it:
-            createTypeBoundOps(tracked.c, x.typ, x.info)
+            createTypeBoundOps(tracked.graph, tracked.c, x.typ, x.info)
         else:
-          createTypeBoundOps(tracked.c, it.typ, it.info)
+          createTypeBoundOps(tracked.graph, tracked.c, it.typ, it.info)
     let iterCall = n[n.len-2]
     let loopBody = n[n.len-1]
     if tracked.owner.kind != skMacro and iterCall.safelen > 1:
       # XXX this is a bit hacky:
       if iterCall[1].typ != nil and iterCall[1].typ.skipTypes(abstractVar).kind notin {tyVarargs, tyOpenArray}:
-        createTypeBoundOps(tracked.c, iterCall[1].typ, iterCall[1].info)
+        createTypeBoundOps(tracked.graph, tracked.c, iterCall[1].typ, iterCall[1].info)
     track(tracked, iterCall)
     track(tracked, loopBody)
     setLen(tracked.init, oldState)