Implements RFCs #209 (#13995)

* add test
* add changelod entry
Co-authored-by: cooldome <ariabushenko@bk.ru>
This commit is contained in:
cooldome 2020-04-16 20:04:05 +01:00 • committed by GitHub
commit 9295251e68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 292 additions and 108 deletions

View file

@ -12,7 +12,34 @@
one in an instantiation context while `-d:nimIntHash1` recovers it globally. one in an instantiation context while `-d:nimIntHash1` recovers it globally.
## Language changes ## Language changes
- In newruntime it is now allowed to assign discriminator field without restrictions as long as case object doesn't have custom destructor. Discriminator value doesn't have to be a constant either. If you have custom destructor for case object and you do want to freely assign discriminator fields, it is recommended to refactor object into 2 objects like this:
```nim
type
MyObj = object
case kind: bool
of true: y: ptr UncheckedArray[float]
of false: z: seq[int]
proc `=destroy`(x: MyObj) =
if x.kind and x.y != nil:
deallocShared(x.y)
x.y = nil
```
Refactor into:
```nim
type
MySubObj = object
val: ptr UncheckedArray[float]
MyObj = object
case kind: bool
of true: y: MySubObj
of false: z: seq[int]
proc `=destroy`(x: MySubObj) =
if x.val != nil:
deallocShared(x.val)
x.val = nil
```
## Compiler changes ## Compiler changes

View file

@ -1048,3 +1048,7 @@ proc listSymbolNames*(symbols: openArray[PSym]): string =
result.add ", " result.add ", "
result.add sym.name.s result.add sym.name.s
proc isDiscriminantField*(n: PNode): bool =
if n.kind == nkCheckedFieldExpr: sfDiscriminant in n[0][1].sym.flags
elif n.kind == nkDotExpr: sfDiscriminant in n[1].sym.flags
else: false

View file

@ -1489,15 +1489,6 @@ proc genPragma(p: BProc, n: PNode) =
p.module.injectStmt = p.s(cpsStmts) p.module.injectStmt = p.s(cpsStmts)
else: discard else: discard
proc fieldDiscriminantCheckNeeded(p: BProc, asgn: PNode): bool =
if optFieldCheck in p.options:
var le = asgn[0]
if le.kind == nkCheckedFieldExpr:
var field = le[0][1].sym
result = sfDiscriminant in field.flags
elif le.kind == nkDotExpr:
var field = le[1].sym
result = sfDiscriminant in field.flags
proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType, proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
field: PSym) = field: PSym) =
@ -1534,24 +1525,20 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
initLocExpr(p, e[0], a) initLocExpr(p, e[0], a)
getTemp(p, a.t, tmp) getTemp(p, a.t, tmp)
expr(p, e[1], tmp) expr(p, e[1], tmp)
let field = dotExpr[1].sym if optTinyRtti notin p.config.globalOptions:
if optTinyRtti in p.config.globalOptions: let field = dotExpr[1].sym
let t = dotExpr[0].typ.skipTypes(abstractInst)
var oldVal, newVal: TLoc
genCaseObjDiscMapping(p, e[0], t, field, oldVal)
genCaseObjDiscMapping(p, e[1], t, field, newVal)
lineCg(p, cpsStmts,
"if ($1 != $2) { #raiseObjectCaseTransition(); $3}$n",
[rdLoc(oldVal), rdLoc(newVal), raiseInstr(p)])
else:
genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field) genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field)
message(p.config, e.info, warnCaseTransition)
genAssignment(p, a, tmp, {}) genAssignment(p, a, tmp, {})
proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
if e[0].kind == nkSym and sfGoto in e[0].sym.flags: if e[0].kind == nkSym and sfGoto in e[0].sym.flags:
genLineDir(p, e) genLineDir(p, e)
genGotoVar(p, e[1]) genGotoVar(p, e[1])
elif not fieldDiscriminantCheckNeeded(p, e): elif optFieldCheck in p.options and isDiscriminantField(e[0]):
genLineDir(p, e)
asgnFieldDiscriminant(p, e)
else:
let le = e[0] let le = e[0]
let ri = e[1] let ri = e[1]
var a: TLoc var a: TLoc
@ -1565,10 +1552,6 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
assert(a.t != nil) assert(a.t != nil)
genLineDir(p, ri) genLineDir(p, ri)
loadInto(p, le, ri, a) loadInto(p, le, ri, a)
else:
genLineDir(p, e)
asgnFieldDiscriminant(p, e)
message(p.config, e.info, warnCaseTransition)
proc genStmts(p: BProc, t: PNode) = proc genStmts(p: BProc, t: PNode) =
var a: TLoc var a: TLoc

View file

@ -22,9 +22,9 @@
# - eliminate 'wasMoved(x); destroy(x)' pairs as a post processing step. # - eliminate 'wasMoved(x); destroy(x)' pairs as a post processing step.
import import
intsets, ast, msgs, renderer, magicsys, types, idents, intsets, ast, astalgo, msgs, renderer, magicsys, types, idents,
strutils, options, dfa, lowerings, tables, modulegraphs, msgs, strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
lineinfos, parampatterns, sighashes lineinfos, parampatterns, sighashes, liftdestructors
from trees import exprStructuralEquivalent from trees import exprStructuralEquivalent
from algorithm import reverse from algorithm import reverse
@ -49,6 +49,12 @@ type
uninit: IntSet # set of uninit'ed vars uninit: IntSet # set of uninit'ed vars
uninitComputed: bool uninitComputed: bool
ProcessMode = enum
normal
consumed
sinkArg
const toDebug {.strdefine.} = "" const toDebug {.strdefine.} = ""
template dbg(body) = template dbg(body) =
@ -56,6 +62,9 @@ template dbg(body) =
if c.owner.name.s == toDebug or toDebug == "always": if c.owner.name.s == toDebug or toDebug == "always":
body body
proc p(n: PNode; c: var Con; mode: ProcessMode): PNode
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode
proc isLastRead(location: PNode; c: var Con; pc, comesFrom: int): int = proc isLastRead(location: PNode; c: var Con; pc, comesFrom: int): int =
var pc = pc var pc = pc
while pc < c.g.len: while pc < c.g.len:
@ -220,16 +229,19 @@ proc makePtrType(c: Con, baseType: PType): PType =
result = newType(tyPtr, c.owner) result = newType(tyPtr, c.owner)
addSonSkipIntLit(result, baseType) addSonSkipIntLit(result, baseType)
proc genOp(c: Con; op: PSym; dest: PNode): PNode =
let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
addrExp.add(dest)
result = newTree(nkCall, newSymNode(op), addrExp)
proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
var op = t.attachedOps[kind] var op = t.attachedOps[kind]
if op == nil or op.ast[genericParamsPos].kind != nkEmpty: if op == nil or op.ast[genericParamsPos].kind != nkEmpty:
# give up and find the canonical type instead: # give up and find the canonical type instead:
let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct}) let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
let canon = c.graph.canonTypes.getOrDefault(h) let canon = c.graph.canonTypes.getOrDefault(h)
if canon != nil: if canon != nil:
op = canon.attachedOps[kind] op = canon.attachedOps[kind]
if op == nil: if op == nil:
#echo dest.typ.id #echo dest.typ.id
globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] & globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
@ -241,9 +253,7 @@ proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
if kind == attachedDestructor: if kind == attachedDestructor:
echo "destructor is ", op.id, " ", op.ast echo "destructor is ", op.id, " ", op.ast
if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind]) if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ)) genOp(c, op, dest)
addrExp.add(dest)
result = newTree(nkCall, newSymNode(op), addrExp)
proc genDestroy(c: Con; dest: PNode): PNode = proc genDestroy(c: Con; dest: PNode): PNode =
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
@ -300,6 +310,44 @@ proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
sym.typ = typ sym.typ = typ
result = newSymNode(sym) result = newSymNode(sym)
proc genDiscriminantAsgn(c: var Con; n: PNode): PNode =
# discriminator is ordinal value that doesn't need sink destroy
# but fields within active case branch might need destruction
# tmp to support self assignments
let tmp = getTemp(c, n[1].typ, n.info)
c.addTopVar(tmp)
result = newTree(nkStmtList)
result.add newTree(nkFastAsgn, tmp, p(n[1], c, consumed))
result.add p(n[0], c, normal)
let le = p(n[0], c, normal)
let leDotExpr = if le.kind == nkCheckedFieldExpr: le[0] else: le
let objType = leDotExpr[0].typ
if hasDestructor(objType):
if objType.attachedOps[attachedDestructor] != nil and
sfOverriden in objType.attachedOps[attachedDestructor].flags:
localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for object's with user defined destructor is not supported, object must have default destructor.
It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""")
result.add newTree(nkFastAsgn, le, tmp)
return
# generate: if le != tmp: `=destroy`(le)
let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info)
let cond = newNodeIT(nkInfix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
cond.add newSymNode(getMagicEqSymForType(c.graph, le.typ, n.info))
cond.add le
cond.add tmp
let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
notExpr.add newSymNode(createMagic(c.graph, "not", mNot))
notExpr.add cond
result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, genOp(c, branchDestructor, le)))
result.add newTree(nkFastAsgn, le, tmp)
else:
result.add newTree(nkFastAsgn, le, tmp)
proc genWasMoved(n: PNode; c: var Con): PNode = proc genWasMoved(n: PNode; c: var Con): PNode =
result = newNodeI(nkCall, n.info) result = newNodeI(nkCall, n.info)
result.add(newSymNode(createMagic(c.graph, "wasMoved", mWasMoved))) result.add(newSymNode(createMagic(c.graph, "wasMoved", mWasMoved)))
@ -338,15 +386,6 @@ proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s & localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s &
"` is already consumed at " & toFileLineCol(c. graph.config, s.info)) "` is already consumed at " & toFileLineCol(c. graph.config, s.info))
type
ProcessMode = enum
normal
consumed
sinkArg
proc p(n: PNode; c: var Con; mode: ProcessMode): PNode
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode
proc isClosureEnv(n: PNode): bool = n.kind == nkSym and n.sym.name.s[0] == ':' proc isClosureEnv(n: PNode): bool = n.kind == nkSym and n.sym.name.s[0] == ':'
proc passCopyToSink(n: PNode; c: var Con): PNode = proc passCopyToSink(n: PNode; c: var Con): PNode =
@ -867,6 +906,8 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
cycleCheck(n, c) cycleCheck(n, c)
assert n[1].kind notin {nkAsgn, nkFastAsgn} assert n[1].kind notin {nkAsgn, nkFastAsgn}
result = moveOrCopy(p(n[0], c, mode), n[1], c) result = moveOrCopy(p(n[0], c, mode), n[1], c)
elif isDiscriminantField(n[0]):
result = genDiscriminantAsgn(c, n)
else: else:
result = copyNode(n) result = copyNode(n)
result.add p(n[0], c, mode) result.add p(n[0], c, mode)

View file

@ -26,6 +26,8 @@ type
fn: PSym fn: PSym
asgnForType: PType asgnForType: PType
recurse: bool recurse: bool
filterDiscriminator: PSym # we generating destructor for case branch
addMemReset: bool # add wasMoved() call after destructor call
c: PContext # c can be nil, then we are called from lambdalifting! c: PContext # c can be nil, then we are called from lambdalifting!
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
@ -72,14 +74,62 @@ proc genAddr(g: ModuleGraph; x: PNode): PNode =
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ)) result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ))
result.add x result.add x
proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode =
result = newNodeIT(nkCall, x.info, op.typ[0]) proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
result.add(newSymNode(op)) result = newNodeI(nkCall, i.info)
result.add genAddr(g, x) result.add createMagic(g, name, magic).newSymNode
result.add i
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
result = newNodeI(nkWhileStmt, c.info, 2)
let cmp = genBuiltin(c.g, mLtI, "<", i)
cmp.add genLen(c.g, dest)
cmp.typ = getSysType(c.g, c.info, tyBool)
result[0] = cmp
result[1] = newNodeI(nkStmtList, c.info)
proc genIf(c: var TLiftCtx; cond, action: PNode): PNode =
result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action))
proc genContainerOf(c: TLiftCtx; objType: PType, field, x: PSym): PNode =
# generate: cast[ptr ObjType](cast[int](addr(x)) - offsetOf(objType.field))
let intType = getSysType(c.g, unknownLineInfo, tyInt)
let addrOf = newNodeIT(nkAddr, c.info, makePtrType(x.owner, x.typ))
addrOf.add newDeref(newSymNode(x))
let castExpr1 = newNodeIT(nkCast, c.info, intType)
castExpr1.add newNodeIT(nkType, c.info, intType)
castExpr1.add addrOf
let dotExpr = newNodeIT(nkDotExpr, c.info, x.typ)
dotExpr.add newNodeIT(nkType, c.info, objType)
dotExpr.add newSymNode(field)
let offsetOf = genBuiltin(c.g, mOffsetOf, "offsetof", dotExpr)
offsetOf.typ = intType
let minusExpr = genBuiltin(c.g, mSubI, "-", castExpr1)
minusExpr.typ = intType
minusExpr.add offsetOf
let objPtr = makePtrType(objType.owner, objType)
result = newNodeIT(nkCast, c.info, objPtr)
result.add newNodeIT(nkType, c.info, objPtr)
result.add minusExpr
proc destructorCall(c: TLiftCtx; op: PSym; x: PNode): PNode =
var destroy = newNodeIT(nkCall, x.info, op.typ[0])
destroy.add(newSymNode(op))
destroy.add genAddr(c.g, x)
if c.addMemReset:
result = newTree(nkStmtList, destroy, genBuiltin(c.g, mWasMoved, "wasMoved", x))
else:
result = destroy
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) = proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) =
case n.kind case n.kind
of nkSym: of nkSym:
if c.filterDiscriminator != nil: return
let f = n.sym let f = n.sym
let b = if c.kind == attachedTrace: y else: y.dotField(f) let b = if c.kind == attachedTrace: y else: y.dotField(f)
if (sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and if (sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and
@ -90,6 +140,9 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
fillBody(c, f.typ, body, x.dotField(f), b) fillBody(c, f.typ, body, x.dotField(f), b)
of nkNilLit: discard of nkNilLit: discard
of nkRecCase: of nkRecCase:
let oldfilterDiscriminator = c.filterDiscriminator
if c.filterDiscriminator == n[0].sym:
c.filterDiscriminator = nil # we have found the case part, proceed as normal
# XXX This is only correct for 'attachedSink'! # XXX This is only correct for 'attachedSink'!
var localEnforceDefaultOp = enforceDefaultOp var localEnforceDefaultOp = enforceDefaultOp
if c.kind == attachedSink: if c.kind == attachedSink:
@ -121,6 +174,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
caseStmt.add(branch) caseStmt.add(branch)
if emptyBranches != n.len-1: if emptyBranches != n.len-1:
body.add(caseStmt) body.add(caseStmt)
c.filterDiscriminator = oldfilterDiscriminator
of nkRecList: of nkRecList:
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp) for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp)
else: else:
@ -275,7 +329,7 @@ proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
if op != nil: if op != nil:
#markUsed(c.g.config, c.info, op, c.g.usageSym) #markUsed(c.g.config, c.info, op, c.g.usageSym)
onUse(c.info, op) onUse(c.info, op)
body.add destructorCall(c.g, op, x) body.add destructorCall(c, op, x)
elif useNoGc(c, t): elif useNoGc(c, t):
internalError(c.g.config, c.info, internalError(c.g.config, c.info,
"type-bound operator could not be resolved") "type-bound operator could not be resolved")
@ -293,7 +347,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
#markUsed(c.g.config, c.info, op, c.g.usageSym) #markUsed(c.g.config, c.info, op, c.g.usageSym)
onUse(c.info, op) onUse(c.info, op)
body.add destructorCall(c.g, op, x) body.add destructorCall(c, op, x)
result = true result = true
#result = addDestructorCall(c, t, body, x) #result = addDestructorCall(c, t, body, x)
of attachedAsgn, attachedSink, attachedTrace: of attachedAsgn, attachedSink, attachedTrace:
@ -318,22 +372,6 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
v.addVar(result, lowerings.newIntLit(c.g, body.info, first)) v.addVar(result, lowerings.newIntLit(c.g, body.info, first))
body.add v body.add v
proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
result = newNodeI(nkCall, i.info)
result.add createMagic(g, name, magic).newSymNode
result.add i
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
result = newNodeI(nkWhileStmt, c.info, 2)
let cmp = genBuiltin(c.g, mLtI, "<", i)
cmp.add genLen(c.g, dest)
cmp.typ = getSysType(c.g, c.info, tyBool)
result[0] = cmp
result[1] = newNodeI(nkStmtList, c.info)
proc genIf(c: var TLiftCtx; cond, action: PNode): PNode =
result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action))
proc addIncStmt(c: var TLiftCtx; body, i: PNode) = proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
let incCall = genBuiltin(c.g, mInc, "inc", i) let incCall = genBuiltin(c.g, mInc, "inc", i)
incCall.add lowerings.newIntLit(c.g, c.info, 1) incCall.add lowerings.newIntLit(c.g, c.info, 1)
@ -382,7 +420,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let moveCall = genBuiltin(c.g, mMove, "move", x) let moveCall = genBuiltin(c.g, mMove, "move", x)
moveCall.add y moveCall.add y
doAssert t.destructor != nil doAssert t.destructor != nil
moveCall.add destructorCall(c.g, t.destructor, x) moveCall.add destructorCall(c, t.destructor, x)
body.add moveCall body.add moveCall
of attachedDestructor: of attachedDestructor:
# destroy all elements: # destroy all elements:
@ -413,7 +451,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let moveCall = genBuiltin(c.g, mMove, "move", x) let moveCall = genBuiltin(c.g, mMove, "move", x)
moveCall.add y moveCall.add y
doAssert t.destructor != nil doAssert t.destructor != nil
moveCall.add destructorCall(c.g, t.destructor, x) moveCall.add destructorCall(c, t.destructor, x)
body.add moveCall body.add moveCall
# alternatively we could do this: # alternatively we could do this:
when false: when false:
@ -421,7 +459,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add newHookCall(c.g, t.asink, x, y) body.add newHookCall(c.g, t.asink, x, y)
of attachedDestructor: of attachedDestructor:
doAssert t.destructor != nil doAssert t.destructor != nil
body.add destructorCall(c.g, t.destructor, x) body.add destructorCall(c, t.destructor, x)
of attachedTrace: of attachedTrace:
body.add newHookCall(c.g, t.attachedOps[c.kind], x, y) body.add newHookCall(c.g, t.attachedOps[c.kind], x, y)
of attachedDispose: of attachedDispose:
@ -435,7 +473,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let moveCall = genBuiltin(c.g, mMove, "move", x) let moveCall = genBuiltin(c.g, mMove, "move", x)
moveCall.add y moveCall.add y
doAssert t.destructor != nil doAssert t.destructor != nil
moveCall.add destructorCall(c.g, t.destructor, x) moveCall.add destructorCall(c, t.destructor, x)
body.add moveCall body.add moveCall
of attachedDestructor, attachedDispose: of attachedDestructor, attachedDispose:
body.add genBuiltin(c.g, mDestroy, "destroy", x) body.add genBuiltin(c.g, mDestroy, "destroy", x)
@ -738,33 +776,14 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
typ.attachedOps[kind] = baseType.attachedOps[kind] typ.attachedOps[kind] = baseType.attachedOps[kind]
result = typ.attachedOps[kind] result = typ.attachedOps[kind]
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; proc symPrototype(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym = info: TLineInfo): PSym =
if typ.kind == tyDistinct:
return produceSymDistinctType(g, c, typ, kind, info)
var a: TLiftCtx
a.info = info
a.g = g
a.kind = kind
a.c = c
let body = newNodeI(nkStmtList, info)
let procname = getIdent(g.cache, AttachedOpToStr[kind]) let procname = getIdent(g.cache, AttachedOpToStr[kind])
result = newSym(skProc, procname, typ.owner, info) result = newSym(skProc, procname, typ.owner, info)
a.fn = result
a.asgnForType = typ
let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info) let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info)
let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"), result, info) let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"), result, info)
var d: PNode
#if kind notin {attachedTrace, attachedDispose}:
dest.typ = makeVarType(typ.owner, typ) dest.typ = makeVarType(typ.owner, typ)
d = newDeref(newSymNode(dest))
#else:
# dest.typ = typ
# d = newSymNode(dest)
if kind == attachedTrace: if kind == attachedTrace:
src.typ = getSysType(g, info, tyPointer) src.typ = getSysType(g, info, tyPointer)
else: else:
@ -775,6 +794,30 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
if kind notin {attachedDestructor, attachedDispose}: if kind notin {attachedDestructor, attachedDispose}:
result.typ.addParam src result.typ.addParam src
var n = newNodeI(nkProcDef, info, bodyPos+1)
for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
n[namePos] = newSymNode(result)
n[paramsPos] = result.typ.n
n[bodyPos] = newNodeI(nkStmtList, info)
result.ast = n
incl result.flags, sfFromGeneric
incl result.flags, sfGeneratedOp
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym =
if typ.kind == tyDistinct:
return produceSymDistinctType(g, c, typ, kind, info)
result = symPrototype(g, typ, kind, info)
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType:typ)
a.fn = result
let dest = result.typ.n[1].sym
let d = newDeref(newSymNode(dest))
let src = if kind in {attachedDestructor, attachedDispose}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
else: newSymNode(result.typ.n[2].sym)
# register this operation already: # register this operation already:
typ.attachedOps[kind] = result typ.attachedOps[kind] = result
@ -782,8 +825,8 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
sfOverriden in typ.attachedOps[attachedDestructor].flags: sfOverriden in typ.attachedOps[attachedDestructor].flags:
## compiler can use a combination of `=destroy` and memCopy for sink op ## compiler can use a combination of `=destroy` and memCopy for sink op
dest.flags.incl sfCursor dest.flags.incl sfCursor
body.add newOpCall(typ.attachedOps[attachedDestructor], d[0]) result.ast[bodyPos].add newOpCall(typ.attachedOps[attachedDestructor], d[0])
body.add newAsgnStmt(d, newSymNode(src)) result.ast[bodyPos].add newAsgnStmt(d, src)
else: else:
var tk: TTypeKind var tk: TTypeKind
if g.config.selectedGC in {gcArc, gcOrc, gcHooks}: if g.config.selectedGC in {gcArc, gcOrc, gcHooks}:
@ -792,20 +835,33 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
tk = tyNone # no special casing for strings and seqs tk = tyNone # no special casing for strings and seqs
case tk case tk
of tySequence: of tySequence:
fillSeqOp(a, typ, body, d, newSymNode(src)) fillSeqOp(a, typ, result.ast[bodyPos], d, src)
of tyString: of tyString:
fillStrOp(a, typ, body, d, newSymNode(src)) fillStrOp(a, typ, result.ast[bodyPos], d, src)
else: else:
fillBody(a, typ, body, d, newSymNode(src)) fillBody(a, typ, result.ast[bodyPos], d, src)
proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym, info: TLineInfo): PSym =
assert(typ.kind == tyObject)
result = symPrototype(g, field.typ, attachedDestructor, info)
var a = TLiftCtx(info: info, g: g, kind: attachedDestructor, asgnForType: typ)
a.fn = result
a.asgnForType = typ
a.filterDiscriminator = field
a.addMemReset = true
let discrimantDest = result.typ.n[1].sym
let dst = newSym(skVar, getIdent(g.cache, "dest"), result, info)
dst.typ = makePtrType(typ.owner, typ)
let dstSym = newSymNode(dst)
let d = newDeref(dstSym)
let v = newNodeI(nkVarSection, info)
v.addVar(dstSym, genContainerOf(a, typ, field, discrimantDest))
result.ast[bodyPos].add v
let placeHolder = newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
fillBody(a, typ, result.ast[bodyPos], d, placeHolder)
var n = newNodeI(nkProcDef, info, bodyPos+1)
for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
n[namePos] = newSymNode(result)
n[paramsPos] = result.typ.n
n[bodyPos] = body
result.ast = n
incl result.flags, sfFromGeneric
incl result.flags, sfGeneratedOp
template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
discard "now a nop" discard "now a nop"

View file

@ -177,3 +177,28 @@ proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
strTableGet(g.exposed, getIdent(g.cache, name)) strTableGet(g.exposed, getIdent(g.cache, name))
proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed) proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed)
proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
case t.kind
of tyInt, tyInt8, tyInt16, tyInt32, tyInt64,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64:
result = getSysMagic(g, info, "==", mEqI)
of tyEnum:
result = getSysMagic(g, info, "==", mEqEnum)
of tyBool:
result = getSysMagic(g, info, "==", mEqB)
of tyRef, tyPtr, tyPointer:
result = getSysMagic(g, info, "==", mEqRef)
of tyString:
result = getSysMagic(g, info, "==", mEqStr)
of tyChar:
result = getSysMagic(g, info, "==", mEqCh)
of tySet:
result = getSysMagic(g, info, "==", mEqSet)
of tyProc:
result = getSysMagic(g, info, "==", mEqProc)
else:
globalError(g.config, info,
"can't find magic equals operator for type kind " & $t.kind)

View file

@ -278,10 +278,13 @@ proc addToLib*(lib: PLib, sym: PSym) =
proc newTypeS*(kind: TTypeKind, c: PContext): PType = proc newTypeS*(kind: TTypeKind, c: PContext): PType =
result = newType(kind, getCurrOwner(c)) result = newType(kind, getCurrOwner(c))
proc makePtrType*(c: PContext, baseType: PType): PType = proc makePtrType*(owner: PSym, baseType: PType): PType =
result = newTypeS(tyPtr, c) result = newType(tyPtr, owner)
addSonSkipIntLit(result, baseType) addSonSkipIntLit(result, baseType)
proc makePtrType*(c: PContext, baseType: PType): PType =
makePtrType(getCurrOwner(c), baseType)
proc makeTypeWithModifier*(c: PContext, proc makeTypeWithModifier*(c: PContext,
modifier: TTypeKind, modifier: TTypeKind,
baseType: PType): PType = baseType: PType): PType =

View file

@ -8,6 +8,7 @@ A
B B
begin begin
end end
prevented
myobj destroyed myobj destroyed
''' '''
""" """
@ -144,3 +145,51 @@ when true:
let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'})]) let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'})])
echo "end" echo "end"
testSubObjAssignment() testSubObjAssignment()
#------------------------------------------------
type
MyObject = object
x1: string
case kind1: bool
of false: y1: string
of true:
y2: seq[string]
case kind2: bool
of true: z1: string
of false:
z2: seq[string]
flag: bool
x2: string
proc test_myobject =
var x: MyObject
x.x1 = "x1"
x.x2 = "x2"
x.y1 = "ljhkjhkjh"
x.kind1 = true
x.y2 = @["1", "2"]
x.kind2 = true
x.z1 = "yes"
x.kind2 = false
x.z2 = @["1", "2"]
x.kind2 = true
x.z1 = "yes"
x.kind2 = true # should be no effect
doAssert(x.z1 == "yes")
x.kind2 = false
x.kind1 = x.kind2 # support self assignment with effect
try:
x.kind1 = x.flag # flag is not accesible
except FieldError:
echo "prevented"
doAssert(x.x1 == "x1")
doAssert(x.x2 == "x2")
test_myobject()

View file

@ -1,7 +1,6 @@
discard """ discard """
cmd: "nim c --gc:arc --exceptions:goto --panics:off $file" cmd: "nim c --gc:arc --exceptions:goto --panics:off $file"
output: '''field error prevented output: '''prevented!
prevented!
caught caught
AssertionError AssertionError
900''' 900'''
@ -26,11 +25,8 @@ proc helper = doAssert(false)
proc main(i: int) = proc main(i: int) =
var obj = Obj(kind: kindA, s: "abc") var obj = Obj(kind: kindA, s: "abc")
try: obj.kind = kindB
obj.kind = kindB obj.i = 2
except FieldError:
echo "field error prevented"
try: try:
var objA = ObjA() var objA = ObjA()
bplease(ObjB(objA)) bplease(ObjB(objA))