diff options
-rw-r--r-- | compiler/semtypes.nim | 12 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 6 | ||||
-rw-r--r-- | doc/manual.txt | 33 | ||||
-rw-r--r-- | lib/system.nim | 23 | ||||
-rw-r--r-- | tests/compile/tbindtypedesc.nim | 87 |
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)) + |