implemented and documented the new typedesc binding rules

This commit is contained in:
Zahary Karadjov 2013-08-23 15:43:27 +03:00
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: 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)

View file

@ -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:

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_. 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

View file

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

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