(cherry picked from commit ebfd1c5090)
This commit is contained in:
Andreas Rumpf
2026-08-11 22:27:49 +02:00
committed by narimiran
parent b1f54761e9
commit 13f4ed0d70
4 changed files with 87 additions and 0 deletions

View File

@@ -183,6 +183,12 @@ type
inUncheckedAssignSection*: int
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
forwardFlagUpdates*: seq[(PType, PType)]
# (owner, son) pairs whose `propagateToOwner` ran on a not yet reified
# forward type and has to be redone in the final pass
staleTypeFlags*: IntSet
# ids of the owners in `forwardFlagUpdates`; their flags are provisional
# too, so reading them makes the reader provisional in turn
inTypeofContext*: int
semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.}
@@ -357,6 +363,7 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext =
unknownIdents: initIntSet(),
shadowDiscardedDefs: initIntSet(),
realizedDefs: initIntSet(),
staleTypeFlags: initIntSet(),
cache: graph.cache,
graph: graph,
signatures: initStrTable(),

View File

@@ -1787,6 +1787,23 @@ proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
internalAssert c.config, false
proc typeSectionFinalPass(c: PContext, n: PNode) =
# a son that still was a `tyForward` could not propagate `tfHasAsgn` and
# friends to its owner back then, see `rememberFlagUpdate`. Now that every
# forward declaration has a body, redo those propagations. They are recorded
# in declaration order rather than dependency order and an owner can itself
# be the son of another pair, so repeat until nothing changes; this
# terminates because flags are only ever added.
if c.forwardFlagUpdates.len > 0:
let updates = move c.forwardFlagUpdates
c.staleTypeFlags = initIntSet()
var changed = true
while changed:
changed = false
for (owner, elem) in updates:
let before = owner.flags
propagateToOwner(owner, elem)
if owner.flags != before: changed = true
for i in 0..<n.len:
var a = n[i]
if a.kind == nkCommentStmt: continue

View File

@@ -60,6 +60,18 @@ proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
else:
result = newTypeS(kind, c)
proc rememberFlagUpdate(c: PContext; owner, elem: PType) =
## `propagateToOwner` just derived `owner`'s `tfHasAsgn` & friends from
## `elem`, but inside a type section `elem` can still be an unreified
## `tyForward` which has nothing to derive from yet -- and a type that read
## such a type is provisional in turn. Remember the pair so
## `typeSectionFinalPass` can redo the propagation once every forward
## declaration has a body, the same way `forwardFieldUpdates` defers the
## field defaults.
if elem != nil and (elem.kind == tyForward or elem.id in c.staleTypeFlags):
c.forwardFlagUpdates.add (owner, elem)
c.staleTypeFlags.incl owner.id
proc newConstraint(c: PContext, k: TTypeKind): PType =
result = newTypeS(tyBuiltInTypeClass, c)
result.flags.incl tfCheckedForDestructor
@@ -201,6 +213,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
var base = semTypeNode(c, n[1], nil)
if base.kind == tyTypeDesc: base = base.base # unwrap from type traits like distinctBase
addSonSkipIntLit(result, base, c.idgen)
rememberFlagUpdate(c, result, base)
if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base)
if base.kind notin {tyGenericParam, tyGenericInvocation, tyFromExpr}:
if base.kind == tyForward:
@@ -219,6 +232,7 @@ proc semContainerArg(c: PContext; n: PNode, kindStr: string; result: PType) =
if base.kind == tyVoid:
localError(c.config, n.info, errTIsNotAConcreteType % typeToString(base))
addSonSkipIntLit(result, base, c.idgen)
rememberFlagUpdate(c, result, base)
else:
localError(c.config, n.info, errXExpectsOneTypeParam % kindStr)
addSonSkipIntLit(result, errorType(c), c.idgen)
@@ -311,6 +325,7 @@ proc addSonSkipIntLitChecked(c: PContext; father, son: PType; it: PNode, id: IdG
localError(c.config, it.info, "illegal recursion in type '" & typeToString(s) & "'")
else:
propagateToOwner(father, s)
rememberFlagUpdate(c, father, s)
proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
if n.len == 0: return newConstraint(c, tyDistinct)
@@ -475,6 +490,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
# index type:
result = newOrPrevType(tyArray, prev, c, indx)
addSonSkipIntLit(result, base, c.idgen)
rememberFlagUpdate(c, result, base)
else:
localError(c.config, n.info, errArrayExpectsTwoTypeParams)
result = newOrPrevType(tyError, prev, c)
@@ -557,6 +573,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
fSym.sym.ast.flags.incl nfSkipFieldChecking
result.n.add fSym
addSonSkipIntLit(result, typ, c.idgen)
rememberFlagUpdate(c, result, typ)
styleCheckDef(c, a[j].info, field)
onDef(field.info, field)
if result.n.len == 0: result.n = nil
@@ -917,6 +934,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
n[^1] = firstRange(c.config, typ)
hasDefaultField = true
propagateToOwner(rectype, typ)
rememberFlagUpdate(c, rectype, typ)
var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner
else: rectype.sym
for i in 0..<n.len-2:
@@ -1039,6 +1057,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
if n.kind != nkObjectTy: internalError(c.config, n.info, "semObjectNode")
result = newOrPrevType(tyObject, prev, c)
rawAddSon(result, realBase)
rememberFlagUpdate(c, result, realBase)
if realBase == nil and tfInheritable in flags:
result.flags.incl tfInheritable
if tfAcyclic in flags: result.flags.incl tfAcyclic

View File

@@ -921,3 +921,47 @@ proc mainRegen() =
doAssert b.a.c == right
mainRegen()
from std/typetraits import distinctBase, supportsCopyMem
block: # bug #26025
type
M[B] = distinct seq[B]
W = object
g: U # `U` is only declared below, so it used to be a `tyForward`
# here and `W` ended up without `tfHasAsgn`
U = M[uint64]
doAssert not supportsCopyMem(W)
var h: M[W]
seq[W](h).add W(g: U(@[1'u64]))
var copied = h
for it in items(distinctBase(copied)):
doAssert seq[uint64](it.g) == @[1'u64]
doAssert seq[uint64](seq[W](h)[0].g) == @[1'u64]
block: # bug #26025, the propagation has to reach the indirect owners too
type
M2[B] = distinct seq[B]
ViaArray = object
g: array[2, Late] # the forward type sits inside the field's type
Outer = object # `Inner` is forward here...
a: Inner
Inner = object
b: Late
Late = M2[uint64]
Reader = object # ...whereas `Outer` is already reified but its
z: Outer # own flags were still provisional
AsTuple = tuple[a: Late]
doAssert not supportsCopyMem(ViaArray)
doAssert not supportsCopyMem(Inner)
doAssert not supportsCopyMem(Outer)
doAssert not supportsCopyMem(Reader)
doAssert not supportsCopyMem(AsTuple)