fixes #21260; add check for illegal recursion for defaults (#21270)

* fixes #21260; add check for illegal recursion for defaults

* fixes differently
This commit is contained in:
ringabout 2023-01-18 18:52:18 +08:00 • committed by GitHub
commit fc35f83eee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 38 additions and 30 deletions

View file

@ -550,18 +550,17 @@ proc pickCaseBranchIndex(caseExpr, matched: PNode): int =
if endsWithElse: if endsWithElse:
return caseExpr.len - 1 return caseExpr.len - 1
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, id: var IntSet): seq[PNode] proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): 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, aTyp: PType): PNode proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode
proc defaultNodeField(c: PContext, a: PNode): PNode
const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink} const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink}
proc defaultFieldsForTuple(c: PContext, recNode: PNode, id: var IntSet, hasDefault: var bool): seq[PNode] = proc defaultFieldsForTuple(c: PContext, recNode: PNode, 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, id, hasDefault) result.add defaultFieldsForTuple(c, field, 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)
@ -570,7 +569,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, id: var IntSet, hasDefau
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, id) let asgnExpr = defaultNodeField(c, recNode, recNode.typ)
if asgnExpr != nil: if asgnExpr != nil:
hasDefault = true hasDefault = true
asgnExpr.flags.incl nfUseDefaultField asgnExpr.flags.incl nfUseDefaultField
@ -589,11 +588,11 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, id: var IntSet, hasDefau
else: else:
doAssert false doAssert false
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, id: var IntSet): seq[PNode] = proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): 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, id) result.add defaultFieldsForTheUninitialized(c, field)
of nkRecCase: of nkRecCase:
let discriminator = recNode[0] let discriminator = recNode[0]
var selectedBranch: int var selectedBranch: int
@ -607,14 +606,14 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, id: var IntSe
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], id) result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1])
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, recType, id) let asgnExpr = defaultNodeField(c, recNode, recType)
if asgnExpr != nil: if asgnExpr != nil:
asgnExpr.typ = recType asgnExpr.typ = recType
asgnExpr.flags.incl nfUseDefaultField asgnExpr.flags.incl nfUseDefaultField
@ -622,19 +621,17 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, id: var IntSe
else: else:
doAssert false doAssert false
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, id: var IntSet): PNode = proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes) let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
if aTypSkip.kind == tyObject: if aTypSkip.kind == tyObject:
if id.containsOrIncl(aTypSkip.id): let child = defaultFieldsForTheUninitialized(c, aTypSkip.n)
return
let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, id)
if child.len > 0: if child.len > 0:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTypSkip)) var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTypSkip))
asgnExpr.typ = aTypSkip 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], id) let child = defaultNodeField(c, a, aTypSkip[1])
if child != nil: if child != nil:
let node = newNode(nkIntLit) let node = newNode(nkIntLit)
@ -647,20 +644,15 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, id: var IntSet): 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, id, hasDefault) let children = defaultFieldsForTuple(c, aTypSkip.n, 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 =
var s = initIntSet() result = defaultNodeField(c, a, a.typ)
result = defaultNodeField(c, a, a.typ, s)
include semtempl, semgnrc, semstmts, semexprs include semtempl, semgnrc, semstmts, semexprs

View file

@ -75,6 +75,7 @@ type
# overload resolution. # overload resolution.
efNoDiagnostics, efNoDiagnostics,
efTypeAllowed # typeAllowed will be called after efTypeAllowed # typeAllowed will be called after
efWantNoDefaults
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]

View file

@ -20,9 +20,8 @@ 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, id) asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n)
let base = t[0] let base = t[0]
if base == nil: if base == nil:
break break

View file

@ -326,12 +326,15 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
result.status = initUnknown result.status = initUnknown
result.defaults.add newTree(nkExprColonExpr, n, field.ast) result.defaults.add newTree(nkExprColonExpr, n, field.ast)
else: else:
if efWantNoDefaults notin flags: # cannot compute defaults at the typeRightPass
let defaultExpr = defaultNodeField(c, n) let defaultExpr = defaultNodeField(c, n)
if defaultExpr != nil: if defaultExpr != nil:
result.status = initUnknown result.status = initUnknown
result.defaults.add newTree(nkExprColonExpr, n, defaultExpr) result.defaults.add newTree(nkExprColonExpr, n, defaultExpr)
else: else:
result.status = initNone result.status = initNone
else:
result.status = initNone
else: else:
internalAssert c.config, false internalAssert c.config, false
@ -364,7 +367,7 @@ proc initConstrContext(t: PType, initExpr: PNode): ObjConstrContext =
proc computeRequiresInit(c: PContext, t: PType): bool = proc computeRequiresInit(c: PContext, t: PType): bool =
assert t.kind == tyObject assert t.kind == tyObject
var constrCtx = initConstrContext(t, newNode(nkObjConstr)) var constrCtx = initConstrContext(t, newNode(nkObjConstr))
let initResult = semConstructTypeAux(c, constrCtx, {}) let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults})
constrCtx.missingFields.len > 0 constrCtx.missingFields.len > 0
proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
@ -374,7 +377,7 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
assert objType != nil assert objType != nil
if objType.kind == tyObject: if objType.kind == tyObject:
var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info)) var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info))
let initResult = semConstructTypeAux(c, constrCtx, {}) let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults})
if constrCtx.missingFields.len > 0: if constrCtx.missingFields.len > 0:
localError(c.config, info, localError(c.config, info,
"The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)]) "The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)])

13
tests/types/t21260.nim Normal file
View file

@ -0,0 +1,13 @@
discard """
errormsg: "illegal recursion in type 'Foo'"
line: 8
"""
type
Kind = enum kA, kB
Foo = object
case k: Kind:
of kA:
foo: Foo
of kB:
discard