# # # The Nim Compiler # (c) Copyright 2013 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, renderer, options, lineinfos, int128 type TPreferedDesc* = enum preferName, # default preferDesc, # probably should become what preferResolved is preferExported, preferModuleInfo, # fully qualified preferGenericArg, preferTypeName, preferResolved, # fully resolved symbols preferMixed, # most useful, shows: symbol + resolved symbols if it differs, eg: # tuple[a: MyInt{int}, b: float] proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string template `$`*(typ: PType): string = typeToString(typ) proc base*(t: PType): PType = result = t[0] # ------------------- type iterator: ---------------------------------------- type TTypeIter* = proc (t: PType, closure: RootRef): bool {.nimcall.} # true if iteration should stop TTypeMutator* = proc (t: PType, closure: RootRef): PType {.nimcall.} # copy t and mutate it TTypePredicate* = proc (t: PType): bool {.nimcall.} proc iterOverType*(t: PType, iter: TTypeIter, closure: RootRef): bool # Returns result of `iter`. proc mutateType*(t: PType, iter: TTypeMutator, closure: RootRef): 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 const # TODO: Remove tyTypeDesc from each abstractX and (where necessary) # replace with typedescX abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tyInferred, tySink, tyLent, tyOwned} abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tyInferred, tySink, tyLent, tyOwned} abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tyInferred, tySink, tyOwned} # see also ast.abstractVarRange abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tyInferred, tySink, tyOwned} abstractInstOwned* = abstractInst + {tyOwned} skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyTypeDesc, tyAlias, tyInferred, tySink, tyLent, tyOwned} # typedescX is used if we're sure tyTypeDesc should be included (or skipped) typedescPtrs* = abstractPtrs + {tyTypeDesc} typedescInst* = abstractInst + {tyTypeDesc, tyOwned} proc invalidGenericInst*(f: PType): bool = result = f.kind == tyGenericInst and lastSon(f) == nil proc isPureObject*(typ: PType): bool = var t = typ while t.kind == tyObject and t[0] != nil: t = t[0].skipTypes(skipPtrs) result = t.sym != nil and sfPure in t.sym.flags proc isUnsigned*(t: PType): bool = t.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64} proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 = var k = n.kind if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}: k = nkUIntLit case k of nkCharLit, nkUIntLit..nkUInt64Lit: # XXX: enable this assert #assert n.typ == nil or isUnsigned(n.typ), $n.typ toInt128(cast[uint64](n.intVal)) of nkIntLit..nkInt64Lit: # XXX: enable this assert #assert n.typ == nil or not isUnsigned(n.typ), $n.typ.kind toInt128(n.intVal) of nkNilLit: int128.Zero of nkHiddenStdConv: getOrdValue(n[1], onError) else: # XXX: The idea behind the introduction of int128 was to finally # have all calculations numerically far away from any # overflows. This command just introduces such overflows and # should therefore really be revisited. onError proc getFloatValue*(n: PNode): BiggestFloat = case n.kind of nkFloatLiterals: n.floatVal of nkHiddenStdConv: getFloatValue(n[1]) else: NaN proc isIntLit*(t: PType): bool {.inline.} = result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit proc isFloatLit*(t: PType): bool {.inline.} = result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit proc addDeclaredLoc(result: var string, conf: ConfigRef; sym: PSym) = result.add " [declared in " & conf$sym.info & "]" proc addTypeHeader*(result: var string, conf: ConfigRef; typ: PType; prefer: TPreferedDesc = preferMixed; getDeclarationPath = true) = result.add typeToString(typ, prefer) if getDeclarationPath: result.addDeclaredLoc(conf, typ.sym) proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName; getDeclarationPath = true): string = assert sym != nil # consider using `skipGenericOwner` to avoid fun2.fun2 when fun2 is generic result = sym.owner.name.s & '.' & sym.name.s if sym.kind in routineKinds: result.add '(' var n = sym.typ.n for i in 1.. 0: result.add ": " var first = true for son in t.sons: if not first: result.add " or " result.add son.typeToString first = false else: result = t.sym.owner.name.s & '.' & t.sym.name.s result.addTypeFlags(t) return case t.kind of tyInt: if not isIntLit(t) or prefer == preferExported: result = typeToStr[t.kind] else: if prefer == preferGenericArg: result = $t.n.intVal else: result = "int literal(" & $t.n.intVal & ")" of tyGenericInst, tyGenericInvocation: result = typeToString(t[0]) & '[' for i in 1.. 1: result.add(", ") result.add(typeToString(t[i], preferGenericArg)) result.add(']') of tyGenericBody: result = typeToString(t.lastSon) & '[' for i in 0.. 0: result.add(", ") result.add(typeToString(t[i], preferTypeName)) result.add(']') of tyTypeDesc: if t[0].kind == tyNone: result = "typedesc" else: result = "type " & typeToString(t[0]) of tyStatic: if prefer == preferGenericArg and t.n != nil: result = t.n.renderTree else: result = "static[" & (if t.len > 0: typeToString(t[0]) else: "") & "]" if t.n != nil: result.add "(" & renderTree(t.n) & ")" of tyUserTypeClass: if t.sym != nil and t.sym.owner != nil: if t.isResolvedUserTypeClass: return typeToString(t.lastSon) return t.sym.owner.name.s else: result = "" of tyBuiltInTypeClass: result = case t.base.kind: of tyVar: "var" of tyRef: "ref" of tyPtr: "ptr" of tySequence: "seq" of tyArray: "array" of tySet: "set" of tyRange: "range" of tyDistinct: "distinct" of tyProc: "proc" of tyObject: "object" of tyTuple: "tuple" of tyOpenArray: "openArray" else: typeToStr[t.base.kind] of tyInferred: let concrete = t.previouslyInferred if concrete != nil: result = typeToString(concrete) else: result = "inferred[" & typeToString(t.base) & "]" of tyUserTypeClassInst: let body = t.base result = body.sym.name.s & "[" for i in 1.. 1: result.add(", ") result.add(typeToString(t[i])) result.add "]" of tyAnd: for i, son in t.sons: result.add(typeToString(son)) if i < t.sons.high: result.add(" and ") of tyOr: for i, son in t.sons: result.add(typeToString(son)) if i < t.sons.high: result.add(" or ") of tyNot: result = "not " & typeToString(t[0]) of tyUntyped: #internalAssert t.len == 0 result = "untyped" of tyFromExpr: if t.n == nil: result = "unknown" else: result = "typeof(" & renderTree(t.n) & ")" of tyArray: result = "array" if t.len > 0: if t[0].kind == tyRange: result &= "[" & rangeToStr(t[0].n) & ", " & typeToString(t[1]) & ']' else: result &= "[" & typeToString(t[0]) & ", " & typeToString(t[1]) & ']' of tyUncheckedArray: result = "UncheckedArray" if t.len > 0: result &= "[" & typeToString(t[0]) & ']' of tySequence: if t.sym != nil and prefer != preferResolved: result = t.sym.name.s else: result = "seq" if t.len > 0: result &= "[" & typeToString(t[0]) & ']' of tyOrdinal: result = "ordinal" if t.len > 0: result &= "[" & typeToString(t[0]) & ']' of tySet: result = "set" if t.len > 0: result &= "[" & typeToString(t[0]) & ']' of tyOpenArray: result = "openArray" if t.len > 0: result &= "[" & typeToString(t[0]) & ']' of tyDistinct: result = "distinct " & typeToString(t[0], if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName) of tyTuple: # we iterate over t.sons here, because t.n may be nil if t.n != nil: result = "tuple[" assert(t.n.len == t.len) for i in 0..= 2: setLen(result, result.len-1) result.add '[' for i in 0.. 0 and t[0] != nil: result.add(": " & typeToString(t[0])) var prag = if t.callConv == ccDefault: "" else: CallingConvToStr[t.callConv] if tfNoSideEffect in t.flags: addSep(prag) prag.add("noSideEffect") if tfThread in t.flags: addSep(prag) prag.add("gcsafe") if t.lockLevel.ord != UnspecifiedLockLevel.ord: addSep(prag) prag.add("locks: " & $t.lockLevel) if prag.len != 0: result.add("{." & prag & ".}") of tyVarargs: result = typeToStr[t.kind] % typeToString(t[0]) of tySink: result = "sink " & typeToString(t[0]) of tyOwned: result = "owned " & typeToString(t[0]) else: result = typeToStr[t.kind] result.addTypeFlags(t) result = typeToString(typ, prefer) proc firstOrd*(conf: ConfigRef; t: PType): Int128 = case t.kind of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy: result = Zero of tySet, tyVar: result = firstOrd(conf, t[0]) of tyArray: result = firstOrd(conf, t[0]) of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) result = getOrdValue(t.n[0]) of tyInt: if conf != nil and conf.target.intSize == 4: result = toInt128(-2147483648) else: result = toInt128(0x8000000000000000'i64) of tyInt8: result = toInt128(-128) of tyInt16: result = toInt128(-32768) of tyInt32: result = toInt128(-2147483648) of tyInt64: result = toInt128(0x8000000000000000'i64) of tyUInt..tyUInt64: result = Zero of tyEnum: # if basetype <> nil then return firstOrd of basetype if t.len > 0 and t[0] != nil: result = firstOrd(conf, t[0]) else: assert(t.n[0].kind == nkSym) result = toInt128(t.n[0].sym.position) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, tyStatic, tyInferred, tyUserTypeClasses, tyLent: result = firstOrd(conf, lastSon(t)) of tyOrdinal: if t.len > 0: result = firstOrd(conf, lastSon(t)) else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') of tyUncheckedArray, tyCString: result = Zero else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') result = Zero proc firstFloat*(t: PType): BiggestFloat = case t.kind of tyFloat..tyFloat128: -Inf of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) getFloatValue(t.n[0]) of tyVar: firstFloat(t[0]) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, tyStatic, tyInferred, tyUserTypeClasses: firstFloat(lastSon(t)) else: internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')') NaN proc lastOrd*(conf: ConfigRef; t: PType): Int128 = case t.kind of tyBool: result = toInt128(1'u) of tyChar: result = toInt128(255'u) of tySet, tyVar: result = lastOrd(conf, t[0]) of tyArray: result = lastOrd(conf, t[0]) of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) result = getOrdValue(t.n[1]) of tyInt: if conf != nil and conf.target.intSize == 4: result = toInt128(0x7FFFFFFF) else: result = toInt128(0x7FFFFFFFFFFFFFFF'u64) of tyInt8: result = toInt128(0x0000007F) of tyInt16: result = toInt128(0x00007FFF) of tyInt32: result = toInt128(0x7FFFFFFF) of tyInt64: result = toInt128(0x7FFFFFFFFFFFFFFF'u64) of tyUInt: if conf != nil and conf.target.intSize == 4: result = toInt128(0xFFFFFFFF) else: result = toInt128(0xFFFFFFFFFFFFFFFF'u64) of tyUInt8: result = toInt128(0xFF) of tyUInt16: result = toInt128(0xFFFF) of tyUInt32: result = toInt128(0xFFFFFFFF) of tyUInt64: result = toInt128(0xFFFFFFFFFFFFFFFF'u64) of tyEnum: assert(t.n[^1].kind == nkSym) result = toInt128(t.n[^1].sym.position) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, tyStatic, tyInferred, tyUserTypeClasses, tyLent: result = lastOrd(conf, lastSon(t)) of tyProxy: result = Zero of tyOrdinal: if t.len > 0: result = lastOrd(conf, lastSon(t)) else: internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') of tyUncheckedArray: result = Zero else: internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') result = Zero proc lastFloat*(t: PType): BiggestFloat = case t.kind of tyFloat..tyFloat128: Inf of tyVar: lastFloat(t[0]) of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) getFloatValue(t.n[1]) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, tyStatic, tyInferred, tyUserTypeClasses: lastFloat(lastSon(t)) else: internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')') NaN proc floatRangeCheck*(x: BiggestFloat, t: PType): bool = case t.kind # This needs to be special cased since NaN is never # part of firstFloat(t)..lastFloat(t) of tyFloat..tyFloat128: true of tyRange: x in firstFloat(t)..lastFloat(t) of tyVar: floatRangeCheck(x, t[0]) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, tyStatic, tyInferred, tyUserTypeClasses: floatRangeCheck(x, lastSon(t)) else: internalError(newPartialConfigRef(), "invalid kind for floatRangeCheck:" & $t.kind) false proc lengthOrd*(conf: ConfigRef; t: PType): Int128 = if t.skipTypes(tyUserTypeClasses).kind == tyDistinct: result = lengthOrd(conf, t[0]) else: let last = lastOrd(conf, t) let first = firstOrd(conf, t) result = last - first + One # -------------- type equality ----------------------------------------------- type TDistinctCompare* = enum ## how distinct types are to be compared dcEq, ## a and b should be the same type dcEqIgnoreDistinct, ## compare symmetrically: (distinct a) == b, a == b ## or a == (distinct b) dcEqOrDistinctOf ## a equals b or a is distinct of b TTypeCmpFlag* = enum IgnoreTupleFields ## NOTE: Only set this flag for backends! IgnoreCC ExactTypeDescValues ExactGenericParams ExactConstraints ExactGcSafety AllowCommonBase TTypeCmpFlags* = set[TTypeCmpFlag] TSameTypeClosure = object cmp: TDistinctCompare recCheck: int flags: TTypeCmpFlags 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 lazily for performance (avoids memory allocations) discard proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool = result = c.s.len > 0 and c.s.contains((a.id, b.id)) if not result: when not defined(nimNoNilSeqs): 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 sameType*(a, b: PType, flags: TTypeCmpFlags = {}): bool = var c = initSameTypeClosure() c.flags = flags result = sameTypeAux(a, b, c) proc sameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool = if a == b: result = true else: if a == nil or b == nil: result = false else: result = sameType(a, b, flags) proc equalParam(a, b: PSym): TParamsEquality = if sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}) and exprStructuralEquivalent(a.constraint, b.constraint): 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 sameConstraints(a, b: PNode): bool = if isNil(a) and isNil(b): return true if a.len != b.len: return false for i in 1.. 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: var x = a.typ var y = b.typ if IgnoreTupleFields in c.flags: if x != nil: x = skipTypes(x, {tyRange, tyGenericInst, tyAlias}) if y != nil: y = skipTypes(y, {tyRange, tyGenericInst, tyAlias}) if sameTypeOrNilAux(x, y, 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 a.len == b.len: for i in 0..= a.len or a[i] == nil: return false a = a[i] result = a.kind == last proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, flags: TTypeAllowedFlags = {}): PType = assert(kind in {skVar, skLet, skConst, skProc, skFunc, skParam, skResult}) # if we have already checked the type, return true, because we stop the # evaluation if something is wrong: result = nil if typ == nil: return nil if containsOrIncl(marker, typ.id): return nil var t = skipTypes(typ, abstractInst-{tyTypeDesc}) case t.kind of tyVar, tyLent: if kind in {skProc, skFunc, skConst}: result = t elif t.kind == tyLent and kind != skResult: result = t else: var t2 = skipTypes(t[0], abstractInst-{tyTypeDesc}) case t2.kind of tyVar, tyLent: if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap of tyOpenArray: if kind != skParam or taIsOpenArray in flags: result = t else: result = typeAllowedAux(marker, t2[0], kind, flags+{taIsOpenArray}) of tyUncheckedArray: if kind != skParam: result = t else: result = typeAllowedAux(marker, t2[0], kind, flags) else: if kind notin {skParam, skResult}: result = t else: result = typeAllowedAux(marker, t2, kind, flags) of tyProc: if kind in {skVar, skLet, skConst} and taIsTemplateOrMacro in flags: result = t else: if isInlineIterator(typ) and kind in {skVar, skLet, skConst, skParam, skResult}: # only closure iterators may be assigned to anything. result = t let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags for i in 1.. " & "but expected '" & x & "'" if formal.kind == tyProc and actual.kind == tyProc: case compatibleEffects(formal, actual) of efCompat: discard of efRaisesDiffer: msg.add "\n.raise effects differ" of efRaisesUnknown: msg.add "\n.raise effect is 'can raise any'" of efTagsDiffer: msg.add "\n.tag effects differ" of efTagsUnknown: msg.add "\n.tag effect is 'any tag allowed'" of efLockLevelsDiffer: msg.add "\nlock levels differ" localError(conf, info, msg) proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool = if t == nil: return false if cycleDetector.containsOrIncl(t.id): return true case t.kind of tyTuple: var cycleDetectorCopy: IntSet for i in 0..