mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-14 02:54:59 +00:00
@@ -186,6 +186,12 @@ type
|
||||
forwardFieldUpdates*: seq[(PType, PNode, PType)]
|
||||
# object/tuple field definitions whose default values mention forward
|
||||
# types and need delayed const checking
|
||||
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.}
|
||||
@@ -369,6 +375,7 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext =
|
||||
unknownIdents: initIntSet(),
|
||||
shadowDiscardedDefs: initIntSet(),
|
||||
realizedDefs: initIntSet(),
|
||||
staleTypeFlags: initIntSet(),
|
||||
cache: graph.cache,
|
||||
graph: graph,
|
||||
signatures: initStrTable(),
|
||||
|
||||
@@ -1837,6 +1837,24 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
for (owner, field, expectedType) in c.forwardFieldUpdates:
|
||||
semDelayedFieldDefault(c, owner, expectedType, field)
|
||||
c.forwardFieldUpdates = @[]
|
||||
|
||||
# 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
|
||||
|
||||
@@ -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.incl tfCheckedForDestructor
|
||||
@@ -221,6 +233,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:
|
||||
@@ -239,6 +252,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)
|
||||
@@ -387,6 +401,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)
|
||||
@@ -560,6 +575,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)
|
||||
@@ -636,6 +652,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
|
||||
@@ -989,6 +1006,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:
|
||||
@@ -1124,6 +1142,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
||||
# the entire object needs to be checked again
|
||||
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) # we retry in the final pass
|
||||
rawAddSon(result, realBase)
|
||||
rememberFlagUpdate(c, result, realBase)
|
||||
if realBase == nil and tfInheritable in flags:
|
||||
result.incl tfInheritable
|
||||
if tfAcyclic in flags: result.incl tfAcyclic
|
||||
|
||||
@@ -937,3 +937,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)
|
||||
|
||||
Reference in New Issue
Block a user