From 05e39cf6ad431003ed7b8388b559009e88c00e47 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 6 Apr 2015 23:20:41 +0200 Subject: [PATCH] overloading of '=' works --- compiler/semasgn.nim | 1 + compiler/semexprs.nim | 2 - compiler/semstmts.nim | 37 +++++++++++++++- doc/manual/type_bound_ops.txt | 2 +- tests/assign/moverload_asgn2.nim | 10 +++++ tests/assign/toverload_asgn1.nim | 75 ++++++++++++++++++++++++++++++++ tests/assign/toverload_asgn2.nim | 22 ++++++++++ todo.txt | 2 +- web/news.txt | 2 + 9 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 tests/assign/moverload_asgn2.nim create mode 100644 tests/assign/toverload_asgn1.nim create mode 100644 tests/assign/toverload_asgn2.nim diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 6e18c3af7..a1e209263 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -190,6 +190,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = of tyArrayConstr, tyArray, tySequence: 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) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c449fadc2..accf90d8b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1233,8 +1233,6 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = template resultTypeIsInferrable(typ: PType): expr = typ.isMetaType and typ.kind != tyTypeDesc -include semasgn - proc semAsgn(c: PContext, n: PNode): PNode = checkSonsLen(n, 2) var a = n.sons[0] diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index ad5327642..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] diff --git a/doc/manual/type_bound_ops.txt b/doc/manual/type_bound_ops.txt index 213ec5581..2f7cbc79c 100644 --- a/doc/manual/type_bound_ops.txt +++ b/doc/manual/type_bound_ops.txt @@ -22,7 +22,7 @@ operator `=` ------------ This operator is the assignment operator. Note that in the contexts -like ``result = expr``, ``parameter = defaultValue`` or for +``result = expr``, ``parameter = defaultValue`` or for parameter passing no assignment is performed. For a type ``T`` that has an overloaded assignment operator ``var v = T()`` is rewritten to ``var v: T; v = T()``; in other words ``var`` and ``let`` contexts do count diff --git a/tests/assign/moverload_asgn2.nim b/tests/assign/moverload_asgn2.nim new file mode 100644 index 000000000..6620adbeb --- /dev/null +++ b/tests/assign/moverload_asgn2.nim @@ -0,0 +1,10 @@ +type + Concrete* = object + a*, b*: string + rc*: int # refcount + +proc `=`(d: var Concrete; src: Concrete) = + shallowCopy(d.a, src.a) + shallowCopy(d.b, src.b) + dec d.rc + d.rc = src.rc + 1 diff --git a/tests/assign/toverload_asgn1.nim b/tests/assign/toverload_asgn1.nim new file mode 100644 index 000000000..dbc3a71c4 --- /dev/null +++ b/tests/assign/toverload_asgn1.nim @@ -0,0 +1,75 @@ +discard """ + output: '''Concrete '=' +Concrete '=' +Concrete '=' +Concrete '=' +Concrete '=' +GenericT[T] '=' int +GenericT[T] '=' float +GenericT[T] '=' float +GenericT[T] '=' float +GenericT[T] '=' string +GenericT[T] '=' int8 +GenericT[T] '=' bool +GenericT[T] '=' bool +GenericT[T] '=' bool +GenericT[T] '=' bool''' +""" + +import typetraits + +type + Concrete = object + a, b: string + +proc `=`(d: var Concrete; src: Concrete) = + shallowCopy(d.a, src.a) + shallowCopy(d.b, src.b) + echo "Concrete '='" + +var x, y: array[0..2, Concrete] +var cA, cB: Concrete + +var cATup, cBTup: tuple[x: int, ha: Concrete] + +x = y +cA = cB +cATup = cBTup + +type + GenericT[T] = object + a, b: T + +proc `=`[T](d: var GenericT[T]; src: GenericT[T]) = + shallowCopy(d.a, src.a) + shallowCopy(d.b, src.b) + echo "GenericT[T] '=' ", type(T).name + +var ag: GenericT[int] +var bg: GenericT[int] + +ag = bg + +var xg, yg: array[0..2, GenericT[float]] +var cAg, cBg: GenericT[string] + +var cATupg, cBTupg: tuple[x: int, ha: GenericT[int8]] + +xg = yg +cAg = cBg +cATupg = cBTupg + +var caSeqg, cbSeqg: seq[GenericT[bool]] +newSeq(cbSeqg, 4) +caSeqg = cbSeqg + +when false: + type + Foo = object + case b: bool + of false: xx: GenericT[int] + of true: yy: bool + + var + a, b: Foo + a = b diff --git a/tests/assign/toverload_asgn2.nim b/tests/assign/toverload_asgn2.nim new file mode 100644 index 000000000..243c90494 --- /dev/null +++ b/tests/assign/toverload_asgn2.nim @@ -0,0 +1,22 @@ +discard """ + output: '''i value 88 +2aa''' +""" + +import moverload_asgn2 + +proc passAround(i: int): Concrete = + echo "i value ", i + result = Concrete(a: "aa", b: "bb", rc: 0) + +proc main = + let + i = 88 + v = passAround(i) + z = v.a + var + x: Concrete + x = v + echo x.rc, z # 2aa + +main() diff --git a/todo.txt b/todo.txt index 4365daa52..ad36947d6 100644 --- a/todo.txt +++ b/todo.txt @@ -2,13 +2,13 @@ version 0.10.4 ============== - make 'nil' work for 'add' and 'len' -- overloading of '=' - introduce typed/untyped types version 1.0 =========== +- macro support for '='; bind '=' to a memory region - remove echo $foo gotcha - add "all threads are blocked" detection to 'spawn' - figure out why C++ bootstrapping is so much slower diff --git a/web/news.txt b/web/news.txt index 0b28c29bf..54867036a 100644 --- a/web/news.txt +++ b/web/news.txt @@ -113,6 +113,8 @@ News - Array and seq indexing can now use the builtin ``^`` operator to access things from backwards: ``a[^1]`` is like Python's ``a[-1]``. + - A first version of the specification and implementation of the overloading + of the assignment operator has arrived! Library additions