Implements RFCs #209 (#13995)

* add test
* add changelod entry
Co-authored-by: cooldome <ariabushenko@bk.ru>

(cherry picked from commit 9295251e68)
This commit is contained in:
cooldome
2020-04-16 20:04:05 +01:00
committed by narimiran
parent 9b510643fa
commit 8cb8cb7861
9 changed files with 292 additions and 108 deletions

View File

@@ -79,7 +79,34 @@
hangs if a process had both reads from stdin and writes (eg to stdout).
## 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

View File

@@ -1048,3 +1048,7 @@ proc listSymbolNames*(symbols: openArray[PSym]): string =
result.add ", "
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)
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,
field: PSym) =
@@ -1534,24 +1525,20 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
initLocExpr(p, e[0], a)
getTemp(p, a.t, tmp)
expr(p, e[1], tmp)
let field = dotExpr[1].sym
if optTinyRtti in p.config.globalOptions:
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:
if optTinyRtti notin p.config.globalOptions:
let field = dotExpr[1].sym
genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field)
message(p.config, e.info, warnCaseTransition)
genAssignment(p, a, tmp, {})
proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
if e[0].kind == nkSym and sfGoto in e[0].sym.flags:
genLineDir(p, e)
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 ri = e[1]
var a: TLoc
@@ -1565,10 +1552,6 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
assert(a.t != nil)
genLineDir(p, ri)
loadInto(p, le, ri, a)
else:
genLineDir(p, e)
asgnFieldDiscriminant(p, e)
message(p.config, e.info, warnCaseTransition)
proc genStmts(p: BProc, t: PNode) =
var a: TLoc

View File

@@ -22,9 +22,9 @@
# - eliminate 'wasMoved(x); destroy(x)' pairs as a post processing step.
import
intsets, ast, msgs, renderer, magicsys, types, idents,
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents,
strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
lineinfos, parampatterns, sighashes
lineinfos, parampatterns, sighashes, liftdestructors
from trees import exprStructuralEquivalent
from algorithm import reverse
@@ -49,6 +49,12 @@ type
uninit: IntSet # set of uninit'ed vars
uninitComputed: bool
ProcessMode = enum
normal
consumed
sinkArg
const toDebug {.strdefine.} = ""
template dbg(body) =
@@ -56,6 +62,9 @@ template dbg(body) =
if c.owner.name.s == toDebug or toDebug == "always":
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 =
var pc = pc
while pc < c.g.len:
@@ -220,16 +229,19 @@ proc makePtrType(c: Con, baseType: PType): PType =
result = newType(tyPtr, c.owner)
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 =
var op = t.attachedOps[kind]
if op == nil or op.ast[genericParamsPos].kind != nkEmpty:
# give up and find the canonical type instead:
let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
let canon = c.graph.canonTypes.getOrDefault(h)
if canon != nil:
op = canon.attachedOps[kind]
if op == nil:
#echo dest.typ.id
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:
echo "destructor is ", op.id, " ", op.ast
if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
addrExp.add(dest)
result = newTree(nkCall, newSymNode(op), addrExp)
genOp(c, op, dest)
proc genDestroy(c: Con; dest: PNode): PNode =
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
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 =
result = newNodeI(nkCall, n.info)
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 &
"` 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 passCopyToSink(n: PNode; c: var Con): PNode =
@@ -867,6 +906,8 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
cycleCheck(n, c)
assert n[1].kind notin {nkAsgn, nkFastAsgn}
result = moveOrCopy(p(n[0], c, mode), n[1], c)
elif isDiscriminantField(n[0]):
result = genDiscriminantAsgn(c, n)
else:
result = copyNode(n)
result.add p(n[0], c, mode)

View File

@@ -26,6 +26,8 @@ type
fn: PSym
asgnForType: PType
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!
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.add x
proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode =
result = newNodeIT(nkCall, x.info, op.typ[0])
result.add(newSymNode(op))
result.add genAddr(g, x)
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 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) =
case n.kind
of nkSym:
if c.filterDiscriminator != nil: return
let f = n.sym
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
@@ -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)
of nkNilLit: discard
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'!
var localEnforceDefaultOp = enforceDefaultOp
if c.kind == attachedSink:
@@ -121,6 +174,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
caseStmt.add(branch)
if emptyBranches != n.len-1:
body.add(caseStmt)
c.filterDiscriminator = oldfilterDiscriminator
of nkRecList:
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp)
else:
@@ -275,7 +329,7 @@ proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
if op != nil:
#markUsed(c.g.config, c.info, op, c.g.usageSym)
onUse(c.info, op)
body.add destructorCall(c.g, op, x)
body.add destructorCall(c, op, x)
elif useNoGc(c, t):
internalError(c.g.config, c.info,
"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)
onUse(c.info, op)
body.add destructorCall(c.g, op, x)
body.add destructorCall(c, op, x)
result = true
#result = addDestructorCall(c, t, body, x)
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))
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) =
let incCall = genBuiltin(c.g, mInc, "inc", i)
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)
moveCall.add y
doAssert t.destructor != nil
moveCall.add destructorCall(c.g, t.destructor, x)
moveCall.add destructorCall(c, t.destructor, x)
body.add moveCall
of attachedDestructor:
# 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)
moveCall.add y
doAssert t.destructor != nil
moveCall.add destructorCall(c.g, t.destructor, x)
moveCall.add destructorCall(c, t.destructor, x)
body.add moveCall
# alternatively we could do this:
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)
of attachedDestructor:
doAssert t.destructor != nil
body.add destructorCall(c.g, t.destructor, x)
body.add destructorCall(c, t.destructor, x)
of attachedTrace:
body.add newHookCall(c.g, t.attachedOps[c.kind], x, y)
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)
moveCall.add y
doAssert t.destructor != nil
moveCall.add destructorCall(c.g, t.destructor, x)
moveCall.add destructorCall(c, t.destructor, x)
body.add moveCall
of attachedDestructor, attachedDispose:
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]
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 =
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])
result = newSym(skProc, procname, typ.owner, info)
a.fn = result
a.asgnForType = typ
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)
var d: PNode
#if kind notin {attachedTrace, attachedDispose}:
dest.typ = makeVarType(typ.owner, typ)
d = newDeref(newSymNode(dest))
#else:
# dest.typ = typ
# d = newSymNode(dest)
if kind == attachedTrace:
src.typ = getSysType(g, info, tyPointer)
else:
@@ -775,6 +794,30 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
if kind notin {attachedDestructor, attachedDispose}:
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:
typ.attachedOps[kind] = result
@@ -782,8 +825,8 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
sfOverriden in typ.attachedOps[attachedDestructor].flags:
## compiler can use a combination of `=destroy` and memCopy for sink op
dest.flags.incl sfCursor
body.add newOpCall(typ.attachedOps[attachedDestructor], d[0])
body.add newAsgnStmt(d, newSymNode(src))
result.ast[bodyPos].add newOpCall(typ.attachedOps[attachedDestructor], d[0])
result.ast[bodyPos].add newAsgnStmt(d, src)
else:
var tk: TTypeKind
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
case tk
of tySequence:
fillSeqOp(a, typ, body, d, newSymNode(src))
fillSeqOp(a, typ, result.ast[bodyPos], d, src)
of tyString:
fillStrOp(a, typ, body, d, newSymNode(src))
fillStrOp(a, typ, result.ast[bodyPos], d, src)
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) =
discard "now a nop"

View File

@@ -177,3 +177,28 @@ proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
strTableGet(g.exposed, getIdent(g.cache, name))
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

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

View File

@@ -8,6 +8,7 @@ A
B
begin
end
prevented
myobj destroyed
'''
"""
@@ -144,3 +145,51 @@ when true:
let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'})])
echo "end"
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 """
cmd: "nim c --gc:arc --exceptions:goto --panics:off $file"
output: '''field error prevented
prevented!
output: '''prevented!
caught
AssertionError
900'''
@@ -26,11 +25,8 @@ proc helper = doAssert(false)
proc main(i: int) =
var obj = Obj(kind: kindA, s: "abc")
try:
obj.kind = kindB
except FieldError:
echo "field error prevented"
obj.kind = kindB
obj.i = 2
try:
var objA = ObjA()
bplease(ObjB(objA))