summary refs log blame commit diff stats
path: root/compiler/seminst.nim
blob: 85c68923c7aa9c5fe027659be26af651b9b8c35d (plain) (tree)
1
2
3
4
5
6
7
8
9


                               
                                         



                                                   
 
                                                            
                       
 


                                                                     
                                                                           

                                    
                     

                                                                     
                 
                                                                                   
                                                  
                   
                                               
                                        
                

                                                         
                                 
                                                                       



                                           

                 
                              
 





                                                                     
 
                                                                          


                                                        



                                                                          
 















                                                                           
                                                           
                                     



                                                                
                                                           
                         


                                                          




                                                     


                                                    


                                  
                                           

                                               



                                   







                                                                    


























                                                                                 

                                                           
                                  

                                                                         
                                                                              


                                


                                     
                                                                   
                          


                   
                                               


                                             



                                                                     

                                          







                                                                   

                                    
                                       
                                

                                        
                                  

                                                              

                                            
                                 
                               
       



                                                          
                            
                    
  







                                                                         
#
#
#           The Nimrod 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 instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
                                 entry: var TInstantiatedSymbol) = 
  if n.kind != nkGenericParams: 
    InternalError(n.info, "instantiateGenericParamList; no generic params")
  newSeq(entry.concreteTypes, n.len)
  for i in countup(0, n.len - 1):
    var a = n.sons[i]
    if a.kind != nkSym: 
      InternalError(a.info, "instantiateGenericParamList; no symbol")
    var q = a.sym
    if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyTypeClass, tyExpr}: continue
    var s = newSym(skType, q.name, getCurrOwner())
    s.info = q.info
    s.flags = s.flags + {sfUsed, sfFromGeneric}
    var t = PType(IdTableGet(pt, q.typ))
    if t == nil:
      LocalError(a.info, errCannotInstantiateX, s.name.s)
      break
    if t.kind == tyGenericParam: 
      InternalError(a.info, "instantiateGenericParamList: " & q.name.s)
    elif t.kind == tyGenericInvokation:
      #t = instGenericContainer(c, a, t)
      t = generateTypeInstance(c, pt, a, t)
      #t = ReplaceTypeVarsT(cl, t)
    s.typ = t
    addDecl(c, s)
    entry.concreteTypes[i] = t

proc sameInstantiation(a, b: TInstantiatedSymbol): bool =
  if a.genericSym.id == b.genericSym.id and 
      a.concreteTypes.len == b.concreteTypes.len:
    for i in 0 .. < a.concreteTypes.len:
      if not sameType(a.concreteTypes[i], b.concreteTypes[i]): return
    result = true

proc GenericCacheGet(c: PContext, entry: var TInstantiatedSymbol): PSym = 
  for i in countup(0, Len(c.generics.generics) - 1):
    if sameInstantiation(entry, c.generics.generics[i]):
      result = c.generics.generics[i].instSym
      # checking for the concrete parameter list is wrong and unnecessary!
      #if equalParams(b.typ.n, instSym.typ.n) == paramsEqual:
      #echo "found in cache: ", getProcHeader(result)
      return

proc removeDefaultParamValues(n: PNode) = 
  # we remove default params, because they cannot be instantiated properly
  # and they are not needed anyway for instantiation (each param is already
  # provided).
  when false:
    for i in countup(1, sonsLen(n)-1): 
      var a = n.sons[i]
      if a.kind != nkIdentDefs: IllFormedAst(a)
      var L = a.len
      if a.sons[L-1].kind != nkEmpty and a.sons[L-2].kind != nkEmpty:
        # ``param: typ = defaultVal``. 
        # We don't need defaultVal for semantic checking and it's wrong for
        # ``cmp: proc (a, b: T): int = cmp``. Hm, for ``cmp = cmp`` that is
        # not possible... XXX We don't solve this issue here.
        a.sons[L-1] = ast.emptyNode

proc instantiateBody(c: PContext, n: PNode, result: PSym) =
  if n.sons[bodyPos].kind != nkEmpty:
    # add it here, so that recursive generic procs are possible:
    addDecl(c, result)
    pushProcCon(c, result)
    if result.kind in {skProc, skMethod, skConverter}: 
      addResult(c, result.typ.sons[0], n.info, result.kind)
      addResultNode(c, n)
    var b = semStmtScope(c, n.sons[bodyPos])
    # XXX Bad hack for tests/titer2 and tests/tactiontable
    n.sons[bodyPos] = transform(c.module, b)
    #echo "code instantiated ", result.name.s
    excl(result.flags, sfForward)
    popProcCon(c)

proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
  for i in countup(0, Len(c.generics.generics) - 1):
    if c.generics.generics[i].genericSym.id == s.id:
      var oldPrc = c.generics.generics[i].instSym
      pushInfoContext(oldPrc.info)
      openScope(c.tab)
      var n = oldPrc.ast
      n.sons[bodyPos] = copyTree(s.getBody)
      if n.sons[paramsPos].kind != nkEmpty: 
        addParams(c, oldPrc.typ.n, oldPrc.kind)
      instantiateBody(c, n, oldPrc)
      closeScope(c.tab)
      popInfoContext()

proc sideEffectsCheck(c: PContext, s: PSym) = 
  if {sfNoSideEffect, sfSideEffect} * s.flags ==
      {sfNoSideEffect, sfSideEffect}: 
    LocalError(s.info, errXhasSideEffects, s.name.s)
  elif sfThread in s.flags and semthreads.needsGlobalAnalysis() and 
      s.ast.sons[genericParamsPos].kind == nkEmpty:
    c.threadEntries.add(s)

proc applyConcreteTypesToSig(genericProc: PSym, concTypes: seq[PType]): PType =
  # XXX: This is intended to replace the use of semParamList in generateInstance.
  # The results of semParamList's analysis are already encoded in the original
  # proc type and any concrete types may be aplied directly over it.
  # Besides being more efficient, it will remove the awkward case of
  # genericParams == nil in semParamList.
  # Currenly, it fails in some cases such as:
  # proc inc2*[T](x: var ordinal[T], y = 1) {.magic: "Inc", noSideEffect.}
  let sig = genericProc.typ
  result = copyType(sig, getCurrOwner(), false)
  result.n = sig.n.shallowCopy
  
  for i in countup(0, sig.len - 1):
    let tOrig = sig.sons[i]
    if tOrig == nil: continue        
    let oGenParams = genericProc.ast.sons[genericParamsPos]
    if skipTypes(tOrig, skipPtrs).kind in {tyGenericParam}:
      var tConcrete = concTypes[tOrig.sym.position]
      if i > 0:
        let param = sig.n.sons[i].sym.copySym
        param.typ = tConcrete
        result.n.sons[i] = newSymNode(param)
      result.sons[i] = tConcrete
    else:
      result.sons[i] = tOrig
      if i > 0: result.n.sons[i] = sig.n.sons[i]

proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, 
                      info: TLineInfo): PSym = 
  # generates an instantiated proc
  if c.InstCounter > 1000: InternalError(fn.ast.info, "nesting too deep")
  inc(c.InstCounter)
  # NOTE: for access of private fields within generics from a different module
  # we set the friend module:
  var oldFriend = c.friendModule
  c.friendModule = getModule(fn)
  result = copySym(fn, false)
  incl(result.flags, sfFromGeneric)
  result.owner = getCurrOwner().owner
  # careful! we copy the whole AST including the possibly nil body!
  var n = copyTree(fn.ast)
  result.ast = n
  pushOwner(result)
  openScope(c.tab)
  if n.sons[genericParamsPos].kind == nkEmpty: 
    InternalError(n.info, "generateInstance")
  n.sons[namePos] = newSymNode(result)
  pushInfoContext(info)
  var entry: TInstantiatedSymbol
  entry.instSym = result
  entry.genericSym = fn
  instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry)
  n.sons[genericParamsPos] = ast.emptyNode
  # semantic checking for the parameters:
  if n.sons[paramsPos].kind != nkEmpty:
    if false and nimdbg:
      result.typ = applyConcreteTypesToSig(fn, entry.concreteTypes)
      addParams(c, result.typ.n, fn.kind)
    else:
      removeDefaultParamValues(n.sons[ParamsPos])
      semParamList(c, n.sons[ParamsPos], nil, result)
  else:
    result.typ = newTypeS(tyProc, c)
    addSon(result.typ, nil)
  result.typ.callConv = fn.typ.callConv
  ParamsTypeCheck(c, result.typ)
  var oldPrc = GenericCacheGet(c, entry)
  if oldPrc == nil:
    c.generics.generics.add(entry)
    if n.sons[pragmasPos].kind != nkEmpty:
      pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
    if isNil(n.sons[bodyPos]):
      n.sons[bodyPos] = copyTree(fn.getBody)
    instantiateBody(c, n, result)
    sideEffectsCheck(c, result)
  else:
    result = oldPrc
  popInfoContext()
  closeScope(c.tab)           # close scope for parameters
  popOwner()
  c.friendModule = oldFriend
  dec(c.InstCounter)
  
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = 
  var cl: TReplTypeVars
  InitIdTable(cl.symMap)
  InitIdTable(cl.typeMap)
  cl.info = n.info
  cl.c = c
  result = ReplaceTypeVarsT(cl, header)