Merge pull request #7681 from nim-lang/typedesc-reforms
Typedesc reforms
This commit is contained in:
commit
7cec5d1cfb
39 changed files with 1702 additions and 270 deletions
|
|
@ -119,6 +119,13 @@
|
||||||
- In order to make ``for`` loops and iterators more flexible to use Nim now
|
- In order to make ``for`` loops and iterators more flexible to use Nim now
|
||||||
supports so called "for-loop macros". See
|
supports so called "for-loop macros". See
|
||||||
the `manual <manual.html#macros-for-loop-macros>`_ for more details.
|
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
|
### Language changes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -293,6 +293,10 @@ const
|
||||||
# the compiler will avoid printing such names
|
# the compiler will avoid printing such names
|
||||||
# in user messages.
|
# in user messages.
|
||||||
|
|
||||||
|
sfHoisted* = sfForward
|
||||||
|
# an expression was hoised to an anonymous variable.
|
||||||
|
# the flag is applied to the var/let symbol
|
||||||
|
|
||||||
sfNoForward* = sfRegister
|
sfNoForward* = sfRegister
|
||||||
# forward declarations are not required (per module)
|
# forward declarations are not required (per module)
|
||||||
sfReorder* = sfForward
|
sfReorder* = sfForward
|
||||||
|
|
@ -454,6 +458,9 @@ type
|
||||||
nfPreventCg # this node should be ignored by the codegen
|
nfPreventCg # this node should be ignored by the codegen
|
||||||
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
|
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
|
||||||
nfFromTemplate # a top-level node returned from a template
|
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]
|
TNodeFlags* = set[TNodeFlag]
|
||||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that)
|
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that)
|
||||||
|
|
@ -571,8 +578,8 @@ type
|
||||||
TMagic* = enum # symbols that require compiler magic:
|
TMagic* = enum # symbols that require compiler magic:
|
||||||
mNone,
|
mNone,
|
||||||
mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
|
mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
|
||||||
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
|
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mType, mTypeOf,
|
||||||
mEcho, mShallowCopy, mSlurp, mStaticExec,
|
mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic,
|
||||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
|
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
|
||||||
mUnaryLt, mInc, mDec, mOrd,
|
mUnaryLt, mInc, mDec, mOrd,
|
||||||
mNew, mNewFinalize, mNewSeq, mNewSeqOfCap,
|
mNew, mNewFinalize, mNewSeq, mNewSeqOfCap,
|
||||||
|
|
@ -971,7 +978,7 @@ const
|
||||||
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
||||||
nfDotSetter, nfDotField,
|
nfDotSetter, nfDotField,
|
||||||
nfIsRef, nfPreventCg, nfLL,
|
nfIsRef, nfPreventCg, nfLL,
|
||||||
nfFromTemplate}
|
nfFromTemplate, nfDefaultRefsParam}
|
||||||
namePos* = 0
|
namePos* = 0
|
||||||
patternPos* = 1 # empty except for term rewriting macros
|
patternPos* = 1 # empty except for term rewriting macros
|
||||||
genericParamsPos* = 2
|
genericParamsPos* = 2
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,21 @@ when declared(echo):
|
||||||
proc debug*(conf: ConfigRef; n: PType) {.deprecated.}
|
proc debug*(conf: ConfigRef; n: PType) {.deprecated.}
|
||||||
proc debug*(conf: ConfigRef; n: PNode) {.deprecated.}
|
proc debug*(conf: ConfigRef; n: PNode) {.deprecated.}
|
||||||
|
|
||||||
template mdbg*: bool {.dirty.} =
|
template debug*(x: PSym|PType|PNode) {.deprecated.} =
|
||||||
when compiles(c.module):
|
when compiles(c.config):
|
||||||
|
debug(c.config, x)
|
||||||
|
elif compiles(c.graph.config):
|
||||||
|
debug(c.graph.config, x)
|
||||||
|
else:
|
||||||
|
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
|
c.module.fileIdx == c.config.projectMainIdx
|
||||||
elif compiles(c.c.module):
|
elif compiles(c.c.module):
|
||||||
c.c.module.fileIdx == c.c.config.projectMainIdx
|
c.c.module.fileIdx == c.c.config.projectMainIdx
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
defineSymbol("nimcomputedgoto")
|
defineSymbol("nimcomputedgoto")
|
||||||
defineSymbol("nimunion")
|
defineSymbol("nimunion")
|
||||||
defineSymbol("nimnewshared")
|
defineSymbol("nimnewshared")
|
||||||
|
defineSymbol("nimNewTypedesc")
|
||||||
defineSymbol("nimrequiresnimframe")
|
defineSymbol("nimrequiresnimframe")
|
||||||
defineSymbol("nimparsebiggestfloatmagic")
|
defineSymbol("nimparsebiggestfloatmagic")
|
||||||
defineSymbol("nimalias")
|
defineSymbol("nimalias")
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,18 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
|
||||||
if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
|
if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
|
||||||
result = not containsGarbageCollectedRef(t)
|
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;
|
proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
|
||||||
v: PNode; useShallowCopy=false): PSym =
|
v: PNode; useShallowCopy=false): PSym =
|
||||||
result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
|
result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,9 @@ type
|
||||||
SymbolMode = enum
|
SymbolMode = enum
|
||||||
smNormal, smAllowNil, smAfterDot
|
smNormal, smAllowNil, smAfterDot
|
||||||
|
|
||||||
|
TPrimaryMode = enum
|
||||||
|
pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
|
||||||
|
|
||||||
proc parseAll*(p: var TParser): PNode
|
proc parseAll*(p: var TParser): PNode
|
||||||
proc closeParser*(p: var TParser)
|
proc closeParser*(p: var TParser)
|
||||||
proc parseTopLevelStmt*(p: var TParser): PNode
|
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 postExprBlocks(p: var TParser, x: PNode): PNode
|
||||||
proc parseExprStmt(p: var TParser): PNode
|
proc parseExprStmt(p: var TParser): PNode
|
||||||
proc parseBlock(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
|
# implementation
|
||||||
|
|
||||||
proc getTok(p: var TParser) =
|
proc getTok(p: var TParser) =
|
||||||
|
|
@ -332,6 +338,8 @@ proc colcom(p: var TParser, n: PNode) =
|
||||||
eat(p, tkColon)
|
eat(p, tkColon)
|
||||||
skipComment(p, n)
|
skipComment(p, n)
|
||||||
|
|
||||||
|
const tkBuiltInMagics = {tkType, tkStatic, tkAddr}
|
||||||
|
|
||||||
proc parseSymbol(p: var TParser, mode = smNormal): PNode =
|
proc parseSymbol(p: var TParser, mode = smNormal): PNode =
|
||||||
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
|
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
|
||||||
#| | IDENT | KEYW
|
#| | IDENT | KEYW
|
||||||
|
|
@ -340,7 +348,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
|
||||||
result = newIdentNodeP(p.tok.ident, p)
|
result = newIdentNodeP(p.tok.ident, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
of tokKeywordLow..tokKeywordHigh:
|
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:
|
# for backwards compatibility these 2 are always valid:
|
||||||
result = newIdentNodeP(p.tok.ident, p)
|
result = newIdentNodeP(p.tok.ident, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -529,9 +537,6 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = a
|
result = a
|
||||||
|
|
||||||
type
|
|
||||||
TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
|
|
||||||
|
|
||||||
proc complexOrSimpleStmt(p: var TParser): PNode
|
proc complexOrSimpleStmt(p: var TParser): PNode
|
||||||
proc simpleExpr(p: var TParser, mode = pmNormal): 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 ')'
|
#| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
|
||||||
#| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
|
#| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
|
||||||
case p.tok.tokType
|
case p.tok.tokType
|
||||||
of tkSymbol, tkType, tkAddr:
|
of tkSymbol, tkBuiltInMagics:
|
||||||
result = newIdentNodeP(p.tok.ident, p)
|
result = newIdentNodeP(p.tok.ident, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result = parseGStrLit(p, result)
|
result = parseGStrLit(p, result)
|
||||||
|
|
@ -745,7 +750,12 @@ proc commandParam(p: var TParser, isFirstParam: var bool): PNode =
|
||||||
addSon(result, parseExpr(p))
|
addSon(result, parseExpr(p))
|
||||||
isFirstParam = false
|
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?
|
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
|
||||||
#| | doBlocks
|
#| | doBlocks
|
||||||
#| | '.' optInd symbol generalizedLit?
|
#| | '.' optInd symbol generalizedLit?
|
||||||
|
|
@ -762,7 +772,14 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||||
case p.tok.tokType
|
case p.tok.tokType
|
||||||
of tkParLe:
|
of tkParLe:
|
||||||
# progress guaranteed
|
# 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)
|
result = namedParams(p, result, nkCall, tkParRi)
|
||||||
if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
|
if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
|
||||||
result.kind = nkObjConstr
|
result.kind = nkObjConstr
|
||||||
|
|
@ -778,8 +795,13 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||||
# progress guaranteed
|
# progress guaranteed
|
||||||
somePar()
|
somePar()
|
||||||
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
|
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
|
||||||
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType,
|
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast,
|
||||||
tkOpr, tkDotDot:
|
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}):
|
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
|
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
|
||||||
# solution, but pragmas.nim can't handle that
|
# solution, but pragmas.nim can't handle that
|
||||||
|
|
@ -804,9 +826,6 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||||
else:
|
else:
|
||||||
break
|
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,
|
proc parseOperators(p: var TParser, headNode: PNode,
|
||||||
limit: int, mode: TPrimaryMode): PNode =
|
limit: int, mode: TPrimaryMode): PNode =
|
||||||
result = headNode
|
result = headNode
|
||||||
|
|
@ -1127,9 +1146,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
|
||||||
proc isExprStart(p: TParser): bool =
|
proc isExprStart(p: TParser): bool =
|
||||||
case p.tok.tokType
|
case p.tok.tokType
|
||||||
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
|
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,
|
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
|
||||||
tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
|
tkTuple, tkObject, tkWhen, tkCase, tkOut:
|
||||||
result = true
|
result = true
|
||||||
else: result = false
|
else: result = false
|
||||||
|
|
||||||
|
|
@ -1190,7 +1209,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
#| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
|
#| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
|
||||||
#| primary = typeKeyw typeDescK
|
#| primary = typeKeyw typeDescK
|
||||||
#| / prefixOperator* identOrLiteral primarySuffix*
|
#| / prefixOperator* identOrLiteral primarySuffix*
|
||||||
#| / 'static' primary
|
|
||||||
#| / 'bind' primary
|
#| / 'bind' primary
|
||||||
if isOperator(p.tok):
|
if isOperator(p.tok):
|
||||||
let isSigil = isSigilLike(p.tok)
|
let isSigil = isSigilLike(p.tok)
|
||||||
|
|
@ -1203,7 +1221,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
#XXX prefix operators
|
#XXX prefix operators
|
||||||
let baseInd = p.lex.currLineIndent
|
let baseInd = p.lex.currLineIndent
|
||||||
addSon(result, primary(p, pmSkipSuffix))
|
addSon(result, primary(p, pmSkipSuffix))
|
||||||
result = primarySuffix(p, result, baseInd)
|
result = primarySuffix(p, result, baseInd, mode)
|
||||||
else:
|
else:
|
||||||
addSon(result, primary(p, pmNormal))
|
addSon(result, primary(p, pmNormal))
|
||||||
return
|
return
|
||||||
|
|
@ -1233,14 +1251,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
result = parseTypeClass(p)
|
result = parseTypeClass(p)
|
||||||
else:
|
else:
|
||||||
parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
|
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:
|
of tkBind:
|
||||||
result = newNodeP(nkBind, p)
|
result = newNodeP(nkBind, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -1255,7 +1265,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
let baseInd = p.lex.currLineIndent
|
let baseInd = p.lex.currLineIndent
|
||||||
result = identOrLiteral(p, mode)
|
result = identOrLiteral(p, mode)
|
||||||
if mode != pmSkipSuffix:
|
if mode != pmSkipSuffix:
|
||||||
result = primarySuffix(p, result, baseInd)
|
result = primarySuffix(p, result, baseInd, mode)
|
||||||
|
|
||||||
proc parseTypeDesc(p: var TParser): PNode =
|
proc parseTypeDesc(p: var TParser): PNode =
|
||||||
#| typeDesc = simpleExpr
|
#| typeDesc = simpleExpr
|
||||||
|
|
|
||||||
|
|
@ -387,7 +387,9 @@ proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
|
||||||
assert(theEnd < 0)
|
assert(theEnd < 0)
|
||||||
result = 0
|
result = 0
|
||||||
for i in countup(start, sonsLen(n) + theEnd):
|
for i in countup(start, sonsLen(n) + theEnd):
|
||||||
inc(result, lsub(g, n.sons[i]))
|
let param = n.sons[i]
|
||||||
|
if nfDefaultParam notin param.flags:
|
||||||
|
inc(result, lsub(g, param))
|
||||||
inc(result, 2) # for ``, ``
|
inc(result, 2) # for ``, ``
|
||||||
if result > 0:
|
if result > 0:
|
||||||
dec(result, 2) # last does not get a comma!
|
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 finishMethod(c: PContext, s: PSym)
|
||||||
proc evalAtCompileTime(c: PContext, n: PNode): PNode
|
proc evalAtCompileTime(c: PContext, n: PNode): PNode
|
||||||
proc indexTypesMatch(c: PContext, f, a: PType, arg: 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.} =
|
proc isArrayConstr(n: PNode): bool {.inline.} =
|
||||||
result = n.kind == nkBracket and
|
result = n.kind == nkBracket and
|
||||||
n.typ.skipTypes(abstractInst).kind == tyArray
|
n.typ.skipTypes(abstractInst).kind == tyArray
|
||||||
|
|
@ -70,6 +73,16 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
|
||||||
# echo "passing to safeSemExpr: ", renderTree(n)
|
# echo "passing to safeSemExpr: ", renderTree(n)
|
||||||
discard safeSemExpr(c, 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 =
|
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||||
if arg.typ.isNil:
|
if arg.typ.isNil:
|
||||||
localError(c.config, arg.info, "expression has no type: " &
|
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 = copyTree(arg)
|
||||||
result.typ = formal
|
result.typ = formal
|
||||||
else:
|
else:
|
||||||
let x = result.skipConv
|
result = fitNodePostMatch(c, formal, result)
|
||||||
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 inferWithMetatype(c: PContext, formal: PType,
|
proc inferWithMetatype(c: PContext, formal: PType,
|
||||||
arg: PNode, coerceDistincts = false): PNode
|
arg: PNode, coerceDistincts = false): PNode
|
||||||
|
|
|
||||||
|
|
@ -391,7 +391,23 @@ proc inferWithMetatype(c: PContext, formal: PType,
|
||||||
result = copyTree(arg)
|
result = copyTree(arg)
|
||||||
result.typ = formal
|
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
|
assert x.state == csMatch
|
||||||
var finalCallee = x.calleeSym
|
var finalCallee = x.calleeSym
|
||||||
markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym)
|
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
|
result = x.call
|
||||||
instGenericConvertersSons(c, result, x)
|
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]
|
result.typ = finalCallee.typ.sons[0]
|
||||||
|
updateDefaultParams(result)
|
||||||
|
|
||||||
proc canDeref(n: PNode): bool {.inline.} =
|
proc canDeref(n: PNode): bool {.inline.} =
|
||||||
result = n.len >= 2 and (let t = n[1].typ;
|
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,
|
message(c.config, n.info, hintUserRaw,
|
||||||
"Non-matching candidates for " & renderTree(n) & "\n" &
|
"Non-matching candidates for " & renderTree(n) & "\n" &
|
||||||
candidates)
|
candidates)
|
||||||
result = semResolvedCall(c, n, r)
|
result = semResolvedCall(c, r, n, flags)
|
||||||
elif implicitDeref in c.features and canDeref(n):
|
elif implicitDeref in c.features and canDeref(n):
|
||||||
# try to deref the first argument and then try overloading resolution again:
|
# 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
|
n.sons[1] = n.sons[1].tryDeref
|
||||||
var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
|
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:
|
else:
|
||||||
# get rid of the deref again for a better error message:
|
# get rid of the deref again for a better error message:
|
||||||
n.sons[1] = n.sons[1].sons[0]
|
n.sons[1] = n.sons[1].sons[0]
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||||
result.addSonSkipIntLit(typ)
|
result.addSonSkipIntLit(typ)
|
||||||
|
|
||||||
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
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,
|
let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
|
||||||
c.config.options).linkTo(typedesc)
|
c.config.options).linkTo(typedesc)
|
||||||
return newSymNode(sym, info)
|
return newSymNode(sym, info)
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,25 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||||
return n
|
return n
|
||||||
|
|
||||||
result = newNodeI(nkConv, n.info)
|
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)
|
maybeLiftType(targetType, c, n[0].info)
|
||||||
|
|
||||||
if targetType.kind in {tySink, tyLent}:
|
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)
|
n.typ = getSysType(c.graph, n.info, tyInt)
|
||||||
result = n
|
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 =
|
proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
internalAssert c.config, n.sonsLen == 3 and
|
internalAssert c.config,
|
||||||
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
|
n.sonsLen == 3 and
|
||||||
|
n[1].typ != nil and
|
||||||
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
|
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}:
|
if n[2].kind in {nkStrLit..nkTripleStrLit}:
|
||||||
case n[2].strVal.normalize
|
case n[2].strVal.normalize
|
||||||
of "closure":
|
of "closure":
|
||||||
let t = skipTypes(t1, abstractRange)
|
let t = skipTypes(t1, abstractRange)
|
||||||
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
res = t.kind == tyProc and
|
||||||
t.callConv == ccClosure and
|
t.callConv == ccClosure and
|
||||||
tfIterator notin t.flags))
|
tfIterator notin t.flags
|
||||||
else:
|
else:
|
||||||
result = newIntNode(nkIntLit, 0)
|
res = false
|
||||||
else:
|
else:
|
||||||
var rhsOrigType = n[2].typ
|
|
||||||
var t2 = rhsOrigType.skipTypes({tyTypeDesc})
|
|
||||||
maybeLiftType(t2, c, n.info)
|
maybeLiftType(t2, c, n.info)
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
initCandidate(c, m, t2)
|
initCandidate(c, m, t2)
|
||||||
if efExplain in flags:
|
if efExplain in flags:
|
||||||
m.diagnostics = @[]
|
m.diagnostics = @[]
|
||||||
m.diagnosticsEnabled = true
|
m.diagnosticsEnabled = true
|
||||||
let match = typeRel(m, t2, t1) >= isSubtype # isNone
|
res = typeRel(m, t2, t1) >= isSubtype # isNone
|
||||||
result = newIntNode(nkIntLit, ord(match))
|
|
||||||
|
|
||||||
|
result = newIntNode(nkIntLit, ord(res))
|
||||||
result.typ = n.typ
|
result.typ = n.typ
|
||||||
|
|
||||||
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
if sonsLen(n) != 3:
|
if sonsLen(n) != 3:
|
||||||
localError(c.config, n.info, "'is' operator takes 2 arguments")
|
localError(c.config, n.info, "'is' operator takes 2 arguments")
|
||||||
|
|
||||||
|
let boolType = getSysType(c.graph, n.info, tyBool)
|
||||||
result = n
|
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})
|
n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
|
||||||
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
|
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
|
||||||
let t2 = semTypeNode(c, n[2], nil)
|
let t2 = semTypeNode(c, n[2], nil)
|
||||||
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
|
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:
|
if lhsType.kind != tyTypeDesc:
|
||||||
n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
if liftLhs:
|
||||||
elif lhsType.base.kind == tyNone:
|
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
|
# this is a typedesc variable, leave for evals
|
||||||
return
|
return
|
||||||
|
if lhsType.base.containsGenericType:
|
||||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||||
if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags)
|
return
|
||||||
|
|
||||||
|
result = isOpImpl(c, n, flags)
|
||||||
|
|
||||||
proc semOpAux(c: PContext, n: PNode) =
|
proc semOpAux(c: PContext, n: PNode) =
|
||||||
const flags = {efDetermineType}
|
const flags = {efDetermineType}
|
||||||
|
|
@ -483,6 +542,36 @@ proc fixAbstractType(c: PContext, n: PNode) =
|
||||||
proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
|
proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
|
||||||
result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
|
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 =
|
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
||||||
if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or
|
if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or
|
||||||
sfCompileToCpp in c.module.flags):
|
sfCompileToCpp in c.module.flags):
|
||||||
|
|
@ -632,7 +721,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||||
# echo "SUCCESS evaluated at compile time: ", call.renderTree
|
# echo "SUCCESS evaluated at compile time: ", call.renderTree
|
||||||
|
|
||||||
proc semStaticExpr(c: PContext, n: PNode): PNode =
|
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
|
if a.findUnresolvedStatic != nil: return a
|
||||||
result = evalStaticExpr(c.module, c.graph, a, c.p.owner)
|
result = evalStaticExpr(c.module, c.graph, a, c.p.owner)
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
|
|
@ -768,6 +857,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
else:
|
else:
|
||||||
result = m.call
|
result = m.call
|
||||||
instGenericConvertersSons(c, result, m)
|
instGenericConvertersSons(c, result, m)
|
||||||
|
|
||||||
elif t != nil and t.kind == tyTypeDesc:
|
elif t != nil and t.kind == tyTypeDesc:
|
||||||
if n.len == 1: return semObjConstr(c, n, flags)
|
if n.len == 1: return semObjConstr(c, n, flags)
|
||||||
return semConv(c, n)
|
return semConv(c, n)
|
||||||
|
|
@ -1034,7 +1124,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||||
of skType:
|
of skType:
|
||||||
markUsed(c.config, n.info, s, c.graph.usageSym)
|
markUsed(c.config, n.info, s, c.graph.usageSym)
|
||||||
styleCheckUse(n.info, s)
|
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
|
return s.typ.n
|
||||||
result = newSymNode(s, n.info)
|
result = newSymNode(s, n.info)
|
||||||
result.typ = makeTypeDesc(c, s.typ)
|
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
|
# make sure we don't evaluate generic macros/templates
|
||||||
n.sons[0] = semExprWithType(c, n.sons[0],
|
n.sons[0] = semExprWithType(c, n.sons[0],
|
||||||
{efNoEvaluateGeneric})
|
{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})
|
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
|
case arr.kind
|
||||||
of tyArray, tyOpenArray, tyVarargs, tySequence, tyString,
|
of tyArray, tyOpenArray, tyVarargs, tySequence, tyString,
|
||||||
tyCString:
|
tyCString:
|
||||||
|
|
@ -2426,8 +2526,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
# handling of sym choices is context dependent
|
# handling of sym choices is context dependent
|
||||||
# the node is left intact for now
|
# the node is left intact for now
|
||||||
discard
|
discard
|
||||||
of nkStaticExpr:
|
of nkStaticExpr: result = semStaticExpr(c, n[0])
|
||||||
result = semStaticExpr(c, n)
|
|
||||||
of nkAsgn: result = semAsgn(c, n)
|
of nkAsgn: result = semAsgn(c, n)
|
||||||
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
|
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
|
||||||
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
|
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,12 @@ type
|
||||||
cursorInBody: bool # only for nimsuggest
|
cursorInBody: bool # only for nimsuggest
|
||||||
bracketExpr: PNode
|
bracketExpr: PNode
|
||||||
|
|
||||||
type
|
|
||||||
TSemGenericFlag = enum
|
TSemGenericFlag = enum
|
||||||
withinBind, withinTypeDesc, withinMixin, withinConcept
|
withinBind,
|
||||||
|
withinTypeDesc,
|
||||||
|
withinMixin,
|
||||||
|
withinConcept
|
||||||
|
|
||||||
TSemGenericFlags = set[TSemGenericFlag]
|
TSemGenericFlags = set[TSemGenericFlag]
|
||||||
|
|
||||||
proc semGenericStmt(c: PContext, n: PNode,
|
proc semGenericStmt(c: PContext, n: PNode,
|
||||||
|
|
@ -53,8 +56,15 @@ template macroToExpand(s): untyped =
|
||||||
template macroToExpandSym(s): untyped =
|
template macroToExpandSym(s): untyped =
|
||||||
s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr
|
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,
|
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)
|
semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
|
||||||
incl(s.flags, sfUsed)
|
incl(s.flags, sfUsed)
|
||||||
case s.kind
|
case s.kind
|
||||||
|
|
@ -115,10 +125,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||||
else:
|
else:
|
||||||
if withinBind in flags:
|
if withinBind in flags:
|
||||||
result = symChoice(c, n, s, scClosed)
|
result = symChoice(c, n, s, scClosed)
|
||||||
elif s.name.id in ctx.toMixin:
|
elif s.isMixedIn:
|
||||||
result = symChoice(c, n, s, scForceOpen)
|
result = symChoice(c, n, s, scForceOpen)
|
||||||
else:
|
else:
|
||||||
result = semGenericStmtSymbol(c, n, s, ctx)
|
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||||
# else: leave as nkIdent
|
# else: leave as nkIdent
|
||||||
|
|
||||||
proc newDot(n, b: PNode): PNode =
|
proc newDot(n, b: PNode): PNode =
|
||||||
|
|
@ -135,7 +145,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||||
|
|
||||||
var s = qualifiedLookUp(c, n, luf)
|
var s = qualifiedLookUp(c, n, luf)
|
||||||
if s != nil:
|
if s != nil:
|
||||||
result = semGenericStmtSymbol(c, n, s, ctx)
|
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||||
else:
|
else:
|
||||||
n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
|
n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
|
||||||
result = n
|
result = n
|
||||||
|
|
@ -146,10 +156,10 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||||
isMacro = s.kind in {skTemplate, skMacro}
|
isMacro = s.kind in {skTemplate, skMacro}
|
||||||
if withinBind in flags:
|
if withinBind in flags:
|
||||||
result = newDot(result, symChoice(c, n, s, scClosed))
|
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))
|
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||||
else:
|
else:
|
||||||
let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true)
|
let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
|
||||||
if syms.kind == nkSym:
|
if syms.kind == nkSym:
|
||||||
let choice = symChoice(c, n, s, scForceOpen)
|
let choice = symChoice(c, n, s, scForceOpen)
|
||||||
choice.kind = nkClosedSymChoice
|
choice.kind = nkClosedSymChoice
|
||||||
|
|
@ -215,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||||
if s != nil:
|
if s != nil:
|
||||||
incl(s.flags, sfUsed)
|
incl(s.flags, sfUsed)
|
||||||
mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
|
mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
|
||||||
let sc = symChoice(c, fn, s,
|
let sc = symChoice(c, fn, s, if s.isMixedIn: scForceOpen else: scOpen)
|
||||||
if s.name.id in ctx.toMixin: scForceOpen else: scOpen)
|
|
||||||
case s.kind
|
case s.kind
|
||||||
of skMacro:
|
of skMacro:
|
||||||
if macroToExpand(s) and sc.safeLen <= 1:
|
if macroToExpand(s) and sc.safeLen <= 1:
|
||||||
|
|
@ -472,7 +481,6 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||||
when defined(nimsuggest):
|
when defined(nimsuggest):
|
||||||
if withinTypeDesc in flags: dec c.inTypeContext
|
if withinTypeDesc in flags: dec c.inTypeContext
|
||||||
|
|
||||||
|
|
||||||
proc semGenericStmt(c: PContext, n: PNode): PNode =
|
proc semGenericStmt(c: PContext, n: PNode): PNode =
|
||||||
var ctx: GenericCtx
|
var ctx: GenericCtx
|
||||||
ctx.toMixin = initIntset()
|
ctx.toMixin = initIntset()
|
||||||
|
|
@ -484,3 +492,4 @@ proc semConceptBody(c: PContext, n: PNode): PNode =
|
||||||
ctx.toMixin = initIntset()
|
ctx.toMixin = initIntset()
|
||||||
result = semGenericStmt(c, n, {withinConcept}, ctx)
|
result = semGenericStmt(c, n, {withinConcept}, ctx)
|
||||||
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
|
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,14 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
|
||||||
result = replaceTypeVarsT(cl, header)
|
result = replaceTypeVarsT(cl, header)
|
||||||
closeScope(c)
|
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,
|
proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||||
prc: PSym, info: TLineInfo) =
|
prc: PSym, info: TLineInfo) =
|
||||||
# XXX: Instantiates a generic proc signature, while at the same
|
# XXX: Instantiates a generic proc signature, while at the same
|
||||||
|
|
@ -247,25 +255,56 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||||
if i > 1:
|
if i > 1:
|
||||||
resetIdTable(cl.symMap)
|
resetIdTable(cl.symMap)
|
||||||
resetIdTable(cl.localCache)
|
resetIdTable(cl.localCache)
|
||||||
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
|
|
||||||
propagateToOwner(result, result.sons[i])
|
# 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
|
internalAssert c.config, originalParams[i].kind == nkSym
|
||||||
when true:
|
|
||||||
let oldParam = originalParams[i].sym
|
let oldParam = originalParams[i].sym
|
||||||
let param = copySym(oldParam)
|
let param = copySym(oldParam)
|
||||||
param.owner = prc
|
param.owner = prc
|
||||||
param.typ = result.sons[i]
|
param.typ = result[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])!
|
# The default value is instantiated and fitted against the final
|
||||||
result.n.sons[i] = newSymNode(param)
|
# concrete param type. We avoid calling `replaceTypeVarsN` on the
|
||||||
addDecl(c, param)
|
# 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:
|
else:
|
||||||
let param = replaceTypeVarsN(cl, originalParams[i])
|
param.ast = fitNodePostMatch(c, typeToFit, converted)
|
||||||
result.n.sons[i] = param
|
param.typ = result[i]
|
||||||
param.sym.owner = prc
|
|
||||||
addDecl(c, result.n.sons[i].sym)
|
result.n[i] = newSymNode(param)
|
||||||
|
propagateToOwner(result, result[i])
|
||||||
|
addDecl(c, param)
|
||||||
|
|
||||||
resetIdTable(cl.symMap)
|
resetIdTable(cl.symMap)
|
||||||
resetIdTable(cl.localCache)
|
resetIdTable(cl.localCache)
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,12 @@ const
|
||||||
errNoGenericParamsAllowedForX = "no generic parameters allowed for $1"
|
errNoGenericParamsAllowedForX = "no generic parameters allowed for $1"
|
||||||
errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types"
|
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 =
|
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
|
||||||
if prev == nil:
|
if prev == nil:
|
||||||
result = newTypeS(kind, c)
|
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]))
|
localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
|
||||||
|
|
||||||
for i in 0..1:
|
for i in 0..1:
|
||||||
if hasGenericArguments(range[i]):
|
if hasUnresolvedArgs(c, range[i]):
|
||||||
result.n.addSon makeStaticExpr(c, range[i])
|
result.n.addSon makeStaticExpr(c, range[i])
|
||||||
result.flags.incl tfUnresolved
|
result.flags.incl tfUnresolved
|
||||||
else:
|
else:
|
||||||
|
|
@ -295,7 +301,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||||
localError(c.config, info, errOrdinalTypeExpected)
|
localError(c.config, info, errOrdinalTypeExpected)
|
||||||
result = makeRangeWithStaticExpr(c, e)
|
result = makeRangeWithStaticExpr(c, e)
|
||||||
if c.inGenericContext > 0: result.flags.incl tfUnresolved
|
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):
|
if not isOrdinalType(e.typ):
|
||||||
localError(c.config, n[1].info, errOrdinalTypeExpected)
|
localError(c.config, n[1].info, errOrdinalTypeExpected)
|
||||||
# This is an int returning call, depending on an
|
# This is an int returning call, depending on an
|
||||||
|
|
@ -363,6 +369,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||||
if result != nil:
|
if result != nil:
|
||||||
markUsed(c.config, n.info, result, c.graph.usageSym)
|
markUsed(c.config, n.info, result, c.graph.usageSym)
|
||||||
styleCheckUse(n.info, result)
|
styleCheckUse(n.info, result)
|
||||||
|
|
||||||
if result.kind == skParam and result.typ.kind == tyTypeDesc:
|
if result.kind == skParam and result.typ.kind == tyTypeDesc:
|
||||||
# This is a typedesc param. is it already bound?
|
# This is a typedesc param. is it already bound?
|
||||||
# it's not bound when it's used multiple times in the
|
# it's not bound when it's used multiple times in the
|
||||||
|
|
@ -388,8 +395,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, errTypeExpected)
|
localError(c.config, n.info, errTypeExpected)
|
||||||
return errorSym(c, n)
|
return errorSym(c, n)
|
||||||
|
if result.kind != skType and result.magic notin (mStaticTy + mTypeTy):
|
||||||
if result.kind != skType:
|
|
||||||
# this implements the wanted ``var v: V, x: V`` feature ...
|
# this implements the wanted ``var v: V, x: V`` feature ...
|
||||||
var ov: TOverloadIter
|
var ov: TOverloadIter
|
||||||
var amb = initOverloadIter(ov, c, n)
|
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)
|
result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil)
|
||||||
|
|
||||||
of tyStatic:
|
of tyStatic:
|
||||||
# proc(a: expr{string}, b: expr{nkLambda})
|
if paramType.base.kind != tyNone and paramType.n != nil:
|
||||||
# overload on compile time values and AST trees
|
# this is a concrete static value
|
||||||
if paramType.n != nil: return # this is a concrete type
|
return
|
||||||
if tfUnresolved in paramType.flags: return # already lifted
|
if tfUnresolved in paramType.flags: return # already lifted
|
||||||
let base = paramType.base.maybeLift
|
let base = paramType.base.maybeLift
|
||||||
if base.isMetaType and procKind == skMacro:
|
if base.isMetaType and procKind == skMacro:
|
||||||
|
|
@ -879,7 +885,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
of tyDistinct:
|
of tyDistinct:
|
||||||
if paramType.sonsLen == 1:
|
if paramType.sonsLen == 1:
|
||||||
# disable the bindOnce behavior for the type class
|
# 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,
|
of tySequence, tySet, tyArray, tyOpenArray,
|
||||||
tyVar, tyLent, tyPtr, tyRef, tyProc:
|
tyVar, tyLent, tyPtr, tyRef, tyProc:
|
||||||
|
|
@ -996,13 +1005,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
prev: PType, kind: TSymKind; isType=false): PType =
|
prev: PType, kind: TSymKind; isType=false): PType =
|
||||||
# for historical reasons (code grows) this is invoked for parameter
|
# for historical reasons (code grows) this is invoked for parameter
|
||||||
# lists too and then 'isType' is false.
|
# lists too and then 'isType' is false.
|
||||||
var cl: IntSet
|
|
||||||
checkMinSonsLen(n, 1, c.config)
|
checkMinSonsLen(n, 1, c.config)
|
||||||
result = newProcType(c, n.info, prev)
|
result = newProcType(c, n.info, prev)
|
||||||
if genericParams != nil and sonsLen(genericParams) == 0:
|
|
||||||
cl = initIntSet()
|
|
||||||
var check = initIntSet()
|
var check = initIntSet()
|
||||||
var counter = 0
|
var counter = 0
|
||||||
|
|
||||||
for i in countup(1, n.len - 1):
|
for i in countup(1, n.len - 1):
|
||||||
var a = n.sons[i]
|
var a = n.sons[i]
|
||||||
if a.kind != nkIdentDefs:
|
if a.kind != nkIdentDefs:
|
||||||
|
|
@ -1012,6 +1019,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
# pass over this instantiation:
|
# pass over this instantiation:
|
||||||
if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
|
if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
|
||||||
illFormedAst(a, c.config)
|
illFormedAst(a, c.config)
|
||||||
|
|
||||||
checkMinSonsLen(a, 3, c.config)
|
checkMinSonsLen(a, 3, c.config)
|
||||||
var
|
var
|
||||||
typ: PType = nil
|
typ: PType = nil
|
||||||
|
|
@ -1020,26 +1028,52 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
length = sonsLen(a)
|
length = sonsLen(a)
|
||||||
hasType = a.sons[length-2].kind != nkEmpty
|
hasType = a.sons[length-2].kind != nkEmpty
|
||||||
hasDefault = a.sons[length-1].kind != nkEmpty
|
hasDefault = a.sons[length-1].kind != nkEmpty
|
||||||
|
|
||||||
if hasType:
|
if hasType:
|
||||||
typ = semParamType(c, a.sons[length-2], constraint)
|
typ = semParamType(c, a.sons[length-2], constraint)
|
||||||
|
|
||||||
if hasDefault:
|
if hasDefault:
|
||||||
def = semExprWithType(c, a.sons[length-1])
|
def = a[^1]
|
||||||
# check type compatibility between def.typ and typ:
|
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:
|
if typ == nil:
|
||||||
typ = def.typ
|
typ = def.typ
|
||||||
elif def != nil:
|
if typ.kind == tyTypeDesc:
|
||||||
# and def.typ != nil and def.typ.kind != tyNone:
|
# 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:
|
# example code that triggers it:
|
||||||
# proc sort[T](cmp: proc(a, b: T): int = cmp)
|
# proc sort[T](cmp: proc(a, b: T): int = cmp)
|
||||||
if not containsGenericType(typ):
|
if not containsGenericType(typ):
|
||||||
|
# check type compatibility between def.typ and typ:
|
||||||
def = fitNode(c, typ, def, def.info)
|
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 not hasType and not hasDefault:
|
||||||
if isType: localError(c.config, a.info, "':' expected")
|
if isType: localError(c.config, a.info, "':' expected")
|
||||||
if kind in {skTemplate, skMacro}:
|
if kind in {skTemplate, skMacro}:
|
||||||
typ = newTypeS(tyExpr, c)
|
typ = newTypeS(tyExpr, c)
|
||||||
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
|
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for j in countup(0, length-3):
|
for j in countup(0, length-3):
|
||||||
var arg = newSymG(skParam, a.sons[j], c)
|
var arg = newSymG(skParam, a.sons[j], c)
|
||||||
if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
|
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.position = counter
|
||||||
arg.constraint = constraint
|
arg.constraint = constraint
|
||||||
inc(counter)
|
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):
|
if containsOrIncl(check, arg.name.id):
|
||||||
localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'")
|
localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'")
|
||||||
addSon(result.n, newSymNode(arg))
|
addSon(result.n, newSymNode(arg))
|
||||||
|
|
@ -1310,7 +1345,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||||
incl dummyParam.flags, sfUsed
|
incl dummyParam.flags, sfUsed
|
||||||
addDecl(c, dummyParam)
|
addDecl(c, dummyParam)
|
||||||
|
|
||||||
result.n.sons[3] = semConceptBody(c, n[3])
|
result.n[3] = semConceptBody(c, n[3])
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
|
|
||||||
proc semProcTypeWithScope(c: PContext, n: PNode,
|
proc semProcTypeWithScope(c: PContext, n: PNode,
|
||||||
|
|
@ -1349,6 +1384,12 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
|
||||||
localError(c.config, n.info, errTypeExpected)
|
localError(c.config, n.info, errTypeExpected)
|
||||||
result = errorSym(c, n)
|
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 =
|
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = nil
|
result = nil
|
||||||
inc c.inTypeContext
|
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 mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
||||||
of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
|
of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
|
||||||
of mVarargs: result = semVarargs(c, n, 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:
|
of mExpr:
|
||||||
result = semTypeNode(c, n.sons[0], nil)
|
result = semTypeNode(c, n.sons[0], nil)
|
||||||
if result != 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 nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
|
||||||
of nkVarTy: result = semVarType(c, n, prev)
|
of nkVarTy: result = semVarType(c, n, prev)
|
||||||
of nkDistinctTy: result = semDistinct(c, n, prev)
|
of nkDistinctTy: result = semDistinct(c, n, prev)
|
||||||
of nkStaticTy:
|
of nkStaticTy: result = semStaticType(c, n[0], prev)
|
||||||
result = newOrPrevType(tyStatic, prev, c)
|
|
||||||
var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
|
|
||||||
result.rawAddSon(base)
|
|
||||||
result.flags.incl tfHasStatic
|
|
||||||
of nkIteratorTy:
|
of nkIteratorTy:
|
||||||
if n.sonsLen == 0:
|
if n.sonsLen == 0:
|
||||||
result = newTypeS(tyBuiltInTypeClass, c)
|
result = newTypeS(tyBuiltInTypeClass, c)
|
||||||
|
|
@ -1645,9 +1686,12 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||||
of mStmt:
|
of mStmt:
|
||||||
setMagicType(c.config, m, tyStmt, 0)
|
setMagicType(c.config, m, tyStmt, 0)
|
||||||
if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt
|
if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt
|
||||||
of mTypeDesc:
|
of mTypeDesc, mType:
|
||||||
setMagicType(c.config, m, tyTypeDesc, 0)
|
setMagicType(c.config, m, tyTypeDesc, 0)
|
||||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||||
|
of mStatic:
|
||||||
|
setMagicType(c.config, m, tyStatic, 0)
|
||||||
|
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||||
of mVoidType:
|
of mVoidType:
|
||||||
setMagicType(c.config, m, tyVoid, 0)
|
setMagicType(c.config, m, tyVoid, 0)
|
||||||
of mArray:
|
of mArray:
|
||||||
|
|
|
||||||
|
|
@ -144,17 +144,6 @@ proc isTypeParam(n: PNode): bool =
|
||||||
(n.sym.kind == skGenericParam or
|
(n.sym.kind == skGenericParam or
|
||||||
(n.sym.kind == skType and sfFromGeneric in n.sym.flags))
|
(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 =
|
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
|
||||||
# This is needed for tgenericshardcases
|
# This is needed for tgenericshardcases
|
||||||
# It's possible that a generic param will be used in a proc call to a
|
# 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:
|
# symbol is not our business:
|
||||||
if cl.owner != nil and s.owner != cl.owner:
|
if cl.owner != nil and s.owner != cl.owner:
|
||||||
return s
|
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))
|
#result = PSym(idTableGet(cl.symMap, s))
|
||||||
#if result == nil:
|
#if result == nil:
|
||||||
result = copySym(s, false)
|
result = copySym(s, false)
|
||||||
|
|
@ -278,7 +278,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# is difficult to handle:
|
# is difficult to handle:
|
||||||
const eqFlags = eqTypeFlags + {tfGcSafe}
|
const eqFlags = eqTypeFlags + {tfGcSafe}
|
||||||
var body = t.sons[0]
|
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
|
var header: PType = t
|
||||||
# search for some instantiation here:
|
# search for some instantiation here:
|
||||||
if cl.allowMetaTypes:
|
if cl.allowMetaTypes:
|
||||||
|
|
|
||||||
|
|
@ -1089,8 +1089,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||||
else: isNone
|
else: isNone
|
||||||
|
|
||||||
of tyAnything:
|
of tyAnything:
|
||||||
return if f.kind == tyAnything: isGeneric
|
if f.kind == tyAnything: return isGeneric
|
||||||
else: isNone
|
else: return isNone
|
||||||
|
|
||||||
of tyUserTypeClass, tyUserTypeClassInst:
|
of tyUserTypeClass, tyUserTypeClassInst:
|
||||||
if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4:
|
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))
|
let prev = PType(idTableGet(c.bindings, f))
|
||||||
if prev == nil:
|
if prev == nil:
|
||||||
if aOrig.kind == tyStatic:
|
if aOrig.kind == tyStatic:
|
||||||
result = typeRel(c, f.lastSon, a)
|
if f.base.kind != tyNone:
|
||||||
|
result = typeRel(c, f.base, a)
|
||||||
if result != isNone and f.n != nil:
|
if result != isNone and f.n != nil:
|
||||||
if not exprStructuralEquivalent(f.n, aOrig.n):
|
if not exprStructuralEquivalent(f.n, aOrig.n):
|
||||||
result = isNone
|
result = isNone
|
||||||
|
else:
|
||||||
|
result = isGeneric
|
||||||
if result != isNone: put(c, f, aOrig)
|
if result != isNone: put(c, f, aOrig)
|
||||||
elif aOrig.n != nil and aOrig.n.typ != nil:
|
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:
|
if result != isNone:
|
||||||
var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])
|
var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])
|
||||||
boundType.n = aOrig.n
|
boundType.n = aOrig.n
|
||||||
|
|
@ -1707,9 +1711,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||||
# proc foo(T: typedesc, x: T)
|
# proc foo(T: typedesc, x: T)
|
||||||
# when `f` is an unresolved typedesc, `a` could be any
|
# when `f` is an unresolved typedesc, `a` could be any
|
||||||
# type, so we should not perform this check earlier
|
# type, so we should not perform this check earlier
|
||||||
if a.kind != tyTypeDesc: return isNone
|
if a.kind != tyTypeDesc:
|
||||||
|
if a.kind == tyGenericParam and tfWildcard in a.flags:
|
||||||
if f.base.kind == tyNone:
|
# TODO: prevent `a` from matching as a wildcard again
|
||||||
|
result = isGeneric
|
||||||
|
else:
|
||||||
|
result = isNone
|
||||||
|
elif f.base.kind == tyNone:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
else:
|
else:
|
||||||
result = typeRel(c, f.base, a.base)
|
result = typeRel(c, f.base, a.base)
|
||||||
|
|
@ -2353,12 +2361,22 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
|
||||||
m.firstMismatch = f
|
m.firstMismatch = f
|
||||||
break
|
break
|
||||||
else:
|
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)
|
var def = copyTree(formal.ast)
|
||||||
if def.kind == nkNilLit:
|
if def.kind == nkNilLit:
|
||||||
def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
|
def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
|
||||||
if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
|
if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
|
||||||
put(m, formal.typ, def.typ)
|
put(m, formal.typ, def.typ)
|
||||||
|
def.flags.incl nfDefaultParam
|
||||||
setSon(m.call, formal.position + 1, def)
|
setSon(m.call, formal.position + 1, def)
|
||||||
inc(f)
|
inc(f)
|
||||||
# forget all inferred types if the overload matching failed
|
# forget all inferred types if the overload matching failed
|
||||||
|
|
|
||||||
|
|
@ -780,6 +780,43 @@ proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = n
|
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 =
|
proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
when false:
|
when false:
|
||||||
var oldDeferAnchor: PNode
|
var oldDeferAnchor: PNode
|
||||||
|
|
@ -849,6 +886,15 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
of nkBreakStmt: result = transformBreak(c, n)
|
of nkBreakStmt: result = transformBreak(c, n)
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
result = transformCall(c, n)
|
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:
|
of nkAddr, nkHiddenAddr:
|
||||||
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
|
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
|
||||||
of nkDerefExpr, nkHiddenDeref:
|
of nkDerefExpr, nkHiddenDeref:
|
||||||
|
|
|
||||||
|
|
@ -502,7 +502,10 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
#internalAssert t.len == 0
|
#internalAssert t.len == 0
|
||||||
result = "untyped"
|
result = "untyped"
|
||||||
of tyFromExpr:
|
of tyFromExpr:
|
||||||
result = renderTree(t.n)
|
if t.n == nil:
|
||||||
|
result = "unknown"
|
||||||
|
else:
|
||||||
|
result = "type(" & renderTree(t.n) & ")"
|
||||||
of tyArray:
|
of tyArray:
|
||||||
if t.sons[0].kind == tyRange:
|
if t.sons[0].kind == tyRange:
|
||||||
result = "array[" & rangeToStr(t.sons[0].n) & ", " &
|
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]>`_
|
`#len,seq[T] <system.html#len,seq[T]>`_
|
||||||
* ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>**
|
* ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>**
|
||||||
`#pairs.i,seq[T] <system.html#pairs.i,seq[T]>`_
|
`#pairs.i,seq[T] <system.html#pairs.i,seq[T]>`_
|
||||||
* ``template newException[](exceptn: typedesc; message: string): expr`` **=>**
|
* ``template newException[](exceptn: type; message: string): expr`` **=>**
|
||||||
`#newException.t,typedesc,string
|
`#newException.t,type,string
|
||||||
<system.html#newException.t,typedesc,string>`_
|
<system.html#newException.t,type,string>`_
|
||||||
|
|
||||||
|
|
||||||
Index (idx) file format
|
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_.
|
currency. This can be solved with templates_.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template additive(typ: typedesc) =
|
template additive(typ: type) =
|
||||||
proc `+` *(x, y: typ): typ {.borrow.}
|
proc `+` *(x, y: typ): typ {.borrow.}
|
||||||
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.}
|
||||||
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: typ, y: base): typ {.borrow.}
|
||||||
proc `*` *(x: base, y: typ): typ {.borrow.}
|
proc `*` *(x: base, y: typ): typ {.borrow.}
|
||||||
proc `div` *(x: typ, y: base): typ {.borrow.}
|
proc `div` *(x: typ, y: base): typ {.borrow.}
|
||||||
proc `mod` *(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.}
|
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)
|
rem unresolvedExpression(undeclaredIdentifier)
|
||||||
|
|
||||||
``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other
|
``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
|
Varargs matching
|
||||||
|
|
@ -4274,29 +4274,6 @@ therefore very useful for type specialization within generic code:
|
||||||
deletedKeys: seq[bool]
|
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
|
Type Classes
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
@ -4450,10 +4427,10 @@ the presence of callable symbols with specific signatures:
|
||||||
OutputStream = concept var s
|
OutputStream = concept var s
|
||||||
s.write(string)
|
s.write(string)
|
||||||
|
|
||||||
In order to check for symbols accepting ``typedesc`` params, you must prefix
|
In order to check for symbols accepting ``type`` params, you must prefix
|
||||||
the type with an explicit ``type`` modifier. The named instance of the type,
|
the type with the explicit ``type`` modifier. The named instance of the
|
||||||
following the ``concept`` keyword is also considered an explicit ``typedesc``
|
type, following the ``concept`` keyword is also considered to have the
|
||||||
value that will be matched only as a type.
|
explicit modifier and will be matched only as a type.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
|
|
@ -4513,7 +4490,7 @@ The concept types can be parametric just like the regular generic types:
|
||||||
import typetraits
|
import typetraits
|
||||||
|
|
||||||
type
|
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.ValueType is T
|
||||||
M.Rows == R
|
M.Rows == R
|
||||||
M.Cols == C
|
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]
|
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]
|
AnyTransform3D* = AnyMatrix[4, 4, float]
|
||||||
|
|
||||||
|
|
@ -4542,7 +4519,7 @@ The concept types can be parametric just like the regular generic types:
|
||||||
### matrix.nim
|
### matrix.nim
|
||||||
|
|
||||||
type
|
type
|
||||||
Matrix*[M, N: static[int]; T] = object
|
Matrix*[M, N: static int; T] = object
|
||||||
data: array[M*N, T]
|
data: array[M*N, T]
|
||||||
|
|
||||||
proc `[]`*(M: Matrix; m, n: int): M.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
|
# Adapt the Matrix type to the concept's requirements
|
||||||
template Rows*(M: type Matrix): expr = M.M
|
template Rows*(M: type Matrix): expr = M.M
|
||||||
template Cols*(M: type Matrix): expr = M.N
|
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
|
### usage.nim
|
||||||
|
|
@ -4582,7 +4559,7 @@ operator and also when types dependent on them are being matched:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
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]
|
x.reduce(SquareMatrix[N, T]) is array[M, int]
|
||||||
|
|
||||||
The Nim compiler includes a simple linear equation solver, allowing it to
|
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 varargs param will here be converted to an array of StringRefValues
|
||||||
# the proc will have only two instantiations for the two character types
|
# 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
|
# this proc will allow char and wchar values to be mixed in
|
||||||
# the same call at the cost of additional instantiations
|
# the same call at the cost of additional instantiations
|
||||||
# the varargs param will be converted to a tuple
|
# 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.
|
| ``notin`` and ``isnot`` have the obvious meanings.
|
||||||
|
|
||||||
The "types" of templates can be the symbols ``untyped``,
|
The "types" of templates can be the symbols ``untyped``,
|
||||||
``typed`` or ``typedesc`` (stands for *type
|
``typed`` or ``type``. These are "meta types", they can only be used in certain
|
||||||
description*). These are "meta types", they can only be used in certain
|
contexts. Regular types can be used too; this implies that ``typed`` expressions
|
||||||
contexts. Real types can be used too; this implies that ``typed`` expressions
|
|
||||||
are expected.
|
are expected.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5109,7 +5085,7 @@ In templates identifiers can be constructed with the backticks notation:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template typedef(name: untyped, typ: typedesc) =
|
template typedef(name: untyped, typ: type) =
|
||||||
type
|
type
|
||||||
`T name`* {.inject.} = typ
|
`T name`* {.inject.} = typ
|
||||||
`P name`* {.inject.} = ref `T name`
|
`P name`* {.inject.} = ref `T name`
|
||||||
|
|
@ -5171,7 +5147,7 @@ template cannot be accessed in the instantiation context:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template newException*(exceptn: typedesc, message: string): untyped =
|
template newException*(exceptn: type, message: string): untyped =
|
||||||
var
|
var
|
||||||
e: ref exceptn # e is implicitly gensym'ed here
|
e: ref exceptn # e is implicitly gensym'ed here
|
||||||
new(e)
|
new(e)
|
||||||
|
|
@ -5493,7 +5469,7 @@ As their name suggests, static parameters must be known at compile-time:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
proc precompiledRegex(pattern: static[string]): RegEx =
|
proc precompiledRegex(pattern: static string): RegEx =
|
||||||
var res {.global.} = re(pattern)
|
var res {.global.} = re(pattern)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
@ -5513,9 +5489,9 @@ Static params can also appear in the signatures of generic types:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
type
|
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
|
# 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]
|
AffineTransform2D[T] = Matrix[3, 3, T]
|
||||||
AffineTransform3D[T] = Matrix[4, 4, 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 m1: AffineTransform3D[float] # OK
|
||||||
var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
|
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
|
.. code-block:: nim
|
||||||
(i.e. if types are compile-time values and all values have a type, then
|
import math
|
||||||
typedesc must be their type).
|
|
||||||
|
|
||||||
When used as a regular proc param, typedesc acts as a type class. The proc
|
echo static(fac(5)), " ", static[bool](16.isPowerOfTwo)
|
||||||
will be instantiated for each unique type parameter and one can refer to the
|
|
||||||
instantiation type using the param name:
|
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
|
.. code-block:: nim
|
||||||
|
|
||||||
proc new(T: typedesc): ref T =
|
proc new(T: type): ref T =
|
||||||
echo "allocating ", T.name
|
echo "allocating ", T.name
|
||||||
new(result)
|
new(result)
|
||||||
|
|
||||||
var n = Node.new
|
var n = Node.new
|
||||||
var tree = new(BinaryTree[int])
|
var tree = new(BinaryTree[int])
|
||||||
|
|
||||||
When multiple typedesc params are present, they will bind freely to different
|
When multiple type params are present, they will bind freely to different
|
||||||
types. To force a bind-once behavior
|
types. To force a bind-once behavior one can use an explicit generic param:
|
||||||
one can use an explicit ``typedesc[T]`` generic param:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
.. 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
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template declareVariableWithType(T: typedesc, value: T) =
|
template declareVariableWithType(T: type, value: T) =
|
||||||
var x: T = value
|
var x: T = value
|
||||||
|
|
||||||
declareVariableWithType int, 42
|
declareVariableWithType int, 42
|
||||||
|
|
||||||
|
|
||||||
Overload resolution can be further influenced by constraining the set of
|
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
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template maxval(T: typedesc[int]): int = high(int)
|
template maxval(T: type int): int = high(int)
|
||||||
template maxval(T: typedesc[float]): float = Inf
|
template maxval(T: type float): float = Inf
|
||||||
|
|
||||||
var i = int.maxval
|
var i = int.maxval
|
||||||
var f = float.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.
|
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
|
Special Operators
|
||||||
|
|
@ -7458,7 +7484,7 @@ Custom pragmas are defined using templates annotated with pragma ``pragma``:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template dbTable(name: string, table_space: string = "") {.pragma.}
|
template dbTable(name: string, table_space: string = "") {.pragma.}
|
||||||
template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
|
template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
|
||||||
template dbForeignKey(t: typedesc) {.pragma.}
|
template dbForeignKey(t: type) {.pragma.}
|
||||||
template dbIgnore {.pragma.}
|
template dbIgnore {.pragma.}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -618,9 +618,9 @@ Turning the ``log`` proc into a template solves this problem:
|
||||||
log("x has the value: " & $x)
|
log("x has the value: " & $x)
|
||||||
|
|
||||||
The parameters' types can be ordinary types or the meta types ``untyped``,
|
The parameters' types can be ordinary types or the meta types ``untyped``,
|
||||||
``typed``, or ``typedesc``.
|
``typed``, or ``type``. ``type`` suggests that only a type symbol may be given
|
||||||
``typedesc`` stands for *type description*, and ``untyped`` means symbol lookups and
|
as an argument, and ``untyped`` means symbol lookups and type resolution is not
|
||||||
type resolution is not performed before the expression is passed to the template.
|
performed before the expression is passed to the template.
|
||||||
|
|
||||||
If the template has no explicit return type,
|
If the template has no explicit return type,
|
||||||
``void`` is used for consistency with procs and methods.
|
``void`` is used for consistency with procs and methods.
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
## Note: For inter thread communication use
|
## Note: For inter thread communication use
|
||||||
## a `Channel <channels.html>`_ instead.
|
## a `Channel <channels.html>`_ instead.
|
||||||
|
|
||||||
import math
|
import math, typetraits
|
||||||
|
|
||||||
type
|
type
|
||||||
Deque*[T] = object
|
Deque*[T] = object
|
||||||
|
|
@ -160,16 +160,18 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} =
|
||||||
emptyCheck(deq)
|
emptyCheck(deq)
|
||||||
result = deq.data[(deq.tail - 1) and deq.mask]
|
result = deq.data[(deq.tail - 1) and deq.mask]
|
||||||
|
|
||||||
template default[T](t: typedesc[T]): T =
|
template destroy(x: untyped) =
|
||||||
var v: T
|
when defined(nimNewRuntime) and not supportsCopyMem(type(x)):
|
||||||
v
|
`=destroy`(x)
|
||||||
|
else:
|
||||||
|
reset(x)
|
||||||
|
|
||||||
proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
||||||
## Remove and returns the first element of the `deq`.
|
## Remove and returns the first element of the `deq`.
|
||||||
emptyCheck(deq)
|
emptyCheck(deq)
|
||||||
dec deq.count
|
dec deq.count
|
||||||
result = deq.data[deq.head]
|
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
|
deq.head = (deq.head + 1) and deq.mask
|
||||||
|
|
||||||
proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
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
|
dec deq.count
|
||||||
deq.tail = (deq.tail - 1) and deq.mask
|
deq.tail = (deq.tail - 1) and deq.mask
|
||||||
result = deq.data[deq.tail]
|
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 =
|
proc `$`*[T](deq: Deque[T]): string =
|
||||||
## Turn a deque into its string representation.
|
## Turn a deque into its string representation.
|
||||||
|
|
@ -215,6 +244,22 @@ when isMainModule:
|
||||||
assert deq.find(6) >= 0
|
assert deq.find(6) >= 0
|
||||||
assert deq.find(789) < 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:
|
for i in -2 .. 10:
|
||||||
if i in deq:
|
if i in deq:
|
||||||
assert deq.contains(i) and deq.find(i) >= 0
|
assert deq.contains(i) and deq.find(i) >= 0
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,11 @@ proc write*(s: Stream, x: string) =
|
||||||
else:
|
else:
|
||||||
if x.len > 0: writeData(s, cstring(x), x.len)
|
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, `$`]) =
|
proc writeLine*(s: Stream, args: varargs[string, `$`]) =
|
||||||
## writes one or more strings to the the stream `s` followed
|
## writes one or more strings to the the stream `s` followed
|
||||||
## by a new line. No length field or terminating zero is written.
|
## 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 =
|
proc readLine*(s: Stream, line: var TaintedString): bool =
|
||||||
## reads a line of text from the stream `s` into `line`. `line` must not be
|
## reads a line of text from the stream `s` into `line`. `line` must not be
|
||||||
## ``nil``! May throw an IO exception.
|
## ``nil``! May throw an IO exception.
|
||||||
## A line of text may be delimited by ``CR``, ``LF`` or
|
## A line of text may be delimited by ```LF`` or ``CRLF``.
|
||||||
## ``CRLF``. The newline character(s) are not part of the returned string.
|
## The newline character(s) are not part of the returned string.
|
||||||
## Returns ``false`` if the end of the file has been reached, ``true``
|
## Returns ``false`` if the end of the file has been reached, ``true``
|
||||||
## otherwise. If ``false`` is returned `line` contains no new data.
|
## otherwise. If ``false`` is returned `line` contains no new data.
|
||||||
line.string.setLen(0)
|
line.string.setLen(0)
|
||||||
|
|
@ -317,6 +322,13 @@ proc peekLine*(s: Stream): TaintedString =
|
||||||
defer: setPosition(s, pos)
|
defer: setPosition(s, pos)
|
||||||
result = readLine(s)
|
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):
|
when not defined(js):
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,20 @@ proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
|
||||||
## Cannot be overloaded.
|
## Cannot be overloaded.
|
||||||
discard
|
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.} =
|
proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} =
|
||||||
## Builtin 'type' operator for accessing the type of an expression.
|
## Builtin 'type' operator for accessing the type of an expression.
|
||||||
## Cannot be overloaded.
|
## Cannot be overloaded.
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ template checkOsError =
|
||||||
if err.len > 0: raise newException(OSError, err)
|
if err.len > 0: raise newException(OSError, err)
|
||||||
|
|
||||||
template log(msg: string, body: untyped) =
|
template log(msg: string, body: untyped) =
|
||||||
if mode == ScriptMode.Verbose or mode == ScriptMode.Whatif:
|
if mode in {ScriptMode.Verbose, ScriptMode.Whatif}:
|
||||||
echo "[NimScript] ", msg
|
echo "[NimScript] ", msg
|
||||||
if mode != ScriptMode.WhatIf:
|
if mode != ScriptMode.WhatIf:
|
||||||
body
|
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"
|
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] = @[]
|
#s[1..3] = @[]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,16 @@ type
|
||||||
x: T
|
x: T
|
||||||
y: U
|
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)
|
result = "float " & $(a.len > 5)
|
||||||
|
|
||||||
proc foo(T: typedesc[TFoo], a: int): string =
|
proc foo(T: typedesc[TFoo], a: int): string =
|
||||||
result = "TFoo " & $(a)
|
result = "TFoo " & $(a)
|
||||||
|
|
||||||
proc foo(T: typedesc[int or bool]): string =
|
proc foo(T: type[int or bool]): string =
|
||||||
var a: T
|
var a: T
|
||||||
a = 10
|
a = 10
|
||||||
result = "int or bool " & ($a)
|
result = "int or bool " & ($a)
|
||||||
|
|
@ -23,8 +24,8 @@ template foo(T: typedesc[seq]): string = "seq"
|
||||||
test "types can be used as proc params":
|
test "types can be used as proc params":
|
||||||
# XXX: `check` needs to know that TFoo[int, float] is a type and
|
# XXX: `check` needs to know that TFoo[int, float] is a type and
|
||||||
# cannot be assigned for a local variable for later inspection
|
# cannot be assigned for a local variable for later inspection
|
||||||
check ((string.getTypeName == "string"))
|
check ((string.getTypeName1 == "string"))
|
||||||
check ((getTypeName(int) == "int"))
|
check ((getTypeName2(int) == "int"))
|
||||||
|
|
||||||
check ((foo(TFoo[int, float], 1000) == "TFoo 1000"))
|
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[int]) == "seq"))
|
||||||
check ((foo(seq[TFoo[bool, string]]) == "seq"))
|
check ((foo(seq[TFoo[bool, string]]) == "seq"))
|
||||||
|
|
||||||
when false:
|
template accept(x) =
|
||||||
proc foo(T: typedesc[seq], s: T) = nil
|
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]
|
type Point[T] = tuple[x, y: T]
|
||||||
proc origin(T: typedesc): Point[T] = discard
|
proc origin(T: typedesc): Point[T] = discard
|
||||||
discard origin(int)
|
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
|
Base = object of RootObj
|
||||||
Child = object of Base
|
Child = object of Base
|
||||||
|
|
||||||
proc pr(T: typedesc[Base]) = echo "proc " & T.name
|
proc pr(T: type[Base]) = echo "proc " & T.name
|
||||||
method me(T: typedesc[Base]) = echo "method " & T.name
|
method me(T: type[Base]) = echo "method " & T.name
|
||||||
iterator it(T: typedesc[Base]): auto = yield "yield " & T.name
|
iterator it(T: type[Base]): auto = yield "yield " & T.name
|
||||||
|
|
||||||
Base.pr
|
Base.pr
|
||||||
Child.pr
|
Child.pr
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,16 @@ output: "8\n8\n4"
|
||||||
import
|
import
|
||||||
macros, typetraits
|
macros, typetraits
|
||||||
|
|
||||||
template selectType(x: int): typeDesc =
|
template selectType(x: int): type =
|
||||||
when x < 10:
|
when x < 10:
|
||||||
int
|
int
|
||||||
else:
|
else:
|
||||||
string
|
string
|
||||||
|
|
||||||
template simpleTypeTempl: typeDesc =
|
template simpleTypeTempl: type =
|
||||||
string
|
string
|
||||||
|
|
||||||
macro typeFromMacro: typedesc = string
|
macro typeFromMacro: type = string
|
||||||
|
|
||||||
# The tests below check that the result variable of the
|
# The tests below check that the result variable of the
|
||||||
# selected type matches the literal types in the code:
|
# 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
|
type
|
||||||
R = ref
|
R = ref
|
||||||
V = var
|
V = var
|
||||||
D = distinct
|
D = distinct
|
||||||
P = ptr
|
P = ptr
|
||||||
|
T = type
|
||||||
|
S = static
|
||||||
|
OBJ = object
|
||||||
|
TPL = tuple
|
||||||
|
SEQ = seq
|
||||||
|
|
||||||
|
var i: int
|
||||||
var x: ref int
|
var x: ref int
|
||||||
var y: distinct int
|
var y: distinct int
|
||||||
var z: ptr 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
|
false
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
no'''
|
yes'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc IsVoid[T](): string =
|
proc IsVoid[T](): string =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue