From bd0de5f9aab0dae07d33912e80fa56dfc29990b8 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 8 Jul 2026 11:32:50 +0200 Subject: [PATCH] massive PType refactor; the compiler moves to a NIF based type representation internally --- compiler/lowerings.nim | 18 ++-- compiler/magicsys.nim | 25 ++--- compiler/nifcmain.nim | 154 +++++++++++++++++++++++++++ compiler/semdata.nim | 111 ++++++++++++-------- compiler/semtypes.nim | 213 ++++++++++++++++++++++++-------------- compiler/typebuilders.nim | 133 ++++++++++++++++++++++++ 6 files changed, 507 insertions(+), 147 deletions(-) create mode 100644 compiler/nifcmain.nim create mode 100644 compiler/typebuilders.nim diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index d72b403a37..5a991499f7 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -144,18 +144,19 @@ proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNod result.add newFastAsgnStmt(n[2], tempAsNode) proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo; final=true): PType = - result = newType(tyObject, idgen, owner) + var b = openType(tyObject, idgen, owner) if final: - rawAddSon(result, nil) - incl result, tfFinal + b.addRaw nil + b.incl tfFinal else: - rawAddSon(result, getCompilerProc(g, "RootObj").typ) - result.n = newNodeI(nkRecList, info) + b.addRaw getCompilerProc(g, "RootObj").typ + b.setN newNodeI(nkRecList, info) let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s), idgen, owner, info, owner.options) incl s.flagsImpl, sfAnon + b.setSym s + result = finish b s.typ = result - result.sym = s template fieldCheck {.dirty.} = when false: @@ -385,8 +386,9 @@ proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode = proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode = result = newNodeI(nkAddr, n.info, 1) result[0] = n - result.typ = newType(typeKind, idgen, n.typ.owner) - result.typ.rawAddSon(n.typ) + var b = openType(typeKind, idgen, n.typ.owner) + b.addRaw n.typ + result.typ = finish b proc genDeref*(n: PNode; k = nkHiddenDeref): PNode = result = newNodeIT(k, n.info, diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 9e839aca5f..ad984ffd5e 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -11,9 +11,10 @@ import ast, msgs, platform, idents, - modulegraphs, lineinfos, types + modulegraphs, lineinfos, types, typebuilders export createMagic +export typebuilders proc nilOrSysInt*(g: ModuleGraph): PType = g.sysTypes[tyInt] @@ -91,24 +92,13 @@ proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType = result = newSysType(g, tyFloat, size=8) result.n = literal -proc skipIntLit*(t: PType; id: IdGenerator): PType {.inline.} = - if t.n != nil and t.kind in {tyInt, tyFloat}: - result = copyType(t, id, t.owner) - result.n = nil - else: - result = t - -proc addSonSkipIntLit*(father, son: PType; id: IdGenerator) = - let s = son.skipIntLit(id) - father.add(s) - propagateToOwner(father, s) - proc makeVarType*(owner: PSym; baseType: PType; idgen: IdGenerator; kind = tyVar): PType = if baseType.kind == kind: result = baseType else: - result = newType(kind, idgen, owner) - addSonSkipIntLit(result, baseType, idgen) + var b = openType(kind, idgen, owner) + b.add baseType + result = finish b proc getCompilerProc*(g: ModuleGraph; name: string): PSym = let ident = getIdent(g.cache, name) @@ -158,8 +148,9 @@ proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym = "can't find magic equals operator for type kind " & $t.kind) proc makePtrType*(baseType: PType; idgen: IdGenerator): PType = - result = newType(tyPtr, idgen, baseType.owner) - addSonSkipIntLit(result, baseType, idgen) + var b = openType(tyPtr, idgen, baseType.owner) + b.add baseType + result = finish b proc makeAddr*(n: PNode; idgen: IdGenerator): PNode = if n.kind == nkHiddenAddr: diff --git a/compiler/nifcmain.nim b/compiler/nifcmain.nim new file mode 100644 index 0000000000..d0017f9685 --- /dev/null +++ b/compiler/nifcmain.nim @@ -0,0 +1,154 @@ +# +# +# The Nim Compiler +# (c) Copyright 2026 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Backend-only driver for `nim ic`'s C-generation stages (the `nifc` command: +## `--icBackendStage:lower|cg|merge|emit|link`). This produces the separate +## `bin/nifc` binary that `deps.nim` invokes per rule instead of re-entering the +## monolithic `nim` compiler. +## +## Crucially, it imports NEITHER `main`/`pipelines` NOR `cmdlinehelper`/`nimconf`. +## Those are the only two edges that pull the frontend semantic analyzer (`sem`) +## and the NimScript VM (`scriptconfig`) into the ordinary compiler binary. The +## backend graph (`nifbackend` -> `cgen`/`ast2nif`/`transf`/`injectdestructors`/ +## `modulegraphs`) is entirely sem-free, and config is replayed sem-free from the +## precompiled `.cfg.nif` via `icconfig.applyIcConfig` (no file read, no VM run). +## +## Keeping `sem` out of the closure is the prerequisite for building this binary +## with `-d:nimBackend`, under which `astdef` swaps `PNode` to a cursor-backed +## value representation: `sem`'s pervasive `PNode(kind: ...)` literal construction +## could not compile against such a type, but it is no longer linked here. + +import std/[os, parseopt, strutils] + +when defined(nimPreviewSlimSystem): + import std/assertions + +import + commands, options, msgs, extccomp, idents, lineinfos, + pathutils, modulegraphs, condsyms, platform, modules +import "../dist/checksums/src/checksums/sha1" + +from ast import setUseIc +from ast2nif import registerNifAstTags +import icconfig +import nifbackend + +proc hashMainCompilationParams(conf: ConfigRef): string = + ## Mirrors `main.hashMainCompilationParams` (inlined to avoid importing `main`, + ## which pulls in `pipelines`/`sem`). + var state = newSha1State() + state.update os.getAppFilename() + state.update conf.commandLine + state.update $conf.projectFull + result = $SecureHash(state.finalize()) + +proc setOutFile(conf: ConfigRef) = + ## Mirrors `main.setOutFile` (inlined, same reason). + if conf.outFile.isEmpty: + var base = conf.projectName + if optUseNimcache in conf.globalOptions: + base.add "_" & hashMainCompilationParams(conf) + let targetName = + if optGenDynLib in conf.globalOptions: + platform.OS[conf.target.targetOS].dllFrmt % base + elif optGenStaticLib in conf.globalOptions: + (if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a") % base + else: base & platform.OS[conf.target.targetOS].exeExt + conf.outFile = RelativeFile targetName + +proc addCmdPrefix(result: var string, kind: CmdLineKind) = + case kind + of cmdLongOption: result.add "--" + of cmdShortOption: result.add "-" + of cmdArgument, cmdEnd: discard + +proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = + ## Slim copy of `nim.processCmdLine` (no nimble-lock probing, no stdin project): + ## the `nifc` child is always launched by `deps.nim` with an explicit project + ## NIF and forwarded switches. + var p = parseopt.initOptParser(cmd) + var argsCount = 0 + config.commandLine.setLen 0 + while true: + parseopt.next(p) + case p.kind + of cmdEnd: break + of cmdLongOption, cmdShortOption: + config.commandLine.add " " + config.commandLine.addCmdPrefix p.kind + config.commandLine.add p.key.quoteShell + if p.val.len > 0: + config.commandLine.add ':' + config.commandLine.add p.val.quoteShell + processSwitch(pass, p, config) + of cmdArgument: + config.commandLine.add " " + config.commandLine.add p.key.quoteShell + if processArgument(pass, p, argsCount, config): break + +proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = + # NIF tag registration must run before any NIF read/write, independent of + # module init order (see ast2nif.registerNifAstTags). + registerNifAstTags() + condsyms.initDefines(conf.symbols) + defineSymbol(conf.symbols, "nim_compiler") + + if paramCount() == 0: + rawMessage(conf, errGenerated, "nifc: no arguments (expected a NIF project)") + return + + # Pass 1: learn the command (`nifc`), the project NIF, and switches including + # `--icPreparsedConfig` (needed before config replay below). + processCmdLine(passCmd1, "", conf) + if conf.projectName != "": + setFromProjectName(conf, conf.projectName) + else: + conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir()) + + var graph = newModuleGraph(cache, conf) + + # Sem-free config: replay the precompiled `.cfg.nif` produced once by the + # `nim icconfig` process. No `nimconf`, no `scriptconfig`, no VM. A missing or + # format-incompatible artifact is fatal here (unlike the frontend, this binary + # has no fallback config parser on purpose). + setDefaultLibpath(conf) + if conf.icPreparsedConfig.len == 0 or not applyIcConfig(conf, conf.icPreparsedConfig): + rawMessage(conf, errGenerated, + "nifc backend requires a valid precompiled config (--icPreparsedConfig)") + return + if conf.backend != backendJs: extccomp.initVars(conf) + + # Pass 2: command-line switches override the replayed config. + processCmdLine(passCmd2, "", conf) + + if conf.selectedGC == gcUnselected: + initOrcDefines(conf) + + if conf.cmd != cmdNifC: + rawMessage(conf, errGenerated, "nifc: only the 'nifc' command is supported") + return + + # cmdNifC arm, mirroring `main.mainCommand`: + setUseIc(true) + excl conf.features, Feature.vtables + wantMainModule(conf) + setOutFile(conf) + + # `main.commandNifC` body, inlined: + extccomp.initVars(conf) + if not extccomp.ccHasSaneOverflow(conf): + conf.symbols.defineSymbol("nimEmulateOverflowChecks") + nifbackend.generateCode(graph, conf.projectMainIdx) + +when compileOption("gc", "refc"): + GC_disableMarkAndSweep() + +let conf = newConfigRef() +handleCmdLine(newIdentCache(), conf) +msgQuit(int8(conf.errorCounter > 0)) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 5cdcc18be4..84c2f66fe6 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -17,7 +17,9 @@ when defined(nimPreviewSlimSystem): import options, ast, msgs, idents, renderer, magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable, - types, lowerings, trees, parampatterns, astalgo + types, lowerings, trees, parampatterns, astalgo, typebuilders + +export typebuilders type TOptionEntry* = object # entries to put on a stack for pragma parsing @@ -445,8 +447,14 @@ proc addToLib*(lib: PLib, sym: PSym) = proc newTypeS*(kind: TTypeKind; c: PContext; son: sink PType = nil): PType = result = newType(kind, c.idgen, getCurrOwner(c), son = son) +proc openType*(c: PContext; kind: TTypeKind): TypeBuilder {.inline.} = + ## `PContext`-flavored `openType`: the type is owned by the current owner. + openType(kind, c.idgen, getCurrOwner(c)) + proc makePtrType*(owner: PSym, baseType: PType; idgen: IdGenerator): PType = - result = newType(tyPtr, idgen, owner, skipIntLit(baseType, idgen)) + var b = openType(tyPtr, idgen, owner) + b.addKeep skipIntLit(baseType, idgen) # son= fast path: skip int-lit, no propagate + result = finish b proc makePtrType*(c: PContext, baseType: PType): PType = makePtrType(getCurrOwner(c), baseType, c.idgen) @@ -459,27 +467,33 @@ proc makeTypeWithModifier*(c: PContext, if modifier in {tyVar, tyLent, tyTypeDesc} and baseType.kind == modifier: result = baseType else: - result = newTypeS(modifier, c, skipIntLit(baseType, c.idgen)) + var b = openType(c, modifier) + b.addKeep skipIntLit(baseType, c.idgen) + result = finish b proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType = if baseType.kind == kind: result = baseType else: - result = newTypeS(kind, c, skipIntLit(baseType, c.idgen)) + var b = openType(c, kind) + b.addKeep skipIntLit(baseType, c.idgen) + result = finish b proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = - let typedesc = newTypeS(tyTypeDesc, c) - incl typedesc.flagsImpl, tfCheckedForDestructor internalAssert(c.config, typ != nil) - typedesc.addSonSkipIntLit(typ, c.idgen) + var b = openType(c, tyTypeDesc) + b.incl tfCheckedForDestructor + b.add typ + let typedesc = finish b let sym = newSym(skType, c.cache.idAnon, c.idgen, getCurrOwner(c), info, c.config.options).linkTo(typedesc) result = newSymNode(sym, info) proc makeTypeFromExpr*(c: PContext, n: PNode): PType = - result = newTypeS(tyFromExpr, c) assert n != nil - result.n = n + var b = openType(c, tyFromExpr) + b.setN n + result = finish b when false: proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType]; @@ -493,42 +507,49 @@ when false: proc makeStaticExpr*(c: PContext, n: PNode): PNode = result = newNodeI(nkStaticExpr, n.info) result.sons = @[n] - result.typ = if n.typ != nil and n.typ.kind == tyStatic: n.typ - else: newTypeS(tyStatic, c, n.typ) + result.typ = + if n.typ != nil and n.typ.kind == tyStatic: n.typ + else: + var b = openType(c, tyStatic) + b.addKeep n.typ + finish b proc makeAndType*(c: PContext, t1, t2: PType): PType = - result = newTypeS(tyAnd, c) - result.rawAddSon t1 - result.rawAddSon t2 - propagateToOwner(result, t1) - propagateToOwner(result, t2) - result.flagsImpl.incl((t1.flags + t2.flags) * {tfHasStatic}) - result.flagsImpl.incl tfHasMeta + var b = openType(c, tyAnd) + b.addRaw t1 + b.addRaw t2 + b.propagateFrom t1 + b.propagateFrom t2 + b.incl((t1.flags + t2.flags) * {tfHasStatic}) + b.incl tfHasMeta + result = finish b proc makeOrType*(c: PContext, t1, t2: PType): PType = + var b = openType(c, tyOr) if t1.kind != tyOr and t2.kind != tyOr: - result = newTypeS(tyOr, c) - result.rawAddSon t1 - result.rawAddSon t2 + b.addRaw t1 + b.addRaw t2 else: - result = newTypeS(tyOr, c) template addOr(t1) = if t1.kind == tyOr: - for x in t1.kids: result.rawAddSon x + for x in t1.kids: b.addRaw x else: - result.rawAddSon t1 + b.addRaw t1 addOr(t1) addOr(t2) - propagateToOwner(result, t1) - propagateToOwner(result, t2) - result.incl((t1.flags + t2.flags) * {tfHasStatic}) - result.incl tfHasMeta + b.propagateFrom t1 + b.propagateFrom t2 + b.incl((t1.flags + t2.flags) * {tfHasStatic}) + b.incl tfHasMeta + result = finish b proc makeNotType*(c: PContext, t1: PType): PType = - result = newTypeS(tyNot, c, son = t1) - propagateToOwner(result, t1) - result.flagsImpl.incl(t1.flags * {tfHasStatic}) - result.flagsImpl.incl tfHasMeta + var b = openType(c, tyNot) + b.addKeep t1 + b.propagateFrom t1 + b.incl(t1.flags * {tfHasStatic}) + b.incl tfHasMeta + result = finish b proc nMinusOne(c: PContext; n: PNode): PNode = result = newTreeI(nkCall, n.info, newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n) @@ -536,19 +557,22 @@ proc nMinusOne(c: PContext; n: PNode): PNode = # Remember to fix the procs below this one when you make changes! proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType = let intType = getSysType(c.graph, n.info, tyInt) - result = newTypeS(tyRange, c, son = intType) + var b = openType(c, tyRange) + b.addKeep intType if n.typ != nil and n.typ.n == nil: - result.incl tfUnresolved - result.n = newTreeI(nkRange, n.info, newIntTypeNode(0, intType), + b.incl tfUnresolved + b.setN newTreeI(nkRange, n.info, newIntTypeNode(0, intType), makeStaticExpr(c, nMinusOne(c, n))) + result = finish b template rangeHasUnresolvedStatic*(t: PType): bool = tfUnresolved in t.flags proc errorType*(c: PContext): PType = ## creates a type representing an error state - result = newTypeS(tyError, c) - result.flagsImpl.incl tfCheckedForDestructor + var b = openType(c, tyError) + b.incl tfCheckedForDestructor + result = finish b proc errorNode*(c: PContext, n: PNode): PNode = result = newNodeI(nkEmpty, n.info) @@ -585,9 +609,10 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt; var n = newNodeI(nkRange, info) n.add newIntTypeNode(first, intType) n.add newIntTypeNode(last, intType) - result = newTypeS(tyRange, c) - result.n = n - addSonSkipIntLit(result, intType, c.idgen) # basetype of range + var b = openType(c, tyRange) + b.setN n + b.add intType # basetype of range + result = finish b proc isSelf*(t: PType): bool {.inline.} = ## Is this the magical 'Self' type from concepts? @@ -597,8 +622,10 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = if typ.kind == tyTypeDesc and not isSelf(typ): result = typ else: - result = newTypeS(tyTypeDesc, c, skipIntLit(typ, c.idgen)) - incl result, tfCheckedForDestructor + var b = openType(c, tyTypeDesc) + b.addKeep skipIntLit(typ, c.idgen) + b.incl tfCheckedForDestructor + result = finish b proc symFromType*(c: PContext; t: PType, info: TLineInfo): PSym = if t.sym != nil: return t.sym diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index a026fc994a..1bae6e11b4 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -44,6 +44,18 @@ proc reusePrev(prev: PType): bool {.inline.} = # partial object marks sym as `sfForward` (sfForward in prev.sym.flags or prev.sym.magic != mNone))) +proc openType(c: PContext; kind: TTypeKind; prev: PType): TypeBuilder = + ## Prev-aware `openType`. This folds the identity decision into one place: for + ## a forward-declared / partial `prev` the reserved *name* is kept and its + ## *tree structure* is rebuilt in place; otherwise a fresh type (and identity) + ## is minted -- at the same sequence point as `newTypeS`, so type ids stay + ## byte-identical. Callers become the uniform "build structure, then `finish`". + if reusePrev(prev): + if prev.kind == tyForward: prev.kind = kind + result = reopen(prev, c.idgen) + else: + result = openType(c, kind) + proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext, son: sink PType): PType = if reusePrev(prev): result = prev @@ -54,16 +66,15 @@ proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext, son: sink PType): #if kind == tyError: result.flags.incl tfCheckedForDestructor proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = - if reusePrev(prev): - result = prev - if result.kind == tyForward: result.kind = kind - else: - result = newTypeS(kind, c) + # centralized on the `openType` chokepoint: keep the reserved name of a + # forward/partial `prev`, else mint a fresh identity. + finish openType(c, kind, prev) proc newConstraint(c: PContext, k: TTypeKind): PType = - result = newTypeS(tyBuiltInTypeClass, c) - result.incl tfCheckedForDestructor - result.addSonSkipIntLit(newTypeS(k, c), c.idgen) + var b = openType(c, tyBuiltInTypeClass) + b.incl tfCheckedForDestructor + b.add newTypeS(k, c) + result = finish b proc skipGenericPrev(prev: PType): PType = result = prev @@ -98,15 +109,19 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = counterSet = initPackedSet[BiggestInt]() counter = 0 base = nil - result = newOrPrevType(tyEnum, prev, c) - result.n = newNodeI(nkEnumTy, n.info) + var b = openType(c, tyEnum, prev) + b.setN newNodeI(nkEnumTy, n.info) checkMinSonsLen(n, 1, c.config) if n[0].kind != nkEmpty: base = semTypeNode(c, n[0][0], nil) if base.kind != tyEnum: localError(c.config, n[0].info, "inheritance only works with an enum") counter = toInt64(lastOrd(c.config, base)) + 1 - rawAddSon(result, base) + b.addRaw base + result = finish b + # the type each enum field belongs to; a field referring to its own enum is + # the canonical self-reference that will migrate to an id-based `TypePair.id`. + let self = typePair(result) let isPure = result.sym != nil and sfPure in result.sym.flags var symbols: TStrTable = initStrTable() var hasNull = false @@ -176,7 +191,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = elif counterSet.containsOrIncl(counter): localError(c.config, n[i].info, errDuplicateAliasInEnumX % e.name.s) - e.typ = result + e.typ = self.decl e.position = int(counter) let symNode = newSymNode(e) if identToReplace != nil and c.config.cmd notin cmdDocLike: @@ -216,10 +231,11 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = setToStringProc(c.graph, result, genEnumToStrProc(result, n.info, c.graph, c.idgen)) proc semSet(c: PContext, n: PNode, prev: PType): PType = - result = newOrPrevType(tySet, prev, c) + var b = openType(c, tySet, prev) if n.len == 2 and n[1].kind != nkEmpty: var base = semTypeNode(c, n[1], nil) - addSonSkipIntLit(result, base, c.idgen) + b.add base + result = finish b if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base) if base.kind notin {tyGenericParam, tyGenericInvocation}: if base.kind == tyForward: @@ -230,45 +246,49 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errSetTooBig) else: localError(c.config, n.info, errXExpectsOneTypeParam % "set") - addSonSkipIntLit(result, errorType(c), c.idgen) + b.add errorType(c) + result = finish b -proc semContainerArg(c: PContext; n: PNode, kindStr: string; result: PType) = +proc semContainerArg(c: PContext; n: PNode, kindStr: string; b: var TypeBuilder) = if n.len == 2: var base = semTypeNode(c, n[1], nil) if base.kind == tyVoid: localError(c.config, n.info, errTIsNotAConcreteType % typeToString(base)) - addSonSkipIntLit(result, base, c.idgen) + b.add base else: localError(c.config, n.info, errXExpectsOneTypeParam % kindStr) - addSonSkipIntLit(result, errorType(c), c.idgen) + b.add errorType(c) proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, prev: PType): PType = - result = newOrPrevType(kind, prev, c) - semContainerArg(c, n, kindStr, result) + var b = openType(c, kind, prev) + semContainerArg(c, n, kindStr, b) + result = finish b proc semVarargs(c: PContext, n: PNode, prev: PType): PType = - result = newOrPrevType(tyVarargs, prev, c) + var b = openType(c, tyVarargs, prev) if n.len == 2 or n.len == 3: var base = semTypeNode(c, n[1], nil) - addSonSkipIntLit(result, base, c.idgen) + b.add base if n.len == 3: - result.n = newIdentNode(considerQuotedIdent(c, n[2]), n[2].info) + b.setN newIdentNode(considerQuotedIdent(c, n[2]), n[2].info) else: localError(c.config, n.info, errXExpectsOneTypeParam % "varargs") - addSonSkipIntLit(result, errorType(c), c.idgen) + b.add errorType(c) + result = finish b proc semVarOutType(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType = if n.len == 1: - result = newOrPrevType(tyVar, prev, c) - result.flags = flags + var b = openType(c, tyVar, prev) + b.setFlags flags var base = semTypeNode(c, n[0], nil) if base.kind == tyTypeDesc and not isSelf(base): base = base[0] if base.kind == tyVar: localError(c.config, n.info, "type 'var var' is not allowed") base = base[0] - addSonSkipIntLit(result, base, c.idgen) + b.add base + result = finish b else: result = newConstraint(c, tyVar) @@ -379,22 +399,23 @@ proc isRecursiveType*(t: PType): bool = var cycleDetector = initIntSet() isRecursiveType(t, cycleDetector) -proc addSonSkipIntLitChecked(c: PContext; father, son: PType; it: PNode, id: IdGenerator) = - let s = son.skipIntLit(id) - father.add(s) +proc addSonSkipIntLitChecked(c: PContext; b: var TypeBuilder; son: PType; it: PNode) = + let s = son.skipIntLit(c.idgen) + b.addKeep s if isRecursiveType(s): localError(c.config, it.info, "illegal recursion in type '" & typeToString(s) & "'") else: - propagateToOwner(father, s) + b.propagateFrom s proc semDistinct(c: PContext, n: PNode, prev: PType): PType = if n.len == 0: return newConstraint(c, tyDistinct) if prevIsKind(prev, tyDistinct): # the symbol already has a distinct type (likely resem), don't create a new type return skipGenericPrev(prev) - result = newOrPrevType(tyDistinct, prev, c) - addSonSkipIntLitChecked(c, result, semTypeNode(c, n[0], nil), n[0], c.idgen) - if n.len > 1: result.n = n[1] + var b = openType(c, tyDistinct, prev) + addSonSkipIntLitChecked(c, b, semTypeNode(c, n[0], nil), n[0]) + if n.len > 1: b.setN n[1] + result = finish b proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = assert isRange(n) @@ -557,29 +578,33 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = # ensure we only construct a tyArray when there was no error (bug #3048): # bug #6682: Do not propagate initialization requirements etc for the # index type: - result = newOrPrevType(tyArray, prev, c, indx) - addSonSkipIntLit(result, base, c.idgen) + var b = openType(c, tyArray, prev) + b.addKeep indx + b.add base + result = finish b else: localError(c.config, n.info, errArrayExpectsTwoTypeParams) result = newOrPrevType(tyError, prev, c) proc semIterableType(c: PContext, n: PNode, prev: PType): PType = - result = newOrPrevType(tyIterable, prev, c) + var b = openType(c, tyIterable, prev) if n.len == 2: let base = semTypeNode(c, n[1], nil) - addSonSkipIntLit(result, base, c.idgen) + b.add base + result = finish b else: localError(c.config, n.info, errXExpectsOneTypeParam % "iterable") result = newOrPrevType(tyError, prev, c) proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = - result = newOrPrevType(tyOrdinal, prev, c) + var b = openType(c, tyOrdinal, prev) if n.len == 2: var base = semTypeNode(c, n[1], nil) if base.kind != tyGenericParam: if not isOrdinalType(base): localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(base, preferDesc)) - addSonSkipIntLit(result, base, c.idgen) + b.add base + result = finish b else: localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal") result = newOrPrevType(tyError, prev, c) @@ -587,10 +612,11 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = if n.len == 0: localError(c.config, n.info, errTypeExpected) - result = newOrPrevType(tyTuple, prev, c) + var b = openType(c, tyTuple, prev) for it in n: let t = semTypeNode(c, it, nil) - addSonSkipIntLitChecked(c, result, t, it, c.idgen) + addSonSkipIntLitChecked(c, b, t, it) + result = finish b proc firstRange(config: ConfigRef, t: PType): PNode = if t.skipModifier().kind in tyFloat..tyFloat64: @@ -1163,7 +1189,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = t = t.base if t.kind == tyVoid: localError(c.config, n.info, "type '$1 void' is not allowed" % kind.toHumanStr) - result = newOrPrevType(kind, prev, c) + var b = openType(c, kind, prev) var isNilable = false var wrapperKind = tyNone # check every except the last is an object: @@ -1179,23 +1205,26 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = elif region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin { tyError, tyObject}: message c.config, n[i].info, errGenerated, "region needs to be an object type" - addSonSkipIntLit(result, region, c.idgen) + b.add region else: message(c.config, n.info, warnDeprecated, "region for pointer types is deprecated") - addSonSkipIntLit(result, region, c.idgen) - addSonSkipIntLit(result, t, c.idgen) + b.add region + b.add t + result = finish b if tfPartial in result.flags: if result.elementType.kind == tyObject: incl(result.elementType, tfPartial) # if not isNilable: result.flags.incl tfNotNil case wrapperKind of tyOwned: if optOwnedRefs in c.config.globalOptions: - let t = newTypeS(tyOwned, c, result) - t.incl tfHasOwned - result = t + var wrap = openType(c, tyOwned) + wrap.addKeep result + wrap.incl tfHasOwned + result = finish wrap of tySink: - let t = newTypeS(tySink, c, result) - result = t + var wrap = openType(c, tySink) + wrap.addKeep result + result = finish wrap else: discard if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcYrc} and @@ -1299,7 +1328,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, let base = (if lifted != nil: lifted else: paramType.base) if base.isMetaType and procKind == skMacro: localError(c.config, info, errMacroBodyDependsOnGenericTypes % paramName) - result = addImplicitGeneric(c, newTypeS(tyStatic, c, base), + var b = openType(c, tyStatic) + b.addKeep base + result = addImplicitGeneric(c, finish b, paramTypId, info, genericParams, paramName) if result != nil: result.incl({tfHasStatic, tfUnresolved}) @@ -1311,9 +1342,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramTypId.id == getIdent(c.cache, "type").id): # XXX Why doesn't this check for tyTypeDesc instead? paramTypId = nil - let t = newTypeS(tyTypeDesc, c, paramType.base) - incl t, tfCheckedForDestructor - result = addImplicitGeneric(c, t, paramTypId, info, genericParams, paramName) + var b = openType(c, tyTypeDesc) + b.addKeep paramType.base + b.incl tfCheckedForDestructor + result = addImplicitGeneric(c, finish b, paramTypId, info, genericParams, paramName) else: result = nil of tyDistinct: @@ -1342,9 +1374,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # Maybe there is another better place to associate # the seq type class with the seq identifier. if paramType.kind == tySequence and paramType.elementType.kind == tyNone: - let typ = newTypeS(tyBuiltInTypeClass, c, - newTypeS(paramType.kind, c)) - result = addImplicitGeneric(c, typ, paramTypId, info, genericParams, paramName) + # allocate the inner son first so the type-id order matches the old + # argument-evaluation order (inner before outer). + let inner = newTypeS(paramType.kind, c) + var b = openType(c, tyBuiltInTypeClass) + b.addKeep inner + result = addImplicitGeneric(c, finish b, paramTypId, info, genericParams, paramName) else: result = nil for i in 0..