fixes object default fields bugs and add tests (#20839)
* fixes object default fields bugs and add tests * Update compiler/semmagic.nim * Update compiler/sem.nim * Update compiler/sem.nim Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
This commit is contained in:
parent
1daf43fb14
commit
7f2ff909d9
4 changed files with 66 additions and 20 deletions
|
|
@ -553,17 +553,18 @@ proc pickCaseBranchIndex(caseExpr, matched: PNode): int =
|
||||||
if endsWithElse:
|
if endsWithElse:
|
||||||
return caseExpr.len - 1
|
return caseExpr.len - 1
|
||||||
|
|
||||||
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode]
|
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, id: var IntSet): seq[PNode]
|
||||||
|
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, id: var IntSet): PNode
|
||||||
proc defaultNodeField(c: PContext, a: PNode): PNode
|
proc defaultNodeField(c: PContext, a: PNode): PNode
|
||||||
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode
|
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode
|
||||||
|
|
||||||
const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink}
|
const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink}
|
||||||
|
|
||||||
proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): seq[PNode] =
|
proc defaultFieldsForTuple(c: PContext, recNode: PNode, id: var IntSet, hasDefault: var bool): seq[PNode] =
|
||||||
case recNode.kind
|
case recNode.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for field in recNode:
|
for field in recNode:
|
||||||
result.add defaultFieldsForTuple(c, field, hasDefault)
|
result.add defaultFieldsForTuple(c, field, id, hasDefault)
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let field = recNode.sym
|
let field = recNode.sym
|
||||||
let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
|
let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
|
||||||
|
|
@ -572,7 +573,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): s
|
||||||
result.add newTree(nkExprColonExpr, recNode, field.ast)
|
result.add newTree(nkExprColonExpr, recNode, field.ast)
|
||||||
else:
|
else:
|
||||||
if recType.kind in {tyObject, tyArray, tyTuple}:
|
if recType.kind in {tyObject, tyArray, tyTuple}:
|
||||||
let asgnExpr = defaultNodeField(c, recNode, recNode.typ)
|
let asgnExpr = defaultNodeField(c, recNode, recNode.typ, id)
|
||||||
if asgnExpr != nil:
|
if asgnExpr != nil:
|
||||||
hasDefault = true
|
hasDefault = true
|
||||||
asgnExpr.flags.incl nfUseDefaultField
|
asgnExpr.flags.incl nfUseDefaultField
|
||||||
|
|
@ -591,11 +592,11 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): s
|
||||||
else:
|
else:
|
||||||
doAssert false
|
doAssert false
|
||||||
|
|
||||||
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
|
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, id: var IntSet): seq[PNode] =
|
||||||
case recNode.kind
|
case recNode.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for field in recNode:
|
for field in recNode:
|
||||||
result.add defaultFieldsForTheUninitialized(c, field)
|
result.add defaultFieldsForTheUninitialized(c, field, id)
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
let discriminator = recNode[0]
|
let discriminator = recNode[0]
|
||||||
var selectedBranch: int
|
var selectedBranch: int
|
||||||
|
|
@ -609,31 +610,34 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
|
||||||
selectedBranch = recNode.pickCaseBranchIndex defaultValue
|
selectedBranch = recNode.pickCaseBranchIndex defaultValue
|
||||||
defaultValue.flags.incl nfUseDefaultField
|
defaultValue.flags.incl nfUseDefaultField
|
||||||
result.add newTree(nkExprColonExpr, discriminator, defaultValue)
|
result.add newTree(nkExprColonExpr, discriminator, defaultValue)
|
||||||
result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1])
|
result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1], id)
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let field = recNode.sym
|
let field = recNode.sym
|
||||||
let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
|
let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
|
||||||
if field.ast != nil: #Try to use default value
|
if field.ast != nil: #Try to use default value
|
||||||
result.add newTree(nkExprColonExpr, recNode, field.ast)
|
result.add newTree(nkExprColonExpr, recNode, field.ast)
|
||||||
elif recType.kind in {tyObject, tyArray, tyTuple}:
|
elif recType.kind in {tyObject, tyArray, tyTuple}:
|
||||||
let asgnExpr = defaultNodeField(c, recNode, recNode.typ)
|
let asgnExpr = defaultNodeField(c, recNode, recType, id)
|
||||||
if asgnExpr != nil:
|
if asgnExpr != nil:
|
||||||
|
asgnExpr.typ = recType
|
||||||
asgnExpr.flags.incl nfUseDefaultField
|
asgnExpr.flags.incl nfUseDefaultField
|
||||||
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
|
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
|
||||||
else:
|
else:
|
||||||
doAssert false
|
doAssert false
|
||||||
|
|
||||||
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
|
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, id: var IntSet): PNode =
|
||||||
let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
|
let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
|
||||||
if aTypSkip.kind == tyObject:
|
if aTypSkip.kind == tyObject:
|
||||||
let child = defaultFieldsForTheUninitialized(c, aTyp.skipTypes(defaultFieldsSkipTypes).n)
|
if id.containsOrIncl(aTypSkip.id):
|
||||||
|
return
|
||||||
|
let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, id)
|
||||||
if child.len > 0:
|
if child.len > 0:
|
||||||
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
|
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTypSkip))
|
||||||
asgnExpr.typ = aTyp
|
asgnExpr.typ = aTypSkip
|
||||||
asgnExpr.sons.add child
|
asgnExpr.sons.add child
|
||||||
result = semExpr(c, asgnExpr)
|
result = semExpr(c, asgnExpr)
|
||||||
elif aTypSkip.kind == tyArray:
|
elif aTypSkip.kind == tyArray:
|
||||||
let child = defaultNodeField(c, a, aTypSkip[1])
|
let child = defaultNodeField(c, a, aTypSkip[1], id)
|
||||||
|
|
||||||
if child != nil:
|
if child != nil:
|
||||||
let node = newNode(nkIntLit)
|
let node = newNode(nkIntLit)
|
||||||
|
|
@ -646,15 +650,20 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
|
||||||
elif aTypSkip.kind == tyTuple:
|
elif aTypSkip.kind == tyTuple:
|
||||||
var hasDefault = false
|
var hasDefault = false
|
||||||
if aTypSkip.n != nil:
|
if aTypSkip.n != nil:
|
||||||
let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault)
|
let children = defaultFieldsForTuple(c, aTypSkip.n, id, hasDefault)
|
||||||
if hasDefault and children.len > 0:
|
if hasDefault and children.len > 0:
|
||||||
result = newNodeI(nkTupleConstr, a.info)
|
result = newNodeI(nkTupleConstr, a.info)
|
||||||
result.typ = aTyp
|
result.typ = aTyp
|
||||||
result.sons.add children
|
result.sons.add children
|
||||||
result = semExpr(c, result)
|
result = semExpr(c, result)
|
||||||
|
|
||||||
|
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
|
||||||
|
var s = initIntSet()
|
||||||
|
defaultNodeField(c, a, aTyp, s)
|
||||||
|
|
||||||
proc defaultNodeField(c: PContext, a: PNode): PNode =
|
proc defaultNodeField(c: PContext, a: PNode): PNode =
|
||||||
result = defaultNodeField(c, a, a.typ)
|
var s = initIntSet()
|
||||||
|
result = defaultNodeField(c, a, a.typ, s)
|
||||||
|
|
||||||
include semtempl, semgnrc, semstmts, semexprs
|
include semtempl, semgnrc, semstmts, semexprs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,9 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
|
||||||
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[1].info, typ))
|
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[1].info, typ))
|
||||||
asgnExpr.typ = typ
|
asgnExpr.typ = typ
|
||||||
var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
|
var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
|
||||||
|
var id = initIntSet()
|
||||||
while true:
|
while true:
|
||||||
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n)
|
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, id)
|
||||||
let base = t[0]
|
let base = t[0]
|
||||||
if base == nil:
|
if base == nil:
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -325,6 +325,11 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||||
elif field.ast != nil:
|
elif field.ast != nil:
|
||||||
result.status = initUnknown
|
result.status = initUnknown
|
||||||
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
|
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
|
||||||
|
else:
|
||||||
|
let defaultExpr = defaultNodeField(c, n)
|
||||||
|
if defaultExpr != nil:
|
||||||
|
result.status = initUnknown
|
||||||
|
result.defaults.add newTree(nkExprColonExpr, n, defaultExpr)
|
||||||
else:
|
else:
|
||||||
result.status = initNone
|
result.status = initNone
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ discard """
|
||||||
targets: "c cpp js"
|
targets: "c cpp js"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import std/[times, tables, macros]
|
import std/[times, macros]
|
||||||
|
|
||||||
type
|
type
|
||||||
Guess = object
|
Guess = object
|
||||||
|
|
@ -27,6 +27,10 @@ import mobject_default_value
|
||||||
block:
|
block:
|
||||||
let x = Default()
|
let x = Default()
|
||||||
doAssert x.se == 0'i32
|
doAssert x.se == 0'i32
|
||||||
|
|
||||||
|
block:
|
||||||
|
let x = default(Default)
|
||||||
|
doAssert x.se == 0'i32
|
||||||
# echo Default(poi: 12)
|
# echo Default(poi: 12)
|
||||||
# echo Default(poi: 17)
|
# echo Default(poi: 17)
|
||||||
|
|
||||||
|
|
@ -120,6 +124,19 @@ template main {.dirty.} =
|
||||||
doAssert rVal == 0 # it should be 1
|
doAssert rVal == 0 # it should be 1
|
||||||
doAssert objVal.r == 1
|
doAssert objVal.r == 1
|
||||||
|
|
||||||
|
block: # bug #16744
|
||||||
|
type
|
||||||
|
R = range[1..10]
|
||||||
|
Obj = object
|
||||||
|
r: R
|
||||||
|
|
||||||
|
var
|
||||||
|
rVal: R = default(R) # Works fine
|
||||||
|
objVal = Obj()
|
||||||
|
|
||||||
|
doAssert rVal == 0 # it should be 1
|
||||||
|
doAssert objVal.r == 1
|
||||||
|
|
||||||
block: # bug #3608
|
block: # bug #3608
|
||||||
type
|
type
|
||||||
abc = ref object
|
abc = ref object
|
||||||
|
|
@ -148,6 +165,9 @@ template main {.dirty.} =
|
||||||
let y = ObjectBaseDistinct(default(ObjectBase))
|
let y = ObjectBaseDistinct(default(ObjectBase))
|
||||||
doAssert ObjectBase(y).value == 12
|
doAssert ObjectBase(y).value == 12
|
||||||
|
|
||||||
|
let m = ObjectBaseDistinct(ObjectBase())
|
||||||
|
doAssert ObjectBase(m).value == 12
|
||||||
|
|
||||||
proc hello(): ObjectBaseDistinct =
|
proc hello(): ObjectBaseDistinct =
|
||||||
result = ObjectBaseDistinct(default(ObjectBase))
|
result = ObjectBaseDistinct(default(ObjectBase))
|
||||||
|
|
||||||
|
|
@ -192,13 +212,25 @@ template main {.dirty.} =
|
||||||
doAssert x.name.time == 1.2
|
doAssert x.name.time == 1.2
|
||||||
doAssert x.name.scale == 1
|
doAssert x.name.scale == 1
|
||||||
|
|
||||||
|
block:
|
||||||
|
let x = Object2()
|
||||||
|
doAssert x.name.value == 12
|
||||||
|
doAssert x.name.time == 1.2
|
||||||
|
doAssert x.name.scale == 1
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var x: ref Object2
|
var x: ref Object2
|
||||||
new x
|
new x
|
||||||
doAssert x[] == default(Object2)
|
doAssert x[] == default(Object2)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var x = default(Object3) # todo Object3() ?
|
var x = default(Object3)
|
||||||
|
doAssert x.obj.name.value == 12
|
||||||
|
doAssert x.obj.name.time == 1.2
|
||||||
|
doAssert x.obj.name.scale == 1
|
||||||
|
|
||||||
|
block:
|
||||||
|
var x = Object3()
|
||||||
doAssert x.obj.name.value == 12
|
doAssert x.obj.name.value == 12
|
||||||
doAssert x.obj.name.time == 1.2
|
doAssert x.obj.name.time == 1.2
|
||||||
doAssert x.obj.name.scale == 1
|
doAssert x.obj.name.scale == 1
|
||||||
|
|
@ -275,7 +307,6 @@ template main {.dirty.} =
|
||||||
doAssert y.time == 1.2
|
doAssert y.time == 1.2
|
||||||
doAssert y.scale == 1
|
doAssert y.scale == 1
|
||||||
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var x: PrellDeque[int]
|
var x: PrellDeque[int]
|
||||||
doAssert x.pendingTasks == 0
|
doAssert x.pendingTasks == 0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue