mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-11 07:55:49 +00:00
Merge pull request #7681 from nim-lang/typedesc-reforms
Typedesc reforms
This commit is contained in:
@@ -119,6 +119,13 @@
|
||||
- In order to make ``for`` loops and iterators more flexible to use Nim now
|
||||
supports so called "for-loop macros". See
|
||||
the `manual <manual.html#macros-for-loop-macros>`_ for more details.
|
||||
- the `typedesc` special type has been renamed to just `type`.
|
||||
- `static` and `type` are now also modifiers similar to `ref` and `ptr`.
|
||||
They denote the special types `static[T]` and `type[T]`.
|
||||
- Forcing compile-time evaluation with `static` now supports specifying
|
||||
the desired target type (as a concrete type or as a type class)
|
||||
- The `type` operator now supports checking that the supplied expression
|
||||
matches an expected type constraint.
|
||||
|
||||
### Language changes
|
||||
|
||||
|
||||
@@ -293,6 +293,10 @@ const
|
||||
# the compiler will avoid printing such names
|
||||
# in user messages.
|
||||
|
||||
sfHoisted* = sfForward
|
||||
# an expression was hoised to an anonymous variable.
|
||||
# the flag is applied to the var/let symbol
|
||||
|
||||
sfNoForward* = sfRegister
|
||||
# forward declarations are not required (per module)
|
||||
sfReorder* = sfForward
|
||||
@@ -454,6 +458,9 @@ type
|
||||
nfPreventCg # this node should be ignored by the codegen
|
||||
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
|
||||
nfFromTemplate # a top-level node returned from a template
|
||||
nfDefaultParam # an automatically inserter default parameter
|
||||
nfDefaultRefsParam # a default param value references another parameter
|
||||
# the flag is applied to proc default values and to calls
|
||||
|
||||
TNodeFlags* = set[TNodeFlag]
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that)
|
||||
@@ -571,8 +578,8 @@ type
|
||||
TMagic* = enum # symbols that require compiler magic:
|
||||
mNone,
|
||||
mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
|
||||
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
|
||||
mEcho, mShallowCopy, mSlurp, mStaticExec,
|
||||
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mType, mTypeOf,
|
||||
mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic,
|
||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
|
||||
mUnaryLt, mInc, mDec, mOrd,
|
||||
mNew, mNewFinalize, mNewSeq, mNewSeqOfCap,
|
||||
@@ -971,7 +978,7 @@ const
|
||||
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
||||
nfDotSetter, nfDotField,
|
||||
nfIsRef, nfPreventCg, nfLL,
|
||||
nfFromTemplate}
|
||||
nfFromTemplate, nfDefaultRefsParam}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
|
||||
@@ -31,26 +31,39 @@ when declared(echo):
|
||||
proc debug*(conf: ConfigRef; n: PType) {.deprecated.}
|
||||
proc debug*(conf: ConfigRef; n: PNode) {.deprecated.}
|
||||
|
||||
template mdbg*: bool {.dirty.} =
|
||||
when compiles(c.module):
|
||||
c.module.fileIdx == c.config.projectMainIdx
|
||||
elif compiles(c.c.module):
|
||||
c.c.module.fileIdx == c.c.config.projectMainIdx
|
||||
elif compiles(m.c.module):
|
||||
m.c.module.fileIdx == m.c.config.projectMainIdx
|
||||
elif compiles(cl.c.module):
|
||||
cl.c.module.fileIdx == cl.c.config.projectMainIdx
|
||||
elif compiles(p):
|
||||
when compiles(p.lex):
|
||||
p.lex.fileIdx == p.lex.config.projectMainIdx
|
||||
template debug*(x: PSym|PType|PNode) {.deprecated.} =
|
||||
when compiles(c.config):
|
||||
debug(c.config, x)
|
||||
elif compiles(c.graph.config):
|
||||
debug(c.graph.config, x)
|
||||
else:
|
||||
p.module.module.fileIdx == p.config.projectMainIdx
|
||||
elif compiles(m.module.fileIdx):
|
||||
m.module.fileIdx == m.config.projectMainIdx
|
||||
elif compiles(L.fileIdx):
|
||||
L.fileIdx == L.config.projectMainIdx
|
||||
else:
|
||||
error()
|
||||
error()
|
||||
|
||||
template debug*(x: auto) {.deprecated.} =
|
||||
echo x
|
||||
|
||||
template mdbg*: bool {.deprecated.} =
|
||||
when compiles(c.graph):
|
||||
c.module.fileIdx == c.graph.config.projectMainIdx
|
||||
elif compiles(c.module):
|
||||
c.module.fileIdx == c.config.projectMainIdx
|
||||
elif compiles(c.c.module):
|
||||
c.c.module.fileIdx == c.c.config.projectMainIdx
|
||||
elif compiles(m.c.module):
|
||||
m.c.module.fileIdx == m.c.config.projectMainIdx
|
||||
elif compiles(cl.c.module):
|
||||
cl.c.module.fileIdx == cl.c.config.projectMainIdx
|
||||
elif compiles(p):
|
||||
when compiles(p.lex):
|
||||
p.lex.fileIdx == p.lex.config.projectMainIdx
|
||||
else:
|
||||
p.module.module.fileIdx == p.config.projectMainIdx
|
||||
elif compiles(m.module.fileIdx):
|
||||
m.module.fileIdx == m.config.projectMainIdx
|
||||
elif compiles(L.fileIdx):
|
||||
L.fileIdx == L.config.projectMainIdx
|
||||
else:
|
||||
error()
|
||||
|
||||
# --------------------------- ident tables ----------------------------------
|
||||
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
|
||||
|
||||
@@ -44,6 +44,7 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimcomputedgoto")
|
||||
defineSymbol("nimunion")
|
||||
defineSymbol("nimnewshared")
|
||||
defineSymbol("nimNewTypedesc")
|
||||
defineSymbol("nimrequiresnimframe")
|
||||
defineSymbol("nimparsebiggestfloatmagic")
|
||||
defineSymbol("nimalias")
|
||||
|
||||
@@ -336,6 +336,18 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
|
||||
if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
|
||||
result = not containsGarbageCollectedRef(t)
|
||||
|
||||
proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym =
|
||||
result = newSym(skLet, name, owner, varSection.info, owner.options)
|
||||
result.flags.incl sfHoisted
|
||||
result.typ = expr.typ
|
||||
|
||||
var varDef = newNodeI(nkIdentDefs, varSection.info, 3)
|
||||
varDef.sons[0] = newSymNode(result)
|
||||
varDef.sons[1] = newNodeI(nkEmpty, varSection.info)
|
||||
varDef.sons[2] = expr
|
||||
|
||||
varSection.add varDef
|
||||
|
||||
proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
|
||||
v: PNode; useShallowCopy=false): PSym =
|
||||
result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
|
||||
|
||||
@@ -50,6 +50,9 @@ type
|
||||
SymbolMode = enum
|
||||
smNormal, smAllowNil, smAfterDot
|
||||
|
||||
TPrimaryMode = enum
|
||||
pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
|
||||
|
||||
proc parseAll*(p: var TParser): PNode
|
||||
proc closeParser*(p: var TParser)
|
||||
proc parseTopLevelStmt*(p: var TParser): PNode
|
||||
@@ -81,6 +84,9 @@ proc parsePragma(p: var TParser): PNode
|
||||
proc postExprBlocks(p: var TParser, x: PNode): PNode
|
||||
proc parseExprStmt(p: var TParser): PNode
|
||||
proc parseBlock(p: var TParser): PNode
|
||||
proc primary(p: var TParser, mode: TPrimaryMode): PNode
|
||||
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
|
||||
|
||||
# implementation
|
||||
|
||||
proc getTok(p: var TParser) =
|
||||
@@ -332,6 +338,8 @@ proc colcom(p: var TParser, n: PNode) =
|
||||
eat(p, tkColon)
|
||||
skipComment(p, n)
|
||||
|
||||
const tkBuiltInMagics = {tkType, tkStatic, tkAddr}
|
||||
|
||||
proc parseSymbol(p: var TParser, mode = smNormal): PNode =
|
||||
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
|
||||
#| | IDENT | KEYW
|
||||
@@ -340,7 +348,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
|
||||
result = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
of tokKeywordLow..tokKeywordHigh:
|
||||
if p.tok.tokType == tkAddr or p.tok.tokType == tkType or mode == smAfterDot:
|
||||
if p.tok.tokType in tkBuiltInMagics or mode == smAfterDot:
|
||||
# for backwards compatibility these 2 are always valid:
|
||||
result = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
@@ -529,9 +537,6 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode =
|
||||
else:
|
||||
result = a
|
||||
|
||||
type
|
||||
TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
|
||||
|
||||
proc complexOrSimpleStmt(p: var TParser): PNode
|
||||
proc simpleExpr(p: var TParser, mode = pmNormal): PNode
|
||||
|
||||
@@ -629,7 +634,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
|
||||
#| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
|
||||
#| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
|
||||
case p.tok.tokType
|
||||
of tkSymbol, tkType, tkAddr:
|
||||
of tkSymbol, tkBuiltInMagics:
|
||||
result = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
result = parseGStrLit(p, result)
|
||||
@@ -745,7 +750,12 @@ proc commandParam(p: var TParser, isFirstParam: var bool): PNode =
|
||||
addSon(result, parseExpr(p))
|
||||
isFirstParam = false
|
||||
|
||||
proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||
const
|
||||
tkTypeClasses = {tkRef, tkPtr, tkVar, tkStatic, tkType,
|
||||
tkEnum, tkTuple, tkObject, tkProc}
|
||||
|
||||
proc primarySuffix(p: var TParser, r: PNode,
|
||||
baseIndent: int, mode: TPrimaryMode): PNode =
|
||||
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
|
||||
#| | doBlocks
|
||||
#| | '.' optInd symbol generalizedLit?
|
||||
@@ -762,7 +772,14 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||
case p.tok.tokType
|
||||
of tkParLe:
|
||||
# progress guaranteed
|
||||
somePar()
|
||||
if p.tok.strongSpaceA > 0:
|
||||
# inside type sections, expressions such as `ref (int, bar)`
|
||||
# are parsed as a nkCommand with a single tuple argument (nkPar)
|
||||
if mode == pmTypeDef:
|
||||
result = newNodeP(nkCommand, p)
|
||||
result.addSon r
|
||||
result.addSon primary(p, pmNormal)
|
||||
break
|
||||
result = namedParams(p, result, nkCall, tkParRi)
|
||||
if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
|
||||
result.kind = nkObjConstr
|
||||
@@ -778,8 +795,13 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||
# progress guaranteed
|
||||
somePar()
|
||||
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
|
||||
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType,
|
||||
tkOpr, tkDotDot:
|
||||
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast,
|
||||
tkOpr, tkDotDot, tkTypeClasses - {tkRef, tkPtr}:
|
||||
# XXX: In type sections we allow the free application of the
|
||||
# command syntax, with the exception of expressions such as
|
||||
# `foo ref` or `foo ptr`. Unfortunately, these two are also
|
||||
# used as infix operators for the memory regions feature and
|
||||
# the current parsing rules don't play well here.
|
||||
if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}):
|
||||
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
|
||||
# solution, but pragmas.nim can't handle that
|
||||
@@ -804,9 +826,6 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||
else:
|
||||
break
|
||||
|
||||
proc primary(p: var TParser, mode: TPrimaryMode): PNode
|
||||
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
|
||||
|
||||
proc parseOperators(p: var TParser, headNode: PNode,
|
||||
limit: int, mode: TPrimaryMode): PNode =
|
||||
result = headNode
|
||||
@@ -1127,9 +1146,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
|
||||
proc isExprStart(p: TParser): bool =
|
||||
case p.tok.tokType
|
||||
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
|
||||
tkProc, tkFunc, tkIterator, tkBind, tkAddr,
|
||||
tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics,
|
||||
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
|
||||
tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
|
||||
tkTuple, tkObject, tkWhen, tkCase, tkOut:
|
||||
result = true
|
||||
else: result = false
|
||||
|
||||
@@ -1190,7 +1209,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||
#| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
|
||||
#| primary = typeKeyw typeDescK
|
||||
#| / prefixOperator* identOrLiteral primarySuffix*
|
||||
#| / 'static' primary
|
||||
#| / 'bind' primary
|
||||
if isOperator(p.tok):
|
||||
let isSigil = isSigilLike(p.tok)
|
||||
@@ -1203,7 +1221,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||
#XXX prefix operators
|
||||
let baseInd = p.lex.currLineIndent
|
||||
addSon(result, primary(p, pmSkipSuffix))
|
||||
result = primarySuffix(p, result, baseInd)
|
||||
result = primarySuffix(p, result, baseInd, mode)
|
||||
else:
|
||||
addSon(result, primary(p, pmNormal))
|
||||
return
|
||||
@@ -1233,14 +1251,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||
result = parseTypeClass(p)
|
||||
else:
|
||||
parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
|
||||
of tkStatic:
|
||||
let info = parLineInfo(p)
|
||||
getTokNoInd(p)
|
||||
let next = primary(p, pmNormal)
|
||||
if next.kind == nkBracket and next.sonsLen == 1:
|
||||
result = newNode(nkStaticTy, info, @[next.sons[0]])
|
||||
else:
|
||||
result = newNode(nkStaticExpr, info, @[next])
|
||||
of tkBind:
|
||||
result = newNodeP(nkBind, p)
|
||||
getTok(p)
|
||||
@@ -1255,7 +1265,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||
let baseInd = p.lex.currLineIndent
|
||||
result = identOrLiteral(p, mode)
|
||||
if mode != pmSkipSuffix:
|
||||
result = primarySuffix(p, result, baseInd)
|
||||
result = primarySuffix(p, result, baseInd, mode)
|
||||
|
||||
proc parseTypeDesc(p: var TParser): PNode =
|
||||
#| typeDesc = simpleExpr
|
||||
|
||||
@@ -387,8 +387,10 @@ proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
|
||||
assert(theEnd < 0)
|
||||
result = 0
|
||||
for i in countup(start, sonsLen(n) + theEnd):
|
||||
inc(result, lsub(g, n.sons[i]))
|
||||
inc(result, 2) # for ``, ``
|
||||
let param = n.sons[i]
|
||||
if nfDefaultParam notin param.flags:
|
||||
inc(result, lsub(g, param))
|
||||
inc(result, 2) # for ``, ``
|
||||
if result > 0:
|
||||
dec(result, 2) # last does not get a comma!
|
||||
|
||||
|
||||
@@ -48,7 +48,10 @@ proc semQuoteAst(c: PContext, n: PNode): PNode
|
||||
proc finishMethod(c: PContext, s: PSym)
|
||||
proc evalAtCompileTime(c: PContext, n: PNode): PNode
|
||||
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
|
||||
|
||||
proc semStaticExpr(c: PContext, n: PNode): PNode
|
||||
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType
|
||||
proc semTypeOf(c: PContext; n: PNode): PNode
|
||||
proc hasUnresolvedArgs(c: PContext, n: PNode): bool
|
||||
proc isArrayConstr(n: PNode): bool {.inline.} =
|
||||
result = n.kind == nkBracket and
|
||||
n.typ.skipTypes(abstractInst).kind == tyArray
|
||||
@@ -70,6 +73,16 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
|
||||
# echo "passing to safeSemExpr: ", renderTree(n)
|
||||
discard safeSemExpr(c, n)
|
||||
|
||||
proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
|
||||
result = arg
|
||||
let x = result.skipConv
|
||||
if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr:
|
||||
changeType(c, x, formal, check=true)
|
||||
else:
|
||||
result = skipHiddenSubConv(result)
|
||||
#result.typ = takeType(formal, arg.typ)
|
||||
#echo arg.info, " picked ", result.typ.typeToString
|
||||
|
||||
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||
if arg.typ.isNil:
|
||||
localError(c.config, arg.info, "expression has no type: " &
|
||||
@@ -85,13 +98,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||
result = copyTree(arg)
|
||||
result.typ = formal
|
||||
else:
|
||||
let x = result.skipConv
|
||||
if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr:
|
||||
changeType(c, x, formal, check=true)
|
||||
else:
|
||||
result = skipHiddenSubConv(result)
|
||||
#result.typ = takeType(formal, arg.typ)
|
||||
#echo arg.info, " picked ", result.typ.typeToString
|
||||
result = fitNodePostMatch(c, formal, result)
|
||||
|
||||
proc inferWithMetatype(c: PContext, formal: PType,
|
||||
arg: PNode, coerceDistincts = false): PNode
|
||||
|
||||
@@ -391,7 +391,23 @@ proc inferWithMetatype(c: PContext, formal: PType,
|
||||
result = copyTree(arg)
|
||||
result.typ = formal
|
||||
|
||||
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
||||
proc updateDefaultParams(call: PNode) =
|
||||
# In generic procs, the default parameter may be unique for each
|
||||
# instantiation (see tlateboundgenericparams).
|
||||
# After a call is resolved, we need to re-assign any default value
|
||||
# that was used during sigmatch. sigmatch is responsible for marking
|
||||
# the default params with `nfDefaultParam` and `instantiateProcType`
|
||||
# computes correctly the default values for each instantiation.
|
||||
let calleeParams = call[0].sym.typ.n
|
||||
for i in countdown(call.len - 1, 1):
|
||||
if nfDefaultParam notin call[i].flags:
|
||||
return
|
||||
let def = calleeParams[i].sym.ast
|
||||
if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam
|
||||
call[i] = def
|
||||
|
||||
proc semResolvedCall(c: PContext, x: TCandidate,
|
||||
n: PNode, flags: TExprFlags): PNode =
|
||||
assert x.state == csMatch
|
||||
var finalCallee = x.calleeSym
|
||||
markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym)
|
||||
@@ -424,8 +440,9 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
||||
|
||||
result = x.call
|
||||
instGenericConvertersSons(c, result, x)
|
||||
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
|
||||
result[0] = newSymNode(finalCallee, result[0].info)
|
||||
result.typ = finalCallee.typ.sons[0]
|
||||
updateDefaultParams(result)
|
||||
|
||||
proc canDeref(n: PNode): bool {.inline.} =
|
||||
result = n.len >= 2 and (let t = n[1].typ;
|
||||
@@ -447,7 +464,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
message(c.config, n.info, hintUserRaw,
|
||||
"Non-matching candidates for " & renderTree(n) & "\n" &
|
||||
candidates)
|
||||
result = semResolvedCall(c, n, r)
|
||||
result = semResolvedCall(c, r, n, flags)
|
||||
elif implicitDeref in c.features and canDeref(n):
|
||||
# try to deref the first argument and then try overloading resolution again:
|
||||
#
|
||||
@@ -458,7 +475,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
#
|
||||
n.sons[1] = n.sons[1].tryDeref
|
||||
var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
|
||||
if r.state == csMatch: result = semResolvedCall(c, n, r)
|
||||
if r.state == csMatch: result = semResolvedCall(c, r, n, flags)
|
||||
else:
|
||||
# get rid of the deref again for a better error message:
|
||||
n.sons[1] = n.sons[1].sons[0]
|
||||
|
||||
@@ -288,7 +288,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||
result.addSonSkipIntLit(typ)
|
||||
|
||||
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
||||
let typedesc = makeTypeDesc(c, typ)
|
||||
let typedesc = newTypeS(tyTypeDesc, c)
|
||||
typedesc.addSonSkipIntLit(assertNotNil(c.config, typ))
|
||||
let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
|
||||
c.config.options).linkTo(typedesc)
|
||||
return newSymNode(sym, info)
|
||||
|
||||
@@ -191,7 +191,25 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||
return n
|
||||
|
||||
result = newNodeI(nkConv, n.info)
|
||||
var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
|
||||
|
||||
var targetType = semTypeNode(c, n.sons[0], nil)
|
||||
if targetType.kind == tyTypeDesc:
|
||||
internalAssert c.config, targetType.len > 0
|
||||
if targetType.base.kind == tyNone:
|
||||
return semTypeOf(c, n[1])
|
||||
else:
|
||||
targetType = targetType.base
|
||||
elif targetType.kind == tyStatic:
|
||||
var evaluated = semStaticExpr(c, n[1])
|
||||
if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc:
|
||||
result = n
|
||||
result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil)
|
||||
return
|
||||
elif targetType.base.kind == tyNone:
|
||||
return evaluated
|
||||
else:
|
||||
targetType = targetType.base
|
||||
|
||||
maybeLiftType(targetType, c, n[0].info)
|
||||
|
||||
if targetType.kind in {tySink, tyLent}:
|
||||
@@ -298,57 +316,98 @@ proc semSizeof(c: PContext, n: PNode): PNode =
|
||||
n.typ = getSysType(c.graph, n.info, tyInt)
|
||||
result = n
|
||||
|
||||
proc fixupStaticType(c: PContext, n: PNode) =
|
||||
# This proc can be applied to evaluated expressions to assign
|
||||
# them a static type.
|
||||
#
|
||||
# XXX: with implicit static, this should not be necessary,
|
||||
# because the output type of operations such as `semConstExpr`
|
||||
# should be a static type (as well as the type of any other
|
||||
# expression that can be implicitly evaluated). For now, we
|
||||
# apply this measure only in code that is enlightened to work
|
||||
# with static types.
|
||||
if n.typ.kind != tyStatic:
|
||||
n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ])
|
||||
n.typ.n = n # XXX: cycles like the one here look dangerous.
|
||||
# Consider using `n.copyTree`
|
||||
|
||||
proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
internalAssert c.config, n.sonsLen == 3 and
|
||||
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
|
||||
internalAssert c.config,
|
||||
n.sonsLen == 3 and
|
||||
n[1].typ != nil and
|
||||
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
|
||||
|
||||
let t1 = n[1].typ.skipTypes({tyTypeDesc})
|
||||
var
|
||||
res = false
|
||||
t1 = n[1].typ
|
||||
t2 = n[2].typ
|
||||
|
||||
if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc:
|
||||
t1 = t1.base
|
||||
|
||||
if n[2].kind in {nkStrLit..nkTripleStrLit}:
|
||||
case n[2].strVal.normalize
|
||||
of "closure":
|
||||
let t = skipTypes(t1, abstractRange)
|
||||
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
||||
t.callConv == ccClosure and
|
||||
tfIterator notin t.flags))
|
||||
res = t.kind == tyProc and
|
||||
t.callConv == ccClosure and
|
||||
tfIterator notin t.flags
|
||||
else:
|
||||
result = newIntNode(nkIntLit, 0)
|
||||
res = false
|
||||
else:
|
||||
var rhsOrigType = n[2].typ
|
||||
var t2 = rhsOrigType.skipTypes({tyTypeDesc})
|
||||
maybeLiftType(t2, c, n.info)
|
||||
var m: TCandidate
|
||||
initCandidate(c, m, t2)
|
||||
if efExplain in flags:
|
||||
m.diagnostics = @[]
|
||||
m.diagnosticsEnabled = true
|
||||
let match = typeRel(m, t2, t1) >= isSubtype # isNone
|
||||
result = newIntNode(nkIntLit, ord(match))
|
||||
res = typeRel(m, t2, t1) >= isSubtype # isNone
|
||||
|
||||
result = newIntNode(nkIntLit, ord(res))
|
||||
result.typ = n.typ
|
||||
|
||||
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
if sonsLen(n) != 3:
|
||||
localError(c.config, n.info, "'is' operator takes 2 arguments")
|
||||
|
||||
let boolType = getSysType(c.graph, n.info, tyBool)
|
||||
result = n
|
||||
n.typ = getSysType(c.graph, n.info, tyBool)
|
||||
n.typ = boolType
|
||||
var liftLhs = true
|
||||
|
||||
n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
|
||||
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
|
||||
let t2 = semTypeNode(c, n[2], nil)
|
||||
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
|
||||
if t2.kind == tyStatic:
|
||||
let evaluated = tryConstExpr(c, n[1])
|
||||
if evaluated != nil:
|
||||
c.fixupStaticType(evaluated)
|
||||
n[1] = evaluated
|
||||
else:
|
||||
result = newIntNode(nkIntLit, 0)
|
||||
result.typ = boolType
|
||||
return
|
||||
elif t2.kind == tyTypeDesc and
|
||||
(t2.base.kind == tyNone or tfExplicit in t2.flags):
|
||||
# When the right-hand side is an explicit type, we must
|
||||
# not allow regular values to be matched against the type:
|
||||
liftLhs = false
|
||||
|
||||
let lhsType = n[1].typ
|
||||
var lhsType = n[1].typ
|
||||
if lhsType.kind != tyTypeDesc:
|
||||
n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
||||
elif lhsType.base.kind == tyNone:
|
||||
# this is a typedesc variable, leave for evals
|
||||
return
|
||||
if liftLhs:
|
||||
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
||||
lhsType = n[1].typ
|
||||
else:
|
||||
if lhsType.base.kind == tyNone:
|
||||
# this is a typedesc variable, leave for evals
|
||||
return
|
||||
if lhsType.base.containsGenericType:
|
||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||
return
|
||||
|
||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||
if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags)
|
||||
result = isOpImpl(c, n, flags)
|
||||
|
||||
proc semOpAux(c: PContext, n: PNode) =
|
||||
const flags = {efDetermineType}
|
||||
@@ -483,6 +542,36 @@ proc fixAbstractType(c: PContext, n: PNode) =
|
||||
proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
|
||||
result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
|
||||
|
||||
proc isUnresolvedSym(s: PSym): bool =
|
||||
return s.kind == skGenericParam or
|
||||
tfInferrableStatic in s.typ.flags or
|
||||
(s.kind == skParam and s.typ.isMetaType) or
|
||||
(s.kind == skType and
|
||||
s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
|
||||
|
||||
proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
|
||||
# Checks whether an expression depends on generic parameters that
|
||||
# don't have bound values yet. E.g. this could happen in situations
|
||||
# such as:
|
||||
# type Slot[T] = array[T.size, byte]
|
||||
# proc foo[T](x: default(T))
|
||||
#
|
||||
# Both static parameter and type parameters can be unresolved.
|
||||
case n.kind
|
||||
of nkSym:
|
||||
return isUnresolvedSym(n.sym)
|
||||
of nkIdent, nkAccQuoted:
|
||||
let ident = considerQuotedIdent(c, n)
|
||||
let sym = searchInScopes(c, ident)
|
||||
if sym != nil:
|
||||
return isUnresolvedSym(sym)
|
||||
else:
|
||||
return false
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
if hasUnresolvedArgs(c, n.sons[i]): return true
|
||||
return false
|
||||
|
||||
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
||||
if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or
|
||||
sfCompileToCpp in c.module.flags):
|
||||
@@ -632,7 +721,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
# echo "SUCCESS evaluated at compile time: ", call.renderTree
|
||||
|
||||
proc semStaticExpr(c: PContext, n: PNode): PNode =
|
||||
let a = semExpr(c, n.sons[0])
|
||||
let a = semExpr(c, n)
|
||||
if a.findUnresolvedStatic != nil: return a
|
||||
result = evalStaticExpr(c.module, c.graph, a, c.p.owner)
|
||||
if result.isNil:
|
||||
@@ -768,6 +857,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
else:
|
||||
result = m.call
|
||||
instGenericConvertersSons(c, result, m)
|
||||
|
||||
elif t != nil and t.kind == tyTypeDesc:
|
||||
if n.len == 1: return semObjConstr(c, n, flags)
|
||||
return semConv(c, n)
|
||||
@@ -1034,7 +1124,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||
of skType:
|
||||
markUsed(c.config, n.info, s, c.graph.usageSym)
|
||||
styleCheckUse(n.info, s)
|
||||
if s.typ.kind == tyStatic and s.typ.n != nil:
|
||||
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
|
||||
return s.typ.n
|
||||
result = newSymNode(s, n.info)
|
||||
result.typ = makeTypeDesc(c, s.typ)
|
||||
@@ -1261,8 +1351,18 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
# make sure we don't evaluate generic macros/templates
|
||||
n.sons[0] = semExprWithType(c, n.sons[0],
|
||||
{efNoEvaluateGeneric})
|
||||
let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst,
|
||||
var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst,
|
||||
tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
|
||||
if arr.kind == tyStatic:
|
||||
if arr.base.kind == tyNone:
|
||||
result = n
|
||||
result.typ = semStaticType(c, n[1], nil)
|
||||
return
|
||||
elif arr.n != nil:
|
||||
return semSubscript(c, arr.n, flags)
|
||||
else:
|
||||
arr = arr.base
|
||||
|
||||
case arr.kind
|
||||
of tyArray, tyOpenArray, tyVarargs, tySequence, tyString,
|
||||
tyCString:
|
||||
@@ -2426,8 +2526,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
# handling of sym choices is context dependent
|
||||
# the node is left intact for now
|
||||
discard
|
||||
of nkStaticExpr:
|
||||
result = semStaticExpr(c, n)
|
||||
of nkStaticExpr: result = semStaticExpr(c, n[0])
|
||||
of nkAsgn: result = semAsgn(c, n)
|
||||
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
|
||||
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
|
||||
|
||||
@@ -32,9 +32,12 @@ type
|
||||
cursorInBody: bool # only for nimsuggest
|
||||
bracketExpr: PNode
|
||||
|
||||
type
|
||||
TSemGenericFlag = enum
|
||||
withinBind, withinTypeDesc, withinMixin, withinConcept
|
||||
withinBind,
|
||||
withinTypeDesc,
|
||||
withinMixin,
|
||||
withinConcept
|
||||
|
||||
TSemGenericFlags = set[TSemGenericFlag]
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode,
|
||||
@@ -53,8 +56,15 @@ template macroToExpand(s): untyped =
|
||||
template macroToExpandSym(s): untyped =
|
||||
s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr
|
||||
|
||||
template isMixedIn(sym): bool =
|
||||
let s = sym
|
||||
s.name.id in ctx.toMixin or (withinConcept in flags and
|
||||
s.magic == mNone and
|
||||
s.kind in OverloadableSyms)
|
||||
|
||||
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
ctx: var GenericCtx; fromDotExpr=false): PNode =
|
||||
ctx: var GenericCtx; flags: TSemGenericFlags,
|
||||
fromDotExpr=false): PNode =
|
||||
semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
|
||||
incl(s.flags, sfUsed)
|
||||
case s.kind
|
||||
@@ -115,10 +125,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
else:
|
||||
if withinBind in flags:
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
elif s.name.id in ctx.toMixin:
|
||||
elif s.isMixedIn:
|
||||
result = symChoice(c, n, s, scForceOpen)
|
||||
else:
|
||||
result = semGenericStmtSymbol(c, n, s, ctx)
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||
# else: leave as nkIdent
|
||||
|
||||
proc newDot(n, b: PNode): PNode =
|
||||
@@ -135,7 +145,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
|
||||
var s = qualifiedLookUp(c, n, luf)
|
||||
if s != nil:
|
||||
result = semGenericStmtSymbol(c, n, s, ctx)
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||
else:
|
||||
n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
|
||||
result = n
|
||||
@@ -146,10 +156,10 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
if withinBind in flags:
|
||||
result = newDot(result, symChoice(c, n, s, scClosed))
|
||||
elif s.name.id in ctx.toMixin:
|
||||
elif s.isMixedIn:
|
||||
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||
else:
|
||||
let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true)
|
||||
let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
|
||||
if syms.kind == nkSym:
|
||||
let choice = symChoice(c, n, s, scForceOpen)
|
||||
choice.kind = nkClosedSymChoice
|
||||
@@ -215,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
if s != nil:
|
||||
incl(s.flags, sfUsed)
|
||||
mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
|
||||
let sc = symChoice(c, fn, s,
|
||||
if s.name.id in ctx.toMixin: scForceOpen else: scOpen)
|
||||
let sc = symChoice(c, fn, s, if s.isMixedIn: scForceOpen else: scOpen)
|
||||
case s.kind
|
||||
of skMacro:
|
||||
if macroToExpand(s) and sc.safeLen <= 1:
|
||||
@@ -472,7 +481,6 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
when defined(nimsuggest):
|
||||
if withinTypeDesc in flags: dec c.inTypeContext
|
||||
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode): PNode =
|
||||
var ctx: GenericCtx
|
||||
ctx.toMixin = initIntset()
|
||||
@@ -484,3 +492,4 @@ proc semConceptBody(c: PContext, n: PNode): PNode =
|
||||
ctx.toMixin = initIntset()
|
||||
result = semGenericStmt(c, n, {withinConcept}, ctx)
|
||||
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
|
||||
|
||||
|
||||
@@ -220,6 +220,14 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
|
||||
result = replaceTypeVarsT(cl, header)
|
||||
closeScope(c)
|
||||
|
||||
proc referencesAnotherParam(n: PNode, p: PSym): bool =
|
||||
if n.kind == nkSym:
|
||||
return n.sym.kind == skParam and n.sym.owner == p
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
if referencesAnotherParam(n[i], p): return true
|
||||
return false
|
||||
|
||||
proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
prc: PSym, info: TLineInfo) =
|
||||
# XXX: Instantiates a generic proc signature, while at the same
|
||||
@@ -247,25 +255,56 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
if i > 1:
|
||||
resetIdTable(cl.symMap)
|
||||
resetIdTable(cl.localCache)
|
||||
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
|
||||
propagateToOwner(result, result.sons[i])
|
||||
internalAssert c.config, originalParams[i].kind == nkSym
|
||||
when true:
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam)
|
||||
param.owner = prc
|
||||
param.typ = result.sons[i]
|
||||
if oldParam.ast != nil:
|
||||
param.ast = fitNode(c, param.typ, oldParam.ast, oldParam.ast.info)
|
||||
|
||||
# don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
|
||||
result.n.sons[i] = newSymNode(param)
|
||||
addDecl(c, param)
|
||||
else:
|
||||
let param = replaceTypeVarsN(cl, originalParams[i])
|
||||
result.n.sons[i] = param
|
||||
param.sym.owner = prc
|
||||
addDecl(c, result.n.sons[i].sym)
|
||||
# take a note of the original type. If't a free type or static parameter
|
||||
# we'll need to keep it unbound for the `fitNode` operation below...
|
||||
var typeToFit = result[i]
|
||||
|
||||
let needsStaticSkipping = result[i].kind == tyFromExpr
|
||||
result[i] = replaceTypeVarsT(cl, result[i])
|
||||
if needsStaticSkipping:
|
||||
result[i] = result[i].skipTypes({tyStatic})
|
||||
|
||||
# ...otherwise, we use the instantiated type in `fitNode`
|
||||
if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and
|
||||
(typeToFit.kind != tyStatic):
|
||||
typeToFit = result[i]
|
||||
|
||||
internalAssert c.config, originalParams[i].kind == nkSym
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam)
|
||||
param.owner = prc
|
||||
param.typ = result[i]
|
||||
|
||||
# The default value is instantiated and fitted against the final
|
||||
# concrete param type. We avoid calling `replaceTypeVarsN` on the
|
||||
# call head symbol, because this leads to infinite recursion.
|
||||
if oldParam.ast != nil:
|
||||
var def = oldParam.ast.copyTree
|
||||
if def.kind == nkCall:
|
||||
for i in 1 ..< def.len:
|
||||
def[i] = replaceTypeVarsN(cl, def[i])
|
||||
|
||||
def = semExprWithType(c, def)
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
def.flags.incl nfDefaultRefsParam
|
||||
|
||||
var converted = indexTypesMatch(c, typeToFit, def.typ, def)
|
||||
if converted == nil:
|
||||
# The default value doesn't match the final instantiated type.
|
||||
# As an example of this, see:
|
||||
# https://github.com/nim-lang/Nim/issues/1201
|
||||
# We are replacing the default value with an error node in case
|
||||
# the user calls an explicit instantiation of the proc (this is
|
||||
# the only way the default value might be inserted).
|
||||
param.ast = errorNode(c, def)
|
||||
else:
|
||||
param.ast = fitNodePostMatch(c, typeToFit, converted)
|
||||
param.typ = result[i]
|
||||
|
||||
result.n[i] = newSymNode(param)
|
||||
propagateToOwner(result, result[i])
|
||||
addDecl(c, param)
|
||||
|
||||
resetIdTable(cl.symMap)
|
||||
resetIdTable(cl.localCache)
|
||||
|
||||
@@ -37,6 +37,12 @@ const
|
||||
errNoGenericParamsAllowedForX = "no generic parameters allowed for $1"
|
||||
errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types"
|
||||
|
||||
const
|
||||
mStaticTy = {mStatic}
|
||||
mTypeTy = {mType, mTypeOf}
|
||||
# XXX: This should be needed only temporarily until the C
|
||||
# sources are rebuilt
|
||||
|
||||
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
|
||||
if prev == nil:
|
||||
result = newTypeS(kind, c)
|
||||
@@ -238,7 +244,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
|
||||
|
||||
for i in 0..1:
|
||||
if hasGenericArguments(range[i]):
|
||||
if hasUnresolvedArgs(c, range[i]):
|
||||
result.n.addSon makeStaticExpr(c, range[i])
|
||||
result.flags.incl tfUnresolved
|
||||
else:
|
||||
@@ -295,7 +301,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
localError(c.config, info, errOrdinalTypeExpected)
|
||||
result = makeRangeWithStaticExpr(c, e)
|
||||
if c.inGenericContext > 0: result.flags.incl tfUnresolved
|
||||
elif e.kind in nkCallKinds and hasGenericArguments(e):
|
||||
elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e):
|
||||
if not isOrdinalType(e.typ):
|
||||
localError(c.config, n[1].info, errOrdinalTypeExpected)
|
||||
# This is an int returning call, depending on an
|
||||
@@ -363,6 +369,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
if result != nil:
|
||||
markUsed(c.config, n.info, result, c.graph.usageSym)
|
||||
styleCheckUse(n.info, result)
|
||||
|
||||
if result.kind == skParam and result.typ.kind == tyTypeDesc:
|
||||
# This is a typedesc param. is it already bound?
|
||||
# it's not bound when it's used multiple times in the
|
||||
@@ -388,8 +395,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
else:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
return errorSym(c, n)
|
||||
|
||||
if result.kind != skType:
|
||||
if result.kind != skType and result.magic notin (mStaticTy + mTypeTy):
|
||||
# this implements the wanted ``var v: V, x: V`` feature ...
|
||||
var ov: TOverloadIter
|
||||
var amb = initOverloadIter(ov, c, n)
|
||||
@@ -856,9 +862,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil)
|
||||
|
||||
of tyStatic:
|
||||
# proc(a: expr{string}, b: expr{nkLambda})
|
||||
# overload on compile time values and AST trees
|
||||
if paramType.n != nil: return # this is a concrete type
|
||||
if paramType.base.kind != tyNone and paramType.n != nil:
|
||||
# this is a concrete static value
|
||||
return
|
||||
if tfUnresolved in paramType.flags: return # already lifted
|
||||
let base = paramType.base.maybeLift
|
||||
if base.isMetaType and procKind == skMacro:
|
||||
@@ -879,7 +885,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
of tyDistinct:
|
||||
if paramType.sonsLen == 1:
|
||||
# disable the bindOnce behavior for the type class
|
||||
result = liftingWalk(paramType.sons[0], true)
|
||||
result = liftingWalk(paramType.base, true)
|
||||
|
||||
of tyAlias:
|
||||
result = liftingWalk(paramType.base)
|
||||
|
||||
of tySequence, tySet, tyArray, tyOpenArray,
|
||||
tyVar, tyLent, tyPtr, tyRef, tyProc:
|
||||
@@ -996,13 +1005,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
prev: PType, kind: TSymKind; isType=false): PType =
|
||||
# for historical reasons (code grows) this is invoked for parameter
|
||||
# lists too and then 'isType' is false.
|
||||
var cl: IntSet
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
result = newProcType(c, n.info, prev)
|
||||
if genericParams != nil and sonsLen(genericParams) == 0:
|
||||
cl = initIntSet()
|
||||
var check = initIntSet()
|
||||
var counter = 0
|
||||
|
||||
for i in countup(1, n.len - 1):
|
||||
var a = n.sons[i]
|
||||
if a.kind != nkIdentDefs:
|
||||
@@ -1012,6 +1019,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
# pass over this instantiation:
|
||||
if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
|
||||
illFormedAst(a, c.config)
|
||||
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
var
|
||||
typ: PType = nil
|
||||
@@ -1020,26 +1028,52 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
length = sonsLen(a)
|
||||
hasType = a.sons[length-2].kind != nkEmpty
|
||||
hasDefault = a.sons[length-1].kind != nkEmpty
|
||||
|
||||
if hasType:
|
||||
typ = semParamType(c, a.sons[length-2], constraint)
|
||||
|
||||
if hasDefault:
|
||||
def = semExprWithType(c, a.sons[length-1])
|
||||
# check type compatibility between def.typ and typ:
|
||||
def = a[^1]
|
||||
block determineType:
|
||||
if genericParams != nil and genericParams.len > 0:
|
||||
def = semGenericStmt(c, def)
|
||||
if hasUnresolvedArgs(c, def):
|
||||
def.typ = makeTypeFromExpr(c, def.copyTree)
|
||||
break determineType
|
||||
|
||||
def = semExprWithType(c, def, {efDetermineType})
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
def.flags.incl nfDefaultRefsParam
|
||||
|
||||
if typ == nil:
|
||||
typ = def.typ
|
||||
elif def != nil:
|
||||
# and def.typ != nil and def.typ.kind != tyNone:
|
||||
if typ.kind == tyTypeDesc:
|
||||
# consider a proc such as:
|
||||
# proc takesType(T = int)
|
||||
# a naive analysis may conclude that the proc type is type[int]
|
||||
# which will prevent other types from matching - clearly a very
|
||||
# surprising behavior. We must instead fix the expected type of
|
||||
# the proc to be the unbound typedesc type:
|
||||
typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
|
||||
|
||||
else:
|
||||
# if def.typ != nil and def.typ.kind != tyNone:
|
||||
# example code that triggers it:
|
||||
# proc sort[T](cmp: proc(a, b: T): int = cmp)
|
||||
if not containsGenericType(typ):
|
||||
# check type compatibility between def.typ and typ:
|
||||
def = fitNode(c, typ, def, def.info)
|
||||
elif typ.kind == tyStatic:
|
||||
def = semConstExpr(c, def)
|
||||
def = fitNode(c, typ, def, def.info)
|
||||
|
||||
if not hasType and not hasDefault:
|
||||
if isType: localError(c.config, a.info, "':' expected")
|
||||
if kind in {skTemplate, skMacro}:
|
||||
typ = newTypeS(tyExpr, c)
|
||||
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
|
||||
continue
|
||||
|
||||
for j in countup(0, length-3):
|
||||
var arg = newSymG(skParam, a.sons[j], c)
|
||||
if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
|
||||
@@ -1055,7 +1089,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
arg.position = counter
|
||||
arg.constraint = constraint
|
||||
inc(counter)
|
||||
if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
|
||||
if def != nil and def.kind != nkEmpty:
|
||||
arg.ast = copyTree(def)
|
||||
if containsOrIncl(check, arg.name.id):
|
||||
localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'")
|
||||
addSon(result.n, newSymNode(arg))
|
||||
@@ -1310,7 +1345,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
incl dummyParam.flags, sfUsed
|
||||
addDecl(c, dummyParam)
|
||||
|
||||
result.n.sons[3] = semConceptBody(c, n[3])
|
||||
result.n[3] = semConceptBody(c, n[3])
|
||||
closeScope(c)
|
||||
|
||||
proc semProcTypeWithScope(c: PContext, n: PNode,
|
||||
@@ -1349,6 +1384,12 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
result = errorSym(c, n)
|
||||
|
||||
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
|
||||
result = newOrPrevType(tyStatic, prev, c)
|
||||
var base = semTypeNode(c, childNode, nil).skipTypes({tyTypeDesc, tyAlias})
|
||||
result.rawAddSon(base)
|
||||
result.flags.incl tfHasStatic
|
||||
|
||||
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = nil
|
||||
inc c.inTypeContext
|
||||
@@ -1456,7 +1497,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
||||
of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
|
||||
of mVarargs: result = semVarargs(c, n, prev)
|
||||
of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
||||
of mTypeDesc, mTypeTy:
|
||||
result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
||||
result.flags.incl tfExplicit
|
||||
of mStaticTy:
|
||||
result = semStaticType(c, n[1], prev)
|
||||
of mExpr:
|
||||
result = semTypeNode(c, n.sons[0], nil)
|
||||
if result != nil:
|
||||
@@ -1545,11 +1590,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
|
||||
of nkVarTy: result = semVarType(c, n, prev)
|
||||
of nkDistinctTy: result = semDistinct(c, n, prev)
|
||||
of nkStaticTy:
|
||||
result = newOrPrevType(tyStatic, prev, c)
|
||||
var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
|
||||
result.rawAddSon(base)
|
||||
result.flags.incl tfHasStatic
|
||||
of nkStaticTy: result = semStaticType(c, n[0], prev)
|
||||
of nkIteratorTy:
|
||||
if n.sonsLen == 0:
|
||||
result = newTypeS(tyBuiltInTypeClass, c)
|
||||
@@ -1645,9 +1686,12 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
of mStmt:
|
||||
setMagicType(c.config, m, tyStmt, 0)
|
||||
if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt
|
||||
of mTypeDesc:
|
||||
of mTypeDesc, mType:
|
||||
setMagicType(c.config, m, tyTypeDesc, 0)
|
||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||
of mStatic:
|
||||
setMagicType(c.config, m, tyStatic, 0)
|
||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||
of mVoidType:
|
||||
setMagicType(c.config, m, tyVoid, 0)
|
||||
of mArray:
|
||||
|
||||
@@ -144,17 +144,6 @@ proc isTypeParam(n: PNode): bool =
|
||||
(n.sym.kind == skGenericParam or
|
||||
(n.sym.kind == skType and sfFromGeneric in n.sym.flags))
|
||||
|
||||
proc hasGenericArguments*(n: PNode): bool =
|
||||
if n.kind == nkSym:
|
||||
return n.sym.kind == skGenericParam or
|
||||
tfInferrableStatic in n.sym.typ.flags or
|
||||
(n.sym.kind == skType and
|
||||
n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
if hasGenericArguments(n.sons[i]): return true
|
||||
return false
|
||||
|
||||
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
# This is needed for tgenericshardcases
|
||||
# It's possible that a generic param will be used in a proc call to a
|
||||
@@ -231,6 +220,17 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
|
||||
# symbol is not our business:
|
||||
if cl.owner != nil and s.owner != cl.owner:
|
||||
return s
|
||||
|
||||
# XXX: Bound symbols in default parameter expressions may reach here.
|
||||
# We cannot process them, becase `sym.n` may point to a proc body with
|
||||
# cyclic references that will lead to an infinite recursion.
|
||||
# Perhaps we should not use a black-list here, but a whitelist instead
|
||||
# (e.g. skGenericParam and skType).
|
||||
# Note: `s.magic` may be `mType` in an example such as:
|
||||
# proc foo[T](a: T, b = myDefault(type(a)))
|
||||
if s.kind == skProc or s.magic != mNone:
|
||||
return s
|
||||
|
||||
#result = PSym(idTableGet(cl.symMap, s))
|
||||
#if result == nil:
|
||||
result = copySym(s, false)
|
||||
@@ -278,7 +278,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
# is difficult to handle:
|
||||
const eqFlags = eqTypeFlags + {tfGcSafe}
|
||||
var body = t.sons[0]
|
||||
if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body")
|
||||
if body.kind != tyGenericBody:
|
||||
internalError(cl.c.config, cl.info, "no generic body")
|
||||
var header: PType = t
|
||||
# search for some instantiation here:
|
||||
if cl.allowMetaTypes:
|
||||
|
||||
@@ -1089,8 +1089,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||
else: isNone
|
||||
|
||||
of tyAnything:
|
||||
return if f.kind == tyAnything: isGeneric
|
||||
else: isNone
|
||||
if f.kind == tyAnything: return isGeneric
|
||||
else: return isNone
|
||||
|
||||
of tyUserTypeClass, tyUserTypeClassInst:
|
||||
if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4:
|
||||
@@ -1666,13 +1666,17 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||
let prev = PType(idTableGet(c.bindings, f))
|
||||
if prev == nil:
|
||||
if aOrig.kind == tyStatic:
|
||||
result = typeRel(c, f.lastSon, a)
|
||||
if result != isNone and f.n != nil:
|
||||
if not exprStructuralEquivalent(f.n, aOrig.n):
|
||||
result = isNone
|
||||
if f.base.kind != tyNone:
|
||||
result = typeRel(c, f.base, a)
|
||||
if result != isNone and f.n != nil:
|
||||
if not exprStructuralEquivalent(f.n, aOrig.n):
|
||||
result = isNone
|
||||
else:
|
||||
result = isGeneric
|
||||
if result != isNone: put(c, f, aOrig)
|
||||
elif aOrig.n != nil and aOrig.n.typ != nil:
|
||||
result = typeRel(c, f.lastSon, aOrig.n.typ)
|
||||
result = if f.base.kind != tyNone: typeRel(c, f.lastSon, aOrig.n.typ)
|
||||
else: isGeneric
|
||||
if result != isNone:
|
||||
var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])
|
||||
boundType.n = aOrig.n
|
||||
@@ -1707,9 +1711,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||
# proc foo(T: typedesc, x: T)
|
||||
# when `f` is an unresolved typedesc, `a` could be any
|
||||
# type, so we should not perform this check earlier
|
||||
if a.kind != tyTypeDesc: return isNone
|
||||
|
||||
if f.base.kind == tyNone:
|
||||
if a.kind != tyTypeDesc:
|
||||
if a.kind == tyGenericParam and tfWildcard in a.flags:
|
||||
# TODO: prevent `a` from matching as a wildcard again
|
||||
result = isGeneric
|
||||
else:
|
||||
result = isNone
|
||||
elif f.base.kind == tyNone:
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(c, f.base, a.base)
|
||||
@@ -2353,12 +2361,22 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
|
||||
m.firstMismatch = f
|
||||
break
|
||||
else:
|
||||
# use default value:
|
||||
if formal.ast.kind == nkEmpty:
|
||||
# The default param value is set to empty in `instantiateProcType`
|
||||
# when the type of the default expression doesn't match the type
|
||||
# of the instantiated proc param:
|
||||
localError(c.config, m.call.info,
|
||||
("The default parameter '$1' has incompatible type " &
|
||||
"with the explicitly requested proc instantiation") %
|
||||
formal.name.s)
|
||||
if nfDefaultRefsParam in formal.ast.flags:
|
||||
m.call.flags.incl nfDefaultRefsParam
|
||||
var def = copyTree(formal.ast)
|
||||
if def.kind == nkNilLit:
|
||||
def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
|
||||
if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
|
||||
put(m, formal.typ, def.typ)
|
||||
def.flags.incl nfDefaultParam
|
||||
setSon(m.call, formal.position + 1, def)
|
||||
inc(f)
|
||||
# forget all inferred types if the overload matching failed
|
||||
|
||||
@@ -780,6 +780,43 @@ proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc hoistParamsUsedInDefault(c: PTransf, call, letSection, defExpr: PNode): PNode =
|
||||
# This takes care of complicated signatures such as:
|
||||
# proc foo(a: int, b = a)
|
||||
# proc bar(a: int, b: int, c = a + b)
|
||||
#
|
||||
# The recursion may confuse you. It performs two duties:
|
||||
#
|
||||
# 1) extracting all referenced params from default expressions
|
||||
# into a let section preceeding the call
|
||||
#
|
||||
# 2) replacing the "references" within the default expression
|
||||
# with these extracted skLet symbols.
|
||||
#
|
||||
# The first duty is carried out directly in the code here, while the second
|
||||
# duty is activated by returning a non-nil value. The caller is responsible
|
||||
# for replacing the input to the function with the returned non-nil value.
|
||||
# (which is the hoisted symbol)
|
||||
if defExpr.kind == nkSym:
|
||||
if defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
|
||||
let paramPos = defExpr.sym.position + 1
|
||||
|
||||
if call[paramPos].kind == nkSym and sfHoisted in call[paramPos].sym.flags:
|
||||
# Already hoisted, we still need to return it in order to replace the
|
||||
# placeholder expression in the default value.
|
||||
return call[paramPos]
|
||||
|
||||
let hoistedVarSym = hoistExpr(letSection,
|
||||
call[paramPos],
|
||||
getIdent(c.graph.cache, genPrefix),
|
||||
c.transCon.owner).newSymNode
|
||||
call[paramPos] = hoistedVarSym
|
||||
return hoistedVarSym
|
||||
else:
|
||||
for i in 0..<defExpr.safeLen:
|
||||
let hoisted = hoistParamsUsedInDefault(c, call, letSection, defExpr[i])
|
||||
if hoisted != nil: defExpr[i] = hoisted
|
||||
|
||||
proc transform(c: PTransf, n: PNode): PTransNode =
|
||||
when false:
|
||||
var oldDeferAnchor: PNode
|
||||
@@ -849,6 +886,15 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||
of nkBreakStmt: result = transformBreak(c, n)
|
||||
of nkCallKinds:
|
||||
result = transformCall(c, n)
|
||||
var call = result.PNode
|
||||
if nfDefaultRefsParam in call.flags:
|
||||
# We've found a default value that references another param.
|
||||
# See the notes in `hoistParamsUsedInDefault` for more details.
|
||||
var hoistedParams = newNodeI(nkLetSection, call.info, 0)
|
||||
for i in 1 ..< call.len:
|
||||
let hoisted = hoistParamsUsedInDefault(c, call, hoistedParams, call[i])
|
||||
if hoisted != nil: call[i] = hoisted
|
||||
result = newTree(nkStmtListExpr, hoistedParams, call).PTransNode
|
||||
of nkAddr, nkHiddenAddr:
|
||||
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
|
||||
of nkDerefExpr, nkHiddenDeref:
|
||||
|
||||
@@ -502,7 +502,10 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
#internalAssert t.len == 0
|
||||
result = "untyped"
|
||||
of tyFromExpr:
|
||||
result = renderTree(t.n)
|
||||
if t.n == nil:
|
||||
result = "unknown"
|
||||
else:
|
||||
result = "type(" & renderTree(t.n) & ")"
|
||||
of tyArray:
|
||||
if t.sons[0].kind == tyRange:
|
||||
result = "array[" & rangeToStr(t.sons[0].n) & ", " &
|
||||
|
||||
@@ -303,9 +303,9 @@ symbols in the `system module <system.html>`_.
|
||||
`#len,seq[T] <system.html#len,seq[T]>`_
|
||||
* ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>**
|
||||
`#pairs.i,seq[T] <system.html#pairs.i,seq[T]>`_
|
||||
* ``template newException[](exceptn: typedesc; message: string): expr`` **=>**
|
||||
`#newException.t,typedesc,string
|
||||
<system.html#newException.t,typedesc,string>`_
|
||||
* ``template newException[](exceptn: type; message: string): expr`` **=>**
|
||||
`#newException.t,type,string
|
||||
<system.html#newException.t,type,string>`_
|
||||
|
||||
|
||||
Index (idx) file format
|
||||
|
||||
156
doc/manual.rst
156
doc/manual.rst
@@ -1732,7 +1732,7 @@ But it seems all this boilerplate code needs to be repeated for the ``Euro``
|
||||
currency. This can be solved with templates_.
|
||||
|
||||
.. code-block:: nim
|
||||
template additive(typ: typedesc) =
|
||||
template additive(typ: type) =
|
||||
proc `+` *(x, y: typ): typ {.borrow.}
|
||||
proc `-` *(x, y: typ): typ {.borrow.}
|
||||
|
||||
@@ -1740,13 +1740,13 @@ currency. This can be solved with templates_.
|
||||
proc `+` *(x: typ): typ {.borrow.}
|
||||
proc `-` *(x: typ): typ {.borrow.}
|
||||
|
||||
template multiplicative(typ, base: typedesc) =
|
||||
template multiplicative(typ, base: type) =
|
||||
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) =
|
||||
template comparable(typ: type) =
|
||||
proc `<` * (x, y: typ): bool {.borrow.}
|
||||
proc `<=` * (x, y: typ): bool {.borrow.}
|
||||
proc `==` * (x, y: typ): bool {.borrow.}
|
||||
@@ -2396,7 +2396,7 @@ argument's resolution:
|
||||
rem unresolvedExpression(undeclaredIdentifier)
|
||||
|
||||
``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other
|
||||
metatypes ``typed`` and ``typedesc`` are not lazy.
|
||||
metatypes ``typed`` and ``type`` are not lazy.
|
||||
|
||||
|
||||
Varargs matching
|
||||
@@ -4274,29 +4274,6 @@ therefore very useful for type specialization within generic code:
|
||||
deletedKeys: seq[bool]
|
||||
|
||||
|
||||
Type operator
|
||||
-------------
|
||||
|
||||
The ``type`` (in many other languages called `typeof`:idx:) operator can
|
||||
be used to get the type of an expression:
|
||||
|
||||
.. code-block:: nim
|
||||
var x = 0
|
||||
var y: type(x) # y has type int
|
||||
|
||||
If ``type`` is used to determine the result type of a proc/iterator/converter
|
||||
call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the
|
||||
interpretation where ``c`` is an iterator is preferred over the
|
||||
other interpretations:
|
||||
|
||||
.. code-block:: nim
|
||||
import strutils
|
||||
|
||||
# strutils contains both a ``split`` proc and iterator, but since an
|
||||
# an iterator is the preferred interpretation, `y` has the type ``string``:
|
||||
var y: type("a b c".split)
|
||||
|
||||
|
||||
Type Classes
|
||||
------------
|
||||
|
||||
@@ -4450,10 +4427,10 @@ the presence of callable symbols with specific signatures:
|
||||
OutputStream = concept var s
|
||||
s.write(string)
|
||||
|
||||
In order to check for symbols accepting ``typedesc`` params, you must prefix
|
||||
the type with an explicit ``type`` modifier. The named instance of the type,
|
||||
following the ``concept`` keyword is also considered an explicit ``typedesc``
|
||||
value that will be matched only as a type.
|
||||
In order to check for symbols accepting ``type`` params, you must prefix
|
||||
the type with the explicit ``type`` modifier. The named instance of the
|
||||
type, following the ``concept`` keyword is also considered to have the
|
||||
explicit modifier and will be matched only as a type.
|
||||
|
||||
.. code-block:: nim
|
||||
type
|
||||
@@ -4513,7 +4490,7 @@ The concept types can be parametric just like the regular generic types:
|
||||
import typetraits
|
||||
|
||||
type
|
||||
AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
|
||||
AnyMatrix*[R, C: static int; T] = concept m, var mvar, type M
|
||||
M.ValueType is T
|
||||
M.Rows == R
|
||||
M.Cols == C
|
||||
@@ -4523,7 +4500,7 @@ The concept types can be parametric just like the regular generic types:
|
||||
|
||||
type TransposedType = stripGenericParams(M)[C, R, T]
|
||||
|
||||
AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T]
|
||||
AnySquareMatrix*[N: static int, T] = AnyMatrix[N, N, T]
|
||||
|
||||
AnyTransform3D* = AnyMatrix[4, 4, float]
|
||||
|
||||
@@ -4542,7 +4519,7 @@ The concept types can be parametric just like the regular generic types:
|
||||
### matrix.nim
|
||||
|
||||
type
|
||||
Matrix*[M, N: static[int]; T] = object
|
||||
Matrix*[M, N: static int; T] = object
|
||||
data: array[M*N, T]
|
||||
|
||||
proc `[]`*(M: Matrix; m, n: int): M.T =
|
||||
@@ -4554,7 +4531,7 @@ The concept types can be parametric just like the regular generic types:
|
||||
# Adapt the Matrix type to the concept's requirements
|
||||
template Rows*(M: type Matrix): expr = M.M
|
||||
template Cols*(M: type Matrix): expr = M.N
|
||||
template ValueType*(M: type Matrix): typedesc = M.T
|
||||
template ValueType*(M: type Matrix): type = M.T
|
||||
|
||||
-------------
|
||||
### usage.nim
|
||||
@@ -4582,7 +4559,7 @@ operator and also when types dependent on them are being matched:
|
||||
|
||||
.. code-block:: nim
|
||||
type
|
||||
MatrixReducer[M, N: static[int]; T] = concept x
|
||||
MatrixReducer[M, N: static int; T] = concept x
|
||||
x.reduce(SquareMatrix[N, T]) is array[M, int]
|
||||
|
||||
The Nim compiler includes a simple linear equation solver, allowing it to
|
||||
@@ -4771,12 +4748,12 @@ object inheritance syntax involving the ``of`` keyword:
|
||||
|
||||
# the varargs param will here be converted to an array of StringRefValues
|
||||
# the proc will have only two instantiations for the two character types
|
||||
proc log(format: static[string], varargs[StringRef])
|
||||
proc log(format: static string, varargs[StringRef])
|
||||
|
||||
# this proc will allow char and wchar values to be mixed in
|
||||
# the same call at the cost of additional instantiations
|
||||
# the varargs param will be converted to a tuple
|
||||
proc log(format: static[string], varargs[distinct StringRef])
|
||||
proc log(format: static string, varargs[distinct StringRef])
|
||||
|
||||
|
||||
..
|
||||
@@ -4940,9 +4917,8 @@ templates:
|
||||
| ``notin`` and ``isnot`` have the obvious meanings.
|
||||
|
||||
The "types" of templates can be the symbols ``untyped``,
|
||||
``typed`` or ``typedesc`` (stands for *type
|
||||
description*). These are "meta types", they can only be used in certain
|
||||
contexts. Real types can be used too; this implies that ``typed`` expressions
|
||||
``typed`` or ``type``. These are "meta types", they can only be used in certain
|
||||
contexts. Regular types can be used too; this implies that ``typed`` expressions
|
||||
are expected.
|
||||
|
||||
|
||||
@@ -5109,7 +5085,7 @@ In templates identifiers can be constructed with the backticks notation:
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template typedef(name: untyped, typ: typedesc) =
|
||||
template typedef(name: untyped, typ: type) =
|
||||
type
|
||||
`T name`* {.inject.} = typ
|
||||
`P name`* {.inject.} = ref `T name`
|
||||
@@ -5171,7 +5147,7 @@ template cannot be accessed in the instantiation context:
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template newException*(exceptn: typedesc, message: string): untyped =
|
||||
template newException*(exceptn: type, message: string): untyped =
|
||||
var
|
||||
e: ref exceptn # e is implicitly gensym'ed here
|
||||
new(e)
|
||||
@@ -5493,7 +5469,7 @@ As their name suggests, static parameters must be known at compile-time:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc precompiledRegex(pattern: static[string]): RegEx =
|
||||
proc precompiledRegex(pattern: static string): RegEx =
|
||||
var res {.global.} = re(pattern)
|
||||
return res
|
||||
|
||||
@@ -5513,9 +5489,9 @@ Static params can also appear in the signatures of generic types:
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T]
|
||||
Matrix[M,N: static int; T: Number] = array[0..(M*N - 1), T]
|
||||
# Note how `Number` is just a type constraint here, while
|
||||
# `static[int]` requires us to supply a compile-time int value
|
||||
# `static int` requires us to supply a compile-time int value
|
||||
|
||||
AffineTransform2D[T] = Matrix[3, 3, T]
|
||||
AffineTransform3D[T] = Matrix[4, 4, T]
|
||||
@@ -5523,53 +5499,75 @@ Static params can also appear in the signatures of generic types:
|
||||
var m1: AffineTransform3D[float] # OK
|
||||
var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
|
||||
|
||||
Please note that ``static T`` is just a syntactic convenience for the
|
||||
underlying generic type ``static[T]``. The type param can be omitted
|
||||
to obtain the type class of all values known at compile-time. A more
|
||||
specific type class can be created by instantiating ``static`` with
|
||||
another type class.
|
||||
|
||||
typedesc
|
||||
--------
|
||||
You can force the evaluation of a certain expression at compile-time by
|
||||
coercing it to a corresponding ``static`` type:
|
||||
|
||||
`typedesc` is a special type allowing one to treat types as compile-time values
|
||||
(i.e. if types are compile-time values and all values have a type, then
|
||||
typedesc must be their type).
|
||||
.. code-block:: nim
|
||||
import math
|
||||
|
||||
When used as a regular proc param, typedesc acts as a type class. The proc
|
||||
will be instantiated for each unique type parameter and one can refer to the
|
||||
instantiation type using the param name:
|
||||
echo static(fac(5)), " ", static[bool](16.isPowerOfTwo)
|
||||
|
||||
The complier will report any failure to evaluate the expression or a
|
||||
possible type mismatch error.
|
||||
|
||||
type[T]
|
||||
-------
|
||||
|
||||
In many contexts, Nim allows you to treat the names of types as regular
|
||||
values. These values exists only during the compilation phase, but since
|
||||
all values must have a type, ``type`` is considered their special type.
|
||||
|
||||
``type`` acts like a generic type. For instance, the type of the symbol
|
||||
``int`` is ``type[int]``. Just like with regular generic types, when the
|
||||
generic param is ommited, ``type`` denotes the type class of all types.
|
||||
As a syntactic convenience, you can also use ``type`` as a modifier.
|
||||
``type int`` is considered the same as ``type[int]``.
|
||||
|
||||
Procs featuring ``type`` params are considered implicitly generic.
|
||||
They will be instantiated for each unique combination of supplied types
|
||||
and within the body of the proc, the name of each param will refer to
|
||||
the bound concrete type:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc new(T: typedesc): ref T =
|
||||
proc new(T: type): ref T =
|
||||
echo "allocating ", T.name
|
||||
new(result)
|
||||
|
||||
var n = Node.new
|
||||
var tree = new(BinaryTree[int])
|
||||
|
||||
When multiple typedesc params are present, they will bind freely to different
|
||||
types. To force a bind-once behavior
|
||||
one can use an explicit ``typedesc[T]`` generic param:
|
||||
When multiple type params are present, they will bind freely to different
|
||||
types. To force a bind-once behavior one can use an explicit generic param:
|
||||
|
||||
.. code-block:: nim
|
||||
proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U])
|
||||
proc acceptOnlyTypePairs[T, U](A, B: type[T]; C, D: type[U])
|
||||
|
||||
Once bound, typedesc params can appear in the rest of the proc signature:
|
||||
Once bound, type params can appear in the rest of the proc signature:
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template declareVariableWithType(T: typedesc, value: T) =
|
||||
template declareVariableWithType(T: type, value: T) =
|
||||
var x: T = value
|
||||
|
||||
declareVariableWithType int, 42
|
||||
|
||||
|
||||
Overload resolution can be further influenced by constraining the set of
|
||||
types that will match the typedesc param:
|
||||
types that will match the type param:
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template maxval(T: typedesc[int]): int = high(int)
|
||||
template maxval(T: typedesc[float]): float = Inf
|
||||
template maxval(T: type int): int = high(int)
|
||||
template maxval(T: type float): float = Inf
|
||||
|
||||
var i = int.maxval
|
||||
var f = float.maxval
|
||||
@@ -5578,7 +5576,35 @@ types that will match the typedesc param:
|
||||
|
||||
The constraint can be a concrete type or a type class.
|
||||
|
||||
type operator
|
||||
-------------
|
||||
|
||||
You can obtain the type of a given expression by constructing a ``type``
|
||||
value from it (in many other languages this is known as the `typeof`:idx:
|
||||
operator):
|
||||
|
||||
.. code-block:: nim
|
||||
var x = 0
|
||||
var y: type(x) # y has type int
|
||||
|
||||
You may add a constraint to the resulting type to trigger a compile-time error
|
||||
if the expression doesn't have the expected type:
|
||||
|
||||
.. code-block:: nim
|
||||
var x = 0
|
||||
var y: type[object](x) # Error: type mismatch: got <int> but expected 'object'
|
||||
|
||||
If ``type`` is used to determine the result type of a proc/iterator/converter
|
||||
call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the
|
||||
interpretation where ``c`` is an iterator is preferred over the
|
||||
other interpretations:
|
||||
|
||||
.. code-block:: nim
|
||||
import strutils
|
||||
|
||||
# strutils contains both a ``split`` proc and iterator, but since an
|
||||
# an iterator is the preferred interpretation, `y` has the type ``string``:
|
||||
var y: type("a b c".split)
|
||||
|
||||
|
||||
Special Operators
|
||||
@@ -7458,7 +7484,7 @@ Custom pragmas are defined using templates annotated with pragma ``pragma``:
|
||||
.. code-block:: nim
|
||||
template dbTable(name: string, table_space: string = "") {.pragma.}
|
||||
template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
|
||||
template dbForeignKey(t: typedesc) {.pragma.}
|
||||
template dbForeignKey(t: type) {.pragma.}
|
||||
template dbIgnore {.pragma.}
|
||||
|
||||
|
||||
|
||||
@@ -618,9 +618,9 @@ Turning the ``log`` proc into a template solves this problem:
|
||||
log("x has the value: " & $x)
|
||||
|
||||
The parameters' types can be ordinary types or the meta types ``untyped``,
|
||||
``typed``, or ``typedesc``.
|
||||
``typedesc`` stands for *type description*, and ``untyped`` means symbol lookups and
|
||||
type resolution is not performed before the expression is passed to the template.
|
||||
``typed``, or ``type``. ``type`` suggests that only a type symbol may be given
|
||||
as an argument, and ``untyped`` means symbol lookups and type resolution is not
|
||||
performed before the expression is passed to the template.
|
||||
|
||||
If the template has no explicit return type,
|
||||
``void`` is used for consistency with procs and methods.
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
## Note: For inter thread communication use
|
||||
## a `Channel <channels.html>`_ instead.
|
||||
|
||||
import math
|
||||
import math, typetraits
|
||||
|
||||
type
|
||||
Deque*[T] = object
|
||||
@@ -160,16 +160,18 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} =
|
||||
emptyCheck(deq)
|
||||
result = deq.data[(deq.tail - 1) and deq.mask]
|
||||
|
||||
template default[T](t: typedesc[T]): T =
|
||||
var v: T
|
||||
v
|
||||
template destroy(x: untyped) =
|
||||
when defined(nimNewRuntime) and not supportsCopyMem(type(x)):
|
||||
`=destroy`(x)
|
||||
else:
|
||||
reset(x)
|
||||
|
||||
proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
||||
## Remove and returns the first element of the `deq`.
|
||||
emptyCheck(deq)
|
||||
dec deq.count
|
||||
result = deq.data[deq.head]
|
||||
deq.data[deq.head] = default(type(result))
|
||||
destroy(deq.data[deq.head])
|
||||
deq.head = (deq.head + 1) and deq.mask
|
||||
|
||||
proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
||||
@@ -178,7 +180,34 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
||||
dec deq.count
|
||||
deq.tail = (deq.tail - 1) and deq.mask
|
||||
result = deq.data[deq.tail]
|
||||
deq.data[deq.tail] = default(type(result))
|
||||
destroy(deq.data[deq.tail])
|
||||
|
||||
proc clear*[T](deq: var Deque[T]) {.inline.} =
|
||||
## Resets the deque so that it is empty.
|
||||
for el in mitems(deq): destroy(el)
|
||||
deq.count = 0
|
||||
deq.tail = deq.head
|
||||
|
||||
proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
|
||||
## Remove `fromFirst` elements from the front of the deque and
|
||||
## `fromLast` elements from the back. If the supplied number of
|
||||
## elements exceeds the total number of elements in the deque,
|
||||
## the deque will remain empty.
|
||||
##
|
||||
## Any user defined destructors
|
||||
if fromFirst + fromLast > deq.count:
|
||||
clear(deq)
|
||||
return
|
||||
|
||||
for i in 0 ..< fromFirst:
|
||||
destroy(deq.data[deq.head])
|
||||
deq.head = (deq.head + 1) and deq.mask
|
||||
|
||||
for i in 0 ..< fromLast:
|
||||
destroy(deq.data[deq.tail])
|
||||
deq.tail = (deq.tail - 1) and deq.mask
|
||||
|
||||
dec deq.count, fromFirst + fromLast
|
||||
|
||||
proc `$`*[T](deq: Deque[T]): string =
|
||||
## Turn a deque into its string representation.
|
||||
@@ -215,6 +244,22 @@ when isMainModule:
|
||||
assert deq.find(6) >= 0
|
||||
assert deq.find(789) < 0
|
||||
|
||||
block:
|
||||
var d = initDeque[int](1)
|
||||
d.addLast 7
|
||||
d.addLast 8
|
||||
d.addLast 10
|
||||
d.addFirst 5
|
||||
d.addFirst 2
|
||||
d.addFirst 1
|
||||
d.addLast 20
|
||||
d.shrink(fromLast = 2)
|
||||
doAssert($d == "[1, 2, 5, 7, 8]")
|
||||
d.shrink(2, 1)
|
||||
doAssert($d == "[5, 7]")
|
||||
d.shrink(2, 2)
|
||||
doAssert d.len == 0
|
||||
|
||||
for i in -2 .. 10:
|
||||
if i in deq:
|
||||
assert deq.contains(i) and deq.find(i) >= 0
|
||||
|
||||
@@ -135,6 +135,11 @@ proc write*(s: Stream, x: string) =
|
||||
else:
|
||||
if x.len > 0: writeData(s, cstring(x), x.len)
|
||||
|
||||
proc write*(s: Stream, args: varargs[string, `$`]) =
|
||||
## writes one or more strings to the the stream. No length fields or
|
||||
## terminating zeros are written.
|
||||
for str in args: write(s, str)
|
||||
|
||||
proc writeLine*(s: Stream, args: varargs[string, `$`]) =
|
||||
## writes one or more strings to the the stream `s` followed
|
||||
## by a new line. No length field or terminating zero is written.
|
||||
@@ -266,8 +271,8 @@ proc peekStr*(s: Stream, length: int): TaintedString =
|
||||
proc readLine*(s: Stream, line: var TaintedString): bool =
|
||||
## reads a line of text from the stream `s` into `line`. `line` must not be
|
||||
## ``nil``! May throw an IO exception.
|
||||
## A line of text may be delimited by ``CR``, ``LF`` or
|
||||
## ``CRLF``. The newline character(s) are not part of the returned string.
|
||||
## A line of text may be delimited by ```LF`` or ``CRLF``.
|
||||
## The newline character(s) are not part of the returned string.
|
||||
## Returns ``false`` if the end of the file has been reached, ``true``
|
||||
## otherwise. If ``false`` is returned `line` contains no new data.
|
||||
line.string.setLen(0)
|
||||
@@ -317,6 +322,13 @@ proc peekLine*(s: Stream): TaintedString =
|
||||
defer: setPosition(s, pos)
|
||||
result = readLine(s)
|
||||
|
||||
iterator lines*(s: Stream): TaintedString =
|
||||
## Iterates over every line in the stream.
|
||||
## The iteration is based on ``readLine``.
|
||||
var line: TaintedString
|
||||
while s.readLine(line):
|
||||
yield line
|
||||
|
||||
when not defined(js):
|
||||
|
||||
type
|
||||
|
||||
@@ -179,10 +179,24 @@ proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
|
||||
## Cannot be overloaded.
|
||||
discard
|
||||
|
||||
proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} =
|
||||
## Builtin 'type' operator for accessing the type of an expression.
|
||||
## Cannot be overloaded.
|
||||
discard
|
||||
when defined(nimNewTypedesc):
|
||||
type
|
||||
`static`* {.magic: "Static".}[T]
|
||||
## meta type representing all values that can be evaluated at compile-time.
|
||||
##
|
||||
## The type coercion ``static(x)`` can be used to force the compile-time
|
||||
## evaluation of the given expression ``x``.
|
||||
|
||||
`type`* {.magic: "Type".}[T]
|
||||
## meta type representing the type of all type values.
|
||||
##
|
||||
## The coercion ``type(x)`` can be used to obtain the type of the given
|
||||
## expression ``x``.
|
||||
else:
|
||||
proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} =
|
||||
## Builtin 'type' operator for accessing the type of an expression.
|
||||
## Cannot be overloaded.
|
||||
discard
|
||||
|
||||
proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.}
|
||||
## Boolean not; returns true iff ``x == false``.
|
||||
|
||||
@@ -182,7 +182,7 @@ template checkOsError =
|
||||
if err.len > 0: raise newException(OSError, err)
|
||||
|
||||
template log(msg: string, body: untyped) =
|
||||
if mode == ScriptMode.Verbose or mode == ScriptMode.Whatif:
|
||||
if mode in {ScriptMode.Verbose, ScriptMode.Whatif}:
|
||||
echo "[NimScript] ", msg
|
||||
if mode != ScriptMode.WhatIf:
|
||||
body
|
||||
|
||||
26
tests/concepts/libs/trie.nim
Normal file
26
tests/concepts/libs/trie.nim
Normal file
@@ -0,0 +1,26 @@
|
||||
import
|
||||
hashes, tables, trie_database
|
||||
|
||||
type
|
||||
MemDBTable = Table[KeccakHash, string]
|
||||
|
||||
MemDB* = object
|
||||
tbl: MemDBTable
|
||||
|
||||
proc hash*(key: KeccakHash): int =
|
||||
hashes.hash(key.data)
|
||||
|
||||
proc get*(db: MemDB, key: KeccakHash): string =
|
||||
db.tbl[key]
|
||||
|
||||
proc del*(db: var MemDB, key: KeccakHash): bool =
|
||||
if db.tbl.hasKey(key):
|
||||
db.tbl.del(key)
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
proc put*(db: var MemDB, key: KeccakHash, value: string): bool =
|
||||
db.tbl[key] = value
|
||||
return true
|
||||
|
||||
12
tests/concepts/libs/trie_database.nim
Normal file
12
tests/concepts/libs/trie_database.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
type
|
||||
KeccakHash* = object
|
||||
data*: string
|
||||
|
||||
BytesRange* = object
|
||||
bytes*: string
|
||||
|
||||
TrieDatabase* = concept db
|
||||
put(var db, KeccakHash, string) is bool
|
||||
del(var db, KeccakHash) is bool
|
||||
get(db, KeccakHash) is string
|
||||
|
||||
7
tests/concepts/ttrieconcept.nim
Normal file
7
tests/concepts/ttrieconcept.nim
Normal file
@@ -0,0 +1,7 @@
|
||||
import libs/[trie_database, trie]
|
||||
|
||||
proc takeDb(d: TrieDatabase) = discard
|
||||
var mdb: MemDB
|
||||
|
||||
takeDb(mdb)
|
||||
|
||||
145
tests/generics/tlateboundgenericparams.nim
Normal file
145
tests/generics/tlateboundgenericparams.nim
Normal file
@@ -0,0 +1,145 @@
|
||||
discard """
|
||||
output: "1\n10\n1\n10"
|
||||
nimout: '''
|
||||
bar instantiated with 1
|
||||
bar instantiated with 10
|
||||
'''
|
||||
"""
|
||||
|
||||
import typetraits
|
||||
|
||||
type
|
||||
Foo = object
|
||||
|
||||
proc defaultFoo: Foo = discard
|
||||
proc defaultInt: int = 1
|
||||
proc defaultTInt(T: type): int = 2
|
||||
proc defaultTFoo[T](x: typedesc[T]): Foo = discard
|
||||
proc defaultTOldSchool[T](x: typedesc[T]): T = discard
|
||||
proc defaultTModern(T: type): T = discard
|
||||
|
||||
proc specializedDefault(T: type int): int = 10
|
||||
proc specializedDefault(T: type string): string = "default"
|
||||
|
||||
converter intFromFoo(x: Foo): int = 3
|
||||
|
||||
proc consumeInt(x: int) =
|
||||
discard
|
||||
|
||||
const activeTests = {1..100}
|
||||
|
||||
when true:
|
||||
template test(n, body) =
|
||||
when n in activeTests:
|
||||
block:
|
||||
body
|
||||
|
||||
template reject(x) =
|
||||
static: assert(not compiles(x))
|
||||
|
||||
test 1:
|
||||
proc t[T](val: T = defaultInt()) =
|
||||
consumeInt val
|
||||
|
||||
t[int]()
|
||||
reject t[string]()
|
||||
|
||||
test 2:
|
||||
proc t1[T](val: T = defaultFoo()) =
|
||||
static:
|
||||
assert type(val).name == "int"
|
||||
assert T.name == "int"
|
||||
|
||||
consumeInt val
|
||||
|
||||
# here, the converter should kick in, but notice
|
||||
# how `val` is still typed `int` inside the proc.
|
||||
t1[int]()
|
||||
|
||||
proc t2[T](val: T = defaultFoo()) =
|
||||
discard
|
||||
|
||||
reject t2[string]()
|
||||
|
||||
test 3:
|
||||
proc tInt[T](val = defaultInt()): string =
|
||||
return type(val).name
|
||||
|
||||
doAssert tInt[int]() == "int"
|
||||
doAssert tInt[string]() == "int"
|
||||
|
||||
proc tInt2[T](val = defaultTInt(T)): string =
|
||||
return type(val).name
|
||||
|
||||
doAssert tInt2[int]() == "int"
|
||||
doAssert tInt2[string]() == "int"
|
||||
|
||||
proc tDefTModern[T](val = defaultTModern(T)): string =
|
||||
return type(val).name
|
||||
|
||||
doAssert tDefTModern[int]() == "int"
|
||||
doAssert tDefTModern[string]() == "string"
|
||||
doAssert tDefTModern[Foo]() == "Foo"
|
||||
|
||||
proc tDefTOld[T](val = defaultTOldSchool(T)): string =
|
||||
return type(val).name
|
||||
|
||||
doAssert tDefTOld[int]() == "int"
|
||||
doAssert tDefTOld[string]() == "string"
|
||||
doAssert tDefTOld[Foo]() == "Foo"
|
||||
|
||||
test 4:
|
||||
proc t[T](val: T = defaultTFoo(T)): string =
|
||||
return type(val).name
|
||||
|
||||
doAssert t[int]() == "int"
|
||||
doAssert t[Foo]() == "Foo"
|
||||
reject t[string]()
|
||||
|
||||
test 5:
|
||||
proc t1[T](a: T = specializedDefault(T)): T =
|
||||
return a
|
||||
|
||||
doAssert t1[int]() == 10
|
||||
doAssert t1[string]() == "default"
|
||||
|
||||
proc t2[T](a: T, b = specializedDefault(T)): auto =
|
||||
return $a & $b
|
||||
|
||||
doAssert t2(5) == "510"
|
||||
doAssert t2("string ") == "string default"
|
||||
|
||||
proc t3[T](a: T, b = specializedDefault(type(a))): auto =
|
||||
return $a & $b
|
||||
|
||||
doAssert t3(100) == "10010"
|
||||
doAssert t3("another ") == "another default"
|
||||
|
||||
test 6:
|
||||
# https://github.com/nim-lang/Nim/issues/5595
|
||||
type
|
||||
Point[T] = object
|
||||
x, y: T
|
||||
|
||||
proc getOrigin[T](): Point[T] = Point[T](x: 0, y: 0)
|
||||
|
||||
proc rotate[T](point: Point[T], radians: float,
|
||||
origin = getOrigin[T]()): Point[T] =
|
||||
discard
|
||||
|
||||
var p = getOrigin[float]()
|
||||
var rotated = p.rotate(2.1)
|
||||
|
||||
test 7:
|
||||
proc bar(x: static[int]) =
|
||||
static: echo "bar instantiated with ", x
|
||||
echo x
|
||||
|
||||
proc foo(x: static[int] = 1) =
|
||||
bar(x)
|
||||
|
||||
foo()
|
||||
foo(10)
|
||||
foo(1)
|
||||
foo(10)
|
||||
|
||||
@@ -4,7 +4,7 @@ discard """
|
||||
errormsg: "invalid indentation"
|
||||
"""
|
||||
|
||||
import strutils var s: seq[int] = @[0, 1, 2, 3, 4, 5, 6]
|
||||
import strutils let s: seq[int] = @[0, 1, 2, 3, 4, 5, 6]
|
||||
|
||||
#s[1..3] = @[]
|
||||
|
||||
|
||||
@@ -5,15 +5,16 @@ type
|
||||
x: T
|
||||
y: U
|
||||
|
||||
proc getTypeName(t: typedesc): string = t.name
|
||||
proc getTypeName1(t: typedesc): string = t.name
|
||||
proc getTypeName2(t: type): string = t.name
|
||||
|
||||
proc foo(T: typedesc[float], a: auto): string =
|
||||
proc foo(T: type float, a: auto): string =
|
||||
result = "float " & $(a.len > 5)
|
||||
|
||||
proc foo(T: typedesc[TFoo], a: int): string =
|
||||
result = "TFoo " & $(a)
|
||||
|
||||
proc foo(T: typedesc[int or bool]): string =
|
||||
proc foo(T: type[int or bool]): string =
|
||||
var a: T
|
||||
a = 10
|
||||
result = "int or bool " & ($a)
|
||||
@@ -23,8 +24,8 @@ template foo(T: typedesc[seq]): string = "seq"
|
||||
test "types can be used as proc params":
|
||||
# XXX: `check` needs to know that TFoo[int, float] is a type and
|
||||
# cannot be assigned for a local variable for later inspection
|
||||
check ((string.getTypeName == "string"))
|
||||
check ((getTypeName(int) == "int"))
|
||||
check ((string.getTypeName1 == "string"))
|
||||
check ((getTypeName2(int) == "int"))
|
||||
|
||||
check ((foo(TFoo[int, float], 1000) == "TFoo 1000"))
|
||||
|
||||
@@ -37,6 +38,25 @@ test "types can be used as proc params":
|
||||
check ((foo(seq[int]) == "seq"))
|
||||
check ((foo(seq[TFoo[bool, string]]) == "seq"))
|
||||
|
||||
when false:
|
||||
proc foo(T: typedesc[seq], s: T) = nil
|
||||
template accept(x) =
|
||||
static: assert(compiles(x))
|
||||
|
||||
template reject(x) =
|
||||
static: assert(not compiles(x))
|
||||
|
||||
var
|
||||
si: seq[int]
|
||||
ss: seq[string]
|
||||
|
||||
proc foo(T: typedesc[seq], s: T) =
|
||||
discard
|
||||
|
||||
accept:
|
||||
foo seq[int], si
|
||||
|
||||
reject:
|
||||
foo seq[string], si
|
||||
|
||||
reject:
|
||||
foo seq[int], ss
|
||||
|
||||
|
||||
@@ -34,3 +34,17 @@ when true:
|
||||
type Point[T] = tuple[x, y: T]
|
||||
proc origin(T: typedesc): Point[T] = discard
|
||||
discard origin(int)
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/7516
|
||||
import typetraits
|
||||
|
||||
proc hasDefault1(T: type = int): auto = return T.name
|
||||
doAssert hasDefault1(int) == "int"
|
||||
doAssert hasDefault1(string) == "string"
|
||||
doAssert hasDefault1() == "int"
|
||||
|
||||
proc hasDefault2(T = string): auto = return T.name
|
||||
doAssert hasDefault2(int) == "int"
|
||||
doAssert hasDefault2(string) == "string"
|
||||
doAssert hasDefault2() == "string"
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ type
|
||||
Base = object of RootObj
|
||||
Child = object of Base
|
||||
|
||||
proc pr(T: typedesc[Base]) = echo "proc " & T.name
|
||||
method me(T: typedesc[Base]) = echo "method " & T.name
|
||||
iterator it(T: typedesc[Base]): auto = yield "yield " & T.name
|
||||
proc pr(T: type[Base]) = echo "proc " & T.name
|
||||
method me(T: type[Base]) = echo "method " & T.name
|
||||
iterator it(T: type[Base]): auto = yield "yield " & T.name
|
||||
|
||||
Base.pr
|
||||
Child.pr
|
||||
|
||||
@@ -5,16 +5,16 @@ output: "8\n8\n4"
|
||||
import
|
||||
macros, typetraits
|
||||
|
||||
template selectType(x: int): typeDesc =
|
||||
template selectType(x: int): type =
|
||||
when x < 10:
|
||||
int
|
||||
else:
|
||||
string
|
||||
|
||||
template simpleTypeTempl: typeDesc =
|
||||
template simpleTypeTempl: type =
|
||||
string
|
||||
|
||||
macro typeFromMacro: typedesc = string
|
||||
macro typeFromMacro: type = string
|
||||
|
||||
# The tests below check that the result variable of the
|
||||
# selected type matches the literal types in the code:
|
||||
|
||||
114
tests/misc/tparamsindefault.nim
Normal file
114
tests/misc/tparamsindefault.nim
Normal file
@@ -0,0 +1,114 @@
|
||||
discard """
|
||||
output: '''
|
||||
@[1, 2, 3]@[1, 2, 3]
|
||||
a
|
||||
a
|
||||
1
|
||||
3 is an int
|
||||
2 is an int
|
||||
miau is a string
|
||||
f1 1 1 1
|
||||
f1 2 3 3
|
||||
f1 10 20 30
|
||||
f2 100 100 100
|
||||
f2 200 300 300
|
||||
f2 300 400 400
|
||||
f3 10 10 20
|
||||
f3 10 15 25
|
||||
true true
|
||||
false true
|
||||
world
|
||||
'''
|
||||
"""
|
||||
|
||||
template reject(x) =
|
||||
assert(not compiles(x))
|
||||
|
||||
block:
|
||||
# https://github.com/nim-lang/Nim/issues/7756
|
||||
proc foo[T](x: seq[T], y: seq[T] = x) =
|
||||
echo x, y
|
||||
|
||||
let a = @[1, 2, 3]
|
||||
foo(a)
|
||||
|
||||
block:
|
||||
# https://github.com/nim-lang/Nim/issues/1201
|
||||
proc issue1201(x: char|int = 'a') = echo x
|
||||
|
||||
issue1201()
|
||||
issue1201('a')
|
||||
issue1201(1)
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/7000
|
||||
proc test(a: int|string = 2) =
|
||||
when a is int:
|
||||
echo a, " is an int"
|
||||
elif a is string:
|
||||
echo a, " is a string"
|
||||
|
||||
test(3) # works
|
||||
test() # works
|
||||
test("miau")
|
||||
|
||||
block:
|
||||
# https://github.com/nim-lang/Nim/issues/3002 and similar
|
||||
proc f1(a: int, b = a, c = b) =
|
||||
echo "f1 ", a, " ", b, " ", c
|
||||
|
||||
proc f2(a: int, b = a, c: int = b) =
|
||||
echo "f2 ", a, " ", b, " ", c
|
||||
|
||||
proc f3(a: int, b = a, c = a + b) =
|
||||
echo "f3 ", a, " ", b, " ", c
|
||||
|
||||
f1 1
|
||||
f1(2, 3)
|
||||
f1 10, 20, 30
|
||||
100.f2
|
||||
200.f2 300
|
||||
300.f2(400)
|
||||
|
||||
10.f3()
|
||||
10.f3(15)
|
||||
|
||||
reject:
|
||||
# This is a type mismatch error:
|
||||
proc f4(a: int, b = a, c: float = b) = discard
|
||||
|
||||
reject:
|
||||
# undeclared identifier
|
||||
proc f5(a: int, b = c, c = 10) = discard
|
||||
|
||||
reject:
|
||||
# undeclared identifier
|
||||
proc f6(a: int, b = b) = discard
|
||||
|
||||
reject:
|
||||
# undeclared identifier
|
||||
proc f7(a = a) = discard
|
||||
|
||||
block:
|
||||
proc f(a: var int, b: ptr int, c = addr(a)) =
|
||||
echo addr(a) == b, " ", b == c
|
||||
|
||||
var x = 10
|
||||
f(x, addr(x))
|
||||
f(x, nil, nil)
|
||||
|
||||
block:
|
||||
# https://github.com/nim-lang/Nim/issues/1046
|
||||
proc pySubstr(s: string, start: int, endd = s.len()): string =
|
||||
var
|
||||
revStart = start
|
||||
revEnd = endd
|
||||
|
||||
if start < 0:
|
||||
revStart = s.len() + start
|
||||
if endd < 0:
|
||||
revEnd = s.len() + endd
|
||||
|
||||
return s[revStart .. revEnd-1]
|
||||
|
||||
echo pySubstr("Hello world", -5)
|
||||
|
||||
@@ -1,17 +1,42 @@
|
||||
discard """
|
||||
action: run
|
||||
"""
|
||||
|
||||
type
|
||||
R = ref
|
||||
V = var
|
||||
D = distinct
|
||||
P = ptr
|
||||
T = type
|
||||
S = static
|
||||
OBJ = object
|
||||
TPL = tuple
|
||||
SEQ = seq
|
||||
|
||||
var i: int
|
||||
var x: ref int
|
||||
var y: distinct int
|
||||
var z: ptr int
|
||||
const C = @[1, 2, 3]
|
||||
|
||||
static:
|
||||
assert x is ref
|
||||
assert y is distinct
|
||||
assert z is ptr
|
||||
assert C is static
|
||||
assert C[1] is static[int]
|
||||
assert C[0] is static[SomeInteger]
|
||||
assert C isnot static[string]
|
||||
assert C is SEQ|OBJ
|
||||
assert C isnot OBJ|TPL
|
||||
assert int is int
|
||||
assert int is T
|
||||
assert int is SomeInteger
|
||||
assert seq[int] is type
|
||||
assert seq[int] is type[seq]
|
||||
assert seq[int] isnot type[seq[float]]
|
||||
assert i isnot type[int]
|
||||
assert type(i) is type[int]
|
||||
assert x isnot T
|
||||
assert y isnot S
|
||||
assert z isnot enum
|
||||
assert x isnot object
|
||||
assert y isnot tuple
|
||||
assert z isnot seq
|
||||
|
||||
doAssert x is ref
|
||||
doAssert y is distinct
|
||||
doAssert z is ptr
|
||||
527
tests/parser/ttypemodifiers.nim
Normal file
527
tests/parser/ttypemodifiers.nim
Normal file
@@ -0,0 +1,527 @@
|
||||
discard """
|
||||
nimout: '''
|
||||
StmtList
|
||||
TypeSection
|
||||
TypeDef
|
||||
Ident "BarePtr"
|
||||
Empty
|
||||
PtrTy
|
||||
TypeDef
|
||||
Ident "GenericPtr"
|
||||
Empty
|
||||
PtrTy
|
||||
Bracket
|
||||
Ident "int"
|
||||
TypeDef
|
||||
Ident "PrefixPtr"
|
||||
Empty
|
||||
PtrTy
|
||||
Ident "int"
|
||||
TypeDef
|
||||
Ident "PtrTuple"
|
||||
Empty
|
||||
PtrTy
|
||||
Par
|
||||
Ident "int"
|
||||
Ident "string"
|
||||
TypeDef
|
||||
Ident "BareRef"
|
||||
Empty
|
||||
RefTy
|
||||
TypeDef
|
||||
Ident "GenericRef"
|
||||
Empty
|
||||
RefTy
|
||||
Bracket
|
||||
Ident "int"
|
||||
TypeDef
|
||||
Ident "RefTupleCl"
|
||||
Empty
|
||||
RefTy
|
||||
TupleTy
|
||||
TypeDef
|
||||
Ident "RefTupleType"
|
||||
Empty
|
||||
RefTy
|
||||
Par
|
||||
Ident "int"
|
||||
Ident "string"
|
||||
TypeDef
|
||||
Ident "RefTupleVars"
|
||||
Empty
|
||||
RefTy
|
||||
Par
|
||||
Ident "a"
|
||||
Ident "b"
|
||||
TypeDef
|
||||
Ident "BareStatic"
|
||||
Empty
|
||||
Ident "static"
|
||||
TypeDef
|
||||
Ident "GenericStatic"
|
||||
Empty
|
||||
BracketExpr
|
||||
Ident "static"
|
||||
Ident "int"
|
||||
TypeDef
|
||||
Ident "PrefixStatic"
|
||||
Empty
|
||||
Command
|
||||
Ident "static"
|
||||
Ident "int"
|
||||
TypeDef
|
||||
Ident "StaticTupleCl"
|
||||
Empty
|
||||
Command
|
||||
Ident "static"
|
||||
TupleClassTy
|
||||
TypeDef
|
||||
Ident "StaticTuple"
|
||||
Empty
|
||||
Command
|
||||
Ident "static"
|
||||
Par
|
||||
Ident "int"
|
||||
Ident "string"
|
||||
TypeDef
|
||||
Ident "BareType"
|
||||
Empty
|
||||
Ident "type"
|
||||
TypeDef
|
||||
Ident "GenericType"
|
||||
Empty
|
||||
BracketExpr
|
||||
Ident "type"
|
||||
Ident "float"
|
||||
TypeDef
|
||||
Ident "TypeTupleGen"
|
||||
Empty
|
||||
BracketExpr
|
||||
Ident "type"
|
||||
TupleClassTy
|
||||
TypeDef
|
||||
Ident "TypeTupleCl"
|
||||
Empty
|
||||
Command
|
||||
Ident "type"
|
||||
TupleClassTy
|
||||
TypeDef
|
||||
Ident "TypeInstance"
|
||||
Empty
|
||||
Command
|
||||
Ident "type"
|
||||
BracketExpr
|
||||
Ident "Foo"
|
||||
RefTy
|
||||
TypeDef
|
||||
Ident "bareTypeDesc"
|
||||
Empty
|
||||
Ident "typedesc"
|
||||
TypeDef
|
||||
Ident "TypeOfVar"
|
||||
Empty
|
||||
Call
|
||||
Ident "type"
|
||||
Ident "a"
|
||||
TypeDef
|
||||
Ident "TypeOfVarAlt"
|
||||
Empty
|
||||
Command
|
||||
Ident "type"
|
||||
Par
|
||||
Ident "a"
|
||||
TypeDef
|
||||
Ident "TypeOfTuple1"
|
||||
Empty
|
||||
Call
|
||||
Ident "type"
|
||||
Ident "a"
|
||||
TypeDef
|
||||
Ident "TypeOfTuple2"
|
||||
Empty
|
||||
Call
|
||||
Ident "type"
|
||||
Ident "a"
|
||||
Ident "b"
|
||||
TypeDef
|
||||
Ident "TypeOfTuple1A"
|
||||
Empty
|
||||
Command
|
||||
Ident "type"
|
||||
TupleConstr
|
||||
Ident "a"
|
||||
TypeDef
|
||||
Ident "TypeOfTuple2A"
|
||||
Empty
|
||||
Command
|
||||
Ident "type"
|
||||
Par
|
||||
Ident "a"
|
||||
Ident "b"
|
||||
TypeDef
|
||||
Ident "TypeTuple"
|
||||
Empty
|
||||
Command
|
||||
Ident "type"
|
||||
Par
|
||||
Ident "int"
|
||||
Ident "string"
|
||||
TypeDef
|
||||
Ident "GenericTypedesc"
|
||||
Empty
|
||||
BracketExpr
|
||||
Ident "typedesc"
|
||||
Ident "int"
|
||||
TypeDef
|
||||
Ident "T"
|
||||
Empty
|
||||
Ident "type"
|
||||
ProcDef
|
||||
Ident "foo"
|
||||
Empty
|
||||
Empty
|
||||
FormalParams
|
||||
Ident "type"
|
||||
IdentDefs
|
||||
Ident "bareType"
|
||||
Ident "type"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "genType"
|
||||
BracketExpr
|
||||
Ident "type"
|
||||
Ident "int"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "typeInt"
|
||||
Command
|
||||
Ident "type"
|
||||
Ident "int"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "typeIntAlt"
|
||||
Call
|
||||
Ident "type"
|
||||
Ident "int"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "typeOfVar"
|
||||
Call
|
||||
Ident "type"
|
||||
Ident "a"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "typeDotType"
|
||||
DotExpr
|
||||
Ident "foo"
|
||||
Ident "type"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "typeTupleCl"
|
||||
Command
|
||||
Ident "type"
|
||||
TupleClassTy
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "bareStatic"
|
||||
Ident "static"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "genStatic"
|
||||
BracketExpr
|
||||
Ident "static"
|
||||
Ident "int"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "staticInt"
|
||||
Command
|
||||
Ident "static"
|
||||
Ident "int"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "staticVal1"
|
||||
Command
|
||||
Ident "static"
|
||||
IntLit 10
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "staticVal2"
|
||||
Call
|
||||
Ident "static"
|
||||
StrLit "str"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "staticVal3"
|
||||
Command
|
||||
Ident "static"
|
||||
StrLit "str"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "staticVal4"
|
||||
CallStrLit
|
||||
Ident "static"
|
||||
RStrLit "str"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "staticDotVal"
|
||||
DotExpr
|
||||
IntLit 10
|
||||
Ident "static"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "bareRef"
|
||||
RefTy
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "refTuple1"
|
||||
RefTy
|
||||
Par
|
||||
Ident "int"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "refTuple1A"
|
||||
RefTy
|
||||
TupleConstr
|
||||
Ident "int"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "refTuple2"
|
||||
RefTy
|
||||
Par
|
||||
Ident "int"
|
||||
Ident "string"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "genRef"
|
||||
RefTy
|
||||
Bracket
|
||||
Ident "int"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "refInt"
|
||||
RefTy
|
||||
Ident "int"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "refCall"
|
||||
RefTy
|
||||
Par
|
||||
Ident "a"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "macroCall1"
|
||||
Command
|
||||
Ident "foo"
|
||||
Ident "bar"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "macroCall2"
|
||||
Call
|
||||
Ident "foo"
|
||||
Ident "bar"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "macroCall3"
|
||||
Call
|
||||
DotExpr
|
||||
Ident "foo"
|
||||
Ident "bar"
|
||||
Ident "baz"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "macroCall4"
|
||||
Call
|
||||
BracketExpr
|
||||
Ident "foo"
|
||||
Ident "bar"
|
||||
Ident "baz"
|
||||
Empty
|
||||
IdentDefs
|
||||
Ident "macroCall5"
|
||||
Command
|
||||
Ident "foo"
|
||||
Command
|
||||
Ident "bar"
|
||||
Ident "baz"
|
||||
IntLit 10
|
||||
Empty
|
||||
Empty
|
||||
StmtList
|
||||
Asgn
|
||||
Ident "staticTen"
|
||||
Command
|
||||
Ident "static"
|
||||
IntLit 10
|
||||
Asgn
|
||||
Ident "staticA"
|
||||
Call
|
||||
Ident "static"
|
||||
Ident "a"
|
||||
Asgn
|
||||
Ident "staticCall"
|
||||
Command
|
||||
Ident "static"
|
||||
Call
|
||||
Ident "foo"
|
||||
IntLit 1
|
||||
Asgn
|
||||
Ident "staticStrCall"
|
||||
Command
|
||||
Ident "static"
|
||||
CallStrLit
|
||||
Ident "foo"
|
||||
RStrLit "x"
|
||||
Asgn
|
||||
Ident "staticChainCall"
|
||||
Command
|
||||
Ident "static"
|
||||
Command
|
||||
Ident "foo"
|
||||
Ident "bar"
|
||||
Asgn
|
||||
Ident "typeTen"
|
||||
Command
|
||||
Ident "type"
|
||||
IntLit 10
|
||||
Asgn
|
||||
Ident "typeA"
|
||||
Call
|
||||
Ident "type"
|
||||
Ident "a"
|
||||
Asgn
|
||||
Ident "typeCall"
|
||||
Command
|
||||
Ident "type"
|
||||
Call
|
||||
Ident "foo"
|
||||
IntLit 1
|
||||
Asgn
|
||||
Ident "typeStrCall"
|
||||
Command
|
||||
Ident "type"
|
||||
CallStrLit
|
||||
Ident "foo"
|
||||
RStrLit "x"
|
||||
Asgn
|
||||
Ident "typeChainCall"
|
||||
Command
|
||||
Ident "type"
|
||||
Command
|
||||
Ident "foo"
|
||||
Ident "bar"
|
||||
Asgn
|
||||
Ident "normalChainCall"
|
||||
Command
|
||||
Ident "foo"
|
||||
Command
|
||||
Ident "bar"
|
||||
Ident "baz"
|
||||
Asgn
|
||||
Ident "normalTupleCall2"
|
||||
Call
|
||||
Ident "foo"
|
||||
Ident "a"
|
||||
Ident "b"
|
||||
StaticStmt
|
||||
StmtList
|
||||
Ident "singleStaticStmt"
|
||||
StaticStmt
|
||||
StmtList
|
||||
Ident "staticStmtList1"
|
||||
Ident "staticStmtList2"
|
||||
'''
|
||||
"""
|
||||
|
||||
import macros
|
||||
|
||||
dumpTree:
|
||||
type
|
||||
BarePtr = ptr
|
||||
GenericPtr = ptr[int]
|
||||
PrefixPtr = ptr int
|
||||
PtrTuple = ptr (int, string)
|
||||
BareRef = ref
|
||||
GenericRef = ref[int]
|
||||
RefTupleCl = ref tuple
|
||||
RefTupleType = ref (int, string)
|
||||
RefTupleVars = ref (a, b)
|
||||
BareStatic = static # Used to be Error: invalid indentation
|
||||
GenericStatic = static[int]
|
||||
PrefixStatic = static int
|
||||
StaticTupleCl = static tuple
|
||||
StaticTuple = static (int, string)
|
||||
BareType = type
|
||||
GenericType = type[float]
|
||||
TypeTupleGen = type[tuple]
|
||||
TypeTupleCl = type tuple # Used to be Error: invalid indentation
|
||||
TypeInstance = type Foo[ref]
|
||||
bareTypeDesc = typedesc
|
||||
TypeOfVar = type(a)
|
||||
TypeOfVarAlt = type (a) # Used to be Error: invalid indentation
|
||||
TypeOfTuple1 = type(a,)
|
||||
TypeOfTuple2 = type(a,b)
|
||||
TypeOfTuple1A = type (a,) # Used to be Error: invalid indentation
|
||||
TypeOfTuple2A = type (a,b) # Used to be Error: invalid indentation
|
||||
TypeTuple = type (int, string) # Used to be Error: invalid indentation
|
||||
GenericTypedesc = typedesc[int]
|
||||
T = type
|
||||
|
||||
proc foo(
|
||||
bareType : type,
|
||||
genType : type[int],
|
||||
typeInt : type int,
|
||||
typeIntAlt : type(int),
|
||||
typeOfVar : type(a),
|
||||
typeDotType : foo.type,
|
||||
typeTupleCl : type tuple, # Used to be Error: ')' expected
|
||||
bareStatic : static, # Used to be Error: expression expected, but found ','
|
||||
genStatic : static[int],
|
||||
staticInt : static int,
|
||||
staticVal1 : static 10,
|
||||
staticVal2 : static("str"),
|
||||
staticVal3 : static "str",
|
||||
staticVal4 : static"str", # Used to be Error: expression expected, but found 'str'
|
||||
staticDotVal : 10.static,
|
||||
bareRef : ref,
|
||||
refTuple1 : ref (int),
|
||||
refTuple1A : ref (int,),
|
||||
refTuple2 : ref (int,string),
|
||||
genRef : ref[int],
|
||||
refInt : ref int,
|
||||
refCall : ref(a),
|
||||
macroCall1 : foo bar,
|
||||
macroCall2 : foo(bar),
|
||||
macroCall3 : foo.bar(baz),
|
||||
macroCall4 : foo[bar](baz),
|
||||
macroCall5 : foo bar baz = 10
|
||||
): type =
|
||||
staticTen = static 10
|
||||
staticA = static(a)
|
||||
# staticAspace = static (a) # With newTypedesc: Error: invalid indentation
|
||||
# staticAtuple = static (a,) # With newTypedesc: Error: invalid indentation
|
||||
# staticTuple = static (a,b) # With newTypedesc: Error: invalid indentation
|
||||
# staticTypeTuple = static (int,string) # With newTypedesc: Error: invalid indentation
|
||||
staticCall = static foo(1)
|
||||
staticStrCall = static foo"x"
|
||||
staticChainCall = static foo bar
|
||||
|
||||
typeTen = type 10
|
||||
typeA = type(a)
|
||||
# typeAspace = type (a) # Error: invalid indentation
|
||||
# typeAtuple = type (a,) # Error: invalid indentation
|
||||
# typeTuple = type (a,b) # Error: invalid indentation
|
||||
# typeTypeTuple = type (int,string) # Error: invalid indentation
|
||||
typeCall = type foo(1)
|
||||
typeStrCall = type foo"x"
|
||||
typeChainCall = type foo bar
|
||||
|
||||
normalChainCall = foo bar baz
|
||||
# normalTupleCall1 = foo(a,) # Error: invalid indentation
|
||||
normalTupleCall2 = foo(a,b)
|
||||
# normalTupleCall3 = foo (a,b) # Error: invalid indentation
|
||||
|
||||
static: singleStaticStmt
|
||||
static:
|
||||
staticStmtList1
|
||||
staticStmtList2
|
||||
|
||||
109
tests/statictypes/tstatictypes.nim
Normal file
109
tests/statictypes/tstatictypes.nim
Normal file
@@ -0,0 +1,109 @@
|
||||
discard """
|
||||
nimout: '''
|
||||
staticAlialProc instantiated with 358
|
||||
staticAlialProc instantiated with 368
|
||||
'''
|
||||
output: '''
|
||||
16
|
||||
16
|
||||
b is 2 times a
|
||||
17
|
||||
'''
|
||||
"""
|
||||
|
||||
import macros
|
||||
|
||||
template ok(x) = assert(x)
|
||||
template no(x) = assert(not x)
|
||||
|
||||
template accept(x) =
|
||||
static: assert(compiles(x))
|
||||
|
||||
template reject(x) =
|
||||
static: assert(not compiles(x))
|
||||
|
||||
proc plus(a, b: int): int = a + b
|
||||
|
||||
template isStatic(x: static): bool = true
|
||||
template isStatic(x: auto): bool = false
|
||||
|
||||
var v = 1
|
||||
|
||||
when true:
|
||||
# test that `isStatic` works as expected
|
||||
const C = 2
|
||||
|
||||
static:
|
||||
ok C.isStatic
|
||||
ok isStatic(plus(1, 2))
|
||||
ok plus(C, 2).isStatic
|
||||
|
||||
no isStatic(v)
|
||||
no plus(1, v).isStatic
|
||||
|
||||
when true:
|
||||
# test that proc instantiation works as expected
|
||||
type
|
||||
StaticTypeAlias = static[int]
|
||||
|
||||
proc staticAliasProc(a: StaticTypeAlias,
|
||||
b: static[int],
|
||||
c: static int) =
|
||||
static:
|
||||
assert a.isStatic and b.isStatic and c.isStatic
|
||||
assert isStatic(a + plus(b, c))
|
||||
echo "staticAlialProc instantiated with ", a, b, c
|
||||
|
||||
when b mod a == 0:
|
||||
echo "b is ", b div a, " times a"
|
||||
|
||||
echo a + b + c
|
||||
|
||||
staticAliasProc 1+2, 5, 8
|
||||
staticAliasProc 3, 2+3, 9-1
|
||||
staticAliasProc 3, 3+3, 4+4
|
||||
|
||||
when true:
|
||||
# test static coercions. normal cases that should work:
|
||||
accept:
|
||||
var s1 = static[int] plus(1, 2)
|
||||
var s2 = static(plus(1,2))
|
||||
var s3 = static plus(1,2)
|
||||
var s4 = static[SomeInteger](1 + 2)
|
||||
|
||||
# the sub-script operator can be used only with types:
|
||||
reject:
|
||||
var just_static3 = static[plus(1,2)]
|
||||
|
||||
# static coercion takes into account the type:
|
||||
reject:
|
||||
var x = static[string](plus(1, 2))
|
||||
reject:
|
||||
var x = static[string] plus(1, 2)
|
||||
reject:
|
||||
var x = static[SomeFloat] plus(3, 4)
|
||||
|
||||
# you cannot coerce a run-time variable
|
||||
reject:
|
||||
var x = static(v)
|
||||
|
||||
when true:
|
||||
type
|
||||
ArrayWrapper1[S: static int] = object
|
||||
data: array[S + 1, int]
|
||||
|
||||
ArrayWrapper2[S: static[int]] = object
|
||||
data: array[S.plus(2), int]
|
||||
|
||||
ArrayWrapper3[S: static[(int, string)]] = object
|
||||
data: array[S[0], int]
|
||||
|
||||
var aw1: ArrayWrapper1[5]
|
||||
var aw2: ArrayWrapper2[5]
|
||||
var aw3: ArrayWrapper3[(10, "str")]
|
||||
|
||||
static:
|
||||
assert aw1.data.high == 5
|
||||
assert aw2.data.high == 6
|
||||
assert aw3.data.high == 9
|
||||
|
||||
@@ -5,7 +5,7 @@ false
|
||||
false
|
||||
true
|
||||
true
|
||||
no'''
|
||||
yes'''
|
||||
"""
|
||||
|
||||
proc IsVoid[T](): string =
|
||||
|
||||
Reference in New Issue
Block a user