compilation cache: various bugfixes; works for the compiler itself

This commit is contained in:
Araq 2011-10-27 00:41:42 +02:00
commit 90db9171a2
13 changed files with 307 additions and 148 deletions

View file

@ -293,7 +293,10 @@ type
tfAcyclic, # type is acyclic (for GC optimization) tfAcyclic, # type is acyclic (for GC optimization)
tfEnumHasHoles, # enum cannot be mapped into a range tfEnumHasHoles, # enum cannot be mapped into a range
tfShallow, # type can be shallow copied on assignment tfShallow, # type can be shallow copied on assignment
tfThread # proc type is marked as ``thread`` tfThread, # proc type is marked as ``thread``
tfFromGeneric # type is an instantiation of a generic; this is needed
# because for instantiations of objects, structural
# type equality has to be used
TTypeFlags* = set[TTypeFlag] TTypeFlags* = set[TTypeFlag]
@ -487,14 +490,14 @@ type
# same id; there may be multiple copies of a type # same id; there may be multiple copies of a type
# in memory! # in memory!
kind*: TTypeKind # kind of type kind*: TTypeKind # kind of type
callConv*: TCallingConvention # for procs
flags*: TTypeFlags # flags of the type
sons*: TTypeSeq # base types, etc. sons*: TTypeSeq # base types, etc.
n*: PNode # node for types: n*: PNode # node for types:
# for range types a nkRange node # for range types a nkRange node
# for record types a nkRecord node # for record types a nkRecord node
# for enum types a list of symbols # for enum types a list of symbols
# else: unused # else: unused
flags*: TTypeFlags # flags of the type
callConv*: TCallingConvention # for procs
owner*: PSym # the 'owner' of the type owner*: PSym # the 'owner' of the type
sym*: PSym # types have the sym associated with them sym*: PSym # types have the sym associated with them
# it is used for converting types to strings # it is used for converting types to strings

View file

@ -1395,8 +1395,7 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) =
toRope(magic)])) toRope(magic)]))
proc genConv(p: BProc, e: PNode, d: var TLoc) = proc genConv(p: BProc, e: PNode, d: var TLoc) =
if equalOrDistinctOf(e.typ, e.sons[1].typ) or if compareTypes(e.typ, e.sons[1].typ, dcEqIgnoreDistinct):
equalOrDistinctOf(e.sons[1].typ, e.typ):
expr(p, e.sons[1], d) expr(p, e.sons[1], d)
else: else:
genCast(p, e, d) genCast(p, e, d)

View file

@ -56,35 +56,70 @@ proc hashString*(s: string): biggestInt =
a = a +% `shl`(a, 15'i32) a = a +% `shl`(a, 15'i32)
result = a result = a
var gTypeTable: array[TTypeKind, TIdTable] var
gTypeTable: array[TTypeKind, TIdTable]
gCanonicalTypes: array[TTypeKind, PType]
proc initTypeTables() = proc initTypeTables() =
for i in countup(low(TTypeKind), high(TTypeKind)): InitIdTable(gTypeTable[i]) for i in countup(low(TTypeKind), high(TTypeKind)): InitIdTable(gTypeTable[i])
when false:
proc echoStats*() =
for i in countup(low(TTypeKind), high(TTypeKind)):
echo i, " ", gTypeTable[i].counter
proc GetUniqueType*(key: PType): PType = proc GetUniqueType*(key: PType): PType =
# this is a hotspot in the compiler! # this is a hotspot in the compiler!
result = key
if key == nil: return if key == nil: return
var k = key.kind var k = key.kind
case k case k
of tyObject, tyEnum: of tyNone, tyBool, tyChar, tyEmpty,
tyNil, tyExpr, tyStmt, tyTypeDesc, tyPointer, tyString, tyCString,
tyInt, tyInt8, tyInt16, tyInt32, tyInt64,
tyFloat, tyFloat32, tyFloat64, tyFloat128,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64, tyBigNum:
result = gCanonicalTypes[k]
if result == nil:
gCanonicalTypes[k] = key
result = key
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter:
result = GetUniqueType(lastSon(key))
of tyArrayConstr, tyGenericInvokation, tyGenericBody, tyGenericParam,
tyOpenArray, tyArray, tyTuple, tySet, tyRange,
tyPtr, tyRef, tySequence, tyForward, tyVarargs, tyProxy:
# we have to do a slow linear search because types may need
# to be compared by their structure:
if IdTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameType(t, key):
return t
IdTablePut(gTypeTable[k], key, key)
result = key
of tyObject:
if tfFromGeneric notin key.flags:
# fast case; lookup per id suffices:
result = PType(IdTableGet(gTypeTable[k], key))
if result == nil:
IdTablePut(gTypeTable[k], key, key)
result = key
else:
# ugly slow case: need to compare by structure
if IdTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameType(t, key):
return t
IdTablePut(gTypeTable[k], key, key)
result = key
of tyEnum:
result = PType(IdTableGet(gTypeTable[k], key)) result = PType(IdTableGet(gTypeTable[k], key))
if result == nil: if result == nil:
IdTablePut(gTypeTable[k], key, key) IdTablePut(gTypeTable[k], key, key)
result = key result = key
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter: of tyProc, tyVar:
result = GetUniqueType(lastSon(key)) # tyVar is not 100% correct, but speeds things up a little:
of tyProc: result = key
nil
else:
# we have to do a slow linear search because types may need
# to be compared by their structure:
if IdTableHasObjectAsKey(gTypeTable[k], key): return
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if (t != nil) and sameType(t, key):
return t
IdTablePut(gTypeTable[k], key, key)
proc TableGetType*(tab: TIdTable, key: PType): PObject = proc TableGetType*(tab: TIdTable, key: PType): PObject =
# returns nil if we need to declare this type # returns nil if we need to declare this type

View file

@ -18,3 +18,5 @@ const
VersionPatch* = 13 VersionPatch* = 13
VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch
RodFileVersion* = "1030" # modify this if the rod-format changes!

View file

@ -56,16 +56,17 @@ proc SearchForProc(c: PContext, fn: PSym, tos: int): PSym =
nil nil
result = NextIdentIter(it, c.tab.stack[tos]) result = NextIdentIter(it, c.tab.stack[tos])
proc paramsFitBorrow(a, b: PNode): bool = proc paramsFitBorrow(child, parent: PNode): bool =
var length = sonsLen(a) var length = sonsLen(child)
result = false result = false
if length == sonsLen(b): if length == sonsLen(parent):
for i in countup(1, length - 1): for i in countup(1, length - 1):
var m = a.sons[i].sym var m = child.sons[i].sym
var n = b.sons[i].sym var n = parent.sons[i].sym
assert((m.kind == skParam) and (n.kind == skParam)) assert((m.kind == skParam) and (n.kind == skParam))
if not equalOrDistinctOf(m.typ, n.typ): return if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return
if not equalOrDistinctOf(a.sons[0].typ, b.sons[0].typ): return if not compareTypes(child.sons[0].typ, parent.sons[0].typ,
dcEqOrDistinctOf): return
result = true result = true
proc SearchForBorrowProc(c: PContext, fn: PSym, tos: int): PSym = proc SearchForBorrowProc(c: PContext, fn: PSym, tos: int): PSym =

View file

@ -141,9 +141,6 @@ type
PRodReader* = ref TRodReader PRodReader* = ref TRodReader
const
FileVersion* = "1026" # modify this if the rod-format changes!
var rodCompilerprocs*: TStrTable var rodCompilerprocs*: TStrTable
proc handleSymbolFile*(module: PSym, filename: string): PRodReader proc handleSymbolFile*(module: PSym, filename: string): PRodReader
@ -376,7 +373,6 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
if r.s[r.pos] == '@': if r.s[r.pos] == '@':
inc(r.pos) inc(r.pos)
result.magic = TMagic(decodeVInt(r.s, r.pos)) result.magic = TMagic(decodeVInt(r.s, r.pos))
if r.s[r.pos] == '(': result.ast = decodeNode(r, result.info)
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
result.options = cast[TOptions](int32(decodeVInt(r.s, r.pos))) result.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
@ -395,6 +391,7 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
result.offset = - 1 result.offset = - 1
decodeLoc(r, result.loc, result.info) decodeLoc(r, result.loc, result.info)
result.annex = decodeLib(r, info) result.annex = decodeLib(r, info)
if r.s[r.pos] == '(': result.ast = decodeNode(r, result.info)
#echo "decoded: ", ident.s, "}" #echo "decoded: ", ident.s, "}"
proc skipSection(r: PRodReader) = proc skipSection(r: PRodReader) =
@ -618,7 +615,7 @@ proc newRodReader(modfilename: string, crc: TCrc32,
add(version, r.s[r.pos]) add(version, r.s[r.pos])
inc(r.pos) inc(r.pos)
if r.s[r.pos] == '\x0A': inc(r.pos) if r.s[r.pos] == '\x0A': inc(r.pos)
if version == FileVersion: if version == RodFileVersion:
# since ROD files are only for caching, no backwarts compatibility is # since ROD files are only for caching, no backwarts compatibility is
# needed # needed
processRodFile(r, crc) processRodFile(r, crc)

View file

@ -46,8 +46,6 @@ proc addInterfaceSym(w: PRodWriter, s: PSym)
proc addStmt(w: PRodWriter, n: PNode) proc addStmt(w: PRodWriter, n: PNode)
proc writeRod(w: PRodWriter) proc writeRod(w: PRodWriter)
proc processStacks(w: PRodWriter)
proc getDefines(): string = proc getDefines(): string =
var it: TTabIter var it: TTabIter
var s = InitTabIter(it, gSymbols) var s = InitTabIter(it, gSymbols)
@ -282,7 +280,20 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
if s.magic != mNone: if s.magic != mNone:
result.add('@') result.add('@')
encodeVInt(ord(s.magic), result) encodeVInt(ord(s.magic), result)
if s.ast != nil: if s.options != w.options:
result.add('!')
encodeVInt(cast[int32](s.options), result)
if s.position != 0:
result.add('%')
encodeVInt(s.position, result)
if s.offset != - 1:
result.add('`')
encodeVInt(s.offset, result)
encodeLoc(w, s.loc, result)
if s.annex != nil: encodeLib(w, s.annex, s.info, result)
# lazy loading will soon reload the ast lazily, so the ast needs to be
# the last entry of a symbol:
if s.ast != nil:
# we used to attempt to save space here by only storing a dummy AST if # we used to attempt to save space here by only storing a dummy AST if
# it is not necessary, but Nimrod's heavy compile-time evaluation features # it is not necessary, but Nimrod's heavy compile-time evaluation features
# make that unfeasible nowadays: # make that unfeasible nowadays:
@ -297,17 +308,6 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
if codeAst != nil: if codeAst != nil:
# resore the AST: # resore the AST:
s.ast.sons[codePos] = codeAst s.ast.sons[codePos] = codeAst
if s.options != w.options:
result.add('!')
encodeVInt(cast[int32](s.options), result)
if s.position != 0:
result.add('%')
encodeVInt(s.position, result)
if s.offset != - 1:
result.add('`')
encodeVInt(s.offset, result)
encodeLoc(w, s.loc, result)
if s.annex != nil: encodeLib(w, s.annex, s.info, result)
proc addToIndex(w: var TIndex, key, val: int) = proc addToIndex(w: var TIndex, key, val: int) =
if key - w.lastIdxKey == 1: if key - w.lastIdxKey == 1:
@ -327,11 +327,14 @@ const debugWrittenIds = false
when debugWrittenIds: when debugWrittenIds:
var debugWritten = initIntSet() var debugWritten = initIntSet()
proc symStack(w: PRodWriter) = proc symStack(w: PRodWriter): int =
var i = 0 var i = 0
while i < len(w.sstack): while i < len(w.sstack):
var s = w.sstack[i] var s = w.sstack[i]
if IiTableGet(w.index.tab, s.id) == invalidKey: if sfForward in s.flags:
w.sstack[result] = s
inc result
elif IiTableGet(w.index.tab, s.id) == invalidKey:
var m = getModule(s) var m = getModule(s)
if m == nil: InternalError("symStack: module nil: " & s.name.s) if m == nil: InternalError("symStack: module nil: " & s.name.s)
if (m.id == w.module.id) or (sfFromGeneric in s.flags): if (m.id == w.module.id) or (sfFromGeneric in s.flags):
@ -367,29 +370,40 @@ proc symStack(w: PRodWriter) =
debug(s) debug(s)
debug(s.owner) debug(s.owner)
debug(m) debug(m)
InternalError("BUG!!!!") InternalError("Symbol referred to but never written")
inc(i) inc(i)
setlen(w.sstack, 0) setlen(w.sstack, result)
proc typeStack(w: PRodWriter) = proc typeStack(w: PRodWriter): int =
var i = 0 var i = 0
while i < len(w.tstack): while i < len(w.tstack):
if IiTableGet(w.index.tab, w.tstack[i].id) == invalidKey: var t = w.tstack[i]
if t.kind == tyForward:
w.tstack[result] = t
inc result
elif IiTableGet(w.index.tab, t.id) == invalidKey:
var L = w.data.len var L = w.data.len
addToIndex(w.index, w.tstack[i].id, L) addToIndex(w.index, t.id, L)
encodeType(w, w.tstack[i], w.data) encodeType(w, t, w.data)
add(w.data, rodNL) add(w.data, rodNL)
inc(i) inc(i)
setlen(w.tstack, 0) setlen(w.tstack, result)
proc processStacks(w: PRodWriter) = proc processStacks(w: PRodWriter, finalPass: bool) =
while (len(w.tstack) > 0) or (len(w.sstack) > 0): var oldS = 0
symStack(w) var oldT = 0
typeStack(w) while true:
var slen = symStack(w)
var tlen = typeStack(w)
if slen == oldS and tlen == oldT: break
oldS = slen
oldT = tlen
if finalPass and (oldS != 0 or oldT != 0):
InternalError("could not serialize some forwarded symbols/types")
proc rawAddInterfaceSym(w: PRodWriter, s: PSym) = proc rawAddInterfaceSym(w: PRodWriter, s: PSym) =
pushSym(w, s) pushSym(w, s)
processStacks(w) processStacks(w, false)
proc addInterfaceSym(w: PRodWriter, s: PSym) = proc addInterfaceSym(w: PRodWriter, s: PSym) =
if w == nil: return if w == nil: return
@ -402,17 +416,17 @@ proc addStmt(w: PRodWriter, n: PNode) =
add(w.init, rodNL) add(w.init, rodNL)
encodeNode(w, UnknownLineInfo(), n, w.data) encodeNode(w, UnknownLineInfo(), n, w.data)
add(w.data, rodNL) add(w.data, rodNL)
processStacks(w) processStacks(w, false)
proc writeRod(w: PRodWriter) = proc writeRod(w: PRodWriter) =
processStacks(w) processStacks(w, true)
var f: TFile var f: TFile
if not open(f, completeGeneratedFilePath(changeFileExt(w.filename, "rod")), if not open(f, completeGeneratedFilePath(changeFileExt(w.filename, "rod")),
fmWrite): fmWrite):
return return
# write header: # write header:
f.write("NIM:") f.write("NIM:")
f.write(FileVersion) f.write(RodFileVersion)
f.write(rodNL) f.write(rodNL)
var id = "ID:" var id = "ID:"
encodeVInt(w.module.id, id) encodeVInt(w.module.id, id)

View file

@ -128,8 +128,7 @@ proc checkConvertible(info: TLineInfo, castDest, src: PType) =
# we use d, s here to speed up that operation a bit: # we use d, s here to speed up that operation a bit:
case cmpTypes(d, s) case cmpTypes(d, s)
of isNone, isGeneric: of isNone, isGeneric:
if not equalOrDistinctOf(castDest, src) and if not compareTypes(castDest, src, dcEqIgnoreDistinct):
not equalOrDistinctOf(src, castDest):
GlobalError(info, errGenerated, `%`( GlobalError(info, errGenerated, `%`(
MsgKindToString(errIllegalConvFromXtoY), MsgKindToString(errIllegalConvFromXtoY),
[typeToString(src), typeToString(castDest)])) [typeToString(src), typeToString(castDest)]))
@ -381,8 +380,7 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult =
# Object and tuple conversions are still addressable, so we skip them # Object and tuple conversions are still addressable, so we skip them
if skipTypes(n.typ, abstractPtrs).kind in {tyOpenArray, tyTuple, tyObject}: if skipTypes(n.typ, abstractPtrs).kind in {tyOpenArray, tyTuple, tyObject}:
result = isAssignable(c, n.sons[1]) result = isAssignable(c, n.sons[1])
elif equalOrDistinctOf(n.typ, n.sons[1].typ) or elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
equalOrDistinctOf(n.sons[1].typ, n.typ):
# types that are equal modulo distinction preserve l-value: # types that are equal modulo distinction preserve l-value:
result = isAssignable(c, n.sons[1]) result = isAssignable(c, n.sons[1])
of nkHiddenDeref, nkDerefExpr: of nkHiddenDeref, nkDerefExpr:

View file

@ -502,7 +502,14 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
s.typ.kind = tyGenericBody s.typ.kind = tyGenericBody
if s.typ.containerID != 0: if s.typ.containerID != 0:
InternalError(a.info, "semTypeSection: containerID") InternalError(a.info, "semTypeSection: containerID")
s.typ.containerID = getID() s.typ.containerID = s.typ.id
# XXX for generic type aliases this is not correct! We need the
# underlying Id really:
#
# type
# TGObj[T] = object
# TAlias[T] = TGObj[T]
#
a.sons[1] = semGenericParamList(c, a.sons[1], s.typ) a.sons[1] = semGenericParamList(c, a.sons[1], s.typ)
s.typ.size = -1 # could not be computed properly s.typ.size = -1 # could not be computed properly
# we fill it out later. For magic generics like 'seq', it won't be filled # we fill it out later. For magic generics like 'seq', it won't be filled

View file

@ -44,7 +44,7 @@ proc searchInstTypes(tab: TIdTable, key: PType): PType =
for h in countup(0, high(tab.data)): for h in countup(0, high(tab.data)):
var t = PType(tab.data[h].key) var t = PType(tab.data[h].key)
if t != nil: if t != nil:
if key.containerId == t.containerID: if key.containerId == t.containerId:
var match = true var match = true
for j in countup(0, sonsLen(t) - 1): for j in countup(0, sonsLen(t) - 1):
# XXX sameType is not really correct for nested generics? # XXX sameType is not really correct for nested generics?
@ -184,6 +184,7 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
else: else:
if containsGenericType(t): if containsGenericType(t):
result = copyType(t, t.owner, false) result = copyType(t, t.owner, false)
incl(result.flags, tfFromGeneric)
result.size = -1 # needs to be recomputed result.size = -1 # needs to be recomputed
for i in countup(0, sonsLen(result) - 1): for i in countup(0, sonsLen(result) - 1):
result.sons[i] = ReplaceTypeVarsT(cl, result.sons[i]) result.sons[i] = ReplaceTypeVarsT(cl, result.sons[i])

View file

@ -161,9 +161,9 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
elif (k >= tyFloat) and (k <= tyFloat128): result = isConvertible elif (k >= tyFloat) and (k <= tyFloat128): result = isConvertible
else: result = isNone else: result = isNone
proc isObjectSubtype(a, f: PType): bool = proc isObjectSubtype(a, f: PType): bool =
var t = a var t = a
while t != nil and t.id != f.id: t = base(t) while t != nil and not sameObjectTypes(f, t): t = t.sons[0]
result = t != nil result = t != nil
proc minRel(a, b: TTypeRelation): TTypeRelation = proc minRel(a, b: TTypeRelation): TTypeRelation =
@ -240,8 +240,8 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
return typeRel(mapping, f, a.sons[0]) return typeRel(mapping, f, a.sons[0])
case f.kind case f.kind
of tyEnum: of tyEnum:
if a.kind == f.kind and a.id == f.id: result = isEqual if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual
elif skipTypes(a, {tyRange}).id == f.id: result = isSubtype elif sameEnumTypes(f, skipTypes(a, {tyRange})): result = isSubtype
of tyBool, tyChar: of tyBool, tyChar:
if a.kind == f.kind: result = isEqual if a.kind == f.kind: result = isEqual
elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype
@ -327,11 +327,11 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
of tyTuple: of tyTuple:
if a.kind == tyTuple: result = tupleRel(mapping, f, a) if a.kind == tyTuple: result = tupleRel(mapping, f, a)
of tyObject: of tyObject:
if a.kind == tyObject: if a.kind == tyObject:
if a.id == f.id: result = isEqual if sameObjectTypes(f, a): result = isEqual
elif isObjectSubtype(a, f): result = isSubtype elif isObjectSubtype(a, f): result = isSubtype
of tyDistinct: of tyDistinct:
if (a.kind == tyDistinct) and (a.id == f.id): result = isEqual if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual
of tySet: of tySet:
if a.kind == tySet: if a.kind == tySet:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):

View file

@ -32,9 +32,7 @@ proc IterOverType*(t: PType, iter: TTypeIter, closure: PObject): bool
# Returns result of `iter`. # Returns result of `iter`.
proc mutateType*(t: PType, iter: TTypeMutator, closure: PObject): PType proc mutateType*(t: PType, iter: TTypeMutator, closure: PObject): PType
# Returns result of `iter`. # Returns result of `iter`.
proc SameType*(x, y: PType): bool
proc SameTypeOrNil*(a, b: PType): bool
proc equalOrDistinctOf*(x, y: PType): bool
type type
TParamsEquality* = enum # they are equal, but their TParamsEquality* = enum # they are equal, but their
# identifiers or their return # identifiers or their return
@ -543,6 +541,53 @@ proc lengthOrd(t: PType): biggestInt =
of tyInt64, tyInt32, tyInt: result = lastOrd(t) of tyInt64, tyInt32, tyInt: result = lastOrd(t)
of tyDistinct, tyConst, tyMutable: result = lengthOrd(t.sons[0]) of tyDistinct, tyConst, tyMutable: result = lengthOrd(t.sons[0])
else: result = lastOrd(t) - firstOrd(t) + 1 else: result = lastOrd(t) - firstOrd(t) + 1
# -------------- type equality -----------------------------------------------
type
TDistinctCompare* = enum ## how distinct types are to be compared
dcEq, ## a and b should be the same type
dcEqIgnoreDistinct, ## compare symetrically: (distinct a) == b, a == b
## or a == (distinct b)
dcEqOrDistinctOf ## a equals b or a is distinct of b
TSameTypeClosure = object {.pure.}
cmp: TDistinctCompare
initSets: bool
recCheck: int
a: TIntSet
b: TIntSet
proc initSameTypeClosure: TSameTypeClosure =
# we do the initialization lazy for performance (avoids memory allocations)
nil
proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool =
result = c.initSets and c.a.contains(a.id) and c.b.contains(b.id)
if not result:
if not c.initSets:
c.initSets = true
c.a = initIntSet()
c.b = initIntSet()
c.a.incl(a.id)
c.b.incl(b.id)
proc SameTypeAux(x, y: PType, c: var TSameTypeClosure): bool
proc SameTypeOrNilAux(a, b: PType, c: var TSameTypeClosure): bool =
if a == b:
result = true
else:
if a == nil or b == nil: result = false
else: result = SameTypeAux(a, b, c)
proc SameTypeOrNil*(a, b: PType): bool =
if a == b:
result = true
else:
if a == nil or b == nil: result = false
else:
var c = initSameTypeClosure()
result = SameTypeAux(a, b, c)
proc equalParam(a, b: PSym): TParamsEquality = proc equalParam(a, b: PSym): TParamsEquality =
if SameTypeOrNil(a.typ, b.typ): if SameTypeOrNil(a.typ, b.typ):
@ -587,13 +632,6 @@ proc equalParams(a, b: PNode): TParamsEquality =
result = paramsIncompatible # overloading by different result = paramsIncompatible # overloading by different
# result types does not work # result types does not work
proc SameTypeOrNil(a, b: PType): bool =
if a == b:
result = true
else:
if a == nil or b == nil: result = false
else: result = SameType(a, b)
proc SameLiteral(x, y: PNode): bool = proc SameLiteral(x, y: PNode): bool =
if x.kind == y.kind: if x.kind == y.kind:
case x.kind case x.kind
@ -606,15 +644,14 @@ proc SameRanges(a, b: PNode): bool =
result = SameLiteral(a.sons[0], b.sons[0]) and result = SameLiteral(a.sons[0], b.sons[0]) and
SameLiteral(a.sons[1], b.sons[1]) SameLiteral(a.sons[1], b.sons[1])
proc sameTuple(a, b: PType, DistinctOf: bool): bool = proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
# two tuples are equivalent iff the names, types and positions are the same; # two tuples are equivalent iff the names, types and positions are the same;
# however, both types may not have any field names (t.n may be nil) which # however, both types may not have any field names (t.n may be nil) which
# complicates the matter a bit. # complicates the matter a bit.
if sonsLen(a) == sonsLen(b): if sonsLen(a) == sonsLen(b):
result = true result = true
for i in countup(0, sonsLen(a) - 1): for i in countup(0, sonsLen(a) - 1):
if DistinctOf: result = equalOrDistinctOf(a.sons[i], b.sons[i]) result = SameTypeAux(a.sons[i], b.sons[i], c)
else: result = SameType(a.sons[i], b.sons[i])
if not result: return if not result: return
if a.n != nil and b.n != nil: if a.n != nil and b.n != nil:
for i in countup(0, sonsLen(a.n) - 1): for i in countup(0, sonsLen(a.n) - 1):
@ -627,74 +664,138 @@ proc sameTuple(a, b: PType, DistinctOf: bool): bool =
if not result: break if not result: break
else: else:
result = false result = false
proc SameType(x, y: PType): bool = template IfFastObjectTypeCheckFailed(a, b: PType, body: stmt) =
if tfFromGeneric notin a.flags + b.flags:
# fast case: id comparison suffices:
result = a.id == b.id
else:
# expensive structural equality test; however due to the way generic and
# objects work, if one of the types does **not** contain tfFromGeneric,
# they cannot be equal. The check ``a.sym.Id == b.sym.Id`` checks
# for the same origin and is essential because we don't want "pure"
# structural type equivalence:
#
# type
# TA[T] = object
# TB[T] = object
# --> TA[int] != TB[int]
if tfFromGeneric in a.flags * b.flags and a.sym.Id == b.sym.Id:
# ok, we need the expensive structural check
body
proc sameObjectTypes*(a, b: PType): bool =
# specialized for efficiency (sigmatch uses it)
IfFastObjectTypeCheckFailed(a, b):
var c = initSameTypeClosure()
result = sameTypeAux(a, b, c)
proc sameDistinctTypes*(a, b: PType): bool {.inline.} =
result = sameObjectTypes(a, b)
proc sameEnumTypes*(a, b: PType): bool {.inline.} =
result = a.id == b.id
proc SameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool =
if a == b:
result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind):
if sameTypeOrNilAux(a.typ, b.typ, c):
case a.kind
of nkSym:
# same symbol as string is enough:
result = a.sym.name.id == b.sym.name.id
of nkIdent: result = a.ident.id == b.ident.id
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkEmpty, nkNilLit, nkType: result = true
else:
if sonsLen(a) == sonsLen(b):
for i in countup(0, sonsLen(a) - 1):
if not SameObjectTree(a.sons[i], b.sons[i], c): return
result = true
proc sameObjectStructures(a, b: PType, c: var TSameTypeClosure): bool =
# check base types:
if sonsLen(a) != sonsLen(b): return
for i in countup(0, sonsLen(a) - 1):
if not SameTypeOrNilAux(a.sons[i], b.sons[i], c): return
if not SameObjectTree(a.n, b.n, c): return
result = true
proc SameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
template CycleCheck() =
# believe it or not, the direct check for ``containsOrIncl(c, a, b)``
# increases bootstrapping time from 2.4s to 3.3s on my laptop! So we cheat
# again: Since the recursion check is only to not get caught in an endless
# recursion, we use a counter and only if it's value is over some
# threshold we perform the expansive exact cycle check:
if c.recCheck < 20:
inc c.recCheck
else:
if containsOrIncl(c, a, b): return true
if x == y: return true if x == y: return true
var a = skipTypes(x, {tyGenericInst}) var a = skipTypes(x, {tyGenericInst})
var b = skipTypes(y, {tyGenericInst}) var b = skipTypes(y, {tyGenericInst})
assert(a != nil) assert(a != nil)
assert(b != nil) assert(b != nil)
if a.kind != b.kind: if a.kind != b.kind:
return false case c.cmp
of dcEq: return false
of dcEqIgnoreDistinct:
while a.kind == tyDistinct: a = a.sons[0]
while b.kind == tyDistinct: b = b.sons[0]
if a.kind != b.kind: return false
of dcEqOrDistinctOf:
while a.kind == tyDistinct: a = a.sons[0]
if a.kind != b.kind: return false
case a.Kind case a.Kind
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
tyInt..tyBigNum, tyExpr, tyStmt, tyTypeDesc: tyInt..tyBigNum, tyExpr, tyStmt, tyTypeDesc:
result = true result = true
of tyEnum, tyForward, tyObject, tyDistinct, tyProxy: result = (a.id == b.id) of tyObject:
of tyTuple: result = sameTuple(a, b, false) IfFastObjectTypeCheckFailed(a, b):
of tyGenericInst: result = sameType(lastSon(a), lastSon(b)) CycleCheck()
of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, tyOrdinal, result = sameObjectStructures(a, b, c)
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc, of tyDistinct:
tyConst, tyMutable, tyVarargs, tyIter: CycleCheck()
if sonsLen(a) == sonsLen(b): result = sameTypeAux(a.sons[0], b.sons[0], c)
of tyEnum, tyForward, tyProxy:
# XXX generic enums do not make much sense, but require structural checking
result = a.id == b.id
of tyTuple:
CycleCheck()
result = sameTuple(a, b, c)
of tyGenericInst: result = sameTypeAux(lastSon(a), lastSon(b), c)
of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence,
tyOrdinal, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr,
tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter:
if sonsLen(a) == sonsLen(b):
CycleCheck()
result = true result = true
for i in countup(0, sonsLen(a) - 1): for i in countup(0, sonsLen(a) - 1):
result = SameTypeOrNil(a.sons[i], b.sons[i]) # BUGFIX result = SameTypeOrNilAux(a.sons[i], b.sons[i], c)
if not result: return if not result: return
if result and (a.kind == tyProc): if result and (a.kind == tyProc):
result = a.callConv == b.callConv # BUGFIX result = a.callConv == b.callConv
else: of tyRange:
result = false CycleCheck()
of tyRange: result = SameTypeOrNilAux(a.sons[0], b.sons[0], c) and
result = SameTypeOrNil(a.sons[0], b.sons[0]) and
SameValue(a.n.sons[0], b.n.sons[0]) and SameValue(a.n.sons[0], b.n.sons[0]) and
SameValue(a.n.sons[1], b.n.sons[1]) SameValue(a.n.sons[1], b.n.sons[1])
of tyNone: result = false of tyNone: result = false
proc SameType*(x, y: PType): bool =
var c = initSameTypeClosure()
result = sameTypeAux(x, y, c)
proc equalOrDistinctOf(x, y: PType): bool = proc compareTypes*(x, y: PType, cmp: TDistinctCompare): bool =
if x == y: return true ## compares two type for equality (modulo type distinction)
if x == nil or y == nil: return false var c = initSameTypeClosure()
var a = skipTypes(x, {tyGenericInst}) c.cmp = cmp
var b = skipTypes(y, {tyGenericInst}) result = sameTypeAux(x, y, c)
assert(a != nil)
assert(b != nil)
if a.kind != b.kind:
if a.kind == tyDistinct: a = a.sons[0]
if a.kind != b.kind:
return false
case a.Kind
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
tyInt..tyBigNum, tyExpr, tyStmt, tyTypeDesc:
result = true
of tyEnum, tyForward, tyObject, tyDistinct, tyProxy: result = (a.id == b.id)
of tyTuple: result = sameTuple(a, b, true)
of tyGenericInst: result = equalOrDistinctOf(lastSon(a), lastSon(b))
of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, tyOrdinal,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc,
tyConst, tyMutable, tyVarargs, tyIter:
if sonsLen(a) == sonsLen(b):
result = true
for i in countup(0, sonsLen(a) - 1):
result = equalOrDistinctOf(a.sons[i], b.sons[i])
if not result: return
if result and (a.kind == tyProc): result = a.callConv == b.callConv
else:
result = false
of tyRange:
result = equalOrDistinctOf(a.sons[0], b.sons[0]) and
SameValue(a.n.sons[0], b.n.sons[0]) and
SameValue(a.n.sons[1], b.n.sons[1])
of tyNone: result = false
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool
proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool = proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool =
@ -927,8 +1028,9 @@ proc computeSize(typ: PType): biggestInt =
proc getReturnType*(s: PSym): PType = proc getReturnType*(s: PSym): PType =
# Obtains the return type of a iterator/proc/macro/template # Obtains the return type of a iterator/proc/macro/template
assert s.kind in { skProc, skTemplate, skMacro, skIterator } assert s.kind in {skProc, skTemplate, skMacro, skIterator}
result = s.typ.n.sons[0].typ # XXX ask Zahary if this change broke something
result = s.typ.sons[0]
proc getSize(typ: PType): biggestInt = proc getSize(typ: PType): biggestInt =
result = computeSize(typ) result = computeSize(typ)

View file

@ -16,11 +16,11 @@ Version 0.8.14
incremental compilation incremental compilation
----------------------- -----------------------
- object types need to be compared by container ID! - fix DLL interfacing!
- the loading has to be MUCH more lazy! --> next version: We should re-load
symbol.ast lazily
- adapt thread var implementation to care about the new merge operation - adapt thread var implementation to care about the new merge operation
- write test cases: needs test script support - write test cases: needs test script support
- stress test with whole compiler
- test DLL interfacing!
- test thread var - test thread var
- automate tests: - automate tests: