mirror of
https://github.com/nim-lang/Nim.git
synced 2026-05-24 21:59:52 +00:00
fix 25667; Generic forward type confusion (#25737)
ref: #25667 drain deferred reification in a loop until there is no more work to do. Could potentially evaluate the same deferred work more than once. --------- Co-authored-by: Andreas Rumpf <araq4k@proton.me>
This commit is contained in:
@@ -180,9 +180,9 @@ type
|
||||
sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index
|
||||
inUncheckedAssignSection*: int
|
||||
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
|
||||
forwardTypeUpdates*: seq[(PType, PNode)]
|
||||
# types that need to be updated in a type section
|
||||
# due to containing forward types, and their corresponding nodes
|
||||
forwardTypeUpdates*: seq[(PSym, PType, PNode)]
|
||||
# top-level owner, type, and type node for delayed retries inside a
|
||||
# type section due to containing forward types
|
||||
forwardFieldUpdates*: seq[(PType, PNode, PType)]
|
||||
# object/tuple field definitions whose default values mention forward
|
||||
# types and need delayed const checking
|
||||
|
||||
@@ -1808,15 +1808,32 @@ proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
|
||||
internalAssert c.config, false
|
||||
|
||||
proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
for (typ, typeNode) in c.forwardTypeUpdates:
|
||||
# types that need to be updated due to containing forward types
|
||||
# and their corresponding type nodes
|
||||
# for example generic invocations of forward types end up here
|
||||
var reified = semTypeNode(c, typeNode, nil)
|
||||
assert reified != nil
|
||||
assignType(typ, reified)
|
||||
typ.itemId = reified.itemId # same id
|
||||
c.forwardTypeUpdates = @[]
|
||||
# each top level type needs to be processed, each epoch should reify at least one
|
||||
var remainingOwners = initIntSet()
|
||||
for (owner, _, _) in c.forwardTypeUpdates:
|
||||
remainingOwners.incl owner.id
|
||||
|
||||
while c.forwardTypeUpdates.len > 0:
|
||||
let pending = move c.forwardTypeUpdates
|
||||
var madeProgress = false
|
||||
|
||||
for (owner, typ, typeNode) in pending:
|
||||
# types that need to be updated due to containing forward types
|
||||
# and their corresponding type nodes
|
||||
# for example generic invocations of forward types end up here
|
||||
var reified = semTypeNode(c, typeNode, nil)
|
||||
assert reified != nil
|
||||
assignType(typ, reified)
|
||||
typ.itemId = reified.itemId # same id
|
||||
if containsForwardType(typ):
|
||||
c.forwardTypeUpdates.add (owner, typ, typeNode)
|
||||
elif not remainingOwners.missingOrExcl(owner.id):
|
||||
madeProgress = true
|
||||
|
||||
if not madeProgress:
|
||||
# can't error here unfortunately
|
||||
break
|
||||
|
||||
for (owner, field, expectedType) in c.forwardFieldUpdates:
|
||||
semDelayedFieldDefault(c, owner, expectedType, field)
|
||||
c.forwardFieldUpdates = @[]
|
||||
|
||||
@@ -223,7 +223,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
||||
if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base)
|
||||
if base.kind notin {tyGenericParam, tyGenericInvocation}:
|
||||
if base.kind == tyForward:
|
||||
c.forwardTypeUpdates.add (base, n[1])
|
||||
c.forwardTypeUpdates.add (getCurrOwner(c), base, n[1])
|
||||
elif not isOrdinalType(base, allowEnumWithHoles = true):
|
||||
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc))
|
||||
elif lengthOrd(c.config, base) > MaxSetElements:
|
||||
@@ -1114,7 +1114,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
||||
if needsForwardUpdate:
|
||||
# if the inherited object is a forward type,
|
||||
# the entire object needs to be checked again
|
||||
c.forwardTypeUpdates.add (result, n) # we retry in the final pass
|
||||
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) # we retry in the final pass
|
||||
rawAddSon(result, realBase)
|
||||
if realBase == nil and tfInheritable in flags:
|
||||
result.incl tfInheritable
|
||||
@@ -1762,7 +1762,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
for i in 1..<n.len:
|
||||
var elem = semGenericParamInInvocation(c, n[i])
|
||||
addToResult(elem, true)
|
||||
c.forwardTypeUpdates.add (result, n)
|
||||
c.forwardTypeUpdates.add (getCurrOwner(c), result, n)
|
||||
return
|
||||
elif t.kind != tyGenericBody:
|
||||
# we likely got code of the form TypeA[TypeB] where TypeA is
|
||||
@@ -1838,7 +1838,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
else:
|
||||
assignType(result, newTypeS(tyForward, c))
|
||||
result.sym = s
|
||||
c.forwardTypeUpdates.add (result, n) #fixes 1500
|
||||
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) #fixes 1500
|
||||
return
|
||||
else:
|
||||
result = instGenericContainer(c, n.info, result,
|
||||
|
||||
@@ -68,3 +68,20 @@ block:
|
||||
let sized = Sized()
|
||||
doAssert sized.files.x == sizeof(Sized)
|
||||
|
||||
block:
|
||||
type
|
||||
Generic[T] = object
|
||||
t: T
|
||||
|
||||
WindowObj = object
|
||||
svgCache: Generic[SVGSVGElement]
|
||||
|
||||
SVGSVGElement = Generic[SVGSVGElementObj]
|
||||
|
||||
SVGSVGElementObj = object
|
||||
|
||||
proc foo() =
|
||||
let p: pointer = nil
|
||||
discard cast[ptr WindowObj](p)
|
||||
|
||||
foo()
|
||||
|
||||
10
tests/types/tforwardcycletimeout.nim
Normal file
10
tests/types/tforwardcycletimeout.nim
Normal file
@@ -0,0 +1,10 @@
|
||||
discard """
|
||||
timeout: "1.0"
|
||||
"""
|
||||
|
||||
type
|
||||
Generic[T] = object
|
||||
t: T
|
||||
|
||||
A = Generic[B]
|
||||
B = Generic[A]
|
||||
Reference in New Issue
Block a user