summary refs log blame commit diff stats
path: root/compiler/semtypinst.nim
blob: 70453c6dbaf7e9936d18d4c857c70dffc5cdbb0d (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 does the instantiation of generic types.

import ast, astalgo, msgs, types, magicsys, semdata, renderer

proc checkPartialConstructedType(info: TLineInfo, t: PType) =
  if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
    LocalError(info, errInvalidPragmaX, "acyclic")
  elif t.kind == tyVar and t.sons[0].kind == tyVar:
    LocalError(info, errVarVarTypeNotAllowed)

proc checkConstructedType*(info: TLineInfo, typ: PType) = 
  var t = typ.skipTypes({tyDistinct})
  if t.kind in {tyTypeClass}: nil
  elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: 
    LocalError(info, errInvalidPragmaX, "acyclic")
  elif t.kind == tyVar and t.sons[0].kind == tyVar: 
    LocalError(info, errVarVarTypeNotAllowed)
  elif computeSize(t) < 0:
    LocalError(info, errIllegalRecursionInTypeX, typeToString(t))
  when false:
    if t.kind == tyObject and t.sons[0] != nil:
      if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: 
        localError(info, errInheritanceOnlyWithNonFinalObjects)

proc searchInstTypes(tab: TIdTable, key: PType): PType = 
  # returns nil if we need to declare this type
  result = PType(IdTableGet(tab, key))
  if result == nil and tab.counter > 0: 
    # we have to do a slow linear search because types may need
    # to be compared by their structure:
    for h in countup(0, high(tab.data)): 
      var t = PType(tab.data[h].key)
      if t != nil: 
        if key.containerId == t.containerId: 
          var match = true
          for j in countup(0, sonsLen(t) - 1): 
            # XXX sameType is not really correct for nested generics?
            if not sameType(t.sons[j], key.sons[j]): 
              match = false
              break 
          if match: 
            return PType(tab.data[h].val)

type
  TReplTypeVars* {.final.} = object 
    c*: PContext
    typeMap*: TIdTable        # map PType to PType
    symMap*: TIdTable         # map PSym to PSym
    info*: TLineInfo

proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType
proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode

proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
  result = copyNode(n)
  result.typ = ReplaceTypeVarsT(cl, n.typ)
  for i in 0 .. safeLen(n)-1: 
    # XXX HACK: ``f(a, b)``, avoid to instantiate `f` 
    if i == 0: result.add(n[i])
    else: result.add(ReplaceTypeVarsN(cl, n[i]))

proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
  if n == nil: return
  result = copyNode(n)
  result.typ = ReplaceTypeVarsT(cl, n.typ)
  case n.kind
  of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
    nil
  of nkSym:
    result.sym = ReplaceTypeVarsS(cl, n.sym)
  of nkRecWhen:
    var branch: PNode = nil              # the branch to take
    for i in countup(0, sonsLen(n) - 1):
      var it = n.sons[i]
      if it == nil: illFormedAst(n)
      case it.kind
      of nkElifBranch:
        checkSonsLen(it, 2)
        var cond = prepareNode(cl, it.sons[0])
        var e = cl.c.semConstExpr(cl.c, cond)
        if e.kind != nkIntLit:
          InternalError(e.info, "ReplaceTypeVarsN: when condition not a bool")
        if e.intVal != 0 and branch == nil: branch = it.sons[1]
      of nkElse:
        checkSonsLen(it, 1)
        if branch == nil: branch = it.sons[0]
      else: illFormedAst(n)
    if branch != nil:
      result = ReplaceTypeVarsN(cl, branch)
    else:
      result = newNodeI(nkRecList, n.info)
  else:
    var length = sonsLen(n)
    if length > 0:
      newSons(result, length)
      for i in countup(0, length - 1):
        result.sons[i] = ReplaceTypeVarsN(cl, n.sons[i])
  
proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = 
  if s == nil: return nil
  result = PSym(idTableGet(cl.symMap, s))
  if result == nil: 
    result = copySym(s, false)
    incl(result.flags, sfFromGeneric)
    idTablePut(cl.symMap, s, result)
    result.typ = ReplaceTypeVarsT(cl, s.typ)
    result.owner = s.owner
    result.ast = ReplaceTypeVarsN(cl, s.ast)

proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType = 
  result = PType(idTableGet(cl.typeMap, t))
  if result == nil:
    LocalError(t.sym.info, errCannotInstantiateX, typeToString(t))
    result = errorType(cl.c)
  elif result.kind == tyGenericParam: 
    InternalError(cl.info, "substitution with generic parameter")
  
proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = 
  # tyGenericInvokation[A, tyGenericInvokation[A, B]]
  # is difficult to handle: 
  var body = t.sons[0]
  if body.kind != tyGenericBody: InternalError(cl.info, "no generic body")
  var header: PType = nil
  when true:
    # search for some instantiation here:
    result = searchInstTypes(cl.c.generics.InstTypes, t)
    if result != nil: return
    for i in countup(1, sonsLen(t) - 1):
      var x = t.sons[i]
      if x.kind == tyGenericParam: 
        x = lookupTypeVar(cl, x)
        if header == nil: header = copyType(t, t.owner, false)
        header.sons[i] = x
        #idTablePut(cl.typeMap, body.sons[i-1], x)
    if header != nil:
      # search again after first pass:
      result = searchInstTypes(cl.c.generics.InstTypes, header)
      if result != nil: return
    else:
      header = copyType(t, t.owner, false)
    # ugh need another pass for deeply recursive generic types (e.g. PActor)
    # we need to add the candidate here, before it's fully instantiated for
    # recursive instantions:
    result = newType(tyGenericInst, t.sons[0].owner)
    idTablePut(cl.c.generics.InstTypes, header, result)

    for i in countup(1, sonsLen(t) - 1):
      var x = replaceTypeVarsT(cl, t.sons[i])
      assert x.kind != tyGenericInvokation
      header.sons[i] = x
      idTablePut(cl.typeMap, body.sons[i-1], x)
    
    for i in countup(0, sonsLen(t) - 1): 
      # if one of the params is not concrete, we cannot do anything
      # but we already raised an error!
      rawAddSon(result, header.sons[i])
    
    var newbody = ReplaceTypeVarsT(cl, lastSon(body))
    newbody.flags = newbody.flags + t.flags + body.flags
    result.flags = result.flags + newbody.flags
    newbody.callConv = body.callConv
    newbody.n = ReplaceTypeVarsN(cl, lastSon(body).n)
    rawAddSon(result, newbody)
    checkPartialConstructedType(cl.info, newbody)
  else:
    for i in countup(1, sonsLen(t) - 1):
      if PType(idTableGet(cl.typeMap, t.sons[i])) == nil: debug(t)
      var x = replaceTypeVarsT(cl, t.sons[i])
      if t.sons[i].kind == tyGenericParam: 
        if header == nil: header = copyType(t, t.owner, false)
        header.sons[i] = x
      assert x.kind != tyGenericInvokation
      idTablePut(cl.typeMap, body.sons[i-1], x)
    if header == nil: header = t
    result = searchInstTypes(cl.c.generics.InstTypes, header)
    if result != nil: return 
    result = newType(tyGenericInst, t.sons[0].owner)
    for i in countup(0, sonsLen(t) - 1): 
      # if one of the params is not concrete, we cannot do anything
      # but we already raised an error!
      addSon(result, header.sons[i])
    idTablePut(cl.c.generics.InstTypes, header, result)
    var newbody = ReplaceTypeVarsT(cl, lastSon(body))
    newbody.flags = newbody.flags + t.flags + body.flags
    newbody.n = ReplaceTypeVarsN(cl, lastSon(body).n)
    addSon(result, newbody)
    checkPartialConstructedType(cl.info, newbody)
  
proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = 
  result = t
  if t == nil: return 
  case t.kind
  of tyTypeClass: nil
  of tyGenericParam:
    result = lookupTypeVar(cl, t)
    if result.kind == tyGenericInvokation:
      result = handleGenericInvokation(cl, result)
  of tyGenericInvokation: 
    result = handleGenericInvokation(cl, t)
  of tyGenericBody: 
    InternalError(cl.info, "ReplaceTypeVarsT: tyGenericBody")
    result = ReplaceTypeVarsT(cl, lastSon(t))
  of tyInt:
    result = skipIntLit(t)
  else:
    if containsGenericType(t):
      result = copyType(t, t.owner, false)
      incl(result.flags, tfFromGeneric)
      result.size = -1 # needs to be recomputed
      for i in countup(0, sonsLen(result) - 1):
        result.sons[i] = ReplaceTypeVarsT(cl, result.sons[i])
      result.n = ReplaceTypeVarsN(cl, result.n)
      if result.Kind in GenericTypes:
        LocalError(cl.info, errCannotInstantiateX, TypeToString(t, preferName))
      if result.kind == tyProc and result.sons[0] != nil:
        if result.sons[0].kind == tyEmpty:
          result.sons[0] = nil
  
proc generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode, 
                           t: PType): PType = 
  var cl: TReplTypeVars
  InitIdTable(cl.symMap)
  copyIdTable(cl.typeMap, pt)
  cl.info = arg.info
  cl.c = p
  pushInfoContext(arg.info)
  result = ReplaceTypeVarsT(cl, t)
  popInfoContext()