Skip to content

Commit

Permalink
Default fields
Browse files Browse the repository at this point in the history
  • Loading branch information
Clyybber committed Dec 19, 2019
1 parent cb0a20b commit 44b3f90
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 221 deletions.
18 changes: 12 additions & 6 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
optInd(p, result)
result.add(parseExpr(p))
else:
result.add(newNodeP(nkEmpty, p))
result.add(p.emptyNode)

proc parseTuple(p: var TParser, indentAllowed = false): PNode =
#| inlTupleDecl = 'tuple'
Expand Down Expand Up @@ -1898,11 +1898,17 @@ proc parseObjectCase(p: var TParser): PNode =
#| | IND{=} objectBranches)
result = newNodeP(nkRecCase, p)
getTokNoInd(p)
var a = newNodeP(nkIdentDefs, p)
a.add(identWithPragma(p))
eat(p, tkColon)
a.add(parseTypeDesc(p))
a.add(p.emptyNode)
var a = parseIdentColonEquals(p, {withPragma})
# var a = newNodeP(nkIdentDefs, p)
# a.add(identWithPragma(p))
# eat(p, tkColon)
# a.add(parseTypeDesc(p))
# if p.tok.tokType == tkEquals:
# getTok(p)
# optInd(p, result)
# a.add(parseExpr(p))
# else:
# a.add(p.emptyNode)
result.add(a)
if p.tok.tokType == tkColon: getTok(p)
flexComment(p, result)
Expand Down
306 changes: 119 additions & 187 deletions compiler/semobjconstr.nim

Large diffs are not rendered by default.

17 changes: 10 additions & 7 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ proc getIntSetOfType(c: PContext, t: PType): IntSet =
result = initIntSet()
if t.enumHasHoles:
let t = t.skipTypes(abstractRange)
for field in t.n.sons:
for field in t.n:
result.incl(field.sym.position)
else:
assert(lengthOrd(c.config, t) <= BiggestInt(MaxSetElements))
Expand All @@ -607,7 +607,7 @@ iterator processBranchVals(b: PNode): int =
assert b.kind in {nkOfBranch, nkElifBranch, nkElse}
if b.kind == nkOfBranch:
for i in 0..<b.len-1:
if b[i].kind == nkIntLit:
if b[i].kind in {nkIntLit..nkUInt64Lit}:
yield b[i].intVal.int
elif b[i].kind == nkRange:
for i in b[i][0].intVal..b[i][1].intVal:
Expand Down Expand Up @@ -742,18 +742,15 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
if a != father: father.add a
of nkIdentDefs:
checkMinSonsLen(n, 3, c.config)
var a: PNode
if father.kind != nkRecList and n.len >= 4: a = newNodeI(nkRecList, n.info)
else: a = newNodeI(nkEmpty, n.info)
if n[^1].kind != nkEmpty:
localError(c.config, n[^1].info, errInitHereNotAllowed)
var typ: PType
if n[^2].kind == nkEmpty:
localError(c.config, n.info, errTypeExpected)
typ = errorType(c)
else:
typ = semTypeNode(c, n[^2], nil)
propagateToOwner(rectype, typ)
var a: PNode = if father.kind != nkRecList and n.len > 3: newNodeI(nkRecList, n.info)
else: newNodeI(nkEmpty, n.info)
var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner
else: rectype.sym
for i in 0..<n.len-2:
Expand All @@ -774,6 +771,12 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
inc(pos)
if containsOrIncl(check, f.name.id):
localError(c.config, info, "attempt to redefine: '" & f.name.s & "'")
var fSym = newSymNode(f)
if n[^1].kind != nkEmpty:
n[^1] = semConstExpr(c, n[^1])
fSym.sym.ast = n[^1]
elif typ.kind in {tyRange, tyOrdinal}: #Node flag? Handle embedded objects?
fSym.sym.ast = semConstExpr(c, newIntNode(nkIntLit, firstOrd(c.config, typ)))
if a.kind == nkEmpty: father.add newSymNode(f)
else: a.add newSymNode(f)
styleCheckDef(c.config, f)
Expand Down
107 changes: 89 additions & 18 deletions compiler/transf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# * introduces method dispatchers
# * performs lambda lifting for closure support
# * transforms 'defer' into a 'try finally' statement
# * generates default field values for objects and transforms object contructors

import
options, ast, astalgo, trees, msgs,
Expand Down Expand Up @@ -44,7 +45,7 @@ type
module: PSym
transCon: PTransCon # top of a TransCon stack
inlining: int # > 0 if we are in inlining context (copy vars)
nestedProcs: int # > 0 if we are in a nested proc
genResult: bool # XXX
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
deferDetected, tooEarly: bool
graph: ModuleGraph
Expand Down Expand Up @@ -383,7 +384,7 @@ proc transformYield(c: PTransf, n: PNode): PNode =
let rhs = transform(c, e)
result.add(asgnTo(lhs, rhs))

inc(c.transCon.yieldStmts)
inc c.transCon.yieldStmts
if c.transCon.yieldStmts <= 1:
# common case
result.add(c.transCon.forLoopBody)
Expand Down Expand Up @@ -658,7 +659,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =

let body = transformBody(c.graph, iter, true)
pushInfoContext(c.graph.config, n.info)
inc(c.inlining)
inc c.inlining
stmtList.add(transform(c, body))
#findWrongOwners(c, stmtList.pnode)
dec(c.inlining)
Expand Down Expand Up @@ -743,13 +744,13 @@ proc transformCall(c: PTransf, n: PNode): PNode =
var j = 1
while j < n.len:
var a = transform(c, n[j])
inc(j)
inc j
if isConstExpr(a):
while (j < n.len):
let b = transform(c, n[j])
if not isConstExpr(b): break
a = evalOp(op.magic, n, a, b, nil, c.graph)
inc(j)
inc j
result.add(a)
if result.len == 2: result = result[1]
elif magic == mAddr:
Expand Down Expand Up @@ -828,13 +829,13 @@ proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
var j = 0
while j < args.len:
var a = args[j]
inc(j)
inc j
if isConstExpr(a):
while j < args.len:
let b = args[j]
if not isConstExpr(b): break
a = evalOp(op.magic, result, a, b, nil, g)
inc(j)
inc j
result.add(a)
if result.len == 2: result = result[1]
else:
Expand Down Expand Up @@ -882,6 +883,47 @@ proc hoistParamsUsedInDefault(c: PTransf, call, letSection, defExpr: PNode): PNo
let hoisted = hoistParamsUsedInDefault(c, call, letSection, defExpr[i])
if hoisted != nil: defExpr[i] = hoisted

import nimsets
proc caseBranchMatchesExpr(branch, matched: PNode): bool =
for i in 0 ..< branch.len-1:
if branch[i].kind == nkRange:
if overlap(branch[i], matched): return true
elif exprStructuralEquivalent(branch[i], matched):
return true

proc pickCaseBranch(caseExpr, matched: PNode): int =
let endsWithElse = caseExpr[^1].kind == nkElse
for i in 1..<caseExpr.len - endsWithElse.int:
if caseExpr[i].caseBranchMatchesExpr(matched):
return i
if endsWithElse:
return caseExpr.len - 1

proc defaultFieldsForTheUninitialized(c: PTransf, recNode: PNode): seq[PNode] =
case recNode.kind
of nkRecList:
for field in recNode:
result.add defaultFieldsForTheUninitialized(c, field)
of nkRecCase:
let discriminator = recNode[0]
var selectedBranch: int
let defaultValue = discriminator.sym.ast
if defaultValue == nil:
# None of the branches were explicitly selected by the user and no value
# was given to the discrimator. We can assume that it will be initialized
# to zero and this will select a particular branch as a result:
selectedBranch = recNode.pickCaseBranch newIntNode(nkIntLit#[c.graph]#, 0)
else: # Try to use default value
selectedBranch = recNode.pickCaseBranch defaultValue
result.add newTree(nkExprColonExpr, discriminator, defaultValue)
result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1])
of nkSym:
let field = recNode.sym
if field.ast != nil: #Try to use default value
result.add newTree(nkExprColonExpr, recNode, field.ast)
else:
assert false

proc transform(c: PTransf, n: PNode): PNode =
when false:
var oldDeferAnchor: PNode
Expand All @@ -890,6 +932,18 @@ proc transform(c: PTransf, n: PNode): PNode =
nkBlockStmt, nkBlockExpr}:
oldDeferAnchor = c.deferAnchor
c.deferAnchor = n
if c.genResult:
c.genResult = false
result = newNodeIT(nkStmtList, n.info, nil)
let toInit = c.getCurrOwner().ast[resultPos]
if toInit.typ != nil and toInit.typ.kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, toInit.info, toInit.typ))
asgnExpr.typ = toInit.typ
#TODO: Once the VM is ready this should be done in injectdestructors so that it can be elided properly
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, toInit.typ.n)
result.add transform(c, newTree(nkAsgn, toInit, asgnExpr))
result.add transform(c, n)
return result
case n.kind
of nkSym:
result = transformSym(c, n)
Expand Down Expand Up @@ -947,17 +1001,26 @@ proc transform(c: PTransf, n: PNode): PNode =
result.add(newSymNode(labl))
of nkBreakStmt: result = transformBreak(c, n)
of nkCallKinds:
result = transformCall(c, n)
var call = result
if nfDefaultRefsParam in call.flags:
# We've found a default value that references another param.
# See the notes in `hoistParamsUsedInDefault` for more details.
var hoistedParams = newNodeI(nkLetSection, call.info, 0)
for i in 1..<call.len:
let hoisted = hoistParamsUsedInDefault(c, call, hoistedParams, call[i])
if hoisted != nil: call[i] = hoisted
result = newTree(nkStmtListExpr, hoistedParams, call)
result.typ = call.typ
# if (n[0].kind == nkSym) and (n[0].sym.magic == mReset) and (n[1].typ.kind == tyObject):
# result = newNodeIT(nkStmtList, n.info, n.typ)
# result.add transformCall(c, n)
# result.add initDefaultFields(n[1], n[1].typ.n)
# elif (n[0].kind == nkSym) and (n[0].sym.magic in {mNew, mNewFinalize}) and (n[1].typ.kind == tyRef):
# result = newNodeIT(nkStmtListExpr, n.info, n.typ)
# result.add transformCall(c, n)
# result.add initDefaultFields(n[1], n[1].typ[0].n)
# else:
result = transformCall(c, n)
var call = result
if nfDefaultRefsParam in call.flags:
# We've found a default value that references another param.
# See the notes in `hoistParamsUsedInDefault` for more details.
var hoistedParams = newNodeI(nkLetSection, call.info, 0)
for i in 1 ..< call.len:
let hoisted = hoistParamsUsedInDefault(c, call, hoistedParams, call[i])
if hoisted != nil: call[i] = hoisted
result = newTree(nkStmtListExpr, hoistedParams, call)
result.typ = call.typ
of nkAddr, nkHiddenAddr:
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
of nkDerefExpr, nkHiddenDeref:
Expand Down Expand Up @@ -1013,6 +1076,13 @@ proc transform(c: PTransf, n: PNode): PNode =
return n
of nkExceptBranch:
result = transformExceptBranch(c, n)
of nkObjConstr:
result = n
if result.typ.skipTypes(abstractInst).kind == tyObject or
result.typ.skipTypes(abstractInst).kind == tyRef and result.typ.skipTypes(abstractInst)[0].kind == tyObject:
result.sons.add result[0].sons
result[0] = newNodeIT(nkType, result.info, result.typ)
result = transformSons(c, result)
else:
result = transformSons(c, n)
when false:
Expand All @@ -1034,6 +1104,7 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
# nodes into an empty node.
if nfTransf in n.flags: return n
pushTransCon(c, newTransCon(owner))
c.genResult = c.getCurrOwner().kind in routineKinds and c.transCon.owner.ast.len > resultPos and c.transCon.owner.ast[resultPos] != nil
result = transform(c, n)
popTransCon(c)
incl(result.flags, nfTransf)
Expand Down
2 changes: 1 addition & 1 deletion compiler/vmgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1639,7 +1639,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
else:
if s.kind == skForVar and c.mode == emRepl: c.setSlot(s)
if s.position > 0 or (s.position == 0 and
s.kind in {skParam, skResult}):
s.kind in {skParam, skResult, skTemp}):
if dest < 0:
dest = s.position + ord(s.kind == skParam)
internalAssert(c.config, c.prc.slots[dest].kind < slotSomeTemp)
Expand Down
2 changes: 1 addition & 1 deletion tests/constructors/tinvalid_construction.nim
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ accept TObj()
accept TObj(choice: A)
reject TObj(choice: A, bc: 10) # bc is in the wrong branch
accept TObj(choice: B, bc: 20)
reject TObj(a: 10) # branch selected without providing discriminator
accept TObj(a: 10) # branch selected with the default value "low(T)" of the discriminator
reject TObj(choice: x, a: 10) # the discrimantor must be a compile-time value when a branch is selected
accept TObj(choice: x) # it's OK to use run-time value when a branch is not selected
accept TObj(choice: F, f: "") # match an else clause
Expand Down
6 changes: 6 additions & 0 deletions tests/objvariant/trt_discrim.nim
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ reject:
var varkind = k4

reject: # not immutable.
case varkind
of k1, k2, k3: discard KindObj(kind: varkind, i32: 1)
of k4: discard KindObj(kind: varkind, f32: 2.0)
else: discard KindObj(kind: varkind, str: "3")

reject: # complete bogus
case varkind
of k1, k2, k3: discard KindObj(varkind: kind, i32: 1)
of k4: discard KindObj(varkind: kind, f32: 2.0)
Expand Down
2 changes: 1 addition & 1 deletion tests/objvariant/trt_discrim_err0.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
discard """
errormsg: "possible values {k1, k3, k4} are in conflict with discriminator values for selected object branch 3"
errormsg: "runtime discriminator could select multiple branches, so you can't initialize these fields: str"
line: 17
"""

Expand Down

0 comments on commit 44b3f90

Please sign in to comment.