mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-03 11:42:33 +00:00
Merge branch 'master' of github.com:Araq/Nimrod
This commit is contained in:
@@ -626,6 +626,7 @@ type
|
||||
case kind*: TSymKind
|
||||
of skType:
|
||||
typeInstCache*: seq[PType]
|
||||
typScope*: PScope
|
||||
of routineKinds:
|
||||
procInstCache*: seq[PInstantiation]
|
||||
scope*: PScope # the scope where the proc was defined
|
||||
@@ -799,9 +800,9 @@ const
|
||||
# imported via 'importc: "fullname"' and no format string.
|
||||
|
||||
# creator procs:
|
||||
proc NewSym*(symKind: TSymKind, Name: PIdent, owner: PSym,
|
||||
proc newSym*(symKind: TSymKind, Name: PIdent, owner: PSym,
|
||||
info: TLineInfo): PSym
|
||||
proc NewType*(kind: TTypeKind, owner: PSym): PType
|
||||
proc newType*(kind: TTypeKind, owner: PSym): PType
|
||||
proc newNode*(kind: TNodeKind): PNode
|
||||
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode
|
||||
proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode
|
||||
@@ -1111,7 +1112,7 @@ proc copySym(s: PSym, keepId: bool = false): PSym =
|
||||
result.loc = s.loc
|
||||
result.annex = s.annex # BUGFIX
|
||||
|
||||
proc NewSym(symKind: TSymKind, Name: PIdent, owner: PSym,
|
||||
proc newSym(symKind: TSymKind, Name: PIdent, owner: PSym,
|
||||
info: TLineInfo): PSym =
|
||||
# generates a symbol and initializes the hash field too
|
||||
new(result)
|
||||
|
||||
@@ -904,18 +904,15 @@ proc evalParseStmt(c: PEvalContext, n: PNode): PNode =
|
||||
code.info.line.int)
|
||||
#result.typ = newType(tyStmt, c.module)
|
||||
|
||||
proc evalTypeTrait*(n: PNode, context: PSym): PNode =
|
||||
## XXX: This should be pretty much guaranteed to be true
|
||||
# by the type traits procs' signatures, but until the
|
||||
# code is more mature it doesn't hurt to be extra safe
|
||||
internalAssert n.sons.len >= 2 and n.sons[1].kind == nkSym
|
||||
proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode =
|
||||
InternalAssert operand.kind == nkSym
|
||||
|
||||
let typ = n.sons[1].sym.typ.skipTypes({tyTypeDesc})
|
||||
case n.sons[0].sym.name.s.normalize
|
||||
let typ = operand.sym.typ.skipTypes({tyTypeDesc})
|
||||
case trait.sym.name.s.normalize
|
||||
of "name":
|
||||
result = newStrNode(nkStrLit, typ.typeToString(preferExported))
|
||||
result = newStrNode(nkStrLit, typ.typeToString(preferName))
|
||||
result.typ = newType(tyString, context)
|
||||
result.info = n.info
|
||||
result.info = trait.info
|
||||
else:
|
||||
internalAssert false
|
||||
|
||||
@@ -1037,8 +1034,8 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
||||
of mParseStmtToAst: result = evalParseStmt(c, n)
|
||||
of mExpandToAst: result = evalExpandToAst(c, n)
|
||||
of mTypeTrait:
|
||||
n.sons[1] = evalAux(c, n.sons[1], {})
|
||||
result = evalTypeTrait(n, c.module)
|
||||
let operand = evalAux(c, n.sons[1], {})
|
||||
result = evalTypeTrait(n[0], operand, c.module)
|
||||
of mIs:
|
||||
n.sons[1] = evalAux(c, n.sons[1], {})
|
||||
result = evalIsOp(n)
|
||||
|
||||
@@ -156,6 +156,8 @@ var
|
||||
|
||||
const oKeepVariableNames* = true
|
||||
|
||||
const oUseLateInstantiation* = false
|
||||
|
||||
proc mainCommandArg*: string =
|
||||
## This is intended for commands like check or parse
|
||||
## which will work on the main project file unless
|
||||
|
||||
@@ -225,14 +225,14 @@ proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) =
|
||||
dest.owner = getCurrOwner()
|
||||
dest.size = - 1
|
||||
|
||||
proc makeRangeType*(c: PContext, first, last: biggestInt,
|
||||
info: TLineInfo): PType =
|
||||
proc makeRangeType*(c: PContext; first, last: biggestInt;
|
||||
info: TLineInfo; intType = getSysType(tyInt)): PType =
|
||||
var n = newNodeI(nkRange, info)
|
||||
addSon(n, newIntNode(nkIntLit, first))
|
||||
addSon(n, newIntNode(nkIntLit, last))
|
||||
addSon(n, newIntTypeNode(nkIntLit, first, intType))
|
||||
addSon(n, newIntTypeNode(nkIntLit, last, intType))
|
||||
result = newTypeS(tyRange, c)
|
||||
result.n = n
|
||||
rawAddSon(result, getSysType(tyInt)) # basetype of range
|
||||
addSonSkipIntLit(result, intType) # basetype of range
|
||||
|
||||
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
|
||||
if s.kind in {skProc, skConverter, skMethod, skIterator}:
|
||||
|
||||
@@ -113,7 +113,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||
of skGenericParam:
|
||||
if s.typ.kind == tyExpr:
|
||||
result = newSymNode(s, n.info)
|
||||
result.typ = s.typ.lastSon
|
||||
result.typ = s.typ
|
||||
elif s.ast != nil:
|
||||
result = semExpr(c, s.ast)
|
||||
else:
|
||||
|
||||
@@ -561,9 +561,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
var s = n.sym
|
||||
if s.kind == skEnumField:
|
||||
case s.kind
|
||||
of skEnumField:
|
||||
result = newIntNodeT(s.position, n)
|
||||
elif s.kind == skConst:
|
||||
of skConst:
|
||||
case s.magic
|
||||
of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n)
|
||||
of mCompileDate: result = newStrNodeT(times.getDateStr(), n)
|
||||
@@ -581,10 +582,17 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
of mNegInf: result = newFloatNodeT(NegInf, n)
|
||||
else:
|
||||
if sfFakeConst notin s.flags: result = copyTree(s.ast)
|
||||
elif s.kind in {skProc, skMethod}: # BUGFIX
|
||||
of {skProc, skMethod}:
|
||||
result = n
|
||||
elif s.kind in {skType, skGenericParam}:
|
||||
of skType:
|
||||
result = newSymNodeTypeDesc(s, n.info)
|
||||
of skGenericParam:
|
||||
if s.typ.kind == tyExpr:
|
||||
result = s.typ.n
|
||||
result.typ = s.typ.sons[0]
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, n.info)
|
||||
else: nil
|
||||
of nkCharLit..nkNilLit:
|
||||
result = copyNode(n)
|
||||
of nkIfExpr:
|
||||
|
||||
@@ -130,13 +130,50 @@ proc sideEffectsCheck(c: PContext, s: PSym) =
|
||||
s.ast.sons[genericParamsPos].kind == nkEmpty:
|
||||
c.threadEntries.add(s)
|
||||
|
||||
proc lateInstantiateGeneric(c: PContext, invocation: PType, info: TLineInfo): PType =
|
||||
InternalAssert invocation.kind == tyGenericInvokation
|
||||
|
||||
let cacheHit = searchInstTypes(invocation)
|
||||
if cacheHit != nil:
|
||||
result = cacheHit
|
||||
else:
|
||||
let s = invocation.sons[0].sym
|
||||
let oldScope = c.currentScope
|
||||
c.currentScope = s.typScope
|
||||
openScope(c)
|
||||
pushInfoContext(info)
|
||||
for i in 0 .. <s.typ.n.sons.len:
|
||||
let genericParam = s.typ.n[i].sym
|
||||
let symKind = if genericParam.typ.kind == tyExpr: skConst
|
||||
else: skType
|
||||
|
||||
var boundSym = newSym(symKind, s.typ.n[i].sym.name, s, info)
|
||||
boundSym.typ = invocation.sons[i+1].skipTypes({tyExpr})
|
||||
boundSym.ast = invocation.sons[i+1].n
|
||||
addDecl(c, boundSym)
|
||||
# XXX: copyTree would have been unnecessary here if semTypeNode
|
||||
# didn't modify its input parameters. Currently, it does modify
|
||||
# at least the record lists of the passed object and tuple types
|
||||
var instantiated = semTypeNode(c, copyTree(s.ast[2]), nil)
|
||||
popInfoContext()
|
||||
closeScope(c)
|
||||
c.currentScope = oldScope
|
||||
if instantiated != nil:
|
||||
result = invocation
|
||||
result.kind = tyGenericInst
|
||||
result.sons.add instantiated
|
||||
cacheTypeInst result
|
||||
|
||||
proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType =
|
||||
var cl: TReplTypeVars
|
||||
InitIdTable(cl.symMap)
|
||||
InitIdTable(cl.typeMap)
|
||||
cl.info = info
|
||||
cl.c = c
|
||||
result = ReplaceTypeVarsT(cl, header)
|
||||
when oUseLateInstantiation:
|
||||
lateInstantiateGeneric(c, header, info)
|
||||
else:
|
||||
var cl: TReplTypeVars
|
||||
InitIdTable(cl.symMap)
|
||||
InitIdTable(cl.typeMap)
|
||||
cl.info = info
|
||||
cl.c = c
|
||||
result = ReplaceTypeVarsT(cl, header)
|
||||
|
||||
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
|
||||
result = instGenericContainer(c, n.info, header)
|
||||
|
||||
@@ -40,7 +40,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
|
||||
(typArg.kind == skParam and typArg.typ.sonsLen > 0):
|
||||
# This is either a type known to sem or a typedesc
|
||||
# param to a regular proc (again, known at instantiation)
|
||||
result = evalTypeTrait(n, GetCurrOwner())
|
||||
result = evalTypeTrait(n[0], n[1], GetCurrOwner())
|
||||
else:
|
||||
# a typedesc variable, pass unmodified to evals
|
||||
result = n
|
||||
|
||||
@@ -729,12 +729,16 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
# like: mydata.seq
|
||||
rawAddSon(s.typ, newTypeS(tyEmpty, c))
|
||||
s.ast = a
|
||||
inc c.InGenericContext
|
||||
var body = semTypeNode(c, a.sons[2], nil)
|
||||
dec c.InGenericContext
|
||||
if body != nil:
|
||||
body.sym = s
|
||||
body.size = -1 # could not be computed properly
|
||||
when oUseLateInstantiation:
|
||||
var body: PType = nil
|
||||
s.typScope = c.currentScope.parent
|
||||
else:
|
||||
inc c.InGenericContext
|
||||
var body = semTypeNode(c, a.sons[2], nil)
|
||||
dec c.InGenericContext
|
||||
if body != nil:
|
||||
body.sym = s
|
||||
body.size = -1 # could not be computed properly
|
||||
s.typ.sons[sonsLen(s.typ) - 1] = body
|
||||
popOwner()
|
||||
closeScope(c)
|
||||
|
||||
@@ -156,7 +156,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
LocalError(n.Info, errRangeIsEmpty)
|
||||
var a = semConstExpr(c, n[1])
|
||||
var b = semConstExpr(c, n[2])
|
||||
if not sameType(a.typ, b.typ):
|
||||
if not sameType(a.typ, b.typ):
|
||||
LocalError(n.info, errPureTypeMismatch)
|
||||
elif a.typ.kind notin {tyInt..tyInt64,tyEnum,tyBool,tyChar,
|
||||
tyFloat..tyFloat128,tyUInt8..tyUInt32}:
|
||||
@@ -195,17 +195,19 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||
else:
|
||||
let e = semExprWithType(c, n.sons[1], {efDetermineType})
|
||||
if e.kind in {nkIntLit..nkUInt64Lit}:
|
||||
indx = newTypeS(tyRange, c)
|
||||
indx.n = newNodeI(nkRange, n.info)
|
||||
addSon(indx.n, newIntTypeNode(e.kind, 0, e.typ))
|
||||
addSon(indx.n, newIntTypeNode(e.kind, e.intVal-1, e.typ))
|
||||
addSonSkipIntLit(indx, e.typ)
|
||||
indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
|
||||
elif e.kind == nkSym and e.typ.kind == tyExpr:
|
||||
if e.sym.ast != nil: return semArray(c, e.sym.ast, nil)
|
||||
InternalAssert c.InGenericContext > 0
|
||||
if not isOrdinalType(e.typ.lastSon):
|
||||
localError(n[1].info, errOrdinalTypeExpected)
|
||||
indx = e.typ
|
||||
else:
|
||||
indx = e.typ.skipTypes({tyTypeDesc})
|
||||
addSonSkipIntLit(result, indx)
|
||||
if indx.kind == tyGenericInst: indx = lastSon(indx)
|
||||
if indx.kind != tyGenericParam:
|
||||
if not isOrdinalType(indx):
|
||||
if indx.kind notin {tyGenericParam, tyExpr}:
|
||||
if not isOrdinalType(indx):
|
||||
LocalError(n.sons[1].info, errOrdinalTypeExpected)
|
||||
elif enumHasHoles(indx):
|
||||
LocalError(n.sons[1].info, errEnumXHasHoles, indx.sym.name.s)
|
||||
@@ -587,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 =
|
||||
@@ -634,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:
|
||||
@@ -760,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)
|
||||
@@ -846,7 +853,10 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
LocalError(n.info, errCannotInstantiateX, s.name.s)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
else:
|
||||
result = instGenericContainer(c, n, result)
|
||||
when oUseLateInstantiation:
|
||||
result = lateInstantiateGeneric(c, result, n.info)
|
||||
else:
|
||||
result = instGenericContainer(c, n, result)
|
||||
|
||||
proc semTypeExpr(c: PContext, n: PNode): PType =
|
||||
var n = semExprWithType(c, n, {efDetermineType})
|
||||
@@ -1073,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)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
|
||||
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
|
||||
localError(info, errInheritanceOnlyWithNonFinalObjects)
|
||||
|
||||
proc searchInstTypes(key: PType): PType =
|
||||
proc searchInstTypes*(key: PType): PType =
|
||||
let genericTyp = key.sons[0]
|
||||
InternalAssert genericTyp.kind == tyGenericBody and
|
||||
key.sons[0] == genericTyp and
|
||||
@@ -55,7 +55,7 @@ proc searchInstTypes(key: PType): PType =
|
||||
|
||||
return inst
|
||||
|
||||
proc cacheTypeInst(inst: PType) =
|
||||
proc cacheTypeInst*(inst: PType) =
|
||||
# XXX: add to module's generics
|
||||
# update the refcount
|
||||
let genericTyp = inst.sons[0]
|
||||
@@ -208,6 +208,12 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
|
||||
of tyInt:
|
||||
result = skipIntLit(t)
|
||||
else:
|
||||
if t.kind == tyArray:
|
||||
let idxt = t.sons[0]
|
||||
if idxt.kind == tyExpr and
|
||||
idxt.sym != nil and idxt.sym.kind == skGenericParam:
|
||||
let value = lookupTypeVar(cl, idxt).n
|
||||
t.sons[0] = makeRangeType(cl.c, 0, value.intVal - 1, value.info)
|
||||
if containsGenericType(t):
|
||||
result = copyType(t, t.owner, false)
|
||||
incl(result.flags, tfFromGeneric)
|
||||
|
||||
@@ -626,7 +626,8 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
of tyGenericParam, tyTypeClass:
|
||||
var x = PType(idTableGet(c.bindings, f))
|
||||
if x == nil:
|
||||
if c.calleeSym.kind == skType and f.kind == tyGenericParam and not c.typedescMatched:
|
||||
if c.calleeSym != nil and c.calleeSym.kind == skType and
|
||||
f.kind == tyGenericParam and not c.typedescMatched:
|
||||
# XXX: The fact that generic types currently use tyGenericParam for
|
||||
# their parameters is really a misnomer. tyGenericParam means "match
|
||||
# any value" and what we need is "match any type", which can be encoded
|
||||
@@ -670,7 +671,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:
|
||||
@@ -772,6 +775,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
|
||||
if evaluated != nil:
|
||||
r = isGeneric
|
||||
arg.typ = newTypeS(tyExpr, c)
|
||||
arg.typ.sons = @[evaluated.typ]
|
||||
arg.typ.n = evaluated
|
||||
|
||||
if r == isGeneric:
|
||||
|
||||
@@ -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
|
||||
|
||||
43
install.txt
43
install.txt
@@ -62,46 +62,3 @@ Currently, the following C compilers are supported under Windows:
|
||||
| http://www.digitalmars.com/download/freecompiler.html
|
||||
|
||||
However, most testing is done with GCC.
|
||||
|
||||
|
||||
|
||||
Bootstrapping from github
|
||||
-------------------------
|
||||
|
||||
To get the source code you need either of these:
|
||||
|
||||
* A working web browser + tar(or equivalent):
|
||||
https://github.com/Araq/Nimrod/tarball/master
|
||||
* wget + tar:
|
||||
``wget --no-check-certificate "https://github.com/Araq/Nimrod/tarball/master"``
|
||||
* git: ``git clone git://github.com/Araq/Nimrod.git``
|
||||
|
||||
After downloading the source (and extracting it), you need to
|
||||
extract ``build/csources.zip``:
|
||||
|
||||
* ``cd build``
|
||||
* ``unzip csources.zip``
|
||||
* ``cd ..``
|
||||
|
||||
and then you can bootstrap with:
|
||||
|
||||
On Windows
|
||||
~~~~~~~~~~
|
||||
|
||||
* ``build.bat``
|
||||
* ``bin\nimrod c koch``
|
||||
* ``koch boot -d:release``
|
||||
|
||||
If you want a 64 bit build, make sure that you have a GCC built for Win64 and
|
||||
execute ``build64.bat`` instead of ``build.bat``.
|
||||
|
||||
|
||||
On UNIX
|
||||
~~~~~~~
|
||||
|
||||
* ``./build.sh``
|
||||
* ``bin/nimrod c koch``
|
||||
* ``./koch boot -d:release``
|
||||
|
||||
Installation on UNIX can then be done with ``koch install [dir]``.
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
## This module implements a simple HTTP client that can be used to retrieve
|
||||
## webpages/other data.
|
||||
##
|
||||
##
|
||||
## **Note**: This module is not ideal, connection is not kept alive so sites with
|
||||
## many redirects are expensive. As such in the future this module may change,
|
||||
## and the current procedures will be deprecated.
|
||||
##
|
||||
## Retrieving a website
|
||||
## ====================
|
||||
##
|
||||
@@ -62,8 +67,15 @@
|
||||
## that as long as the server is sending data an exception will not be raised,
|
||||
## if however data does not reach client within the specified timeout an ETimeout
|
||||
## exception will then be raised.
|
||||
##
|
||||
## Proxy
|
||||
## =====
|
||||
##
|
||||
## A proxy can be specified as a param to any of these procedures, the ``newProxy``
|
||||
## constructor should be used for this purpose. However,
|
||||
## currently only basic authentication is supported.
|
||||
|
||||
import sockets, strutils, parseurl, parseutils, strtabs
|
||||
import sockets, strutils, parseurl, parseutils, strtabs, base64
|
||||
|
||||
type
|
||||
TResponse* = tuple[
|
||||
@@ -72,6 +84,10 @@ type
|
||||
headers: PStringTable,
|
||||
body: string]
|
||||
|
||||
PProxy* = ref object
|
||||
url*: TUrl
|
||||
auth*: string
|
||||
|
||||
EInvalidProtocol* = object of ESynch ## exception that is raised when server
|
||||
## does not conform to the implemented
|
||||
## protocol
|
||||
@@ -239,23 +255,34 @@ when not defined(ssl):
|
||||
else:
|
||||
let defaultSSLContext = newContext(verifyMode = CVerifyNone)
|
||||
|
||||
proc newProxy*(url: string, auth = ""): PProxy =
|
||||
## Constructs a new ``TProxy`` object.
|
||||
result = PProxy(url: parseUrl(url), auth: auth)
|
||||
|
||||
proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
|
||||
body = "",
|
||||
sslContext: PSSLContext = defaultSSLContext,
|
||||
timeout = -1, userAgent = defUserAgent): TResponse =
|
||||
timeout = -1, userAgent = defUserAgent,
|
||||
proxy: PProxy = nil): TResponse =
|
||||
## | Requests ``url`` with the specified ``httpMethod``.
|
||||
## | Extra headers can be specified and must be seperated by ``\c\L``
|
||||
## | An optional timeout can be specified in miliseconds, if reading from the
|
||||
## server takes longer than specified an ETimeout exception will be raised.
|
||||
var r = parseUrl(url)
|
||||
var r = if proxy == nil: parseUrl(url) else: proxy.url
|
||||
var headers = substr($httpMethod, len("http"))
|
||||
headers.add(" /" & r.path & r.query)
|
||||
if proxy == nil:
|
||||
headers.add(" /" & r.path & r.query)
|
||||
else:
|
||||
headers.add(" " & url)
|
||||
|
||||
headers.add(" HTTP/1.1\c\L")
|
||||
|
||||
add(headers, "Host: " & r.hostname & "\c\L")
|
||||
if userAgent != "":
|
||||
add(headers, "User-Agent: " & userAgent & "\c\L")
|
||||
if proxy != nil and proxy.auth != "":
|
||||
let auth = base64.encode(proxy.auth, newline = "")
|
||||
add(headers, "Proxy-Authorization: basic " & auth & "\c\L")
|
||||
add(headers, extraHeaders)
|
||||
add(headers, "\c\L")
|
||||
|
||||
@@ -299,30 +326,34 @@ proc getNewLocation(lastUrl: string, headers: PStringTable): string =
|
||||
|
||||
proc get*(url: string, extraHeaders = "", maxRedirects = 5,
|
||||
sslContext: PSSLContext = defaultSSLContext,
|
||||
timeout = -1, userAgent = defUserAgent): TResponse =
|
||||
timeout = -1, userAgent = defUserAgent,
|
||||
proxy: PProxy = nil): TResponse =
|
||||
## | GETs the ``url`` and returns a ``TResponse`` object
|
||||
## | This proc also handles redirection
|
||||
## | Extra headers can be specified and must be separated by ``\c\L``.
|
||||
## | An optional timeout can be specified in miliseconds, if reading from the
|
||||
## server takes longer than specified an ETimeout exception will be raised.
|
||||
result = request(url, httpGET, extraHeaders, "", sslContext, timeout, userAgent)
|
||||
result = request(url, httpGET, extraHeaders, "", sslContext, timeout,
|
||||
userAgent, proxy)
|
||||
var lastURL = url
|
||||
for i in 1..maxRedirects:
|
||||
if result.status.redirection():
|
||||
let redirectTo = getNewLocation(lastURL, result.headers)
|
||||
result = request(redirectTo, httpGET, extraHeaders, "", sslContext,
|
||||
timeout, userAgent)
|
||||
timeout, userAgent, proxy)
|
||||
lastUrl = redirectTo
|
||||
|
||||
proc getContent*(url: string, extraHeaders = "", maxRedirects = 5,
|
||||
sslContext: PSSLContext = defaultSSLContext,
|
||||
timeout = -1, userAgent = defUserAgent): string =
|
||||
timeout = -1, userAgent = defUserAgent,
|
||||
proxy: PProxy = nil): string =
|
||||
## | GETs the body and returns it as a string.
|
||||
## | Raises exceptions for the status codes ``4xx`` and ``5xx``
|
||||
## | Extra headers can be specified and must be separated by ``\c\L``.
|
||||
## | An optional timeout can be specified in miliseconds, if reading from the
|
||||
## server takes longer than specified an ETimeout exception will be raised.
|
||||
var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent)
|
||||
var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent,
|
||||
proxy)
|
||||
if r.status[0] in {'4','5'}:
|
||||
raise newException(EHTTPRequestErr, r.status)
|
||||
else:
|
||||
@@ -331,7 +362,8 @@ proc getContent*(url: string, extraHeaders = "", maxRedirects = 5,
|
||||
proc post*(url: string, extraHeaders = "", body = "",
|
||||
maxRedirects = 5,
|
||||
sslContext: PSSLContext = defaultSSLContext,
|
||||
timeout = -1, userAgent = defUserAgent): TResponse =
|
||||
timeout = -1, userAgent = defUserAgent,
|
||||
proxy: PProxy = nil): TResponse =
|
||||
## | POSTs ``body`` to the ``url`` and returns a ``TResponse`` object.
|
||||
## | This proc adds the necessary Content-Length header.
|
||||
## | This proc also handles redirection.
|
||||
@@ -339,27 +371,29 @@ proc post*(url: string, extraHeaders = "", body = "",
|
||||
## | An optional timeout can be specified in miliseconds, if reading from the
|
||||
## server takes longer than specified an ETimeout exception will be raised.
|
||||
var xh = extraHeaders & "Content-Length: " & $len(body) & "\c\L"
|
||||
result = request(url, httpPOST, xh, body, sslContext, timeout, userAgent)
|
||||
result = request(url, httpPOST, xh, body, sslContext, timeout, userAgent,
|
||||
proxy)
|
||||
var lastUrl = ""
|
||||
for i in 1..maxRedirects:
|
||||
if result.status.redirection():
|
||||
let redirectTo = getNewLocation(lastURL, result.headers)
|
||||
var meth = if result.status != "307": httpGet else: httpPost
|
||||
result = request(redirectTo, meth, xh, body, sslContext, timeout,
|
||||
userAgent)
|
||||
userAgent, proxy)
|
||||
lastUrl = redirectTo
|
||||
|
||||
proc postContent*(url: string, extraHeaders = "", body = "",
|
||||
maxRedirects = 5,
|
||||
sslContext: PSSLContext = defaultSSLContext,
|
||||
timeout = -1, userAgent = defUserAgent): string =
|
||||
timeout = -1, userAgent = defUserAgent,
|
||||
proxy: PProxy = nil): string =
|
||||
## | POSTs ``body`` to ``url`` and returns the response's body as a string
|
||||
## | Raises exceptions for the status codes ``4xx`` and ``5xx``
|
||||
## | Extra headers can be specified and must be separated by ``\c\L``.
|
||||
## | An optional timeout can be specified in miliseconds, if reading from the
|
||||
## server takes longer than specified an ETimeout exception will be raised.
|
||||
var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout,
|
||||
userAgent)
|
||||
userAgent, proxy)
|
||||
if r.status[0] in {'4','5'}:
|
||||
raise newException(EHTTPRequestErr, r.status)
|
||||
else:
|
||||
@@ -367,14 +401,15 @@ proc postContent*(url: string, extraHeaders = "", body = "",
|
||||
|
||||
proc downloadFile*(url: string, outputFilename: string,
|
||||
sslContext: PSSLContext = defaultSSLContext,
|
||||
timeout = -1, userAgent = defUserAgent) =
|
||||
timeout = -1, userAgent = defUserAgent,
|
||||
proxy: PProxy = nil) =
|
||||
## | Downloads ``url`` and saves it to ``outputFilename``
|
||||
## | An optional timeout can be specified in miliseconds, if reading from the
|
||||
## server takes longer than specified an ETimeout exception will be raised.
|
||||
var f: TFile
|
||||
if open(f, outputFilename, fmWrite):
|
||||
f.write(getContent(url, sslContext = sslContext, timeout = timeout,
|
||||
userAgent = userAgent))
|
||||
userAgent = userAgent, proxy = proxy))
|
||||
f.close()
|
||||
else:
|
||||
fileError("Unable to open file")
|
||||
|
||||
@@ -54,7 +54,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
|
||||
@@ -78,6 +78,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.
|
||||
@@ -1487,7 +1498,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):
|
||||
@@ -1499,7 +1510,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):
|
||||
@@ -1802,7 +1813,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
|
||||
@@ -1815,7 +1826,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:
|
||||
@@ -1824,7 +1835,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)
|
||||
|
||||
@@ -6,8 +6,7 @@ documentation.
|
||||
Compiling the Nimrod compiler is quite straightforward. Because
|
||||
the Nimrod compiler itself is written in the Nimrod programming language
|
||||
the C source of an older version of the compiler are needed to bootstrap the
|
||||
latest version. The C sources are however included with this repository under
|
||||
the build directory.
|
||||
latest version. The C sources are available in a separate repo [here](http://github.com/nimrod-code/csources).
|
||||
|
||||
Pre-compiled snapshots of the compiler are also available on
|
||||
[Nimbuild](http://build.nimrod-code.org/). Your platform however may not
|
||||
@@ -53,7 +52,7 @@ and you can also get help in the IRC channel
|
||||
on [Freenode](irc://irc.freenode.net/nimrod) in #nimrod.
|
||||
|
||||
## License
|
||||
The compiler and the standard library is licensed under the MIT license,
|
||||
The compiler and the standard library are licensed under the MIT license,
|
||||
except for some modules where the documentation suggests otherwise. This means
|
||||
that you can use any license for your own programs developed with Nimrod,
|
||||
allowing you to create commercial applications.
|
||||
|
||||
@@ -6,8 +6,7 @@ documentation.
|
||||
Compiling the Nimrod compiler is quite straightforward. Because
|
||||
the Nimrod compiler itself is written in the Nimrod programming language
|
||||
the C source of an older version of the compiler are needed to bootstrap the
|
||||
latest version. The C sources are however included with this repository under
|
||||
the build directory.
|
||||
latest version. The C sources are available in a separate repo [here](http://github.com/nimrod-code/csources).
|
||||
|
||||
Pre-compiled snapshots of the compiler are also available on
|
||||
[Nimbuild](http://build.nimrod-code.org/). Your platform however may not
|
||||
@@ -53,7 +52,7 @@ and you can also get help in the IRC channel
|
||||
on [Freenode](irc://irc.freenode.net/nimrod) in #nimrod.
|
||||
|
||||
## License
|
||||
The compiler and the standard library is licensed under the MIT license,
|
||||
The compiler and the standard library are licensed under the MIT license,
|
||||
except for some modules where the documentation suggests otherwise. This means
|
||||
that you can use any license for your own programs developed with Nimrod,
|
||||
allowing you to create commercial applications.
|
||||
|
||||
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))
|
||||
|
||||
30
tests/compile/tgenericshardcases.nim
Normal file
30
tests/compile/tgenericshardcases.nim
Normal file
@@ -0,0 +1,30 @@
|
||||
discard """
|
||||
file: "tgenericshardcases.nim"
|
||||
output: "int\nfloat\nint\nstring"
|
||||
"""
|
||||
|
||||
import typetraits
|
||||
|
||||
proc typeNameLen(x: typedesc): int {.compileTime.} =
|
||||
result = x.name.len
|
||||
|
||||
macro selectType(a, b: typedesc): typedesc =
|
||||
result = a
|
||||
|
||||
type
|
||||
Foo[T] = object
|
||||
data1: array[high(T), int]
|
||||
data2: array[1..typeNameLen(T), selectType(float, string)]
|
||||
|
||||
MyEnum = enum A, B, C,D
|
||||
|
||||
var f1: Foo[MyEnum]
|
||||
var f2: Foo[int8]
|
||||
|
||||
static:
|
||||
assert high(f1.data1) == D
|
||||
assert high(f1.data2) == 6 # length of MyEnum
|
||||
|
||||
assert high(f2.data1) == 127
|
||||
assert high(f2.data2) == 4 # length of int8
|
||||
|
||||
39
tests/run/tmacrogenerics.nim
Normal file
39
tests/run/tmacrogenerics.nim
Normal file
@@ -0,0 +1,39 @@
|
||||
discard """
|
||||
file: "tmacrogenerics.nim"
|
||||
msg: '''
|
||||
instantiation 1 with int and float
|
||||
instantiation 2 with float and string
|
||||
instantiation 3 with string and string
|
||||
counter: 3
|
||||
'''
|
||||
output: "int\nfloat\nint\nstring"
|
||||
"""
|
||||
|
||||
import typetraits, macros
|
||||
|
||||
var counter {.compileTime.} = 0
|
||||
|
||||
macro makeBar(A, B: typedesc): typedesc =
|
||||
inc counter
|
||||
echo "instantiation ", counter, " with ", A.name, " and ", B.name
|
||||
result = A
|
||||
|
||||
type
|
||||
Bar[T, U] = makeBar(T, U)
|
||||
|
||||
var bb1: Bar[int, float]
|
||||
var bb2: Bar[float, string]
|
||||
var bb3: Bar[int, float]
|
||||
var bb4: Bar[string, string]
|
||||
|
||||
proc match(a: int) = echo "int"
|
||||
proc match(a: string) = echo "string"
|
||||
proc match(a: float) = echo "float"
|
||||
|
||||
match(bb1)
|
||||
match(bb2)
|
||||
match(bb3)
|
||||
match(bb4)
|
||||
|
||||
static:
|
||||
echo "counter: ", counter
|
||||
@@ -1,17 +1,22 @@
|
||||
discard """
|
||||
file: "tstaticparams.nim"
|
||||
output: "abracadabra\ntest"
|
||||
output: "abracadabra\ntest\n3"
|
||||
"""
|
||||
|
||||
type
|
||||
TFoo[T; Val: expr[string]] = object
|
||||
data: array[4, T]
|
||||
|
||||
TBar[T; I: expr[int]] = object
|
||||
data: array[I, T]
|
||||
|
||||
proc takeFoo(x: TFoo) =
|
||||
echo "abracadabra"
|
||||
echo TFoo.Val
|
||||
|
||||
var x: TFoo[int, "test"]
|
||||
|
||||
takeFoo(x)
|
||||
|
||||
var y: TBar[float, 4]
|
||||
echo high(y.data)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user