implemented and documented the new typedesc binding rules

This commit is contained in:
Zahary Karadjov
2013-08-23 15:43:27 +03:00
parent 8682ed9bd0
commit 56d75bd23b
5 changed files with 143 additions and 18 deletions

View File

@@ -589,6 +589,8 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
else:
if sfGenSym notin param.flags: addDecl(c, param)
let typedescId = getIdent"typedesc"
proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramType: PType, paramName: string,
info: TLineInfo, anon = false): PType =
@@ -636,6 +638,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result = addImplicitGeneric(c.newTypeWithSons(tyExpr, paramType.sons))
of tyTypeDesc:
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))
of tyDistinct:
if paramType.sonsLen == 1:
@@ -762,7 +767,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
r.flags.incl tfRetType
result.sons[0] = skipIntLit(r)
res.typ = result.sons[0]
proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
checkMinSonsLen(n, 1)
var length = sonsLen(n)
@@ -1078,8 +1083,9 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
if constraint.kind != nkEmpty:
typ = semTypeNode(c, constraint, nil)
if typ.kind != tyExpr or typ.len == 0:
if typ.len == 0 and typ.kind == tyTypeDesc:
typ = newTypeS(tyGenericParam, c)
if typ.kind == tyTypeDesc:
if typ.len == 0:
typ = newTypeS(tyTypeDesc, c)
else:
typ = semGenericConstraints(c, typ)

View File

@@ -655,7 +655,7 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = typeRel(c, x, a) # check if it fits
of tyTypeDesc:
var prev = PType(idTableGet(c.bindings, f))
if prev == nil or true:
if prev == nil:
if a.kind == tyTypeDesc:
if f.sonsLen == 0:
result = isGeneric
@@ -667,7 +667,9 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = isNone
else:
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:
result = isGeneric
of tyProxy:

View File

@@ -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_.
.. 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.}
@@ -1459,13 +1459,13 @@ currency. This can be solved with templates_.
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: base, y: typ): typ {.borrow.}
proc `div` *(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.}
@@ -3323,10 +3323,10 @@ The template body does not open a new scope. To open a new scope a ``block``
statement can be used:
.. code-block:: nimrod
template declareInScope(x: expr, t: typeDesc): stmt {.immediate.} =
template declareInScope(x: expr, t: typedesc): stmt {.immediate.} =
var x: t
template declareInNewScope(x: expr, t: typeDesc): stmt {.immediate.} =
template declareInNewScope(x: expr, t: typedesc): stmt {.immediate.} =
# open a new scope:
block:
var x: t
@@ -3419,7 +3419,7 @@ In templates identifiers can be constructed with the backticks notation:
.. code-block:: nimrod
template typedef(name: expr, typ: typeDesc) {.immediate.} =
template typedef(name: expr, typ: typedesc) {.immediate.} =
type
`T name`* {.inject.} = typ
`P name`* {.inject.} = ref `T name`
@@ -3480,7 +3480,7 @@ template cannot be accessed in the instantiation context:
.. code-block:: nimrod
template newException*(exceptn: typeDesc, message: string): expr =
template newException*(exceptn: typedesc, message: string): expr =
var
e: ref exceptn # e is implicitly gensym'ed here
new(e)
@@ -3728,6 +3728,25 @@ instantiation type using the param name:
var n = TNode.new
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
does not need to instantiate the code multiple times, because types then can be
manipulated using the unified internal symbol representation. In such context

View File

@@ -52,7 +52,7 @@ type
`nil` {.magic: "Nil".}
expr* {.magic: Expr.} ## meta type to denote an expression (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
auto* = expr
any* = distinct auto
@@ -76,6 +76,17 @@ type
TNumber* = TInteger|TReal
## 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.}
## Special compile-time procedure that checks whether `x` is
## 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.} =
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
## the element type `T`.
if seqToPtr(x) == seqToPtr(y):
@@ -1485,7 +1496,7 @@ when not defined(NimrodVM):
if x[i] != y[i]: return false
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
## appropriate `items` and `==` operations to work.
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
## 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
## to `message`. Returns the new exception object.
var
@@ -1801,7 +1812,7 @@ when hostOS == "standalone":
include panicoverride
when not defined(sysFatal):
template sysFatal(exceptn: typeDesc, message: string) =
template sysFatal(exceptn: typedesc, message: string) =
when hostOS == "standalone":
panic(message)
else:
@@ -1810,7 +1821,7 @@ when not defined(sysFatal):
e.msg = message
raise e
template sysFatal(exceptn: typeDesc, message, arg: string) =
template sysFatal(exceptn: typedesc, message, arg: string) =
when hostOS == "standalone":
rawoutput(message)
panic(arg)

View 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))