mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-06 21:17:48 +00:00
implemented and documented the new typedesc binding rules
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
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))
|
||||
|
||||
Reference in New Issue
Block a user