summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2014-03-16 11:39:53 +0200
committerZahary Karadjov <zahary@gmail.com>2014-03-16 20:42:06 +0200
commitac271e76b18110bea8046af64ceccd6b804978dd (patch)
tree3cecf7bed8b9f5759d766e0ec9e8323277b14b7f
parent7dcf6ff50b03dfd54968383ad5a4258f040eec1b (diff)
downloadNim-ac271e76b18110bea8046af64ceccd6b804978dd.tar.gz
more robust handling of proc signatures containing inter-param type references
-rw-r--r--compiler/semexprs.nim13
-rw-r--r--compiler/seminst.nim69
-rw-r--r--compiler/semtypes.nim22
-rw-r--r--compiler/semtypinst.nim10
-rw-r--r--compiler/sigmatch.nim13
-rw-r--r--tests/generics/tsigtypeop.nim9
6 files changed, 106 insertions, 30 deletions
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 133b4ac1e..5a12156ec 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -899,10 +899,13 @@ proc makeDeref(n: PNode): PNode =
     addSon(result, a)
     t = skipTypes(t.sons[0], {tyGenericInst})
 
-proc readTypeParameter(c: PContext, ty: PType,
+const tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
+
+proc readTypeParameter(c: PContext, typ: PType,
                        paramName: PIdent, info: TLineInfo): PNode =
-  internalAssert ty.kind == tyGenericInst
-  let ty = ty.skipGenericAlias
+  let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias
+           else: (internalAssert typ.kind == tyCompositeTypeClass; typ.sons[1])
+  
   let tbody = ty.sons[0]
   for s in countup(0, tbody.len-2):
     let tParam = tbody.sons[s]
@@ -946,7 +949,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
         result.typ = ty
         markUsed(n, f)
         return
-    of tyGenericInst:
+    of tyTypeParamsHolders:
       return readTypeParameter(c, ty, i, n.info)
     of tyObject, tyTuple:
       if ty.n.kind == nkRecList:
@@ -996,7 +999,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
       result = n
 
   # we didn't find any field, let's look for a generic param
-  if result == nil and n.sons[0].typ.kind == tyGenericInst:
+  if result == nil and n.sons[0].typ.kind in tyTypeParamsHolders:
     result = readTypeParameter(c, n.sons[0].typ, i, n.info)
 
 proc dotTransformation(c: PContext, n: PNode): PNode =
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index dca9e3378..3d3227e4e 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -85,19 +85,21 @@ proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
 
 proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
 
+proc addProcDecls(c: PContext, fn: PSym) =
+  # get the proc itself in scope (e.g. for recursion)
+  addDecl(c, fn)
+
+  for i in 1 .. <fn.typ.n.len:
+    var param = fn.typ.n.sons[i].sym
+    param.owner = fn
+    addParamOrResult(c, param, fn.kind)
+  
+  maybeAddResult(c, fn, fn.ast)
+
 proc instantiateBody(c: PContext, n: PNode, result: PSym) =
   if n.sons[bodyPos].kind != nkEmpty:
     inc c.inGenericInst
     # add it here, so that recursive generic procs are possible:
-    addDecl(c, result)
-    pushProcCon(c, result)
-    # add params to scope
-    for i in 1 .. <result.typ.n.len:
-      var param = result.typ.n.sons[i].sym
-      param.owner = result
-      addParamOrResult(c, param, result.kind)
-    # debug result.typ.n
-    maybeAddResult(c, result, n)
     var b = n.sons[bodyPos]
     var symMap: TIdTable
     initIdTable symMap
@@ -107,7 +109,6 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
     n.sons[bodyPos] = transformBody(c.module, b, result)
     #echo "code instantiated ", result.name.s
     excl(result.flags, sfForward)
-    popProcCon(c)
     dec c.inGenericInst
 
 proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
@@ -116,9 +117,12 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
       var oldPrc = c.generics[i].inst.sym
       pushInfoContext(oldPrc.info)
       openScope(c)
+      pushProcCon(c, oldPrc)
+      addProcDecls(c, oldPrc)
       var n = oldPrc.ast
       n.sons[bodyPos] = copyTree(s.getBody)
       instantiateBody(c, n, oldPrc)
+      popProcCon(c)
       closeScope(c)
       popInfoContext()
 
@@ -144,6 +148,47 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
 proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
   result = instGenericContainer(c, n.info, header)
 
+proc instantiateProcType(c: PContext, pt: TIdTable,
+                          prc: PSym, info: TLineInfo) =
+  # XXX: Instantiates a generic proc signature, while at the same
+  # time adding the instantiated proc params into the current scope.
+  # This is necessary, because the instantiation process may refer to
+  # these params in situations like this:
+  # proc foo[Container](a: Container, b: a.type.Item): type(b.x)
+  #
+  # Alas, doing this here is probably not enough, because another
+  # proc signature could appear in the params:
+  # proc foo[T](a: proc (x: T, b: type(x.y))
+  #   
+  # The solution would be to move this logic into semtypinst, but
+  # at this point semtypinst have to become part of sem, because it
+  # will need to use openScope, addDecl, etc
+  #
+  pushInfoContext(info)
+  var cl = initTypeVars(c, pt, info)
+  var result = instCopyType(cl, prc.typ)
+  addDecl(c, prc)
+  let originalParams = result.n
+  result.n = originalParams.shallowCopy
+  
+  for i in 1 .. <result.len:
+    result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
+    propagateToOwner(result, result.sons[i])
+    let param = replaceTypeVarsN(cl, originalParams[i])
+    internalAssert param.kind == nkSym
+    result.n.sons[i] = param
+    addDecl(c, param.sym)
+  
+  result.sons[0] = replaceTypeVarsT(cl, result.sons[0])
+  result.n.sons[0] = originalParams[0].copyTree
+  
+  eraseVoidParams(result)
+  skipIntLiteralParams(result)
+ 
+  prc.typ = result
+  maybeAddResult(c, prc, prc.ast)
+  popInfoContext()
+
 proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
                       info: TLineInfo): PSym =
   # no need to instantiate generic templates/macros:
@@ -171,7 +216,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   var entry = TInstantiation.new
   entry.sym = result
   instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
-  result.typ = generateTypeInstance(c, pt, info, fn.typ)
+  pushProcCon(c, result)
+  instantiateProcType(c, pt, result, info)
   n.sons[genericParamsPos] = ast.emptyNode
   var oldPrc = genericCacheGet(fn, entry[])
   if oldPrc == nil:
@@ -186,6 +232,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     paramsTypeCheck(c, result.typ)
   else:
     result = oldPrc
+  popProcCon(c)
   popInfoContext()
   closeScope(c)           # close scope for parameters
   popOwner()
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 6e47af837..74378801d 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -680,6 +680,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     s.position = genericParams.len
     genericParams.addSon(newSymNode(s))
     result = typeClass
+    addDecl(c, s)
  
   # XXX: There are codegen errors if this is turned into a nested proc
   template liftingWalk(typ: PType, anonFlag = false): expr =
@@ -802,10 +803,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
       result = addImplicitGeneric(newTypeS(tyAnything, c))
   
   of tyGenericParam:
+    markUsed(genericParams, paramType.sym)
     if tfWildcard in paramType.flags:
       paramType.flags.excl tfWildcard
       paramType.sym.kind = skType
- 
+    
   else: discard
 
   # result = liftingWalk(paramType)
@@ -1108,13 +1110,17 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         for i in countup(1, n.len - 1):
           result.rawAddSon(semTypeNode(c, n.sons[i], nil))
     else: result = semGeneric(c, n, s, prev)
-  of nkIdent, nkDotExpr, nkAccQuoted: 
-    if n.kind == nkDotExpr:
-      let head = qualifiedLookUp(c, n[0], {checkAmbiguity, checkUndeclared})
-      if head.kind in {skType}:
-        var toBind = initIntSet()
-        var preprocessed = semGenericStmt(c, n, {}, toBind)
-        return makeTypeFromExpr(c, preprocessed)
+  of nkDotExpr:
+    var typeExpr = semExpr(c, n)
+    if typeExpr.typ.kind != tyTypeDesc:
+      localError(n.info, errTypeExpected)
+      return errorType(c)
+    result = typeExpr.typ.base
+    if result.isMetaType:
+      var toBind = initIntSet()
+      var preprocessed = semGenericStmt(c, n, {}, toBind)
+      return makeTypeFromExpr(c, preprocessed)
+  of nkIdent, nkAccQuoted:
     var s = semTypeIdent(c, n)
     if s.typ == nil: 
       if s.kind != skError: localError(n.info, errTypeExpected)
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index f8cddd781..30810a1d1 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -80,7 +80,7 @@ type
 
 proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
 proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
-proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode
+proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode): PNode
 
 template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
   when false:
@@ -210,7 +210,7 @@ proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
   elif result.kind == tyGenericParam and not cl.allowMetaTypes:
     internalError(cl.info, "substitution with generic parameter")
 
-proc instCopyType(cl: var TReplTypeVars, t: PType): PType =
+proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
   # XXX: relying on allowMetaTypes is a kludge
   result = copyType(t, t.owner, cl.allowMetaTypes)
   result.flags.incl tfFromGeneric
@@ -281,7 +281,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
   rawAddSon(result, newbody)
   checkPartialConstructedType(cl.info, newbody)
 
-proc eraseVoidParams(t: PType) =
+proc eraseVoidParams*(t: PType) =
   if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
     t.sons[0] = nil
   
@@ -298,7 +298,7 @@ proc eraseVoidParams(t: PType) =
       setLen t.n.sons, pos
       return
 
-proc skipIntLiteralParams(t: PType) =
+proc skipIntLiteralParams*(t: PType) =
   for i in 0 .. <t.sonsLen:
     let p = t.sons[i]
     if p == nil: continue
@@ -409,7 +409,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       
       else: discard
 
-proc initTypeVars(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
+proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
   initIdTable(result.symMap)
   copyIdTable(result.typeMap, pt)
   initIdTable(result.localCache)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 433455365..4ad7849e5 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -944,8 +944,19 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   
   of tyProxy:
     result = isEqual
+
+  of tyFromExpr:
+    # fix the expression, so it contains the already instantiated types
+    let instantiated = replaceTypesInBody(c.c, c.bindings, f.n)
+    let reevaluted = c.c.semExpr(c.c, instantiated)
+    if reevaluted.typ.kind != tyTypeDesc:
+      localError(f.n.info, errTypeExpected)
+      result = isNone
+    else:
+      result = typeRel(c, a, reevaluted.typ.base)
   
-  else: internalAssert false
+  else:
+    internalAssert false
   
 proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = 
   var m: TCandidate
diff --git a/tests/generics/tsigtypeop.nim b/tests/generics/tsigtypeop.nim
new file mode 100644
index 000000000..4c863cba1
--- /dev/null
+++ b/tests/generics/tsigtypeop.nim
@@ -0,0 +1,9 @@
+type
+  Vec3[T] = array[3, T]
+
+proc foo(x: Vec3, y: Vec3.T, z: x.T): x.type.T =
+  return 10
+
+var y: Vec3[int] = [1, 2, 3]
+var z: int = foo(y, 3, 4)
+