Merge pull request #7681 from nim-lang/typedesc-reforms

Typedesc reforms
This commit is contained in:
Andreas Rumpf 2018-06-26 23:53:30 +02:00 • committed by GitHub
commit 7cec5d1cfb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 1702 additions and 270 deletions

View file

@ -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

View file

@ -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

View file

@ -31,26 +31,39 @@ 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):
c.module.fileIdx == c.config.projectMainIdx debug(c.config, x)
elif compiles(c.c.module): elif compiles(c.graph.config):
c.c.module.fileIdx == c.c.config.projectMainIdx debug(c.graph.config, x)
elif compiles(m.c.module):
m.c.module.fileIdx == m.c.config.projectMainIdx
elif compiles(cl.c.module):
cl.c.module.fileIdx == cl.c.config.projectMainIdx
elif compiles(p):
when compiles(p.lex):
p.lex.fileIdx == p.lex.config.projectMainIdx
else: else:
p.module.module.fileIdx == p.config.projectMainIdx error()
elif compiles(m.module.fileIdx):
m.module.fileIdx == m.config.projectMainIdx template debug*(x: auto) {.deprecated.} =
elif compiles(L.fileIdx): echo x
L.fileIdx == L.config.projectMainIdx
else: template mdbg*: bool {.deprecated.} =
error() when compiles(c.graph):
c.module.fileIdx == c.graph.config.projectMainIdx
elif compiles(c.module):
c.module.fileIdx == c.config.projectMainIdx
elif compiles(c.c.module):
c.c.module.fileIdx == c.c.config.projectMainIdx
elif compiles(m.c.module):
m.c.module.fileIdx == m.c.config.projectMainIdx
elif compiles(cl.c.module):
cl.c.module.fileIdx == cl.c.config.projectMainIdx
elif compiles(p):
when compiles(p.lex):
p.lex.fileIdx == p.lex.config.projectMainIdx
else:
p.module.module.fileIdx == p.config.projectMainIdx
elif compiles(m.module.fileIdx):
m.module.fileIdx == m.config.projectMainIdx
elif compiles(L.fileIdx):
L.fileIdx == L.config.projectMainIdx
else:
error()
# --------------------------- ident tables ---------------------------------- # --------------------------- ident tables ----------------------------------
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef proc idTableGet*(t: TIdTable, key: PIdObj): RootRef

View file

@ -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")

View file

@ -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,

View file

@ -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

View file

@ -387,8 +387,10 @@ 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]
inc(result, 2) # for ``, `` if nfDefaultParam notin param.flags:
inc(result, lsub(g, param))
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!

View file

@ -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

View file

@ -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]

View file

@ -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)

View file

@ -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)
# this is a typedesc variable, leave for evals lhsType = n[1].typ
return else:
if lhsType.base.kind == tyNone:
# this is a typedesc variable, leave for evals
return
if lhsType.base.containsGenericType:
# BUGFIX: don't evaluate this too early: ``T is void``
return
# BUGFIX: don't evaluate this too early: ``T is void`` result = isOpImpl(c, n, flags)
if not n[1].typ.base.containsGenericType: 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)

View file

@ -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)

View file

@ -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])
internalAssert c.config, originalParams[i].kind == nkSym
when true:
let oldParam = originalParams[i].sym
let param = copySym(oldParam)
param.owner = prc
param.typ = result.sons[i]
if oldParam.ast != nil:
param.ast = fitNode(c, param.typ, oldParam.ast, oldParam.ast.info)
# don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])! # take a note of the original type. If't a free type or static parameter
result.n.sons[i] = newSymNode(param) # we'll need to keep it unbound for the `fitNode` operation below...
addDecl(c, param) var typeToFit = result[i]
else:
let param = replaceTypeVarsN(cl, originalParams[i]) let needsStaticSkipping = result[i].kind == tyFromExpr
result.n.sons[i] = param result[i] = replaceTypeVarsT(cl, result[i])
param.sym.owner = prc if needsStaticSkipping:
addDecl(c, result.n.sons[i].sym) result[i] = result[i].skipTypes({tyStatic})
# ...otherwise, we use the instantiated type in `fitNode`
if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and
(typeToFit.kind != tyStatic):
typeToFit = result[i]
internalAssert c.config, originalParams[i].kind == nkSym
let oldParam = originalParams[i].sym
let param = copySym(oldParam)
param.owner = prc
param.typ = result[i]
# The default value is instantiated and fitted against the final
# concrete param type. We avoid calling `replaceTypeVarsN` on the
# call head symbol, because this leads to infinite recursion.
if oldParam.ast != nil:
var def = oldParam.ast.copyTree
if def.kind == nkCall:
for i in 1 ..< def.len:
def[i] = replaceTypeVarsN(cl, def[i])
def = semExprWithType(c, def)
if def.referencesAnotherParam(getCurrOwner(c)):
def.flags.incl nfDefaultRefsParam
var converted = indexTypesMatch(c, typeToFit, def.typ, def)
if converted == nil:
# The default value doesn't match the final instantiated type.
# As an example of this, see:
# https://github.com/nim-lang/Nim/issues/1201
# We are replacing the default value with an error node in case
# the user calls an explicit instantiation of the proc (this is
# the only way the default value might be inserted).
param.ast = errorNode(c, def)
else:
param.ast = fitNodePostMatch(c, typeToFit, converted)
param.typ = result[i]
result.n[i] = newSymNode(param)
propagateToOwner(result, result[i])
addDecl(c, param)
resetIdTable(cl.symMap) resetIdTable(cl.symMap)
resetIdTable(cl.localCache) resetIdTable(cl.localCache)

View file

@ -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:

View file

@ -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:

View file

@ -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:
if result != isNone and f.n != nil: result = typeRel(c, f.base, a)
if not exprStructuralEquivalent(f.n, aOrig.n): if result != isNone and f.n != nil:
result = isNone if not exprStructuralEquivalent(f.n, aOrig.n):
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

View file

@ -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:

View file

@ -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) & ", " &

View file

@ -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

View file

@ -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.}

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -179,10 +179,24 @@ proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
## Cannot be overloaded. ## Cannot be overloaded.
discard discard
proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} = when defined(nimNewTypedesc):
## Builtin 'type' operator for accessing the type of an expression. type
## Cannot be overloaded. `static`* {.magic: "Static".}[T]
discard ## meta type representing all values that can be evaluated at compile-time.
##
## The type coercion ``static(x)`` can be used to force the compile-time
## evaluation of the given expression ``x``.
`type`* {.magic: "Type".}[T]
## meta type representing the type of all type values.
##
## The coercion ``type(x)`` can be used to obtain the type of the given
## expression ``x``.
else:
proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} =
## Builtin 'type' operator for accessing the type of an expression.
## Cannot be overloaded.
discard
proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.} proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.}
## Boolean not; returns true iff ``x == false``. ## Boolean not; returns true iff ``x == false``.

View file

@ -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

View 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

View 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

View file

@ -0,0 +1,7 @@
import libs/[trie_database, trie]
proc takeDb(d: TrieDatabase) = discard
var mdb: MemDB
takeDb(mdb)

View 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)

View file

@ -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] = @[]

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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:

View 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)

View file

@ -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

View 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

View 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

View file

@ -5,7 +5,7 @@ false
false false
true true
true true
no''' yes'''
""" """
proc IsVoid[T](): string = proc IsVoid[T](): string =