Field checks for everybody (#8957)
* Field checks for JS backend * Clean nkCall nodes with no arguments Generating a nkEmpty in place of no arguments makes no sense form the AST point of view and also trips up the VM codegen. * Field checks for VM backend * Test case for #6612 This patchset fixes #6612 * Add test case for LHS double evaluation * Prevent LHS double-eval for JS backend * Prevent double evaluation in VM backend
This commit is contained in:
parent
a3fb0a769c
commit
ee14ace5d3
10 changed files with 223 additions and 43 deletions
|
|
@ -210,7 +210,7 @@ proc newUnrollFinallyAccess(ctx: var Ctx, info: TLineInfo): PNode =
|
||||||
|
|
||||||
proc newCurExcAccess(ctx: var Ctx): PNode =
|
proc newCurExcAccess(ctx: var Ctx): PNode =
|
||||||
if ctx.curExcSym.isNil:
|
if ctx.curExcSym.isNil:
|
||||||
ctx.curExcSym = ctx.newEnvVar(":curExc", ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode).typ)
|
ctx.curExcSym = ctx.newEnvVar(":curExc", ctx.g.callCodegenProc("getCurrentException").typ)
|
||||||
ctx.newEnvVarAccess(ctx.curExcSym)
|
ctx.newEnvVarAccess(ctx.curExcSym)
|
||||||
|
|
||||||
proc newState(ctx: var Ctx, n, gotoOut: PNode): int =
|
proc newState(ctx: var Ctx, n, gotoOut: PNode): int =
|
||||||
|
|
@ -324,7 +324,7 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
|
||||||
assert(c[i].kind == nkType)
|
assert(c[i].kind == nkType)
|
||||||
let nextCond = newTree(nkCall,
|
let nextCond = newTree(nkCall,
|
||||||
newSymNode(g.getSysMagic(c.info, "of", mOf)),
|
newSymNode(g.getSysMagic(c.info, "of", mOf)),
|
||||||
g.callCodegenProc("getCurrentException", ctx.g.emptyNode),
|
g.callCodegenProc("getCurrentException"),
|
||||||
c[i])
|
c[i])
|
||||||
nextCond.typ = ctx.g.getSysType(c.info, tyBool)
|
nextCond.typ = ctx.g.getSysType(c.info, tyBool)
|
||||||
nextCond.info = c.info
|
nextCond.info = c.info
|
||||||
|
|
@ -364,7 +364,7 @@ proc addElseToExcept(ctx: var Ctx, n: PNode) =
|
||||||
block: # :curExc = getCurrentException()
|
block: # :curExc = getCurrentException()
|
||||||
branchBody.add(newTree(nkAsgn,
|
branchBody.add(newTree(nkAsgn,
|
||||||
ctx.newCurExcAccess(),
|
ctx.newCurExcAccess(),
|
||||||
ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode)))
|
ctx.g.callCodegenProc("getCurrentException")))
|
||||||
|
|
||||||
block: # goto nearestFinally
|
block: # goto nearestFinally
|
||||||
branchBody.add(newTree(nkGotoState, ctx.g.newIntLit(n.info, ctx.nearestFinally)))
|
branchBody.add(newTree(nkGotoState, ctx.g.newIntLit(n.info, ctx.nearestFinally)))
|
||||||
|
|
@ -803,7 +803,7 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
|
||||||
let branch = newTree(nkElifBranch, cmp, retStmt)
|
let branch = newTree(nkElifBranch, cmp, retStmt)
|
||||||
|
|
||||||
# The C++ backend requires `getCurrentException` here.
|
# The C++ backend requires `getCurrentException` here.
|
||||||
let raiseStmt = newTree(nkRaiseStmt, ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode))
|
let raiseStmt = newTree(nkRaiseStmt, ctx.g.callCodegenProc("getCurrentException"))
|
||||||
raiseStmt.info = info
|
raiseStmt.info = info
|
||||||
let elseBranch = newTree(nkElse, raiseStmt)
|
let elseBranch = newTree(nkElse, raiseStmt)
|
||||||
|
|
||||||
|
|
@ -1204,7 +1204,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
|
||||||
block:
|
block:
|
||||||
result.add(newTree(nkAsgn,
|
result.add(newTree(nkAsgn,
|
||||||
ctx.newCurExcAccess(),
|
ctx.newCurExcAccess(),
|
||||||
ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode)))
|
ctx.g.callCodegenProc("getCurrentException")))
|
||||||
|
|
||||||
proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} =
|
proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} =
|
||||||
let setupExc = newTree(nkCall,
|
let setupExc = newTree(nkCall,
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,7 @@ proc destructiveMoveSink(n: PNode; c: var Con): PNode =
|
||||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||||
let bit = newSymNode dropBit(c, n.sym)
|
let bit = newSymNode dropBit(c, n.sym)
|
||||||
if optMoveCheck in c.owner.options:
|
if optMoveCheck in c.owner.options:
|
||||||
result.add callCodegenProc(c.graph, "chckMove", bit)
|
result.add callCodegenProc(c.graph, "chckMove", bit.info, bit)
|
||||||
result.add newTree(nkAsgn, bit,
|
result.add newTree(nkAsgn, bit,
|
||||||
newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool)))
|
newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool)))
|
||||||
result.add n
|
result.add n
|
||||||
|
|
|
||||||
|
|
@ -986,13 +986,50 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
|
|
||||||
proc genAddr(p: PProc, n: PNode, r: var TCompRes)
|
proc genAddr(p: PProc, n: PNode, r: var TCompRes)
|
||||||
|
|
||||||
proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
|
proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
|
||||||
let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
|
internalAssert p.config, n.kind == nkCheckedFieldExpr
|
||||||
internalAssert p.config, m.kind == nkCheckedFieldExpr
|
# nkDotExpr to access the requested field
|
||||||
genAddr(p, m, r) # XXX
|
let accessExpr = n[0]
|
||||||
|
# nkCall to check if the discriminant is valid
|
||||||
|
var checkExpr = n[1]
|
||||||
|
|
||||||
proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
|
let negCheck = checkExpr[0].sym.magic == mNot
|
||||||
genFieldAccess(p, n.sons[0], r) # XXX
|
if negCheck:
|
||||||
|
checkExpr = checkExpr[^1]
|
||||||
|
|
||||||
|
# Field symbol
|
||||||
|
var field = accessExpr[1].sym
|
||||||
|
internalAssert p.config, field.kind == skField
|
||||||
|
if field.loc.r == nil: field.loc.r = mangleName(p.module, field)
|
||||||
|
# Discriminant symbol
|
||||||
|
let disc = checkExpr[2].sym
|
||||||
|
internalAssert p.config, disc.kind == skField
|
||||||
|
if disc.loc.r == nil: disc.loc.r = mangleName(p.module, disc)
|
||||||
|
|
||||||
|
var setx: TCompRes
|
||||||
|
gen(p, checkExpr[1], setx)
|
||||||
|
|
||||||
|
var obj: TCompRes
|
||||||
|
gen(p, accessExpr[0], obj)
|
||||||
|
# Avoid evaluating the LHS twice (one to read the discriminant and one to read
|
||||||
|
# the field)
|
||||||
|
let tmp = p.getTemp()
|
||||||
|
lineF(p, "var $1 = $2;$n", tmp, obj.res)
|
||||||
|
|
||||||
|
useMagic(p, "raiseFieldError")
|
||||||
|
useMagic(p, "makeNimstrLit")
|
||||||
|
lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError(makeNimstrLit($5)); }$n",
|
||||||
|
setx.res, tmp, disc.loc.r, if negCheck: ~"!==" else: ~"===",
|
||||||
|
makeJSString(field.name.s))
|
||||||
|
|
||||||
|
if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex:
|
||||||
|
r.typ = etyBaseIndex
|
||||||
|
r.res = makeJSString($field.loc.r)
|
||||||
|
r.address = tmp
|
||||||
|
else:
|
||||||
|
r.typ = etyNone
|
||||||
|
r.res = "$1.$2" % [tmp, field.loc.r]
|
||||||
|
r.kind = resExpr
|
||||||
|
|
||||||
proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
|
proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
var
|
var
|
||||||
|
|
@ -1071,7 +1108,7 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
#internalError(p.config, n.info, "genAddr: 4 " & renderTree(n))
|
#internalError(p.config, n.info, "genAddr: 4 " & renderTree(n))
|
||||||
else: internalError(p.config, n.info, "genAddr: 2")
|
else: internalError(p.config, n.info, "genAddr: 2")
|
||||||
of nkCheckedFieldExpr:
|
of nkCheckedFieldExpr:
|
||||||
genCheckedFieldAddr(p, n, r)
|
genCheckedFieldOp(p, n[0], n.typ, r)
|
||||||
of nkDotExpr:
|
of nkDotExpr:
|
||||||
if mapType(p, n.typ) == etyBaseIndex:
|
if mapType(p, n.typ) == etyBaseIndex:
|
||||||
genFieldAddr(p, n.sons[0], r)
|
genFieldAddr(p, n.sons[0], r)
|
||||||
|
|
@ -2140,7 +2177,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r)
|
of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r)
|
||||||
of nkBracketExpr: genArrayAccess(p, n, r)
|
of nkBracketExpr: genArrayAccess(p, n, r)
|
||||||
of nkDotExpr: genFieldAccess(p, n, r)
|
of nkDotExpr: genFieldAccess(p, n, r)
|
||||||
of nkCheckedFieldExpr: genCheckedFieldAccess(p, n, r)
|
of nkCheckedFieldExpr: genCheckedFieldOp(p, n, nil, r)
|
||||||
of nkObjDownConv: gen(p, n.sons[0], r)
|
of nkObjDownConv: gen(p, n.sons[0], r)
|
||||||
of nkObjUpConv: upConv(p, n, r)
|
of nkObjUpConv: upConv(p, n, r)
|
||||||
of nkCast: genCast(p, n, r)
|
of nkCast: genCast(p, n, r)
|
||||||
|
|
|
||||||
|
|
@ -281,15 +281,16 @@ proc genDeref*(n: PNode): PNode =
|
||||||
n.typ.skipTypes(abstractInst).sons[0])
|
n.typ.skipTypes(abstractInst).sons[0])
|
||||||
result.add n
|
result.add n
|
||||||
|
|
||||||
proc callCodegenProc*(g: ModuleGraph; name: string, arg1: PNode;
|
proc callCodegenProc*(g: ModuleGraph; name: string;
|
||||||
arg2, arg3, optionalArgs: PNode = nil): PNode =
|
info: TLineInfo = unknownLineInfo();
|
||||||
result = newNodeI(nkCall, arg1.info)
|
arg1, arg2, arg3, optionalArgs: PNode = nil): PNode =
|
||||||
|
result = newNodeI(nkCall, info)
|
||||||
let sym = magicsys.getCompilerProc(g, name)
|
let sym = magicsys.getCompilerProc(g, name)
|
||||||
if sym == nil:
|
if sym == nil:
|
||||||
localError(g.config, arg1.info, "system module needs: " & name)
|
localError(g.config, info, "system module needs: " & name)
|
||||||
else:
|
else:
|
||||||
result.add newSymNode(sym)
|
result.add newSymNode(sym)
|
||||||
result.add arg1
|
if arg1 != nil: result.add arg1
|
||||||
if arg2 != nil: result.add arg2
|
if arg2 != nil: result.add arg2
|
||||||
if arg3 != nil: result.add arg3
|
if arg3 != nil: result.add arg3
|
||||||
if optionalArgs != nil:
|
if optionalArgs != nil:
|
||||||
|
|
@ -410,7 +411,8 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
|
||||||
threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner,
|
threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner,
|
||||||
barrier.typ, barrier)
|
barrier.typ, barrier)
|
||||||
body.add varSection2
|
body.add varSection2
|
||||||
body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.newSymNode)
|
body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info,
|
||||||
|
threadLocalBarrier.newSymNode)
|
||||||
var threadLocalProm: PSym
|
var threadLocalProm: PSym
|
||||||
if spawnKind == srByVar:
|
if spawnKind == srByVar:
|
||||||
threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
|
threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
|
||||||
|
|
@ -425,7 +427,8 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
|
||||||
body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
|
body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
|
||||||
"owner", fv.info, g.cache), threadParam.newSymNode)
|
"owner", fv.info, g.cache), threadParam.newSymNode)
|
||||||
|
|
||||||
body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.newSymNode)
|
body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.info,
|
||||||
|
threadParam.newSymNode)
|
||||||
if spawnKind == srByVar:
|
if spawnKind == srByVar:
|
||||||
body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
|
body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
|
||||||
elif fv != nil:
|
elif fv != nil:
|
||||||
|
|
@ -444,11 +447,13 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
|
||||||
if barrier == nil:
|
if barrier == nil:
|
||||||
# by now 'fv' is shared and thus might have beeen overwritten! we need
|
# by now 'fv' is shared and thus might have beeen overwritten! we need
|
||||||
# to use the thread-local view instead:
|
# to use the thread-local view instead:
|
||||||
body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.newSymNode)
|
body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info,
|
||||||
|
threadLocalProm.newSymNode)
|
||||||
else:
|
else:
|
||||||
body.add call
|
body.add call
|
||||||
if barrier != nil:
|
if barrier != nil:
|
||||||
body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.newSymNode)
|
body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.info,
|
||||||
|
threadLocalBarrier.newSymNode)
|
||||||
|
|
||||||
var params = newNodeI(nkFormalParams, f.info)
|
var params = newNodeI(nkFormalParams, f.info)
|
||||||
params.add newNodeI(nkEmpty, f.info)
|
params.add newNodeI(nkEmpty, f.info)
|
||||||
|
|
@ -710,7 +715,8 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
|
||||||
# create flowVar:
|
# create flowVar:
|
||||||
result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
|
result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
|
||||||
if barrier == nil:
|
if barrier == nil:
|
||||||
result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField)
|
result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info,
|
||||||
|
fvField)
|
||||||
|
|
||||||
elif spawnKind == srByVar:
|
elif spawnKind == srByVar:
|
||||||
var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
|
var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
|
||||||
|
|
@ -723,7 +729,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
|
||||||
let wrapper = createWrapperProc(g, fn, threadParam, argsParam,
|
let wrapper = createWrapperProc(g, fn, threadParam, argsParam,
|
||||||
varSection, varInit, call,
|
varSection, varInit, call,
|
||||||
barrierAsExpr, fvAsExpr, spawnKind)
|
barrierAsExpr, fvAsExpr, spawnKind)
|
||||||
result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
|
result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.info,
|
||||||
genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
|
wrapper.newSymNode, genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
|
||||||
|
|
||||||
if spawnKind == srFlowVar: result.add fvField
|
if spawnKind == srFlowVar: result.add fvField
|
||||||
|
|
|
||||||
|
|
@ -493,6 +493,6 @@ proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
|
||||||
result = newNodeI(nkStmtList, n.info)
|
result = newNodeI(nkStmtList, n.info)
|
||||||
generateAliasChecks(a, result)
|
generateAliasChecks(a, result)
|
||||||
result.add varSection
|
result.add varSection
|
||||||
result.add callCodegenProc(g, "openBarrier", barrier)
|
result.add callCodegenProc(g, "openBarrier", barrier.info, barrier)
|
||||||
result.add transformSpawn(g, owner, body, barrier)
|
result.add transformSpawn(g, owner, body, barrier)
|
||||||
result.add callCodegenProc(g, "closeBarrier", barrier)
|
result.add callCodegenProc(g, "closeBarrier", barrier.info, barrier)
|
||||||
|
|
|
||||||
|
|
@ -739,7 +739,7 @@ proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
|
||||||
let actions = newTransNode(nkStmtListExpr, n[1], 2)
|
let actions = newTransNode(nkStmtListExpr, n[1], 2)
|
||||||
# Generating `let exc = (excType)(getCurrentException())`
|
# Generating `let exc = (excType)(getCurrentException())`
|
||||||
# -> getCurrentException()
|
# -> getCurrentException()
|
||||||
let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", newNodeI(nkEmpty, n.info)))
|
let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException"))
|
||||||
# -> (excType)
|
# -> (excType)
|
||||||
let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
|
let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
|
||||||
convNode[0] = PTransNode(newNodeI(nkEmpty, n.info))
|
convNode[0] = PTransNode(newNodeI(nkEmpty, n.info))
|
||||||
|
|
|
||||||
|
|
@ -562,6 +562,8 @@ proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister =
|
||||||
else:
|
else:
|
||||||
result = c.genx(n)
|
result = c.genx(n)
|
||||||
|
|
||||||
|
proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags)
|
||||||
|
|
||||||
proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
|
proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
|
||||||
case le.kind
|
case le.kind
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
|
|
@ -570,12 +572,16 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
|
||||||
c.gABC(le, opcWrArr, dest, idx, value)
|
c.gABC(le, opcWrArr, dest, idx, value)
|
||||||
c.freeTemp(dest)
|
c.freeTemp(dest)
|
||||||
c.freeTemp(idx)
|
c.freeTemp(idx)
|
||||||
of nkDotExpr, nkCheckedFieldExpr:
|
of nkCheckedFieldExpr:
|
||||||
# XXX field checks here
|
var objR: TDest = -1
|
||||||
let left = if le.kind == nkDotExpr: le else: le.sons[0]
|
genCheckedObjAccessAux(c, le, objR, {gfNode})
|
||||||
let dest = c.genx(left.sons[0], {gfNode})
|
let idx = genField(c, le[0].sons[1])
|
||||||
let idx = genField(c, left.sons[1])
|
c.gABC(le[0], opcWrObj, objR, idx, value)
|
||||||
c.gABC(left, opcWrObj, dest, idx, value)
|
c.freeTemp(objR)
|
||||||
|
of nkDotExpr:
|
||||||
|
let dest = c.genx(le.sons[0], {gfNode})
|
||||||
|
let idx = genField(c, le.sons[1])
|
||||||
|
c.gABC(le, opcWrObj, dest, idx, value)
|
||||||
c.freeTemp(dest)
|
c.freeTemp(dest)
|
||||||
of nkDerefExpr, nkHiddenDeref:
|
of nkDerefExpr, nkHiddenDeref:
|
||||||
let dest = c.genx(le.sons[0], {gfNode})
|
let dest = c.genx(le.sons[0], {gfNode})
|
||||||
|
|
@ -1419,13 +1425,19 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
||||||
else:
|
else:
|
||||||
c.preventFalseAlias(le, opcWrArr, dest, idx, tmp)
|
c.preventFalseAlias(le, opcWrArr, dest, idx, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
of nkDotExpr, nkCheckedFieldExpr:
|
of nkCheckedFieldExpr:
|
||||||
# XXX field checks here
|
var objR: TDest = -1
|
||||||
let left = if le.kind == nkDotExpr: le else: le.sons[0]
|
genCheckedObjAccessAux(c, le, objR, {gfNode})
|
||||||
let dest = c.genx(left.sons[0], {gfNode})
|
let idx = genField(c, le[0].sons[1])
|
||||||
let idx = genField(c, left.sons[1])
|
|
||||||
let tmp = c.genx(ri)
|
let tmp = c.genx(ri)
|
||||||
c.preventFalseAlias(left, opcWrObj, dest, idx, tmp)
|
c.preventFalseAlias(le[0], opcWrObj, objR, idx, tmp)
|
||||||
|
c.freeTemp(tmp)
|
||||||
|
c.freeTemp(objR)
|
||||||
|
of nkDotExpr:
|
||||||
|
let dest = c.genx(le.sons[0], {gfNode})
|
||||||
|
let idx = genField(c, le.sons[1])
|
||||||
|
let tmp = c.genx(ri)
|
||||||
|
c.preventFalseAlias(le, opcWrObj, dest, idx, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
of nkDerefExpr, nkHiddenDeref:
|
of nkDerefExpr, nkHiddenDeref:
|
||||||
let dest = c.genx(le.sons[0], {gfNode})
|
let dest = c.genx(le.sons[0], {gfNode})
|
||||||
|
|
@ -1561,9 +1573,59 @@ proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
c.gABC(n, opcLdObj, dest, a, b)
|
c.gABC(n, opcLdObj, dest, a, b)
|
||||||
c.freeTemp(a)
|
c.freeTemp(a)
|
||||||
|
|
||||||
|
proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
|
internalAssert c.config, n.kind == nkCheckedFieldExpr
|
||||||
|
# nkDotExpr to access the requested field
|
||||||
|
let accessExpr = n[0]
|
||||||
|
# nkCall to check if the discriminant is valid
|
||||||
|
var checkExpr = n[1]
|
||||||
|
|
||||||
|
let negCheck = checkExpr[0].sym.magic == mNot
|
||||||
|
if negCheck:
|
||||||
|
checkExpr = checkExpr[^1]
|
||||||
|
|
||||||
|
# Discriminant symbol
|
||||||
|
let disc = checkExpr[2]
|
||||||
|
internalAssert c.config, disc.sym.kind == skField
|
||||||
|
|
||||||
|
# Load the object in `dest`
|
||||||
|
c.gen(accessExpr[0], dest, flags)
|
||||||
|
# Load the discriminant
|
||||||
|
var discVal = c.getTemp(disc.typ)
|
||||||
|
c.gABC(n, opcLdObj, discVal, dest, genField(c, disc))
|
||||||
|
# Check if its value is contained in the supplied set
|
||||||
|
let setLit = c.genx(checkExpr[1])
|
||||||
|
var rs = c.getTemp(getSysType(c.graph, n.info, tyBool))
|
||||||
|
c.gABC(n, opcContainsSet, rs, setLit, discVal)
|
||||||
|
c.freeTemp(setLit)
|
||||||
|
# If the check fails let the user know
|
||||||
|
let L1 = c.xjmp(n, if negCheck: opcFJmp else: opcTJmp, rs)
|
||||||
|
c.freeTemp(rs)
|
||||||
|
# Not ideal but will do for the moment
|
||||||
|
c.gABC(n, opcQuit)
|
||||||
|
c.patch(L1)
|
||||||
|
|
||||||
proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
# XXX implement field checks!
|
var objR: TDest = -1
|
||||||
genObjAccess(c, n.sons[0], dest, flags)
|
genCheckedObjAccessAux(c, n, objR, flags)
|
||||||
|
|
||||||
|
let accessExpr = n[0]
|
||||||
|
# Field symbol
|
||||||
|
var field = accessExpr[1]
|
||||||
|
internalAssert c.config, field.sym.kind == skField
|
||||||
|
|
||||||
|
# Load the content now
|
||||||
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
|
let fieldPos = genField(c, field)
|
||||||
|
if needsRegLoad():
|
||||||
|
var cc = c.getTemp(accessExpr.typ)
|
||||||
|
c.gABC(n, opcLdObj, cc, objR, fieldPos)
|
||||||
|
c.gABC(n, opcNodeToReg, dest, cc)
|
||||||
|
c.freeTemp(cc)
|
||||||
|
else:
|
||||||
|
c.gABC(n, opcLdObj, dest, objR, fieldPos)
|
||||||
|
|
||||||
|
c.freeTemp(objR)
|
||||||
|
|
||||||
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
let arrayType = n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
|
let arrayType = n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
|
||||||
|
|
|
||||||
24
tests/js/t6612.nim
Normal file
24
tests/js/t6612.nim
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
discard """
|
||||||
|
action: "run"
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc fillWith(sq: var seq[int], n: int, unused: string) =
|
||||||
|
sq = @[n]
|
||||||
|
|
||||||
|
type
|
||||||
|
Object = object of RootObj
|
||||||
|
case hasNums: bool
|
||||||
|
of true:
|
||||||
|
numbers: seq[int]
|
||||||
|
of false:
|
||||||
|
discard
|
||||||
|
always: seq[int]
|
||||||
|
|
||||||
|
var obj = Object(hasNums: true)
|
||||||
|
|
||||||
|
obj.always.fillWith(5, "unused")
|
||||||
|
doAssert obj.always == @[5]
|
||||||
|
|
||||||
|
obj.numbers.fillWith(3, "unused")
|
||||||
|
doAssert obj.numbers == @[3]
|
||||||
|
doAssert obj.always == @[5]
|
||||||
48
tests/js/tfieldchecks.nim
Normal file
48
tests/js/tfieldchecks.nim
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
discard """
|
||||||
|
output: '''
|
||||||
|
foo
|
||||||
|
C
|
||||||
|
3.14
|
||||||
|
foo
|
||||||
|
3.14
|
||||||
|
3.14
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
V = enum
|
||||||
|
A, B, C
|
||||||
|
X = object
|
||||||
|
f0: string
|
||||||
|
case f1: V
|
||||||
|
of A: f2: string
|
||||||
|
of B: discard
|
||||||
|
of C: f3: float
|
||||||
|
|
||||||
|
var obj = X(f0: "foo", f1: C, f3: 3.14)
|
||||||
|
|
||||||
|
block:
|
||||||
|
echo obj.f0
|
||||||
|
echo obj.f1
|
||||||
|
doAssertRaises(FieldError): echo obj.f2
|
||||||
|
echo obj.f3
|
||||||
|
|
||||||
|
block:
|
||||||
|
let a0 = addr(obj.f0)
|
||||||
|
echo a0[]
|
||||||
|
# let a1 = unsafeAddr(obj.f1)
|
||||||
|
# echo a1[]
|
||||||
|
doAssertRaises(FieldError):
|
||||||
|
let a2 = addr(obj.f2)
|
||||||
|
echo a2[]
|
||||||
|
let a3 = addr(obj.f3)
|
||||||
|
echo a3[]
|
||||||
|
|
||||||
|
# Prevent double evaluation of LHS
|
||||||
|
block:
|
||||||
|
var flag = false
|
||||||
|
proc wrap(x: X): X =
|
||||||
|
doAssert flag == false
|
||||||
|
flag = true
|
||||||
|
result = x
|
||||||
|
echo wrap(obj).f3
|
||||||
3
tests/vm/tcgemptycallarg.nim
Normal file
3
tests/vm/tcgemptycallarg.nim
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
static:
|
||||||
|
doAssertRaises(ValueError):
|
||||||
|
raise newException(ValueError, "Yes")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue