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:
ringabout 2022-11-15 00:31:06 +08:00 • committed by GitHub
commit 7f2ff909d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 20 deletions

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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