Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default fields for objects #12378

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1898,11 +1898,7 @@ 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})
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
2 changes: 1 addition & 1 deletion tests/objvariant/trt_discrim_err1.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
discard """
errormsg: "branch initialization with a runtime discriminator is not supported inside of an `elif` branch."
errormsg: "runtime discriminator could select multiple branches, so you can't initialize these fields: green"
line: 16
"""
type
Expand Down
2 changes: 1 addition & 1 deletion tests/objvariant/trt_discrim_err3.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
discard """
errormsg: "runtime discriminator must be immutable if branch fields are initialized, a 'let' binding is required."
errormsg: "runtime discriminator could select multiple branches, so you can't initialize these fields: i32"
line: 16
"""

Expand Down