diff options
-rw-r--r-- | compiler/vm.nim | 40 | ||||
-rw-r--r-- | compiler/vmdeps.nim | 163 | ||||
-rw-r--r-- | compiler/vmgen.nim | 7 | ||||
-rw-r--r-- | lib/core/macros.nim | 12 | ||||
-rw-r--r-- | tests/macros/tgettypeinst.nim | 122 |
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]) |