fixes #22301; fixes #22324; rejects branch initialization with a runtime discriminator with defaults (#22303)

* fixes #22301; rejects branch initialization with a runtime discriminator with defaults

* undefault nimPreviewRangeDefault

* fixes tests

* use oldCheckDefault
This commit is contained in:
ringabout
2023-07-25 18:08:32 +08:00
committed by GitHub
parent dce714b259
commit 1c2ccfad08
6 changed files with 64 additions and 21 deletions

View File

@@ -553,17 +553,17 @@ proc pickCaseBranchIndex(caseExpr, matched: PNode): int =
if endsWithElse:
return caseExpr.len - 1
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode]
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode
proc defaultNodeField(c: PContext, a: PNode): PNode
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode]
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode
proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode
const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink}
proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): seq[PNode] =
proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, checkDefault: bool): seq[PNode] =
case recNode.kind
of nkRecList:
for field in recNode:
result.add defaultFieldsForTuple(c, field, hasDefault)
result.add defaultFieldsForTuple(c, field, hasDefault, checkDefault)
of nkSym:
let field = recNode.sym
let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
@@ -572,7 +572,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): s
result.add newTree(nkExprColonExpr, recNode, field.ast)
else:
if recType.kind in {tyObject, tyArray, tyTuple}:
let asgnExpr = defaultNodeField(c, recNode, recNode.typ)
let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
if asgnExpr != nil:
hasDefault = true
asgnExpr.flags.incl nfSkipFieldChecking
@@ -591,11 +591,11 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): s
else:
doAssert false
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode] =
case recNode.kind
of nkRecList:
for field in recNode:
result.add defaultFieldsForTheUninitialized(c, field)
result.add defaultFieldsForTheUninitialized(c, field, checkDefault)
of nkRecCase:
let discriminator = recNode[0]
var selectedBranch: int
@@ -604,19 +604,21 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
# 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:
if checkDefault: # don't add defaults when checking whether a case branch has default fields
return
defaultValue = newIntNode(nkIntLit#[c.graph]#, 0)
defaultValue.typ = discriminator.typ
selectedBranch = recNode.pickCaseBranchIndex defaultValue
defaultValue.flags.incl nfSkipFieldChecking
result.add newTree(nkExprColonExpr, discriminator, defaultValue)
result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1])
result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1], checkDefault)
of nkSym:
let field = recNode.sym
let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
if field.ast != nil: #Try to use default value
result.add newTree(nkExprColonExpr, recNode, field.ast)
elif recType.kind in {tyObject, tyArray, tyTuple}:
let asgnExpr = defaultNodeField(c, recNode, recNode.typ)
let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
if asgnExpr != nil:
asgnExpr.typ = recNode.typ
asgnExpr.flags.incl nfSkipFieldChecking
@@ -624,17 +626,17 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
else:
doAssert false
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode =
let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
if aTypSkip.kind == tyObject:
let child = defaultFieldsForTheUninitialized(c, aTypSkip.n)
let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault)
if child.len > 0:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
asgnExpr.typ = aTyp
asgnExpr.sons.add child
result = semExpr(c, asgnExpr)
elif aTypSkip.kind == tyArray:
let child = defaultNodeField(c, a, aTypSkip[1])
let child = defaultNodeField(c, a, aTypSkip[1], checkDefault)
if child != nil:
let node = newNode(nkIntLit)
@@ -647,15 +649,15 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
elif aTypSkip.kind == tyTuple:
var hasDefault = false
if aTypSkip.n != nil:
let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault)
let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault)
if hasDefault and children.len > 0:
result = newNodeI(nkTupleConstr, a.info)
result.typ = aTyp
result.sons.add children
result = semExpr(c, result)
proc defaultNodeField(c: PContext, a: PNode): PNode =
result = defaultNodeField(c, a, a.typ)
proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode =
result = defaultNodeField(c, a, a.typ, checkDefault)
include semtempl, semgnrc, semstmts, semexprs

View File

@@ -21,7 +21,7 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
asgnExpr.typ = typ
var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
while true:
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n)
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, false)
let base = t[0]
if base == nil:
break
@@ -647,7 +647,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mDefault:
result = checkDefault(c, n)
let typ = result[^1].typ.skipTypes({tyTypeDesc})
let defaultExpr = defaultNodeField(c, result[^1], typ)
let defaultExpr = defaultNodeField(c, result[^1], typ, false)
if defaultExpr != nil:
result = defaultExpr
of mZeroDefault:

View File

@@ -21,6 +21,7 @@ type
# set this to true while visiting
# parent types.
missingFields: seq[PSym] # Fields that the user failed to specify
checkDefault: bool # Checking defaults
InitStatus = enum # This indicates the result of object construction
initUnknown
@@ -342,6 +343,16 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
# All bets are off. If any of the branches has a mandatory
# fields we must produce an error:
for i in 1..<n.len:
let branchNode = n[i]
if branchNode != nil:
let oldCheckDefault = constrCtx.checkDefault
constrCtx.checkDefault = true
let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags)
constrCtx.checkDefault = oldCheckDefault
if len(defaults) > 0:
localError(c.config, discriminatorVal.info, "branch initialization " &
"with a runtime discriminator is not supported " &
"for a branch whose fields have default values.")
discard collectMissingCaseFields(c, n[i], constrCtx, @[])
of nkSym:
let field = n.sym
@@ -353,7 +364,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
else:
if efWantNoDefaults notin flags: # cannot compute defaults at the typeRightPass
let defaultExpr = defaultNodeField(c, n)
let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault)
if defaultExpr != nil:
result.status = initUnknown
result.defaults.add newTree(nkExprColonExpr, n, defaultExpr)

View File

@@ -21,7 +21,6 @@ cc = gcc
#hint[XDeclaredButNotUsed]=off
threads:on
define:nimPreviewRangeDefault
# Examples of how to setup a cross-compiler:
# Nim can target architectures and OSes different than the local host

17
tests/objects/t22301.nim Normal file
View File

@@ -0,0 +1,17 @@
discard """
errormsg: "branch initialization with a runtime discriminator is not supported for a branch whose fields have default values."
"""
# bug #22301
type
Enum = enum A, B
Object = object
case a: Enum
of A:
integer: int = 200
of B:
time: string
let x = A
let s = Object(a: x)
echo s

View File

@@ -109,4 +109,18 @@ block titerator2:
echo key, ": ", val
for val in fields(co):
echo val
echo val
block:
type
Enum = enum A, B
Object = object
case a: Enum
of A:
integer: int
of B:
time: string
let x = A
let s = Object(a: x)
doAssert s.integer == 0