bugfix: proper cache for generic instantiations

This commit is contained in:
Araq 2011-07-21 00:57:39 +02:00
commit 569c1ce5ec
24 changed files with 459 additions and 643 deletions

View file

@ -15,7 +15,7 @@ import
options, intsets,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
rodutils
rodutils, renderer
when options.hasTinyCBackend:
import tccgen

View file

@ -134,15 +134,13 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode =
include semtypes, semexprs, semgnrc, semstmts
proc addCodeForGenerics(c: PContext, n: PNode) =
for i in countup(c.lastGenericIdx, sonsLen(c.generics) - 1):
var it = c.generics.sons[i].sons[1]
if it.kind != nkSym: InternalError("addCodeForGenerics")
var prc = it.sym
for i in countup(lastGenericIdx, Len(generics) - 1):
var prc = generics[i].instSym
if prc.kind in {skProc, skMethod, skConverter} and prc.magic == mNone:
if prc.ast == nil or prc.ast.sons[codePos] == nil:
InternalError(prc.info, "no code for " & prc.name.s)
addSon(n, prc.ast)
c.lastGenericIdx = sonsLen(c.generics)
lastGenericIdx = Len(generics)
proc semExprNoFlags(c: PContext, n: PNode): PNode =
result = semExpr(c, n, {})
@ -174,7 +172,7 @@ proc myOpenCached(module: PSym, filename: string,
proc SemStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
result = semStmt(c, n)
# BUGFIX: process newly generated generics here, not at the end!
if sonsLen(c.generics) > 0:
if lastGenericIdx < Len(generics):
var a = newNodeI(nkStmtList, n.info)
addCodeForGenerics(c, a)
if sonsLen(a) > 0:

View file

@ -119,12 +119,4 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
# candidateCount != 1: return explicitGenericInstError(n)
else:
assert false
when false:
var x: TCandidate
initCandidate(x, s, n)
var newInst = generateInstance(c, s, x.bindings, n.info)
markUsed(n, s)
result = newSymNode(newInst, n.info)

View file

@ -33,16 +33,17 @@ type
nestedBlockCounter*: int # whether we are in a block or not
next*: PProcCon # used for stacking procedure contexts
TInstantiatedSymbol* {.final.} = object
genericSym*, instSym*: PSym
concreteTypes*: seq[PType]
PContext* = ref TContext
TContext* = object of TPassContext # a context represents a module
module*: PSym # the module sym belonging to the context
p*: PProcCon # procedure context
InstCounter*: int # to prevent endless instantiations
generics*: PNode # a list of the things to compile; list of
# nkExprEqExpr nodes which contain the
# generic symbol and the instantiated symbol
threadEntries*: TSymSeq # list of thread entries to check
lastGenericIdx*: int # used for the generics stack
tab*: TSymTab # each module has its own symbol table
AmbiguousSymbols*: TIntSet # ids of all ambiguous symbols (cannot
# store this info in the syms themselves!)
@ -58,6 +59,8 @@ type
var gInstTypes*: TIdTable # map PType to PType
var generics*: seq[TInstantiatedSymbol] = @[] # a list of the things to compile
var lastGenericIdx*: int # used for the generics stack
proc newContext*(module: PSym, nimfile: string): PContext
@ -124,7 +127,6 @@ proc newContext(module: PSym, nimfile: string): PContext =
initLinkedList(result.libs)
append(result.optionStack, newOptionEntry())
result.module = module
result.generics = newNode(nkStmtList)
result.threadEntries = @[]
result.converters = @[]
result.filename = nimfile

View file

@ -9,15 +9,17 @@
# This module implements the instantiation of generic procs.
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable) =
if (n.kind != nkGenericParams):
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
entry: var TInstantiatedSymbol) =
if n.kind != nkGenericParams:
InternalError(n.info, "instantiateGenericParamList; no generic params")
for i in countup(0, sonsLen(n) - 1):
newSeq(entry.concreteTypes, n.len)
for i in countup(0, n.len - 1):
var a = n.sons[i]
if a.kind != nkSym:
InternalError(a.info, "instantiateGenericParamList; no symbol")
var q = a.sym
if not (q.typ.kind in {tyTypeDesc, tyGenericParam}): continue
if q.typ.kind notin {tyTypeDesc, tyGenericParam}: continue
var s = newSym(skType, q.name, getCurrOwner())
s.info = q.info
incl(s.flags, sfUsed)
@ -29,24 +31,23 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable) =
InternalError(a.info, "instantiateGenericParamList: " & q.name.s)
s.typ = t
addDecl(c, s)
entry.concreteTypes[i] = t
proc GenericCacheGet(c: PContext, genericSym, instSym: PSym): PSym =
result = nil
for i in countup(0, sonsLen(c.generics) - 1):
if c.generics.sons[i].kind != nkExprEqExpr:
InternalError(genericSym.info, "GenericCacheGet")
var a = c.generics.sons[i].sons[0].sym
if genericSym.id == a.id:
var b = c.generics.sons[i].sons[1].sym
if equalParams(b.typ.n, instSym.typ.n) == paramsEqual:
#echo "found in cache: ", getProcHeader(instSym)
return b
proc sameInstantiation(a, b: TInstantiatedSymbol): bool =
if a.genericSym.id == b.genericSym.id and
a.concreteTypes.len == b.concreteTypes.len:
for i in 0 .. < a.concreteTypes.len:
if not sameType(a.concreteTypes[i], b.concreteTypes[i]): return
result = true
proc GenericCacheAdd(c: PContext, genericSym, instSym: PSym) =
var n = newNode(nkExprEqExpr)
addSon(n, newSymNode(genericSym))
addSon(n, newSymNode(instSym))
addSon(c.generics, n)
proc GenericCacheGet(c: PContext, entry: var TInstantiatedSymbol): PSym =
for i in countup(0, Len(generics) - 1):
if sameInstantiation(entry, generics[i]):
result = generics[i].instSym
# checking for the concrete parameter list is wrong and unnecessary!
#if equalParams(b.typ.n, instSym.typ.n) == paramsEqual:
#echo "found in cache: ", getProcHeader(result)
return
proc removeDefaultParamValues(n: PNode) =
# we remove default params, because they cannot be instantiated properly
@ -81,11 +82,14 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
result.ast = n
pushOwner(result)
openScope(c.tab)
if (n.sons[genericParamsPos].kind == nkEmpty):
if n.sons[genericParamsPos].kind == nkEmpty:
InternalError(n.info, "generateInstance")
n.sons[namePos] = newSymNode(result)
pushInfoContext(info)
instantiateGenericParamList(c, n.sons[genericParamsPos], pt)
var entry: TInstantiatedSymbol
entry.instSym = result
entry.genericSym = fn
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry)
n.sons[genericParamsPos] = ast.emptyNode
# semantic checking for the parameters:
if n.sons[paramsPos].kind != nkEmpty:
@ -96,10 +100,10 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
result.typ = newTypeS(tyProc, c)
addSon(result.typ, nil)
result.typ.callConv = fn.typ.callConv
var oldPrc = GenericCacheGet(c, fn, result)
if oldPrc == nil:
var oldPrc = GenericCacheGet(c, entry)
if oldPrc == nil:
# add it here, so that recursive generic procs are possible:
GenericCacheAdd(c, fn, result)
generics.add(entry)
addDecl(c, result)
if n.sons[codePos].kind != nkEmpty:
pushProcCon(c, result)

View file

@ -358,6 +358,8 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef,
nkConverterDef, nkMacroDef, nkTemplateDef:
result = toVoid
of nkExprColonExpr:
result = analyse(c, n.sons[1])
else: InternalError(n.info, "analysis not implemented for: " & $n.kind)
proc analyseThreadProc*(prc: PSym) =
@ -368,17 +370,6 @@ proc analyseThreadProc*(prc: PSym) =
c.mapping[formal.id] = toTheirs # thread receives foreign data!
discard analyse(c, prc.ast.sons[codePos])
when false:
proc analyseThreadCreationCall(n: PNode) =
# thread proc is second param of ``createThread``:
if n[2].kind != nkSym or n[2].sym.kind != skProc:
Message(n.info, warnAnalysisLoophole, renderTree(n))
return
analyseProc(n[2].sym)
proc AnalyseThread*(threadCreation: PNode) =
analyseThreadCreationCall(threadCreation)
proc needsGlobalAnalysis*: bool =
result = gGlobalOptions * {optThreads, optThreadAnalysis} ==
{optThreads, optThreadAnalysis}

View file

@ -12,19 +12,6 @@
import
ast, astalgo, lexer, msgs, strutils, wordrecg
proc getMagic*(op: PNode): TMagic
proc isConstExpr*(n: PNode): bool
proc flattenTree*(root: PNode, op: TMagic): PNode
proc TreeToSym*(t: PNode): PSym
proc SwapOperands*(op: PNode)
proc getOpSym*(op: PNode): PSym
proc getProcSym*(call: PNode): PSym
proc ExprStructuralEquivalent*(a, b: PNode): bool
proc sameTree*(a, b: PNode): bool
proc cyclicTree*(n: PNode): bool
# implementation
proc hasSon(father, son: PNode): bool =
for i in countup(0, sonsLen(father) - 1):
if father.sons[i] == son:
@ -45,11 +32,11 @@ proc cyclicTreeAux(n, s: PNode): bool =
result = false
delSon(s, m)
proc cyclicTree(n: PNode): bool =
proc cyclicTree*(n: PNode): bool =
var s = newNodeI(nkEmpty, n.info)
result = cyclicTreeAux(n, s)
proc ExprStructuralEquivalent(a, b: PNode): bool =
proc ExprStructuralEquivalent*(a, b: PNode): bool =
result = false
if a == b:
result = true
@ -69,7 +56,7 @@ proc ExprStructuralEquivalent(a, b: PNode): bool =
if not ExprStructuralEquivalent(a.sons[i], b.sons[i]): return
result = true
proc sameTree(a, b: PNode): bool =
proc sameTree*(a, b: PNode): bool =
result = false
if a == b:
result = true
@ -93,10 +80,10 @@ proc sameTree(a, b: PNode): bool =
if not sameTree(a.sons[i], b.sons[i]): return
result = true
proc getProcSym(call: PNode): PSym =
proc getProcSym*(call: PNode): PSym =
result = call.sons[0].sym
proc getOpSym(op: PNode): PSym =
proc getOpSym*(op: PNode): PSym =
if not (op.kind in {nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit}):
result = nil
else:
@ -104,7 +91,7 @@ proc getOpSym(op: PNode): PSym =
if op.sons[0].Kind == nkSym: result = op.sons[0].sym
else: result = nil
proc getMagic(op: PNode): TMagic =
proc getMagic*(op: PNode): TMagic =
case op.kind
of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit, nkPrefix, nkPostfix,
nkInfix:
@ -113,10 +100,10 @@ proc getMagic(op: PNode): TMagic =
else: result = mNone
else: result = mNone
proc TreeToSym(t: PNode): PSym =
proc TreeToSym*(t: PNode): PSym =
result = t.sym
proc isConstExpr(n: PNode): bool =
proc isConstExpr*(n: PNode): bool =
result = (n.kind in
{nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags)
@ -128,14 +115,14 @@ proc flattenTreeAux(d, a: PNode, op: TMagic) =
else:
addSon(d, copyTree(a))
proc flattenTree(root: PNode, op: TMagic): PNode =
proc flattenTree*(root: PNode, op: TMagic): PNode =
result = copyNode(root)
if (getMagic(root) == op):
# BUGFIX: forget to copy prc
addSon(result, copyNode(root.sons[0]))
flattenTreeAux(result, root, op)
proc SwapOperands(op: PNode) =
proc SwapOperands*(op: PNode) =
var tmp = op.sons[1]
op.sons[1] = op.sons[2]
op.sons[2] = tmp