summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2013-08-23 15:43:27 +0300
committerZahary Karadjov <zahary@gmail.com>2013-08-23 15:43:27 +0300
commit56d75bd23ba8e302277614aa5e0ec1ae002fb56d (patch)
tree63bf7cb90583d370cde738a1a910796304dcd240
parent8682ed9bd0bd8230e779e45cc65c2bfd4661a966 (diff)
downloadNim-56d75bd23ba8e302277614aa5e0ec1ae002fb56d.tar.gz
implemented and documented the new typedesc binding rules
-rw-r--r--compiler/semtypes.nim12
-rw-r--r--compiler/sigmatch.nim6
-rw-r--r--doc/manual.txt33
-rw-r--r--lib/system.nim23
-rw-r--r--tests/compile/tbindtypedesc.nim87
5 files changed, 143 insertions, 18 deletions
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index e5d9058b4..b02fa7c31 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -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)
     
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 4483b1f8b..5766aa164 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -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:
diff --git a/doc/manual.txt b/doc/manual.txt
index d2fc7e5c9..0a2009961 100644
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -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
diff --git a/lib/system.nim b/lib/system.nim
index 08e4c367b..749124ac9 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -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)
diff --git a/tests/compile/tbindtypedesc.nim b/tests/compile/tbindtypedesc.nim
new file mode 100644
index 000000000..dd4ef854c
--- /dev/null
+++ b/tests/compile/tbindtypedesc.nim
@@ -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))
+