From f907cb6ff56ce1bf42cb38ecb4a63f9e4d6f6015 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 9 Jan 2020 10:38:17 -0800 Subject: [PATCH] * fix #9855, fix #9855, new: getTypeId, fix genericHead --- compiler/ast.nim | 3 ++- compiler/semexprs.nim | 11 +++++++- compiler/semmagic.nim | 31 ++++++++++++++-------- compiler/semtypes.nim | 9 +++---- lib/pure/typetraits.nim | 10 ++++++++ tests/metatype/ttypetraits.nim | 47 ++++++++++++++++++++++++++++++++-- tests/types/tisopr.nim | 20 +++++++++++++++ 7 files changed, 112 insertions(+), 19 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 6ac4a7175b409..f1be5179de526 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -976,8 +976,9 @@ const tyPointer, tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64} - IntegralTypes* = {tyBool, tyChar, tyEnum, tyInt..tyInt64, + NumberLikeTypes* = {tyBool, tyChar, tyEnum, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64} + IntegralTypes* = NumberLikeTypes + {tyEnum} # weird name because it contains tyFloat ConstantDataTypes*: TTypeKinds = {tyArray, tySet, tyTuple, tySequence} NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cbdb5ce77c9e2..a725e908772fd 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -396,12 +396,21 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = else: res = false else: - maybeLiftType(t2, c, n.info) + if t1.skipTypes({tyAlias}).kind != tyGenericBody: + maybeLiftType(t2, c, n.info) + else: + #[ + for this case: + type Foo = object[T] + Foo is Foo + ]# + discard var m = newCandidate(c, t2) if efExplain in flags: m.diagnostics = @[] m.diagnosticsEnabled = true res = typeRel(m, t2, t1) >= isSubtype # isNone + # `res = sameType(t1, t2)` would be wrong, eg for `int is (int|float)` result = newIntNode(nkIntLit, ord(res)) result.typ = n.typ diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 30ff73d7f09de..f70daafbdd9a0 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -121,6 +121,11 @@ proc uninstantiate(t: PType): PType = of tyCompositeTypeClass: uninstantiate t[1] else: t +proc getTypeDescNode(typ: PType, sym: PSym, info: TLineInfo): PNode = + var resType = newType(tyTypeDesc, sym) + rawAddSon(resType, typ) + result = toNode(resType, info) + proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym): PNode = const skippedTypes = {tyTypeDesc, tyAlias, tySink} let trait = traitCall[0] @@ -160,14 +165,22 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc)) result.typ = newType(tyInt, context) result.info = traitCall.info + of "getTypeid": + var arg = operand + if arg.kind in NumberLikeTypes: + # needed otherwise we could get different ids, see tests + arg = getSysType(c.graph, traitCall[1].info, arg.kind) + result = newIntNode(nkIntLit, arg.id) + # `id` better than cast[int](arg) so that it's reproducible across compiles + result.typ = getSysType(c.graph, traitCall[1].info, tyInt) + result.info = traitCall.info of "genericHead": - var res = uninstantiate(operand) - if res == operand and res.kind notin tyMagicGenerics: - localError(c.config, traitCall.info, - "genericHead expects a generic type. The given type was " & - typeToString(operand)) - return newType(tyError, context).toNode(traitCall.info) - result = res.base.toNode(traitCall.info) + var arg = operand + if arg.kind == tyGenericInst: + result = getTypeDescNode(arg.base, operand.owner, traitCall.info) + else: + localError(c.config, traitCall.info, "expected generic instantiation, got: " & $(arg.kind, typeToString(operand))) + result = newType(tyError, context).toNode(traitCall.info) of "stripGenericParams": result = uninstantiate(operand).toNode(traitCall.info) of "supportsCopyMem": @@ -185,9 +198,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) while arg.kind == tyDistinct: arg = arg.base arg = arg.skipTypes(skippedTypes + {tyGenericInst}) - var resType = newType(tyTypeDesc, operand.owner) - rawAddSon(resType, arg) - result = toNode(resType, traitCall.info) + result = getTypeDescNode(arg, operand.owner, traitCall.info) else: localError(c.config, traitCall.info, "distinctBase expects a distinct type as argument. The given type was " & typeToString(operand)) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index ed59ce36a2e29..f19e6c856858a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1383,7 +1383,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = return newOrPrevType(tyError, prev, c) var t = s.typ - if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody: + if t.kind in {tyCompositeTypeClass, tyAlias} and t.base.kind == tyGenericBody: t = t.base result = newOrPrevType(tyGenericInvocation, prev, c) @@ -1403,7 +1403,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = elif t.kind != tyGenericBody: # we likely got code of the form TypeA[TypeB] where TypeA is # not generic. - localError(c.config, n.info, errNoGenericParamsAllowedForX % s.name.s) + localError(c.config, n.info, errNoGenericParamsAllowedForX % $(s.name.s, t.kind)) return newOrPrevType(tyError, prev, c) else: var m = newCandidate(c, t) @@ -1454,7 +1454,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = recomputeFieldPositions(tx, tx.n, position) proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType = - if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward} and prev != nil: + if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody} and prev != nil: result = newTypeS(tyAlias, c) result.rawAddSon typeExpr result.sym = prev.sym @@ -1781,8 +1781,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: assignType(prev, s.typ) # bugfix: keep the fresh id for aliases to integral types: - if s.typ.kind notin {tyBool, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, - tyUInt..tyUInt64}: + if s.typ.kind notin NumberLikeTypes: prev.id = s.typ.id result = prev of nkSym: diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index fd729b59eae4e..4daac5177c70d 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -16,6 +16,16 @@ export system.`$` # for backward compatibility include "system/inclrtl" +proc getTypeid*(t: typedesc): int {.magic: "TypeTrait", since: (1, 1).} = + ## returns a unique id representing a type; the id is stable across + ## recompilations of the same program, but may differ if the program source + ## changes. + runnableExamples: + type Foo[T] = object + type Foo2 = Foo + doAssert Foo[int].getTypeid == Foo2[type(1)].getTypeid + doAssert Foo[int].getTypeid != Foo[float].getTypeid + proc name*(t: typedesc): string {.magic: "TypeTrait".} ## Returns the name of the given type. ## diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim index c4fb9beefe513..aecd1e12145c1 100644 --- a/tests/metatype/ttypetraits.nim +++ b/tests/metatype/ttypetraits.nim @@ -47,7 +47,7 @@ block: # typeToString doAssert MyInt.name3 == "MyInt{int}" doAssert (tuple[a: MyInt, b: float]).name3 == "tuple[a: MyInt{int}, b: float]" doAssert (tuple[a: C2b[MyInt, C4[cstring]], b: cint, c: float]).name3 == - "tuple[a: C2b{C}[MyInt{int}, C4[cstring]], b: cint{int32}, c: float]" + "tuple[a: C[MyInt{int}, C4[cstring]], b: cint{int32}, c: float]" block distinctBase: block: @@ -125,4 +125,47 @@ var x = CpuStorage[string]() static: doAssert(not string.supportsCopyMem) doAssert x.T is string # true - doAssert x.raw_buffer is seq \ No newline at end of file + doAssert x.raw_buffer is seq + +block: + const c1 = getTypeid(type(12)) + const a1 = getTypeid(type(12)) + const a2 = getTypeid(type(12)) + let a3 = getTypeid(type(12)) + doAssert a1 == a2 + # we need to check that because nim uses different id's + # for different instances of tyInt (etc), so we make sure implementation of + # `getTypeid` is robust to that + doAssert a1 == a3 + doAssert getTypeid(type(12.0)) != getTypeid(type(12)) + doAssert getTypeid(type(12.0)) == getTypeid(float) + + type Foo = object + x1: int + + type FooT[T] = object + x1: int + type Foo2 = Foo + type FooT2 = FooT + doAssert Foo.getTypeid == Foo2.getTypeid + doAssert FooT2.getTypeid == FooT.getTypeid + doAssert FooT2[float].getTypeid == FooT[type(1.2)].getTypeid + doAssert FooT2[float].getTypeid != FooT[type(1)].getTypeid + doAssert Foo.x1.type.getTypeid == int.getTypeid + +block genericHead: + type Foo[T1,T2] = object + x1: T1 + x2: T2 + type FooInst = Foo[int, float] + type Foo2 = genericHead(FooInst) + doAssert Foo2 is Foo # issue #13066 + type Foo2Inst = Foo2[int, float] + doAssert FooInst.default == Foo2Inst.default + doAssert FooInst.default.x2 == 0.0 + doAssert Foo2Inst is FooInst + doAssert FooInst is Foo2Inst + doAssert compiles(genericHead(FooInst)) + doAssert not compiles(genericHead(Foo)) + type Bar = object + doAssert not compiles(genericHead(Bar)) diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 636377301e290..1bbd0da94e969 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -89,3 +89,23 @@ proc test[T](x: T) = echo "no" test(7) + +block: + # bug #13066 + type Bar[T1,T2] = object + type Foo[T1,T2] = object + type Foo2 = Foo + doAssert Foo2 is Foo + doAssert Foo is Foo2 + doAssert Foo is Foo + doAssert Foo2 is Foo2 + doAssert Foo2 isnot Bar + doAssert Foo[int,float] is Foo2[int,float] + + # other + doAssert Foo[int,float] isnot Foo2[float,float] + doAssert Foo[int,float] is Foo2 + doAssert Foo[int,float|int] is Foo2 + doAssert Foo2[int,float|int] is Foo + doAssert Foo2[int,float|int] isnot Bar + doAssert int is (int|float)