# # # The Nimrod Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # this module contains routines for accessing and iterating over types import intsets, ast, astalgo, trees, msgs, strutils, platform proc firstOrd*(t: PType): biggestInt proc lastOrd*(t: PType): biggestInt proc lengthOrd*(t: PType): biggestInt type TPreferedDesc* = enum preferName, preferDesc proc TypeToString*(typ: PType, prefer: TPreferedDesc = preferName): string proc getProcHeader*(sym: PSym): string proc base*(t: PType): PType # ------------------- type iterator: ---------------------------------------- type TTypeIter* = proc (t: PType, closure: PObject): bool # should return true if the iteration should stop TTypeMutator* = proc (t: PType, closure: PObject): PType # copy t and mutate it TTypePredicate* = proc (t: PType): bool proc IterOverType*(t: PType, iter: TTypeIter, closure: PObject): bool # Returns result of `iter`. proc mutateType*(t: PType, iter: TTypeMutator, closure: PObject): PType # Returns result of `iter`. type TParamsEquality* = enum # they are equal, but their # identifiers or their return # type differ (i.e. they cannot be # overloaded) # this used to provide better error messages paramsNotEqual, # parameters are not equal paramsEqual, # parameters are equal paramsIncompatible proc equalParams*(a, b: PNode): TParamsEquality # returns whether the parameter lists of the procs a, b are exactly the same proc isOrdinalType*(t: PType): bool proc enumHasHoles*(t: PType): bool const abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable} abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable} abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable} abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal, tyConst, tyMutable} abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable} skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyConst, tyMutable} proc skipTypes*(t: PType, kinds: TTypeKinds): PType proc containsObject*(t: PType): bool proc containsGarbageCollectedRef*(typ: PType): bool proc containsHiddenPointer*(typ: PType): bool proc canFormAcycle*(typ: PType): bool proc isCompatibleToCString*(a: PType): bool proc getOrdValue*(n: PNode): biggestInt proc computeSize*(typ: PType): biggestInt proc getSize*(typ: PType): biggestInt proc isPureObject*(typ: PType): bool proc InvalidGenericInst*(f: PType): bool # for debugging type TTypeFieldResult* = enum frNone, # type has no object type field frHeader, # type has an object type field only in the header frEmbedded # type has an object type field somewhere embedded proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult # this does a complex analysis whether a call to ``objectInit`` needs to be # made or intializing of the type field suffices or if there is no type field # at all in this type. proc typeAllowed*(t: PType, kind: TSymKind): bool # implementation proc InvalidGenericInst(f: PType): bool = result = (f.kind == tyGenericInst) and (lastSon(f) == nil) proc isPureObject(typ: PType): bool = var t = typ while t.sons[0] != nil: t = t.sons[0] result = t.sym != nil and sfPure in t.sym.flags proc getOrdValue(n: PNode): biggestInt = case n.kind of nkCharLit..nkInt64Lit: result = n.intVal of nkNilLit: result = 0 else: LocalError(n.info, errOrdinalTypeExpected) result = 0 proc isCompatibleToCString(a: PType): bool = if a.kind == tyArray: if (firstOrd(a.sons[0]) == 0) and (skipTypes(a.sons[0], {tyRange, tyConst, tyMutable, tyGenericInst}).kind in {tyInt..tyInt64, tyUInt..tyUInt64}) and (a.sons[1].kind == tyChar): result = true proc getProcHeader(sym: PSym): string = result = sym.name.s & '(' var n = sym.typ.n for i in countup(1, sonsLen(n) - 1): var p = n.sons[i] if p.kind != nkSym: InternalError("getProcHeader") add(result, p.sym.name.s) add(result, ": ") add(result, typeToString(p.sym.typ)) if i != sonsLen(n)-1: add(result, ", ") add(result, ')') if n.sons[0].typ != nil: result.add(": " & typeToString(n.sons[0].typ)) proc elemType*(t: PType): PType = assert(t != nil) case t.kind of tyGenericInst, tyDistinct: result = elemType(lastSon(t)) of tyArray, tyArrayConstr: result = t.sons[1] else: result = t.sons[0] assert(result != nil) proc skipGeneric(t: PType): PType = result = t while result.kind == tyGenericInst: result = lastSon(result) proc skipTypes(t: PType, kinds: TTypeKinds): PType = result = t while result.kind in kinds: result = lastSon(result) proc isOrdinalType(t: PType): bool = assert(t != nil) result = (t.Kind in {tyChar, tyInt..tyInt64, tyBool, tyEnum}) or (t.Kind in {tyRange, tyOrdinal, tyConst, tyMutable, tyGenericInst}) and isOrdinalType(t.sons[0]) proc enumHasHoles(t: PType): bool = var b = t while b.kind in {tyConst, tyMutable, tyRange, tyGenericInst}: b = b.sons[0] result = b.Kind == tyEnum and tfEnumHasHoles in b.flags proc iterOverTypeAux(marker: var TIntSet, t: PType, iter: TTypeIter, closure: PObject): bool proc iterOverNode(marker: var TIntSet, n: PNode, iter: TTypeIter, closure: PObject): bool = if n != nil: case n.kind of nkNone..nkNilLit: # a leaf result = iterOverTypeAux(marker, n.typ, iter, closure) else: for i in countup(0, sonsLen(n) - 1): result = iterOverNode(marker, n.sons[i], iter, closure) if result: return proc iterOverTypeAux(marker: var TIntSet, t: PType, iter: TTypeIter, closure: PObject): bool = result = false if t == nil: return result = iter(t, closure) if result: return if not ContainsOrIncl(marker, t.id): case t.kind of tyGenericInst, tyGenericBody: result = iterOverTypeAux(marker, lastSon(t), iter, closure) else: for i in countup(0, sonsLen(t) - 1): result = iterOverTypeAux(marker, t.sons[i], iter, closure) if result: return if t.n != nil: result = iterOverNode(marker, t.n, iter, closure) proc IterOverType(t: PType, iter: TTypeIter, closure: PObject): bool = var marker = InitIntSet() result = iterOverTypeAux(marker, t, iter, closure) proc searchTypeForAux(t: PType, predicate: TTypePredicate, marker: var TIntSet): bool proc searchTypeNodeForAux(n: PNode, p: TTypePredicate, marker: var TIntSet): bool = result = false case n.kind of nkRecList: for i in countup(0, sonsLen(n) - 1): result = searchTypeNodeForAux(n.sons[i], p, marker) if result: return of nkRecCase: assert(n.sons[0].kind == nkSym) result = searchTypeNodeForAux(n.sons[0], p, marker) if result: return for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind of nkOfBranch, nkElse: result = searchTypeNodeForAux(lastSon(n.sons[i]), p, marker) if result: return else: internalError("searchTypeNodeForAux(record case branch)") of nkSym: result = searchTypeForAux(n.sym.typ, p, marker) else: internalError(n.info, "searchTypeNodeForAux()") proc searchTypeForAux(t: PType, predicate: TTypePredicate, marker: var TIntSet): bool = # iterates over VALUE types! result = false if t == nil: return if ContainsOrIncl(marker, t.id): return result = Predicate(t) if result: return case t.kind of tyObject: result = searchTypeForAux(t.sons[0], predicate, marker) if not result: result = searchTypeNodeForAux(t.n, predicate, marker) of tyGenericInst, tyDistinct: result = searchTypeForAux(lastSon(t), predicate, marker) of tyArray, tyArrayConstr, tySet, tyTuple: for i in countup(0, sonsLen(t) - 1): result = searchTypeForAux(t.sons[i], predicate, marker) if result: return else: nil proc searchTypeFor(t: PType, predicate: TTypePredicate): bool = var marker = InitIntSet() result = searchTypeForAux(t, predicate, marker) proc isObjectPredicate(t: PType): bool = result = t.kind == tyObject proc containsObject(t: PType): bool = result = searchTypeFor(t, isObjectPredicate) proc isObjectWithTypeFieldPredicate(t: PType): bool = result = t.kind == tyObject and t.sons[0] == nil and not (t.sym != nil and sfPure in t.sym.flags) and tfFinal notin t.flags proc analyseObjectWithTypeFieldAux(t: PType, marker: var TIntSet): TTypeFieldResult = var res: TTypeFieldResult result = frNone if t == nil: return case t.kind of tyObject: if (t.n != nil): if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker): return frEmbedded for i in countup(0, sonsLen(t) - 1): res = analyseObjectWithTypeFieldAux(t.sons[i], marker) if res == frEmbedded: return frEmbedded if res == frHeader: result = frHeader if result == frNone: if isObjectWithTypeFieldPredicate(t): result = frHeader of tyGenericInst, tyDistinct, tyConst, tyMutable: result = analyseObjectWithTypeFieldAux(lastSon(t), marker) of tyArray, tyArrayConstr, tyTuple: for i in countup(0, sonsLen(t) - 1): res = analyseObjectWithTypeFieldAux(t.sons[i], marker) if res != frNone: return frEmbedded else: nil proc analyseObjectWithTypeField(t: PType): TTypeFieldResult = var marker = InitIntSet() result = analyseObjectWithTypeFieldAux(t, marker) proc isGBCRef(t: PType): bool = result = t.kind in GcTypeKinds proc containsGarbageCollectedRef(typ: PType): bool = # returns true if typ contains a reference, sequence or string (all the # things that are garbage-collected) result = searchTypeFor(typ, isGBCRef) proc isTyRef(t: PType): bool = result = t.kind == tyRef proc containsTyRef*(typ: PType): bool = # returns true if typ contains a 'ref' result = searchTypeFor(typ, isTyRef) proc isHiddenPointer(t: PType): bool = result = t.kind in {tyString, tySequence} proc containsHiddenPointer(typ: PType): bool = # returns true if typ contains a string, table or sequence (all the things # that need to be copied deeply) result = searchTypeFor(typ, isHiddenPointer) proc canFormAcycleAux(marker: var TIntSet, typ: PType, startId: int): bool proc canFormAcycleNode(marker: var TIntSet, n: PNode, startId: int): bool = result = false if n != nil: result = canFormAcycleAux(marker, n.typ, startId) if not result: case n.kind of nkNone..nkNilLit: nil else: for i in countup(0, sonsLen(n) - 1): result = canFormAcycleNode(marker, n.sons[i], startId) if result: return proc canFormAcycleAux(marker: var TIntSet, typ: PType, startId: int): bool = result = false if typ == nil: return if tfAcyclic in typ.flags: return var t = skipTypes(typ, abstractInst) if tfAcyclic in t.flags: return case t.kind of tyTuple, tyObject, tyRef, tySequence, tyArray, tyArrayConstr, tyOpenArray: if not ContainsOrIncl(marker, t.id): for i in countup(0, sonsLen(t) - 1): result = canFormAcycleAux(marker, t.sons[i], startId) if result: return if t.n != nil: result = canFormAcycleNode(marker, t.n, startId) else: result = t.id == startId else: nil proc canFormAcycle(typ: PType): bool = var marker = InitIntSet() result = canFormAcycleAux(marker, typ, typ.id) proc mutateTypeAux(marker: var TIntSet, t: PType, iter: TTypeMutator, closure: PObject): PType proc mutateNode(marker: var TIntSet, n: PNode, iter: TTypeMutator, closure: PObject): PNode = result = nil if n != nil: result = copyNode(n) result.typ = mutateTypeAux(marker, n.typ, iter, closure) case n.kind of nkNone..nkNilLit: # a leaf else: for i in countup(0, sonsLen(n) - 1): addSon(result, mutateNode(marker, n.sons[i], iter, closure)) proc mutateTypeAux(marker: var TIntSet, t: PType, iter: TTypeMutator, closure: PObject): PType = result = nil if t == nil: return result = iter(t, closure) if not ContainsOrIncl(marker, t.id): for i in countup(0, sonsLen(t) - 1): result.sons[i] = mutateTypeAux(marker, result.sons[i], iter, closure) if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure) assert(result != nil) proc mutateType(t: PType, iter: TTypeMutator, closure: PObject): PType = var marker = InitIntSet() result = mutateTypeAux(marker, t, iter, closure) proc rangeToStr(n: PNode): string = assert(n.kind == nkRange) result = ValueToString(n.sons[0]) & ".." & ValueToString(n.sons[1]) proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = const typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty", "Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc", "GenericInvokation", "GenericBody", "GenericInst", "GenericParam", "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", "pointer", "OpenArray[$1]", "string", "CString", "Forward", "int", "int8", "int16", "int32", "int64", "float", "float32", "float64", "float128", "uint", "uint8", "uint16", "uint32", "uint64", "bignum", "const ", "!", "varargs[$1]", "iter[$1]", "proxy[$1]"] var t = typ result = "" if t == nil: return if prefer == preferName and t.sym != nil: return t.sym.Name.s case t.Kind of tyGenericBody, tyGenericInst, tyGenericInvokation: result = typeToString(t.sons[0]) & '[' for i in countup(1, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)): if i > 1: add(result, ", ") add(result, typeToString(t.sons[i])) add(result, ']') of tyArray: if t.sons[0].kind == tyRange: result = "array[" & rangeToStr(t.sons[0].n) & ", " & typeToString(t.sons[1]) & ']' else: result = "array[" & typeToString(t.sons[0]) & ", " & typeToString(t.sons[1]) & ']' of tyArrayConstr: result = "Array constructor[" & rangeToStr(t.sons[0].n) & ", " & typeToString(t.sons[1]) & ']' of tySequence: result = "seq[" & typeToString(t.sons[0]) & ']' of tyOrdinal: result = "ordinal[" & typeToString(t.sons[0]) & ']' of tySet: result = "set[" & typeToString(t.sons[0]) & ']' of tyOpenArray: result = "openarray[" & typeToString(t.sons[0]) & ']' of tyDistinct: result = "distinct " & typeToString(t.sons[0], preferName) of tyTuple: # we iterate over t.sons here, because t.n may be nil result = "tuple[" if t.n != nil: assert(sonsLen(t.n) == sonsLen(t)) for i in countup(0, sonsLen(t.n) - 1): assert(t.n.sons[i].kind == nkSym) add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i])) if i < sonsLen(t.n) - 1: add(result, ", ") else: for i in countup(0, sonsLen(t) - 1): add(result, typeToString(t.sons[i])) if i < sonsLen(t) - 1: add(result, ", ") add(result, ']') of tyPtr, tyRef, tyVar, tyMutable, tyConst: result = typeToStr[t.kind] & typeToString(t.sons[0]) of tyRange: result = "range " & rangeToStr(t.n) of tyProc: result = "proc (" for i in countup(1, sonsLen(t) - 1): add(result, typeToString(t.sons[i])) if i < sonsLen(t) - 1: add(result, ", ") add(result, ')') if t.sons[0] != nil: add(result, ": " & TypeToString(t.sons[0])) var prag: string if t.callConv != ccDefault: prag = CallingConvToStr[t.callConv] else: prag = "" if tfNoSideEffect in t.flags: addSep(prag) add(prag, "noSideEffect") if tfThread in t.flags: addSep(prag) add(prag, "thread") if len(prag) != 0: add(result, "{." & prag & ".}") of tyVarargs, tyIter, tyProxy: result = typeToStr[t.kind] % typeToString(t.sons[0]) else: result = typeToStr[t.kind] proc resultType(t: PType): PType = assert(t.kind == tyProc) result = t.sons[0] # nil is allowed proc base(t: PType): PType = result = t.sons[0] proc firstOrd(t: PType): biggestInt = case t.kind of tyBool, tyChar, tySequence, tyOpenArray, tyString: result = 0 of tySet, tyVar: result = firstOrd(t.sons[0]) of tyArray, tyArrayConstr: result = firstOrd(t.sons[0]) of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) result = getOrdValue(t.n.sons[0]) of tyInt: if platform.intSize == 4: result = - (2147483646) - 2 else: result = 0x8000000000000000'i64 of tyInt8: result = - 128 of tyInt16: result = - 32768 of tyInt32: result = - 2147483646 - 2 of tyInt64: result = 0x8000000000000000'i64 of tyEnum: # if basetype <> nil then return firstOrd of basetype if (sonsLen(t) > 0) and (t.sons[0] != nil): result = firstOrd(t.sons[0]) else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position of tyGenericInst, tyDistinct, tyConst, tyMutable: result = firstOrd(lastSon(t)) else: InternalError("invalid kind for first(" & $t.kind & ')') result = 0 proc lastOrd(t: PType): biggestInt = case t.kind of tyBool: result = 1 of tyChar: result = 255 of tySet, tyVar: result = lastOrd(t.sons[0]) of tyArray, tyArrayConstr: result = lastOrd(t.sons[0]) of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) result = getOrdValue(t.n.sons[1]) of tyInt: if platform.intSize == 4: result = 0x7FFFFFFF else: result = 0x7FFFFFFFFFFFFFFF'i64 of tyInt8: result = 0x0000007F of tyInt16: result = 0x00007FFF of tyInt32: result = 0x7FFFFFFF of tyInt64: result = 0x7FFFFFFFFFFFFFFF'i64 of tyEnum: assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) result = t.n.sons[sonsLen(t.n) - 1].sym.position of tyGenericInst, tyDistinct, tyConst, tyMutable: result = lastOrd(lastSon(t)) else: InternalError("invalid kind for last(" & $t.kind & ')') result = 0 proc lengthOrd(t: PType): biggestInt = case t.kind of tyInt64, tyInt32, tyInt: result = lastOrd(t) of tyDistinct, tyConst, tyMutable: result = lengthOrd(t.sons[0]) 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 recCheck: int s: seq[tuple[a,b: int]] # seq for a set as it's hopefully faster # (few elements expected) proc initSameTypeClosure: TSameTypeClosure = # we do the initialization lazy for performance (avoids memory allocations) nil proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool = result = not IsNil(c.s) and c.s.contains((a.id, b.id)) if not result: if IsNil(c.s): c.s = @[] c.s.add((a.id, 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 = if SameTypeOrNil(a.typ, b.typ): if a.ast == b.ast: result = paramsEqual elif a.ast != nil and b.ast != nil: if ExprStructuralEquivalent(a.ast, b.ast): result = paramsEqual else: result = paramsIncompatible elif a.ast != nil: result = paramsEqual elif b.ast != nil: result = paramsIncompatible else: result = paramsNotEqual proc equalParams(a, b: PNode): TParamsEquality = result = paramsEqual var length = sonsLen(a) if length != sonsLen(b): result = paramsNotEqual else: for i in countup(1, length - 1): var m = a.sons[i].sym var n = b.sons[i].sym assert((m.kind == skParam) and (n.kind == skParam)) case equalParam(m, n) of paramsNotEqual: return paramsNotEqual of paramsEqual: nil of paramsIncompatible: result = paramsIncompatible if (m.name.id != n.name.id): # BUGFIX return paramsNotEqual # paramsIncompatible; # continue traversal! If not equal, we can return immediately; else # it stays incompatible if not SameTypeOrNil(a.sons[0].typ, b.sons[0].typ): if (a.sons[0].typ == nil) or (b.sons[0].typ == nil): result = paramsNotEqual # one proc has a result, the other not is OK else: result = paramsIncompatible # overloading by different # result types does not work proc SameLiteral(x, y: PNode): bool = if x.kind == y.kind: case x.kind of nkCharLit..nkInt64Lit: result = x.intVal == y.intVal of nkFloatLit..nkFloat64Lit: result = x.floatVal == y.floatVal of nkNilLit: result = true else: assert(false) proc SameRanges(a, b: PNode): bool = result = SameLiteral(a.sons[0], b.sons[0]) and SameLiteral(a.sons[1], b.sons[1]) proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = # 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 # complicates the matter a bit. if sonsLen(a) == sonsLen(b): result = true for i in countup(0, sonsLen(a) - 1): result = SameTypeAux(a.sons[i], b.sons[i], c) if not result: return if a.n != nil and b.n != nil: for i in countup(0, sonsLen(a.n) - 1): # check field names: if a.n.sons[i].kind != nkSym: InternalError(a.n.info, "sameTuple") if b.n.sons[i].kind != nkSym: InternalError(b.n.info, "sameTuple") var x = a.n.sons[i].sym var y = b.n.sons[i].sym result = x.name.id == y.name.id if not result: break else: result = false 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 < 3: inc c.recCheck else: if containsOrIncl(c, a, b): return true if x == y: return true var a = skipTypes(x, {tyGenericInst}) var b = skipTypes(y, {tyGenericInst}) assert(a != nil) assert(b != nil) if a.kind != b.kind: 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 of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, tyInt..tyBigNum, tyExpr, tyStmt, tyTypeDesc: result = true of tyObject: IfFastObjectTypeCheckFailed(a, b): CycleCheck() result = sameObjectStructures(a, b, c) of tyDistinct: CycleCheck() if c.cmp == dcEq: result = sameDistinctTypes(a, b) else: 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 for i in countup(0, sonsLen(a) - 1): result = SameTypeOrNilAux(a.sons[i], b.sons[i], c) if not result: return if result and (a.kind == tyProc): result = a.callConv == b.callConv of tyRange: CycleCheck() result = SameTypeOrNilAux(a.sons[0], b.sons[0], c) 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 SameType*(x, y: PType): bool = var c = initSameTypeClosure() result = sameTypeAux(x, y, c) proc compareTypes*(x, y: PType, cmp: TDistinctCompare): bool = ## compares two type for equality (modulo type distinction) var c = initSameTypeClosure() c.cmp = cmp result = sameTypeAux(x, y, c) proc inheritanceDiff*(a, b: PType): int = # | returns: 0 iff `a` == `b` # | returns: -x iff `a` is the x'th direct superclass of `b` # | returns: +x iff `a` is the x'th direct subclass of `b` # | returns: `maxint` iff `a` and `b` are not compatible at all var x = a result = 0 while x != nil: if sameObjectTypes(x, b): return x = x.sons[0] dec(result) var y = b result = 0 while y != nil: if sameObjectTypes(y, a): return y = y.sons[0] inc(result) result = high(int) proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool = result = true if n != nil: result = typeAllowedAux(marker, n.typ, kind) #if not result: debug(n.typ) if result: case n.kind of nkNone..nkNilLit: nil else: for i in countup(0, sonsLen(n) - 1): result = typeAllowedNode(marker, n.sons[i], kind) if not result: break proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]], last: TTypeKind): bool = var a = a for k, i in pattern.items: if a.kind != k: return false if i >= a.sonslen or a.sons[i] == nil: return false a = a.sons[i] result = a.kind == last proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool = assert(kind in {skVar, skLet, skConst, skParam, skResult}) # if we have already checked the type, return true, because we stop the # evaluation if something is wrong: r
#
#
#            Nim's Runtime Library
#        (c) Copyright 2013 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

include "system/inclrtl"

## This module contains the interface to the compiler's abstract syntax
## tree (`AST`:idx:). Macros operate on this tree.

## .. include:: ../doc/astspec.txt

type
  TNimrodNodeKind* = enum
    nnkNone, nnkEmpty, nnkIdent, nnkSym,
    nnkType, nnkCharLit, nnkIntLit, nnkInt8Li