* add test * add changelod entry Co-authored-by: cooldome <ariabushenko@bk.ru>
This commit is contained in:
parent
b6f99409a9
commit
9295251e68
9 changed files with 292 additions and 108 deletions
27
changelog.md
27
changelog.md
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue