# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This module does the instantiation of generic types. import ast, astalgo, msgs, types, magicsys, semdata, renderer, options, lineinfos, modulegraphs from concepts import makeTypeDesc const tfInstClearedFlags = {tfHasMeta, tfUnresolved} proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) = if t.kind in {tyVar, tyLent} and t[0].kind in {tyVar, tyLent}: localError(conf, info, "type 'var var' is not allowed") proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) = var t = typ.skipTypes({tyDistinct}) if t.kind in tyTypeClasses: discard elif t.kind in {tyVar, tyLent} and t[0].kind in {tyVar, tyLent}: localError(conf, info, "type 'var var' is not allowed") elif computeSize(conf, t) == szIllegalRecursion or isTupleRecursive(t): localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'") when false: if t.kind == tyObject and t[0] != nil: if t[0].kind != tyObject or tfFinal in t[0].flags: localError(info, errInheritanceOnlyWithNonFinalObjects) proc searchInstTypes*(g: ModuleGraph; key: PType): PType = let genericTyp = key[0] if not (genericTyp.kind == tyGenericBody and key[0] == genericTyp and genericTyp.sym != nil): return for inst in typeInstCacheItems(g, genericTyp.sym): if inst.id == key.id: return inst if inst.len < key.len: # XXX: This happens for prematurely cached # types such as Channel[empty]. Why? # See the notes for PActor in handleGenericInvocation return if not sameFlags(inst, key): continue block matchType: for j in 1..high(key.sons): # XXX sameType is not really correct for nested generics? if not compareTypes(inst[j], key[j], flags = {ExactGenericParams, PickyCAliases}): break matchType return inst proc cacheTypeInst(c: PContext; inst: PType) = let gt = inst[0] let t = if gt.kind == tyGenericBody: gt.lastSon else: gt if t.kind in {tyStatic, tyError, tyGenericParam} + tyTypeClasses: return addToGenericCache(c, gt.sym, inst) type LayeredIdTable* {.acyclic.} = ref object topLayer*: TIdTable nextLayer*: LayeredIdTable TReplTypeVars* = object c*: PContext typeMap*: LayeredIdTable # map PType to PType symMap*: TIdTable # map PSym to PSym localCache*: TIdTable # local cache for remembering already replaced # types during instantiation of meta types # (they are not stored in the global cache) info*: TLineInfo allowMetaTypes*: bool # allow types such as seq[Number] # i.e. the result contains unresolved generics skipTypedesc*: bool # whether we should skip typeDescs isReturnType*: bool owner*: PSym # where this instantiation comes from recursionLimit: int proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0): PNode proc initLayeredTypeMap*(pt: TIdTable): LayeredIdTable = result = LayeredIdTable() copyIdTable(result.topLayer, pt) proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable = result = LayeredIdTable() result.nextLayer = cl.typeMap initIdTable(result.topLayer) proc lookup(typeMap: LayeredIdTable, key: PType): PType = var tm = typeMap while tm != nil: result = PType(idTableGet(tm.topLayer, key)) if result != nil: return tm = tm.nextLayer template put(typeMap: LayeredIdTable, key, value: PType) = idTablePut(typeMap.topLayer, key, value) template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code when false: if t != nil and tfHasMeta in t.flags and cl.allowMetaTypes == false: echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1) debug t writeStackTrace() proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = result = replaceTypeVarsTAux(cl, t) checkMetaInvariants(cl, result) proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = let t = replaceTypeVarsT(cl, n.typ) if t != nil and t.kind == tyStatic and t.n != nil: return if tfUnresolved in t.flags: prepareNode(cl, t.n) else: t.n result = copyNode(n) result.typ = t if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym) let isCall = result.kind in nkCallKinds for i in 0.. 0: newSons(result, n.len) if start > 0: result[0] = n[0] for i in start.. 0: t.n[i].sym.typ = skipped # when the typeof operator is used on a static input # param, the results gets infected with static as well: if t[0] != nil and t[0].kind == tyStatic: t[0] = t[0].base proc propagateFieldFlags(t: PType, n: PNode) = # This is meant for objects and tuples # The type must be fully instantiated! if n.isNil: return #internalAssert n.kind != nkRecWhen case n.kind of nkSym: propagateToOwner(t, n.sym.typ) of nkRecList, nkRecCase, nkOfBranch, nkElse: for son in n: propagateFieldFlags(t, son) else: discard proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = template bailout = if cl.recursionLimit > 100: # bail out, see bug #2509. But note this caching is in general wrong, # look at this example where TwoVectors should not share the generic # instantiations (bug #3112): # type # Vector[N: static[int]] = array[N, float64] # TwoVectors[Na, Nb: static[int]] = (Vector[Na], Vector[Nb]) result = PType(idTableGet(cl.localCache, t)) if result != nil: return result inc cl.recursionLimit result = t if t == nil: return if t.kind in {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses: let lookup = cl.typeMap.lookup(t) if lookup != nil: return lookup case t.kind of tyGenericInvocation: result = handleGenericInvocation(cl, t) if result.lastSon.kind == tyUserTypeClass: result.kind = tyUserTypeClassInst of tyGenericBody: localError( cl.c.config, cl.info, "cannot instantiate: '" & typeToString(t, preferDesc) & "'; Maybe generic arguments are missing?") result = errorType(cl.c) #result = replaceTypeVarsT(cl, lastSon(t)) of tyFromExpr: if cl.allowMetaTypes: return # This assert is triggered when a tyFromExpr was created in a cyclic # way. You should break the cycle at the point of creation by introducing # a call such as: `n.typ = makeTypeFromExpr(c, n.copyTree)` # Otherwise, the cycle will be fatal for the prepareNode call below assert t.n.typ != t var n = prepareNode(cl, t.n) if n.kind != nkEmpty: n = cl.c.semConstExpr(cl.c, n) if n.typ.kind == tyTypeDesc: # XXX: sometimes, chained typedescs enter here. # It may be worth investigating why this is happening, # because it may cause other bugs elsewhere. result = n.typ.skipTypes({tyTypeDesc}) # result = n.typ.base else: if n.typ.kind != tyStatic and n.kind != nkType: # XXX: In the future, semConstExpr should # return tyStatic values to let anyone make # use of this knowledge. The patching here # won't be necessary then. result = newTypeS(tyStatic, cl.c) result.sons = @[n.typ] result.n = n else: result = n.typ of tyInt, tyFloat: result = skipIntLit(t, cl.c.idgen) of tyTypeDesc: let lookup = cl.typeMap.lookup(t) if lookup != nil: result = lookup if result.kind != tyTypeDesc: result = makeTypeDesc(cl.c, result) elif tfUnresolved in t.flags or cl.skipTypedesc: result = result.base elif t[0].kind != tyNone: result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t[0])) of tyUserTypeClass, tyStatic: result = t of tyGenericInst, tyUserTypeClassInst: bailout() result = instCopyType(cl, t) idTablePut(cl.localCache, t, result) for i in 1.. 0 and t[0].kind == tyObject and t[0].n != nil: discard replaceObjBranches(cl, t[0].n) elif result.n != nil and t.kind == tyObject: # Invalidate the type size as we may alter its structure result.size = -1 result.n = replaceObjBranches(cl, result.n) proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo; owner: PSym): TReplTypeVars = initIdTable(result.symMap) initIdTable(result.localCache) result.typeMap = typeMap result.info = info result.c = p result.owner = owner proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; owner: PSym, allowMetaTypes = false): PNode = var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, typeMap, n.info, owner) cl.allowMetaTypes = allowMetaTypes pushInfoContext(p.config, n.info) result = replaceTypeVarsN(cl, n) popInfoContext(p.config) when false: # deadcode proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode; original, new: PSym): PNode = var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, typeMap, n.info, original) idTablePut(cl.symMap, original, new) pushInfoContext(p.config, n.info) result = replaceTypeVarsN(cl, n) popInfoContext(p.config) proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) = if t != nil and t.len > 0 and t[0] != nil: let b = skipTypes(t[0], skipPtrs) recomputeFieldPositions(b, b.n, currPosition) case obj.kind of nkRecList: for i in 0.. int # Desired result: Foo[int] # proc (x: T = 0); T -> int ----> proc (x: int = 0) var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, typeMap, info, nil) pushInfoContext(p.config, info) result = replaceTypeVarsT(cl, t) popInfoContext(p.config) let objType = result.skipTypes(abstractInst) if objType.kind == tyObject: var position = 0 recomputeFieldPositions(objType, objType.n, position) proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo, t: PType): PType = var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, typeMap, info, nil) cl.allowMetaTypes = true pushInfoContext(p.config, info) result = replaceTypeVarsT(cl, t) popInfoContext(p.config) template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode, t: PType): untyped = generateTypeInstance(p, pt, arg.info, t)