implemented and documented the new typedesc binding rules
This commit is contained in:
parent
8682ed9bd0
commit
56d75bd23b
5 changed files with 143 additions and 18 deletions
|
|
@ -589,6 +589,8 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
||||||
else:
|
else:
|
||||||
if sfGenSym notin param.flags: addDecl(c, param)
|
if sfGenSym notin param.flags: addDecl(c, param)
|
||||||
|
|
||||||
|
let typedescId = getIdent"typedesc"
|
||||||
|
|
||||||
proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
paramType: PType, paramName: string,
|
paramType: PType, paramName: string,
|
||||||
info: TLineInfo, anon = false): PType =
|
info: TLineInfo, anon = false): PType =
|
||||||
|
|
@ -636,6 +638,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
result = addImplicitGeneric(c.newTypeWithSons(tyExpr, paramType.sons))
|
result = addImplicitGeneric(c.newTypeWithSons(tyExpr, paramType.sons))
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
if tfUnresolved notin paramType.flags:
|
if tfUnresolved notin paramType.flags:
|
||||||
|
# naked typedescs are not bindOnce types
|
||||||
|
if paramType.sonsLen == 0 and paramTypId != nil and
|
||||||
|
paramTypId.id == typedescId.id: paramTypId = nil
|
||||||
result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons))
|
result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons))
|
||||||
of tyDistinct:
|
of tyDistinct:
|
||||||
if paramType.sonsLen == 1:
|
if paramType.sonsLen == 1:
|
||||||
|
|
@ -1078,8 +1083,9 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||||
if constraint.kind != nkEmpty:
|
if constraint.kind != nkEmpty:
|
||||||
typ = semTypeNode(c, constraint, nil)
|
typ = semTypeNode(c, constraint, nil)
|
||||||
if typ.kind != tyExpr or typ.len == 0:
|
if typ.kind != tyExpr or typ.len == 0:
|
||||||
if typ.len == 0 and typ.kind == tyTypeDesc:
|
if typ.kind == tyTypeDesc:
|
||||||
typ = newTypeS(tyGenericParam, c)
|
if typ.len == 0:
|
||||||
|
typ = newTypeS(tyTypeDesc, c)
|
||||||
else:
|
else:
|
||||||
typ = semGenericConstraints(c, typ)
|
typ = semGenericConstraints(c, typ)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -655,7 +655,7 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
result = typeRel(c, x, a) # check if it fits
|
result = typeRel(c, x, a) # check if it fits
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
var prev = PType(idTableGet(c.bindings, f))
|
var prev = PType(idTableGet(c.bindings, f))
|
||||||
if prev == nil or true:
|
if prev == nil:
|
||||||
if a.kind == tyTypeDesc:
|
if a.kind == tyTypeDesc:
|
||||||
if f.sonsLen == 0:
|
if f.sonsLen == 0:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
|
|
@ -667,7 +667,9 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
result = isNone
|
result = isNone
|
||||||
else:
|
else:
|
||||||
InternalAssert prev.sonsLen == 1
|
InternalAssert prev.sonsLen == 1
|
||||||
result = typeRel(c, prev.sons[0], a)
|
let toMatch = if tfUnresolved in f.flags: a
|
||||||
|
else: a.sons[0]
|
||||||
|
result = typeRel(c, prev.sons[0], toMatch)
|
||||||
of tyExpr, tyStmt:
|
of tyExpr, tyStmt:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
of tyProxy:
|
of tyProxy:
|
||||||
|
|
|
||||||
|
|
@ -1451,7 +1451,7 @@ But it seems all this boilerplate code needs to be repeated for the ``TEuro``
|
||||||
currency. This can be solved with templates_.
|
currency. This can be solved with templates_.
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
template Additive(typ: typeDesc): stmt =
|
template Additive(typ: typedesc): stmt =
|
||||||
proc `+` *(x, y: typ): typ {.borrow.}
|
proc `+` *(x, y: typ): typ {.borrow.}
|
||||||
proc `-` *(x, y: typ): typ {.borrow.}
|
proc `-` *(x, y: typ): typ {.borrow.}
|
||||||
|
|
||||||
|
|
@ -1459,13 +1459,13 @@ currency. This can be solved with templates_.
|
||||||
proc `+` *(x: typ): typ {.borrow.}
|
proc `+` *(x: typ): typ {.borrow.}
|
||||||
proc `-` *(x: typ): typ {.borrow.}
|
proc `-` *(x: typ): typ {.borrow.}
|
||||||
|
|
||||||
template Multiplicative(typ, base: typeDesc): stmt =
|
template Multiplicative(typ, base: typedesc): stmt =
|
||||||
proc `*` *(x: typ, y: base): typ {.borrow.}
|
proc `*` *(x: typ, y: base): typ {.borrow.}
|
||||||
proc `*` *(x: base, y: typ): typ {.borrow.}
|
proc `*` *(x: base, y: typ): typ {.borrow.}
|
||||||
proc `div` *(x: typ, y: base): typ {.borrow.}
|
proc `div` *(x: typ, y: base): typ {.borrow.}
|
||||||
proc `mod` *(x: typ, y: base): typ {.borrow.}
|
proc `mod` *(x: typ, y: base): typ {.borrow.}
|
||||||
|
|
||||||
template Comparable(typ: typeDesc): stmt =
|
template Comparable(typ: typedesc): stmt =
|
||||||
proc `<` * (x, y: typ): bool {.borrow.}
|
proc `<` * (x, y: typ): bool {.borrow.}
|
||||||
proc `<=` * (x, y: typ): bool {.borrow.}
|
proc `<=` * (x, y: typ): bool {.borrow.}
|
||||||
proc `==` * (x, y: typ): bool {.borrow.}
|
proc `==` * (x, y: typ): bool {.borrow.}
|
||||||
|
|
@ -3323,10 +3323,10 @@ The template body does not open a new scope. To open a new scope a ``block``
|
||||||
statement can be used:
|
statement can be used:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
template declareInScope(x: expr, t: typeDesc): stmt {.immediate.} =
|
template declareInScope(x: expr, t: typedesc): stmt {.immediate.} =
|
||||||
var x: t
|
var x: t
|
||||||
|
|
||||||
template declareInNewScope(x: expr, t: typeDesc): stmt {.immediate.} =
|
template declareInNewScope(x: expr, t: typedesc): stmt {.immediate.} =
|
||||||
# open a new scope:
|
# open a new scope:
|
||||||
block:
|
block:
|
||||||
var x: t
|
var x: t
|
||||||
|
|
@ -3419,7 +3419,7 @@ In templates identifiers can be constructed with the backticks notation:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
|
|
||||||
template typedef(name: expr, typ: typeDesc) {.immediate.} =
|
template typedef(name: expr, typ: typedesc) {.immediate.} =
|
||||||
type
|
type
|
||||||
`T name`* {.inject.} = typ
|
`T name`* {.inject.} = typ
|
||||||
`P name`* {.inject.} = ref `T name`
|
`P name`* {.inject.} = ref `T name`
|
||||||
|
|
@ -3480,7 +3480,7 @@ template cannot be accessed in the instantiation context:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
|
|
||||||
template newException*(exceptn: typeDesc, message: string): expr =
|
template newException*(exceptn: typedesc, message: string): expr =
|
||||||
var
|
var
|
||||||
e: ref exceptn # e is implicitly gensym'ed here
|
e: ref exceptn # e is implicitly gensym'ed here
|
||||||
new(e)
|
new(e)
|
||||||
|
|
@ -3728,6 +3728,25 @@ instantiation type using the param name:
|
||||||
var n = TNode.new
|
var n = TNode.new
|
||||||
var tree = new(TBinaryTree[int])
|
var tree = new(TBinaryTree[int])
|
||||||
|
|
||||||
|
When multiple typedesc params are present, they act like a distinct type class
|
||||||
|
(i.e. they will bind freely to different types). To force a bind-once behavior
|
||||||
|
one can use a named alias or an explicit `typedesc` generic param:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
|
||||||
|
# `type1` and `type2` are aliases for typedesc available from system.nim
|
||||||
|
proc acceptOnlyTypePairs(A, B: type1; C, D: type2)
|
||||||
|
proc acceptOnlyTypePairs[T: typedesc, U: typedesc](A, B: T; C, D: U)
|
||||||
|
|
||||||
|
Once bound, typedesc params can appear in the rest of the proc signature:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
|
||||||
|
template declareVariableWithType(T: typedesc, value: T) =
|
||||||
|
var x: T = value
|
||||||
|
|
||||||
|
declareVariableWithType int, 42
|
||||||
|
|
||||||
When used with macros and .compileTime. procs on the other hand, the compiler
|
When used with macros and .compileTime. procs on the other hand, the compiler
|
||||||
does not need to instantiate the code multiple times, because types then can be
|
does not need to instantiate the code multiple times, because types then can be
|
||||||
manipulated using the unified internal symbol representation. In such context
|
manipulated using the unified internal symbol representation. In such context
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ type
|
||||||
`nil` {.magic: "Nil".}
|
`nil` {.magic: "Nil".}
|
||||||
expr* {.magic: Expr.} ## meta type to denote an expression (for templates)
|
expr* {.magic: Expr.} ## meta type to denote an expression (for templates)
|
||||||
stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates)
|
stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates)
|
||||||
typeDesc* {.magic: TypeDesc.} ## meta type to denote a type description
|
typedesc* {.magic: TypeDesc.} ## meta type to denote a type description
|
||||||
void* {.magic: "VoidType".} ## meta type to denote the absense of any type
|
void* {.magic: "VoidType".} ## meta type to denote the absense of any type
|
||||||
auto* = expr
|
auto* = expr
|
||||||
any* = distinct auto
|
any* = distinct auto
|
||||||
|
|
@ -76,6 +76,17 @@ type
|
||||||
TNumber* = TInteger|TReal
|
TNumber* = TInteger|TReal
|
||||||
## type class matching all number types
|
## type class matching all number types
|
||||||
|
|
||||||
|
type
|
||||||
|
## helper types for writing implicitly generic procs
|
||||||
|
T1* = expr
|
||||||
|
T2* = expr
|
||||||
|
T3* = expr
|
||||||
|
T4* = expr
|
||||||
|
T5* = expr
|
||||||
|
type1* = typedesc
|
||||||
|
type2* = typedesc
|
||||||
|
type3* = typedesc
|
||||||
|
|
||||||
proc defined*(x: expr): bool {.magic: "Defined", noSideEffect.}
|
proc defined*(x: expr): bool {.magic: "Defined", noSideEffect.}
|
||||||
## Special compile-time procedure that checks whether `x` is
|
## Special compile-time procedure that checks whether `x` is
|
||||||
## defined. `x` has to be an identifier or a qualified identifier.
|
## defined. `x` has to be an identifier or a qualified identifier.
|
||||||
|
|
@ -1473,7 +1484,7 @@ when not defined(NimrodVM):
|
||||||
proc seqToPtr[T](x: seq[T]): pointer {.noStackFrame, nosideeffect.} =
|
proc seqToPtr[T](x: seq[T]): pointer {.noStackFrame, nosideeffect.} =
|
||||||
asm """return `x`"""
|
asm """return `x`"""
|
||||||
|
|
||||||
proc `==` *[T: typeDesc](x, y: seq[T]): bool {.noSideEffect.} =
|
proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
|
||||||
## Generic equals operator for sequences: relies on a equals operator for
|
## Generic equals operator for sequences: relies on a equals operator for
|
||||||
## the element type `T`.
|
## the element type `T`.
|
||||||
if seqToPtr(x) == seqToPtr(y):
|
if seqToPtr(x) == seqToPtr(y):
|
||||||
|
|
@ -1485,7 +1496,7 @@ when not defined(NimrodVM):
|
||||||
if x[i] != y[i]: return false
|
if x[i] != y[i]: return false
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc find*[T, S: typeDesc](a: T, item: S): int {.inline.}=
|
proc find*[T, S](a: T, item: S): int {.inline.}=
|
||||||
## Returns the first index of `item` in `a` or -1 if not found. This requires
|
## Returns the first index of `item` in `a` or -1 if not found. This requires
|
||||||
## appropriate `items` and `==` operations to work.
|
## appropriate `items` and `==` operations to work.
|
||||||
for i in items(a):
|
for i in items(a):
|
||||||
|
|
@ -1788,7 +1799,7 @@ proc debugEcho*[T](x: varargs[T, `$`]) {.magic: "Echo", noSideEffect,
|
||||||
## to be free of side effects, so that it can be used for debugging routines
|
## to be free of side effects, so that it can be used for debugging routines
|
||||||
## marked as ``noSideEffect``.
|
## marked as ``noSideEffect``.
|
||||||
|
|
||||||
template newException*(exceptn: typeDesc, message: string): expr =
|
template newException*(exceptn: typedesc, message: string): expr =
|
||||||
## creates an exception object of type ``exceptn`` and sets its ``msg`` field
|
## creates an exception object of type ``exceptn`` and sets its ``msg`` field
|
||||||
## to `message`. Returns the new exception object.
|
## to `message`. Returns the new exception object.
|
||||||
var
|
var
|
||||||
|
|
@ -1801,7 +1812,7 @@ when hostOS == "standalone":
|
||||||
include panicoverride
|
include panicoverride
|
||||||
|
|
||||||
when not defined(sysFatal):
|
when not defined(sysFatal):
|
||||||
template sysFatal(exceptn: typeDesc, message: string) =
|
template sysFatal(exceptn: typedesc, message: string) =
|
||||||
when hostOS == "standalone":
|
when hostOS == "standalone":
|
||||||
panic(message)
|
panic(message)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1810,7 +1821,7 @@ when not defined(sysFatal):
|
||||||
e.msg = message
|
e.msg = message
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
template sysFatal(exceptn: typeDesc, message, arg: string) =
|
template sysFatal(exceptn: typedesc, message, arg: string) =
|
||||||
when hostOS == "standalone":
|
when hostOS == "standalone":
|
||||||
rawoutput(message)
|
rawoutput(message)
|
||||||
panic(arg)
|
panic(arg)
|
||||||
|
|
|
||||||
87
tests/compile/tbindtypedesc.nim
Normal file
87
tests/compile/tbindtypedesc.nim
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
discard """
|
||||||
|
msg: '''
|
||||||
|
int
|
||||||
|
float
|
||||||
|
TFoo
|
||||||
|
TFoo
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import typetraits
|
||||||
|
|
||||||
|
type
|
||||||
|
TFoo = object
|
||||||
|
x, y: int
|
||||||
|
|
||||||
|
TBar = tuple
|
||||||
|
x, y: int
|
||||||
|
|
||||||
|
template good(e: expr) =
|
||||||
|
static: assert(compiles(e))
|
||||||
|
|
||||||
|
template bad(e: expr) =
|
||||||
|
static: assert(not compiles(e))
|
||||||
|
|
||||||
|
proc genericParamRepeated[T: typedesc](a: T, b: T) =
|
||||||
|
static:
|
||||||
|
echo a.name
|
||||||
|
echo b.name
|
||||||
|
|
||||||
|
good(genericParamRepeated(int, int))
|
||||||
|
good(genericParamRepeated(float, float))
|
||||||
|
|
||||||
|
bad(genericParamRepeated(string, int))
|
||||||
|
bad(genericParamRepeated(int, float))
|
||||||
|
|
||||||
|
proc genericParamOnce[T: typedesc](a, b: T) =
|
||||||
|
static:
|
||||||
|
echo a.name
|
||||||
|
echo b.name
|
||||||
|
|
||||||
|
good(genericParamOnce(int, int))
|
||||||
|
good(genericParamOnce(TFoo, TFoo))
|
||||||
|
|
||||||
|
bad(genericParamOnce(string, int))
|
||||||
|
bad(genericParamOnce(TFoo, float))
|
||||||
|
|
||||||
|
proc typePairs(A, B: type1; C, D: type2) = nil
|
||||||
|
|
||||||
|
good(typePairs(int, int, TFoo, TFOO))
|
||||||
|
good(typePairs(TBAR, TBar, TBAR, TBAR))
|
||||||
|
good(typePairs(int, int, string, string))
|
||||||
|
|
||||||
|
bad(typePairs(TBAR, TBar, TBar, TFoo))
|
||||||
|
bad(typePairs(string, int, TBAR, TBAR))
|
||||||
|
|
||||||
|
proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = nil
|
||||||
|
|
||||||
|
good(typePairs2(int, int, TFoo, TFOO))
|
||||||
|
good(typePairs2(TBAR, TBar, TBAR, TBAR))
|
||||||
|
good(typePairs2(int, int, string, string))
|
||||||
|
|
||||||
|
bad(typePairs2(TBAR, TBar, TBar, TFoo))
|
||||||
|
bad(typePairs2(string, int, TBAR, TBAR))
|
||||||
|
|
||||||
|
proc dontBind(a: typedesc, b: typedesc) =
|
||||||
|
static:
|
||||||
|
echo a.name
|
||||||
|
echo b.name
|
||||||
|
|
||||||
|
good(dontBind(int, float))
|
||||||
|
good(dontBind(TFoo, TFoo))
|
||||||
|
|
||||||
|
proc dontBind2(a, b: typedesc) = nil
|
||||||
|
|
||||||
|
good(dontBind2(int, float))
|
||||||
|
good(dontBind2(TBar, int))
|
||||||
|
|
||||||
|
proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = nil
|
||||||
|
|
||||||
|
good(bindArg(int, string, 10, 20, "test", "nest"))
|
||||||
|
good(bindArg(int, int, 10, 20, 30, 40))
|
||||||
|
|
||||||
|
bad(bindArg(int, string, 10, "test", "test", "nest"))
|
||||||
|
bad(bindArg(int, int, 10, 20, 30, "test"))
|
||||||
|
bad(bindArg(int, string, 10.0, 20, "test", "nest"))
|
||||||
|
bad(bindArg(int, string, "test", "nest", 10, 20))
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue