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

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