first implementation of overloading of '='; missing: rewriting let/var sections

This commit is contained in:
Araq 2015-04-06 20:11:27 +02:00
commit 78f371c03a
12 changed files with 374 additions and 216 deletions

View file

@ -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})

View file

@ -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:

View file

@ -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)

View file

@ -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 instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
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 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])
caseStmt.add(branch)
father.add(caseStmt)
of nkRecList:
for t in items(typ): liftBodyObj(c, t, x, y)
else:
illFormedAstLocal(typ)
branch.sons[L-1] = newNodeI(nkStmtList, c.info)
proc newAsgnCall(op: PSym; x, y: PNode): PNode =
liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
caseStmt.add(branch)
body.add(caseStmt)
localError(c.info, "cannot lift assignment operator to 'case' object")
of nkRecList:
for t in items(n): liftBodyObj(c, t, body, x, y)
else:
illFormedAstLocal(n)
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,123 @@ 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 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: TLiftCtx; t: PType; x, y: PNode) =
proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if c.kind != attachedDestructor:
c.result.add newAsgnStmt(x, y)
body.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)]
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:
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 +224,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 liftBody(c: PContext; typ: PType; info: TLineInfo): PNode =
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 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)

View file

@ -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 =

View file

@ -1233,6 +1233,8 @@ 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]
@ -1298,6 +1300,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

View file

@ -909,11 +909,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 +936,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 +1081,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:

View file

@ -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

View file

@ -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:

View file

@ -22,11 +22,34 @@ operator `=`
------------
This operator is the assignment operator. Note that in the contexts
like ``let v = expr``, ``var v = expr``, ``parameter = defaultValue`` or for
parameter passing no assignment is performed. The ``override`` pragma is
optional for overriding ``=``.
like ``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
as assignments.
The assignment operator needs to be attached to an object or distinct
type ``T``. Its signature has to be ``(var T, T)``. Example:
.. code-block:: nim
type
Concrete = object
a, b: string
proc `=`(d: var Concrete; src: Concrete) =
shallowCopy(d.a, src.a)
shallowCopy(d.b, src.b)
echo "Concrete '=' called"
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
**Note**: Overriding of operator ``=`` is not yet implemented.
destructors

View file

@ -3,6 +3,7 @@ version 0.10.4
- make 'nil' work for 'add' and 'len'
- overloading of '='
- introduce typed/untyped types
version 1.0