mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-06 13:07:48 +00:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
17
tests/objects/t22301.nim
Normal 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user