Skip to content

Commit

Permalink
* fix nim-lang#9855, fix nim-lang#9855, new: getTypeId, fix genericHead
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheecour committed Feb 8, 2020
1 parent e441542 commit f907cb6
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 19 deletions.
3 changes: 2 additions & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 10 additions & 1 deletion compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 21 additions & 10 deletions compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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":
Expand All @@ -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))
Expand Down
9 changes: 4 additions & 5 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
10 changes: 10 additions & 0 deletions lib/pure/typetraits.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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.
##
Expand Down
47 changes: 45 additions & 2 deletions tests/metatype/ttypetraits.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
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))
20 changes: 20 additions & 0 deletions tests/types/tisopr.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit f907cb6

Please sign in to comment.