mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-03 14:09:05 +00:00
refactorings: progress
This commit is contained in:
@@ -626,7 +626,14 @@ proc `[]`*(n: PType, i: int): PType {.inline.} =
|
||||
else:
|
||||
n.sonsImpl[i]
|
||||
|
||||
proc `[]=`*(n: PType, i: int; x: PType) {.inline.} =
|
||||
proc replaceSon*(n: PType, i: int; x: PType) {.inline.} =
|
||||
## The single low-level "replace son `i` in place" primitive. All in-place son
|
||||
## mutation funnels through here -- call sites go via `TypeBuilder.setSon`
|
||||
## (`typebuilders.nim`, the reopen/mutable-staging seam); this is its backing.
|
||||
## The `PType.[]=` operators used to do this inline; they were removed so that
|
||||
## son replacement is named and greppable, and the PType->NifCursor swap (where
|
||||
## this becomes a token rewrite over a thawed cursor) touches one proc, not
|
||||
## every caller.
|
||||
if n.state == Partial: loadType(n)
|
||||
if n.kind == tyProc and i > 0:
|
||||
assert n.nImpl[i] != nil and n.nImpl[i].sym != nil
|
||||
@@ -638,9 +645,9 @@ proc `[]`*(n: PType, i: BackwardsIndex): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n[n.sonsImpl.len - i.int]
|
||||
|
||||
proc `[]=`*(n: PType, i: BackwardsIndex; x: PType) {.inline.} =
|
||||
proc replaceSon*(n: PType, i: BackwardsIndex; x: PType) {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n[n.sonsImpl.len - i.int] = x
|
||||
replaceSon(n, n.sonsImpl.len - i.int, x)
|
||||
|
||||
proc getDeclPragma*(n: PNode): PNode =
|
||||
## return the `nkPragma` node for declaration `n`, or `nil` if no pragma was found.
|
||||
@@ -1165,7 +1172,7 @@ proc assignType*(dest, src: PType) =
|
||||
dest.sonsImpl[0] = src.sonsImpl[0]
|
||||
else:
|
||||
newSons(dest, src.len)
|
||||
for i in 0..<src.len: dest[i] = src[i]
|
||||
for i in 0..<src.len: replaceSon(dest, i, src[i])
|
||||
|
||||
proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType =
|
||||
result = newType(t.kind, idgen, owner)
|
||||
|
||||
@@ -164,13 +164,6 @@ template fieldCheck {.dirty.} =
|
||||
echo "missed field ", field.name.s
|
||||
writeStackTrace()
|
||||
|
||||
proc rawAddField*(obj: PType; field: PSym) =
|
||||
assert field.kind == skField
|
||||
field.position = obj.n.len
|
||||
obj.n.add newSymNode(field)
|
||||
propagateToOwner(obj, field.typ)
|
||||
fieldCheck()
|
||||
|
||||
proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
|
||||
# returns a[].field as a node
|
||||
assert field.kind == skField
|
||||
@@ -239,40 +232,70 @@ proc lookupCapturedField(n: PNode, s: PSym): PSym =
|
||||
result = n.sym
|
||||
else: discard
|
||||
|
||||
proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym =
|
||||
# Idempotent w.r.t. the captured symbol (mirrors `addUniqueField`): re-lifting
|
||||
# a LOADED routine re-derives its transformed body (never serialized under IC)
|
||||
# and re-captures the same locals, but the env object loaded from the NIF
|
||||
# already carries their fields. Re-adding would duplicate the field and, worse,
|
||||
# mutate a Sealed loaded type via `propagateToOwner` (the `t.state != Sealed`
|
||||
# crash). Return the existing field instead.
|
||||
let existing = lookupInRecord(obj.n, s.itemId)
|
||||
if existing != nil:
|
||||
return existing
|
||||
# Re-lifting a LOADED routine during a VM transform (its transformed body is
|
||||
# re-derived per process, never serialized) re-captures the same locals, but
|
||||
# for a macro-generated gensym (e.g. libp2p `p2pProtocolBackendImpl`'s
|
||||
# `msgVar`) its process-local id diverges from the one baked into the loaded
|
||||
# env field, so the id match above misses. Reuse the existing same-named field
|
||||
# rather than appending a divergent duplicate, which keeps the re-derived
|
||||
# closure consistent (else a stale `:env` access reaches `cannotEval`).
|
||||
# Confined to a loaded (Sealed) env: in a freshly built env ids are consistent,
|
||||
# and two distinct same-named captures legitimately get distinct fields there.
|
||||
if obj.state == Sealed:
|
||||
let byName = lookupCapturedField(obj.n, s)
|
||||
if byName != nil:
|
||||
return byName
|
||||
# Genuinely new field. Under IC the env may be a loaded Sealed type whose
|
||||
# transform-time mutation is process-local (the body is discarded after the
|
||||
# macro runs), so downgrade it to mutable instead of crashing on
|
||||
# `t.state != Sealed` (mirrors `markAsClosure`).
|
||||
type
|
||||
ObjectBuilder* = object
|
||||
## Extends an existing object type with record fields -- the deferred
|
||||
## object-BODY counterpart to `typebuilders.TypeBuilder`. The object's
|
||||
## identity is fixed (a shell from `createObj` or a type loaded from NIF);
|
||||
## only its `nkRecList` body grows, possibly after thawing a loaded Sealed
|
||||
## type. Lives here rather than in `typebuilders.nim` because it needs the
|
||||
## record-walk reuse lookups above; hoist it once those move.
|
||||
## See `doc/ic_type_body_builder.md` for the NIF-cursor migration story.
|
||||
obj {.cursor.}: PType
|
||||
cache {.cursor.}: IdentCache
|
||||
idgen {.cursor.}: IdGenerator
|
||||
|
||||
proc reopenObject*(obj: PType; cache: IdentCache; idgen: IdGenerator): ObjectBuilder {.inline.} =
|
||||
## Positions a builder to append fields to `obj`, keeping its identity. Does
|
||||
## not thaw yet: the idempotency lookups must observe the pre-thaw `Sealed`
|
||||
## state first (see `findField`).
|
||||
ObjectBuilder(obj: obj, cache: cache, idgen: idgen)
|
||||
|
||||
proc findField*(b: ObjectBuilder; s: PSym; byName: bool): PSym =
|
||||
## The idempotency lookup, load-bearing for correctness (not a fast path):
|
||||
## re-lifting a LOADED routine re-derives its transformed body per process and
|
||||
## re-captures the same locals, but the loaded env already carries their
|
||||
## fields -- re-adding would duplicate and mutate Sealed memory.
|
||||
##
|
||||
## By derived item id first. Then, for a loaded (`Sealed`) body and when
|
||||
## `byName`, by the stable name+position key: a macro-generated gensym (e.g.
|
||||
## libp2p `p2pProtocolBackendImpl`'s `msgVar`) has a process-local id that
|
||||
## diverges from the one baked into the loaded env field, so the id match
|
||||
## misses; the same-named field is reused instead of appending a divergent
|
||||
## duplicate (else a stale `:env` access reaches `cannotEval`). A freshly
|
||||
## built env keeps consistent ids, so two same-named captures there
|
||||
## legitimately get distinct fields -- hence the `Sealed`-only gate.
|
||||
result = lookupInRecord(b.obj.n, s.itemId)
|
||||
if result != nil: return
|
||||
if byName and b.obj.state == Sealed:
|
||||
result = lookupCapturedField(b.obj.n, s)
|
||||
|
||||
proc appendField*(b: var ObjectBuilder; field: PSym) =
|
||||
## Low-level append of a prebuilt `skField` (replaces `rawAddField`): set its
|
||||
## position, append it, fold its type into the object.
|
||||
assert field.kind == skField
|
||||
let obj = b.obj
|
||||
field.position = obj.n.len
|
||||
obj.n.add newSymNode(field)
|
||||
propagateToOwner(obj, field.typ)
|
||||
fieldCheck()
|
||||
|
||||
proc captureField*(b: var ObjectBuilder; s: PSym): PSym {.discardable.} =
|
||||
## Idempotent capture of local `s` (= `addField`). On a `findField` miss,
|
||||
## thaws the env if needed then mints the field. Under IC the env may be a
|
||||
## loaded Sealed type whose transform-time mutation is process-local (the body
|
||||
## is discarded after the macro runs), so `unsealForTransform` downgrades it to
|
||||
## mutable instead of crashing on `t.state != Sealed` (mirrors `markAsClosure`).
|
||||
result = b.findField(s, byName = true)
|
||||
if result != nil: return
|
||||
let obj = b.obj
|
||||
unsealForTransform(obj)
|
||||
# because of 'gensym' support, we have to mangle the name with its ID.
|
||||
# This is hacky but the clean solution is much more complex than it looks.
|
||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
|
||||
idgen, s.owner, s.info, s.options)
|
||||
var field = newSym(skField, getIdent(b.cache, s.name.s & $obj.n.len),
|
||||
b.idgen, s.owner, s.info, s.options)
|
||||
field.itemId = derivedFieldId(s.itemId)
|
||||
let t = skipIntLit(s.typ, idgen)
|
||||
let t = skipIntLit(s.typ, b.idgen)
|
||||
field.typ = t
|
||||
if s.kind in {skLet, skVar, skField, skForVar}:
|
||||
#field.bitsize = s.bitsize
|
||||
@@ -286,19 +309,44 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym
|
||||
fieldCheck()
|
||||
result = field
|
||||
|
||||
proc captureUniqueField*(b: var ObjectBuilder; s: PSym): PSym {.discardable.} =
|
||||
## `addUniqueField`: idempotent by item id ONLY (no name fallback, no thaw,
|
||||
## no alignment/flag copy).
|
||||
result = b.findField(s, byName = false)
|
||||
if result != nil: return
|
||||
let obj = b.obj
|
||||
var field = newSym(skField, getIdent(b.cache, s.name.s & $obj.n.len),
|
||||
b.idgen, s.owner, s.info, s.options)
|
||||
field.itemId = derivedFieldId(s.itemId)
|
||||
let t = skipIntLit(s.typ, b.idgen)
|
||||
field.typ = t
|
||||
assert t.kind != tyTyped
|
||||
propagateToOwner(obj, t)
|
||||
field.position = obj.n.len
|
||||
obj.n.add newSymNode(field)
|
||||
result = field
|
||||
|
||||
proc finishObject*(b: sink ObjectBuilder) {.inline.} =
|
||||
## Publish the completed body. A no-op today (the thawed env stays `Complete`,
|
||||
## process-local, never re-serialized); the seam where the NIF backend will
|
||||
## `beginRead` the record buffer into a read-only cursor and republish it
|
||||
## under the object's SymId.
|
||||
discard
|
||||
|
||||
proc rawAddField*(obj: PType; field: PSym) =
|
||||
var b = reopenObject(obj, nil, nil) # prebuilt field: cache/idgen unused
|
||||
b.appendField(field)
|
||||
finishObject b
|
||||
|
||||
proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym =
|
||||
var b = reopenObject(obj, cache, idgen)
|
||||
result = b.captureField(s)
|
||||
finishObject b
|
||||
|
||||
proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
|
||||
result = lookupInRecord(obj.n, s.itemId)
|
||||
if result == nil:
|
||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), idgen,
|
||||
s.owner, s.info, s.options)
|
||||
field.itemId = derivedFieldId(s.itemId)
|
||||
let t = skipIntLit(s.typ, idgen)
|
||||
field.typ = t
|
||||
assert t.kind != tyTyped
|
||||
propagateToOwner(obj, t)
|
||||
field.position = obj.n.len
|
||||
obj.n.add newSymNode(field)
|
||||
result = field
|
||||
var b = reopenObject(obj, cache, idgen)
|
||||
result = b.captureUniqueField(s)
|
||||
finishObject b
|
||||
|
||||
proc newDotExpr*(obj, b: PSym): PNode =
|
||||
result = newNodeI(nkDotExpr, obj.info)
|
||||
|
||||
@@ -183,7 +183,8 @@ proc commonType*(c: PContext; x, y: PType): PType =
|
||||
nt = copyType(a, c.idgen, a.owner)
|
||||
copyTypeProps(c.graph, c.idgen.module, nt, a)
|
||||
|
||||
nt[i] = if aEmpty: bb else: aa
|
||||
var ntb = reopen(nt)
|
||||
ntb.setSon(i, if aEmpty: bb else: aa)
|
||||
if not nt.isNil: result = nt
|
||||
#elif b[idx].kind == tyEmpty: return x
|
||||
elif a.kind == tyRange and b.kind == tyRange:
|
||||
|
||||
@@ -2250,7 +2250,8 @@ proc semYield(c: PContext, n: PNode): PNode =
|
||||
|
||||
if resultTypeIsInferrable(restype):
|
||||
let inferred = n[0].typ
|
||||
iterType[0] = inferred
|
||||
var b = reopen(iterType)
|
||||
b.setSon(0, inferred)
|
||||
if c.p.resultSym != nil:
|
||||
c.p.resultSym.typ = inferred
|
||||
else:
|
||||
|
||||
@@ -24,7 +24,7 @@ when defined(nimPreviewSlimSystem):
|
||||
proc errorType*(g: ModuleGraph): PType =
|
||||
## creates a type representing an error state
|
||||
result = newType(tyError, g.idgen, g.owners[^1])
|
||||
result.flagsImpl.incl tfCheckedForDestructor
|
||||
result.incl tfCheckedForDestructor
|
||||
|
||||
proc getIntLitTypeG(g: ModuleGraph; literal: PNode; idgen: IdGenerator): PType =
|
||||
# we cache some common integer literal types for performance:
|
||||
|
||||
@@ -436,7 +436,8 @@ proc semUnown(c: PContext; n: PNode): PNode =
|
||||
result = copyType(t, c.idgen, t.owner)
|
||||
copyTypeProps(c.graph, c.idgen.module, result, t)
|
||||
|
||||
result[^1] = b
|
||||
var rb = reopen(result)
|
||||
rb.setSon(^1, b)
|
||||
result.excl tfHasOwned
|
||||
else:
|
||||
result = t
|
||||
|
||||
@@ -1152,10 +1152,12 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
case iter[i].kind
|
||||
of tyVar:
|
||||
mutable = true
|
||||
iter[i] = iter[i].skipTypes({tyVar})
|
||||
var b = reopen(iter)
|
||||
b.setSon(i, iter[i].skipTypes({tyVar}))
|
||||
of tyLent:
|
||||
isLent = true
|
||||
iter[i] = iter[i].skipTypes({tyLent})
|
||||
var b = reopen(iter)
|
||||
b.setSon(i, iter[i].skipTypes({tyLent}))
|
||||
else: discard
|
||||
|
||||
if n[i].len-1 != iter[i].len:
|
||||
@@ -1680,7 +1682,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
# object might have been assumed to be final
|
||||
if tfInheritable in oldFlags and tfFinal in body.flags:
|
||||
excl(body, tfFinal)
|
||||
s.typ[^1] = body
|
||||
var b = reopen(s.typ)
|
||||
b.setSon(^1, body)
|
||||
if tfCovariant in s.typ.flags:
|
||||
checkCovariantParamsUsages(c, s.typ)
|
||||
# XXX: This is a temporary limitation:
|
||||
|
||||
@@ -56,6 +56,17 @@ proc openType(c: PContext; kind: TTypeKind; prev: PType): TypeBuilder =
|
||||
else:
|
||||
result = openType(c, kind)
|
||||
|
||||
proc openPair(c: PContext; kind: TTypeKind; prev: PType): TypePairBuilder =
|
||||
## Prev-aware deferred (`TypePair`) open -- the deferred analogue of the
|
||||
## prev-aware `openType` above, for types whose identity is published before
|
||||
## their body is finished. Keeps a forward/partial `prev`'s reserved name,
|
||||
## else mints a fresh identity at the same sequence point as `newTypeS`.
|
||||
if reusePrev(prev):
|
||||
if prev.kind == tyForward: prev.kind = kind
|
||||
result = reopenPair(prev, c.idgen)
|
||||
else:
|
||||
result = openPair(kind, c.idgen, getCurrOwner(c))
|
||||
|
||||
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext, son: sink PType): PType =
|
||||
if reusePrev(prev):
|
||||
result = prev
|
||||
@@ -420,11 +431,17 @@ proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
|
||||
proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
assert isRange(n)
|
||||
checkSonsLen(n, 3, c.config)
|
||||
result = newOrPrevType(tyRange, prev, c)
|
||||
result.n = newNodeI(nkRange, n.info)
|
||||
# Deferred build: a *valid* tyRange must exist before the throwing
|
||||
# `semExprWithType` below (bug #6895), so its base type is minted as an
|
||||
# `errorType` placeholder son up front and back-patched via `setSon(0, …)`
|
||||
# once the real bounds are known. The `.n` (nkRange bound exprs) and flags
|
||||
# stay direct pokes on the live shell, as in `semProcTypeNode`.
|
||||
var rb = openPair(c, tyRange, prev)
|
||||
rb.setN newNodeI(nkRange, n.info)
|
||||
# always create a 'valid' range type, but overwrite it later
|
||||
# because 'semExprWithType' can raise an exception. See bug #6895.
|
||||
addSonSkipIntLit(result, errorType(c), c.idgen)
|
||||
rb.add errorType(c)
|
||||
result = rb.pair.decl
|
||||
|
||||
if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty):
|
||||
localError(c.config, n.info, "range is empty")
|
||||
@@ -465,7 +482,9 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
if weakLeValue(result.n[0], result.n[1]) == impNo:
|
||||
localError(c.config, n.info, "range is empty")
|
||||
|
||||
result[0] = rangeT[0]
|
||||
# overwrite the placeholder son minted above with the real base type, then seal
|
||||
rb.setSon(0, rangeT[0])
|
||||
result = finishPair(rb).decl
|
||||
|
||||
proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = nil
|
||||
@@ -627,8 +646,12 @@ proc firstRange(config: ConfigRef, t: PType): PNode =
|
||||
|
||||
proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
var typ: PType
|
||||
result = newOrPrevType(tyTuple, prev, c)
|
||||
result.n = newNodeI(nkRecList, n.info)
|
||||
# Deferred build: the tuple's identity is handed to `semFieldDefault` (which
|
||||
# propagates each default field's type into the owner) while its fields/sons
|
||||
# are still being appended -- so it goes through `TypePairBuilder`, publishing
|
||||
# `rb.pair` mid-build rather than `openType ... finish`.
|
||||
var rb = openPair(c, tyTuple, prev)
|
||||
rb.setN newNodeI(nkRecList, n.info)
|
||||
var check = initIntSet()
|
||||
var counter = 0
|
||||
for i in ord(n.kind == nkBracketExpr)..<n.len:
|
||||
@@ -638,7 +661,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
var hasDefaultField = a[^1].kind != nkEmpty
|
||||
if hasDefaultField:
|
||||
typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil
|
||||
typ = semFieldDefault(c, result, typ, a)
|
||||
typ = semFieldDefault(c, rb.pair.decl, typ, a)
|
||||
elif a[^2].kind != nkEmpty:
|
||||
typ = semTypeNode(c, a[^2], nil)
|
||||
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
|
||||
@@ -659,11 +682,12 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
if hasDefaultField:
|
||||
fSym.sym.ast = a[^1]
|
||||
fSym.sym.ast.flags.incl nfSkipFieldChecking
|
||||
result.n.add fSym
|
||||
addSonSkipIntLit(result, typ, c.idgen)
|
||||
rb.addRecField fSym
|
||||
rb.add typ
|
||||
styleCheckDef(c, a[j].info, field)
|
||||
onDef(field.info, field)
|
||||
if result.n.len == 0: result.n = nil
|
||||
if rb.pair.decl.n.len == 0: rb.setN nil
|
||||
result = finishPair(rb).decl
|
||||
if isRecursiveStructuralType(result):
|
||||
localError(c.config, n.info, errIllegalRecursionInTypeX % typeToString(result))
|
||||
|
||||
@@ -1143,17 +1167,24 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
||||
base = nil
|
||||
realBase = nil
|
||||
if n.kind != nkObjectTy: internalError(c.config, n.info, "semObjectNode")
|
||||
result = newOrPrevType(tyObject, prev, c)
|
||||
# Deferred build: the object's identity is published to `forwardTypeUpdates`
|
||||
# (a retry pass), to `semRecordNodeAux` (field sem may reference the object
|
||||
# itself), and to the pragma dummy sym -- all before its body is complete. The
|
||||
# son-tree (base son) + initial `.n` (nkRecList) allocation + seal go through
|
||||
# the builder; field growth (via `semRecordNodeAux` into `result.n`) and flags
|
||||
# stay direct pokes on the live shell, as in `semProcTypeNode`/`semRangeAux`.
|
||||
var rb = openPair(c, tyObject, prev)
|
||||
result = rb.pair.decl
|
||||
if needsForwardUpdate:
|
||||
# if the inherited object is a forward type,
|
||||
# the entire object needs to be checked again
|
||||
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) # we retry in the final pass
|
||||
rawAddSon(result, realBase)
|
||||
rb.addRaw realBase
|
||||
if realBase == nil and tfInheritable in flags:
|
||||
result.incl tfInheritable
|
||||
if tfAcyclic in flags: result.incl tfAcyclic
|
||||
if result.n.isNil:
|
||||
result.n = newNodeI(nkRecList, n.info)
|
||||
rb.setN newNodeI(nkRecList, n.info)
|
||||
else:
|
||||
# partial object so add things to the check
|
||||
if not tryAddInheritedFields(c, check, pos, result, n, isPartial = true):
|
||||
@@ -1169,6 +1200,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
||||
incl(result, tfFinal)
|
||||
if c.inGenericContext == 0 and computeRequiresInit(c, result):
|
||||
result.incl tfRequiresInit
|
||||
result = finishPair(rb).decl # seal the deferred object build
|
||||
|
||||
proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
|
||||
if n.len < 1:
|
||||
@@ -1359,7 +1391,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
for i in 0..<paramType.len:
|
||||
let t = recurse(paramType[i])
|
||||
if t != nil:
|
||||
paramType[i] = t
|
||||
var b = reopen(paramType)
|
||||
b.setSon(i, t)
|
||||
result = paramType
|
||||
|
||||
of tyAlias, tyOwned:
|
||||
@@ -1387,32 +1420,38 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
globalError(c.config, info, errIllegalRecursionInTypeX % typeToString(paramType))
|
||||
var lifted = recurse(paramType[i])
|
||||
if lifted != nil:
|
||||
paramType[i] = lifted
|
||||
var b = reopen(paramType)
|
||||
b.setSon(i, lifted)
|
||||
result = paramType
|
||||
|
||||
of tyGenericBody:
|
||||
result = newTypeS(tyGenericInvocation, c)
|
||||
result.rawAddSon(paramType)
|
||||
# A user-type-class body instantiates to a tyUserTypeClassInst, everything
|
||||
# else to a tyGenericInvocation. The kind is decided up front (from the
|
||||
# already-complete `paramType`), so the builder opens with the final tag
|
||||
# rather than the old mint-as-invocation-then-mutate-kind dance.
|
||||
let isUserTypeClass = paramType.typeBodyImpl.kind == tyUserTypeClass
|
||||
var b = openType(c, if isUserTypeClass: tyUserTypeClassInst else: tyGenericInvocation)
|
||||
b.addRaw paramType
|
||||
|
||||
for i in 0..<paramType.len - 1:
|
||||
if paramType[i].kind == tyStatic:
|
||||
var staticCopy = paramType[i].exactReplica(c.idgen)
|
||||
staticCopy.incl tfInferrableStatic
|
||||
result.rawAddSon staticCopy
|
||||
b.addRaw staticCopy
|
||||
else:
|
||||
result.rawAddSon newTypeS(tyAnything, c)
|
||||
b.addRaw newTypeS(tyAnything, c)
|
||||
|
||||
if paramType.typeBodyImpl.kind == tyUserTypeClass:
|
||||
result.kind = tyUserTypeClassInst
|
||||
result.rawAddSon paramType.typeBodyImpl
|
||||
return addImplicitGeneric(c, result, paramTypId, info, genericParams, paramName)
|
||||
if isUserTypeClass:
|
||||
b.addRaw paramType.typeBodyImpl
|
||||
return addImplicitGeneric(c, finish b, paramTypId, info, genericParams, paramName)
|
||||
|
||||
result = finish b
|
||||
let x = instGenericContainer(c, paramType.sym.info, result,
|
||||
allowMetaTypes = true)
|
||||
result = newTypeS(tyCompositeTypeClass, c)
|
||||
result.rawAddSon paramType
|
||||
result.rawAddSon x
|
||||
result = addImplicitGeneric(c, result, paramTypId, info, genericParams, paramName)
|
||||
var cb = openType(c, tyCompositeTypeClass)
|
||||
cb.addRaw paramType
|
||||
cb.addRaw x
|
||||
result = addImplicitGeneric(c, finish cb, paramTypId, info, genericParams, paramName)
|
||||
|
||||
of tyGenericInst:
|
||||
result = nil
|
||||
@@ -1426,7 +1465,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
for i in 1..<paramType.len-1:
|
||||
var lifted = recurse(paramType[i])
|
||||
if lifted != nil:
|
||||
paramType[i] = lifted
|
||||
var b = reopen(paramType)
|
||||
b.setSon(i, lifted)
|
||||
result = paramType
|
||||
result.last.shouldHaveMeta
|
||||
if paramType.isConcept:
|
||||
@@ -1443,7 +1483,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
for i in 1..<paramType.len:
|
||||
#if paramType[i].kind != tyTypeDesc:
|
||||
let lifted = recurse(paramType[i])
|
||||
if lifted != nil: paramType[i] = lifted
|
||||
if lifted != nil:
|
||||
var b = reopen(paramType)
|
||||
b.setSon(i, lifted)
|
||||
|
||||
let body = paramType.base
|
||||
if body.kind in {tyForward, tyError}:
|
||||
@@ -1504,7 +1546,14 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
# for historical reasons (code grows) this is invoked for parameter
|
||||
# lists too and then 'isType' is false.
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
# Deferred build: `newProcType` opens the shell with a nil return-type slot
|
||||
# (son 0) and an effect-list `.n`; params are appended as interleaved son +
|
||||
# `.n` entries below, and son 0 is back-patched once the return type is known.
|
||||
# `openType ... finish` cannot model the placeholder-then-backpatch, so the
|
||||
# son tree grows through a `TypePairBuilder` reopened on the shell. Flags and
|
||||
# `.n.typ` remain direct pokes on the live shell (`result` == `rb.pair.decl`).
|
||||
result = newProcType(c, n.info, prev)
|
||||
var rb = reopenPair(result, c.idgen)
|
||||
var check = initIntSet()
|
||||
var counter = 0
|
||||
template isCurrentlyGeneric: bool =
|
||||
@@ -1642,8 +1691,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
inc(counter)
|
||||
if def != nil and def.kind != nkEmpty:
|
||||
arg.ast = copyTree(def)
|
||||
result.n.add newSymNode(arg)
|
||||
rawAddSon(result, finalType)
|
||||
rb.addRecField newSymNode(arg)
|
||||
rb.addRaw finalType
|
||||
addParamOrResult(c, arg, kind)
|
||||
styleCheckDef(c, a[j].info, arg)
|
||||
onDef(a[j].info, arg)
|
||||
@@ -1703,7 +1752,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
# we don't need to change the return type to iter[T]
|
||||
result.incl tfIterator
|
||||
# XXX Would be nice if we could get rid of this
|
||||
result[0] = r
|
||||
rb.setSon(0, r)
|
||||
let oldFlags = result.flags
|
||||
propagateToOwner(result, r)
|
||||
if oldFlags != result.flags:
|
||||
@@ -1721,6 +1770,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
n.sym.transitionGenericParamToType()
|
||||
n.sym.typ.excl tfWildcard
|
||||
|
||||
result = finishPair(rb).decl # seal the deferred proc-type build
|
||||
|
||||
proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
for i in 0..<n.len - 1:
|
||||
@@ -1789,24 +1840,32 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
var t = s.typ.skipTypes({tyAlias})
|
||||
if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
|
||||
t = t.base
|
||||
result = newOrPrevType(tyGenericInvocation, prev, c)
|
||||
addSonSkipIntLit(result, t, c.idgen)
|
||||
# Deferred build: the tyGenericInvocation's identity is published to
|
||||
# `forwardTypeUpdates` (a retry pass) and consumed by `instGenericContainer`,
|
||||
# both only after its arg sons are appended. The son-tree goes through one
|
||||
# deferred `rb`; `result` stays the live shell (later branches may replace it
|
||||
# with an error/forward type or the instantiated container). Sealed once the
|
||||
# args are in, before any consumer reads it.
|
||||
var rb = openPair(c, tyGenericInvocation, prev)
|
||||
result = rb.pair.decl
|
||||
rb.add t
|
||||
|
||||
template addToResult(typ, skip) =
|
||||
|
||||
if typ.isNil:
|
||||
internalAssert c.config, false
|
||||
rawAddSon(result, typ)
|
||||
rb.addRaw typ
|
||||
else:
|
||||
if skip:
|
||||
addSonSkipIntLit(result, typ, c.idgen)
|
||||
rb.add typ
|
||||
else:
|
||||
rawAddSon(result, makeRangeWithStaticExpr(c, typ.n))
|
||||
rb.addRaw makeRangeWithStaticExpr(c, typ.n)
|
||||
|
||||
if t.kind == tyForward:
|
||||
for i in 1..<n.len:
|
||||
var elem = semGenericParamInInvocation(c, n[i])
|
||||
addToResult(elem, true)
|
||||
result = finishPair(rb).decl # seal the deferred invocation build
|
||||
c.forwardTypeUpdates.add (getCurrOwner(c), result, n)
|
||||
return
|
||||
elif t.kind != tyGenericBody:
|
||||
@@ -1854,6 +1913,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
if typ.kind == tyForward:
|
||||
hasForwardTypeParam = true
|
||||
|
||||
result = finishPair(rb).decl # seal the deferred invocation build (args complete)
|
||||
|
||||
if isConcrete:
|
||||
if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
|
||||
# XXX: What kind of error is this? is it still relevant?
|
||||
@@ -2440,8 +2501,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
let old = result
|
||||
result = copyType(result, c.idgen, getCurrOwner(c))
|
||||
copyTypeProps(c.graph, c.idgen.module, result, old)
|
||||
var b = reopen(result, c.idgen)
|
||||
for i in 1..<n.len:
|
||||
result.rawAddSon(semTypeNode(c, n[i], nil))
|
||||
b.addRaw(semTypeNode(c, n[i], nil))
|
||||
of mDistinct:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
var b = openType(c, tyDistinct, prev)
|
||||
|
||||
@@ -57,12 +57,17 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
|
||||
|
||||
return inst
|
||||
|
||||
proc cacheTypeInst(c: PContext; inst: PType) =
|
||||
let gt = inst[0]
|
||||
proc cacheTypeInst(c: PContext; inst: TypePair) =
|
||||
# Publishes an in-progress instance under its name, for recursive
|
||||
# instantiations. Takes the (identity, tree) pair rather than a bare `PType`:
|
||||
# the cache key is derived from the generic head's identity, and only the
|
||||
# instance's identity is registered -- today via `inst.decl`, under NIF via
|
||||
# `inst.id`.
|
||||
let gt = inst.decl[0]
|
||||
let t = if gt.kind == tyGenericBody: gt.typeBodyImpl else: gt
|
||||
if t.kind in {tyStatic, tyError, tyGenericParam} + tyTypeClasses:
|
||||
return
|
||||
addToGenericCache(c, gt.sym, inst)
|
||||
addToGenericCache(c, gt.sym, inst.decl)
|
||||
|
||||
type
|
||||
TReplTypeVars* = object
|
||||
@@ -466,7 +471,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
x = lookupTypeVar(cl, x)
|
||||
if x != nil:
|
||||
if header == t: header = instCopyType(cl, t)
|
||||
header[i] = x
|
||||
var hb = reopen(header)
|
||||
hb.setSon(i, x)
|
||||
propagateToOwner(header, x)
|
||||
else:
|
||||
# Under IC `t` may be a loaded dep type (Sealed/immutable); mutating it
|
||||
@@ -493,16 +499,17 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
# the generic body's module (`t.genericHead.owner`) has no business owning a
|
||||
# type that references instantiation-site types — that is the IC parent->child
|
||||
# heap leak the write-barrier surfaces.
|
||||
result = newType(tyGenericInst, cl.c.idgen, cl.c.module, son = header.genericHead)
|
||||
result.flags = header.flags
|
||||
var rb = openPair(tyGenericInst, cl.c.idgen, cl.c.module, son = header.genericHead)
|
||||
rb.setFlags header.flags
|
||||
# be careful not to propagate unnecessary flags here (don't use rawAddSon)
|
||||
# ugh need another pass for deeply recursive generic types (e.g. PActor)
|
||||
# we need to add the candidate here, before it's fully instantiated for
|
||||
# recursive instantions:
|
||||
# recursive instantions: publish the instance's *identity* (`rb.pair`) while
|
||||
# its body is still open, so recursive instantiations find it under its name.
|
||||
if not cl.allowMetaTypes:
|
||||
cacheTypeInst(cl.c, result)
|
||||
cacheTypeInst(cl.c, rb.pair)
|
||||
else:
|
||||
cl.localCache[t.itemId] = result
|
||||
cl.localCache[t.itemId] = rb.pair.decl
|
||||
|
||||
let oldSkipTypedesc = cl.skipTypedesc
|
||||
cl.skipTypedesc = true
|
||||
@@ -516,17 +523,18 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
else:
|
||||
header[i]
|
||||
assert x.kind != tyGenericInvocation
|
||||
header[i] = x
|
||||
var hb = reopen(header)
|
||||
hb.setSon(i, x)
|
||||
propagateToOwner(header, x)
|
||||
cl.typeMap.put(body[i-1], x)
|
||||
|
||||
for i in FirstGenericParamAt..<t.kidsLen:
|
||||
# if one of the params is not concrete, we cannot do anything
|
||||
# but we already raised an error!
|
||||
rawAddSon(result, header[i], propagateHasAsgn = false)
|
||||
rb.addRaw(header[i], propagateHasAsgn = false)
|
||||
|
||||
if body.kind == tyError:
|
||||
return
|
||||
return finishPair(rb).decl
|
||||
|
||||
let bbody = last body
|
||||
var newbody = replaceTypeVarsT(cl, bbody, isInstValue = true)
|
||||
@@ -538,7 +546,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
# builtin like `int` when the generic's body is computed by a macro) and is
|
||||
# immutable under IC. Skip the in-place flag accumulation on the shared
|
||||
# type; the instance `result` still receives the flags below.
|
||||
result.flags = result.flags + newbodyFlags - tfInstClearedFlags
|
||||
rb.setFlags(rb.flags + newbodyFlags - tfInstClearedFlags)
|
||||
|
||||
setToPreviousLayer(cl.typeMap)
|
||||
|
||||
@@ -549,7 +557,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
# handleGenericInvocation will handle the alias-to-alias-to-alias case
|
||||
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
|
||||
|
||||
rawAddSon(result, newbody)
|
||||
rb.addRaw newbody
|
||||
result = finishPair(rb).decl
|
||||
checkPartialConstructedType(cl.c.config, cl.info, newbody)
|
||||
if not cl.allowMetaTypes:
|
||||
let dc = cl.c.graph.getAttachedOp(newbody, attachedDeepCopy)
|
||||
@@ -614,10 +623,11 @@ proc eraseTupleVoidFields*(t: PType) =
|
||||
if t.n[i].kind == nkRecList or t[i].kind == tyVoid:
|
||||
# found first void field, compact from here
|
||||
var pos = i
|
||||
var b = reopen(t)
|
||||
for j in i+1..<t.kidsLen:
|
||||
if t[j].kind != tyVoid and j < t.n.len and t.n[j].kind != nkRecList:
|
||||
t.n[pos] = t.n[j]
|
||||
t[pos] = t[j]
|
||||
b.setSon(pos, t[j])
|
||||
if t.n[pos].kind == nkSym:
|
||||
t.n[pos].sym.position = pos
|
||||
inc pos
|
||||
@@ -627,11 +637,12 @@ proc eraseTupleVoidFields*(t: PType) =
|
||||
break
|
||||
|
||||
proc skipIntLiteralParams*(t: PType; idgen: IdGenerator) =
|
||||
var b = reopen(t)
|
||||
for i, p in t.ikids:
|
||||
if p == nil: continue
|
||||
let skipped = p.skipIntLit(idgen)
|
||||
if skipped != p:
|
||||
t[i] = skipped
|
||||
b.setSon(i, skipped)
|
||||
if i > 0: t.n[i].sym.typ = skipped
|
||||
|
||||
# when the typeof operator is used on a static input
|
||||
@@ -769,11 +780,12 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
|
||||
bailout()
|
||||
result = instCopyType(cl, t)
|
||||
cl.localCache[t.itemId] = result
|
||||
var b = reopen(result)
|
||||
for i in FirstGenericParamAt..<result.kidsLen:
|
||||
var r = result[i]
|
||||
if r != nil:
|
||||
r = replaceTypeVarsT(cl, r)
|
||||
result[i] = r
|
||||
b.setSon(i, r)
|
||||
propagateToOwner(result, r)
|
||||
result.n = replaceTypeVarsN(cl, result.n)
|
||||
if not cl.allowMetaTypes and result.n != nil and
|
||||
@@ -785,8 +797,9 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
|
||||
bailout()
|
||||
result = instCopyType(cl, t)
|
||||
cl.localCache[t.itemId] = result
|
||||
var b = reopen(result)
|
||||
for i in FirstGenericParamAt..<result.kidsLen:
|
||||
result[i] = replaceTypeVarsT(cl, result[i])
|
||||
b.setSon(i, replaceTypeVarsT(cl, result[i]))
|
||||
propagateToOwner(result, result.last)
|
||||
|
||||
else:
|
||||
@@ -802,6 +815,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
|
||||
cl.localCache[t.itemId] = result
|
||||
let propagateInstValue = isInstValue and isRefPtrObject(t)
|
||||
|
||||
var b = reopen(result)
|
||||
for i, resulti in result.ikids:
|
||||
if resulti != nil:
|
||||
if resulti.kind == tyGenericBody and not cl.allowMetaTypes:
|
||||
@@ -817,7 +831,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
|
||||
if r2.kind in {tyPtr, tyRef}:
|
||||
r = skipTypes(r2, {tyPtr, tyRef})
|
||||
if result.kind != tyProc or i == 0:
|
||||
result[i] = r
|
||||
b.setSon(i, r)
|
||||
if result.kind != tyArray or i != 0:
|
||||
propagateToOwner(result, r)
|
||||
# bug #4677: Do not instantiate effect lists
|
||||
|
||||
@@ -884,11 +884,13 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
|
||||
|
||||
openScope(c)
|
||||
matchedConceptContext.candidateType = a
|
||||
typeClass[0][0] = a
|
||||
var tcb = reopen(typeClass[0])
|
||||
tcb.setSon(0, a)
|
||||
c.matchedConcept = addr(matchedConceptContext)
|
||||
defer:
|
||||
c.matchedConcept = prevMatchedConcept
|
||||
typeClass[0][0] = prevCandidateType
|
||||
var tcb2 = reopen(typeClass[0])
|
||||
tcb2.setSon(0, prevCandidateType)
|
||||
closeScope(c)
|
||||
|
||||
var typeParams: seq[(PSym, PType)] = @[]
|
||||
|
||||
@@ -102,6 +102,26 @@ proc reopen*(t: PType; idgen: IdGenerator): TypeBuilder {.inline.} =
|
||||
## case: the name (`t`) stays, only its tree structure is (re)built.
|
||||
TypeBuilder(t: t, idgen: idgen)
|
||||
|
||||
proc reopen*(t: PType): TypeBuilder {.inline.} =
|
||||
## Reopens an existing type purely to *transform* its sons in place (see
|
||||
## `setSon`), without adding fresh ones -- so no `idgen` is needed. This is the
|
||||
## "mutable staging buffer" seam for son-replacement: today it is in-place
|
||||
## mutation of `t`; under NIF `reopen` thaws `t`'s sealed cursor into a mutable
|
||||
## buffer, `setSon` rewrites a token, and the buffer is re-sealed. Distinct from
|
||||
## the id-minting `reopen(t, idgen)` used to (re)build a forward type's body.
|
||||
TypeBuilder(t: t, idgen: nil)
|
||||
|
||||
proc setSon*(b: var TypeBuilder; i: int; son: PType) {.inline.} =
|
||||
## Replaces son `i` of a reopened type -- the in-place transform seam. Mirrors
|
||||
## the old `PType.[]=` (via `ast.replaceSon`), including the `tyProc` return/
|
||||
## param slot handling. Distinct from `add` (append a new son) and from the
|
||||
## whole-list `ast.setSon(dest, son)`. Under NIF this is a token rewrite in the
|
||||
## buffer thawed by `reopen`.
|
||||
replaceSon(b.t, i, son)
|
||||
|
||||
proc setSon*(b: var TypeBuilder; i: BackwardsIndex; son: PType) {.inline.} =
|
||||
replaceSon(b.t, i, son)
|
||||
|
||||
proc setN*(b: var TypeBuilder; n: PNode) {.inline.} =
|
||||
b.t.n = n
|
||||
|
||||
@@ -131,3 +151,81 @@ template finish*(b: TypeBuilder): PType =
|
||||
## read with no call/move/destroy overhead over the old direct construction.
|
||||
## Later this becomes `beginRead`, yielding a read-only cursor.
|
||||
b.t
|
||||
|
||||
type
|
||||
TypePairBuilder* = object
|
||||
## The *deferred* construction seam: like `TypeBuilder`, but its identity is
|
||||
## published -- cached, stashed for a later pass, or handed to a recursive
|
||||
## sem call -- *before* its body is finished. Recursive generic
|
||||
## instantiation needs the in-progress instance to be findable under its
|
||||
## name while its sons are still being appended; `TypeBuilder` cannot model
|
||||
## that because `finish` is the seal point and nothing may be appended after
|
||||
## it. `TypePairBuilder` can, because the thing it hands out early is a
|
||||
## `TypePair` -- an (identity, tree) pair -- and early consumers take only
|
||||
## its `id`.
|
||||
##
|
||||
## Contract: whatever observes `pair` before `finishPair` must rely on
|
||||
## `pair.id` (the name) alone -- never the son count or son contents of the
|
||||
## still-open `decl`. Today `decl` is the growing `PType` and `decl.itemId
|
||||
## == id`, so this holds trivially; under NIF `id` is a `SymId` valid the
|
||||
## instant the shell exists and `decl` is the open `TokenBuf`, sealed into a
|
||||
## read-only cursor by `finishPair`.
|
||||
t: PType
|
||||
idgen {.cursor.}: IdGenerator
|
||||
|
||||
proc openPair*(kind: TTypeKind; idgen: IdGenerator; owner: PSym;
|
||||
son: sink PType = nil): TypePairBuilder {.inline.} =
|
||||
## Mints the shell (optionally with `son0` already set -- e.g. the generic
|
||||
## head for `tyGenericInst`). Mirrors `newType(kind, idgen, owner, son)`. The
|
||||
## `pair` is publishable the moment this returns.
|
||||
TypePairBuilder(t: newType(kind, idgen, owner, son), idgen: idgen)
|
||||
|
||||
proc reopenPair*(t: PType; idgen: IdGenerator): TypePairBuilder {.inline.} =
|
||||
## Continues building an *existing* (forward-declared / partial) type as a
|
||||
## deferred pair, preserving its identity. The deferred analogue of
|
||||
## `reopen(t, idgen)`; its `pair` is publishable immediately.
|
||||
TypePairBuilder(t: t, idgen: idgen)
|
||||
|
||||
proc pair*(b: TypePairBuilder): TypePair {.inline.} =
|
||||
## The publishable (identity, tree) handle -- cache it / stash it / thread it
|
||||
## through recursive sem *before* the body is complete. Only `pair.id` may be
|
||||
## relied upon by those early consumers.
|
||||
typePair(b.t)
|
||||
|
||||
proc add*(b: var TypePairBuilder; son: PType) {.inline.} =
|
||||
addSonSkipIntLit(b.t, son, b.idgen)
|
||||
|
||||
proc addRaw*(b: var TypePairBuilder; son: PType; propagateHasAsgn = true) {.inline.} =
|
||||
## Appends a son verbatim while the body is open. Mirrors `rawAddSon` -- the
|
||||
## incremental-append step of the deferred build.
|
||||
rawAddSon(b.t, son, propagateHasAsgn)
|
||||
|
||||
proc setSon*(b: var TypePairBuilder; i: int; son: PType) {.inline.} =
|
||||
replaceSon(b.t, i, son)
|
||||
|
||||
proc setN*(b: var TypePairBuilder; n: PNode) {.inline.} =
|
||||
b.t.n = n
|
||||
|
||||
proc addRecField*(b: var TypePairBuilder; fieldNode: PNode) {.inline.} =
|
||||
## Appends a field entry to the type's record list (`.n`), the way tuple /
|
||||
## object / proc bodies grow their `nkRecList` / `nkFormalParams`. Mirrors
|
||||
## `t.n.add fieldNode`, and pairs with `add`/`addRaw` for the parallel son.
|
||||
b.t.n.add fieldNode
|
||||
|
||||
proc flags*(b: TypePairBuilder): TTypeFlags {.inline.} =
|
||||
## Reads the shell's current flags (they may have accumulated via `addRaw`'s
|
||||
## propagation since the last `setFlags`).
|
||||
b.t.flags
|
||||
|
||||
proc setFlags*(b: var TypePairBuilder; flags: TTypeFlags) {.inline.} =
|
||||
b.t.flags = flags
|
||||
|
||||
proc incl*(b: var TypePairBuilder; flag: TTypeFlag) {.inline.} =
|
||||
b.t.incl flag
|
||||
|
||||
proc finishPair*(b: sink TypePairBuilder): TypePair {.inline.} =
|
||||
## Seals the deferred build. Today returns the pair unchanged; under NIF this
|
||||
## is `beginRead` -- the open `TokenBuf` becomes a read-only cursor, still
|
||||
## reachable through `pair.id`, so recursive references bound to the name now
|
||||
## resolve to the sealed tree.
|
||||
typePair(b.t)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, astalgo, trees, msgs, platform, renderer, options,
|
||||
lineinfos, int128, modulegraphs, astmsgs, wordrecg
|
||||
lineinfos, int128, modulegraphs, astmsgs, wordrecg, typebuilders
|
||||
|
||||
import std/[intsets, strutils]
|
||||
|
||||
@@ -1267,7 +1267,8 @@ proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType =
|
||||
parent = it
|
||||
it = it.elementType
|
||||
if it.kind == tyDistinct and parent != nil:
|
||||
parent[0] = it[0]
|
||||
var b = reopen(parent)
|
||||
b.setSon(0, it[0])
|
||||
|
||||
proc safeInheritanceDiff*(a, b: PType): int =
|
||||
# same as inheritanceDiff but checks for tyError:
|
||||
@@ -1444,7 +1445,8 @@ proc takeType*(formal, arg: PType; g: ModuleGraph; idgen: IdGenerator): PType =
|
||||
arg.isEmptyContainer:
|
||||
let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), idgen, arg.owner)
|
||||
copyTypeProps(g, idgen.module, a, arg)
|
||||
a[ord(arg.kind == tyArray)] = formal[0]
|
||||
var b = reopen(a)
|
||||
b.setSon(ord(arg.kind == tyArray), formal[0])
|
||||
result = a
|
||||
elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind:
|
||||
result = formal
|
||||
|
||||
Reference in New Issue
Block a user