# # # The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This module implements the instantiation of generic procs. # included from sem.nim proc addObjFieldsToLocalScope(c: PContext; n: PNode) = template rec(n) = addObjFieldsToLocalScope(c, n) case n.kind of nkRecList: for i in 0.. 0: rec n[0] for i in 1.. FirstParamAt: resetIdTable(cl.symMap) resetIdTable(cl.localCache) # take a note of the original type. If't a free type or static parameter # we'll need to keep it unbound for the `fitNode` operation below... var typeToFit = resulti let needsStaticSkipping = resulti.kind == tyFromExpr let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags if resulti.kind == tyFromExpr: resulti.flags.incl tfNonConstExpr result[i] = replaceTypeVarsT(cl, resulti) if needsStaticSkipping: result[i] = result[i].skipTypes({tyStatic}) if needsTypeDescSkipping: result[i] = result[i].skipTypes({tyTypeDesc}) typeToFit = result[i] # ...otherwise, we use the instantiated type in `fitNode` if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and (typeToFit.kind != tyStatic): typeToFit = result[i] internalAssert c.config, originalParams[i].kind == nkSym let oldParam = originalParams[i].sym let param = copySym(oldParam, c.idgen) param.owner = prc param.typ = result[i] # The default value is instantiated and fitted against the final # concrete param type. We avoid calling `replaceTypeVarsN` on the # call head symbol, because this leads to infinite recursion. if oldParam.ast != nil: var def = oldParam.ast.copyTree if def.typ.kind == tyFromExpr: def.typ.flags.incl tfNonConstExpr if not isIntLit(def.typ): def = prepareNode(cl, def) # allow symchoice since node will be fit later # although expectedType should cover it def = semExprWithType(c, def, {efAllowSymChoice}, typeToFit) if def.referencesAnotherParam(getCurrOwner(c)): def.flags.incl nfDefaultRefsParam var converted = indexTypesMatch(c, typeToFit, def.typ, def) if converted == nil: # The default value doesn't match the final instantiated type. # As an example of this, see: # https://github.com/nim-lang/Nim/issues/1201 # We are replacing the default value with an error node in case # the user calls an explicit instantiation of the proc (this is # the only way the default value might be inserted). param.ast = errorNode(c, def) # we know the node is empty, we need the actual type for error message param.ast.typ = def.typ else: param.ast = fitNodePostMatch(c, typeToFit, converted) param.typ = result[i] result.n[i] = newSymNode(param) propagateToOwner(result, result[i]) addDecl(c, param) resetIdTable(cl.symMap) resetIdTable(cl.localCache) cl.isReturnType = true result.setReturnType replaceTypeVarsT(cl, result.returnType) cl.isReturnType = false result.n[0] = originalParams[0].copyTree if result[0] != nil: propagateToOwner(result, result[0]) eraseVoidParams(result) skipIntLiteralParams(result, c.idgen) prc.typ = result popInfoContext(c.config) proc instantiateOnlyProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo): PType = # instantiates only the type of a given proc symbol # used by sigmatch for explicit generics # wouldn't be needed if sigmatch could handle complex cases, # examples are in texplicitgenerics # might be buggy, see rest of generateInstance if problems occur let fakeSym = copySym(prc, c.idgen) incl(fakeSym.flags, sfFromGeneric) fakeSym.instantiatedFrom = prc openScope(c) for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt): addDecl(c, s) instantiateProcType(c, pt, fakeSym, info) closeScope(c) result = fakeSym.typ proc fillMixinScope(c: PContext) = var p = c.p while p != nil: for bnd in p.localBindStmts: for n in bnd: addSym(c.currentScope, n.sym) p = p.next proc getLocalPassC(c: PContext, s: PSym): string = when defined(nimsuggest): return "" if s.ast == nil or s.ast.len == 0: return "" result = "" template extractPassc(p: PNode) = if p.kind == nkPragma and p[0][0].ident == c.cache.getIdent"localpassc": return p[0][1].strVal extractPassc(s.ast[0]) #it is set via appendToModule in pragmas (fast access) for n in s.ast: for p in n: extractPassc(p) proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping, info: TLineInfo): PSym = ## Generates a new instance of a generic procedure. ## The `pt` parameter is a type-unsafe mapping table used to link generic ## parameters to their concrete types within the generic instance. # no need to instantiate generic templates/macros: internalAssert c.config, fn.kind notin {skMacro, skTemplate} # generates an instantiated proc if c.instCounter > 50: globalError(c.config, info, "generic instantiation too nested") inc c.instCounter defer: dec c.instCounter # careful! we copy the whole AST including the possibly nil body! var n = copyTree(fn.ast) # NOTE: for access of private fields within generics from a different module # we set the friend module: let producer = getModule(fn) c.friendModules.add(producer) let oldMatchedConcept = c.matchedConcept c.matchedConcept = nil let oldScope = c.currentScope while not isTopLevel(c): c.currentScope = c.currentScope.parent result = copySym(fn, c.idgen) incl(result.flags, sfFromGeneric) result.instantiatedFrom = fn if sfGlobal in result.flags and c.config.symbolFiles != disabledSf: let passc = getLocalPassC(c, producer) if passc != "": #pass the local compiler options to the consumer module too extccomp.addLocalCompileOption(c.config, passc, toFullPathConsiderDirty(c.config, c.module.info.fileIndex)) result.owner = c.module else: result.owner = fn result.ast = n pushOwner(c, result) # mixin scope: openScope(c) fillMixinScope(c) openScope(c) let gp = n[genericParamsPos] if gp.kind != nkGenericParams: # bug #22137 globalError(c.config, info, "generic instantiation too nested") n[namePos] = newSymNode(result) pushInfoContext(c.config, info, fn.detailedInfo) var entry = TInstantiation.new entry.sym = result # we need to compare both the generic types and the concrete types: # generic[void](), generic[int]() # see ttypeor.nim test. var i = 0 newSeq(entry.concreteTypes, fn.typ.paramsLen+gp.len) # let param instantiation know we are in a concept for unresolved statics: c.matchedConcept = oldMatchedConcept for s in instantiateGenericParamList(c, gp, pt): addDecl(c, s) entry.concreteTypes[i] = s.typ inc i c.matchedConcept = nil pushProcCon(c, result) instantiateProcType(c, pt, result, info) for _, param in paramTypes(result.typ): entry.concreteTypes[i] = param inc i #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len if tfTriggersCompileTime in result.typ.flags: incl(result.flags, sfCompileTime) n[genericParamsPos] = c.graph.emptyNode var oldPrc = genericCacheGet(c.graph, fn, entry[], c.compilesContextId) if oldPrc == nil: # we MUST not add potentially wrong instantiations to the caching mechanism. # This means recursive instantiations behave differently when in # a ``compiles`` context but this is the lesser evil. See # bug #1055 (tevilcompiles). #if c.compilesContextId == 0: entry.compilesId = c.compilesContextId addToGenericProcCache(c, fn, entry) c.generics.add(makeInstPair(fn, entry)) # bug #12985 bug #22913 # TODO: use the context of the declaration of generic functions instead # TODO: consider fixing options as well let otherPragmas = c.optionStack[^1].otherPragmas c.optionStack[^1].otherPragmas = nil if n[pragmasPos].kind != nkEmpty: pragma(c, result, n[pragmasPos], allRoutinePragmas) if isNil(n[bodyPos]): n[bodyPos] = copyTree(getBody(c.graph, fn)) instantiateBody(c, n, fn.typ.n, result, fn) c.optionStack[^1].otherPragmas = otherPragmas sideEffectsCheck(c, result) if result.magic notin {mSlice, mTypeOf}: # 'toOpenArray' is special and it is allowed to return 'openArray': paramsTypeCheck(c, result.typ) #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- NEW PROC!", " ", entry.concreteTypes.len else: #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- CACHED! ", typeToString(oldPrc.typ), " ", entry.concreteTypes.len result = oldPrc popProcCon(c) popInfoContext(c.config) closeScope(c) # close scope for parameters closeScope(c) # close scope for 'mixin' declarations popOwner(c) c.currentScope = oldScope discard c.friendModules.pop() c.matchedConcept = oldMatchedConcept if result.kind == skMethod: finishMethod(c, result) # inform IC of the generic #addGeneric(c.ic, result, entry.concreteTypes)