summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/vm.nim40
-rw-r--r--compiler/vmdeps.nim163
-rw-r--r--compiler/vmgen.nim7
-rw-r--r--lib/core/macros.nim12
-rw-r--r--tests/macros/tgettypeinst.nim122
5 files changed, 306 insertions, 38 deletions
diff --git a/compiler/vm.nim b/compiler/vm.nim
index f799334d6..cddb57f17 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1185,19 +1185,35 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNGetType:
       let rb = instr.regB
       let rc = instr.regC
-      if rc == 0:
-        ensureKind(rkNode)
-        if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc])
+      case rc:
+        of 0:
+          # getType opcode:
+          ensureKind(rkNode)
+          if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
+            regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc])
+          else:
+            stackTrace(c, tos, pc, errGenerated, "node has no type")
+        of 1:
+          # typeKind opcode:
+          ensureKind(rkInt)
+          if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
+            regs[ra].intVal = ord(regs[rb].node.typ.kind)
+          #else:
+          #  stackTrace(c, tos, pc, errGenerated, "node has no type")
+        of 2:
+          # getTypeInst opcode:
+          ensureKind(rkNode)
+          if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
+            regs[ra].node = opMapTypeInstToAst(regs[rb].node.typ, c.debug[pc])
+          else:
+            stackTrace(c, tos, pc, errGenerated, "node has no type")
         else:
-          stackTrace(c, tos, pc, errGenerated, "node has no type")
-      else:
-        # typeKind opcode:
-        ensureKind(rkInt)
-        if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].intVal = ord(regs[rb].node.typ.kind)
-        #else:
-        #  stackTrace(c, tos, pc, errGenerated, "node has no type")
+          # getTypeImpl opcode:
+          ensureKind(rkNode)
+          if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
+            regs[ra].node = opMapTypeImplToAst(regs[rb].node.typ, c.debug[pc])
+          else:
+            stackTrace(c, tos, pc, errGenerated, "node has no type")
     of opcNStrVal:
       decodeB(rkNode)
       createStr regs[ra]
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index a4f02092d..5fef00257 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -67,9 +67,11 @@ proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode =
   result = newSymNode(sym)
   result.typ = t
 
-proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode
+proc mapTypeToAstX(t: PType; info: TLineInfo;
+                   inst=false; allowRecursionX=false): PNode
 
-proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode =
+proc mapTypeToBracketX(name: string; t: PType; info: TLineInfo;
+                       inst=false): PNode =
   result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
   result.add atomicTypeX(name, t, info)
   for i in 0 .. < t.len:
@@ -78,10 +80,39 @@ proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode =
       void.typ = newType(tyEmpty, t.owner)
       result.add void
     else:
-      result.add mapTypeToAst(t.sons[i], info)
+      result.add mapTypeToAstX(t.sons[i], info, inst)
 
-proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
+proc mapTypeToAstX(t: PType; info: TLineInfo;
+                   inst=false; allowRecursionX=false): PNode =
+  var allowRecursion = allowRecursionX
   template atomicType(name): expr = atomicTypeX(name, t, info)
+  template mapTypeToAst(t,info): expr = mapTypeToAstX(t, info, inst)
+  template mapTypeToAstR(t,info): expr = mapTypeToAstX(t, info, inst, true)
+  template mapTypeToAst(t,i,info): expr = 
+    if i<t.len and t.sons[i]!=nil: mapTypeToAstX(t.sons[i], info, inst)
+    else: ast.emptyNode
+  template mapTypeToBracket(name,t,info): expr =
+    mapTypeToBracketX(name, t, info, inst)
+  template newNodeX(kind):expr =
+    newNodeIT(kind, if t.n.isNil: info else: t.n.info, t)
+  template newIdent(s):expr =
+    var r = newNodeX(nkIdent)
+    r.add !s
+    r
+  template newIdentDefs(n,t):expr =
+    var id = newNodeX(nkIdentDefs)
+    id.add n  # name
+    id.add mapTypeToAst(t, info)  # type
+    id.add ast.emptyNode  # no assigned value
+    id
+  template newIdentDefs(s):expr = newIdentDefs(s, s.typ)
+
+  if inst:
+    if t.sym != nil:  # if this node has a symbol
+      if allowRecursion:  # getTypeImpl behavior: turn off recursion
+        allowRecursion = false
+      else:  # getTypeInst behavior: return symbol
+        return atomicType(t.sym.name.s) 
 
   case t.kind
   of tyNone: result = atomicType("none")
@@ -94,7 +125,14 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
   of tyArrayConstr, tyArray:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
     result.add atomicType("array")
-    result.add mapTypeToAst(t.sons[0], info)
+    if inst:
+      var rng = newNodeX(nkInfix)
+      rng.add newIdentNode(getIdent(".."), info)
+      rng.add t.sons[0].n.sons[0].copyTree
+      rng.add t.sons[0].n.sons[1].copyTree
+      result.add rng
+    else:
+      result.add mapTypeToAst(t.sons[0], info)
     result.add mapTypeToAst(t.sons[1], info)
   of tyTypeDesc:
     if t.base != nil:
@@ -107,34 +145,95 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
     for i in 0 .. < t.len:
       result.add mapTypeToAst(t.sons[i], info)
-  of tyGenericInst, tyGenericBody, tyOrdinal, tyUserTypeClassInst:
+  of tyGenericInst:
+    if inst:
+      if allowRecursion:
+        result = mapTypeToAstR(t.lastSon, info)
+      else:
+        result = newNodeX(nkBracketExpr)
+        result.add mapTypeToAst(t.lastSon, info)
+        for i in 1 .. < t.len-1:
+          result.add mapTypeToAst(t.sons[i], info)
+    else:
+      result = mapTypeToAst(t.lastSon, info)
+  of tyGenericBody, tyOrdinal, tyUserTypeClassInst:
     result = mapTypeToAst(t.lastSon, info)
   of tyDistinct:
-    if allowRecursion:
-      result = mapTypeToBracket("distinct", t, info)
+    if inst:
+      result = newNodeX(nkDistinctTy)
+      result.add mapTypeToAst(t.sons[0], info)
     else:
-      result = atomicType(t.sym.name.s)
+      if allowRecursion or t.sym==nil:
+        result = mapTypeToBracket("distinct", t, info)
+      else:
+        result = atomicType(t.sym.name.s)
   of tyGenericParam, tyForward: result = atomicType(t.sym.name.s)
   of tyObject:
-    if allowRecursion:
-      result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
-      if t.sons[0] == nil:
-        result.add ast.emptyNode
+    if inst:
+      result = newNodeX(nkObjectTy)
+      result.add ast.emptyNode  # pragmas not reconstructed yet
+      if t.sons[0]==nil: result.add ast.emptyNode  # handle parent object
+      else:
+        var nn = newNodeX(nkOfInherit)
+        nn.add mapTypeToAst(t.sons[0], info)
+        result.add nn
+      if t.n.sons.len>0:
+        var rl = copyNode(t.n)  # handle nkRecList
+        for s in t.n.sons:
+          rl.add newIdentDefs(s)
+        result.add rl
       else:
-        result.add mapTypeToAst(t.sons[0], info)
-      result.add copyTree(t.n)
+        result.add ast.emptyNode
     else:
-      result = atomicType(t.sym.name.s)
+      if allowRecursion or t.sym == nil:
+        result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
+        result.add ast.emptyNode
+        if t.sons[0] == nil:
+          result.add ast.emptyNode
+        else:
+          result.add mapTypeToAst(t.sons[0], info)
+        result.add copyTree(t.n)
+      else:
+        result = atomicType(t.sym.name.s)
   of tyEnum:
     result = newNodeIT(nkEnumTy, if t.n.isNil: info else: t.n.info, t)
     result.add copyTree(t.n)
-  of tyTuple: result = mapTypeToBracket("tuple", t, info)
+  of tyTuple:
+    if inst:
+      result = newNodeX(nkTupleTy)
+      for s in t.n.sons:
+        result.add newIdentDefs(s)
+    else:
+      result = mapTypeToBracket("tuple", t, info)
   of tySet: result = mapTypeToBracket("set", t, info)
-  of tyPtr: result = mapTypeToBracket("ptr", t, info)
-  of tyRef: result = mapTypeToBracket("ref", t, info)
+  of tyPtr:
+    if inst:
+      result = newNodeX(nkPtrTy)
+      result.add mapTypeToAst(t.sons[0], info)
+    else:
+      result = mapTypeToBracket("ptr", t, info)
+  of tyRef:
+    if inst:
+      result = newNodeX(nkRefTy)
+      result.add mapTypeToAst(t.sons[0], info)
+    else:
+      result = mapTypeToBracket("ref", t, info)
   of tyVar: result = mapTypeToBracket("var", t, info)
   of tySequence: result = mapTypeToBracket("seq", t, info)
-  of tyProc: result = mapTypeToBracket("proc", t, info)
+  of tyProc:
+    if inst:
+      result = newNodeX(nkProcTy)
+      var fp = newNodeX(nkFormalParams)
+      if t.sons[0] == nil:
+        fp.add ast.emptyNode
+      else:
+        fp.add mapTypeToAst(t.sons[0], t.n[0].info)
+      for i in 1..<t.sons.len:
+        fp.add newIdentDefs(t.n[i], t.sons[i])
+      result.add fp
+      result.add ast.emptyNode  # pragmas aren't reconstructed yet
+    else:
+      result = mapTypeToBracket("proc", t, info)
   of tyOpenArray: result = mapTypeToBracket("openArray", t, info)
   of tyRange:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
@@ -174,10 +273,24 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
   of tyNot: result = mapTypeToBracket("not", t, info)
   of tyAnything: result = atomicType"anything"
   of tyStatic, tyFromExpr, tyFieldAccessor:
-    result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-    result.add atomicType("static")
-    if t.n != nil:
-      result.add t.n.copyTree
+    if inst:
+      if t.n != nil: result = t.n.copyTree
+      else: result = atomicType "void"
+    else:
+      result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
+      result.add atomicType "static"
+      if t.n != nil:
+        result.add t.n.copyTree
 
 proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAst(t, info, true)
+  result = mapTypeToAstX(t, info, false, true)
+
+# the "Inst" version includes generic parameters in the resulting type tree
+# and also tries to look like the corresponding Nim type declaration
+proc opMapTypeInstToAst*(t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(t, info, true, false)
+
+# the "Impl" version includes generic parameters in the resulting type tree
+# and also tries to look like the corresponding Nim type implementation
+proc opMapTypeImplToAst*(t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(t, info, true, true)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index bd32ccc17..7832aa9b9 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -983,7 +983,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mNGetType:
     let tmp = c.genx(n.sons[1])
     if dest < 0: dest = c.getTemp(n.typ)
-    c.gABC(n, opcNGetType, dest, tmp, if n[0].sym.name.s == "typeKind": 1 else: 0)
+    let rc = case n[0].sym.name.s:
+      of "getType": 0
+      of "typeKind": 1
+      of "getTypeInst": 2
+      else: 3  # "getTypeImpl"
+    c.gABC(n, opcNGetType, dest, tmp, rc)
     c.freeTemp(tmp)
     #genUnaryABC(c, n, dest, opcNGetType)
   of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal)
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 2e3596d0f..bfda6b938 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -197,6 +197,18 @@ proc typeKind*(n: NimNode): NimTypeKind {.magic: "NGetType", noSideEffect.}
   ## Returns the type kind of the node 'n' that should represent a type, that
   ## means the node should have been obtained via `getType`.
 
+proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
+  ## Like getType except it includes generic parameters for a specific instance
+
+proc getTypeInst*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
+  ## Like getType except it includes generic parameters for a specific instance
+
+proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
+  ## Like getType except it includes generic parameters for the implementation
+
+proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
+  ## Like getType except it includes generic parameters for the implementation
+
 proc strVal*(n: NimNode): string  {.magic: "NStrVal", noSideEffect.}
 
 proc `intVal=`*(n: NimNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.}
diff --git a/tests/macros/tgettypeinst.nim b/tests/macros/tgettypeinst.nim
new file mode 100644
index 000000000..22e03a119
--- /dev/null
+++ b/tests/macros/tgettypeinst.nim
@@ -0,0 +1,122 @@
+discard """
+"""
+
+import macros, strUtils
+
+proc symToIdent(x: NimNode): NimNode =
+  case x.kind:
+    of nnkCharLit..nnkUInt64Lit:
+      result = newNimNode(x.kind)
+      result.intVal = x.intVal
+    of nnkFloatLit..nnkFloat64Lit:
+      result = newNimNode(x.kind)
+      result.floatVal = x.floatVal
+    of nnkStrLit..nnkTripleStrLit:
+      result = newNimNode(x.kind)
+      result.strVal = x.strVal
+    of nnkIdent, nnkSym:
+      result = newIdentNode($x)
+    else:
+      result = newNimNode(x.kind)
+      for c in x:
+        result.add symToIdent(c)
+
+macro testX(x,inst0: typed; recurse: static[bool]; implX: stmt): typed =
+  let inst = x.getTypeInst
+  let impl = x.getTypeImpl
+  let inst0r = inst0.symToIdent.treeRepr
+  let instr = inst.symToIdent.treeRepr
+  #echo inst0r
+  #echo instr
+  doAssert(instr == inst0r)
+  var impl0 =
+    if implX.kind == nnkNilLit: inst0
+    else: implX[0][2]
+  let impl0r = impl0.symToIdent.treerepr
+  let implr = impl.symToIdent.treerepr
+  #echo impl0r
+  #echo implr
+  doAssert(implr == impl0r)
+  template echoString(s:string) = echo s.replace("\n","\n  ")
+  result = newStmtList()
+  #result.add getAst(echoString("  " & inst0.repr))
+  #result.add getAst(echoString("  " & inst.repr))
+  #result.add getAst(echoString("  " & impl0.repr))
+  #result.add getAst(echoString("  " & impl.repr))
+  if recurse:
+    template testDecl(n, m :typed) =
+      testV(n, false):
+        type _ = m
+    result.add getAst(testDecl(inst.symToIdent, impl.symToIdent))
+
+template testV(inst, recurse, impl) =
+  block:
+    #echo "testV(" & astToStr(inst) & ", " & $recurse & "):" & astToStr(impl)
+    var x: inst
+    testX(x, inst, recurse, impl)
+template testT(inst, recurse) =
+  block:
+    type myType = inst
+    testV(myType, recurse):
+      type _ = inst
+
+template test(inst) =
+  testT(inst, false)
+  testV(inst, true, nil)
+template test(inst, impl) = testV(inst, true, impl)
+
+type
+  Model = object of RootObj
+  User = object of Model
+    name : string
+    password : string
+
+  Tree = object of RootObj
+    value : int
+    left,right : ref Tree
+
+  MyEnum = enum
+    valueA, valueB, valueC
+
+  MySet = set[MyEnum]
+  MySeq = seq[int]
+  MyIntPtr = ptr int
+  MyIntRef = ref int
+
+  GenericObject[T] = object
+    value:T
+  Foo[N:static[int],T] = object
+  Bar[N:static[int],T] = object
+    #baz:Foo[N+1,GenericObject[T]]
+    baz:Foo[N,GenericObject[T]]
+
+test(bool)
+test(char)
+test(int)
+test(float)
+test(ptr int)
+test(ref int)
+test(array[1..10,Bar[2,Foo[3,float]]])
+test(distinct Bar[2,Foo[3,float]])
+test(tuple[a:int,b:Foo[-1,float]])
+#test(MyEnum):
+#  type _ = enum
+#    valueA, valueB, valueC
+test(set[MyEnum])
+test(seq[int])
+test(Bar[2,Foo[3,float]]):
+  type _ = object
+    baz: Foo[2, GenericObject[Foo[3, float]]]
+test(Model):
+  type _ = object of RootObj
+test(User):
+  type _ = object of Model
+    name: string
+    password: string
+test(Tree):
+  type _ = object of RootObj
+    value: int
+    left: ref Tree
+    right: ref Tree
+test(proc (a: int, b: Foo[2,float]))
+test(proc (a: int, b: Foo[2,float]): Bar[3,int])