Merge pull request #7681 from nim-lang/typedesc-reforms
Typedesc reforms
This commit is contained in:
commit
7cec5d1cfb
39 changed files with 1702 additions and 270 deletions
|
|
@ -293,6 +293,10 @@ const
|
|||
# the compiler will avoid printing such names
|
||||
# in user messages.
|
||||
|
||||
sfHoisted* = sfForward
|
||||
# an expression was hoised to an anonymous variable.
|
||||
# the flag is applied to the var/let symbol
|
||||
|
||||
sfNoForward* = sfRegister
|
||||
# forward declarations are not required (per module)
|
||||
sfReorder* = sfForward
|
||||
|
|
@ -454,6 +458,9 @@ type
|
|||
nfPreventCg # this node should be ignored by the codegen
|
||||
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
|
||||
nfFromTemplate # a top-level node returned from a template
|
||||
nfDefaultParam # an automatically inserter default parameter
|
||||
nfDefaultRefsParam # a default param value references another parameter
|
||||
# the flag is applied to proc default values and to calls
|
||||
|
||||
TNodeFlags* = set[TNodeFlag]
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that)
|
||||
|
|
@ -571,8 +578,8 @@ type
|
|||
TMagic* = enum # symbols that require compiler magic:
|
||||
mNone,
|
||||
mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
|
||||
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
|
||||
mEcho, mShallowCopy, mSlurp, mStaticExec,
|
||||
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mType, mTypeOf,
|
||||
mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic,
|
||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
|
||||
mUnaryLt, mInc, mDec, mOrd,
|
||||
mNew, mNewFinalize, mNewSeq, mNewSeqOfCap,
|
||||
|
|
@ -971,7 +978,7 @@ const
|
|||
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
||||
nfDotSetter, nfDotField,
|
||||
nfIsRef, nfPreventCg, nfLL,
|
||||
nfFromTemplate}
|
||||
nfFromTemplate, nfDefaultRefsParam}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
|
|
|
|||
|
|
@ -31,26 +31,39 @@ when declared(echo):
|
|||
proc debug*(conf: ConfigRef; n: PType) {.deprecated.}
|
||||
proc debug*(conf: ConfigRef; n: PNode) {.deprecated.}
|
||||
|
||||
template mdbg*: bool {.dirty.} =
|
||||
when compiles(c.module):
|
||||
c.module.fileIdx == c.config.projectMainIdx
|
||||
elif compiles(c.c.module):
|
||||
c.c.module.fileIdx == c.c.config.projectMainIdx
|
||||
elif compiles(m.c.module):
|
||||
m.c.module.fileIdx == m.c.config.projectMainIdx
|
||||
elif compiles(cl.c.module):
|
||||
cl.c.module.fileIdx == cl.c.config.projectMainIdx
|
||||
elif compiles(p):
|
||||
when compiles(p.lex):
|
||||
p.lex.fileIdx == p.lex.config.projectMainIdx
|
||||
template debug*(x: PSym|PType|PNode) {.deprecated.} =
|
||||
when compiles(c.config):
|
||||
debug(c.config, x)
|
||||
elif compiles(c.graph.config):
|
||||
debug(c.graph.config, x)
|
||||
else:
|
||||
p.module.module.fileIdx == p.config.projectMainIdx
|
||||
elif compiles(m.module.fileIdx):
|
||||
m.module.fileIdx == m.config.projectMainIdx
|
||||
elif compiles(L.fileIdx):
|
||||
L.fileIdx == L.config.projectMainIdx
|
||||
else:
|
||||
error()
|
||||
error()
|
||||
|
||||
template debug*(x: auto) {.deprecated.} =
|
||||
echo x
|
||||
|
||||
template mdbg*: bool {.deprecated.} =
|
||||
when compiles(c.graph):
|
||||
c.module.fileIdx == c.graph.config.projectMainIdx
|
||||
elif compiles(c.module):
|
||||
c.module.fileIdx == c.config.projectMainIdx
|
||||
elif compiles(c.c.module):
|
||||
c.c.module.fileIdx == c.c.config.projectMainIdx
|
||||
elif compiles(m.c.module):
|
||||
m.c.module.fileIdx == m.c.config.projectMainIdx
|
||||
elif compiles(cl.c.module):
|
||||
cl.c.module.fileIdx == cl.c.config.projectMainIdx
|
||||
elif compiles(p):
|
||||
when compiles(p.lex):
|
||||
p.lex.fileIdx == p.lex.config.projectMainIdx
|
||||
else:
|
||||
p.module.module.fileIdx == p.config.projectMainIdx
|
||||
elif compiles(m.module.fileIdx):
|
||||
m.module.fileIdx == m.config.projectMainIdx
|
||||
elif compiles(L.fileIdx):
|
||||
L.fileIdx == L.config.projectMainIdx
|
||||
else:
|
||||
error()
|
||||
|
||||
# --------------------------- ident tables ----------------------------------
|
||||
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ proc initDefines*(symbols: StringTableRef) =
|
|||
defineSymbol("nimcomputedgoto")
|
||||
defineSymbol("nimunion")
|
||||
defineSymbol("nimnewshared")
|
||||
defineSymbol("nimNewTypedesc")
|
||||
defineSymbol("nimrequiresnimframe")
|
||||
defineSymbol("nimparsebiggestfloatmagic")
|
||||
defineSymbol("nimalias")
|
||||
|
|
|
|||
|
|
@ -336,6 +336,18 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
|
|||
if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
|
||||
result = not containsGarbageCollectedRef(t)
|
||||
|
||||
proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym =
|
||||
result = newSym(skLet, name, owner, varSection.info, owner.options)
|
||||
result.flags.incl sfHoisted
|
||||
result.typ = expr.typ
|
||||
|
||||
var varDef = newNodeI(nkIdentDefs, varSection.info, 3)
|
||||
varDef.sons[0] = newSymNode(result)
|
||||
varDef.sons[1] = newNodeI(nkEmpty, varSection.info)
|
||||
varDef.sons[2] = expr
|
||||
|
||||
varSection.add varDef
|
||||
|
||||
proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
|
||||
v: PNode; useShallowCopy=false): PSym =
|
||||
result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ type
|
|||
SymbolMode = enum
|
||||
smNormal, smAllowNil, smAfterDot
|
||||
|
||||
TPrimaryMode = enum
|
||||
pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
|
||||
|
||||
proc parseAll*(p: var TParser): PNode
|
||||
proc closeParser*(p: var TParser)
|
||||
proc parseTopLevelStmt*(p: var TParser): PNode
|
||||
|
|
@ -81,6 +84,9 @@ proc parsePragma(p: var TParser): PNode
|
|||
proc postExprBlocks(p: var TParser, x: PNode): PNode
|
||||
proc parseExprStmt(p: var TParser): PNode
|
||||
proc parseBlock(p: var TParser): PNode
|
||||
proc primary(p: var TParser, mode: TPrimaryMode): PNode
|
||||
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
|
||||
|
||||
# implementation
|
||||
|
||||
proc getTok(p: var TParser) =
|
||||
|
|
@ -332,6 +338,8 @@ proc colcom(p: var TParser, n: PNode) =
|
|||
eat(p, tkColon)
|
||||
skipComment(p, n)
|
||||
|
||||
const tkBuiltInMagics = {tkType, tkStatic, tkAddr}
|
||||
|
||||
proc parseSymbol(p: var TParser, mode = smNormal): PNode =
|
||||
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
|
||||
#| | IDENT | KEYW
|
||||
|
|
@ -340,7 +348,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
|
|||
result = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
of tokKeywordLow..tokKeywordHigh:
|
||||
if p.tok.tokType == tkAddr or p.tok.tokType == tkType or mode == smAfterDot:
|
||||
if p.tok.tokType in tkBuiltInMagics or mode == smAfterDot:
|
||||
# for backwards compatibility these 2 are always valid:
|
||||
result = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
|
|
@ -529,9 +537,6 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode =
|
|||
else:
|
||||
result = a
|
||||
|
||||
type
|
||||
TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
|
||||
|
||||
proc complexOrSimpleStmt(p: var TParser): PNode
|
||||
proc simpleExpr(p: var TParser, mode = pmNormal): PNode
|
||||
|
||||
|
|
@ -629,7 +634,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
|
|||
#| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
|
||||
#| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
|
||||
case p.tok.tokType
|
||||
of tkSymbol, tkType, tkAddr:
|
||||
of tkSymbol, tkBuiltInMagics:
|
||||
result = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
result = parseGStrLit(p, result)
|
||||
|
|
@ -745,7 +750,12 @@ proc commandParam(p: var TParser, isFirstParam: var bool): PNode =
|
|||
addSon(result, parseExpr(p))
|
||||
isFirstParam = false
|
||||
|
||||
proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||
const
|
||||
tkTypeClasses = {tkRef, tkPtr, tkVar, tkStatic, tkType,
|
||||
tkEnum, tkTuple, tkObject, tkProc}
|
||||
|
||||
proc primarySuffix(p: var TParser, r: PNode,
|
||||
baseIndent: int, mode: TPrimaryMode): PNode =
|
||||
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
|
||||
#| | doBlocks
|
||||
#| | '.' optInd symbol generalizedLit?
|
||||
|
|
@ -762,7 +772,14 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
|||
case p.tok.tokType
|
||||
of tkParLe:
|
||||
# progress guaranteed
|
||||
somePar()
|
||||
if p.tok.strongSpaceA > 0:
|
||||
# inside type sections, expressions such as `ref (int, bar)`
|
||||
# are parsed as a nkCommand with a single tuple argument (nkPar)
|
||||
if mode == pmTypeDef:
|
||||
result = newNodeP(nkCommand, p)
|
||||
result.addSon r
|
||||
result.addSon primary(p, pmNormal)
|
||||
break
|
||||
result = namedParams(p, result, nkCall, tkParRi)
|
||||
if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
|
||||
result.kind = nkObjConstr
|
||||
|
|
@ -778,8 +795,13 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
|||
# progress guaranteed
|
||||
somePar()
|
||||
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
|
||||
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType,
|
||||
tkOpr, tkDotDot:
|
||||
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast,
|
||||
tkOpr, tkDotDot, tkTypeClasses - {tkRef, tkPtr}:
|
||||
# XXX: In type sections we allow the free application of the
|
||||
# command syntax, with the exception of expressions such as
|
||||
# `foo ref` or `foo ptr`. Unfortunately, these two are also
|
||||
# used as infix operators for the memory regions feature and
|
||||
# the current parsing rules don't play well here.
|
||||
if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}):
|
||||
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
|
||||
# solution, but pragmas.nim can't handle that
|
||||
|
|
@ -804,9 +826,6 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
|||
else:
|
||||
break
|
||||
|
||||
proc primary(p: var TParser, mode: TPrimaryMode): PNode
|
||||
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
|
||||
|
||||
proc parseOperators(p: var TParser, headNode: PNode,
|
||||
limit: int, mode: TPrimaryMode): PNode =
|
||||
result = headNode
|
||||
|
|
@ -1127,9 +1146,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
|
|||
proc isExprStart(p: TParser): bool =
|
||||
case p.tok.tokType
|
||||
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
|
||||
tkProc, tkFunc, tkIterator, tkBind, tkAddr,
|
||||
tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics,
|
||||
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
|
||||
tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
|
||||
tkTuple, tkObject, tkWhen, tkCase, tkOut:
|
||||
result = true
|
||||
else: result = false
|
||||
|
||||
|
|
@ -1190,7 +1209,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
|||
#| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
|
||||
#| primary = typeKeyw typeDescK
|
||||
#| / prefixOperator* identOrLiteral primarySuffix*
|
||||
#| / 'static' primary
|
||||
#| / 'bind' primary
|
||||
if isOperator(p.tok):
|
||||
let isSigil = isSigilLike(p.tok)
|
||||
|
|
@ -1203,7 +1221,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
|||
#XXX prefix operators
|
||||
let baseInd = p.lex.currLineIndent
|
||||
addSon(result, primary(p, pmSkipSuffix))
|
||||
result = primarySuffix(p, result, baseInd)
|
||||
result = primarySuffix(p, result, baseInd, mode)
|
||||
else:
|
||||
addSon(result, primary(p, pmNormal))
|
||||
return
|
||||
|
|
@ -1233,14 +1251,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
|||
result = parseTypeClass(p)
|
||||
else:
|
||||
parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
|
||||
of tkStatic:
|
||||
let info = parLineInfo(p)
|
||||
getTokNoInd(p)
|
||||
let next = primary(p, pmNormal)
|
||||
if next.kind == nkBracket and next.sonsLen == 1:
|
||||
result = newNode(nkStaticTy, info, @[next.sons[0]])
|
||||
else:
|
||||
result = newNode(nkStaticExpr, info, @[next])
|
||||
of tkBind:
|
||||
result = newNodeP(nkBind, p)
|
||||
getTok(p)
|
||||
|
|
@ -1255,7 +1265,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
|||
let baseInd = p.lex.currLineIndent
|
||||
result = identOrLiteral(p, mode)
|
||||
if mode != pmSkipSuffix:
|
||||
result = primarySuffix(p, result, baseInd)
|
||||
result = primarySuffix(p, result, baseInd, mode)
|
||||
|
||||
proc parseTypeDesc(p: var TParser): PNode =
|
||||
#| typeDesc = simpleExpr
|
||||
|
|
|
|||
|
|
@ -387,8 +387,10 @@ proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
|
|||
assert(theEnd < 0)
|
||||
result = 0
|
||||
for i in countup(start, sonsLen(n) + theEnd):
|
||||
inc(result, lsub(g, n.sons[i]))
|
||||
inc(result, 2) # for ``, ``
|
||||
let param = n.sons[i]
|
||||
if nfDefaultParam notin param.flags:
|
||||
inc(result, lsub(g, param))
|
||||
inc(result, 2) # for ``, ``
|
||||
if result > 0:
|
||||
dec(result, 2) # last does not get a comma!
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,10 @@ proc semQuoteAst(c: PContext, n: PNode): PNode
|
|||
proc finishMethod(c: PContext, s: PSym)
|
||||
proc evalAtCompileTime(c: PContext, n: PNode): PNode
|
||||
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
|
||||
|
||||
proc semStaticExpr(c: PContext, n: PNode): PNode
|
||||
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType
|
||||
proc semTypeOf(c: PContext; n: PNode): PNode
|
||||
proc hasUnresolvedArgs(c: PContext, n: PNode): bool
|
||||
proc isArrayConstr(n: PNode): bool {.inline.} =
|
||||
result = n.kind == nkBracket and
|
||||
n.typ.skipTypes(abstractInst).kind == tyArray
|
||||
|
|
@ -70,6 +73,16 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
|
|||
# echo "passing to safeSemExpr: ", renderTree(n)
|
||||
discard safeSemExpr(c, n)
|
||||
|
||||
proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
|
||||
result = arg
|
||||
let x = result.skipConv
|
||||
if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr:
|
||||
changeType(c, x, formal, check=true)
|
||||
else:
|
||||
result = skipHiddenSubConv(result)
|
||||
#result.typ = takeType(formal, arg.typ)
|
||||
#echo arg.info, " picked ", result.typ.typeToString
|
||||
|
||||
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||
if arg.typ.isNil:
|
||||
localError(c.config, arg.info, "expression has no type: " &
|
||||
|
|
@ -85,13 +98,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
|||
result = copyTree(arg)
|
||||
result.typ = formal
|
||||
else:
|
||||
let x = result.skipConv
|
||||
if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr:
|
||||
changeType(c, x, formal, check=true)
|
||||
else:
|
||||
result = skipHiddenSubConv(result)
|
||||
#result.typ = takeType(formal, arg.typ)
|
||||
#echo arg.info, " picked ", result.typ.typeToString
|
||||
result = fitNodePostMatch(c, formal, result)
|
||||
|
||||
proc inferWithMetatype(c: PContext, formal: PType,
|
||||
arg: PNode, coerceDistincts = false): PNode
|
||||
|
|
|
|||
|
|
@ -391,7 +391,23 @@ proc inferWithMetatype(c: PContext, formal: PType,
|
|||
result = copyTree(arg)
|
||||
result.typ = formal
|
||||
|
||||
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
||||
proc updateDefaultParams(call: PNode) =
|
||||
# In generic procs, the default parameter may be unique for each
|
||||
# instantiation (see tlateboundgenericparams).
|
||||
# After a call is resolved, we need to re-assign any default value
|
||||
# that was used during sigmatch. sigmatch is responsible for marking
|
||||
# the default params with `nfDefaultParam` and `instantiateProcType`
|
||||
# computes correctly the default values for each instantiation.
|
||||
let calleeParams = call[0].sym.typ.n
|
||||
for i in countdown(call.len - 1, 1):
|
||||
if nfDefaultParam notin call[i].flags:
|
||||
return
|
||||
let def = calleeParams[i].sym.ast
|
||||
if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam
|
||||
call[i] = def
|
||||
|
||||
proc semResolvedCall(c: PContext, x: TCandidate,
|
||||
n: PNode, flags: TExprFlags): PNode =
|
||||
assert x.state == csMatch
|
||||
var finalCallee = x.calleeSym
|
||||
markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym)
|
||||
|
|
@ -424,8 +440,9 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
|||
|
||||
result = x.call
|
||||
instGenericConvertersSons(c, result, x)
|
||||
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
|
||||
result[0] = newSymNode(finalCallee, result[0].info)
|
||||
result.typ = finalCallee.typ.sons[0]
|
||||
updateDefaultParams(result)
|
||||
|
||||
proc canDeref(n: PNode): bool {.inline.} =
|
||||
result = n.len >= 2 and (let t = n[1].typ;
|
||||
|
|
@ -447,7 +464,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
|||
message(c.config, n.info, hintUserRaw,
|
||||
"Non-matching candidates for " & renderTree(n) & "\n" &
|
||||
candidates)
|
||||
result = semResolvedCall(c, n, r)
|
||||
result = semResolvedCall(c, r, n, flags)
|
||||
elif implicitDeref in c.features and canDeref(n):
|
||||
# try to deref the first argument and then try overloading resolution again:
|
||||
#
|
||||
|
|
@ -458,7 +475,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
|||
#
|
||||
n.sons[1] = n.sons[1].tryDeref
|
||||
var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
|
||||
if r.state == csMatch: result = semResolvedCall(c, n, r)
|
||||
if r.state == csMatch: result = semResolvedCall(c, r, n, flags)
|
||||
else:
|
||||
# get rid of the deref again for a better error message:
|
||||
n.sons[1] = n.sons[1].sons[0]
|
||||
|
|
|
|||
|
|
@ -288,7 +288,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
|||
result.addSonSkipIntLit(typ)
|
||||
|
||||
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
||||
let typedesc = makeTypeDesc(c, typ)
|
||||
let typedesc = newTypeS(tyTypeDesc, c)
|
||||
typedesc.addSonSkipIntLit(assertNotNil(c.config, typ))
|
||||
let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
|
||||
c.config.options).linkTo(typedesc)
|
||||
return newSymNode(sym, info)
|
||||
|
|
|
|||
|
|
@ -191,7 +191,25 @@ proc semConv(c: PContext, n: PNode): PNode =
|
|||
return n
|
||||
|
||||
result = newNodeI(nkConv, n.info)
|
||||
var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
|
||||
|
||||
var targetType = semTypeNode(c, n.sons[0], nil)
|
||||
if targetType.kind == tyTypeDesc:
|
||||
internalAssert c.config, targetType.len > 0
|
||||
if targetType.base.kind == tyNone:
|
||||
return semTypeOf(c, n[1])
|
||||
else:
|
||||
targetType = targetType.base
|
||||
elif targetType.kind == tyStatic:
|
||||
var evaluated = semStaticExpr(c, n[1])
|
||||
if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc:
|
||||
result = n
|
||||
result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil)
|
||||
return
|
||||
elif targetType.base.kind == tyNone:
|
||||
return evaluated
|
||||
else:
|
||||
targetType = targetType.base
|
||||
|
||||
maybeLiftType(targetType, c, n[0].info)
|
||||
|
||||
if targetType.kind in {tySink, tyLent}:
|
||||
|
|
@ -298,57 +316,98 @@ proc semSizeof(c: PContext, n: PNode): PNode =
|
|||
n.typ = getSysType(c.graph, n.info, tyInt)
|
||||
result = n
|
||||
|
||||
proc fixupStaticType(c: PContext, n: PNode) =
|
||||
# This proc can be applied to evaluated expressions to assign
|
||||
# them a static type.
|
||||
#
|
||||
# XXX: with implicit static, this should not be necessary,
|
||||
# because the output type of operations such as `semConstExpr`
|
||||
# should be a static type (as well as the type of any other
|
||||
# expression that can be implicitly evaluated). For now, we
|
||||
# apply this measure only in code that is enlightened to work
|
||||
# with static types.
|
||||
if n.typ.kind != tyStatic:
|
||||
n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ])
|
||||
n.typ.n = n # XXX: cycles like the one here look dangerous.
|
||||
# Consider using `n.copyTree`
|
||||
|
||||
proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
internalAssert c.config, n.sonsLen == 3 and
|
||||
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
|
||||
internalAssert c.config,
|
||||
n.sonsLen == 3 and
|
||||
n[1].typ != nil and
|
||||
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
|
||||
|
||||
let t1 = n[1].typ.skipTypes({tyTypeDesc})
|
||||
var
|
||||
res = false
|
||||
t1 = n[1].typ
|
||||
t2 = n[2].typ
|
||||
|
||||
if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc:
|
||||
t1 = t1.base
|
||||
|
||||
if n[2].kind in {nkStrLit..nkTripleStrLit}:
|
||||
case n[2].strVal.normalize
|
||||
of "closure":
|
||||
let t = skipTypes(t1, abstractRange)
|
||||
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
||||
t.callConv == ccClosure and
|
||||
tfIterator notin t.flags))
|
||||
res = t.kind == tyProc and
|
||||
t.callConv == ccClosure and
|
||||
tfIterator notin t.flags
|
||||
else:
|
||||
result = newIntNode(nkIntLit, 0)
|
||||
res = false
|
||||
else:
|
||||
var rhsOrigType = n[2].typ
|
||||
var t2 = rhsOrigType.skipTypes({tyTypeDesc})
|
||||
maybeLiftType(t2, c, n.info)
|
||||
var m: TCandidate
|
||||
initCandidate(c, m, t2)
|
||||
if efExplain in flags:
|
||||
m.diagnostics = @[]
|
||||
m.diagnosticsEnabled = true
|
||||
let match = typeRel(m, t2, t1) >= isSubtype # isNone
|
||||
result = newIntNode(nkIntLit, ord(match))
|
||||
res = typeRel(m, t2, t1) >= isSubtype # isNone
|
||||
|
||||
result = newIntNode(nkIntLit, ord(res))
|
||||
result.typ = n.typ
|
||||
|
||||
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
if sonsLen(n) != 3:
|
||||
localError(c.config, n.info, "'is' operator takes 2 arguments")
|
||||
|
||||
let boolType = getSysType(c.graph, n.info, tyBool)
|
||||
result = n
|
||||
n.typ = getSysType(c.graph, n.info, tyBool)
|
||||
n.typ = boolType
|
||||
var liftLhs = true
|
||||
|
||||
n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
|
||||
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
|
||||
let t2 = semTypeNode(c, n[2], nil)
|
||||
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
|
||||
if t2.kind == tyStatic:
|
||||
let evaluated = tryConstExpr(c, n[1])
|
||||
if evaluated != nil:
|
||||
c.fixupStaticType(evaluated)
|
||||
n[1] = evaluated
|
||||
else:
|
||||
result = newIntNode(nkIntLit, 0)
|
||||
result.typ = boolType
|
||||
return
|
||||
elif t2.kind == tyTypeDesc and
|
||||
(t2.base.kind == tyNone or tfExplicit in t2.flags):
|
||||
# When the right-hand side is an explicit type, we must
|
||||
# not allow regular values to be matched against the type:
|
||||
liftLhs = false
|
||||
|
||||
let lhsType = n[1].typ
|
||||
var lhsType = n[1].typ
|
||||
if lhsType.kind != tyTypeDesc:
|
||||
n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
||||
elif lhsType.base.kind == tyNone:
|
||||
# this is a typedesc variable, leave for evals
|
||||
return
|
||||
if liftLhs:
|
||||
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
||||
lhsType = n[1].typ
|
||||
else:
|
||||
if lhsType.base.kind == tyNone:
|
||||
# this is a typedesc variable, leave for evals
|
||||
return
|
||||
if lhsType.base.containsGenericType:
|
||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||
return
|
||||
|
||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||
if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags)
|
||||
result = isOpImpl(c, n, flags)
|
||||
|
||||
proc semOpAux(c: PContext, n: PNode) =
|
||||
const flags = {efDetermineType}
|
||||
|
|
@ -483,6 +542,36 @@ proc fixAbstractType(c: PContext, n: PNode) =
|
|||
proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
|
||||
result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
|
||||
|
||||
proc isUnresolvedSym(s: PSym): bool =
|
||||
return s.kind == skGenericParam or
|
||||
tfInferrableStatic in s.typ.flags or
|
||||
(s.kind == skParam and s.typ.isMetaType) or
|
||||
(s.kind == skType and
|
||||
s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
|
||||
|
||||
proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
|
||||
# Checks whether an expression depends on generic parameters that
|
||||
# don't have bound values yet. E.g. this could happen in situations
|
||||
# such as:
|
||||
# type Slot[T] = array[T.size, byte]
|
||||
# proc foo[T](x: default(T))
|
||||
#
|
||||
# Both static parameter and type parameters can be unresolved.
|
||||
case n.kind
|
||||
of nkSym:
|
||||
return isUnresolvedSym(n.sym)
|
||||
of nkIdent, nkAccQuoted:
|
||||
let ident = considerQuotedIdent(c, n)
|
||||
let sym = searchInScopes(c, ident)
|
||||
if sym != nil:
|
||||
return isUnresolvedSym(sym)
|
||||
else:
|
||||
return false
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
if hasUnresolvedArgs(c, n.sons[i]): return true
|
||||
return false
|
||||
|
||||
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
||||
if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or
|
||||
sfCompileToCpp in c.module.flags):
|
||||
|
|
@ -632,7 +721,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
|||
# echo "SUCCESS evaluated at compile time: ", call.renderTree
|
||||
|
||||
proc semStaticExpr(c: PContext, n: PNode): PNode =
|
||||
let a = semExpr(c, n.sons[0])
|
||||
let a = semExpr(c, n)
|
||||
if a.findUnresolvedStatic != nil: return a
|
||||
result = evalStaticExpr(c.module, c.graph, a, c.p.owner)
|
||||
if result.isNil:
|
||||
|
|
@ -768,6 +857,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
else:
|
||||
result = m.call
|
||||
instGenericConvertersSons(c, result, m)
|
||||
|
||||
elif t != nil and t.kind == tyTypeDesc:
|
||||
if n.len == 1: return semObjConstr(c, n, flags)
|
||||
return semConv(c, n)
|
||||
|
|
@ -1034,7 +1124,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
|||
of skType:
|
||||
markUsed(c.config, n.info, s, c.graph.usageSym)
|
||||
styleCheckUse(n.info, s)
|
||||
if s.typ.kind == tyStatic and s.typ.n != nil:
|
||||
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
|
||||
return s.typ.n
|
||||
result = newSymNode(s, n.info)
|
||||
result.typ = makeTypeDesc(c, s.typ)
|
||||
|
|
@ -1261,8 +1351,18 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
# make sure we don't evaluate generic macros/templates
|
||||
n.sons[0] = semExprWithType(c, n.sons[0],
|
||||
{efNoEvaluateGeneric})
|
||||
let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst,
|
||||
var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst,
|
||||
tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
|
||||
if arr.kind == tyStatic:
|
||||
if arr.base.kind == tyNone:
|
||||
result = n
|
||||
result.typ = semStaticType(c, n[1], nil)
|
||||
return
|
||||
elif arr.n != nil:
|
||||
return semSubscript(c, arr.n, flags)
|
||||
else:
|
||||
arr = arr.base
|
||||
|
||||
case arr.kind
|
||||
of tyArray, tyOpenArray, tyVarargs, tySequence, tyString,
|
||||
tyCString:
|
||||
|
|
@ -2426,8 +2526,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
|||
# handling of sym choices is context dependent
|
||||
# the node is left intact for now
|
||||
discard
|
||||
of nkStaticExpr:
|
||||
result = semStaticExpr(c, n)
|
||||
of nkStaticExpr: result = semStaticExpr(c, n[0])
|
||||
of nkAsgn: result = semAsgn(c, n)
|
||||
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
|
||||
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
|
||||
|
|
|
|||
|
|
@ -32,9 +32,12 @@ type
|
|||
cursorInBody: bool # only for nimsuggest
|
||||
bracketExpr: PNode
|
||||
|
||||
type
|
||||
TSemGenericFlag = enum
|
||||
withinBind, withinTypeDesc, withinMixin, withinConcept
|
||||
withinBind,
|
||||
withinTypeDesc,
|
||||
withinMixin,
|
||||
withinConcept
|
||||
|
||||
TSemGenericFlags = set[TSemGenericFlag]
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode,
|
||||
|
|
@ -53,8 +56,15 @@ template macroToExpand(s): untyped =
|
|||
template macroToExpandSym(s): untyped =
|
||||
s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr
|
||||
|
||||
template isMixedIn(sym): bool =
|
||||
let s = sym
|
||||
s.name.id in ctx.toMixin or (withinConcept in flags and
|
||||
s.magic == mNone and
|
||||
s.kind in OverloadableSyms)
|
||||
|
||||
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
ctx: var GenericCtx; fromDotExpr=false): PNode =
|
||||
ctx: var GenericCtx; flags: TSemGenericFlags,
|
||||
fromDotExpr=false): PNode =
|
||||
semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
|
||||
incl(s.flags, sfUsed)
|
||||
case s.kind
|
||||
|
|
@ -115,10 +125,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
|||
else:
|
||||
if withinBind in flags:
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
elif s.name.id in ctx.toMixin:
|
||||
elif s.isMixedIn:
|
||||
result = symChoice(c, n, s, scForceOpen)
|
||||
else:
|
||||
result = semGenericStmtSymbol(c, n, s, ctx)
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||
# else: leave as nkIdent
|
||||
|
||||
proc newDot(n, b: PNode): PNode =
|
||||
|
|
@ -135,7 +145,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
|||
|
||||
var s = qualifiedLookUp(c, n, luf)
|
||||
if s != nil:
|
||||
result = semGenericStmtSymbol(c, n, s, ctx)
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||
else:
|
||||
n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
|
||||
result = n
|
||||
|
|
@ -146,10 +156,10 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
|||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
if withinBind in flags:
|
||||
result = newDot(result, symChoice(c, n, s, scClosed))
|
||||
elif s.name.id in ctx.toMixin:
|
||||
elif s.isMixedIn:
|
||||
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||
else:
|
||||
let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true)
|
||||
let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
|
||||
if syms.kind == nkSym:
|
||||
let choice = symChoice(c, n, s, scForceOpen)
|
||||
choice.kind = nkClosedSymChoice
|
||||
|
|
@ -215,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
|||
if s != nil:
|
||||
incl(s.flags, sfUsed)
|
||||
mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
|
||||
let sc = symChoice(c, fn, s,
|
||||
if s.name.id in ctx.toMixin: scForceOpen else: scOpen)
|
||||
let sc = symChoice(c, fn, s, if s.isMixedIn: scForceOpen else: scOpen)
|
||||
case s.kind
|
||||
of skMacro:
|
||||
if macroToExpand(s) and sc.safeLen <= 1:
|
||||
|
|
@ -472,7 +481,6 @@ proc semGenericStmt(c: PContext, n: PNode,
|
|||
when defined(nimsuggest):
|
||||
if withinTypeDesc in flags: dec c.inTypeContext
|
||||
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode): PNode =
|
||||
var ctx: GenericCtx
|
||||
ctx.toMixin = initIntset()
|
||||
|
|
@ -484,3 +492,4 @@ proc semConceptBody(c: PContext, n: PNode): PNode =
|
|||
ctx.toMixin = initIntset()
|
||||
result = semGenericStmt(c, n, {withinConcept}, ctx)
|
||||
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
|
||||
|
||||
|
|
|
|||
|
|
@ -220,6 +220,14 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
|
|||
result = replaceTypeVarsT(cl, header)
|
||||
closeScope(c)
|
||||
|
||||
proc referencesAnotherParam(n: PNode, p: PSym): bool =
|
||||
if n.kind == nkSym:
|
||||
return n.sym.kind == skParam and n.sym.owner == p
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
if referencesAnotherParam(n[i], p): return true
|
||||
return false
|
||||
|
||||
proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
prc: PSym, info: TLineInfo) =
|
||||
# XXX: Instantiates a generic proc signature, while at the same
|
||||
|
|
@ -247,25 +255,56 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
|||
if i > 1:
|
||||
resetIdTable(cl.symMap)
|
||||
resetIdTable(cl.localCache)
|
||||
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
|
||||
propagateToOwner(result, result.sons[i])
|
||||
internalAssert c.config, originalParams[i].kind == nkSym
|
||||
when true:
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam)
|
||||
param.owner = prc
|
||||
param.typ = result.sons[i]
|
||||
if oldParam.ast != nil:
|
||||
param.ast = fitNode(c, param.typ, oldParam.ast, oldParam.ast.info)
|
||||
|
||||
# don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
|
||||
result.n.sons[i] = newSymNode(param)
|
||||
addDecl(c, param)
|
||||
else:
|
||||
let param = replaceTypeVarsN(cl, originalParams[i])
|
||||
result.n.sons[i] = param
|
||||
param.sym.owner = prc
|
||||
addDecl(c, result.n.sons[i].sym)
|
||||
# take a note of the original type. If't a free type or static parameter
|
||||
# we'll need to keep it unbound for the `fitNode` operation below...
|
||||
var typeToFit = result[i]
|
||||
|
||||
let needsStaticSkipping = result[i].kind == tyFromExpr
|
||||
result[i] = replaceTypeVarsT(cl, result[i])
|
||||
if needsStaticSkipping:
|
||||
result[i] = result[i].skipTypes({tyStatic})
|
||||
|
||||
# ...otherwise, we use the instantiated type in `fitNode`
|
||||
if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and
|
||||
(typeToFit.kind != tyStatic):
|
||||
typeToFit = result[i]
|
||||
|
||||
internalAssert c.config, originalParams[i].kind == nkSym
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam)
|
||||
param.owner = prc
|
||||
param.typ = result[i]
|
||||
|
||||
# The default value is instantiated and fitted against the final
|
||||
# concrete param type. We avoid calling `replaceTypeVarsN` on the
|
||||
# call head symbol, because this leads to infinite recursion.
|
||||
if oldParam.ast != nil:
|
||||
var def = oldParam.ast.copyTree
|
||||
if def.kind == nkCall:
|
||||
for i in 1 ..< def.len:
|
||||
def[i] = replaceTypeVarsN(cl, def[i])
|
||||
|
||||
def = semExprWithType(c, def)
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
def.flags.incl nfDefaultRefsParam
|
||||
|
||||
var converted = indexTypesMatch(c, typeToFit, def.typ, def)
|
||||
if converted == nil:
|
||||
# The default value doesn't match the final instantiated type.
|
||||
# As an example of this, see:
|
||||
# https://github.com/nim-lang/Nim/issues/1201
|
||||
# We are replacing the default value with an error node in case
|
||||
# the user calls an explicit instantiation of the proc (this is
|
||||
# the only way the default value might be inserted).
|
||||
param.ast = errorNode(c, def)
|
||||
else:
|
||||
param.ast = fitNodePostMatch(c, typeToFit, converted)
|
||||
param.typ = result[i]
|
||||
|
||||
result.n[i] = newSymNode(param)
|
||||
propagateToOwner(result, result[i])
|
||||
addDecl(c, param)
|
||||
|
||||
resetIdTable(cl.symMap)
|
||||
resetIdTable(cl.localCache)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,12 @@ const
|
|||
errNoGenericParamsAllowedForX = "no generic parameters allowed for $1"
|
||||
errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types"
|
||||
|
||||
const
|
||||
mStaticTy = {mStatic}
|
||||
mTypeTy = {mType, mTypeOf}
|
||||
# XXX: This should be needed only temporarily until the C
|
||||
# sources are rebuilt
|
||||
|
||||
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
|
||||
if prev == nil:
|
||||
result = newTypeS(kind, c)
|
||||
|
|
@ -238,7 +244,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
|||
localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
|
||||
|
||||
for i in 0..1:
|
||||
if hasGenericArguments(range[i]):
|
||||
if hasUnresolvedArgs(c, range[i]):
|
||||
result.n.addSon makeStaticExpr(c, range[i])
|
||||
result.flags.incl tfUnresolved
|
||||
else:
|
||||
|
|
@ -295,7 +301,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
|||
localError(c.config, info, errOrdinalTypeExpected)
|
||||
result = makeRangeWithStaticExpr(c, e)
|
||||
if c.inGenericContext > 0: result.flags.incl tfUnresolved
|
||||
elif e.kind in nkCallKinds and hasGenericArguments(e):
|
||||
elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e):
|
||||
if not isOrdinalType(e.typ):
|
||||
localError(c.config, n[1].info, errOrdinalTypeExpected)
|
||||
# This is an int returning call, depending on an
|
||||
|
|
@ -363,6 +369,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
|||
if result != nil:
|
||||
markUsed(c.config, n.info, result, c.graph.usageSym)
|
||||
styleCheckUse(n.info, result)
|
||||
|
||||
if result.kind == skParam and result.typ.kind == tyTypeDesc:
|
||||
# This is a typedesc param. is it already bound?
|
||||
# it's not bound when it's used multiple times in the
|
||||
|
|
@ -388,8 +395,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
|||
else:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
return errorSym(c, n)
|
||||
|
||||
if result.kind != skType:
|
||||
if result.kind != skType and result.magic notin (mStaticTy + mTypeTy):
|
||||
# this implements the wanted ``var v: V, x: V`` feature ...
|
||||
var ov: TOverloadIter
|
||||
var amb = initOverloadIter(ov, c, n)
|
||||
|
|
@ -856,9 +862,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
|||
result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil)
|
||||
|
||||
of tyStatic:
|
||||
# proc(a: expr{string}, b: expr{nkLambda})
|
||||
# overload on compile time values and AST trees
|
||||
if paramType.n != nil: return # this is a concrete type
|
||||
if paramType.base.kind != tyNone and paramType.n != nil:
|
||||
# this is a concrete static value
|
||||
return
|
||||
if tfUnresolved in paramType.flags: return # already lifted
|
||||
let base = paramType.base.maybeLift
|
||||
if base.isMetaType and procKind == skMacro:
|
||||
|
|
@ -879,7 +885,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
|||
of tyDistinct:
|
||||
if paramType.sonsLen == 1:
|
||||
# disable the bindOnce behavior for the type class
|
||||
result = liftingWalk(paramType.sons[0], true)
|
||||
result = liftingWalk(paramType.base, true)
|
||||
|
||||
of tyAlias:
|
||||
result = liftingWalk(paramType.base)
|
||||
|
||||
of tySequence, tySet, tyArray, tyOpenArray,
|
||||
tyVar, tyLent, tyPtr, tyRef, tyProc:
|
||||
|
|
@ -996,13 +1005,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
|||
prev: PType, kind: TSymKind; isType=false): PType =
|
||||
# for historical reasons (code grows) this is invoked for parameter
|
||||
# lists too and then 'isType' is false.
|
||||
var cl: IntSet
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
result = newProcType(c, n.info, prev)
|
||||
if genericParams != nil and sonsLen(genericParams) == 0:
|
||||
cl = initIntSet()
|
||||
var check = initIntSet()
|
||||
var counter = 0
|
||||
|
||||
for i in countup(1, n.len - 1):
|
||||
var a = n.sons[i]
|
||||
if a.kind != nkIdentDefs:
|
||||
|
|
@ -1012,6 +1019,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
|||
# pass over this instantiation:
|
||||
if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
|
||||
illFormedAst(a, c.config)
|
||||
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
var
|
||||
typ: PType = nil
|
||||
|
|
@ -1020,26 +1028,52 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
|||
length = sonsLen(a)
|
||||
hasType = a.sons[length-2].kind != nkEmpty
|
||||
hasDefault = a.sons[length-1].kind != nkEmpty
|
||||
|
||||
if hasType:
|
||||
typ = semParamType(c, a.sons[length-2], constraint)
|
||||
|
||||
if hasDefault:
|
||||
def = semExprWithType(c, a.sons[length-1])
|
||||
# check type compatibility between def.typ and typ:
|
||||
def = a[^1]
|
||||
block determineType:
|
||||
if genericParams != nil and genericParams.len > 0:
|
||||
def = semGenericStmt(c, def)
|
||||
if hasUnresolvedArgs(c, def):
|
||||
def.typ = makeTypeFromExpr(c, def.copyTree)
|
||||
break determineType
|
||||
|
||||
def = semExprWithType(c, def, {efDetermineType})
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
def.flags.incl nfDefaultRefsParam
|
||||
|
||||
if typ == nil:
|
||||
typ = def.typ
|
||||
elif def != nil:
|
||||
# and def.typ != nil and def.typ.kind != tyNone:
|
||||
if typ.kind == tyTypeDesc:
|
||||
# consider a proc such as:
|
||||
# proc takesType(T = int)
|
||||
# a naive analysis may conclude that the proc type is type[int]
|
||||
# which will prevent other types from matching - clearly a very
|
||||
# surprising behavior. We must instead fix the expected type of
|
||||
# the proc to be the unbound typedesc type:
|
||||
typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
|
||||
|
||||
else:
|
||||
# if def.typ != nil and def.typ.kind != tyNone:
|
||||
# example code that triggers it:
|
||||
# proc sort[T](cmp: proc(a, b: T): int = cmp)
|
||||
if not containsGenericType(typ):
|
||||
# check type compatibility between def.typ and typ:
|
||||
def = fitNode(c, typ, def, def.info)
|
||||
elif typ.kind == tyStatic:
|
||||
def = semConstExpr(c, def)
|
||||
def = fitNode(c, typ, def, def.info)
|
||||
|
||||
if not hasType and not hasDefault:
|
||||
if isType: localError(c.config, a.info, "':' expected")
|
||||
if kind in {skTemplate, skMacro}:
|
||||
typ = newTypeS(tyExpr, c)
|
||||
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
|
||||
continue
|
||||
|
||||
for j in countup(0, length-3):
|
||||
var arg = newSymG(skParam, a.sons[j], c)
|
||||
if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
|
||||
|
|
@ -1055,7 +1089,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
|||
arg.position = counter
|
||||
arg.constraint = constraint
|
||||
inc(counter)
|
||||
if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
|
||||
if def != nil and def.kind != nkEmpty:
|
||||
arg.ast = copyTree(def)
|
||||
if containsOrIncl(check, arg.name.id):
|
||||
localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'")
|
||||
addSon(result.n, newSymNode(arg))
|
||||
|
|
@ -1310,7 +1345,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
|||
incl dummyParam.flags, sfUsed
|
||||
addDecl(c, dummyParam)
|
||||
|
||||
result.n.sons[3] = semConceptBody(c, n[3])
|
||||
result.n[3] = semConceptBody(c, n[3])
|
||||
closeScope(c)
|
||||
|
||||
proc semProcTypeWithScope(c: PContext, n: PNode,
|
||||
|
|
@ -1349,6 +1384,12 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
|
|||
localError(c.config, n.info, errTypeExpected)
|
||||
result = errorSym(c, n)
|
||||
|
||||
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
|
||||
result = newOrPrevType(tyStatic, prev, c)
|
||||
var base = semTypeNode(c, childNode, nil).skipTypes({tyTypeDesc, tyAlias})
|
||||
result.rawAddSon(base)
|
||||
result.flags.incl tfHasStatic
|
||||
|
||||
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = nil
|
||||
inc c.inTypeContext
|
||||
|
|
@ -1456,7 +1497,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
|||
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
||||
of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
|
||||
of mVarargs: result = semVarargs(c, n, prev)
|
||||
of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
||||
of mTypeDesc, mTypeTy:
|
||||
result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
||||
result.flags.incl tfExplicit
|
||||
of mStaticTy:
|
||||
result = semStaticType(c, n[1], prev)
|
||||
of mExpr:
|
||||
result = semTypeNode(c, n.sons[0], nil)
|
||||
if result != nil:
|
||||
|
|
@ -1545,11 +1590,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
|||
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
|
||||
of nkVarTy: result = semVarType(c, n, prev)
|
||||
of nkDistinctTy: result = semDistinct(c, n, prev)
|
||||
of nkStaticTy:
|
||||
result = newOrPrevType(tyStatic, prev, c)
|
||||
var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
|
||||
result.rawAddSon(base)
|
||||
result.flags.incl tfHasStatic
|
||||
of nkStaticTy: result = semStaticType(c, n[0], prev)
|
||||
of nkIteratorTy:
|
||||
if n.sonsLen == 0:
|
||||
result = newTypeS(tyBuiltInTypeClass, c)
|
||||
|
|
@ -1645,9 +1686,12 @@ proc processMagicType(c: PContext, m: PSym) =
|
|||
of mStmt:
|
||||
setMagicType(c.config, m, tyStmt, 0)
|
||||
if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt
|
||||
of mTypeDesc:
|
||||
of mTypeDesc, mType:
|
||||
setMagicType(c.config, m, tyTypeDesc, 0)
|
||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||
of mStatic:
|
||||
setMagicType(c.config, m, tyStatic, 0)
|
||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||
of mVoidType:
|
||||
setMagicType(c.config, m, tyVoid, 0)
|
||||
of mArray:
|
||||
|
|
|
|||
|
|
@ -144,17 +144,6 @@ proc isTypeParam(n: PNode): bool =
|
|||
(n.sym.kind == skGenericParam or
|
||||
(n.sym.kind == skType and sfFromGeneric in n.sym.flags))
|
||||
|
||||
proc hasGenericArguments*(n: PNode): bool =
|
||||
if n.kind == nkSym:
|
||||
return n.sym.kind == skGenericParam or
|
||||
tfInferrableStatic in n.sym.typ.flags or
|
||||
(n.sym.kind == skType and
|
||||
n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
if hasGenericArguments(n.sons[i]): return true
|
||||
return false
|
||||
|
||||
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
# This is needed for tgenericshardcases
|
||||
# It's possible that a generic param will be used in a proc call to a
|
||||
|
|
@ -231,6 +220,17 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
|
|||
# symbol is not our business:
|
||||
if cl.owner != nil and s.owner != cl.owner:
|
||||
return s
|
||||
|
||||
# XXX: Bound symbols in default parameter expressions may reach here.
|
||||
# We cannot process them, becase `sym.n` may point to a proc body with
|
||||
# cyclic references that will lead to an infinite recursion.
|
||||
# Perhaps we should not use a black-list here, but a whitelist instead
|
||||
# (e.g. skGenericParam and skType).
|
||||
# Note: `s.magic` may be `mType` in an example such as:
|
||||
# proc foo[T](a: T, b = myDefault(type(a)))
|
||||
if s.kind == skProc or s.magic != mNone:
|
||||
return s
|
||||
|
||||
#result = PSym(idTableGet(cl.symMap, s))
|
||||
#if result == nil:
|
||||
result = copySym(s, false)
|
||||
|
|
@ -278,7 +278,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
|||
# is difficult to handle:
|
||||
const eqFlags = eqTypeFlags + {tfGcSafe}
|
||||
var body = t.sons[0]
|
||||
if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body")
|
||||
if body.kind != tyGenericBody:
|
||||
internalError(cl.c.config, cl.info, "no generic body")
|
||||
var header: PType = t
|
||||
# search for some instantiation here:
|
||||
if cl.allowMetaTypes:
|
||||
|
|
|
|||
|
|
@ -1089,8 +1089,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
|||
else: isNone
|
||||
|
||||
of tyAnything:
|
||||
return if f.kind == tyAnything: isGeneric
|
||||
else: isNone
|
||||
if f.kind == tyAnything: return isGeneric
|
||||
else: return isNone
|
||||
|
||||
of tyUserTypeClass, tyUserTypeClassInst:
|
||||
if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4:
|
||||
|
|
@ -1666,13 +1666,17 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
|||
let prev = PType(idTableGet(c.bindings, f))
|
||||
if prev == nil:
|
||||
if aOrig.kind == tyStatic:
|
||||
result = typeRel(c, f.lastSon, a)
|
||||
if result != isNone and f.n != nil:
|
||||
if not exprStructuralEquivalent(f.n, aOrig.n):
|
||||
result = isNone
|
||||
if f.base.kind != tyNone:
|
||||
result = typeRel(c, f.base, a)
|
||||
if result != isNone and f.n != nil:
|
||||
if not exprStructuralEquivalent(f.n, aOrig.n):
|
||||
result = isNone
|
||||
else:
|
||||
result = isGeneric
|
||||
if result != isNone: put(c, f, aOrig)
|
||||
elif aOrig.n != nil and aOrig.n.typ != nil:
|
||||
result = typeRel(c, f.lastSon, aOrig.n.typ)
|
||||
result = if f.base.kind != tyNone: typeRel(c, f.lastSon, aOrig.n.typ)
|
||||
else: isGeneric
|
||||
if result != isNone:
|
||||
var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])
|
||||
boundType.n = aOrig.n
|
||||
|
|
@ -1707,9 +1711,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
|||
# proc foo(T: typedesc, x: T)
|
||||
# when `f` is an unresolved typedesc, `a` could be any
|
||||
# type, so we should not perform this check earlier
|
||||
if a.kind != tyTypeDesc: return isNone
|
||||
|
||||
if f.base.kind == tyNone:
|
||||
if a.kind != tyTypeDesc:
|
||||
if a.kind == tyGenericParam and tfWildcard in a.flags:
|
||||
# TODO: prevent `a` from matching as a wildcard again
|
||||
result = isGeneric
|
||||
else:
|
||||
result = isNone
|
||||
elif f.base.kind == tyNone:
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(c, f.base, a.base)
|
||||
|
|
@ -2353,12 +2361,22 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
|
|||
m.firstMismatch = f
|
||||
break
|
||||
else:
|
||||
# use default value:
|
||||
if formal.ast.kind == nkEmpty:
|
||||
# The default param value is set to empty in `instantiateProcType`
|
||||
# when the type of the default expression doesn't match the type
|
||||
# of the instantiated proc param:
|
||||
localError(c.config, m.call.info,
|
||||
("The default parameter '$1' has incompatible type " &
|
||||
"with the explicitly requested proc instantiation") %
|
||||
formal.name.s)
|
||||
if nfDefaultRefsParam in formal.ast.flags:
|
||||
m.call.flags.incl nfDefaultRefsParam
|
||||
var def = copyTree(formal.ast)
|
||||
if def.kind == nkNilLit:
|
||||
def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
|
||||
if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
|
||||
put(m, formal.typ, def.typ)
|
||||
def.flags.incl nfDefaultParam
|
||||
setSon(m.call, formal.position + 1, def)
|
||||
inc(f)
|
||||
# forget all inferred types if the overload matching failed
|
||||
|
|
|
|||
|
|
@ -780,6 +780,43 @@ proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
|
|||
else:
|
||||
result = n
|
||||
|
||||
proc hoistParamsUsedInDefault(c: PTransf, call, letSection, defExpr: PNode): PNode =
|
||||
# This takes care of complicated signatures such as:
|
||||
# proc foo(a: int, b = a)
|
||||
# proc bar(a: int, b: int, c = a + b)
|
||||
#
|
||||
# The recursion may confuse you. It performs two duties:
|
||||
#
|
||||
# 1) extracting all referenced params from default expressions
|
||||
# into a let section preceeding the call
|
||||
#
|
||||
# 2) replacing the "references" within the default expression
|
||||
# with these extracted skLet symbols.
|
||||
#
|
||||
# The first duty is carried out directly in the code here, while the second
|
||||
# duty is activated by returning a non-nil value. The caller is responsible
|
||||
# for replacing the input to the function with the returned non-nil value.
|
||||
# (which is the hoisted symbol)
|
||||
if defExpr.kind == nkSym:
|
||||
if defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
|
||||
let paramPos = defExpr.sym.position + 1
|
||||
|
||||
if call[paramPos].kind == nkSym and sfHoisted in call[paramPos].sym.flags:
|
||||
# Already hoisted, we still need to return it in order to replace the
|
||||
# placeholder expression in the default value.
|
||||
return call[paramPos]
|
||||
|
||||
let hoistedVarSym = hoistExpr(letSection,
|
||||
call[paramPos],
|
||||
getIdent(c.graph.cache, genPrefix),
|
||||
c.transCon.owner).newSymNode
|
||||
call[paramPos] = hoistedVarSym
|
||||
return hoistedVarSym
|
||||
else:
|
||||
for i in 0..<defExpr.safeLen:
|
||||
let hoisted = hoistParamsUsedInDefault(c, call, letSection, defExpr[i])
|
||||
if hoisted != nil: defExpr[i] = hoisted
|
||||
|
||||
proc transform(c: PTransf, n: PNode): PTransNode =
|
||||
when false:
|
||||
var oldDeferAnchor: PNode
|
||||
|
|
@ -849,6 +886,15 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
|||
of nkBreakStmt: result = transformBreak(c, n)
|
||||
of nkCallKinds:
|
||||
result = transformCall(c, n)
|
||||
var call = result.PNode
|
||||
if nfDefaultRefsParam in call.flags:
|
||||
# We've found a default value that references another param.
|
||||
# See the notes in `hoistParamsUsedInDefault` for more details.
|
||||
var hoistedParams = newNodeI(nkLetSection, call.info, 0)
|
||||
for i in 1 ..< call.len:
|
||||
let hoisted = hoistParamsUsedInDefault(c, call, hoistedParams, call[i])
|
||||
if hoisted != nil: call[i] = hoisted
|
||||
result = newTree(nkStmtListExpr, hoistedParams, call).PTransNode
|
||||
of nkAddr, nkHiddenAddr:
|
||||
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
|
||||
of nkDerefExpr, nkHiddenDeref:
|
||||
|
|
|
|||
|
|
@ -502,7 +502,10 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
|||
#internalAssert t.len == 0
|
||||
result = "untyped"
|
||||
of tyFromExpr:
|
||||
result = renderTree(t.n)
|
||||
if t.n == nil:
|
||||
result = "unknown"
|
||||
else:
|
||||
result = "type(" & renderTree(t.n) & ")"
|
||||
of tyArray:
|
||||
if t.sons[0].kind == tyRange:
|
||||
result = "array[" & rangeToStr(t.sons[0].n) & ", " &
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue