Implement the is operator for the new static and typedesc type classes
This also makes the first baby steps towards a sound treatment of higher-order kinds (type type int). Adds test cases showcasing the new features. * Also fixes breakage after the rebase
This commit is contained in:
parent
ab9969ed3b
commit
a49b06a52a
10 changed files with 210 additions and 55 deletions
|
|
@ -798,8 +798,7 @@ proc primarySuffix(p: var TParser, r: PNode,
|
||||||
# `foo ref` or `foo ptr`. Unfortunately, these two are also
|
# `foo ref` or `foo ptr`. Unfortunately, these two are also
|
||||||
# used as infix operators for the memory regions feature and
|
# used as infix operators for the memory regions feature and
|
||||||
# the current parsing rules don't play well here.
|
# the current parsing rules don't play well here.
|
||||||
if mode == pmTypeDef or
|
if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}):
|
||||||
(p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot})):
|
|
||||||
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
|
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
|
||||||
# solution, but pragmas.nim can't handle that
|
# solution, but pragmas.nim can't handle that
|
||||||
let a = result
|
let a = result
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||||
result.addSonSkipIntLit(typ)
|
result.addSonSkipIntLit(typ)
|
||||||
|
|
||||||
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
||||||
let typedesc = makeTypeDesc(c, typ)
|
let typedesc = newTypeS(tyTypeDesc, c)
|
||||||
|
typedesc.addSonSkipIntLit(assertNotNil(c.config, typ))
|
||||||
let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
|
let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
|
||||||
c.config.options).linkTo(typedesc)
|
c.config.options).linkTo(typedesc)
|
||||||
return newSymNode(sym, info)
|
return newSymNode(sym, info)
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||||
|
|
||||||
var targetType = semTypeNode(c, n.sons[0], nil)
|
var targetType = semTypeNode(c, n.sons[0], nil)
|
||||||
if targetType.kind == tyTypeDesc:
|
if targetType.kind == tyTypeDesc:
|
||||||
internalAssert targetType.len > 0
|
internalAssert c.config, targetType.len > 0
|
||||||
if targetType.base.kind == tyNone:
|
if targetType.base.kind == tyNone:
|
||||||
return semTypeOf(c, n[1])
|
return semTypeOf(c, n[1])
|
||||||
else:
|
else:
|
||||||
|
|
@ -316,57 +316,98 @@ proc semSizeof(c: PContext, n: PNode): PNode =
|
||||||
n.typ = getSysType(c.graph, n.info, tyInt)
|
n.typ = getSysType(c.graph, n.info, tyInt)
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
|
proc fixupStaticType(c: PContext, n: PNode) =
|
||||||
|
# This proc can be applied to evaluated expressions to assign
|
||||||
|
# them a static type.
|
||||||
|
#
|
||||||
|
# XXX: with implicit static, this should not be necessary,
|
||||||
|
# because the output type of operations such as `semConstExpr`
|
||||||
|
# should be a static type (as well as the type of any other
|
||||||
|
# expression that can be implicitly evaluated). For now, we
|
||||||
|
# apply this measure only in code that is enlightened to work
|
||||||
|
# with static types.
|
||||||
|
if n.typ.kind != tyStatic:
|
||||||
|
n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ])
|
||||||
|
n.typ.n = n # XXX: cycles like the one here look dangerous.
|
||||||
|
# Consider using `n.copyTree`
|
||||||
|
|
||||||
proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
internalAssert c.config, n.sonsLen == 3 and
|
internalAssert c.config,
|
||||||
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
|
n.sonsLen == 3 and
|
||||||
|
n[1].typ != nil and
|
||||||
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
|
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
|
||||||
|
|
||||||
let t1 = n[1].typ.skipTypes({tyTypeDesc})
|
var
|
||||||
|
res = false
|
||||||
|
t1 = n[1].typ
|
||||||
|
t2 = n[2].typ
|
||||||
|
|
||||||
|
if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc:
|
||||||
|
t1 = t1.base
|
||||||
|
|
||||||
if n[2].kind in {nkStrLit..nkTripleStrLit}:
|
if n[2].kind in {nkStrLit..nkTripleStrLit}:
|
||||||
case n[2].strVal.normalize
|
case n[2].strVal.normalize
|
||||||
of "closure":
|
of "closure":
|
||||||
let t = skipTypes(t1, abstractRange)
|
let t = skipTypes(t1, abstractRange)
|
||||||
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
res = t.kind == tyProc and
|
||||||
t.callConv == ccClosure and
|
t.callConv == ccClosure and
|
||||||
tfIterator notin t.flags))
|
tfIterator notin t.flags
|
||||||
else:
|
else:
|
||||||
result = newIntNode(nkIntLit, 0)
|
res = false
|
||||||
else:
|
else:
|
||||||
var rhsOrigType = n[2].typ
|
|
||||||
var t2 = rhsOrigType.skipTypes({tyTypeDesc})
|
|
||||||
maybeLiftType(t2, c, n.info)
|
maybeLiftType(t2, c, n.info)
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
initCandidate(c, m, t2)
|
initCandidate(c, m, t2)
|
||||||
if efExplain in flags:
|
if efExplain in flags:
|
||||||
m.diagnostics = @[]
|
m.diagnostics = @[]
|
||||||
m.diagnosticsEnabled = true
|
m.diagnosticsEnabled = true
|
||||||
let match = typeRel(m, t2, t1) >= isSubtype # isNone
|
res = typeRel(m, t2, t1) >= isSubtype # isNone
|
||||||
result = newIntNode(nkIntLit, ord(match))
|
|
||||||
|
|
||||||
|
result = newIntNode(nkIntLit, ord(res))
|
||||||
result.typ = n.typ
|
result.typ = n.typ
|
||||||
|
|
||||||
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
if sonsLen(n) != 3:
|
if sonsLen(n) != 3:
|
||||||
localError(c.config, n.info, "'is' operator takes 2 arguments")
|
localError(c.config, n.info, "'is' operator takes 2 arguments")
|
||||||
|
|
||||||
|
let boolType = getSysType(c.graph, n.info, tyBool)
|
||||||
result = n
|
result = n
|
||||||
n.typ = getSysType(c.graph, n.info, tyBool)
|
n.typ = boolType
|
||||||
|
var liftLhs = true
|
||||||
|
|
||||||
n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
|
n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
|
||||||
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
|
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
|
||||||
let t2 = semTypeNode(c, n[2], nil)
|
let t2 = semTypeNode(c, n[2], nil)
|
||||||
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
|
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
|
||||||
|
if t2.kind == tyStatic:
|
||||||
|
let evaluated = tryConstExpr(c, n[1])
|
||||||
|
if evaluated != nil:
|
||||||
|
c.fixupStaticType(evaluated)
|
||||||
|
n[1] = evaluated
|
||||||
|
else:
|
||||||
|
result = newIntNode(nkIntLit, 0)
|
||||||
|
result.typ = boolType
|
||||||
|
return
|
||||||
|
elif t2.kind == tyTypeDesc and
|
||||||
|
(t2.base.kind == tyNone or tfExplicit in t2.flags):
|
||||||
|
# When the right-hand side is an explicit type, we must
|
||||||
|
# not allow regular values to be matched against the type:
|
||||||
|
liftLhs = false
|
||||||
|
|
||||||
let lhsType = n[1].typ
|
var lhsType = n[1].typ
|
||||||
if lhsType.kind != tyTypeDesc:
|
if lhsType.kind != tyTypeDesc:
|
||||||
n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
if liftLhs:
|
||||||
elif lhsType.base.kind == tyNone:
|
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
||||||
|
lhsType = n[1].typ
|
||||||
|
else:
|
||||||
|
if lhsType.base.kind == tyNone:
|
||||||
# this is a typedesc variable, leave for evals
|
# this is a typedesc variable, leave for evals
|
||||||
return
|
return
|
||||||
|
if lhsType.base.containsGenericType:
|
||||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||||
if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags)
|
return
|
||||||
|
|
||||||
|
result = isOpImpl(c, n, flags)
|
||||||
|
|
||||||
proc semOpAux(c: PContext, n: PNode) =
|
proc semOpAux(c: PContext, n: PNode) =
|
||||||
const flags = {efDetermineType}
|
const flags = {efDetermineType}
|
||||||
|
|
|
||||||
|
|
@ -1472,8 +1472,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
||||||
of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
|
of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
|
||||||
of mVarargs: result = semVarargs(c, n, prev)
|
of mVarargs: result = semVarargs(c, n, prev)
|
||||||
of mTypeDesc, mTypeTy: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
of mTypeDesc, mTypeTy:
|
||||||
of mStaticTy: result = semStaticType(c, n[1], prev)
|
result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
||||||
|
result.flags.incl tfExplicit
|
||||||
|
of mStaticTy:
|
||||||
|
result = semStaticType(c, n[1], prev)
|
||||||
of mExpr:
|
of mExpr:
|
||||||
result = semTypeNode(c, n.sons[0], nil)
|
result = semTypeNode(c, n.sons[0], nil)
|
||||||
if result != nil:
|
if result != nil:
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,16 @@ type
|
||||||
x: T
|
x: T
|
||||||
y: U
|
y: U
|
||||||
|
|
||||||
proc getTypeName(t: typedesc): string = t.name
|
proc getTypeName1(t: typedesc): string = t.name
|
||||||
|
proc getTypeName2(t: type): string = t.name
|
||||||
|
|
||||||
proc foo(T: typedesc[float], a: auto): string =
|
proc foo(T: type float, a: auto): string =
|
||||||
result = "float " & $(a.len > 5)
|
result = "float " & $(a.len > 5)
|
||||||
|
|
||||||
proc foo(T: typedesc[TFoo], a: int): string =
|
proc foo(T: typedesc[TFoo], a: int): string =
|
||||||
result = "TFoo " & $(a)
|
result = "TFoo " & $(a)
|
||||||
|
|
||||||
proc foo(T: typedesc[int or bool]): string =
|
proc foo(T: type[int or bool]): string =
|
||||||
var a: T
|
var a: T
|
||||||
a = 10
|
a = 10
|
||||||
result = "int or bool " & ($a)
|
result = "int or bool " & ($a)
|
||||||
|
|
@ -23,8 +24,8 @@ template foo(T: typedesc[seq]): string = "seq"
|
||||||
test "types can be used as proc params":
|
test "types can be used as proc params":
|
||||||
# XXX: `check` needs to know that TFoo[int, float] is a type and
|
# XXX: `check` needs to know that TFoo[int, float] is a type and
|
||||||
# cannot be assigned for a local variable for later inspection
|
# cannot be assigned for a local variable for later inspection
|
||||||
check ((string.getTypeName == "string"))
|
check ((string.getTypeName1 == "string"))
|
||||||
check ((getTypeName(int) == "int"))
|
check ((getTypeName2(int) == "int"))
|
||||||
|
|
||||||
check ((foo(TFoo[int, float], 1000) == "TFoo 1000"))
|
check ((foo(TFoo[int, float], 1000) == "TFoo 1000"))
|
||||||
|
|
||||||
|
|
@ -37,6 +38,25 @@ test "types can be used as proc params":
|
||||||
check ((foo(seq[int]) == "seq"))
|
check ((foo(seq[int]) == "seq"))
|
||||||
check ((foo(seq[TFoo[bool, string]]) == "seq"))
|
check ((foo(seq[TFoo[bool, string]]) == "seq"))
|
||||||
|
|
||||||
when false:
|
template accept(x) =
|
||||||
proc foo(T: typedesc[seq], s: T) = nil
|
static: assert(compiles(x))
|
||||||
|
|
||||||
|
template reject(x) =
|
||||||
|
static: assert(not compiles(x))
|
||||||
|
|
||||||
|
var
|
||||||
|
si: seq[int]
|
||||||
|
ss: seq[string]
|
||||||
|
|
||||||
|
proc foo(T: typedesc[seq], s: T) =
|
||||||
|
discard
|
||||||
|
|
||||||
|
accept:
|
||||||
|
foo seq[int], si
|
||||||
|
|
||||||
|
reject:
|
||||||
|
foo seq[string], si
|
||||||
|
|
||||||
|
reject:
|
||||||
|
foo seq[int], ss
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ type
|
||||||
Base = object of RootObj
|
Base = object of RootObj
|
||||||
Child = object of Base
|
Child = object of Base
|
||||||
|
|
||||||
proc pr(T: typedesc[Base]) = echo "proc " & T.name
|
proc pr(T: type[Base]) = echo "proc " & T.name
|
||||||
method me(T: typedesc[Base]) = echo "method " & T.name
|
method me(T: type[Base]) = echo "method " & T.name
|
||||||
iterator it(T: typedesc[Base]): auto = yield "yield " & T.name
|
iterator it(T: type[Base]): auto = yield "yield " & T.name
|
||||||
|
|
||||||
Base.pr
|
Base.pr
|
||||||
Child.pr
|
Child.pr
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,16 @@ output: "8\n8\n4"
|
||||||
import
|
import
|
||||||
macros, typetraits
|
macros, typetraits
|
||||||
|
|
||||||
template selectType(x: int): typeDesc =
|
template selectType(x: int): type =
|
||||||
when x < 10:
|
when x < 10:
|
||||||
int
|
int
|
||||||
else:
|
else:
|
||||||
string
|
string
|
||||||
|
|
||||||
template simpleTypeTempl: typeDesc =
|
template simpleTypeTempl: type =
|
||||||
string
|
string
|
||||||
|
|
||||||
macro typeFromMacro: typedesc = string
|
macro typeFromMacro: type = string
|
||||||
|
|
||||||
# The tests below check that the result variable of the
|
# The tests below check that the result variable of the
|
||||||
# selected type matches the literal types in the code:
|
# selected type matches the literal types in the code:
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,42 @@
|
||||||
discard """
|
|
||||||
action: run
|
|
||||||
"""
|
|
||||||
|
|
||||||
type
|
type
|
||||||
R = ref
|
R = ref
|
||||||
V = var
|
V = var
|
||||||
D = distinct
|
D = distinct
|
||||||
P = ptr
|
P = ptr
|
||||||
|
T = type
|
||||||
|
S = static
|
||||||
|
OBJ = object
|
||||||
|
TPL = tuple
|
||||||
|
SEQ = seq
|
||||||
|
|
||||||
|
var i: int
|
||||||
var x: ref int
|
var x: ref int
|
||||||
var y: distinct int
|
var y: distinct int
|
||||||
var z: ptr int
|
var z: ptr int
|
||||||
|
const C = @[1, 2, 3]
|
||||||
|
|
||||||
|
static:
|
||||||
|
assert x is ref
|
||||||
|
assert y is distinct
|
||||||
|
assert z is ptr
|
||||||
|
assert C is static
|
||||||
|
assert C[1] is static[int]
|
||||||
|
assert C[0] is static[SomeInteger]
|
||||||
|
assert C isnot static[string]
|
||||||
|
assert C is SEQ|OBJ
|
||||||
|
assert C isnot OBJ|TPL
|
||||||
|
assert int is int
|
||||||
|
assert int is T
|
||||||
|
assert int is SomeInteger
|
||||||
|
assert seq[int] is type
|
||||||
|
assert seq[int] is type[seq]
|
||||||
|
assert seq[int] isnot type[seq[float]]
|
||||||
|
assert i isnot type[int]
|
||||||
|
assert type(i) is type[int]
|
||||||
|
assert x isnot T
|
||||||
|
assert y isnot S
|
||||||
|
assert z isnot enum
|
||||||
|
assert x isnot object
|
||||||
|
assert y isnot tuple
|
||||||
|
assert z isnot seq
|
||||||
|
|
||||||
doAssert x is ref
|
|
||||||
doAssert y is distinct
|
|
||||||
doAssert z is ptr
|
|
||||||
|
|
@ -1,25 +1,91 @@
|
||||||
discard """
|
discard """
|
||||||
nimout: '''
|
nimout: '''
|
||||||
staticAlialProc instantiated with 4
|
staticAlialProc instantiated with 358
|
||||||
staticAlialProc instantiated with 6
|
staticAlialProc instantiated with 368
|
||||||
|
'''
|
||||||
|
output: '''
|
||||||
|
16
|
||||||
|
16
|
||||||
|
b is 2 times a
|
||||||
|
17
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
|
template ok(x) = assert(x)
|
||||||
|
template no(x) = assert(not x)
|
||||||
|
|
||||||
|
template accept(x) =
|
||||||
|
static: assert(compiles(x))
|
||||||
|
|
||||||
|
template reject(x) =
|
||||||
|
static: assert(not compiles(x))
|
||||||
|
|
||||||
proc plus(a, b: int): int = a + b
|
proc plus(a, b: int): int = a + b
|
||||||
|
|
||||||
|
template isStatic(x: static): bool = true
|
||||||
|
template isStatic(x: auto): bool = false
|
||||||
|
|
||||||
|
var v = 1
|
||||||
|
|
||||||
when true:
|
when true:
|
||||||
|
# test that `isStatic` works as expected
|
||||||
|
const C = 2
|
||||||
|
|
||||||
|
static:
|
||||||
|
ok C.isStatic
|
||||||
|
ok isStatic(plus(1, 2))
|
||||||
|
ok plus(C, 2).isStatic
|
||||||
|
|
||||||
|
no isStatic(v)
|
||||||
|
no plus(1, v).isStatic
|
||||||
|
|
||||||
|
when true:
|
||||||
|
# test that proc instantiation works as expected
|
||||||
type
|
type
|
||||||
StaticTypeAlias = static[int]
|
StaticTypeAlias = static[int]
|
||||||
|
|
||||||
proc staticAliasProc(s: StaticTypeAlias) =
|
proc staticAliasProc(a: StaticTypeAlias,
|
||||||
static: echo "staticAlialProc instantiated with ", s + 1
|
b: static[int],
|
||||||
echo s
|
c: static int) =
|
||||||
|
static:
|
||||||
|
assert a.isStatic and b.isStatic and c.isStatic
|
||||||
|
assert isStatic(a + plus(b, c))
|
||||||
|
echo "staticAlialProc instantiated with ", a, b, c
|
||||||
|
|
||||||
staticAliasProc 1+2
|
when b mod a == 0:
|
||||||
staticAliasProc 3
|
echo "b is ", b div a, " times a"
|
||||||
staticAliasProc 5
|
|
||||||
|
echo a + b + c
|
||||||
|
|
||||||
|
staticAliasProc 1+2, 5, 8
|
||||||
|
staticAliasProc 3, 2+3, 9-1
|
||||||
|
staticAliasProc 3, 3+3, 4+4
|
||||||
|
|
||||||
|
when true:
|
||||||
|
# test static coercions. normal cases that should work:
|
||||||
|
accept:
|
||||||
|
var s1 = static[int] plus(1, 2)
|
||||||
|
var s2 = static(plus(1,2))
|
||||||
|
var s3 = static plus(1,2)
|
||||||
|
var s4 = static[SomeInteger](1 + 2)
|
||||||
|
|
||||||
|
# the sub-script operator can be used only with types:
|
||||||
|
reject:
|
||||||
|
var just_static3 = static[plus(1,2)]
|
||||||
|
|
||||||
|
# static coercion takes into account the type:
|
||||||
|
reject:
|
||||||
|
var x = static[string](plus(1, 2))
|
||||||
|
reject:
|
||||||
|
var x = static[string] plus(1, 2)
|
||||||
|
reject:
|
||||||
|
var x = static[SomeFloat] plus(3, 4)
|
||||||
|
|
||||||
|
# you cannot coerce a run-time variable
|
||||||
|
reject:
|
||||||
|
var x = static(v)
|
||||||
|
|
||||||
when true:
|
when true:
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ false
|
||||||
false
|
false
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
no'''
|
yes'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc IsVoid[T](): string =
|
proc IsVoid[T](): string =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue