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
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
supports so called "for-loop macros". See
the `manual <manual.html#macros-for-loop-macros>`_ for more details.
- the `typedesc` special type has been renamed to just `type`.
- `static` and `type` are now also modifiers similar to `ref` and `ptr`.
They denote the special types `static[T]` and `type[T]`.
- Forcing compile-time evaluation with `static` now supports specifying
the desired target type (as a concrete type or as a type class)
- The `type` operator now supports checking that the supplied expression
matches an expected type constraint.
### Language changes

View File

@@ -293,6 +293,10 @@ const
# the compiler will avoid printing such names
# in user messages.
sfHoisted* = sfForward
# an expression was hoised to an anonymous variable.
# the flag is applied to the var/let symbol
sfNoForward* = sfRegister
# forward declarations are not required (per module)
sfReorder* = sfForward
@@ -454,6 +458,9 @@ type
nfPreventCg # this node should be ignored by the codegen
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
nfFromTemplate # a top-level node returned from a template
nfDefaultParam # an automatically inserter default parameter
nfDefaultRefsParam # a default param value references another parameter
# the flag is applied to proc default values and to calls
TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that)
@@ -571,8 +578,8 @@ type
TMagic* = enum # symbols that require compiler magic:
mNone,
mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
mEcho, mShallowCopy, mSlurp, mStaticExec,
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mType, mTypeOf,
mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
mUnaryLt, mInc, mDec, mOrd,
mNew, mNewFinalize, mNewSeq, mNewSeqOfCap,
@@ -971,7 +978,7 @@ const
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
nfDotSetter, nfDotField,
nfIsRef, nfPreventCg, nfLL,
nfFromTemplate}
nfFromTemplate, nfDefaultRefsParam}
namePos* = 0
patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2

View File

@@ -31,26 +31,39 @@ when declared(echo):
proc debug*(conf: ConfigRef; n: PType) {.deprecated.}
proc debug*(conf: ConfigRef; n: PNode) {.deprecated.}
template mdbg*: bool {.dirty.} =
when compiles(c.module):
c.module.fileIdx == c.config.projectMainIdx
elif compiles(c.c.module):
c.c.module.fileIdx == c.c.config.projectMainIdx
elif compiles(m.c.module):
m.c.module.fileIdx == m.c.config.projectMainIdx
elif compiles(cl.c.module):
cl.c.module.fileIdx == cl.c.config.projectMainIdx
elif compiles(p):
when compiles(p.lex):
p.lex.fileIdx == p.lex.config.projectMainIdx
template debug*(x: PSym|PType|PNode) {.deprecated.} =
when compiles(c.config):
debug(c.config, x)
elif compiles(c.graph.config):
debug(c.graph.config, x)
else:
p.module.module.fileIdx == p.config.projectMainIdx
elif compiles(m.module.fileIdx):
m.module.fileIdx == m.config.projectMainIdx
elif compiles(L.fileIdx):
L.fileIdx == L.config.projectMainIdx
else:
error()
error()
template debug*(x: auto) {.deprecated.} =
echo x
template mdbg*: bool {.deprecated.} =
when compiles(c.graph):
c.module.fileIdx == c.graph.config.projectMainIdx
elif compiles(c.module):
c.module.fileIdx == c.config.projectMainIdx
elif compiles(c.c.module):
c.c.module.fileIdx == c.c.config.projectMainIdx
elif compiles(m.c.module):
m.c.module.fileIdx == m.c.config.projectMainIdx
elif compiles(cl.c.module):
cl.c.module.fileIdx == cl.c.config.projectMainIdx
elif compiles(p):
when compiles(p.lex):
p.lex.fileIdx == p.lex.config.projectMainIdx
else:
p.module.module.fileIdx == p.config.projectMainIdx
elif compiles(m.module.fileIdx):
m.module.fileIdx == m.config.projectMainIdx
elif compiles(L.fileIdx):
L.fileIdx == L.config.projectMainIdx
else:
error()
# --------------------------- ident tables ----------------------------------
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef

View File

@@ -44,6 +44,7 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimcomputedgoto")
defineSymbol("nimunion")
defineSymbol("nimnewshared")
defineSymbol("nimNewTypedesc")
defineSymbol("nimrequiresnimframe")
defineSymbol("nimparsebiggestfloatmagic")
defineSymbol("nimalias")

View File

@@ -336,6 +336,18 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
result = not containsGarbageCollectedRef(t)
proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym =
result = newSym(skLet, name, owner, varSection.info, owner.options)
result.flags.incl sfHoisted
result.typ = expr.typ
var varDef = newNodeI(nkIdentDefs, varSection.info, 3)
varDef.sons[0] = newSymNode(result)
varDef.sons[1] = newNodeI(nkEmpty, varSection.info)
varDef.sons[2] = expr
varSection.add varDef
proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
v: PNode; useShallowCopy=false): PSym =
result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,

View File

@@ -50,6 +50,9 @@ type
SymbolMode = enum
smNormal, smAllowNil, smAfterDot
TPrimaryMode = enum
pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
proc parseAll*(p: var TParser): PNode
proc closeParser*(p: var TParser)
proc parseTopLevelStmt*(p: var TParser): PNode
@@ -81,6 +84,9 @@ proc parsePragma(p: var TParser): PNode
proc postExprBlocks(p: var TParser, x: PNode): PNode
proc parseExprStmt(p: var TParser): PNode
proc parseBlock(p: var TParser): PNode
proc primary(p: var TParser, mode: TPrimaryMode): PNode
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
# implementation
proc getTok(p: var TParser) =
@@ -332,6 +338,8 @@ proc colcom(p: var TParser, n: PNode) =
eat(p, tkColon)
skipComment(p, n)
const tkBuiltInMagics = {tkType, tkStatic, tkAddr}
proc parseSymbol(p: var TParser, mode = smNormal): PNode =
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
#| | IDENT | KEYW
@@ -340,7 +348,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
result = newIdentNodeP(p.tok.ident, p)
getTok(p)
of tokKeywordLow..tokKeywordHigh:
if p.tok.tokType == tkAddr or p.tok.tokType == tkType or mode == smAfterDot:
if p.tok.tokType in tkBuiltInMagics or mode == smAfterDot:
# for backwards compatibility these 2 are always valid:
result = newIdentNodeP(p.tok.ident, p)
getTok(p)
@@ -529,9 +537,6 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode =
else:
result = a
type
TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
proc complexOrSimpleStmt(p: var TParser): PNode
proc simpleExpr(p: var TParser, mode = pmNormal): PNode
@@ -629,7 +634,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
#| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
#| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
case p.tok.tokType
of tkSymbol, tkType, tkAddr:
of tkSymbol, tkBuiltInMagics:
result = newIdentNodeP(p.tok.ident, p)
getTok(p)
result = parseGStrLit(p, result)
@@ -745,7 +750,12 @@ proc commandParam(p: var TParser, isFirstParam: var bool): PNode =
addSon(result, parseExpr(p))
isFirstParam = false
proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
const
tkTypeClasses = {tkRef, tkPtr, tkVar, tkStatic, tkType,
tkEnum, tkTuple, tkObject, tkProc}
proc primarySuffix(p: var TParser, r: PNode,
baseIndent: int, mode: TPrimaryMode): PNode =
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
#| | doBlocks
#| | '.' optInd symbol generalizedLit?
@@ -762,7 +772,14 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
case p.tok.tokType
of tkParLe:
# progress guaranteed
somePar()
if p.tok.strongSpaceA > 0:
# inside type sections, expressions such as `ref (int, bar)`
# are parsed as a nkCommand with a single tuple argument (nkPar)
if mode == pmTypeDef:
result = newNodeP(nkCommand, p)
result.addSon r
result.addSon primary(p, pmNormal)
break
result = namedParams(p, result, nkCall, tkParRi)
if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
result.kind = nkObjConstr
@@ -778,8 +795,13 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
# progress guaranteed
somePar()
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType,
tkOpr, tkDotDot:
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast,
tkOpr, tkDotDot, tkTypeClasses - {tkRef, tkPtr}:
# XXX: In type sections we allow the free application of the
# command syntax, with the exception of expressions such as
# `foo ref` or `foo ptr`. Unfortunately, these two are also
# used as infix operators for the memory regions feature and
# the current parsing rules don't play well here.
if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}):
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
# solution, but pragmas.nim can't handle that
@@ -804,9 +826,6 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
else:
break
proc primary(p: var TParser, mode: TPrimaryMode): PNode
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
proc parseOperators(p: var TParser, headNode: PNode,
limit: int, mode: TPrimaryMode): PNode =
result = headNode
@@ -1127,9 +1146,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
proc isExprStart(p: TParser): bool =
case p.tok.tokType
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
tkProc, tkFunc, tkIterator, tkBind, tkAddr,
tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics,
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
tkTuple, tkObject, tkWhen, tkCase, tkOut:
result = true
else: result = false
@@ -1190,7 +1209,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
#| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
#| primary = typeKeyw typeDescK
#| / prefixOperator* identOrLiteral primarySuffix*
#| / 'static' primary
#| / 'bind' primary
if isOperator(p.tok):
let isSigil = isSigilLike(p.tok)
@@ -1203,7 +1221,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
#XXX prefix operators
let baseInd = p.lex.currLineIndent
addSon(result, primary(p, pmSkipSuffix))
result = primarySuffix(p, result, baseInd)
result = primarySuffix(p, result, baseInd, mode)
else:
addSon(result, primary(p, pmNormal))
return
@@ -1233,14 +1251,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
result = parseTypeClass(p)
else:
parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
of tkStatic:
let info = parLineInfo(p)
getTokNoInd(p)
let next = primary(p, pmNormal)
if next.kind == nkBracket and next.sonsLen == 1:
result = newNode(nkStaticTy, info, @[next.sons[0]])
else:
result = newNode(nkStaticExpr, info, @[next])
of tkBind:
result = newNodeP(nkBind, p)
getTok(p)
@@ -1255,7 +1265,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
let baseInd = p.lex.currLineIndent
result = identOrLiteral(p, mode)
if mode != pmSkipSuffix:
result = primarySuffix(p, result, baseInd)
result = primarySuffix(p, result, baseInd, mode)
proc parseTypeDesc(p: var TParser): PNode =
#| typeDesc = simpleExpr

View File

@@ -387,8 +387,10 @@ proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
assert(theEnd < 0)
result = 0
for i in countup(start, sonsLen(n) + theEnd):
inc(result, lsub(g, n.sons[i]))
inc(result, 2) # for ``, ``
let param = n.sons[i]
if nfDefaultParam notin param.flags:
inc(result, lsub(g, param))
inc(result, 2) # for ``, ``
if result > 0:
dec(result, 2) # last does not get a comma!

View File

@@ -48,7 +48,10 @@ proc semQuoteAst(c: PContext, n: PNode): PNode
proc finishMethod(c: PContext, s: PSym)
proc evalAtCompileTime(c: PContext, n: PNode): PNode
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
proc semStaticExpr(c: PContext, n: PNode): PNode
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType
proc semTypeOf(c: PContext; n: PNode): PNode
proc hasUnresolvedArgs(c: PContext, n: PNode): bool
proc isArrayConstr(n: PNode): bool {.inline.} =
result = n.kind == nkBracket and
n.typ.skipTypes(abstractInst).kind == tyArray
@@ -70,6 +73,16 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
# echo "passing to safeSemExpr: ", renderTree(n)
discard safeSemExpr(c, n)
proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
result = arg
let x = result.skipConv
if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr:
changeType(c, x, formal, check=true)
else:
result = skipHiddenSubConv(result)
#result.typ = takeType(formal, arg.typ)
#echo arg.info, " picked ", result.typ.typeToString
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
if arg.typ.isNil:
localError(c.config, arg.info, "expression has no type: " &
@@ -85,13 +98,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
result = copyTree(arg)
result.typ = formal
else:
let x = result.skipConv
if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr:
changeType(c, x, formal, check=true)
else:
result = skipHiddenSubConv(result)
#result.typ = takeType(formal, arg.typ)
#echo arg.info, " picked ", result.typ.typeToString
result = fitNodePostMatch(c, formal, result)
proc inferWithMetatype(c: PContext, formal: PType,
arg: PNode, coerceDistincts = false): PNode

View File

@@ -391,7 +391,23 @@ proc inferWithMetatype(c: PContext, formal: PType,
result = copyTree(arg)
result.typ = formal
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
proc updateDefaultParams(call: PNode) =
# In generic procs, the default parameter may be unique for each
# instantiation (see tlateboundgenericparams).
# After a call is resolved, we need to re-assign any default value
# that was used during sigmatch. sigmatch is responsible for marking
# the default params with `nfDefaultParam` and `instantiateProcType`
# computes correctly the default values for each instantiation.
let calleeParams = call[0].sym.typ.n
for i in countdown(call.len - 1, 1):
if nfDefaultParam notin call[i].flags:
return
let def = calleeParams[i].sym.ast
if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam
call[i] = def
proc semResolvedCall(c: PContext, x: TCandidate,
n: PNode, flags: TExprFlags): PNode =
assert x.state == csMatch
var finalCallee = x.calleeSym
markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym)
@@ -424,8 +440,9 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
result = x.call
instGenericConvertersSons(c, result, x)
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
result[0] = newSymNode(finalCallee, result[0].info)
result.typ = finalCallee.typ.sons[0]
updateDefaultParams(result)
proc canDeref(n: PNode): bool {.inline.} =
result = n.len >= 2 and (let t = n[1].typ;
@@ -447,7 +464,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
message(c.config, n.info, hintUserRaw,
"Non-matching candidates for " & renderTree(n) & "\n" &
candidates)
result = semResolvedCall(c, n, r)
result = semResolvedCall(c, r, n, flags)
elif implicitDeref in c.features and canDeref(n):
# try to deref the first argument and then try overloading resolution again:
#
@@ -458,7 +475,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
#
n.sons[1] = n.sons[1].tryDeref
var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
if r.state == csMatch: result = semResolvedCall(c, n, r)
if r.state == csMatch: result = semResolvedCall(c, r, n, flags)
else:
# get rid of the deref again for a better error message:
n.sons[1] = n.sons[1].sons[0]

View File

@@ -288,7 +288,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
result.addSonSkipIntLit(typ)
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
let typedesc = makeTypeDesc(c, typ)
let typedesc = newTypeS(tyTypeDesc, c)
typedesc.addSonSkipIntLit(assertNotNil(c.config, typ))
let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
c.config.options).linkTo(typedesc)
return newSymNode(sym, info)

View File

@@ -191,7 +191,25 @@ proc semConv(c: PContext, n: PNode): PNode =
return n
result = newNodeI(nkConv, n.info)
var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
var targetType = semTypeNode(c, n.sons[0], nil)
if targetType.kind == tyTypeDesc:
internalAssert c.config, targetType.len > 0
if targetType.base.kind == tyNone:
return semTypeOf(c, n[1])
else:
targetType = targetType.base
elif targetType.kind == tyStatic:
var evaluated = semStaticExpr(c, n[1])
if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc:
result = n
result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil)
return
elif targetType.base.kind == tyNone:
return evaluated
else:
targetType = targetType.base
maybeLiftType(targetType, c, n[0].info)
if targetType.kind in {tySink, tyLent}:
@@ -298,57 +316,98 @@ proc semSizeof(c: PContext, n: PNode): PNode =
n.typ = getSysType(c.graph, n.info, tyInt)
result = n
proc fixupStaticType(c: PContext, n: PNode) =
# This proc can be applied to evaluated expressions to assign
# them a static type.
#
# XXX: with implicit static, this should not be necessary,
# because the output type of operations such as `semConstExpr`
# should be a static type (as well as the type of any other
# expression that can be implicitly evaluated). For now, we
# apply this measure only in code that is enlightened to work
# with static types.
if n.typ.kind != tyStatic:
n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ])
n.typ.n = n # XXX: cycles like the one here look dangerous.
# Consider using `n.copyTree`
proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
internalAssert c.config, n.sonsLen == 3 and
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
internalAssert c.config,
n.sonsLen == 3 and
n[1].typ != nil and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
let t1 = n[1].typ.skipTypes({tyTypeDesc})
var
res = false
t1 = n[1].typ
t2 = n[2].typ
if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc:
t1 = t1.base
if n[2].kind in {nkStrLit..nkTripleStrLit}:
case n[2].strVal.normalize
of "closure":
let t = skipTypes(t1, abstractRange)
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure and
tfIterator notin t.flags))
res = t.kind == tyProc and
t.callConv == ccClosure and
tfIterator notin t.flags
else:
result = newIntNode(nkIntLit, 0)
res = false
else:
var rhsOrigType = n[2].typ
var t2 = rhsOrigType.skipTypes({tyTypeDesc})
maybeLiftType(t2, c, n.info)
var m: TCandidate
initCandidate(c, m, t2)
if efExplain in flags:
m.diagnostics = @[]
m.diagnosticsEnabled = true
let match = typeRel(m, t2, t1) >= isSubtype # isNone
result = newIntNode(nkIntLit, ord(match))
res = typeRel(m, t2, t1) >= isSubtype # isNone
result = newIntNode(nkIntLit, ord(res))
result.typ = n.typ
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
if sonsLen(n) != 3:
localError(c.config, n.info, "'is' operator takes 2 arguments")
let boolType = getSysType(c.graph, n.info, tyBool)
result = n
n.typ = getSysType(c.graph, n.info, tyBool)
n.typ = boolType
var liftLhs = true
n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
let t2 = semTypeNode(c, n[2], nil)
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
if t2.kind == tyStatic:
let evaluated = tryConstExpr(c, n[1])
if evaluated != nil:
c.fixupStaticType(evaluated)
n[1] = evaluated
else:
result = newIntNode(nkIntLit, 0)
result.typ = boolType
return
elif t2.kind == tyTypeDesc and
(t2.base.kind == tyNone or tfExplicit in t2.flags):
# When the right-hand side is an explicit type, we must
# not allow regular values to be matched against the type:
liftLhs = false
let lhsType = n[1].typ
var lhsType = n[1].typ
if lhsType.kind != tyTypeDesc:
n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
elif lhsType.base.kind == tyNone:
# this is a typedesc variable, leave for evals
return
if liftLhs:
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
lhsType = n[1].typ
else:
if lhsType.base.kind == tyNone:
# this is a typedesc variable, leave for evals
return
if lhsType.base.containsGenericType:
# BUGFIX: don't evaluate this too early: ``T is void``
return
# BUGFIX: don't evaluate this too early: ``T is void``
if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags)
result = isOpImpl(c, n, flags)
proc semOpAux(c: PContext, n: PNode) =
const flags = {efDetermineType}
@@ -483,6 +542,36 @@ proc fixAbstractType(c: PContext, n: PNode) =
proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
proc isUnresolvedSym(s: PSym): bool =
return s.kind == skGenericParam or
tfInferrableStatic in s.typ.flags or
(s.kind == skParam and s.typ.isMetaType) or
(s.kind == skType and
s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
# Checks whether an expression depends on generic parameters that
# don't have bound values yet. E.g. this could happen in situations
# such as:
# type Slot[T] = array[T.size, byte]
# proc foo[T](x: default(T))
#
# Both static parameter and type parameters can be unresolved.
case n.kind
of nkSym:
return isUnresolvedSym(n.sym)
of nkIdent, nkAccQuoted:
let ident = considerQuotedIdent(c, n)
let sym = searchInScopes(c, ident)
if sym != nil:
return isUnresolvedSym(sym)
else:
return false
else:
for i in 0..<n.safeLen:
if hasUnresolvedArgs(c, n.sons[i]): return true
return false
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or
sfCompileToCpp in c.module.flags):
@@ -632,7 +721,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
# echo "SUCCESS evaluated at compile time: ", call.renderTree
proc semStaticExpr(c: PContext, n: PNode): PNode =
let a = semExpr(c, n.sons[0])
let a = semExpr(c, n)
if a.findUnresolvedStatic != nil: return a
result = evalStaticExpr(c.module, c.graph, a, c.p.owner)
if result.isNil:
@@ -768,6 +857,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
else:
result = m.call
instGenericConvertersSons(c, result, m)
elif t != nil and t.kind == tyTypeDesc:
if n.len == 1: return semObjConstr(c, n, flags)
return semConv(c, n)
@@ -1034,7 +1124,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
of skType:
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
if s.typ.kind == tyStatic and s.typ.n != nil:
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
return s.typ.n
result = newSymNode(s, n.info)
result.typ = makeTypeDesc(c, s.typ)
@@ -1261,8 +1351,18 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
# make sure we don't evaluate generic macros/templates
n.sons[0] = semExprWithType(c, n.sons[0],
{efNoEvaluateGeneric})
let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst,
var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst,
tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
if arr.kind == tyStatic:
if arr.base.kind == tyNone:
result = n
result.typ = semStaticType(c, n[1], nil)
return
elif arr.n != nil:
return semSubscript(c, arr.n, flags)
else:
arr = arr.base
case arr.kind
of tyArray, tyOpenArray, tyVarargs, tySequence, tyString,
tyCString:
@@ -2426,8 +2526,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# handling of sym choices is context dependent
# the node is left intact for now
discard
of nkStaticExpr:
result = semStaticExpr(c, n)
of nkStaticExpr: result = semStaticExpr(c, n[0])
of nkAsgn: result = semAsgn(c, n)
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)

View File

@@ -32,9 +32,12 @@ type
cursorInBody: bool # only for nimsuggest
bracketExpr: PNode
type
TSemGenericFlag = enum
withinBind, withinTypeDesc, withinMixin, withinConcept
withinBind,
withinTypeDesc,
withinMixin,
withinConcept
TSemGenericFlags = set[TSemGenericFlag]
proc semGenericStmt(c: PContext, n: PNode,
@@ -53,8 +56,15 @@ template macroToExpand(s): untyped =
template macroToExpandSym(s): untyped =
s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr
template isMixedIn(sym): bool =
let s = sym
s.name.id in ctx.toMixin or (withinConcept in flags and
s.magic == mNone and
s.kind in OverloadableSyms)
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
ctx: var GenericCtx; fromDotExpr=false): PNode =
ctx: var GenericCtx; flags: TSemGenericFlags,
fromDotExpr=false): PNode =
semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
incl(s.flags, sfUsed)
case s.kind
@@ -115,10 +125,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
else:
if withinBind in flags:
result = symChoice(c, n, s, scClosed)
elif s.name.id in ctx.toMixin:
elif s.isMixedIn:
result = symChoice(c, n, s, scForceOpen)
else:
result = semGenericStmtSymbol(c, n, s, ctx)
result = semGenericStmtSymbol(c, n, s, ctx, flags)
# else: leave as nkIdent
proc newDot(n, b: PNode): PNode =
@@ -135,7 +145,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
var s = qualifiedLookUp(c, n, luf)
if s != nil:
result = semGenericStmtSymbol(c, n, s, ctx)
result = semGenericStmtSymbol(c, n, s, ctx, flags)
else:
n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
result = n
@@ -146,10 +156,10 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
isMacro = s.kind in {skTemplate, skMacro}
if withinBind in flags:
result = newDot(result, symChoice(c, n, s, scClosed))
elif s.name.id in ctx.toMixin:
elif s.isMixedIn:
result = newDot(result, symChoice(c, n, s, scForceOpen))
else:
let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true)
let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
if syms.kind == nkSym:
let choice = symChoice(c, n, s, scForceOpen)
choice.kind = nkClosedSymChoice
@@ -215,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode,
if s != nil:
incl(s.flags, sfUsed)
mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
let sc = symChoice(c, fn, s,
if s.name.id in ctx.toMixin: scForceOpen else: scOpen)
let sc = symChoice(c, fn, s, if s.isMixedIn: scForceOpen else: scOpen)
case s.kind
of skMacro:
if macroToExpand(s) and sc.safeLen <= 1:
@@ -472,7 +481,6 @@ proc semGenericStmt(c: PContext, n: PNode,
when defined(nimsuggest):
if withinTypeDesc in flags: dec c.inTypeContext
proc semGenericStmt(c: PContext, n: PNode): PNode =
var ctx: GenericCtx
ctx.toMixin = initIntset()
@@ -484,3 +492,4 @@ proc semConceptBody(c: PContext, n: PNode): PNode =
ctx.toMixin = initIntset()
result = semGenericStmt(c, n, {withinConcept}, ctx)
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)

View File

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

View File

@@ -37,6 +37,12 @@ const
errNoGenericParamsAllowedForX = "no generic parameters allowed for $1"
errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types"
const
mStaticTy = {mStatic}
mTypeTy = {mType, mTypeOf}
# XXX: This should be needed only temporarily until the C
# sources are rebuilt
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
if prev == nil:
result = newTypeS(kind, c)
@@ -238,7 +244,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
for i in 0..1:
if hasGenericArguments(range[i]):
if hasUnresolvedArgs(c, range[i]):
result.n.addSon makeStaticExpr(c, range[i])
result.flags.incl tfUnresolved
else:
@@ -295,7 +301,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
localError(c.config, info, errOrdinalTypeExpected)
result = makeRangeWithStaticExpr(c, e)
if c.inGenericContext > 0: result.flags.incl tfUnresolved
elif e.kind in nkCallKinds and hasGenericArguments(e):
elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e):
if not isOrdinalType(e.typ):
localError(c.config, n[1].info, errOrdinalTypeExpected)
# This is an int returning call, depending on an
@@ -363,6 +369,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
if result != nil:
markUsed(c.config, n.info, result, c.graph.usageSym)
styleCheckUse(n.info, result)
if result.kind == skParam and result.typ.kind == tyTypeDesc:
# This is a typedesc param. is it already bound?
# it's not bound when it's used multiple times in the
@@ -388,8 +395,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
else:
localError(c.config, n.info, errTypeExpected)
return errorSym(c, n)
if result.kind != skType:
if result.kind != skType and result.magic notin (mStaticTy + mTypeTy):
# this implements the wanted ``var v: V, x: V`` feature ...
var ov: TOverloadIter
var amb = initOverloadIter(ov, c, n)
@@ -856,9 +862,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil)
of tyStatic:
# proc(a: expr{string}, b: expr{nkLambda})
# overload on compile time values and AST trees
if paramType.n != nil: return # this is a concrete type
if paramType.base.kind != tyNone and paramType.n != nil:
# this is a concrete static value
return
if tfUnresolved in paramType.flags: return # already lifted
let base = paramType.base.maybeLift
if base.isMetaType and procKind == skMacro:
@@ -879,7 +885,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tyDistinct:
if paramType.sonsLen == 1:
# disable the bindOnce behavior for the type class
result = liftingWalk(paramType.sons[0], true)
result = liftingWalk(paramType.base, true)
of tyAlias:
result = liftingWalk(paramType.base)
of tySequence, tySet, tyArray, tyOpenArray,
tyVar, tyLent, tyPtr, tyRef, tyProc:
@@ -996,13 +1005,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
prev: PType, kind: TSymKind; isType=false): PType =
# for historical reasons (code grows) this is invoked for parameter
# lists too and then 'isType' is false.
var cl: IntSet
checkMinSonsLen(n, 1, c.config)
result = newProcType(c, n.info, prev)
if genericParams != nil and sonsLen(genericParams) == 0:
cl = initIntSet()
var check = initIntSet()
var counter = 0
for i in countup(1, n.len - 1):
var a = n.sons[i]
if a.kind != nkIdentDefs:
@@ -1012,6 +1019,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
# pass over this instantiation:
if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
illFormedAst(a, c.config)
checkMinSonsLen(a, 3, c.config)
var
typ: PType = nil
@@ -1020,26 +1028,52 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
length = sonsLen(a)
hasType = a.sons[length-2].kind != nkEmpty
hasDefault = a.sons[length-1].kind != nkEmpty
if hasType:
typ = semParamType(c, a.sons[length-2], constraint)
if hasDefault:
def = semExprWithType(c, a.sons[length-1])
# check type compatibility between def.typ and typ:
def = a[^1]
block determineType:
if genericParams != nil and genericParams.len > 0:
def = semGenericStmt(c, def)
if hasUnresolvedArgs(c, def):
def.typ = makeTypeFromExpr(c, def.copyTree)
break determineType
def = semExprWithType(c, def, {efDetermineType})
if def.referencesAnotherParam(getCurrOwner(c)):
def.flags.incl nfDefaultRefsParam
if typ == nil:
typ = def.typ
elif def != nil:
# and def.typ != nil and def.typ.kind != tyNone:
if typ.kind == tyTypeDesc:
# consider a proc such as:
# proc takesType(T = int)
# a naive analysis may conclude that the proc type is type[int]
# which will prevent other types from matching - clearly a very
# surprising behavior. We must instead fix the expected type of
# the proc to be the unbound typedesc type:
typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
else:
# if def.typ != nil and def.typ.kind != tyNone:
# example code that triggers it:
# proc sort[T](cmp: proc(a, b: T): int = cmp)
if not containsGenericType(typ):
# check type compatibility between def.typ and typ:
def = fitNode(c, typ, def, def.info)
elif typ.kind == tyStatic:
def = semConstExpr(c, def)
def = fitNode(c, typ, def, def.info)
if not hasType and not hasDefault:
if isType: localError(c.config, a.info, "':' expected")
if kind in {skTemplate, skMacro}:
typ = newTypeS(tyExpr, c)
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
continue
for j in countup(0, length-3):
var arg = newSymG(skParam, a.sons[j], c)
if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
@@ -1055,7 +1089,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
arg.position = counter
arg.constraint = constraint
inc(counter)
if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
if def != nil and def.kind != nkEmpty:
arg.ast = copyTree(def)
if containsOrIncl(check, arg.name.id):
localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'")
addSon(result.n, newSymNode(arg))
@@ -1310,7 +1345,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
incl dummyParam.flags, sfUsed
addDecl(c, dummyParam)
result.n.sons[3] = semConceptBody(c, n[3])
result.n[3] = semConceptBody(c, n[3])
closeScope(c)
proc semProcTypeWithScope(c: PContext, n: PNode,
@@ -1349,6 +1384,12 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
localError(c.config, n.info, errTypeExpected)
result = errorSym(c, n)
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
result = newOrPrevType(tyStatic, prev, c)
var base = semTypeNode(c, childNode, nil).skipTypes({tyTypeDesc, tyAlias})
result.rawAddSon(base)
result.flags.incl tfHasStatic
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = nil
inc c.inTypeContext
@@ -1456,7 +1497,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
of mVarargs: result = semVarargs(c, n, prev)
of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
of mTypeDesc, mTypeTy:
result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
result.flags.incl tfExplicit
of mStaticTy:
result = semStaticType(c, n[1], prev)
of mExpr:
result = semTypeNode(c, n.sons[0], nil)
if result != nil:
@@ -1545,11 +1590,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
of nkVarTy: result = semVarType(c, n, prev)
of nkDistinctTy: result = semDistinct(c, n, prev)
of nkStaticTy:
result = newOrPrevType(tyStatic, prev, c)
var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
result.rawAddSon(base)
result.flags.incl tfHasStatic
of nkStaticTy: result = semStaticType(c, n[0], prev)
of nkIteratorTy:
if n.sonsLen == 0:
result = newTypeS(tyBuiltInTypeClass, c)
@@ -1645,9 +1686,12 @@ proc processMagicType(c: PContext, m: PSym) =
of mStmt:
setMagicType(c.config, m, tyStmt, 0)
if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt
of mTypeDesc:
of mTypeDesc, mType:
setMagicType(c.config, m, tyTypeDesc, 0)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mStatic:
setMagicType(c.config, m, tyStatic, 0)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mVoidType:
setMagicType(c.config, m, tyVoid, 0)
of mArray:

View File

@@ -144,17 +144,6 @@ proc isTypeParam(n: PNode): bool =
(n.sym.kind == skGenericParam or
(n.sym.kind == skType and sfFromGeneric in n.sym.flags))
proc hasGenericArguments*(n: PNode): bool =
if n.kind == nkSym:
return n.sym.kind == skGenericParam or
tfInferrableStatic in n.sym.typ.flags or
(n.sym.kind == skType and
n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
else:
for i in 0..<n.safeLen:
if hasGenericArguments(n.sons[i]): return true
return false
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
# This is needed for tgenericshardcases
# It's possible that a generic param will be used in a proc call to a
@@ -231,6 +220,17 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
# symbol is not our business:
if cl.owner != nil and s.owner != cl.owner:
return s
# XXX: Bound symbols in default parameter expressions may reach here.
# We cannot process them, becase `sym.n` may point to a proc body with
# cyclic references that will lead to an infinite recursion.
# Perhaps we should not use a black-list here, but a whitelist instead
# (e.g. skGenericParam and skType).
# Note: `s.magic` may be `mType` in an example such as:
# proc foo[T](a: T, b = myDefault(type(a)))
if s.kind == skProc or s.magic != mNone:
return s
#result = PSym(idTableGet(cl.symMap, s))
#if result == nil:
result = copySym(s, false)
@@ -278,7 +278,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
# is difficult to handle:
const eqFlags = eqTypeFlags + {tfGcSafe}
var body = t.sons[0]
if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body")
if body.kind != tyGenericBody:
internalError(cl.c.config, cl.info, "no generic body")
var header: PType = t
# search for some instantiation here:
if cl.allowMetaTypes:

View File

@@ -1089,8 +1089,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
else: isNone
of tyAnything:
return if f.kind == tyAnything: isGeneric
else: isNone
if f.kind == tyAnything: return isGeneric
else: return isNone
of tyUserTypeClass, tyUserTypeClassInst:
if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4:
@@ -1666,13 +1666,17 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
let prev = PType(idTableGet(c.bindings, f))
if prev == nil:
if aOrig.kind == tyStatic:
result = typeRel(c, f.lastSon, a)
if result != isNone and f.n != nil:
if not exprStructuralEquivalent(f.n, aOrig.n):
result = isNone
if f.base.kind != tyNone:
result = typeRel(c, f.base, a)
if result != isNone and f.n != nil:
if not exprStructuralEquivalent(f.n, aOrig.n):
result = isNone
else:
result = isGeneric
if result != isNone: put(c, f, aOrig)
elif aOrig.n != nil and aOrig.n.typ != nil:
result = typeRel(c, f.lastSon, aOrig.n.typ)
result = if f.base.kind != tyNone: typeRel(c, f.lastSon, aOrig.n.typ)
else: isGeneric
if result != isNone:
var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])
boundType.n = aOrig.n
@@ -1707,9 +1711,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
# proc foo(T: typedesc, x: T)
# when `f` is an unresolved typedesc, `a` could be any
# type, so we should not perform this check earlier
if a.kind != tyTypeDesc: return isNone
if f.base.kind == tyNone:
if a.kind != tyTypeDesc:
if a.kind == tyGenericParam and tfWildcard in a.flags:
# TODO: prevent `a` from matching as a wildcard again
result = isGeneric
else:
result = isNone
elif f.base.kind == tyNone:
result = isGeneric
else:
result = typeRel(c, f.base, a.base)
@@ -2353,12 +2361,22 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
m.firstMismatch = f
break
else:
# use default value:
if formal.ast.kind == nkEmpty:
# The default param value is set to empty in `instantiateProcType`
# when the type of the default expression doesn't match the type
# of the instantiated proc param:
localError(c.config, m.call.info,
("The default parameter '$1' has incompatible type " &
"with the explicitly requested proc instantiation") %
formal.name.s)
if nfDefaultRefsParam in formal.ast.flags:
m.call.flags.incl nfDefaultRefsParam
var def = copyTree(formal.ast)
if def.kind == nkNilLit:
def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
put(m, formal.typ, def.typ)
def.flags.incl nfDefaultParam
setSon(m.call, formal.position + 1, def)
inc(f)
# forget all inferred types if the overload matching failed

View File

@@ -780,6 +780,43 @@ proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
else:
result = n
proc hoistParamsUsedInDefault(c: PTransf, call, letSection, defExpr: PNode): PNode =
# This takes care of complicated signatures such as:
# proc foo(a: int, b = a)
# proc bar(a: int, b: int, c = a + b)
#
# The recursion may confuse you. It performs two duties:
#
# 1) extracting all referenced params from default expressions
# into a let section preceeding the call
#
# 2) replacing the "references" within the default expression
# with these extracted skLet symbols.
#
# The first duty is carried out directly in the code here, while the second
# duty is activated by returning a non-nil value. The caller is responsible
# for replacing the input to the function with the returned non-nil value.
# (which is the hoisted symbol)
if defExpr.kind == nkSym:
if defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
let paramPos = defExpr.sym.position + 1
if call[paramPos].kind == nkSym and sfHoisted in call[paramPos].sym.flags:
# Already hoisted, we still need to return it in order to replace the
# placeholder expression in the default value.
return call[paramPos]
let hoistedVarSym = hoistExpr(letSection,
call[paramPos],
getIdent(c.graph.cache, genPrefix),
c.transCon.owner).newSymNode
call[paramPos] = hoistedVarSym
return hoistedVarSym
else:
for i in 0..<defExpr.safeLen:
let hoisted = hoistParamsUsedInDefault(c, call, letSection, defExpr[i])
if hoisted != nil: defExpr[i] = hoisted
proc transform(c: PTransf, n: PNode): PTransNode =
when false:
var oldDeferAnchor: PNode
@@ -849,6 +886,15 @@ proc transform(c: PTransf, n: PNode): PTransNode =
of nkBreakStmt: result = transformBreak(c, n)
of nkCallKinds:
result = transformCall(c, n)
var call = result.PNode
if nfDefaultRefsParam in call.flags:
# We've found a default value that references another param.
# See the notes in `hoistParamsUsedInDefault` for more details.
var hoistedParams = newNodeI(nkLetSection, call.info, 0)
for i in 1 ..< call.len:
let hoisted = hoistParamsUsedInDefault(c, call, hoistedParams, call[i])
if hoisted != nil: call[i] = hoisted
result = newTree(nkStmtListExpr, hoistedParams, call).PTransNode
of nkAddr, nkHiddenAddr:
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
of nkDerefExpr, nkHiddenDeref:

View File

@@ -502,7 +502,10 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
#internalAssert t.len == 0
result = "untyped"
of tyFromExpr:
result = renderTree(t.n)
if t.n == nil:
result = "unknown"
else:
result = "type(" & renderTree(t.n) & ")"
of tyArray:
if t.sons[0].kind == tyRange:
result = "array[" & rangeToStr(t.sons[0].n) & ", " &

View File

@@ -303,9 +303,9 @@ symbols in the `system module <system.html>`_.
`#len,seq[T] <system.html#len,seq[T]>`_
* ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>**
`#pairs.i,seq[T] <system.html#pairs.i,seq[T]>`_
* ``template newException[](exceptn: typedesc; message: string): expr`` **=>**
`#newException.t,typedesc,string
<system.html#newException.t,typedesc,string>`_
* ``template newException[](exceptn: type; message: string): expr`` **=>**
`#newException.t,type,string
<system.html#newException.t,type,string>`_
Index (idx) file format

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_.
.. code-block:: nim
template additive(typ: typedesc) =
template additive(typ: type) =
proc `+` *(x, y: typ): typ {.borrow.}
proc `-` *(x, y: typ): typ {.borrow.}
@@ -1740,13 +1740,13 @@ currency. This can be solved with templates_.
proc `+` *(x: typ): typ {.borrow.}
proc `-` *(x: typ): typ {.borrow.}
template multiplicative(typ, base: typedesc) =
template multiplicative(typ, base: type) =
proc `*` *(x: typ, y: base): typ {.borrow.}
proc `*` *(x: base, y: typ): typ {.borrow.}
proc `div` *(x: typ, y: base): typ {.borrow.}
proc `mod` *(x: typ, y: base): typ {.borrow.}
template comparable(typ: typedesc) =
template comparable(typ: type) =
proc `<` * (x, y: typ): bool {.borrow.}
proc `<=` * (x, y: typ): bool {.borrow.}
proc `==` * (x, y: typ): bool {.borrow.}
@@ -2396,7 +2396,7 @@ argument's resolution:
rem unresolvedExpression(undeclaredIdentifier)
``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other
metatypes ``typed`` and ``typedesc`` are not lazy.
metatypes ``typed`` and ``type`` are not lazy.
Varargs matching
@@ -4274,29 +4274,6 @@ therefore very useful for type specialization within generic code:
deletedKeys: seq[bool]
Type operator
-------------
The ``type`` (in many other languages called `typeof`:idx:) operator can
be used to get the type of an expression:
.. code-block:: nim
var x = 0
var y: type(x) # y has type int
If ``type`` is used to determine the result type of a proc/iterator/converter
call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the
interpretation where ``c`` is an iterator is preferred over the
other interpretations:
.. code-block:: nim
import strutils
# strutils contains both a ``split`` proc and iterator, but since an
# an iterator is the preferred interpretation, `y` has the type ``string``:
var y: type("a b c".split)
Type Classes
------------
@@ -4450,10 +4427,10 @@ the presence of callable symbols with specific signatures:
OutputStream = concept var s
s.write(string)
In order to check for symbols accepting ``typedesc`` params, you must prefix
the type with an explicit ``type`` modifier. The named instance of the type,
following the ``concept`` keyword is also considered an explicit ``typedesc``
value that will be matched only as a type.
In order to check for symbols accepting ``type`` params, you must prefix
the type with the explicit ``type`` modifier. The named instance of the
type, following the ``concept`` keyword is also considered to have the
explicit modifier and will be matched only as a type.
.. code-block:: nim
type
@@ -4513,7 +4490,7 @@ The concept types can be parametric just like the regular generic types:
import typetraits
type
AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
AnyMatrix*[R, C: static int; T] = concept m, var mvar, type M
M.ValueType is T
M.Rows == R
M.Cols == C
@@ -4523,7 +4500,7 @@ The concept types can be parametric just like the regular generic types:
type TransposedType = stripGenericParams(M)[C, R, T]
AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T]
AnySquareMatrix*[N: static int, T] = AnyMatrix[N, N, T]
AnyTransform3D* = AnyMatrix[4, 4, float]
@@ -4542,7 +4519,7 @@ The concept types can be parametric just like the regular generic types:
### matrix.nim
type
Matrix*[M, N: static[int]; T] = object
Matrix*[M, N: static int; T] = object
data: array[M*N, T]
proc `[]`*(M: Matrix; m, n: int): M.T =
@@ -4554,7 +4531,7 @@ The concept types can be parametric just like the regular generic types:
# Adapt the Matrix type to the concept's requirements
template Rows*(M: type Matrix): expr = M.M
template Cols*(M: type Matrix): expr = M.N
template ValueType*(M: type Matrix): typedesc = M.T
template ValueType*(M: type Matrix): type = M.T
-------------
### usage.nim
@@ -4582,7 +4559,7 @@ operator and also when types dependent on them are being matched:
.. code-block:: nim
type
MatrixReducer[M, N: static[int]; T] = concept x
MatrixReducer[M, N: static int; T] = concept x
x.reduce(SquareMatrix[N, T]) is array[M, int]
The Nim compiler includes a simple linear equation solver, allowing it to
@@ -4771,12 +4748,12 @@ object inheritance syntax involving the ``of`` keyword:
# the varargs param will here be converted to an array of StringRefValues
# the proc will have only two instantiations for the two character types
proc log(format: static[string], varargs[StringRef])
proc log(format: static string, varargs[StringRef])
# this proc will allow char and wchar values to be mixed in
# the same call at the cost of additional instantiations
# the varargs param will be converted to a tuple
proc log(format: static[string], varargs[distinct StringRef])
proc log(format: static string, varargs[distinct StringRef])
..
@@ -4940,9 +4917,8 @@ templates:
| ``notin`` and ``isnot`` have the obvious meanings.
The "types" of templates can be the symbols ``untyped``,
``typed`` or ``typedesc`` (stands for *type
description*). These are "meta types", they can only be used in certain
contexts. Real types can be used too; this implies that ``typed`` expressions
``typed`` or ``type``. These are "meta types", they can only be used in certain
contexts. Regular types can be used too; this implies that ``typed`` expressions
are expected.
@@ -5109,7 +5085,7 @@ In templates identifiers can be constructed with the backticks notation:
.. code-block:: nim
:test: "nim c $1"
template typedef(name: untyped, typ: typedesc) =
template typedef(name: untyped, typ: type) =
type
`T name`* {.inject.} = typ
`P name`* {.inject.} = ref `T name`
@@ -5171,7 +5147,7 @@ template cannot be accessed in the instantiation context:
.. code-block:: nim
:test: "nim c $1"
template newException*(exceptn: typedesc, message: string): untyped =
template newException*(exceptn: type, message: string): untyped =
var
e: ref exceptn # e is implicitly gensym'ed here
new(e)
@@ -5493,7 +5469,7 @@ As their name suggests, static parameters must be known at compile-time:
.. code-block:: nim
proc precompiledRegex(pattern: static[string]): RegEx =
proc precompiledRegex(pattern: static string): RegEx =
var res {.global.} = re(pattern)
return res
@@ -5513,9 +5489,9 @@ Static params can also appear in the signatures of generic types:
.. code-block:: nim
type
Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T]
Matrix[M,N: static int; T: Number] = array[0..(M*N - 1), T]
# Note how `Number` is just a type constraint here, while
# `static[int]` requires us to supply a compile-time int value
# `static int` requires us to supply a compile-time int value
AffineTransform2D[T] = Matrix[3, 3, T]
AffineTransform3D[T] = Matrix[4, 4, T]
@@ -5523,53 +5499,75 @@ Static params can also appear in the signatures of generic types:
var m1: AffineTransform3D[float] # OK
var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
Please note that ``static T`` is just a syntactic convenience for the
underlying generic type ``static[T]``. The type param can be omitted
to obtain the type class of all values known at compile-time. A more
specific type class can be created by instantiating ``static`` with
another type class.
typedesc
--------
You can force the evaluation of a certain expression at compile-time by
coercing it to a corresponding ``static`` type:
`typedesc` is a special type allowing one to treat types as compile-time values
(i.e. if types are compile-time values and all values have a type, then
typedesc must be their type).
.. code-block:: nim
import math
When used as a regular proc param, typedesc acts as a type class. The proc
will be instantiated for each unique type parameter and one can refer to the
instantiation type using the param name:
echo static(fac(5)), " ", static[bool](16.isPowerOfTwo)
The complier will report any failure to evaluate the expression or a
possible type mismatch error.
type[T]
-------
In many contexts, Nim allows you to treat the names of types as regular
values. These values exists only during the compilation phase, but since
all values must have a type, ``type`` is considered their special type.
``type`` acts like a generic type. For instance, the type of the symbol
``int`` is ``type[int]``. Just like with regular generic types, when the
generic param is ommited, ``type`` denotes the type class of all types.
As a syntactic convenience, you can also use ``type`` as a modifier.
``type int`` is considered the same as ``type[int]``.
Procs featuring ``type`` params are considered implicitly generic.
They will be instantiated for each unique combination of supplied types
and within the body of the proc, the name of each param will refer to
the bound concrete type:
.. code-block:: nim
proc new(T: typedesc): ref T =
proc new(T: type): ref T =
echo "allocating ", T.name
new(result)
var n = Node.new
var tree = new(BinaryTree[int])
When multiple typedesc params are present, they will bind freely to different
types. To force a bind-once behavior
one can use an explicit ``typedesc[T]`` generic param:
When multiple type params are present, they will bind freely to different
types. To force a bind-once behavior one can use an explicit generic param:
.. code-block:: nim
proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U])
proc acceptOnlyTypePairs[T, U](A, B: type[T]; C, D: type[U])
Once bound, typedesc params can appear in the rest of the proc signature:
Once bound, type params can appear in the rest of the proc signature:
.. code-block:: nim
:test: "nim c $1"
template declareVariableWithType(T: typedesc, value: T) =
template declareVariableWithType(T: type, value: T) =
var x: T = value
declareVariableWithType int, 42
Overload resolution can be further influenced by constraining the set of
types that will match the typedesc param:
types that will match the type param:
.. code-block:: nim
:test: "nim c $1"
template maxval(T: typedesc[int]): int = high(int)
template maxval(T: typedesc[float]): float = Inf
template maxval(T: type int): int = high(int)
template maxval(T: type float): float = Inf
var i = int.maxval
var f = float.maxval
@@ -5578,7 +5576,35 @@ types that will match the typedesc param:
The constraint can be a concrete type or a type class.
type operator
-------------
You can obtain the type of a given expression by constructing a ``type``
value from it (in many other languages this is known as the `typeof`:idx:
operator):
.. code-block:: nim
var x = 0
var y: type(x) # y has type int
You may add a constraint to the resulting type to trigger a compile-time error
if the expression doesn't have the expected type:
.. code-block:: nim
var x = 0
var y: type[object](x) # Error: type mismatch: got <int> but expected 'object'
If ``type`` is used to determine the result type of a proc/iterator/converter
call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the
interpretation where ``c`` is an iterator is preferred over the
other interpretations:
.. code-block:: nim
import strutils
# strutils contains both a ``split`` proc and iterator, but since an
# an iterator is the preferred interpretation, `y` has the type ``string``:
var y: type("a b c".split)
Special Operators
@@ -7458,7 +7484,7 @@ Custom pragmas are defined using templates annotated with pragma ``pragma``:
.. code-block:: nim
template dbTable(name: string, table_space: string = "") {.pragma.}
template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
template dbForeignKey(t: typedesc) {.pragma.}
template dbForeignKey(t: type) {.pragma.}
template dbIgnore {.pragma.}

View File

@@ -618,9 +618,9 @@ Turning the ``log`` proc into a template solves this problem:
log("x has the value: " & $x)
The parameters' types can be ordinary types or the meta types ``untyped``,
``typed``, or ``typedesc``.
``typedesc`` stands for *type description*, and ``untyped`` means symbol lookups and
type resolution is not performed before the expression is passed to the template.
``typed``, or ``type``. ``type`` suggests that only a type symbol may be given
as an argument, and ``untyped`` means symbol lookups and type resolution is not
performed before the expression is passed to the template.
If the template has no explicit return type,
``void`` is used for consistency with procs and methods.

View File

@@ -38,7 +38,7 @@
## Note: For inter thread communication use
## a `Channel <channels.html>`_ instead.
import math
import math, typetraits
type
Deque*[T] = object
@@ -160,16 +160,18 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} =
emptyCheck(deq)
result = deq.data[(deq.tail - 1) and deq.mask]
template default[T](t: typedesc[T]): T =
var v: T
v
template destroy(x: untyped) =
when defined(nimNewRuntime) and not supportsCopyMem(type(x)):
`=destroy`(x)
else:
reset(x)
proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
## Remove and returns the first element of the `deq`.
emptyCheck(deq)
dec deq.count
result = deq.data[deq.head]
deq.data[deq.head] = default(type(result))
destroy(deq.data[deq.head])
deq.head = (deq.head + 1) and deq.mask
proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
@@ -178,7 +180,34 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
dec deq.count
deq.tail = (deq.tail - 1) and deq.mask
result = deq.data[deq.tail]
deq.data[deq.tail] = default(type(result))
destroy(deq.data[deq.tail])
proc clear*[T](deq: var Deque[T]) {.inline.} =
## Resets the deque so that it is empty.
for el in mitems(deq): destroy(el)
deq.count = 0
deq.tail = deq.head
proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
## Remove `fromFirst` elements from the front of the deque and
## `fromLast` elements from the back. If the supplied number of
## elements exceeds the total number of elements in the deque,
## the deque will remain empty.
##
## Any user defined destructors
if fromFirst + fromLast > deq.count:
clear(deq)
return
for i in 0 ..< fromFirst:
destroy(deq.data[deq.head])
deq.head = (deq.head + 1) and deq.mask
for i in 0 ..< fromLast:
destroy(deq.data[deq.tail])
deq.tail = (deq.tail - 1) and deq.mask
dec deq.count, fromFirst + fromLast
proc `$`*[T](deq: Deque[T]): string =
## Turn a deque into its string representation.
@@ -215,6 +244,22 @@ when isMainModule:
assert deq.find(6) >= 0
assert deq.find(789) < 0
block:
var d = initDeque[int](1)
d.addLast 7
d.addLast 8
d.addLast 10
d.addFirst 5
d.addFirst 2
d.addFirst 1
d.addLast 20
d.shrink(fromLast = 2)
doAssert($d == "[1, 2, 5, 7, 8]")
d.shrink(2, 1)
doAssert($d == "[5, 7]")
d.shrink(2, 2)
doAssert d.len == 0
for i in -2 .. 10:
if i in deq:
assert deq.contains(i) and deq.find(i) >= 0

View File

@@ -135,6 +135,11 @@ proc write*(s: Stream, x: string) =
else:
if x.len > 0: writeData(s, cstring(x), x.len)
proc write*(s: Stream, args: varargs[string, `$`]) =
## writes one or more strings to the the stream. No length fields or
## terminating zeros are written.
for str in args: write(s, str)
proc writeLine*(s: Stream, args: varargs[string, `$`]) =
## writes one or more strings to the the stream `s` followed
## by a new line. No length field or terminating zero is written.
@@ -266,8 +271,8 @@ proc peekStr*(s: Stream, length: int): TaintedString =
proc readLine*(s: Stream, line: var TaintedString): bool =
## reads a line of text from the stream `s` into `line`. `line` must not be
## ``nil``! May throw an IO exception.
## A line of text may be delimited by ``CR``, ``LF`` or
## ``CRLF``. The newline character(s) are not part of the returned string.
## A line of text may be delimited by ```LF`` or ``CRLF``.
## The newline character(s) are not part of the returned string.
## Returns ``false`` if the end of the file has been reached, ``true``
## otherwise. If ``false`` is returned `line` contains no new data.
line.string.setLen(0)
@@ -317,6 +322,13 @@ proc peekLine*(s: Stream): TaintedString =
defer: setPosition(s, pos)
result = readLine(s)
iterator lines*(s: Stream): TaintedString =
## Iterates over every line in the stream.
## The iteration is based on ``readLine``.
var line: TaintedString
while s.readLine(line):
yield line
when not defined(js):
type

View File

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

View File

@@ -182,7 +182,7 @@ template checkOsError =
if err.len > 0: raise newException(OSError, err)
template log(msg: string, body: untyped) =
if mode == ScriptMode.Verbose or mode == ScriptMode.Whatif:
if mode in {ScriptMode.Verbose, ScriptMode.Whatif}:
echo "[NimScript] ", msg
if mode != ScriptMode.WhatIf:
body

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

View File

@@ -5,15 +5,16 @@ type
x: T
y: U
proc getTypeName(t: typedesc): string = t.name
proc getTypeName1(t: typedesc): string = t.name
proc getTypeName2(t: type): string = t.name
proc foo(T: typedesc[float], a: auto): string =
proc foo(T: type float, a: auto): string =
result = "float " & $(a.len > 5)
proc foo(T: typedesc[TFoo], a: int): string =
result = "TFoo " & $(a)
proc foo(T: typedesc[int or bool]): string =
proc foo(T: type[int or bool]): string =
var a: T
a = 10
result = "int or bool " & ($a)
@@ -23,8 +24,8 @@ template foo(T: typedesc[seq]): string = "seq"
test "types can be used as proc params":
# XXX: `check` needs to know that TFoo[int, float] is a type and
# cannot be assigned for a local variable for later inspection
check ((string.getTypeName == "string"))
check ((getTypeName(int) == "int"))
check ((string.getTypeName1 == "string"))
check ((getTypeName2(int) == "int"))
check ((foo(TFoo[int, float], 1000) == "TFoo 1000"))
@@ -37,6 +38,25 @@ test "types can be used as proc params":
check ((foo(seq[int]) == "seq"))
check ((foo(seq[TFoo[bool, string]]) == "seq"))
when false:
proc foo(T: typedesc[seq], s: T) = nil
template accept(x) =
static: assert(compiles(x))
template reject(x) =
static: assert(not compiles(x))
var
si: seq[int]
ss: seq[string]
proc foo(T: typedesc[seq], s: T) =
discard
accept:
foo seq[int], si
reject:
foo seq[string], si
reject:
foo seq[int], ss

View File

@@ -34,3 +34,17 @@ when true:
type Point[T] = tuple[x, y: T]
proc origin(T: typedesc): Point[T] = discard
discard origin(int)
# https://github.com/nim-lang/Nim/issues/7516
import typetraits
proc hasDefault1(T: type = int): auto = return T.name
doAssert hasDefault1(int) == "int"
doAssert hasDefault1(string) == "string"
doAssert hasDefault1() == "int"
proc hasDefault2(T = string): auto = return T.name
doAssert hasDefault2(int) == "int"
doAssert hasDefault2(string) == "string"
doAssert hasDefault2() == "string"

View File

@@ -4,9 +4,9 @@ type
Base = object of RootObj
Child = object of Base
proc pr(T: typedesc[Base]) = echo "proc " & T.name
method me(T: typedesc[Base]) = echo "method " & T.name
iterator it(T: typedesc[Base]): auto = yield "yield " & T.name
proc pr(T: type[Base]) = echo "proc " & T.name
method me(T: type[Base]) = echo "method " & T.name
iterator it(T: type[Base]): auto = yield "yield " & T.name
Base.pr
Child.pr

View File

@@ -5,16 +5,16 @@ output: "8\n8\n4"
import
macros, typetraits
template selectType(x: int): typeDesc =
template selectType(x: int): type =
when x < 10:
int
else:
string
template simpleTypeTempl: typeDesc =
template simpleTypeTempl: type =
string
macro typeFromMacro: typedesc = string
macro typeFromMacro: type = string
# The tests below check that the result variable of the
# selected type matches the literal types in the code:

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
R = ref
V = var
D = distinct
P = ptr
T = type
S = static
OBJ = object
TPL = tuple
SEQ = seq
var i: int
var x: ref int
var y: distinct int
var z: ptr int
const C = @[1, 2, 3]
static:
assert x is ref
assert y is distinct
assert z is ptr
assert C is static
assert C[1] is static[int]
assert C[0] is static[SomeInteger]
assert C isnot static[string]
assert C is SEQ|OBJ
assert C isnot OBJ|TPL
assert int is int
assert int is T
assert int is SomeInteger
assert seq[int] is type
assert seq[int] is type[seq]
assert seq[int] isnot type[seq[float]]
assert i isnot type[int]
assert type(i) is type[int]
assert x isnot T
assert y isnot S
assert z isnot enum
assert x isnot object
assert y isnot tuple
assert z isnot seq
doAssert x is ref
doAssert y is distinct
doAssert z is ptr

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
true
true
no'''
yes'''
"""
proc IsVoid[T](): string =