1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041 |
- #
- #
- # 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
- ast, astalgo, trees, msgs, platform, renderer, options,
- lineinfos, int128, modulegraphs, astmsgs, wordrecg
- import std/[intsets, strutils]
- when defined(nimPreviewSlimSystem):
- import std/[assertions, formatfloat]
- 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, e.g.:
- # tuple[a: MyInt{int}, b: float]
- preferInlayHint,
- preferInferredEffects,
- TTypeRelation* = enum # order is important!
- isNone, isConvertible,
- isIntConv,
- isSubtype,
- isSubrange, # subrange of the wanted type; no type conversion
- # but apart from that counts as ``isSubtype``
- isBothMetaConvertible # generic proc parameter was matched against
- # generic type, e.g., map(mySeq, x=>x+1),
- # maybe recoverable by rerun if the parameter is
- # the proc's return value
- isInferred, # generic proc was matched against a concrete type
- isInferredConvertible, # same as above, but requiring proc CC conversion
- isGeneric,
- isFromIntLit, # conversion *from* int literal; proven safe
- isEqual
- ProcConvMismatch* = enum
- pcmNoSideEffect
- pcmNotGcSafe
- pcmNotIterator
- pcmDifferentCallConv
- proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
- proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) =
- if optDeclaredLocs in conf.globalOptions:
- result.add typeToString(typ, preferMixed)
- result.addDeclaredLoc(conf, typ)
- else:
- result.add typeToString(typ)
- template `$`*(typ: PType): string = typeToString(typ)
- # ------------------- type iterator: ----------------------------------------
- type
- TTypeIter* = proc (t: PType, closure: RootRef): bool {.nimcall.} # true if iteration should stop
- TTypePredicate* = proc (t: PType): bool {.nimcall.}
- proc iterOverType*(t: PType, iter: TTypeIter, closure: RootRef): bool
- # 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}
- 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, tyUserTypeClass}
- proc invalidGenericInst*(f: PType): bool =
- result = f.kind == tyGenericInst and skipModifier(f) == nil
- proc isPureObject*(typ: PType): bool =
- var t = typ
- while t.kind == tyObject and t.baseClass != nil:
- t = t.baseClass.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 getOrdValueAux*(n: PNode, err: var bool): 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:
- getOrdValueAux(n[1], err)
- else:
- err = true
- int128.Zero
- proc getOrdValue*(n: PNode): Int128 =
- var err: bool = false
- result = getOrdValueAux(n, err)
- #assert err == false
- proc getOrdValue*(n: PNode, onError: Int128): Int128 =
- var err = false
- result = getOrdValueAux(n, err)
- if err:
- result = 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 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..<n.len:
- let p = n[i]
- if p.kind == nkSym:
- result.add(p.sym.name.s)
- result.add(": ")
- result.add(typeToString(p.sym.typ, prefer))
- if i != n.len-1: result.add(", ")
- else:
- result.add renderTree(p)
- result.add(')')
- if n[0].typ != nil:
- result.add(": " & typeToString(n[0].typ, prefer))
- if getDeclarationPath: result.addDeclaredLoc(conf, sym)
- proc elemType*(t: PType): PType =
- assert(t != nil)
- case t.kind
- of tyGenericInst, tyDistinct, tyAlias, tySink: result = elemType(skipModifier(t))
- of tyArray: result = t.elementType
- of tyError: result = t
- else: result = t.elementType
- assert(result != nil)
- proc enumHasHoles*(t: PType): bool =
- var b = t.skipTypes({tyRange, tyGenericInst, tyAlias, tySink})
- result = b.kind == tyEnum and tfEnumHasHoles in b.flags
- proc isOrdinalType*(t: PType, allowEnumWithHoles: bool = false): bool =
- assert(t != nil)
- const
- baseKinds = {tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyEnum}
- parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tySink, tyDistinct}
- result = (t.kind in baseKinds and (not t.enumHasHoles or allowEnumWithHoles)) or
- (t.kind in parentKinds and isOrdinalType(t.skipModifier, allowEnumWithHoles))
- proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
- closure: RootRef): bool
- proc iterOverNode(marker: var IntSet, n: PNode, iter: TTypeIter,
- closure: RootRef): bool =
- if n != nil:
- case n.kind
- of nkNone..nkNilLit:
- # a leaf
- result = iterOverTypeAux(marker, n.typ, iter, closure)
- else:
- result = iterOverTypeAux(marker, n.typ, iter, closure)
- if result: return
- for i in 0..<n.len:
- result = iterOverNode(marker, n[i], iter, closure)
- if result: return
- else:
- result = false
- proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
- closure: RootRef): bool =
- result = false
- if t == nil: return
- result = iter(t, closure)
- if result: return
- if not containsOrIncl(marker, t.id):
- case t.kind
- of tyGenericBody:
- # treat as atomic, containsUnresolvedType wants always false,
- # containsGenericType always gives true
- discard
- of tyGenericInst, tyAlias, tySink, tyInferred:
- result = iterOverTypeAux(marker, skipModifier(t), iter, closure)
- else:
- for a in t.kids:
- result = iterOverTypeAux(marker, a, iter, closure)
- if result: return
- if t.n != nil and t.kind != tyProc: result = iterOverNode(marker, t.n, iter, closure)
- proc iterOverType(t: PType, iter: TTypeIter, closure: RootRef): bool =
- var marker = initIntSet()
- result = iterOverTypeAux(marker, t, iter, closure)
- proc searchTypeForAux(t: PType, predicate: TTypePredicate,
- marker: var IntSet): bool
- proc searchTypeNodeForAux(n: PNode, p: TTypePredicate,
- marker: var IntSet): bool =
- result = false
- case n.kind
- of nkRecList:
- for i in 0..<n.len:
- result = searchTypeNodeForAux(n[i], p, marker)
- if result: return
- of nkRecCase:
- assert(n[0].kind == nkSym)
- result = searchTypeNodeForAux(n[0], p, marker)
- if result: return
- for i in 1..<n.len:
- case n[i].kind
- of nkOfBranch, nkElse:
- result = searchTypeNodeForAux(lastSon(n[i]), p, marker)
- if result: return
- else: discard
- of nkSym:
- result = searchTypeForAux(n.sym.typ, p, marker)
- else: discard
- proc searchTypeForAux(t: PType, predicate: TTypePredicate,
- marker: var IntSet): 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:
- if t.baseClass != nil:
- result = searchTypeForAux(t.baseClass.skipTypes(skipPtrs), predicate, marker)
- if not result: result = searchTypeNodeForAux(t.n, predicate, marker)
- of tyGenericInst, tyDistinct, tyAlias, tySink:
- result = searchTypeForAux(skipModifier(t), predicate, marker)
- of tyArray, tySet, tyTuple:
- for a in t.kids:
- result = searchTypeForAux(a, predicate, marker)
- if result: return
- else:
- discard
- 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.baseClass == nil and
- not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and
- tfFinal notin t.flags
- 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 analyseObjectWithTypeFieldAux(t: PType,
- marker: var IntSet): TTypeFieldResult =
- result = frNone
- if t == nil: return
- case t.kind
- of tyObject:
- if t.n != nil:
- if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker):
- return frEmbedded
- var x = t.baseClass
- if x != nil: x = x.skipTypes(skipPtrs)
- let res = analyseObjectWithTypeFieldAux(x, marker)
- if res == frEmbedded:
- return frEmbedded
- if res == frHeader: result = frHeader
- if result == frNone:
- if isObjectWithTypeFieldPredicate(t): result = frHeader
- of tyGenericInst, tyDistinct, tyAlias, tySink:
- result = analyseObjectWithTypeFieldAux(skipModifier(t), marker)
- of tyArray, tyTuple:
- for a in t.kids:
- let res = analyseObjectWithTypeFieldAux(a, marker)
- if res != frNone:
- return frEmbedded
- else:
- discard
- proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult =
- # this does a complex analysis whether a call to ``objectInit`` needs to be
- # made or initializing of the type field suffices or if there is no type field
- # at all in this type.
- var marker = initIntSet()
- result = analyseObjectWithTypeFieldAux(t, marker)
- proc isGCRef(t: PType): bool =
- result = t.kind in GcTypeKinds or
- (t.kind == tyProc and t.callConv == ccClosure)
- if result and t.kind in {tyString, tySequence} and tfHasAsgn in t.flags:
- result = false
- 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, isGCRef)
- proc isManagedMemory(t: PType): bool =
- result = t.kind in GcTypeKinds or
- (t.kind == tyProc and t.callConv == ccClosure)
- proc containsManagedMemory*(typ: PType): bool =
- result = searchTypeFor(typ, isManagedMemory)
- proc isTyRef(t: PType): bool =
- result = t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure)
- 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, tyOpenArray, tyVarargs}
- 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(g: ModuleGraph; marker: var IntSet, typ: PType, orig: PType, withRef: bool, hasTrace: bool): bool
- proc canFormAcycleNode(g: ModuleGraph; marker: var IntSet, n: PNode, orig: PType, withRef: bool, hasTrace: bool): bool =
- result = false
- if n != nil:
- var hasCursor = n.kind == nkSym and sfCursor in n.sym.flags
- # cursor fields don't own the refs, which cannot form reference cycles
- if hasTrace or not hasCursor:
- result = canFormAcycleAux(g, marker, n.typ, orig, withRef, hasTrace)
- if not result:
- case n.kind
- of nkNone..nkNilLit:
- discard
- else:
- for i in 0..<n.len:
- result = canFormAcycleNode(g, marker, n[i], orig, withRef, hasTrace)
- if result: return
- proc sameBackendType*(x, y: PType): bool
- proc canFormAcycleAux(g: ModuleGraph, marker: var IntSet, typ: PType, orig: PType, withRef: bool, hasTrace: bool): bool =
- result = false
- if typ == nil: return
- if tfAcyclic in typ.flags: return
- var t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
- if tfAcyclic in t.flags: return
- case t.kind
- of tyRef, tyPtr, tyUncheckedArray:
- if t.kind == tyRef or hasTrace:
- if withRef and sameBackendType(t, orig):
- result = true
- elif not containsOrIncl(marker, t.id):
- result = canFormAcycleAux(g, marker, t.elementType, orig, withRef or t.kind != tyUncheckedArray, hasTrace)
- of tyObject:
- if withRef and sameBackendType(t, orig):
- result = true
- elif not containsOrIncl(marker, t.id):
- var hasTrace = hasTrace
- let op = getAttachedOp(g, t.skipTypes({tyRef}), attachedTrace)
- if op != nil and sfOverridden in op.flags:
- hasTrace = true
- if t.baseClass != nil:
- result = canFormAcycleAux(g, marker, t.baseClass, orig, withRef, hasTrace)
- if result: return
- if t.n != nil: result = canFormAcycleNode(g, marker, t.n, orig, withRef, hasTrace)
- # Inheritance can introduce cyclic types, however this is not relevant
- # as the type that is passed to 'new' is statically known!
- # er but we use it also for the write barrier ...
- if tfFinal notin t.flags:
- # damn inheritance may introduce cycles:
- result = true
- of tyTuple:
- if withRef and sameBackendType(t, orig):
- result = true
- elif not containsOrIncl(marker, t.id):
- for a in t.kids:
- result = canFormAcycleAux(g, marker, a, orig, withRef, hasTrace)
- if result: return
- of tySequence, tyArray, tyOpenArray, tyVarargs:
- if withRef and sameBackendType(t, orig):
- result = true
- elif not containsOrIncl(marker, t.id):
- result = canFormAcycleAux(g, marker, t.elementType, orig, withRef, hasTrace)
- of tyProc: result = typ.callConv == ccClosure
- else: discard
- proc isFinal*(t: PType): bool =
- let t = t.skipTypes(abstractInst)
- result = t.kind != tyObject or tfFinal in t.flags or isPureObject(t)
- proc canFormAcycle*(g: ModuleGraph, typ: PType): bool =
- var marker = initIntSet()
- let t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
- result = canFormAcycleAux(g, marker, t, t, false, false)
- proc valueToString(a: PNode): string =
- case a.kind
- of nkCharLit, nkUIntLit..nkUInt64Lit:
- result = $cast[uint64](a.intVal)
- of nkIntLit..nkInt64Lit:
- result = $a.intVal
- of nkFloatLit..nkFloat128Lit: result = $a.floatVal
- of nkStrLit..nkTripleStrLit: result = a.strVal
- of nkStaticExpr: result = "static(" & a[0].renderTree & ")"
- else: result = "<invalid value>"
- proc rangeToStr(n: PNode): string =
- assert(n.kind == nkRange)
- result = valueToString(n[0]) & ".." & valueToString(n[1])
- const
- typeToStr: array[TTypeKind, string] = ["None", "bool", "char", "empty",
- "Alias", "typeof(nil)", "untyped", "typed", "typeDesc",
- # xxx typeDesc=>typedesc: typedesc is declared as such, and is 10x more common.
- "GenericInvocation", "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",
- "owned", "sink",
- "lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type",
- "BuiltInTypeClass", "UserTypeClass",
- "UserTypeClassInst", "CompositeTypeClass", "inferred",
- "and", "or", "not", "any", "static", "TypeFromExpr", "concept", # xxx bugfix
- "void", "iterable"]
- const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo,
- preferGenericArg, preferResolved, preferMixed, preferInlayHint, preferInferredEffects}
- template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
- tc.add concrete
- tc.flags.incl tfResolved
- # TODO: It would be a good idea to kill the special state of a resolved
- # concept by switching to tyAlias within the instantiated procs.
- # Currently, tyAlias is always skipped with skipModifier, which means that
- # we can store information about the matched concept in another position.
- # Then builtInFieldAccess can be modified to properly read the derived
- # consts and types stored within the concept.
- template isResolvedUserTypeClass*(t: PType): bool =
- tfResolved in t.flags
- proc addTypeFlags(name: var string, typ: PType) {.inline.} =
- if tfNotNil in typ.flags: name.add(" not nil")
- proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
- let preferToplevel = prefer
- proc getPrefer(prefer: TPreferedDesc): TPreferedDesc =
- if preferToplevel in {preferResolved, preferMixed}:
- preferToplevel # sticky option
- else:
- prefer
- proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
- result = ""
- let prefer = getPrefer(prefer)
- let t = typ
- if t == nil: return
- if prefer in preferToResolveSymbols and t.sym != nil and
- sfAnon notin t.sym.flags and t.kind != tySequence:
- if t.kind == tyInt and isIntLit(t):
- if prefer == preferInlayHint:
- result = t.sym.name.s
- else:
- result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
- elif t.kind == tyAlias and t.elementType.kind != tyAlias:
- result = typeToString(t.elementType)
- elif prefer in {preferResolved, preferMixed}:
- case t.kind
- of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCstring}:
- result = typeToStr[t.kind]
- of tyGenericBody:
- result = typeToString(t.last)
- of tyCompositeTypeClass:
- # avoids showing `A[any]` in `proc fun(a: A)` with `A = object[T]`
- result = typeToString(t.last.last)
- else:
- result = t.sym.name.s
- if prefer == preferMixed and result != t.sym.name.s:
- result = t.sym.name.s & "{" & result & "}"
- elif prefer in {preferName, preferTypeName, preferInlayHint, preferInferredEffects} or t.sym.owner.isNil:
- # note: should probably be: {preferName, preferTypeName, preferGenericArg}
- result = t.sym.name.s
- if t.kind == tyGenericParam and t.genericParamHasConstraints:
- result.add ": "
- result.add t.elementType.typeToString
- 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:
- case prefer:
- of preferGenericArg:
- result = $t.n.intVal
- of preferInlayHint:
- result = "int"
- else:
- result = "int literal(" & $t.n.intVal & ")"
- of tyGenericInst:
- result = typeToString(t.genericHead) & '['
- for needsComma, a in t.genericInstParams:
- if needsComma: result.add(", ")
- result.add(typeToString(a, preferGenericArg))
- result.add(']')
- of tyGenericInvocation:
- result = typeToString(t.genericHead) & '['
- for needsComma, a in t.genericInvocationParams:
- if needsComma: result.add(", ")
- result.add(typeToString(a, preferGenericArg))
- result.add(']')
- of tyGenericBody:
- result = typeToString(t.typeBodyImpl) & '['
- for i, a in t.genericBodyParams:
- if i > 0: result.add(", ")
- result.add(typeToString(a, preferTypeName))
- result.add(']')
- of tyTypeDesc:
- if t.elementType.kind == tyNone: result = "typedesc"
- else: result = "typedesc[" & typeToString(t.elementType) & "]"
- of tyStatic:
- if prefer == preferGenericArg and t.n != nil:
- result = t.n.renderTree
- else:
- result = "static[" & (if t.hasElementType: typeToString(t.skipModifier) 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.last)
- return t.sym.owner.name.s
- else:
- result = "<invalid tyUserTypeClass>"
- 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 needsComma, a in t.userTypeClassInstParams:
- if needsComma: result.add(", ")
- result.add(typeToString(a))
- result.add "]"
- of tyAnd:
- for i, son in t.ikids:
- if i > 0: result.add(" and ")
- result.add(typeToString(son))
- of tyOr:
- for i, son in t.ikids:
- if i > 0: result.add(" or ")
- result.add(typeToString(son))
- of tyNot:
- result = "not " & typeToString(t.elementType)
- 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.hasElementType:
- if t.indexType.kind == tyRange:
- result &= "[" & rangeToStr(t.indexType.n) & ", " &
- typeToString(t.elementType) & ']'
- else:
- result &= "[" & typeToString(t.indexType) & ", " &
- typeToString(t.elementType) & ']'
- of tyUncheckedArray:
- result = "UncheckedArray"
- if t.hasElementType:
- result &= "[" & typeToString(t.elementType) & ']'
- of tySequence:
- if t.sym != nil and prefer != preferResolved:
- result = t.sym.name.s
- else:
- result = "seq"
- if t.hasElementType:
- result &= "[" & typeToString(t.elementType) & ']'
- of tyOrdinal:
- result = "ordinal"
- if t.hasElementType:
- result &= "[" & typeToString(t.skipModifier) & ']'
- of tySet:
- result = "set"
- if t.hasElementType:
- result &= "[" & typeToString(t.elementType) & ']'
- of tyOpenArray:
- result = "openArray"
- if t.hasElementType:
- result &= "[" & typeToString(t.elementType) & ']'
- of tyDistinct:
- result = "distinct " & typeToString(t.elementType,
- if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName)
- of tyIterable:
- # xxx factor this pattern
- result = "iterable"
- if t.hasElementType:
- result &= "[" & typeToString(t.skipModifier) & ']'
- of tyTuple:
- # we iterate over t.sons here, because t.n may be nil
- if t.n != nil:
- result = "tuple["
- for i in 0..<t.n.len:
- assert(t.n[i].kind == nkSym)
- result.add(t.n[i].sym.name.s & ": " & typeToString(t.n[i].sym.typ))
- if i < t.n.len - 1: result.add(", ")
- result.add(']')
- elif t.isEmptyTupleType:
- result = "tuple[]"
- elif t.isSingletonTupleType:
- result = "("
- for son in t.kids:
- result.add(typeToString(son))
- result.add(",)")
- else:
- result = "("
- for i, son in t.ikids:
- if i > 0: result.add ", "
- result.add(typeToString(son))
- result.add(')')
- of tyPtr, tyRef, tyVar, tyLent:
- result = if isOutParam(t): "out " else: typeToStr[t.kind]
- result.add typeToString(t.elementType)
- of tyRange:
- result = "range "
- if t.n != nil and t.n.kind == nkRange:
- result.add rangeToStr(t.n)
- if prefer != preferExported:
- result.add("(" & typeToString(t.elementType) & ")")
- of tyProc:
- result = if tfIterator in t.flags: "iterator "
- elif t.owner != nil:
- case t.owner.kind
- of skTemplate: "template "
- of skMacro: "macro "
- of skConverter: "converter "
- else: "proc "
- else:
- "proc "
- if tfUnresolved in t.flags: result.add "[*missing parameters*]"
- result.add "("
- for i, a in t.paramTypes:
- if i > FirstParamAt: result.add(", ")
- let j = paramTypeToNodeIndex(i)
- if t.n != nil and j < t.n.len and t.n[j].kind == nkSym:
- result.add(t.n[j].sym.name.s)
- result.add(": ")
- result.add(typeToString(a))
- result.add(')')
- if t.returnType != nil: result.add(": " & typeToString(t.returnType))
- var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: $t.callConv
- var hasImplicitRaises = false
- if not isNil(t.owner) and not isNil(t.owner.ast) and (t.owner.ast.len - 1) >= pragmasPos:
- let pragmasNode = t.owner.ast[pragmasPos]
- let raisesSpec = effectSpec(pragmasNode, wRaises)
- if not isNil(raisesSpec):
- addSep(prag)
- prag.add("raises: ")
- prag.add($raisesSpec)
- hasImplicitRaises = true
- if tfNoSideEffect in t.flags:
- addSep(prag)
- prag.add("noSideEffect")
- if tfThread in t.flags:
- addSep(prag)
- prag.add("gcsafe")
- var effectsOfStr = ""
- for i, a in t.paramTypes:
- let j = paramTypeToNodeIndex(i)
- if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags:
- addSep(effectsOfStr)
- effectsOfStr.add(t.n[j].sym.name.s)
- if effectsOfStr != "":
- addSep(prag)
- prag.add("effectsOf: ")
- prag.add(effectsOfStr)
- if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0):
- let effects = t.owner.typ.n[0]
- if effects.kind == nkEffectList and effects.len == effectListLen:
- var inferredRaisesStr = ""
- let effs = effects[exceptionEffects]
- if not isNil(effs):
- for eff in items(effs):
- if not isNil(eff):
- addSep(inferredRaisesStr)
- inferredRaisesStr.add($eff.typ)
- addSep(prag)
- prag.add("raises: <inferred> [")
- prag.add(inferredRaisesStr)
- prag.add("]")
- if prag.len != 0: result.add("{." & prag & ".}")
- of tyVarargs:
- result = typeToStr[t.kind] % typeToString(t.elementType)
- of tySink:
- result = "sink " & typeToString(t.skipModifier)
- of tyOwned:
- result = "owned " & typeToString(t.elementType)
- 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, tyError:
- result = Zero
- of tySet, tyVar: result = firstOrd(conf, t.elementType)
- of tyArray: result = firstOrd(conf, t.indexType)
- of tyRange:
- assert(t.n != nil) # range directly given:
- assert(t.n.kind == nkRange)
- result = getOrdValue(t.n[0])
- of tyInt:
- if conf != nil:
- case conf.target.intSize
- of 8: result = toInt128(0x8000000000000000'i64)
- of 4: result = toInt128(-2147483648)
- of 2: result = toInt128(-32768)
- of 1: result = toInt128(-128)
- else: result = Zero
- 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.baseClass != nil:
- result = firstOrd(conf, t.baseClass)
- else:
- if t.n.len > 0:
- assert(t.n[0].kind == nkSym)
- result = toInt128(t.n[0].sym.position)
- else:
- result = Zero
- of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
- tyStatic, tyInferred, tyLent:
- result = firstOrd(conf, skipModifier(t))
- of tyUserTypeClasses:
- result = firstOrd(conf, last(t))
- of tyOrdinal:
- if t.hasElementType: result = firstOrd(conf, skipModifier(t))
- else:
- result = Zero
- fatal(conf, unknownLineInfo, "invalid kind for firstOrd(" & $t.kind & ')')
- of tyUncheckedArray, tyCstring:
- result = Zero
- else:
- result = Zero
- fatal(conf, unknownLineInfo, "invalid kind for firstOrd(" & $t.kind & ')')
- 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.elementType)
- of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
- tyStatic, tyInferred:
- firstFloat(skipModifier(t))
- of tyUserTypeClasses:
- firstFloat(last(t))
- else:
- internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')')
- NaN
- proc targetSizeSignedToKind*(conf: ConfigRef): TTypeKind =
- case conf.target.intSize
- of 8: result = tyInt64
- of 4: result = tyInt32
- of 2: result = tyInt16
- else: result = tyNone
- proc targetSizeUnsignedToKind*(conf: ConfigRef): TTypeKind =
- case conf.target.intSize
- of 8: result = tyUInt64
- of 4: result = tyUInt32
- of 2: result = tyUInt16
- else: result = tyNone
- proc normalizeKind*(conf: ConfigRef, k: TTypeKind): TTypeKind =
- case k
- of tyInt:
- result = conf.targetSizeSignedToKind()
- of tyUInt:
- result = conf.targetSizeUnsignedToKind()
- else:
- result = k
- 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.elementType)
- of tyArray: result = lastOrd(conf, t.indexType)
- of tyRange:
- assert(t.n != nil) # range directly given:
- assert(t.n.kind == nkRange)
- result = getOrdValue(t.n[1])
- of tyInt:
- if conf != nil:
- case conf.target.intSize
- of 8: result = toInt128(0x7FFFFFFFFFFFFFFF'u64)
- of 4: result = toInt128(0x7FFFFFFF)
- of 2: result = toInt128(0x00007FFF)
- of 1: result = toInt128(0x0000007F)
- else: result = Zero
- 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:
- if t.n.len > 0:
- assert(t.n[^1].kind == nkSym)
- result = toInt128(t.n[^1].sym.position)
- else:
- result = Zero
- of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
- tyStatic, tyInferred, tyLent:
- result = lastOrd(conf, skipModifier(t))
- of tyUserTypeClasses:
- result = lastOrd(conf, last(t))
- of tyError: result = Zero
- of tyOrdinal:
- if t.hasElementType: result = lastOrd(conf, skipModifier(t))
- else:
- result = Zero
- fatal(conf, unknownLineInfo, "invalid kind for lastOrd(" & $t.kind & ')')
- of tyUncheckedArray:
- result = Zero
- else:
- result = Zero
- fatal(conf, unknownLineInfo, "invalid kind for lastOrd(" & $t.kind & ')')
- proc lastFloat*(t: PType): BiggestFloat =
- case t.kind
- of tyFloat..tyFloat128: Inf
- of tyVar: lastFloat(t.elementType)
- 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:
- lastFloat(skipModifier(t))
- of tyUserTypeClasses:
- lastFloat(last(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.elementType)
- of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
- tyStatic, tyInferred:
- floatRangeCheck(x, skipModifier(t))
- of tyUserTypeClasses:
- floatRangeCheck(x, last(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.skipModifier)
- 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
- PickyCAliases # be picky about the distinction between 'cint' and 'int32'
- IgnoreFlags # used for borrowed functions and methods; ignores the tfVarIsPtr flag
- PickyBackendAliases # be picky about different aliases
- IgnoreRangeShallow
- 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)
- result = TSameTypeClosure()
- 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:
- 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
- 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..<a.len:
- if not exprStructuralEquivalent(a[i].sym.constraint,
- b[i].sym.constraint):
- return false
- return true
- proc equalParams(a, b: PNode): TParamsEquality =
- result = paramsEqual
- if a.len != b.len:
- result = paramsNotEqual
- else:
- for i in 1..<a.len:
- var m = a[i].sym
- var n = b[i].sym
- assert((m.kind == skParam) and (n.kind == skParam))
- case equalParam(m, n)
- of paramsNotEqual:
- return paramsNotEqual
- of paramsEqual:
- discard
- 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.typ, b.typ, {ExactTypeDescValues}):
- if (a.typ == nil) or (b.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 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 sameTupleLengths(a, b):
- result = true
- for i, aa, bb in tupleTypePairs(a, b):
- var x = aa
- var y = bb
- if IgnoreTupleFields in c.flags:
- x = skipTypes(x, {tyRange, tyGenericInst, tyAlias})
- y = skipTypes(y, {tyRange, tyGenericInst, tyAlias})
- result = sameTypeAux(x, y, c)
- if not result: return
- if a.n != nil and b.n != nil and IgnoreTupleFields notin c.flags:
- for i in 0..<a.n.len:
- # check field names:
- if a.n[i].kind == nkSym and b.n[i].kind == nkSym:
- var x = a.n[i].sym
- var y = b.n[i].sym
- result = x.name.id == y.name.id
- if not result: break
- else:
- return false
- elif a.n != b.n and (a.n == nil or b.n == nil) and IgnoreTupleFields notin c.flags:
- result = false
- else:
- result = false
- template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) =
- 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
- else:
- result = false
- 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:
- if not sameObjectTree(a[i], b[i], c): return
- result = true
- else:
- result = false
- else:
- result = false
- else:
- result = false
- proc sameObjectStructures(a, b: PType, c: var TSameTypeClosure): bool =
- if not sameTypeOrNilAux(a.baseClass, b.baseClass, c): return false
- if not sameObjectTree(a.n, b.n, c): return false
- result = true
- proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
- if not sameTupleLengths(a, b): return false
- # XXX This is not tuple specific.
- result = true
- for _, x, y in tupleTypePairs(a, b):
- result = sameTypeOrNilAux(x, y, c)
- if not result: return
- proc isGenericAlias*(t: PType): bool =
- return t.kind == tyGenericInst and t.skipModifier.skipTypes({tyAlias}).kind == tyGenericInst
- proc genericAliasDepth*(t: PType): int =
- result = 0
- var it = t.skipTypes({tyAlias})
- while it.isGenericAlias:
- it = it.skipModifier.skipTypes({tyAlias})
- inc result
- proc skipGenericAlias*(t: PType): PType =
- result = t.skipTypes({tyAlias})
- if result.isGenericAlias:
- result = result.skipModifier.skipTypes({tyAlias})
- proc sameFlags*(a, b: PType): bool {.inline.} =
- result = eqTypeFlags*a.flags == eqTypeFlags*b.flags
- proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
- result = false
- 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 expensive exact cycle check:
- if c.recCheck < 3:
- inc c.recCheck
- else:
- if containsOrIncl(c, a, b): return true
- template maybeSkipRange(x: set[TTypeKind]): set[TTypeKind] =
- if IgnoreRangeShallow in c.flags:
- x + {tyRange}
- else:
- x
-
- template withoutShallowFlags(body) =
- let oldFlags = c.flags
- c.flags.excl IgnoreRangeShallow
- body
- c.flags = oldFlags
- if x == y: return true
- let aliasSkipSet = maybeSkipRange({tyAlias})
- var a = skipTypes(x, aliasSkipSet)
- while a.kind == tyUserTypeClass and tfResolved in a.flags:
- a = skipTypes(a.last, aliasSkipSet)
- var b = skipTypes(y, aliasSkipSet)
- while b.kind == tyUserTypeClass and tfResolved in b.flags:
- b = skipTypes(b.last, aliasSkipSet)
- assert(a != nil)
- assert(b != nil)
- case c.cmp
- of dcEq:
- if a.kind != b.kind: return false
- of dcEqIgnoreDistinct:
- let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
- a = a.skipTypes(distinctSkipSet)
- b = b.skipTypes(distinctSkipSet)
- if a.kind != b.kind: return false
- of dcEqOrDistinctOf:
- let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
- a = a.skipTypes(distinctSkipSet)
- if a.kind != b.kind: return false
- #[
- The following code should not run in the case either side is an generic alias,
- but it's not presently possible to distinguish the genericinsts from aliases of
- objects ie `type A[T] = SomeObject`
- ]#
- # this is required by tunique_type but makes no sense really:
- if c.cmp == dcEq and x.kind == tyGenericInst and
- IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
- let
- lhs = x.skipGenericAlias
- rhs = y.skipGenericAlias
- if rhs.kind != tyGenericInst or lhs.base != rhs.base or rhs.kidsLen != lhs.kidsLen:
- return false
- withoutShallowFlags:
- for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
- if not sameTypeAux(ff, aa, c): return false
- return true
- case a.kind
- of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCstring,
- tyInt..tyUInt64, tyTyped, tyUntyped, tyVoid:
- result = sameFlags(a, b)
- if result and {PickyCAliases, ExactTypeDescValues} <= c.flags:
- # additional requirement for the caching of generics for importc'ed types:
- # the symbols must be identical too:
- let symFlagsA = if a.sym != nil: a.sym.flags else: {}
- let symFlagsB = if b.sym != nil: b.sym.flags else: {}
- if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
- result = symFlagsA == symFlagsB
- elif result and PickyBackendAliases in c.flags:
- let symFlagsA = if a.sym != nil: a.sym.flags else: {}
- let symFlagsB = if b.sym != nil: b.sym.flags else: {}
- if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
- result = a.id == b.id
- of tyStatic, tyFromExpr:
- result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
- if result and sameTupleLengths(a, b) and a.hasElementType:
- cycleCheck()
- result = sameTypeAux(a.skipModifier, b.skipModifier, c)
- of tyObject:
- result = sameFlags(a, b)
- if result:
- ifFastObjectTypeCheckFailed(a, b):
- cycleCheck()
- # should be generic, and belong to the same generic head type:
- assert a.typeInst != nil, "generic object " & $a & " has no typeInst"
- assert b.typeInst != nil, "generic object " & $b & " has no typeInst"
- if result:
- withoutShallowFlags:
- # this is required because of generic `ref object`s,
- # the value of their dereferences are not wrapped in `tyGenericInst`,
- # so we need to check the generic parameters here
- for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1):
- if not sameTypeAux(ff, aa, c): return false
- of tyDistinct:
- cycleCheck()
- if c.cmp == dcEq:
- result = sameFlags(a, b)
- if result:
- ifFastObjectTypeCheckFailed(a, b):
- # should be generic, and belong to the same generic head type:
- assert a.typeInst != nil, "generic distinct type " & $a & " has no typeInst"
- assert b.typeInst != nil, "generic distinct type " & $b & " has no typeInst"
- withoutShallowFlags:
- # just in case `tyGenericInst` was skipped at some point,
- # we need to check the generic parameters here
- for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1):
- if not sameTypeAux(ff, aa, c): return false
- else:
- result = sameTypeAux(a.elementType, b.elementType, c) and sameFlags(a, b)
- of tyEnum, tyForward:
- # XXX generic enums do not make much sense, but require structural checking
- result = a.id == b.id and sameFlags(a, b)
- of tyError:
- result = b.kind == tyError
- of tyTuple:
- withoutShallowFlags:
- cycleCheck()
- result = sameTuple(a, b, c) and sameFlags(a, b)
- of tyTypeDesc:
- if c.cmp == dcEqIgnoreDistinct: result = false
- elif ExactTypeDescValues in c.flags:
- cycleCheck()
- result = sameChildrenAux(x, y, c) and sameFlags(a, b)
- else:
- result = sameFlags(a, b)
- of tyGenericParam:
- result = sameChildrenAux(a, b, c) and sameFlags(a, b)
- if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}:
- result = a.sym.position == b.sym.position
- of tyBuiltInTypeClass:
- result = a.elementType.kind == b.elementType.kind and sameFlags(a.elementType, b.elementType)
- if result and a.elementType.kind == tyProc and IgnoreCC notin c.flags:
- let ecc = a.elementType.flags * {tfExplicitCallConv}
- result = ecc == b.elementType.flags * {tfExplicitCallConv} and
- (ecc == {} or a.elementType.callConv == b.elementType.callConv)
- of tyGenericInvocation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef,
- tyPtr, tyVar, tyLent, tySink, tyUncheckedArray, tyArray, tyProc, tyVarargs,
- tyOrdinal, tyCompositeTypeClass, tyUserTypeClass, tyUserTypeClassInst,
- tyAnd, tyOr, tyNot, tyAnything, tyOwned:
- cycleCheck()
- if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
- withoutShallowFlags:
- result = sameChildrenAux(a, b, c)
- if result and IgnoreFlags notin c.flags:
- if IgnoreTupleFields in c.flags:
- result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam}
- else:
- result = sameFlags(a, b)
- if result and ExactGcSafety in c.flags:
- result = a.flags * {tfThread} == b.flags * {tfThread}
- if result and a.kind == tyProc:
- result = ((IgnoreCC in c.flags) or a.callConv == b.callConv) and
- ((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n))
- of tyRange:
- cycleCheck()
- result = sameTypeOrNilAux(a.elementType, b.elementType, c)
- if result and IgnoreRangeShallow notin c.flags:
- result = sameValue(a.n[0], b.n[0]) and
- sameValue(a.n[1], b.n[1])
- of tyAlias, tyInferred, tyIterable:
- cycleCheck()
- result = sameTypeAux(a.skipModifier, b.skipModifier, c)
- of tyGenericInst:
- # BUG #23445
- # The type system must distinguish between `T[int] = object #[empty]#`
- # and `T[float] = object #[empty]#`!
- cycleCheck()
- withoutShallowFlags:
- for ff, aa in underspecifiedPairs(a, b, 1, -1):
- if not sameTypeAux(ff, aa, c): return false
- result = sameTypeAux(a.skipModifier, b.skipModifier, c)
- of tyNone: result = false
- of tyConcept:
- result = exprStructuralEquivalent(a.n, b.n)
- proc sameBackendType*(x, y: PType): bool =
- var c = initSameTypeClosure()
- c.flags.incl IgnoreTupleFields
- c.cmp = dcEqIgnoreDistinct
- result = sameTypeAux(x, y, c)
- proc sameBackendTypeIgnoreRange*(x, y: PType): bool =
- var c = initSameTypeClosure()
- c.flags.incl IgnoreTupleFields
- c.flags.incl IgnoreRangeShallow
- c.cmp = dcEqIgnoreDistinct
- result = sameTypeAux(x, y, c)
- proc sameBackendTypePickyAliases*(x, y: PType): bool =
- var c = initSameTypeClosure()
- c.flags.incl {IgnoreTupleFields, PickyCAliases, PickyBackendAliases}
- c.cmp = dcEqIgnoreDistinct
- result = sameTypeAux(x, y, c)
- proc compareTypes*(x, y: PType,
- cmp: TDistinctCompare = dcEq,
- flags: TTypeCmpFlags = {}): bool =
- ## compares two type for equality (modulo type distinction)
- var c = initSameTypeClosure()
- c.cmp = cmp
- c.flags = flags
- if x == y: result = true
- elif x.isNil or y.isNil: result = false
- else: 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
- if a == b or a.kind == tyError or b.kind == tyError: return 0
- assert a.kind in {tyObject} + skipPtrs
- assert b.kind in {tyObject} + skipPtrs
- var x = a
- result = 0
- while x != nil:
- x = skipTypes(x, skipPtrs)
- if sameObjectTypes(x, b): return
- x = x.baseClass
- dec(result)
- var y = b
- result = 0
- while y != nil:
- y = skipTypes(y, skipPtrs)
- if sameObjectTypes(y, a): return
- y = y.baseClass
- inc(result)
- result = high(int)
- proc commonSuperclass*(a, b: PType): PType =
- result = nil
- # quick check: are they the same?
- if sameObjectTypes(a, b): return a
- # simple algorithm: we store all ancestors of 'a' in a ID-set and walk 'b'
- # up until the ID is found:
- assert a.kind == tyObject
- assert b.kind == tyObject
- var x = a
- var ancestors = initIntSet()
- while x != nil:
- x = skipTypes(x, skipPtrs)
- ancestors.incl(x.id)
- x = x.baseClass
- var y = b
- while y != nil:
- var t = y # bug #7818, save type before skip
- y = skipTypes(y, skipPtrs)
- if ancestors.contains(y.id):
- # bug #7818, defer the previous skipTypes
- if t.kind != tyGenericInst: t = y
- return t
- y = y.baseClass
- proc lacksMTypeField*(typ: PType): bool {.inline.} =
- (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags
- include sizealignoffsetimpl
- proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt =
- computeSizeAlign(conf, typ)
- result = typ.size
- proc getReturnType*(s: PSym): PType =
- # Obtains the return type of a iterator/proc/macro/template
- assert s.kind in skProcKinds
- result = s.typ.returnType
- proc getAlign*(conf: ConfigRef; typ: PType): BiggestInt =
- computeSizeAlign(conf, typ)
- result = typ.align
- proc getSize*(conf: ConfigRef; typ: PType): BiggestInt =
- computeSizeAlign(conf, typ)
- result = typ.size
- proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
- case t.kind
- of tyStatic:
- return t.n == nil
- of tyTypeDesc:
- if t.base.kind == tyNone: return true
- if containsGenericTypeIter(t.base, closure): return true
- return false
- of GenericTypes + tyTypeClasses + {tyFromExpr}:
- return true
- else:
- return false
- proc containsGenericType*(t: PType): bool =
- result = iterOverType(t, containsGenericTypeIter, nil)
- proc containsUnresolvedTypeIter(t: PType, closure: RootRef): bool =
- if tfUnresolved in t.flags: return true
- case t.kind
- of tyStatic:
- return t.n == nil
- of tyTypeDesc:
- if t.base.kind == tyNone: return true
- if containsUnresolvedTypeIter(t.base, closure): return true
- return false
- of tyGenericInvocation, tyGenericParam, tyFromExpr, tyAnything:
- return true
- else:
- return false
- proc containsUnresolvedType*(t: PType): bool =
- result = iterOverType(t, containsUnresolvedTypeIter, nil)
- proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType =
- if t.kind == tyDistinct:
- result = t.elementType
- else:
- result = copyType(t, idgen, t.owner)
- copyTypeProps(g, idgen.module, result, t)
- var parent: PType = nil
- var it = result
- while it.kind in {tyPtr, tyRef, tyOwned}:
- parent = it
- it = it.elementType
- if it.kind == tyDistinct and parent != nil:
- parent[0] = it[0]
- proc safeInheritanceDiff*(a, b: PType): int =
- # same as inheritanceDiff but checks for tyError:
- if a.kind == tyError or b.kind == tyError:
- result = -1
- else:
- result = inheritanceDiff(a.skipTypes(skipPtrs), b.skipTypes(skipPtrs))
- proc compatibleEffectsAux(se, re: PNode): bool =
- if re.isNil: return false
- for r in items(re):
- block search:
- for s in items(se):
- if safeInheritanceDiff(r.typ, s.typ) <= 0:
- break search
- return false
- result = true
- proc isDefectException*(t: PType): bool
- proc compatibleExceptions(se, re: PNode): bool =
- if re.isNil: return false
- for r in items(re):
- block search:
- if isDefectException(r.typ):
- break search
- for s in items(se):
- if safeInheritanceDiff(r.typ, s.typ) <= 0:
- break search
- return false
- result = true
- proc hasIncompatibleEffect(se, re: PNode): bool =
- result = false
- if re.isNil: return false
- for r in items(re):
- for s in items(se):
- if safeInheritanceDiff(r.typ, s.typ) != high(int):
- return true
- type
- EffectsCompat* = enum
- efCompat
- efRaisesDiffer
- efRaisesUnknown
- efTagsDiffer
- efTagsUnknown
- efEffectsDelayed
- efTagsIllegal
- proc compatibleEffects*(formal, actual: PType): EffectsCompat =
- # for proc type compatibility checking:
- assert formal.kind == tyProc and actual.kind == tyProc
- #if tfEffectSystemWorkaround in actual.flags:
- # return efCompat
- if formal.n[0].kind != nkEffectList or
- actual.n[0].kind != nkEffectList:
- return efTagsUnknown
- var spec = formal.n[0]
- if spec.len != 0:
- var real = actual.n[0]
- let se = spec[exceptionEffects]
- # if 'se.kind == nkArgList' it is no formal type really, but a
- # computed effect and as such no spec:
- # 'r.msgHandler = if isNil(msgHandler): defaultMsgHandler else: msgHandler'
- if not isNil(se) and se.kind != nkArgList:
- # spec requires some exception or tag, but we don't know anything:
- if real.len == 0: return efRaisesUnknown
- let res = compatibleExceptions(se, real[exceptionEffects])
- if not res: return efRaisesDiffer
- let st = spec[tagEffects]
- if not isNil(st) and st.kind != nkArgList:
- # spec requires some exception or tag, but we don't know anything:
- if real.len == 0: return efTagsUnknown
- let res = compatibleEffectsAux(st, real[tagEffects])
- if not res:
- #if tfEffectSystemWorkaround notin actual.flags:
- return efTagsDiffer
- let sn = spec[forbiddenEffects]
- if not isNil(sn) and sn.kind != nkArgList:
- if 0 == real.len:
- return efTagsUnknown
- elif hasIncompatibleEffect(sn, real[tagEffects]):
- return efTagsIllegal
- for i in 1 ..< min(formal.n.len, actual.n.len):
- if formal.n[i].sym.flags * {sfEffectsDelayed} != actual.n[i].sym.flags * {sfEffectsDelayed}:
- result = efEffectsDelayed
- break
- result = efCompat
- proc isCompileTimeOnly*(t: PType): bool {.inline.} =
- result = t.kind in {tyTypeDesc, tyStatic, tyGenericParam}
- proc containsCompileTimeOnly*(t: PType): bool =
- if isCompileTimeOnly(t): return true
- for a in t.kids:
- if a != nil and isCompileTimeOnly(a):
- return true
- return false
- proc safeSkipTypes*(t: PType, kinds: TTypeKinds): PType =
- ## same as 'skipTypes' but with a simple cycle detector.
- result = t
- var seen = initIntSet()
- while result.kind in kinds and not containsOrIncl(seen, result.id):
- result = skipModifier(result)
- type
- OrdinalType* = enum
- NoneLike, IntLike, FloatLike
- proc classify*(t: PType): OrdinalType =
- ## for convenient type checking:
- if t == nil:
- result = NoneLike
- else:
- case skipTypes(t, abstractVarRange).kind
- of tyFloat..tyFloat128: result = FloatLike
- of tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyChar, tyEnum:
- result = IntLike
- else: result = NoneLike
- proc skipConv*(n: PNode): PNode =
- result = n
- case n.kind
- of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
- # only skip the conversion if it doesn't lose too important information
- # (see bug #1334)
- if n[0].typ.classify == n.typ.classify:
- result = n[0]
- of nkHiddenStdConv, nkHiddenSubConv, nkConv:
- if n[1].typ.classify == n.typ.classify:
- result = n[1]
- else: discard
- proc skipHidden*(n: PNode): PNode =
- result = n
- while true:
- case result.kind
- of nkHiddenStdConv, nkHiddenSubConv:
- if result[1].typ.classify == result.typ.classify:
- result = result[1]
- else: break
- of nkHiddenDeref, nkHiddenAddr:
- result = result[0]
- else: break
- proc skipConvTakeType*(n: PNode): PNode =
- result = n.skipConv
- result.typ() = n.typ
- proc isEmptyContainer*(t: PType): bool =
- case t.kind
- of tyUntyped, tyNil: result = true
- of tyArray, tySet, tySequence, tyOpenArray, tyVarargs:
- result = t.elementType.kind == tyEmpty
- of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.skipModifier)
- else: result = false
- proc takeType*(formal, arg: PType; g: ModuleGraph; idgen: IdGenerator): PType =
- # param: openArray[string] = []
- # [] is an array constructor of length 0 of type string!
- if arg.kind == tyNil:
- # and not (formal.kind == tyProc and formal.callConv == ccClosure):
- result = formal
- elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and
- arg.isEmptyContainer:
- let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), idgen, arg.owner)
- copyTypeProps(g, idgen.module, a, arg)
- a[ord(arg.kind == tyArray)] = formal[0]
- result = a
- elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind:
- result = formal
- else:
- result = arg
- proc skipHiddenSubConv*(n: PNode; g: ModuleGraph; idgen: IdGenerator): PNode =
- if n.kind == nkHiddenSubConv:
- # param: openArray[string] = []
- # [] is an array constructor of length 0 of type string!
- let formal = n.typ
- result = n[1]
- let arg = result.typ
- let dest = takeType(formal, arg, g, idgen)
- if dest == arg and formal.kind != tyUntyped:
- #echo n.info, " came here for ", formal.typeToString
- result = n
- else:
- result = copyTree(result)
- result.typ() = dest
- else:
- result = n
- proc getProcConvMismatch*(c: ConfigRef, f, a: PType, rel = isNone): (set[ProcConvMismatch], TTypeRelation) =
- ## Returns a set of the reason of mismatch, and the relation for conversion.
- result[1] = rel
- if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
- # Formal is pure, but actual is not
- result[0].incl pcmNoSideEffect
- result[1] = isNone
- if tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and
- optThreadAnalysis in c.globalOptions:
- # noSideEffect implies ``tfThread``!
- result[0].incl pcmNotGcSafe
- result[1] = isNone
- if f.flags * {tfIterator} != a.flags * {tfIterator}:
- # One of them is an iterator so not convertible
- result[0].incl pcmNotIterator
- result[1] = isNone
- if f.callConv != a.callConv:
- # valid to pass a 'nimcall' thingie to 'closure':
- if f.callConv == ccClosure and a.callConv == ccNimCall:
- case result[1]
- of isInferred: result[1] = isInferredConvertible
- of isBothMetaConvertible: result[1] = isBothMetaConvertible
- elif result[1] != isNone: result[1] = isConvertible
- else: result[0].incl pcmDifferentCallConv
- else:
- result[1] = isNone
- result[0].incl pcmDifferentCallConv
- proc addPragmaAndCallConvMismatch*(message: var string, formal, actual: PType, conf: ConfigRef) =
- assert formal.kind == tyProc and actual.kind == tyProc
- let (convMismatch, _) = getProcConvMismatch(conf, formal, actual)
- var
- gotPragmas = ""
- expectedPragmas = ""
- for reason in convMismatch:
- case reason
- of pcmDifferentCallConv:
- message.add "\n Calling convention mismatch: got '{.$1.}', but expected '{.$2.}'." % [$actual.callConv, $formal.callConv]
- of pcmNoSideEffect:
- expectedPragmas.add "noSideEffect, "
- of pcmNotGcSafe:
- expectedPragmas.add "gcsafe, "
- of pcmNotIterator: discard
- if expectedPragmas.len > 0:
- gotPragmas.setLen(max(0, gotPragmas.len - 2)) # Remove ", "
- expectedPragmas.setLen(max(0, expectedPragmas.len - 2)) # Remove ", "
- message.add "\n Pragma mismatch: got '{.$1.}', but expected '{.$2.}'." % [gotPragmas, expectedPragmas]
- proc processPragmaAndCallConvMismatch(msg: var string, formal, actual: PType, conf: ConfigRef) =
- if formal.kind == tyProc and actual.kind == tyProc:
- msg.addPragmaAndCallConvMismatch(formal, actual, conf)
- 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 efEffectsDelayed:
- msg.add "\n.effectsOf annotations differ"
- of efTagsIllegal:
- msg.add "\n.notTag catched an illegal effect"
- proc typeNameAndDesc*(t: PType): string =
- result = typeToString(t)
- let desc = typeToString(t, preferDesc)
- if result != desc:
- result.add(" = ")
- result.add(desc)
- proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: PNode) =
- if formal.kind != tyError and actual.kind != tyError:
- let actualStr = typeToString(actual)
- let formalStr = typeToString(formal)
- let desc = typeToString(formal, preferDesc)
- let x = if formalStr == desc: formalStr else: formalStr & " = " & desc
- let verbose = actualStr == formalStr or optDeclaredLocs in conf.globalOptions
- var msg = "type mismatch:"
- if verbose: msg.add "\n"
- if conf.isDefined("nimLegacyTypeMismatch"):
- msg.add " got <$1>" % actualStr
- else:
- msg.add " got '$1' for '$2'" % [actualStr, n.renderTree]
- if verbose:
- msg.addDeclaredLoc(conf, actual)
- msg.add "\n"
- msg.add " but expected '$1'" % x
- if verbose: msg.addDeclaredLoc(conf, formal)
- var a = formal
- var b = actual
- if formal.kind == tyArray and actual.kind == tyArray:
- a = formal[1]
- b = actual[1]
- processPragmaAndCallConvMismatch(msg, a, b, conf)
- elif formal.kind == tySequence and actual.kind == tySequence:
- a = formal[0]
- b = actual[0]
- processPragmaAndCallConvMismatch(msg, a, b, conf)
- else:
- processPragmaAndCallConvMismatch(msg, a, b, conf)
- 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:
- result = false
- var cycleDetectorCopy: IntSet
- for a in t.kids:
- cycleDetectorCopy = cycleDetector
- if isTupleRecursive(a, cycleDetectorCopy):
- return true
- of tyRef, tyPtr, tyVar, tyLent, tySink,
- tyArray, tyUncheckedArray, tySequence, tyDistinct:
- return isTupleRecursive(t.elementType, cycleDetector)
- of tyAlias, tyGenericInst:
- return isTupleRecursive(t.skipModifier, cycleDetector)
- else:
- return false
- proc isTupleRecursive*(t: PType): bool =
- var cycleDetector = initIntSet()
- isTupleRecursive(t, cycleDetector)
- proc isException*(t: PType): bool =
- # check if `y` is object type and it inherits from Exception
- assert(t != nil)
- var t = t.skipTypes(abstractInst)
- while t.kind == tyObject:
- if t.sym != nil and t.sym.magic == mException: return true
- if t.baseClass == nil: break
- t = skipTypes(t.baseClass, abstractPtrs)
- return false
- proc isDefectException*(t: PType): bool =
- var t = t.skipTypes(abstractPtrs)
- while t.kind == tyObject:
- if t.sym != nil and t.sym.owner != nil and
- sfSystemModule in t.sym.owner.flags and
- t.sym.name.s == "Defect":
- return true
- if t.baseClass == nil: break
- t = skipTypes(t.baseClass, abstractPtrs)
- return false
- proc isDefectOrCatchableError*(t: PType): bool =
- var t = t.skipTypes(abstractPtrs)
- while t.kind == tyObject:
- if t.sym != nil and t.sym.owner != nil and
- sfSystemModule in t.sym.owner.flags and
- (t.sym.name.s == "Defect" or
- t.sym.name.s == "CatchableError"):
- return true
- if t.baseClass == nil: break
- t = skipTypes(t.baseClass, abstractPtrs)
- return false
- proc isSinkTypeForParam*(t: PType): bool =
- # a parameter like 'seq[owned T]' must not be used only once, but its
- # elements must, so we detect this case here:
- result = t.skipTypes({tyGenericInst, tyAlias}).kind in {tySink, tyOwned}
- when false:
- if isSinkType(t):
- if t.skipTypes({tyGenericInst, tyAlias}).kind in {tyArray, tyVarargs, tyOpenArray, tySequence}:
- result = false
- else:
- result = true
- proc lookupFieldAgain*(ty: PType; field: PSym): PSym =
- result = nil
- var ty = ty
- while ty != nil:
- ty = ty.skipTypes(skipPtrs)
- assert(ty.kind in {tyTuple, tyObject})
- result = lookupInRecord(ty.n, field.name)
- if result != nil: break
- ty = ty.baseClass
- if result == nil: result = field
- proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool =
- let t = t.skipTypes(abstractInst)
- if t.kind == tyPtr:
- let pointsTo = t.elementType.skipTypes(abstractInst)
- case pointsTo.kind
- of tyUncheckedArray:
- result = pointsTo.elementType.kind == tyChar
- of tyArray:
- result = pointsTo.elementType.kind == tyChar and firstOrd(nil, pointsTo.indexType) == 0 and
- skipTypes(pointsTo.indexType, {tyRange}).kind in {tyInt..tyInt64}
- of tyChar:
- result = allowPointerToChar
- else:
- result = false
- else:
- result = false
- proc isRefPtrObject*(t: PType): bool =
- t.kind in {tyRef, tyPtr} and tfRefsAnonObj in t.flags
- proc nominalRoot*(t: PType): PType =
- ## the "name" type of a given instance of a nominal type,
- ## i.e. the type directly associated with the symbol where the root
- ## nominal type of `t` was defined, skipping things like generic instances,
- ## aliases, `var`/`sink`/`typedesc` modifiers
- ##
- ## instead of returning the uninstantiated body of a generic type,
- ## returns the type of the symbol instead (with tyGenericBody type)
- result = nil
- case t.kind
- of tyAlias, tyVar, tySink:
- # varargs?
- result = nominalRoot(t.skipModifier)
- of tyTypeDesc:
- # for proc foo(_: type T)
- result = nominalRoot(t.skipModifier)
- of tyGenericInvocation, tyGenericInst:
- result = t
- # skip aliases, so this works in the same module but not in another module:
- # type Foo[T] = object
- # type Bar[T] = Foo[T]
- # proc foo[T](x: Bar[T]) = ... # attached to type
- while result.skipModifier.kind in {tyGenericInvocation, tyGenericInst}:
- result = result.skipModifier
- result = nominalRoot(result[0])
- of tyGenericBody:
- result = t
- # this time skip the aliases but take the generic body
- while result.skipModifier.kind in {tyGenericInvocation, tyGenericInst}:
- result = result.skipModifier[0]
- let val = result.skipModifier
- if val.kind in {tyDistinct, tyEnum, tyObject} or
- isRefPtrObject(val):
- # atomic nominal types, this generic body is attached to them
- discard
- else:
- result = nominalRoot(val)
- of tyCompositeTypeClass:
- # parameter with type Foo
- result = nominalRoot(t.skipModifier)
- of tyGenericParam:
- if t.genericParamHasConstraints:
- # T: Foo
- result = nominalRoot(t.genericConstraint)
- else:
- result = nil
- of tyDistinct, tyEnum, tyObject:
- result = t
- of tyPtr, tyRef:
- if tfRefsAnonObj in t.flags:
- # in the case that we have `type Foo = ref object` etc
- result = t
- else:
- # we could allow this in general, but there's things like `seq[Foo]`
- #result = nominalRoot(t.skipModifier)
- result = nil
- of tyStatic:
- result = nominalRoot(t.base)
- else:
- # skips all typeclasses
- # is this correct for `concept`?
- result = nil
- proc genericRoot*(t: PType): PType =
- ## gets the root generic type (`tyGenericBody`) from `t`,
- ## if `t` is a generic type or the body of a generic instantiation
- case t.kind
- of tyGenericBody:
- result = t
- of tyGenericInst, tyGenericInvocation:
- result = t.genericHead
- else:
- if t.typeInst != nil:
- result = t.typeInst.genericHead
- elif t.sym != nil and t.sym.typ.kind == tyGenericBody:
- # can happen if `t` is the last child (body) of the generic body
- result = t.sym.typ
- else:
- result = nil
|