mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-04 06:28:38 +00:00
massive PType refactor; the compiler moves to a NIF based type representation internally
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
154
compiler/nifcmain.nim
Normal file
154
compiler/nifcmain.nim
Normal file
@@ -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))
|
||||
@@ -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
|
||||
|
||||
@@ -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..<paramType.len:
|
||||
@@ -1553,8 +1588,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
# which will prevent other types from matching - clearly a very
|
||||
# surprising behavior. We must instead fix the expected type of
|
||||
# the proc to be the unbound typedesc type:
|
||||
typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
|
||||
typ.incl tfCheckedForDestructor
|
||||
let none = newTypeS(tyNone, c)
|
||||
var b = openType(c, tyTypeDesc)
|
||||
b.addKeep none
|
||||
b.incl tfCheckedForDestructor
|
||||
typ = finish b
|
||||
|
||||
elif def.typ != nil and def.typ.kind != tyFromExpr: # def.typ can be void
|
||||
# if def.typ != nil and def.typ.kind != tyNone:
|
||||
@@ -1870,9 +1908,10 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
|
||||
if prev != nil and (prev.kind == tyGenericBody or
|
||||
typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody}):
|
||||
result = newTypeS(tyAlias, c)
|
||||
result.rawAddSon typeExpr
|
||||
result.sym = prev.sym
|
||||
var b = openType(c, tyAlias)
|
||||
b.addRaw typeExpr
|
||||
b.setSym prev.sym
|
||||
result = finish b
|
||||
if prev.kind != tyGenericBody:
|
||||
assignType(prev, result)
|
||||
else:
|
||||
@@ -1880,9 +1919,10 @@ proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
|
||||
|
||||
proc fixupTypeOf(c: PContext, prev: PType, typ: PType) =
|
||||
if prev != nil:
|
||||
let result = newTypeS(tyAlias, c)
|
||||
result.rawAddSon typ
|
||||
result.sym = prev.sym
|
||||
var b = openType(c, tyAlias)
|
||||
b.addRaw typ
|
||||
b.setSym prev.sym
|
||||
let result = finish b
|
||||
if prev.kind != tyGenericBody:
|
||||
assignType(prev, result)
|
||||
|
||||
@@ -1950,7 +1990,9 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
inherited = n[2]
|
||||
|
||||
var owner = getCurrOwner(c)
|
||||
var candidateTypeSlot = newTypeS(tyAlias, c, c.errorType)
|
||||
var slotB = openType(c, tyAlias)
|
||||
slotB.addKeep c.errorType
|
||||
var candidateTypeSlot = finish slotB
|
||||
result = newOrPrevType(tyUserTypeClass, prev, c, son = candidateTypeSlot)
|
||||
result.incl tfCheckedForDestructor
|
||||
result.n = n
|
||||
@@ -2070,10 +2112,11 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
|
||||
result = errorSym(c, n)
|
||||
|
||||
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
|
||||
result = newOrPrevType(tyStatic, prev, c)
|
||||
var b = openType(c, tyStatic, prev)
|
||||
var base = semTypeNode(c, childNode, nil).skipTypes({tyTypeDesc, tyAlias})
|
||||
result.rawAddSon(base)
|
||||
result.incl tfHasStatic
|
||||
b.addRaw base
|
||||
b.incl tfHasStatic
|
||||
result = finish b
|
||||
|
||||
proc semTypeOfImpl(c: PContext; n: PNode): PNode =
|
||||
var m = BiggestInt 1 # typeOfIter
|
||||
@@ -2401,16 +2444,18 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result.rawAddSon(semTypeNode(c, n[i], nil))
|
||||
of mDistinct:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result = newOrPrevType(tyDistinct, prev, c)
|
||||
addSonSkipIntLit(result, semTypeNode(c, n[1], nil), c.idgen)
|
||||
var b = openType(c, tyDistinct, prev)
|
||||
b.add semTypeNode(c, n[1], nil)
|
||||
result = finish b
|
||||
of mVar:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result = newOrPrevType(tyVar, prev, c)
|
||||
var b = openType(c, tyVar, prev)
|
||||
var base = semTypeNode(c, n[1], nil)
|
||||
if base.kind in {tyVar, tyLent}:
|
||||
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
|
||||
of mRef: result = semAnyRef(c, n, tyRef, prev)
|
||||
of mPtr: result = semAnyRef(c, n, tyPtr, prev)
|
||||
of mTuple: result = semTuple(c, n, prev)
|
||||
@@ -2506,7 +2551,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of nkProcTy, nkIteratorTy:
|
||||
if n.len == 0 or n[0].kind == nkEmpty:
|
||||
# 0 length or empty param list with possible pragmas imply typeclass
|
||||
result = newTypeS(tyBuiltInTypeClass, c)
|
||||
var b = openType(c, tyBuiltInTypeClass)
|
||||
let child = newTypeS(tyProc, c)
|
||||
if n.kind == nkIteratorTy:
|
||||
child.incl tfIterator
|
||||
@@ -2518,7 +2563,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
s.typ = child
|
||||
# for now only call convention pragmas supported in proc typeclass
|
||||
pragma(c, s, n[1], {FirstCallConv..LastCallConv})
|
||||
result.addSonSkipIntLit(child, c.idgen)
|
||||
b.add child
|
||||
result = finish b
|
||||
else:
|
||||
let symKind = if n.kind == nkIteratorTy: skIterator else: skProc
|
||||
result = semProcTypeWithScope(c, n, prev, symKind)
|
||||
@@ -2647,7 +2693,9 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
else: localError(c.config, m.info, errTypeExpected)
|
||||
|
||||
proc semGenericConstraints(c: PContext, x: PType): PType =
|
||||
result = newTypeS(tyGenericParam, c, x)
|
||||
var b = openType(c, tyGenericParam)
|
||||
b.addKeep x
|
||||
result = finish b
|
||||
|
||||
proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
|
||||
@@ -2674,8 +2722,11 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
if typ.kind != tyStatic or typ.len == 0:
|
||||
if typ.kind == tyTypeDesc:
|
||||
if typ.elementType.kind == tyNone:
|
||||
typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
|
||||
incl typ, tfCheckedForDestructor
|
||||
let none = newTypeS(tyNone, c)
|
||||
var b = openType(c, tyTypeDesc)
|
||||
b.addKeep none
|
||||
b.incl tfCheckedForDestructor
|
||||
typ = finish b
|
||||
else:
|
||||
typ = semGenericConstraints(c, typ)
|
||||
|
||||
@@ -2683,7 +2734,9 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
def = semConstExpr(c, def)
|
||||
if typ == nil:
|
||||
if def.typ.kind != tyTypeDesc:
|
||||
typ = newTypeS(tyStatic, c, def.typ)
|
||||
var b = openType(c, tyStatic)
|
||||
b.addKeep def.typ
|
||||
typ = finish b
|
||||
else:
|
||||
# the following line fixes ``TV2*[T:SomeNumber=TR] = array[0..1, T]``
|
||||
# from manyloc/named_argument_bug/triengine:
|
||||
|
||||
133
compiler/typebuilders.nim
Normal file
133
compiler/typebuilders.nim
Normal file
@@ -0,0 +1,133 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2026 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## `TypeBuilder`: a small, TokenBuf-shaped surface for constructing a `PType`.
|
||||
##
|
||||
## The compiler builds types by creating a mutable `TType` and then poking its
|
||||
## sons/flags/`n` field into place at various call sites. This module funnels
|
||||
## that construction through a builder object instead:
|
||||
##
|
||||
## ```nim
|
||||
## var b = openType(kind, idgen, owner) # or openType(c, kind) with a PContext
|
||||
## b.incl someFlag
|
||||
## b.add someSon # skip int-lit + propagate flags
|
||||
## b.setN someNode
|
||||
## result = finish b
|
||||
## ```
|
||||
##
|
||||
## Today the backing store is a mutable `TType`, so `finish` simply returns it
|
||||
## and there is no runtime cost. The point of routing construction through the
|
||||
## builder is that the backing store can later become a nifcore `TokenBuf` --
|
||||
## with `openType` becoming `openTag`, the `add*` family becoming token/subtree
|
||||
## appends, and `finish` becoming `beginRead` yielding a read-only cursor --
|
||||
## *without touching any call site*.
|
||||
##
|
||||
## Contract: fully configure the type between `openType` and `finish`, and treat
|
||||
## the `finish` result as immutable. Types that need their identity before their
|
||||
## body is complete (recursive / deferred object types, e.g. `semEnum` and
|
||||
## object bodies) are out of scope and keep using the direct `newType` /
|
||||
## `rawAddSon` API for now; they will be modeled via symbol indirection later.
|
||||
|
||||
import ast
|
||||
from itemids import ItemId
|
||||
|
||||
type
|
||||
SymId* = ItemId
|
||||
## The stable identity of a type. Today an `ItemId`; this alias marks every
|
||||
## place that will migrate to a content-based nifcore `SymId` later (once
|
||||
## stable, content-derived names land -- so generic instances dedup across
|
||||
## modules and processes). Keeping the alias means that migration is a
|
||||
## one-line change here rather than a churn across call sites.
|
||||
TypePair* = object
|
||||
## A type as an (identity, tree) pair: `id` names it, `decl` is its tree.
|
||||
## Today `decl.itemId == id`, so the pair is a thin, forward-looking handle
|
||||
## -- the handle a *named* type's body uses to refer to itself (owner /
|
||||
## recursive references). Its real payoff arrives when `decl` becomes a
|
||||
## nameless `NifCursor` and those self/forward references go through `id`.
|
||||
id*: SymId
|
||||
decl*: PType
|
||||
|
||||
proc typePair*(t: PType): TypePair {.inline.} =
|
||||
TypePair(id: t.itemId, decl: t)
|
||||
|
||||
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)
|
||||
|
||||
type
|
||||
TypeBuilder* = object
|
||||
t: PType
|
||||
idgen {.cursor.}: IdGenerator
|
||||
## non-owning: the id generator outlives every builder (it lives for the
|
||||
## whole compilation), so it must not be reference-counted here.
|
||||
|
||||
proc openType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym): TypeBuilder {.inline.} =
|
||||
## Begins a fresh type of the given `kind`. Mirrors `newType`.
|
||||
TypeBuilder(t: newType(kind, idgen, owner), idgen: idgen)
|
||||
|
||||
proc add*(b: var TypeBuilder; son: PType) {.inline.} =
|
||||
## Adds a son, skipping an int-literal wrapper and propagating type flags to
|
||||
## the owner. Mirrors `addSonSkipIntLit` -- the common case.
|
||||
addSonSkipIntLit(b.t, son, b.idgen)
|
||||
|
||||
proc addRaw*(b: var TypeBuilder; son: PType; propagateHasAsgn = true) {.inline.} =
|
||||
## Adds a son verbatim (no int-lit skip) but still propagates type flags.
|
||||
## Mirrors `rawAddSon`.
|
||||
rawAddSon(b.t, son, propagateHasAsgn)
|
||||
|
||||
proc addKeep*(b: var TypeBuilder; son: PType) {.inline.} =
|
||||
## Adds a son verbatim: no int-lit skip and no flag propagation. Mirrors the
|
||||
## `newType(..., son = x)` fast path -- including that a nil son is skipped
|
||||
## (produces a childless type) rather than added.
|
||||
if son != nil: b.t.add son
|
||||
|
||||
proc reopen*(t: PType; idgen: IdGenerator): TypeBuilder {.inline.} =
|
||||
## Continues building an *existing* type in place, preserving its identity
|
||||
## (`itemId`). Used to bind a freshly-built structure onto the reserved name
|
||||
## of a forward-declared / partial type -- the "distinguish name from tree"
|
||||
## case: the name (`t`) stays, only its tree structure is (re)built.
|
||||
TypeBuilder(t: t, idgen: idgen)
|
||||
|
||||
proc setN*(b: var TypeBuilder; n: PNode) {.inline.} =
|
||||
b.t.n = n
|
||||
|
||||
proc setFlags*(b: var TypeBuilder; flags: TTypeFlags) {.inline.} =
|
||||
## Replaces the whole flag set (assignment, not union). Mirrors `t.flags = x`.
|
||||
b.t.flags = flags
|
||||
|
||||
proc incl*(b: var TypeBuilder; flag: TTypeFlag) {.inline.} =
|
||||
b.t.incl flag
|
||||
|
||||
proc incl*(b: var TypeBuilder; flags: TTypeFlags) {.inline.} =
|
||||
b.t.incl flags
|
||||
|
||||
proc propagateFrom*(b: var TypeBuilder; son: PType; propagateHasAsgn = true) {.inline.} =
|
||||
## Propagates a son type's properties (flags, owner) into the type under
|
||||
## construction. Mirrors a bare `propagateToOwner(result, son)`.
|
||||
propagateToOwner(b.t, son, propagateHasAsgn)
|
||||
|
||||
proc setCallConv*(b: var TypeBuilder; cc: TCallingConvention) {.inline.} =
|
||||
b.t.callConv = cc
|
||||
|
||||
proc setSym*(b: var TypeBuilder; s: PSym) {.inline.} =
|
||||
b.t.sym = s
|
||||
|
||||
template finish*(b: TypeBuilder): PType =
|
||||
## Hands out the constructed type. A template so it collapses to a bare field
|
||||
## read with no call/move/destroy overhead over the old direct construction.
|
||||
## Later this becomes `beginRead`, yielding a read-only cursor.
|
||||
b.t
|
||||
Reference in New Issue
Block a user