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 # T and I here can bind to both typedesc and static types
# before this is determined, we'll consider them to be a # before this is determined, we'll consider them to be a
# wildcard type. # wildcard type.
tfGuarded # guarded pointer tfHasAsgn # type has overloaded assignment operator
tfBorrowDot # distinct type borrows '.' tfBorrowDot # distinct type borrows '.'
TTypeFlags* = set[TTypeFlag] TTypeFlags* = set[TTypeFlag]
@ -805,6 +805,7 @@ type
# mean that there is no destructor. # mean that there is no destructor.
# see instantiateDestructor in semdestruct.nim # see instantiateDestructor in semdestruct.nim
deepCopy*: PSym # overriden 'deepCopy' operation deepCopy*: PSym # overriden 'deepCopy' operation
assignment*: PSym # overriden '=' operator
size*: BiggestInt # the size of the type in bytes size*: BiggestInt # the size of the type in bytes
# -1 means that the size is unkwown # -1 means that the size is unkwown
align*: int16 # the type's alignment requirements align*: int16 # the type's alignment requirements
@ -1219,6 +1220,7 @@ proc assignType*(dest, src: PType) =
dest.align = src.align dest.align = src.align
dest.destructor = src.destructor dest.destructor = src.destructor
dest.deepCopy = src.deepCopy dest.deepCopy = src.deepCopy
dest.assignment = src.assignment
dest.lockLevel = src.lockLevel dest.lockLevel = src.lockLevel
# this fixes 'type TLock = TSysLock': # this fixes 'type TLock = TSysLock':
if src.sym != nil: if src.sym != nil:
@ -1335,6 +1337,13 @@ proc propagateToOwner*(owner, elem: PType) =
if elem.isMetaType: if elem.isMetaType:
owner.flags.incl tfHasMeta 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, if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
tyGenericInvocation}: tyGenericInvocation}:
let elemB = elem.skipTypes({tyGenericInst}) let elemB = elem.skipTypes({tyGenericInst})

View file

@ -9,92 +9,92 @@
# This include file implements lambda lifting for the transformator. # This include file implements lambda lifting for the transformator.
import import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, magicsys, rodread, lowerings idents, renderer, types, magicsys, rodread, lowerings
discard """ discard """
The basic approach is that captured vars need to be put on the heap and 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: that the calling chain needs to be explicitly modelled. Things to consider:
proc a = proc a =
var v = 0 var v = 0
proc b = proc b =
var w = 2 var w = 2
for x in 0..3: for x in 0..3:
proc c = capture v, w, x proc c = capture v, w, x
c() c()
b() b()
for x in 0..4: for x in 0..4:
proc d = capture x proc d = capture x
d() d()
Needs to be translated into: Needs to be translated into:
proc a = proc a =
var cl: * var cl: *
new cl new cl
cl.v = 0 cl.v = 0
proc b(cl) = proc b(cl) =
var bcl: * var bcl: *
new bcl new bcl
bcl.w = 2 bcl.w = 2
bcl.up = cl bcl.up = cl
for x in 0..3: for x in 0..3:
var bcl2: * var bcl2: *
new bcl2 new bcl2
bcl2.up = bcl bcl2.up = bcl
bcl2.up2 = cl bcl2.up2 = cl
bcl2.x = x bcl2.x = x
proc c(cl) = capture cl.up2.v, cl.up.w, cl.x proc c(cl) = capture cl.up2.v, cl.up.w, cl.x
c(bcl2) c(bcl2)
c(bcl) c(bcl)
b(cl) b(cl)
for x in 0..4: for x in 0..4:
var acl2: * var acl2: *
new acl2 new acl2
acl2.x = x acl2.x = x
proc d(cl) = capture cl.x proc d(cl) = capture cl.x
d(acl2) d(acl2)
Closures as interfaces: Closures as interfaces:
proc outer: T = proc outer: T =
var captureMe: TObject # value type required for efficiency var captureMe: TObject # value type required for efficiency
proc getter(): int = result = captureMe.x proc getter(): int = result = captureMe.x
proc setter(x: int) = captureMe.x = x proc setter(x: int) = captureMe.x = x
result = (getter, setter) result = (getter, setter)
Is translated to: Is translated to:
proc outer: T = proc outer: T =
var cl: * var cl: *
new cl new cl
proc getter(cl): int = result = cl.captureMe.x proc getter(cl): int = result = cl.captureMe.x
proc setter(cl: *, x: int) = cl.captureMe.x = x proc setter(cl: *, x: int) = cl.captureMe.x = x
result = ((cl, getter), (cl, setter)) result = ((cl, getter), (cl, setter))
For 'byref' capture, the outer proc needs to access the captured var through 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 the indirection too. For 'bycopy' capture, the outer proc accesses the var
not through the indirection. not through the indirection.
Possible optimizations: Possible optimizations:
1) If the closure contains a single 'ref' and this 1) If the closure contains a single 'ref' and this
reference is not re-assigned (check ``sfAddrTaken`` flag) make this the 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. interfaces.
2) If the closure does not escape, put it onto the stack, not on the heap. 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. 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, closureParam, state, resultSym: PSym # most are only valid if
# fn.kind == skClosureIterator # fn.kind == skClosureIterator
obj: PType obj: PType
PEnv = ref TEnv PEnv = ref TEnv
TEnv {.final.} = object of RootObj TEnv {.final.} = object of RootObj
attachedNode, replacementNode: PNode attachedNode, replacementNode: PNode
@ -141,7 +141,7 @@ type
# if up.fn != fn then we cross function boundaries. # if up.fn != fn then we cross function boundaries.
# This is an important case to consider. # This is an important case to consider.
vars: IntSet # variables belonging to this environment vars: IntSet # variables belonging to this environment
TOuterContext = object TOuterContext = object
fn: PSym # may also be a module! fn: PSym # may also be a module!
head: PEnv head: PEnv
@ -284,7 +284,7 @@ proc addClosureParam(fn: PSym; e: PEnv) =
#assert e.obj.kind == tyObject #assert e.obj.kind == tyObject
proc illegalCapture(s: PSym): bool {.inline.} = proc illegalCapture(s: PSym): bool {.inline.} =
result = skipTypes(s.typ, abstractInst).kind in result = skipTypes(s.typ, abstractInst).kind in
{tyVar, tyOpenArray, tyVarargs} or {tyVar, tyOpenArray, tyVarargs} or
s.kind == skResult s.kind == skResult
@ -344,7 +344,7 @@ proc createUpField(obj, fieldType: PType): PSym =
#rawAddField(obj, result) #rawAddField(obj, result)
addField(obj, result) addField(obj, result)
proc captureVar(o: POuterContext; top: PEnv; local: PSym; proc captureVar(o: POuterContext; top: PEnv; local: PSym;
info: TLineInfo): bool = info: TLineInfo): bool =
# first check if we should be concerned at all: # first check if we should be concerned at all:
var it = top var it = top
@ -408,7 +408,7 @@ proc gatherVars(o: POuterContext; e: PEnv; n: PNode): int =
var s = n.sym var s = n.sym
if interestingVar(s) and e.fn != s.owner: if interestingVar(s) and e.fn != s.owner:
if captureVar(o, e, s, n.info): result = 1 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: nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, nkTypeSection:
discard discard
else: else:
@ -418,7 +418,7 @@ proc gatherVars(o: POuterContext; e: PEnv; n: PNode): int =
proc generateThunk(prc: PNode, dest: PType): PNode = proc generateThunk(prc: PNode, dest: PType): PNode =
## Converts 'prc' into '(thunk, nil)' so that it's compatible with ## Converts 'prc' into '(thunk, nil)' so that it's compatible with
## a closure. ## a closure.
# we cannot generate a proper thunk here for GC-safety reasons (see internal # we cannot generate a proper thunk here for GC-safety reasons (see internal
# documentation): # documentation):
if gCmd == cmdCompileToJS: return prc if gCmd == cmdCompileToJS: return prc
@ -515,7 +515,7 @@ proc closureCreationPoint(n: PNode): PNode =
proc addParamsToEnv(fn: PSym; env: PEnv) = proc addParamsToEnv(fn: PSym; env: PEnv) =
let params = fn.typ.n let params = fn.typ.n
for i in 1.. <params.len: for i in 1.. <params.len:
if params.sons[i].kind != nkSym: if params.sons[i].kind != nkSym:
internalError(params.info, "liftLambdas: strange params") internalError(params.info, "liftLambdas: strange params")
let param = params.sons[i].sym let param = params.sons[i].sym
@ -541,7 +541,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
addParamsToEnv(fn, envB) addParamsToEnv(fn, envB)
searchForInnerProcs(o, body, envB) searchForInnerProcs(o, body, envB)
fn.ast.sons[bodyPos] = ex fn.ast.sons[bodyPos] = ex
let capturedCounter = gatherVars(o, envB, body) let capturedCounter = gatherVars(o, envB, body)
# dummy closure param needed? # dummy closure param needed?
if capturedCounter == 0 and fn.typ.callConv == ccClosure: 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: of nkWhileStmt, nkForStmt, nkParForStmt, nkBlockStmt:
# some nodes open a new scope, so they are candidates for the insertion # some nodes open a new scope, so they are candidates for the insertion
# of closure creation; however for simplicity we merge closures between # 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 # yield observable changes in semantics. For Zahary we also
# include ``nkBlock``. We don't do this for closure iterators because # include ``nkBlock``. We don't do this for closure iterators because
# 'yield' can produce wrong code otherwise (XXX show example): # '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") internalError(it.info, "searchForInnerProcs")
of nkClosure: of nkClosure:
searchForInnerProcs(o, n.sons[0], env) searchForInnerProcs(o, n.sons[0], env)
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
nkTypeSection: nkTypeSection:
# don't recurse here: # don't recurse here:
discard discard
@ -606,7 +606,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
searchForInnerProcs(o, n.sons[i], env) 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 # Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would
# mean to be able to capture string literals which have no GC header. # 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, # 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) result.add(v)
# add 'new' statement: # add 'new' statement:
result.add(newCall(getSysSym"internalNew", env)) result.add(newCall(getSysSym"internalNew", env))
# add assignment statements: # add assignment statements:
for local in scope.capturedVars: for local in scope.capturedVars:
let fieldAccess = indirectAccess(env, local, env.info) let fieldAccess = indirectAccess(env, local, env.info)
@ -696,10 +696,10 @@ proc transformYield(c: POuterContext, n: PNode, it: TIter): PNode =
retStmt.add(a) retStmt.add(a)
else: else:
retStmt.add(emptyNode) retStmt.add(emptyNode)
var stateLabelStmt = newNodeI(nkState, n.info) var stateLabelStmt = newNodeI(nkState, n.info)
stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt))) stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
result = newNodeI(nkStmtList, n.info) result = newNodeI(nkStmtList, n.info)
result.add(stateAsgnStmt) result.add(stateAsgnStmt)
result.add(retStmt) result.add(retStmt)
@ -725,7 +725,7 @@ proc liftIterSym(n: PNode; owner: PSym): PNode =
assert iter.kind == skClosureIterator assert iter.kind == skClosureIterator
result = newNodeIT(nkStmtListExpr, n.info, n.typ) result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let hp = getHiddenParam(iter) let hp = getHiddenParam(iter)
let env = newSym(skLet, iter.name, owner, n.info) let env = newSym(skLet, iter.name, owner, n.info)
env.typ = hp.typ env.typ = hp.typ
@ -800,7 +800,7 @@ proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode =
# with some rather primitive check for now: # with some rather primitive check for now:
if n.kind == nkStmtList and n.len > 0: if n.kind == nkStmtList and n.len > 0:
if n.sons[0].kind == nkGotoState: return nil 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: n[1][0].kind == nkGotoState:
return nil return nil
result = newNodeI(nkStmtList, it.fn.info) 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) var state0 = newNodeI(nkState, it.fn.info)
state0.add(newIntNode(nkIntLit, 0)) state0.add(newIntNode(nkIntLit, 0))
result.add(state0) result.add(state0)
let newBody = transformOuterProc(o, n, it) let newBody = transformOuterProc(o, n, it)
if newBody != nil: if newBody != nil:
result.add(newBody) result.add(newBody)
@ -899,7 +899,7 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
let x = closure.createdVar let x = closure.createdVar
assert x != nil assert x != nil
return makeClosure(local, x, n.info) return makeClosure(local, x, n.info)
if not contains(o.capturedVars, local.id): return if not contains(o.capturedVars, local.id): return
# change 'local' to 'closure.local', unless it's a 'byCopy' variable: # change 'local' to 'closure.local', unless it's a 'byCopy' variable:
# if sfByCopy notin local.flags: # 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 = proc liftLambdas*(fn: PSym, body: PNode): PNode =
# XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ... # 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: fn.skipGenericOwner.kind != skModule:
# ignore forward declaration: # ignore forward declaration:
result = body result = body
@ -985,17 +985,17 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
proc liftForLoop*(body: PNode): PNode = proc liftForLoop*(body: PNode): PNode =
# problem ahead: the iterator could be invoked indirectly, but then # 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 = # iterator count(): int =
# yield 0 # yield 0
# #
# iterator count2(): int = # iterator count2(): int =
# var x = 3 # var x = 3
# yield x # yield x
# inc x # inc x
# yield x # yield x
# #
# proc invoke(iter: iterator(): int) = # proc invoke(iter: iterator(): int) =
# for x in iter(): echo x # for x in iter(): echo x
# #
@ -1004,7 +1004,7 @@ proc liftForLoop*(body: PNode): PNode =
for i in foo(): ... for i in foo(): ...
Is transformed to: Is transformed to:
cl = createClosure() cl = createClosure()
while true: while true:
let i = foo(cl) let i = foo(cl)
@ -1016,7 +1016,7 @@ proc liftForLoop*(body: PNode): PNode =
var call = body[L-2] var call = body[L-2]
result = newNodeI(nkStmtList, body.info) result = newNodeI(nkStmtList, body.info)
# static binding? # static binding?
var env: PSym var env: PSym
if call[0].kind == nkSym and call[0].sym.kind == skClosureIterator: if call[0].kind == nkSym and call[0].sym.kind == skClosureIterator:
@ -1030,18 +1030,18 @@ proc liftForLoop*(body: PNode): PNode =
result.add(v) result.add(v)
# add 'new' statement: # add 'new' statement:
result.add(newCall(getSysSym"internalNew", env.newSymNode)) result.add(newCall(getSysSym"internalNew", env.newSymNode))
var loopBody = newNodeI(nkStmtList, body.info, 3) var loopBody = newNodeI(nkStmtList, body.info, 3)
var whileLoop = newNodeI(nkWhileStmt, body.info, 2) var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool)) whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool))
whileLoop.sons[1] = loopBody whileLoop.sons[1] = loopBody
result.add whileLoop result.add whileLoop
# setup loopBody: # setup loopBody:
# gather vars in a tuple: # gather vars in a tuple:
var v2 = newNodeI(nkLetSection, body.info) var v2 = newNodeI(nkLetSection, body.info)
var vpart = newNodeI(if L == 3: nkIdentDefs else: nkVarTuple, 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 assert body[i].kind == nkSym
body[i].sym.kind = skLet body[i].sym.kind = skLet
addSon(vpart, body[i]) addSon(vpart, body[i])

View file

@ -382,11 +382,11 @@ proc getRoot*(n: PNode): PSym =
if getMagic(n) == mSlice: result = getRoot(n.sons[1]) if getMagic(n) == mSlice: result = getRoot(n.sons[1])
else: discard else: discard
proc newIntLit(value: BiggestInt): PNode = proc newIntLit*(value: BiggestInt): PNode =
result = nkIntLit.newIntNode(value) result = nkIntLit.newIntNode(value)
result.typ = getSysType(tyInt) result.typ = getSysType(tyInt)
proc genHigh(n: PNode): PNode = proc genHigh*(n: PNode): PNode =
if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}: if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}:
result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar))) result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
else: else:

View file

@ -398,7 +398,7 @@ proc myOpen(module: PSym): PPassContext =
c.semInferredLambda = semInferredLambda c.semInferredLambda = semInferredLambda
c.semGenerateInstance = generateInstance c.semGenerateInstance = generateInstance
c.semTypeNode = semTypeNode c.semTypeNode = semTypeNode
c.instDeepCopy = sigmatch.instDeepCopy c.instTypeBoundOp = sigmatch.instTypeBoundOp
pushProcCon(c, module) pushProcCon(c, module)
pushOwner(c.module) pushOwner(c.module)

View file

@ -7,111 +7,84 @@
# distribution, for details about the copyright. # 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 # included from sem.nim
type type
TTypeAttachedOp = enum
attachedDestructor,
attachedAsgn,
attachedDeepCopy
TLiftCtx = object TLiftCtx = object
c: PContext c: PContext
info: TLineInfo # for construction info: TLineInfo # for construction
result: PNode
kind: TTypeAttachedOp kind: TTypeAttachedOp
fn: PSym
asgnForType: PType
recurse: bool
type proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
TFieldInstCtx = object # either 'tup[i]' or 'field' is valid proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym
tupleType: PType # if != nil we're traversing a tuple
tupleIndex: int
field: PSym
replaceByFieldName: bool
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 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: of nkSym:
var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid let f = n.sym
fc.field = typ.sym liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
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)
of nkNilLit: discard of nkNilLit: discard
of nkRecCase: of nkRecCase:
let L = forLoop.len # copy the selector:
let call = forLoop.sons[L-2] liftBodyObj(c, n[0], body, x, y)
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)
# we need to generate a case statement: # we need to generate a case statement:
var caseStmt = newNodeI(nkCaseStmt, c.info) var caseStmt = newNodeI(nkCaseStmt, c.info)
# XXX generate 'if' that checks same branches
# generate selector: # generate selector:
var access = newNodeI(nkDotExpr, forLoop.info, 2) var access = dotField(x, n[0].sym)
access.sons[0] = call.sons[1] caseStmt.add(access)
access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info)
caseStmt.add(semExprWithType(c.c, access))
# copy the branches over, but replace the fields with the for loop body: # copy the branches over, but replace the fields with the for loop body:
for i in 1 .. <typ.len: for i in 1 .. <n.len:
var branch = copyTree(typ[i]) var branch = copyTree(n[i])
let L = branch.len let L = branch.len
branch.sons[L-1] = newNodeI(nkStmtList, forLoop.info) branch.sons[L-1] = newNodeI(nkStmtList, c.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)
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 = newNodeI(nkCall, x.info)
result.add(newSymNode(op)) result.add newSymNode(op)
result.add x result.add genAddr(c, x)
result.add y result.add y
proc newAsgnStmt(le, ri: PNode): PNode = 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 = proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
result = newAsgnStmt(x, newDestructorCall(op, y)) result = newAsgnStmt(x, newDestructorCall(op, y))
proc considerOverloadedOp(c: TLiftCtx; t: PType; x, y: PNode): bool = proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
let op = t.attachedOps[c.kind] case c.kind
if op != nil: of attachedDestructor:
markUsed(c.info, op) let op = t.destructor
styleCheckUse(c.info, op) if op != nil:
case c.kind markUsed(c.info, op)
of attachedDestructor: styleCheckUse(c.info, op)
c.result.add newDestructorCall(op, x) body.add newDestructorCall(op, x)
of attachedAsgn: result = true
c.result.add newAsgnCall(op, x, y) of attachedAsgn:
of attachedDeepCopy: if tfHasAsgn in t.flags:
c.result.add newDeepCopyCall(op, x, y) var op: PSym
result = true 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: if c.kind != attachedDestructor:
c.result.add newAsgnStmt(x, y) body.add newAsgnStmt(x, y)
proc liftBodyAux(c: TLiftCtx; t: PType; x, y: PNode) = proc addVar(father, v, value: PNode) =
const hasAttachedOp: array[TTypeAttachedOp, TTypeIter] = [ var vpart = newNodeI(nkIdentDefs, v.info, 3)
(proc (t: PType, closure: PObject): bool = vpart.sons[0] = v
t.attachedOp[attachedDestructor] != nil), vpart.sons[1] = ast.emptyNode
(proc (t: PType, closure: PObject): bool = vpart.sons[2] = value
t.attachedOp[attachedAsgn] != nil), addSon(father, vpart)
(proc (t: PType, closure: PObject): bool =
t.attachedOp[attachedDeepCopy] != nil)] 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 case t.kind
of tyNone, tyEmpty: discard of tyNone, tyEmpty: discard
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString: of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
defaultOp(c, t, x, y) tyPtr, tyString, tyRef:
of tyPtr, tyString: defaultOp(c, t, body, x, y)
if not considerOverloadedOp(c, t, x, y):
defaultOp(c, t, x, y)
of tyArrayConstr, tyArray, tySequence: of tyArrayConstr, tyArray, tySequence:
if iterOverType(lastSon(t), hasAttachedOp[c.kind], nil): if tfHasAsgn in t.flags:
# generate loop and call the attached Op: 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: else:
defaultOp(c, t, x, y) defaultOp(c, t, body, x, y)
of tyObject: of tyObject, tyDistinct:
liftBodyObj(c, t.n, x, y) 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: of tyTuple:
liftBodyTup(c, t, x, y) liftBodyTup(c, t, body, x, y)
of tyRef:
# we MUST NOT check for acyclic here as a DAG might still share nodes:
of tyProc: of tyProc:
if t.callConv != ccClosure or c.kind != attachedDeepCopy: if t.callConv != ccClosure or c.kind != attachedDeepCopy:
defaultOp(c, t, x, y) defaultOp(c, t, body, x, y)
else: else:
# a big problem is that we don't know the enviroment's type here, so we # 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: # 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.typ = t
call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy)) call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy))
call.sons[1] = y call.sons[1] = y
c.result.add newAsgnStmt(x, call) body.add newAsgnStmt(x, call)
of tyVarargs, tyOpenArray: of tyVarargs, tyOpenArray:
localError(c.info, errGenerated, "cannot copy openArray") localError(c.info, errGenerated, "cannot copy openArray")
of tyFromExpr, tyIter, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, 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, tyMutable, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
tyTypeDesc, tyGenericInvocation, tyBigNum, tyConst, tyForward: tyTypeDesc, tyGenericInvocation, tyBigNum, tyConst, tyForward:
internalError(c.info, "assignment requested for type: " & typeToString(t)) internalError(c.info, "assignment requested for type: " & typeToString(t))
of tyDistinct, tyOrdinal, tyRange, of tyOrdinal, tyRange,
tyGenericInst, tyFieldAccessor, tyStatic, tyVar: 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 var a: TLiftCtx
a.info = info a.info = info
a.result = newNodeI(nkStmtList, info) let body = newNodeI(nkStmtList, info)
liftBodyAux(a, typ) result = newSym(skProc, getIdent":lifted=", typ.owner, info)
result = a.result 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 efAllowDestructor, efWantValue, efOperand, efNoSemCheck
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]
TTypeAttachedOp* = enum
attachedAsgn,
attachedDeepCopy,
attachedDestructor
PContext* = ref TContext PContext* = ref TContext
TContext* = object of TPassContext # a context represents a module TContext* = object of TPassContext # a context represents a module
module*: PSym # the module sym belonging to the context module*: PSym # the module sym belonging to the context
@ -93,8 +98,8 @@ type
lastGenericIdx*: int # used for the generics stack lastGenericIdx*: int # used for the generics stack
hloLoopDetector*: int # used to prevent endless loops in the HLO hloLoopDetector*: int # used to prevent endless loops in the HLO
inParallelStmt*: int inParallelStmt*: int
instDeepCopy*: proc (c: PContext; dc: PSym; t: PType; instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
info: TLineInfo): PSym {.nimcall.} op: TTypeAttachedOp): PSym {.nimcall.}
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = 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 = template resultTypeIsInferrable(typ: PType): expr =
typ.isMetaType and typ.kind != tyTypeDesc typ.isMetaType and typ.kind != tyTypeDesc
include semasgn
proc semAsgn(c: PContext, n: PNode): PNode = proc semAsgn(c: PContext, n: PNode): PNode =
checkSonsLen(n, 2) checkSonsLen(n, 2)
var a = n.sons[0] var a = n.sons[0]
@ -1298,6 +1300,9 @@ proc semAsgn(c: PContext, n: PNode): PNode =
typeMismatch(n, lhs.typ, rhs.typ) typeMismatch(n, lhs.typ, rhs.typ)
n.sons[1] = fitNode(c, le, rhs) 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) fixAbstractType(c, n)
asgnToResultVar(c, n, n.sons[0], n.sons[1]) asgnToResultVar(c, n, n.sons[0], n.sons[1])
result = n result = n

View file

@ -909,11 +909,12 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
proc semOverride(c: PContext, s: PSym, n: PNode) = proc semOverride(c: PContext, s: PSym, n: PNode) =
case s.name.s.normalize case s.name.s.normalize
of "destroy": of "destroy", "=destroy":
doDestructorStuff(c, s, n) doDestructorStuff(c, s, n)
if not experimentalMode(c): if not experimentalMode(c):
localError n.info, "use the {.experimental.} pragma to enable destructors" 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 if s.typ.len == 2 and
s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
sameType(s.typ.sons[1], s.typ.sons[0]): sameType(s.typ.sons[1], s.typ.sons[0]):
@ -935,10 +936,35 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
else: else:
localError(n.info, errGenerated, localError(n.info, errGenerated,
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
of "=": discard incl(s.flags, sfUsed)
else: localError(n.info, errGenerated, of "=":
"'destroy' or 'deepCopy' expected for 'override'") incl(s.flags, sfUsed)
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 type
TProcCompilationSteps = enum TProcCompilationSteps = enum
@ -1055,7 +1081,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
popOwner() popOwner()
pushOwner(s) pushOwner(s)
s.options = gOptions 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: if n.sons[bodyPos].kind != nkEmpty:
# for DLL generation it is annoying to check for sfImportc! # for DLL generation it is annoying to check for sfImportc!
if sfBorrow in s.flags: if sfBorrow in s.flags:

View file

@ -16,9 +16,9 @@ const
proc sharedPtrCheck(info: TLineInfo, t: PType) = proc sharedPtrCheck(info: TLineInfo, t: PType) =
if t.kind == tyPtr and t.len > 1: 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) 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: if tfHasGCedMem in t.flags or t.isGCedMem:
localError(info, errGenerated, localError(info, errGenerated,
"shared memory may not refer to GC'ed thread local memory") "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: if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
# 'deepCopy' needs to be instantiated for # 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*: # 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) = proc eraseVoidParams*(t: PType) =
# transform '(): void' into '()' because old parts of the compiler really # 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 # instantiate generic converters for that
result = res != nil result = res != nil
proc instDeepCopy*(c: PContext; dc: PSym; t: PType; info: TLineInfo): PSym {. proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
procvar.} = op: TTypeAttachedOp): PSym {.procvar.} =
var m: TCandidate var m: TCandidate
initCandidate(c, m, dc.typ) initCandidate(c, m, dc.typ)
var f = dc.typ.sons[1] 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: if typeRel(m, f, t) == isNone:
localError(info, errGenerated, "cannot instantiate 'deepCopy'") localError(info, errGenerated, "cannot instantiate 'deepCopy'")
else: else:

View file

@ -21,12 +21,35 @@ helper distinct or object type has to be used for one pointer type.
operator `=` operator `=`
------------ ------------
This operator is the assignment operator. Note that in the contexts This operator is the assignment operator. Note that in the contexts
like ``let v = expr``, ``var v = expr``, ``parameter = defaultValue`` or for like ``result = expr``, ``parameter = defaultValue`` or for
parameter passing no assignment is performed. The ``override`` pragma is parameter passing no assignment is performed. For a type ``T`` that has an
optional for overriding ``=``. 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 destructors
@ -36,12 +59,12 @@ A destructor must have a single parameter with a concrete type (the name of a
generic type is allowed too). The name of the destructor has to be ``destroy`` generic type is allowed too). The name of the destructor has to be ``destroy``
and it need to be annotated with the ``override`` pragma. and it need to be annotated with the ``override`` pragma.
``destroy(v)`` will be automatically invoked for every local stack ``destroy(v)`` will be automatically invoked for every local stack
variable ``v`` that goes out of scope. variable ``v`` that goes out of scope.
If a structured type features a field with destructable type and If a structured type features a field with destructable type and
the user has not provided an explicit implementation, a destructor for the the user has not provided an explicit implementation, a destructor for the
structured type will be automatically generated. Calls to any base class structured type will be automatically generated. Calls to any base class
destructors in both user-defined and generated destructors will be inserted. destructors in both user-defined and generated destructors will be inserted.
A destructor is attached to the type it destructs; expressions of this type A destructor is attached to the type it destructs; expressions of this type
@ -52,13 +75,13 @@ can then only be used in *destructible contexts* and as parameters:
MyObj = object MyObj = object
x, y: int x, y: int
p: pointer p: pointer
proc destroy(o: var MyObj) {.override.} = proc destroy(o: var MyObj) {.override.} =
if o.p != nil: dealloc o.p if o.p != nil: dealloc o.p
proc open: MyObj = proc open: MyObj =
result = MyObj(x: 1, y: 2, p: alloc(3)) result = MyObj(x: 1, y: 2, p: alloc(3))
proc work(o: MyObj) = proc work(o: MyObj) =
echo o.x echo o.x
# No destructor invoked here for 'o' as 'o' is a parameter. # No destructor invoked here for 'o' as 'o' is a parameter.
@ -68,7 +91,7 @@ can then only be used in *destructible contexts* and as parameters:
var x = open() var x = open()
# valid: pass 'x' to some other proc: # valid: pass 'x' to some other proc:
work(x) work(x)
# Error: usage of a type with a destructor in a non destructible context # Error: usage of a type with a destructor in a non destructible context
echo open() echo open()

View file

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