summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim11
-rw-r--r--compiler/ccgexprs.nim4
-rw-r--r--compiler/lambdalifting.nim108
-rw-r--r--compiler/lowerings.nim4
-rw-r--r--compiler/sem.nim2
-rw-r--r--compiler/semasgn.nim341
-rw-r--r--compiler/semdata.nim9
-rw-r--r--compiler/semexprs.nim3
-rw-r--r--compiler/semstmts.nim77
-rw-r--r--compiler/semtypinst.nim12
-rw-r--r--compiler/sigmatch.nim9
11 files changed, 373 insertions, 207 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 6e09916fe..77a532898 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -472,7 +472,7 @@ type
                       # T and I here can bind to both typedesc and static types
                       # before this is determined, we'll consider them to be a
                       # wildcard type.
-    tfGuarded         # guarded pointer
+    tfHasAsgn         # type has overloaded assignment operator
     tfBorrowDot       # distinct type borrows '.'
 
   TTypeFlags* = set[TTypeFlag]
@@ -805,6 +805,7 @@ type
                               # mean that there is no destructor.
                               # see instantiateDestructor in semdestruct.nim
     deepCopy*: PSym           # overriden 'deepCopy' operation
+    assignment*: PSym         # overriden '=' operator
     size*: BiggestInt         # the size of the type in bytes
                               # -1 means that the size is unkwown
     align*: int16             # the type's alignment requirements
@@ -1219,6 +1220,7 @@ proc assignType*(dest, src: PType) =
   dest.align = src.align
   dest.destructor = src.destructor
   dest.deepCopy = src.deepCopy
+  dest.assignment = src.assignment
   dest.lockLevel = src.lockLevel
   # this fixes 'type TLock = TSysLock':
   if src.sym != nil:
@@ -1335,6 +1337,13 @@ proc propagateToOwner*(owner, elem: PType) =
   if elem.isMetaType:
     owner.flags.incl tfHasMeta
 
+  if tfHasAsgn in elem.flags:
+    let o2 = elem.skipTypes({tyGenericInst})
+    if o2.kind in {tyTuple, tyObject, tyArray, tyArrayConstr,
+                   tySequence, tySet, tyDistinct}:
+      o2.flags.incl tfHasAsgn
+      owner.flags.incl tfHasAsgn
+
   if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
                        tyGenericInvocation}:
     let elemB = elem.skipTypes({tyGenericInst})
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 0a6249f8a..75c79c0e2 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1672,7 +1672,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
                                                "$# = #subInt64($#, $#);$n"]
     const fun: array [mInc..mDec, string] = ["$# = #addInt($#, $#);$n",
                                              "$# = #subInt($#, $#);$n"]
-    if optOverflowCheck notin p.options:
+    let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyVar, tyRange})
+    if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}:
       binaryStmt(p, e, d, opr[op])
     else:
       var a, b: TLoc
@@ -1681,7 +1682,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       initLocExpr(p, e.sons[1], a)
       initLocExpr(p, e.sons[2], b)
 
-      let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyVar, tyRange})
       let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyVar})
       let res = binaryArithOverflowRaw(p, ranged, a, b,
         if underlying.kind == tyInt64: fun64[op] else: fun[op])
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 123445e1f..c68bc352c 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -9,92 +9,92 @@
 
 # This include file implements lambda lifting for the transformator.
 
-import 
-  intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, 
+import
+  intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
   idents, renderer, types, magicsys, rodread, lowerings
 
 discard """
   The basic approach is that captured vars need to be put on the heap and
   that the calling chain needs to be explicitly modelled. Things to consider:
-  
+
   proc a =
     var v = 0
     proc b =
       var w = 2
-      
+
       for x in 0..3:
         proc c = capture v, w, x
         c()
     b()
-    
+
     for x in 0..4:
       proc d = capture x
       d()
-  
+
   Needs to be translated into:
-    
+
   proc a =
     var cl: *
     new cl
     cl.v = 0
-    
+
     proc b(cl) =
       var bcl: *
       new bcl
       bcl.w = 2
       bcl.up = cl
-      
+
       for x in 0..3:
         var bcl2: *
         new bcl2
         bcl2.up = bcl
         bcl2.up2 = cl
         bcl2.x = x
-      
+
         proc c(cl) = capture cl.up2.v, cl.up.w, cl.x
         c(bcl2)
-      
+
       c(bcl)
-    
+
     b(cl)
-    
+
     for x in 0..4:
       var acl2: *
       new acl2
       acl2.x = x
       proc d(cl) = capture cl.x
       d(acl2)
-    
+
   Closures as interfaces:
-  
+
   proc outer: T =
     var captureMe: TObject # value type required for efficiency
     proc getter(): int = result = captureMe.x
     proc setter(x: int) = captureMe.x = x
-    
+
     result = (getter, setter)
-    
+
   Is translated to:
-  
+
   proc outer: T =
     var cl: *
     new cl
-    
+
     proc getter(cl): int = result = cl.captureMe.x
     proc setter(cl: *, x: int) = cl.captureMe.x = x
-    
+
     result = ((cl, getter), (cl, setter))
-    
-    
+
+
   For 'byref' capture, the outer proc needs to access the captured var through
   the indirection too. For 'bycopy' capture, the outer proc accesses the var
   not through the indirection.
-    
-  Possible optimizations: 
-  
+
+  Possible optimizations:
+
   1) If the closure contains a single 'ref' and this
   reference is not re-assigned (check ``sfAddrTaken`` flag) make this the
-  closure. This is an important optimization if closures are used as 
+  closure. This is an important optimization if closures are used as
   interfaces.
   2) If the closure does not escape, put it onto the stack, not on the heap.
   3) Dataflow analysis would help to eliminate the 'up' indirections.
@@ -126,7 +126,7 @@ type
     fn, closureParam, state, resultSym: PSym # most are only valid if
                                              # fn.kind == skClosureIterator
     obj: PType
-    
+
   PEnv = ref TEnv
   TEnv {.final.} = object of RootObj
     attachedNode, replacementNode: PNode
@@ -141,7 +141,7 @@ type
                             # if up.fn != fn then we cross function boundaries.
                             # This is an important case to consider.
     vars: IntSet           # variables belonging to this environment
-    
+
   TOuterContext = object
     fn: PSym # may also be a module!
     head: PEnv
@@ -284,7 +284,7 @@ proc addClosureParam(fn: PSym; e: PEnv) =
     #assert e.obj.kind == tyObject
 
 proc illegalCapture(s: PSym): bool {.inline.} =
-  result = skipTypes(s.typ, abstractInst).kind in 
+  result = skipTypes(s.typ, abstractInst).kind in
                    {tyVar, tyOpenArray, tyVarargs} or
       s.kind == skResult
 
@@ -344,7 +344,7 @@ proc createUpField(obj, fieldType: PType): PSym =
   #rawAddField(obj, result)
   addField(obj, result)
 
-proc captureVar(o: POuterContext; top: PEnv; local: PSym; 
+proc captureVar(o: POuterContext; top: PEnv; local: PSym;
                 info: TLineInfo): bool =
   # first check if we should be concerned at all:
   var it = top
@@ -408,7 +408,7 @@ proc gatherVars(o: POuterContext; e: PEnv; n: PNode): int =
     var s = n.sym
     if interestingVar(s) and e.fn != s.owner:
       if captureVar(o, e, s, n.info): result = 1
-  of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkClosure, nkProcDef, 
+  of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkClosure, nkProcDef,
      nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, nkTypeSection:
     discard
   else:
@@ -418,7 +418,7 @@ proc gatherVars(o: POuterContext; e: PEnv; n: PNode): int =
 proc generateThunk(prc: PNode, dest: PType): PNode =
   ## Converts 'prc' into '(thunk, nil)' so that it's compatible with
   ## a closure.
-  
+
   # we cannot generate a proper thunk here for GC-safety reasons (see internal
   # documentation):
   if gCmd == cmdCompileToJS: return prc
@@ -515,7 +515,7 @@ proc closureCreationPoint(n: PNode): PNode =
 
 proc addParamsToEnv(fn: PSym; env: PEnv) =
   let params = fn.typ.n
-  for i in 1.. <params.len: 
+  for i in 1.. <params.len:
     if params.sons[i].kind != nkSym:
       internalError(params.info, "liftLambdas: strange params")
     let param = params.sons[i].sym
@@ -541,7 +541,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
       addParamsToEnv(fn, envB)
       searchForInnerProcs(o, body, envB)
       fn.ast.sons[bodyPos] = ex
-      
+
       let capturedCounter = gatherVars(o, envB, body)
       # dummy closure param needed?
       if capturedCounter == 0 and fn.typ.callConv == ccClosure:
@@ -560,7 +560,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
   of nkWhileStmt, nkForStmt, nkParForStmt, nkBlockStmt:
     # some nodes open a new scope, so they are candidates for the insertion
     # of closure creation; however for simplicity we merge closures between
-    # branches, in fact, only loop bodies are of interest here as only they 
+    # branches, in fact, only loop bodies are of interest here as only they
     # yield observable changes in semantics. For Zahary we also
     # include ``nkBlock``. We don't do this for closure iterators because
     # 'yield' can produce wrong code otherwise (XXX show example):
@@ -598,7 +598,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
         internalError(it.info, "searchForInnerProcs")
   of nkClosure:
     searchForInnerProcs(o, n.sons[0], env)
-  of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, 
+  of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
      nkTypeSection:
     # don't recurse here:
     discard
@@ -606,7 +606,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
     for i in countup(0, sonsLen(n) - 1):
       searchForInnerProcs(o, n.sons[i], env)
 
-proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = 
+proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
   # Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would
   # mean to be able to capture string literals which have no GC header.
   # However this can only happen if the capture happens through a parameter,
@@ -624,7 +624,7 @@ proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PNode): PNode =
     result.add(v)
   # add 'new' statement:
   result.add(newCall(getSysSym"internalNew", env))
-  
+
   # add assignment statements:
   for local in scope.capturedVars:
     let fieldAccess = indirectAccess(env, local, env.info)
@@ -696,10 +696,10 @@ proc transformYield(c: POuterContext, n: PNode, it: TIter): PNode =
     retStmt.add(a)
   else:
     retStmt.add(emptyNode)
-  
+
   var stateLabelStmt = newNodeI(nkState, n.info)
   stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
-  
+
   result = newNodeI(nkStmtList, n.info)
   result.add(stateAsgnStmt)
   result.add(retStmt)
@@ -725,7 +725,7 @@ proc liftIterSym(n: PNode; owner: PSym): PNode =
   assert iter.kind == skClosureIterator
 
   result = newNodeIT(nkStmtListExpr, n.info, n.typ)
-  
+
   let hp = getHiddenParam(iter)
   let env = newSym(skLet, iter.name, owner, n.info)
   env.typ = hp.typ
@@ -800,7 +800,7 @@ proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode =
     # with some rather primitive check for now:
     if n.kind == nkStmtList and n.len > 0:
       if n.sons[0].kind == nkGotoState: return nil
-      if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and 
+      if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and
           n[1][0].kind == nkGotoState:
         return nil
     result = newNodeI(nkStmtList, it.fn.info)
@@ -812,7 +812,7 @@ proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode =
     var state0 = newNodeI(nkState, it.fn.info)
     state0.add(newIntNode(nkIntLit, 0))
     result.add(state0)
-    
+
     let newBody = transformOuterProc(o, n, it)
     if newBody != nil:
       result.add(newBody)
@@ -899,7 +899,7 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
         let x = closure.createdVar
         assert x != nil
         return makeClosure(local, x, n.info)
-    
+
     if not contains(o.capturedVars, local.id): return
     # change 'local' to 'closure.local', unless it's a 'byCopy' variable:
     # if sfByCopy notin local.flags:
@@ -946,7 +946,7 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
 proc liftLambdas*(fn: PSym, body: PNode): PNode =
   # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
   # the transformation even when compiling to JS ...
-  if body.kind == nkEmpty or gCmd == cmdCompileToJS or 
+  if body.kind == nkEmpty or gCmd == cmdCompileToJS or
       fn.skipGenericOwner.kind != skModule:
     # ignore forward declaration:
     result = body
@@ -985,17 +985,17 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
 
 proc liftForLoop*(body: PNode): PNode =
   # problem ahead: the iterator could be invoked indirectly, but then
-  # we don't know what environment to create here: 
-  # 
+  # we don't know what environment to create here:
+  #
   # iterator count(): int =
   #   yield 0
-  # 
+  #
   # iterator count2(): int =
   #   var x = 3
   #   yield x
   #   inc x
   #   yield x
-  # 
+  #
   # proc invoke(iter: iterator(): int) =
   #   for x in iter(): echo x
   #
@@ -1004,7 +1004,7 @@ proc liftForLoop*(body: PNode): PNode =
       for i in foo(): ...
 
     Is transformed to:
-      
+
       cl = createClosure()
       while true:
         let i = foo(cl)
@@ -1016,7 +1016,7 @@ proc liftForLoop*(body: PNode): PNode =
   var call = body[L-2]
 
   result = newNodeI(nkStmtList, body.info)
-  
+
   # static binding?
   var env: PSym
   if call[0].kind == nkSym and call[0].sym.kind == skClosureIterator:
@@ -1030,18 +1030,18 @@ proc liftForLoop*(body: PNode): PNode =
     result.add(v)
     # add 'new' statement:
     result.add(newCall(getSysSym"internalNew", env.newSymNode))
-  
+
   var loopBody = newNodeI(nkStmtList, body.info, 3)
   var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
   whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool))
   whileLoop.sons[1] = loopBody
   result.add whileLoop
-  
+
   # setup loopBody:
   # gather vars in a tuple:
   var v2 = newNodeI(nkLetSection, body.info)
   var vpart = newNodeI(if L == 3: nkIdentDefs else: nkVarTuple, body.info)
-  for i in 0 .. L-3: 
+  for i in 0 .. L-3:
     assert body[i].kind == nkSym
     body[i].sym.kind = skLet
     addSon(vpart, body[i])
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index a51ca9ed6..0b4f97ead 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -382,11 +382,11 @@ proc getRoot*(n: PNode): PSym =
     if getMagic(n) == mSlice: result = getRoot(n.sons[1])
   else: discard
 
-proc newIntLit(value: BiggestInt): PNode =
+proc newIntLit*(value: BiggestInt): PNode =
   result = nkIntLit.newIntNode(value)
   result.typ = getSysType(tyInt)
 
-proc genHigh(n: PNode): PNode =
+proc genHigh*(n: PNode): PNode =
   if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}:
     result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
   else:
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 2d2f15fab..7eabaf491 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -398,7 +398,7 @@ proc myOpen(module: PSym): PPassContext =
   c.semInferredLambda = semInferredLambda
   c.semGenerateInstance = generateInstance
   c.semTypeNode = semTypeNode
-  c.instDeepCopy = sigmatch.instDeepCopy
+  c.instTypeBoundOp = sigmatch.instTypeBoundOp
 
   pushProcCon(c, module)
   pushOwner(c.module)
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index 208c4ce1a..a1e209263 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -7,111 +7,84 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements lifting for assignments and ``deepCopy``.
+## This module implements lifting for assignments. Later versions of this code
+## will be able to also lift ``=deepCopy`` and ``=destroy``.
 
 # included from sem.nim
 
 type
-  TTypeAttachedOp = enum
-    attachedDestructor,
-    attachedAsgn,
-    attachedDeepCopy
-
   TLiftCtx = object
     c: PContext
     info: TLineInfo # for construction
-    result: PNode
     kind: TTypeAttachedOp
+    fn: PSym
+    asgnForType: PType
+    recurse: bool
 
-type
-  TFieldInstCtx = object  # either 'tup[i]' or 'field' is valid
-    tupleType: PType      # if != nil we're traversing a tuple
-    tupleIndex: int
-    field: PSym
-    replaceByFieldName: bool
+proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
+proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym
+
+proc at(a, i: PNode, elemType: PType): PNode =
+  result = newNodeI(nkBracketExpr, a.info, 2)
+  result.sons[0] = a
+  result.sons[1] = i
+  result.typ = elemType
+
+proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  for i in 0 .. <t.len:
+    let lit = lowerings.newIntLit(i)
+    liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
+
+proc dotField(x: PNode, f: PSym): PNode =
+  result = newNodeI(nkDotExpr, x.info, 2)
+  result.sons[0] = x
+  result.sons[1] = newSymNode(f, x.info)
+  result.typ = f.typ
 
-proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
+proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
   case n.kind
-  of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n
-  of nkIdent:
-    result = n
-    var L = sonsLen(forLoop)
-    if c.replaceByFieldName:
-      if n.ident.id == forLoop[0].ident.id:
-        let fieldName = if c.tupleType.isNil: c.field.name.s
-                        elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
-                        else: c.tupleType.n.sons[c.tupleIndex].sym.name.s
-        result = newStrNode(nkStrLit, fieldName)
-        return
-    # other fields:
-    for i in ord(c.replaceByFieldName)..L-3:
-      if n.ident.id == forLoop[i].ident.id:
-        var call = forLoop.sons[L-2]
-        var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
-        if c.field.isNil:
-          result = newNodeI(nkBracketExpr, n.info)
-          result.add(tupl)
-          result.add(newIntNode(nkIntLit, c.tupleIndex))
-        else:
-          result = newNodeI(nkDotExpr, n.info)
-          result.add(tupl)
-          result.add(newSymNode(c.field, n.info))
-        break
-  else:
-    if n.kind == nkContinueStmt:
-      localError(n.info, errGenerated,
-                 "'continue' not supported in a 'fields' loop")
-    result = copyNode(n)
-    newSons(result, sonsLen(n))
-    for i in countup(0, sonsLen(n)-1):
-      result.sons[i] = instFieldLoopBody(c, n.sons[i], forLoop)
-
-proc liftBodyObj(c: TLiftCtx; typ, x, y: PNode) =
-  case typ.kind
   of nkSym:
-    var fc: TFieldInstCtx  # either 'tup[i]' or 'field' is valid
-    fc.field = typ.sym
-    fc.replaceByFieldName = c.m == mFieldPairs
-    openScope(c.c)
-    inc c.c.inUnrolledContext
-    let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
-    father.add(semStmt(c.c, body))
-    dec c.c.inUnrolledContext
-    closeScope(c.c)
+    let f = n.sym
+    liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
   of nkNilLit: discard
   of nkRecCase:
-    let L = forLoop.len
-    let call = forLoop.sons[L-2]
-    if call.len > 2:
-      localError(forLoop.info, errGenerated,
-                 "parallel 'fields' iterator does not work for 'case' objects")
-      return
-    # iterate over the selector:
-    asgnForObjectFields(c, typ[0], forLoop, father)
+    # copy the selector:
+    liftBodyObj(c, n[0], body, x, y)
     # we need to generate a case statement:
     var caseStmt = newNodeI(nkCaseStmt, c.info)
+    # XXX generate 'if' that checks same branches
     # generate selector:
-    var access = newNodeI(nkDotExpr, forLoop.info, 2)
-    access.sons[0] = call.sons[1]
-    access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info)
-    caseStmt.add(semExprWithType(c.c, access))
+    var access = dotField(x, n[0].sym)
+    caseStmt.add(access)
     # copy the branches over, but replace the fields with the for loop body:
-    for i in 1 .. <typ.len:
-      var branch = copyTree(typ[i])
+    for i in 1 .. <n.len:
+      var branch = copyTree(n[i])
       let L = branch.len
-      branch.sons[L-1] = newNodeI(nkStmtList, forLoop.info)
-      semForObjectFields(c, typ[i].lastSon, forLoop, branch[L-1])
+      branch.sons[L-1] = newNodeI(nkStmtList, c.info)
+
+      liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
       caseStmt.add(branch)
-    father.add(caseStmt)
+    body.add(caseStmt)
+    localError(c.info, "cannot lift assignment operator to 'case' object")
   of nkRecList:
-    for t in items(typ): liftBodyObj(c, t, x, y)
+    for t in items(n): liftBodyObj(c, t, body, x, y)
   else:
-    illFormedAstLocal(typ)
+    illFormedAstLocal(n)
 
-proc newAsgnCall(op: PSym; x, y: PNode): PNode =
+proc genAddr(c: PContext; x: PNode): PNode =
+  if x.kind == nkHiddenDeref:
+    checkSonsLen(x, 1)
+    result = x.sons[0]
+  else:
+    result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ))
+    addSon(result, x)
+
+proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode =
+  if sfError in op.flags:
+    localError(x.info, errWrongSymbolX, op.name.s)
   result = newNodeI(nkCall, x.info)
-  result.add(newSymNode(op))
-  result.add x
+  result.add newSymNode(op)
+  result.add genAddr(c, x)
   result.add y
 
 proc newAsgnStmt(le, ri: PNode): PNode =
@@ -127,63 +100,124 @@ proc newDestructorCall(op: PSym; x: PNode): PNode =
 proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
   result = newAsgnStmt(x, newDestructorCall(op, y))
 
-proc considerOverloadedOp(c: TLiftCtx; t: PType; x, y: PNode): bool =
-  let op = t.attachedOps[c.kind]
-  if op != nil:
-    markUsed(c.info, op)
-    styleCheckUse(c.info, op)
-    case c.kind
-    of attachedDestructor:
-      c.result.add newDestructorCall(op, x)
-    of attachedAsgn:
-      c.result.add newAsgnCall(op, x, y)
-    of attachedDeepCopy:
-      c.result.add newDeepCopyCall(op, x, y)
-    result = true
-
-proc defaultOp(c: TLiftCtx; t: PType; x, y: PNode) =
+proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
+  case c.kind
+  of attachedDestructor:
+    let op = t.destructor
+    if op != nil:
+      markUsed(c.info, op)
+      styleCheckUse(c.info, op)
+      body.add newDestructorCall(op, x)
+      result = true
+  of attachedAsgn:
+    if tfHasAsgn in t.flags:
+      var op: PSym
+      if sameType(t, c.asgnForType):
+        # generate recursive call:
+        if c.recurse:
+          op = c.fn
+        else:
+          c.recurse = true
+          return false
+      else:
+        op = t.assignment
+        if op == nil:
+          op = liftBody(c.c, t, c.info)
+      markUsed(c.info, op)
+      styleCheckUse(c.info, op)
+      body.add newAsgnCall(c.c, op, x, y)
+      result = true
+  of attachedDeepCopy:
+    let op = t.deepCopy
+    if op != nil:
+      markUsed(c.info, op)
+      styleCheckUse(c.info, op)
+      body.add newDeepCopyCall(op, x, y)
+      result = true
+
+proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   if c.kind != attachedDestructor:
-    c.result.add newAsgnStmt(x, y)
-
-proc liftBodyAux(c: TLiftCtx; t: PType; x, y: PNode) =
-  const hasAttachedOp: array[TTypeAttachedOp, TTypeIter] = [
-    (proc (t: PType, closure: PObject): bool =
-       t.attachedOp[attachedDestructor] != nil),
-    (proc (t: PType, closure: PObject): bool =
-       t.attachedOp[attachedAsgn] != nil),
-    (proc (t: PType, closure: PObject): bool =
-       t.attachedOp[attachedDeepCopy] != nil)]
+    body.add newAsgnStmt(x, y)
+
+proc addVar(father, v, value: PNode) =
+  var vpart = newNodeI(nkIdentDefs, v.info, 3)
+  vpart.sons[0] = v
+  vpart.sons[1] = ast.emptyNode
+  vpart.sons[2] = value
+  addSon(father, vpart)
+
+proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
+  var temp = newSym(skTemp, getIdent(lowerings.genPrefix), c.fn, c.info)
+  temp.typ = getSysType(tyInt)
+  incl(temp.flags, sfFromGeneric)
+
+  var v = newNodeI(nkVarSection, c.info)
+  result = newSymNode(temp)
+  v.addVar(result, lowerings.newIntLit(first))
+  body.add v
+
+proc genBuiltin(magic: TMagic; name: string; i: PNode): PNode =
+  result = newNodeI(nkCall, i.info)
+  result.add createMagic(name, magic).newSymNode
+  result.add i
+
+proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
+  result = newNodeI(nkWhileStmt, c.info, 2)
+  let cmp = genBuiltin(mLeI, "<=", i)
+  cmp.add genHigh(dest)
+  cmp.typ = getSysType(tyBool)
+  result.sons[0] = cmp
+  result.sons[1] = newNodeI(nkStmtList, c.info)
+
+proc addIncStmt(body, i: PNode) =
+  let incCall = genBuiltin(mInc, "inc", i)
+  incCall.add lowerings.newIntLit(1)
+  body.add incCall
+
+proc newSeqCall(c: PContext; x, y: PNode): PNode =
+  # don't call genAddr(c, x) here:
+  result = genBuiltin(mNewSeq, "newSeq", x)
+  let lenCall = genBuiltin(mLengthSeq, "len", y)
+  lenCall.typ = getSysType(tyInt)
+  result.add lenCall
+
+proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case t.kind
   of tyNone, tyEmpty: discard
-  of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString:
-    defaultOp(c, t, x, y)
-  of tyPtr, tyString:
-    if not considerOverloadedOp(c, t, x, y):
-      defaultOp(c, t, x, y)
+  of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
+      tyPtr, tyString, tyRef:
+    defaultOp(c, t, body, x, y)
   of tyArrayConstr, tyArray, tySequence:
-    if iterOverType(lastSon(t), hasAttachedOp[c.kind], nil):
-      # generate loop and call the attached Op:
-
+    if tfHasAsgn in t.flags:
+      if t.kind == tySequence:
+        # XXX add 'nil' handling here
+        body.add newSeqCall(c.c, x, y)
+      let i = declareCounter(c, body, firstOrd(t))
+      let whileLoop = genWhileLoop(c, i, x)
+      let elemType = t.lastSon
+      liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
+                                                  y.at(i, elemType))
+      addIncStmt(whileLoop.sons[1], i)
+      body.add whileLoop
     else:
-      defaultOp(c, t, x, y)
-  of tyObject:
-    liftBodyObj(c, t.n, x, y)
+      defaultOp(c, t, body, x, y)
+  of tyObject, tyDistinct:
+    if not considerOverloadedOp(c, t, body, x, y):
+      if t.sons[0] != nil: liftBodyAux(c, t.sons[0], body, x, y)
+      if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y)
   of tyTuple:
-    liftBodyTup(c, t, x, y)
-  of tyRef:
-    # we MUST NOT check for acyclic here as a DAG might still share nodes:
-
+    liftBodyTup(c, t, body, x, y)
   of tyProc:
     if t.callConv != ccClosure or c.kind != attachedDeepCopy:
-      defaultOp(c, t, x, y)
+      defaultOp(c, t, body, x, y)
     else:
       # a big problem is that we don't know the enviroment's type here, so we
       # have to go through some indirection; we delegate this to the codegen:
-      call = newNodeI(nkCall, n.info, 2)
+      let call = newNodeI(nkCall, c.info, 2)
       call.typ = t
       call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy))
       call.sons[1] = y
-      c.result.add newAsgnStmt(x, call)
+      body.add newAsgnStmt(x, call)
   of tyVarargs, tyOpenArray:
     localError(c.info, errGenerated, "cannot copy openArray")
   of tyFromExpr, tyIter, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
@@ -191,13 +225,60 @@ proc liftBodyAux(c: TLiftCtx; t: PType; x, y: PNode) =
      tyMutable, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
      tyTypeDesc, tyGenericInvocation, tyBigNum, tyConst, tyForward:
     internalError(c.info, "assignment requested for type: " & typeToString(t))
-  of tyDistinct, tyOrdinal, tyRange,
+  of tyOrdinal, tyRange,
      tyGenericInst, tyFieldAccessor, tyStatic, tyVar:
-    liftBodyAux(c, lastSon(t))
+    liftBodyAux(c, lastSon(t), body, x, y)
+
+proc newProcType(info: TLineInfo; owner: PSym): PType =
+  result = newType(tyProc, owner)
+  result.n = newNodeI(nkFormalParams, info)
+  rawAddSon(result, nil) # return type
+  # result.n[0] used to be `nkType`, but now it's `nkEffectList` because
+  # the effects are now stored in there too ... this is a bit hacky, but as
+  # usual we desperately try to save memory:
+  addSon(result.n, newNodeI(nkEffectList, info))
 
-proc liftBody(c: PContext; typ: PType; info: TLineInfo): PNode =
+proc addParam(procType: PType; param: PSym) =
+  param.position = procType.len-1
+  addSon(procType.n, newSymNode(param))
+  rawAddSon(procType, param.typ)
+
+proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
   var a: TLiftCtx
   a.info = info
-  a.result = newNodeI(nkStmtList, info)
-  liftBodyAux(a, typ)
-  result = a.result
+  let body = newNodeI(nkStmtList, info)
+  result = newSym(skProc, getIdent":lifted=", typ.owner, info)
+  a.fn = result
+  a.asgnForType = typ
+
+  let dest = newSym(skParam, getIdent"dest", result, info)
+  let src = newSym(skParam, getIdent"src", result, info)
+  dest.typ = makeVarType(c, typ)
+  src.typ = typ
+
+  result.typ = newProcType(info, typ.owner)
+  result.typ.addParam dest
+  result.typ.addParam src
+
+  liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
+
+  var n = newNodeI(nkProcDef, info, bodyPos+1)
+  for i in 0 .. < n.len: n.sons[i] = emptyNode
+  n.sons[namePos] = newSymNode(result)
+  n.sons[paramsPos] = result.typ.n
+  n.sons[bodyPos] = body
+  result.ast = n
+
+  # register late as recursion is handled differently
+  typ.assignment = result
+  #echo "Produced this ", n
+
+proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
+  let t = typ.skipTypes({tyGenericInst, tyVar})
+  result = t.assignment
+  if result.isNil:
+    result = liftBody(c, t, info)
+
+proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
+  let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
+  result = newAsgnCall(c, a, dest, src)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 9b5d788af..cec39ff29 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -47,6 +47,11 @@ type
     efAllowDestructor, efWantValue, efOperand, efNoSemCheck
   TExprFlags* = set[TExprFlag]
 
+  TTypeAttachedOp* = enum
+    attachedAsgn,
+    attachedDeepCopy,
+    attachedDestructor
+
   PContext* = ref TContext
   TContext* = object of TPassContext # a context represents a module
     module*: PSym              # the module sym belonging to the context
@@ -93,8 +98,8 @@ type
     lastGenericIdx*: int      # used for the generics stack
     hloLoopDetector*: int     # used to prevent endless loops in the HLO
     inParallelStmt*: int
-    instDeepCopy*: proc (c: PContext; dc: PSym; t: PType;
-                         info: TLineInfo): PSym {.nimcall.}
+    instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
+                            op: TTypeAttachedOp): PSym {.nimcall.}
 
 
 proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 28373e3c6..accf90d8b 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1298,6 +1298,9 @@ proc semAsgn(c: PContext, n: PNode): PNode =
           typeMismatch(n, lhs.typ, rhs.typ)
 
     n.sons[1] = fitNode(c, le, rhs)
+    if tfHasAsgn in lhs.typ.flags and not lhsIsResult:
+      return overloadedAsgn(c, lhs, n.sons[1])
+
     fixAbstractType(c, n)
     asgnToResultVar(c, n, n.sons[0], n.sons[1])
   result = n
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index fd15eab53..50bcca9eb 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -340,6 +340,39 @@ proc checkNilable(v: PSym) =
     elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
       message(v.info, warnProveInit, v.name.s)
 
+include semasgn
+
+proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
+  # consider this:
+  #   var
+  #     x = 0
+  #     withOverloadedAssignment = foo()
+  #     y = use(withOverloadedAssignment)
+  # We need to split this into a statement list with multiple 'var' sections
+  # in order for this transformation to be correct.
+  let L = identDefs.len
+  let value = identDefs[L-1]
+  if value.typ != nil and tfHasAsgn in value.typ.flags:
+    # the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()':
+    identDefs.sons[L-1] = emptyNode
+    if result.kind != nkStmtList:
+      let oldResult = result
+      oldResult.add identDefs
+      result = newNodeI(nkStmtList, result.info)
+      result.add oldResult
+    else:
+      let o = copyNode(orig)
+      o.add identDefs
+      result.add o
+    for i in 0 .. L-3:
+      result.add overloadedAsgn(c, identDefs[i], value)
+  elif result.kind == nkStmtList:
+    let o = copyNode(orig)
+    o.add identDefs
+    result.add o
+  else:
+    result.add identDefs
+
 proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
   var b: PNode
   result = copyNode(n)
@@ -396,7 +429,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         newSons(b, length)
         b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
         b.sons[length-1] = def
-        addSon(result, b)
+        addToVarSection(c, result, n, b)
     elif tup.kind == tyTuple and def.kind == nkPar and
         a.kind == nkIdentDefs and a.len > 3:
       message(a.info, warnEachIdentIsTuple)
@@ -429,7 +462,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         addSon(b, newSymNode(v))
         addSon(b, a.sons[length-2])      # keep type desc for doc generator
         addSon(b, copyTree(def))
-        addSon(result, b)
+        addToVarSection(c, result, n, b)
       else:
         if def.kind == nkPar: v.ast = def[j]
         v.typ = tup.sons[j]
@@ -909,11 +942,12 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
 
 proc semOverride(c: PContext, s: PSym, n: PNode) =
   case s.name.s.normalize
-  of "destroy":
+  of "destroy", "=destroy":
     doDestructorStuff(c, s, n)
     if not experimentalMode(c):
       localError n.info, "use the {.experimental.} pragma to enable destructors"
-  of "deepcopy":
+    incl(s.flags, sfUsed)
+  of "deepcopy", "=deepcopy":
     if s.typ.len == 2 and
         s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
         sameType(s.typ.sons[1], s.typ.sons[0]):
@@ -935,10 +969,35 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
     else:
       localError(n.info, errGenerated,
                  "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
-  of "=": discard
-  else: localError(n.info, errGenerated,
-                   "'destroy' or 'deepCopy' expected for 'override'")
-  incl(s.flags, sfUsed)
+    incl(s.flags, sfUsed)
+  of "=":
+    incl(s.flags, sfUsed)
+    let t = s.typ
+    if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar:
+      var obj = t.sons[1].sons[0]
+      while true:
+        incl(obj.flags, tfHasAsgn)
+        if obj.kind == tyGenericBody: obj = obj.lastSon
+        elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
+        else: break
+      var objB = t.sons[2]
+      while true:
+        if objB.kind == tyGenericBody: objB = objB.lastSon
+        elif objB.kind == tyGenericInvocation: objB = objB.sons[0]
+        else: break
+      if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
+        if obj.assignment.isNil:
+          obj.assignment = s
+        else:
+          localError(n.info, errGenerated,
+                     "cannot bind another '=' to: " & typeToString(obj))
+        return
+    localError(n.info, errGenerated,
+               "signature for '=' must be proc[T: object](x: var T; y: T)")
+  else:
+    if sfOverriden in s.flags:
+      localError(n.info, errGenerated,
+                 "'destroy' or 'deepCopy' expected for 'override'")
 
 type
   TProcCompilationSteps = enum
@@ -1055,7 +1114,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     popOwner()
     pushOwner(s)
   s.options = gOptions
-  if sfOverriden in s.flags: semOverride(c, s, n)
+  if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
   if n.sons[bodyPos].kind != nkEmpty:
     # for DLL generation it is annoying to check for sfImportc!
     if sfBorrow in s.flags:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 5779c3c4b..cbd7999c7 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -16,9 +16,9 @@ const
 
 proc sharedPtrCheck(info: TLineInfo, t: PType) =
   if t.kind == tyPtr and t.len > 1:
-    if t.sons[0].sym.magic in {mShared, mGuarded}:
+    if t.sons[0].sym.magic == mShared:
       incl(t.flags, tfShared)
-      if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded)
+      #if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded)
       if tfHasGCedMem in t.flags or t.isGCedMem:
         localError(info, errGenerated,
                    "shared memory may not refer to GC'ed thread local memory")
@@ -307,7 +307,13 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
     # 'deepCopy' needs to be instantiated for
     # generics *when the type is constructed*:
-    newbody.deepCopy = cl.c.instDeepCopy(cl.c, dc, result, cl.info)
+    newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
+                                            attachedDeepCopy)
+  let asgn = newbody.assignment
+  if asgn != nil and sfFromGeneric notin asgn.flags:
+    # '=' needs to be instantiated for generics when the type is constructed:
+    newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
+                                              attachedAsgn)
 
 proc eraseVoidParams*(t: PType) =
   # transform '(): void' into '()' because old parts of the compiler really
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 10130ddb3..805266389 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -1628,12 +1628,15 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool =
   # instantiate generic converters for that
   result = res != nil
 
-proc instDeepCopy*(c: PContext; dc: PSym; t: PType; info: TLineInfo): PSym {.
-                    procvar.} =
+proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
+                      op: TTypeAttachedOp): PSym {.procvar.} =
   var m: TCandidate
   initCandidate(c, m, dc.typ)
   var f = dc.typ.sons[1]
-  if f.kind in {tyRef, tyPtr}: f = f.lastSon
+  if op == attachedDeepCopy:
+    if f.kind in {tyRef, tyPtr}: f = f.lastSon
+  else:
+    if f.kind == tyVar: f = f.lastSon
   if typeRel(m, f, t) == isNone:
     localError(info, errGenerated, "cannot instantiate 'deepCopy'")
   else: