123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925 |
- #
- #
- # The Nim Compiler
- # (c) Copyright 2015 Andreas Rumpf
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- # included from sem.nim
- discard """
- hygienic templates:
- template `||` (a, b: untyped): untyped =
- let aa = a
- if aa: aa else: b
- var
- a, b: T
- echo a || b || a
- Each evaluation context has to be different and we need to perform
- some form of preliminary symbol lookup in template definitions. Hygiene is
- a way to achieve lexical scoping at compile time.
- """
- const
- errImplOfXNotAllowed = "implementation of '$1' is not allowed"
- type
- TSymBinding = enum
- spNone, spGenSym, spInject
- proc symBinding(n: PNode): TSymBinding =
- result = spNone
- for i in 0..<n.len:
- var it = n[i]
- var key = if it.kind == nkExprColonExpr: it[0] else: it
- if key.kind == nkIdent:
- case whichKeyword(key.ident)
- of wGensym: return spGenSym
- of wInject: return spInject
- else: discard
- type
- TSymChoiceRule = enum
- scClosed, scOpen, scForceOpen
- proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
- isField = false): PNode =
- var
- a: PSym
- o: TOverloadIter = default(TOverloadIter)
- var i = 0
- a = initOverloadIter(o, c, n)
- while a != nil:
- if a.kind != skModule:
- inc(i)
- if i > 1: break
- a = nextOverloadIter(o, c, n)
- let info = getCallLineInfo(n)
- if i <= 1 and r != scForceOpen:
- # XXX this makes more sense but breaks bootstrapping for now:
- # (s.kind notin routineKinds or s.magic != mNone):
- # for instance 'nextTry' is both in tables.nim and astalgo.nim ...
- if not isField or sfGenSym notin s.flags:
- result = newSymNode(s, info)
- markUsed(c, info, s)
- onUse(info, s)
- else:
- result = n
- elif i == 0:
- # forced open but symbol not in scope, retain information
- result = n
- else:
- # semantic checking requires a type; ``fitNode`` deals with it
- # appropriately
- let kind = if r == scClosed or n.kind == nkDotExpr: nkClosedSymChoice
- else: nkOpenSymChoice
- result = newNodeIT(kind, info, newTypeS(tyNone, c))
- a = initOverloadIter(o, c, n)
- while a != nil:
- if a.kind != skModule and (not isField or sfGenSym notin a.flags):
- incl(a.flags, sfUsed)
- markOwnerModuleAsUsed(c, a)
- result.add newSymNode(a, info)
- onUse(info, a)
- a = nextOverloadIter(o, c, n)
- proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
- result = copyNode(n)
- for i in 0..<n.len:
- var a = n[i]
- # If 'a' is an overloaded symbol, we used to use the first symbol
- # as a 'witness' and use the fact that subsequent lookups will yield
- # the same symbol!
- # This is however not true anymore for hygienic templates as semantic
- # processing for them changes the symbol table...
- let s = qualifiedLookUp(c, a, {checkUndeclared})
- if s != nil:
- # we need to mark all symbols:
- let sc = symChoice(c, n, s, scClosed)
- if sc.kind == nkSym:
- toBind.incl(sc.sym.id)
- result.add sc
- else:
- for x in items(sc):
- toBind.incl(x.sym.id)
- result.add x
- else:
- illFormedAst(a, c.config)
- proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
- result = copyNode(n)
- for i in 0..<n.len:
- toMixin.incl(considerQuotedIdent(c, n[i]).id)
- let x = symChoice(c, n[i], nil, scForceOpen)
- result.add x
- proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) =
- case n.kind
- of nkPostfix: replaceIdentBySym(c, n[1], s)
- of nkPragmaExpr: replaceIdentBySym(c, n[0], s)
- of nkIdent, nkAccQuoted, nkSym: n = s
- else: illFormedAst(n, c.config)
- type
- TemplCtx = object
- c: PContext
- toBind, toMixin, toInject: IntSet
- owner: PSym
- cursorInBody: bool # only for nimsuggest
- scopeN: int
- noGenSym: int
- inTemplateHeader: int
- proc isTemplParam(c: TemplCtx, s: PSym): bool {.inline.} =
- result = s.kind == skParam and
- s.owner == c.owner and sfTemplateParam in s.flags
- proc getIdentReplaceParams(c: var TemplCtx, n: var PNode): tuple[node: PNode, hasParam: bool] =
- case n.kind
- of nkPostfix: result = getIdentReplaceParams(c, n[1])
- of nkPragmaExpr: result = getIdentReplaceParams(c, n[0])
- of nkIdent:
- result = (n, false)
- let s = qualifiedLookUp(c.c, n, {})
- if s != nil and isTemplParam(c, s):
- n = newSymNode(s, n.info)
- result = (n, true)
- of nkSym:
- result = (n, isTemplParam(c, n.sym))
- of nkAccQuoted:
- result = (n, false)
- for i in 0..<n.safeLen:
- let (ident, hasParam) = getIdentReplaceParams(c, n[i])
- if hasParam:
- result.node[i] = ident
- result.hasParam = true
- else:
- illFormedAst(n, c.c.config)
- result = (n, false)
- proc semTemplBody(c: var TemplCtx, n: PNode): PNode
- proc openScope(c: var TemplCtx) =
- openScope(c.c)
- proc closeScope(c: var TemplCtx) =
- closeScope(c.c)
- proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
- openScope(c)
- result = semTemplBody(c, n)
- closeScope(c)
- proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
- result = newSym(kind, considerQuotedIdent(c.c, n), c.c.idgen, c.owner, n.info)
- incl(result.flags, sfGenSym)
- incl(result.flags, sfShadowed)
- proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
- # locals default to 'gensym', fields default to 'inject':
- if (n.kind == nkPragmaExpr and symBinding(n[1]) == spInject) or
- k == skField:
- # even if injected, don't produce a sym choice here:
- #n = semTemplBody(c, n)
- let (ident, hasParam) = getIdentReplaceParams(c, n)
- if not hasParam:
- if k != skField:
- c.toInject.incl(considerQuotedIdent(c.c, ident).id)
- else:
- if (n.kind == nkPragmaExpr and n.len >= 2 and n[1].kind == nkPragma):
- let pragmaNode = n[1]
- for i in 0..<pragmaNode.len:
- let ni = pragmaNode[i]
- # see D20210801T100514
- var found = false
- if ni.kind == nkIdent:
- for a in templatePragmas:
- if ni.ident.id == ord(a):
- found = true
- break
- if not found:
- openScope(c)
- pragmaNode[i] = semTemplBody(c, pragmaNode[i])
- closeScope(c)
- let (ident, hasParam) = getIdentReplaceParams(c, n)
- if not hasParam:
- if n.kind != nkSym and not (n.kind == nkIdent and n.ident.id == ord(wUnderscore)):
- let local = newGenSym(k, ident, c)
- addPrelimDecl(c.c, local)
- styleCheckDef(c.c, n.info, local)
- onDef(n.info, local)
- replaceIdentBySym(c.c, n, newSymNode(local, n.info))
- if k == skParam and c.inTemplateHeader > 0:
- local.flags.incl sfTemplateParam
- proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode =
- incl(s.flags, sfUsed)
- # bug #12885; ideally sem'checking is performed again afterwards marking
- # the symbol as used properly, but the nfSem mechanism currently prevents
- # that from happening, so we mark the module as used here already:
- markOwnerModuleAsUsed(c.c, s)
- # we do not call onUse here, as the identifier is not really
- # resolved here. We will fixup the used identifiers later.
- case s.kind
- of skUnknown:
- # Introduced in this pass! Leave it as an identifier.
- result = n
- of OverloadableSyms:
- result = symChoice(c.c, n, s, scOpen, isField)
- if not isField and result.kind in {nkSym, nkOpenSymChoice}:
- if openSym in c.c.features:
- if result.kind == nkSym:
- result = newOpenSym(result)
- else:
- result.typ() = nil
- else:
- result.flags.incl nfDisabledOpenSym
- result.typ() = nil
- of skGenericParam:
- if isField and sfGenSym in s.flags: result = n
- else:
- result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
- if not isField and s.owner != c.owner:
- if openSym in c.c.features:
- result = newOpenSym(result)
- else:
- result.flags.incl nfDisabledOpenSym
- result.typ() = nil
- of skParam:
- result = n
- of skType:
- if isField and sfGenSym in s.flags: result = n
- else:
- if isAmbiguous:
- # ambiguous types should be symchoices since lookup behaves
- # differently for them in regular expressions
- result = symChoice(c.c, n, s, scOpen, isField)
- else: result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
- if not isField and not (s.owner == c.owner and
- s.typ != nil and s.typ.kind == tyGenericParam) and
- result.kind in {nkSym, nkOpenSymChoice}:
- if openSym in c.c.features:
- if result.kind == nkSym:
- result = newOpenSym(result)
- else:
- result.typ() = nil
- else:
- result.flags.incl nfDisabledOpenSym
- result.typ() = nil
- else:
- if isField and sfGenSym in s.flags: result = n
- else:
- result = newSymNode(s, n.info)
- if not isField:
- if openSym in c.c.features:
- result = newOpenSym(result)
- else:
- result.flags.incl nfDisabledOpenSym
- result.typ() = nil
- # Issue #12832
- when defined(nimsuggest):
- suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false)
- # field access (dot expr) will be handled by builtinFieldAccess
- if not isField:
- styleCheckUse(c.c, n.info, s)
- proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode =
- result = n
- if n.kind == nkIdent:
- let s = qualifiedLookUp(c.c, n, {})
- if s != nil:
- if s.owner == c.owner and (s.kind == skParam or
- (sfGenSym in s.flags and not explicitInject)):
- incl(s.flags, sfUsed)
- result = newSymNode(s, n.info)
- onUse(n.info, s)
- else:
- for i in 0..<n.safeLen:
- result[i] = semRoutineInTemplName(c, n[i], explicitInject)
- proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
- result = n
- checkSonsLen(n, bodyPos + 1, c.c.config)
- if n.kind notin nkLambdaKinds:
- # routines default to 'inject':
- let binding = symBinding(n[pragmasPos])
- if binding == spGenSym:
- let (ident, hasParam) = getIdentReplaceParams(c, n[namePos])
- if not hasParam:
- var s = newGenSym(k, ident, c)
- s.ast = n
- addPrelimDecl(c.c, s)
- styleCheckDef(c.c, n.info, s)
- onDef(n.info, s)
- n[namePos] = newSymNode(s, n[namePos].info)
- else:
- n[namePos] = ident
- else:
- n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject)
- # open scope for parameters
- openScope(c)
- for i in patternPos..paramsPos-1:
- n[i] = semTemplBody(c, n[i])
- if k == skTemplate: inc(c.inTemplateHeader)
- n[paramsPos] = semTemplBody(c, n[paramsPos])
- if k == skTemplate: dec(c.inTemplateHeader)
- for i in paramsPos+1..miscPos:
- n[i] = semTemplBody(c, n[i])
- # open scope for locals
- inc c.scopeN
- openScope(c)
- n[bodyPos] = semTemplBody(c, n[bodyPos])
- # close scope for locals
- closeScope(c)
- dec c.scopeN
- # close scope for parameters
- closeScope(c)
- proc semTemplIdentDef(c: var TemplCtx, a: PNode, symKind: TSymKind) =
- checkMinSonsLen(a, 3, c.c.config)
- when defined(nimsuggest):
- inc c.c.inTypeContext
- a[^2] = semTemplBody(c, a[^2])
- when defined(nimsuggest):
- dec c.c.inTypeContext
- a[^1] = semTemplBody(c, a[^1])
- for j in 0..<a.len-2:
- addLocalDecl(c, a[j], symKind)
- proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start = 0) =
- for i in start..<n.len:
- var a = n[i]
- case a.kind:
- of nkCommentStmt: continue
- of nkIdentDefs, nkVarTuple, nkConstDef:
- semTemplIdentDef(c, a, symKind)
- else:
- illFormedAst(a, c.c.config)
- proc semPattern(c: PContext, n: PNode; s: PSym): PNode
- proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode =
- result = n
- for i in 0..<n.len:
- result[i] = semTemplBody(c, n[i])
- proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
- result = n
- semIdeForTemplateOrGenericCheck(c.c.config, n, c.cursorInBody)
- case n.kind
- of nkIdent:
- if n.ident.id in c.toInject: return n
- c.c.isAmbiguous = false
- let s = qualifiedLookUp(c.c, n, {})
- if s != nil:
- if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags:
- incl(s.flags, sfUsed)
- result = newSymNode(s, n.info)
- onUse(n.info, s)
- elif contains(c.toBind, s.id):
- result = symChoice(c.c, n, s, scClosed, c.noGenSym > 0)
- elif contains(c.toMixin, s.name.id):
- result = symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
- elif s.owner == c.owner and sfGenSym in s.flags and c.noGenSym == 0:
- # template tmp[T](x: var seq[T]) =
- # var yz: T
- incl(s.flags, sfUsed)
- result = newSymNode(s, n.info)
- onUse(n.info, s)
- else:
- if s.kind in {skVar, skLet, skConst}:
- discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
- result = semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
- of nkBind:
- result = semTemplBody(c, n[0])
- of nkBindStmt:
- result = semBindStmt(c.c, n, c.toBind)
- of nkMixinStmt:
- if c.scopeN > 0: result = semTemplBodySons(c, n)
- else: result = semMixinStmt(c.c, n, c.toMixin)
- of nkEmpty, nkSym..nkNilLit, nkComesFrom:
- discard
- of nkIfStmt:
- for i in 0..<n.len:
- var it = n[i]
- if it.len == 2:
- openScope(c)
- it[0] = semTemplBody(c, it[0])
- it[1] = semTemplBody(c, it[1])
- closeScope(c)
- else:
- n[i] = semTemplBodyScope(c, it)
- of nkWhileStmt:
- openScope(c)
- for i in 0..<n.len:
- n[i] = semTemplBody(c, n[i])
- closeScope(c)
- of nkCaseStmt:
- openScope(c)
- n[0] = semTemplBody(c, n[0])
- for i in 1..<n.len:
- var a = n[i]
- checkMinSonsLen(a, 1, c.c.config)
- for j in 0..<a.len-1:
- a[j] = semTemplBody(c, a[j])
- a[^1] = semTemplBodyScope(c, a[^1])
- closeScope(c)
- of nkForStmt, nkParForStmt:
- openScope(c)
- n[^2] = semTemplBody(c, n[^2])
- for i in 0..<n.len - 2:
- if n[i].kind == nkVarTuple:
- for j in 0..<n[i].len-1:
- addLocalDecl(c, n[i][j], skForVar)
- else:
- addLocalDecl(c, n[i], skForVar)
- openScope(c)
- n[^1] = semTemplBody(c, n[^1])
- closeScope(c)
- closeScope(c)
- of nkBlockStmt, nkBlockExpr, nkBlockType:
- checkSonsLen(n, 2, c.c.config)
- openScope(c)
- if n[0].kind != nkEmpty:
- addLocalDecl(c, n[0], skLabel)
- when false:
- # labels are always 'gensym'ed:
- let s = newGenSym(skLabel, n[0], c)
- addPrelimDecl(c.c, s)
- styleCheckDef(c.c, s)
- onDef(n[0].info, s)
- n[0] = newSymNode(s, n[0].info)
- n[1] = semTemplBody(c, n[1])
- closeScope(c)
- of nkTryStmt, nkHiddenTryStmt:
- checkMinSonsLen(n, 2, c.c.config)
- n[0] = semTemplBodyScope(c, n[0])
- for i in 1..<n.len:
- var a = n[i]
- checkMinSonsLen(a, 1, c.c.config)
- openScope(c)
- for j in 0..<a.len-1:
- if a[j].isInfixAs():
- addLocalDecl(c, a[j][2], skLet)
- a[j][1] = semTemplBody(c, a[j][1])
- else:
- a[j] = semTemplBody(c, a[j])
- a[^1] = semTemplBodyScope(c, a[^1])
- closeScope(c)
- of nkVarSection: semTemplSomeDecl(c, n, skVar)
- of nkLetSection: semTemplSomeDecl(c, n, skLet)
- of nkFormalParams:
- checkMinSonsLen(n, 1, c.c.config)
- semTemplSomeDecl(c, n, skParam, 1)
- n[0] = semTemplBody(c, n[0])
- of nkConstSection: semTemplSomeDecl(c, n, skConst)
- of nkTypeSection:
- for i in 0..<n.len:
- var a = n[i]
- if a.kind == nkCommentStmt: continue
- if (a.kind != nkTypeDef): illFormedAst(a, c.c.config)
- checkSonsLen(a, 3, c.c.config)
- addLocalDecl(c, a[0], skType)
- for i in 0..<n.len:
- var a = n[i]
- if a.kind == nkCommentStmt: continue
- if (a.kind != nkTypeDef): illFormedAst(a, c.c.config)
- checkSonsLen(a, 3, c.c.config)
- if a[1].kind != nkEmpty:
- openScope(c)
- a[1] = semTemplBody(c, a[1])
- a[2] = semTemplBody(c, a[2])
- closeScope(c)
- else:
- a[2] = semTemplBody(c, a[2])
- of nkObjectTy:
- openScope(c)
- result = semTemplBodySons(c, n)
- closeScope(c)
- of nkRecList:
- for i in 0..<n.len:
- var a = n[i]
- case a.kind:
- of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
- of nkIdentDefs:
- semTemplIdentDef(c, a, skField)
- of nkRecCase, nkRecWhen:
- n[i] = semTemplBody(c, a)
- else:
- illFormedAst(a, c.c.config)
- of nkRecCase:
- semTemplIdentDef(c, n[0], skField)
- for i in 1..<n.len:
- n[i] = semTemplBody(c, n[i])
- of nkProcDef, nkLambdaKinds:
- result = semRoutineInTemplBody(c, n, skProc)
- of nkFuncDef:
- result = semRoutineInTemplBody(c, n, skFunc)
- of nkMethodDef:
- result = semRoutineInTemplBody(c, n, skMethod)
- of nkIteratorDef:
- result = semRoutineInTemplBody(c, n, skIterator)
- of nkTemplateDef:
- result = semRoutineInTemplBody(c, n, skTemplate)
- of nkMacroDef:
- result = semRoutineInTemplBody(c, n, skMacro)
- of nkConverterDef:
- result = semRoutineInTemplBody(c, n, skConverter)
- of nkPragmaExpr:
- result = semTemplBodySons(c, n)
- of nkPostfix:
- result[1] = semTemplBody(c, n[1])
- of nkPragma:
- for i in 0 ..< n.len:
- let x = n[i]
- let prag = whichPragma(x)
- if prag == wInvalid:
- # only sem if not a language-level pragma
- result[i] = semTemplBody(c, x)
- elif x.kind in nkPragmaCallKinds:
- # is pragma, but value still needs to be checked
- for j in 1 ..< x.len:
- x[j] = semTemplBody(c, x[j])
- of nkBracketExpr:
- if n.typ == nil:
- # if a[b] is nested inside a typed expression, don't convert it
- # back to `[]`(a, b), prepareOperand will not typecheck it again
- # and so `[]` will not be resolved
- # checking if a[b] is typed should be enough to cover this case
- result = newNodeI(nkCall, n.info)
- result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
- for i in 0..<n.len: result.add(n[i])
- result = semTemplBodySons(c, result)
- of nkCurlyExpr:
- if n.typ == nil:
- # see nkBracketExpr case for explanation
- result = newNodeI(nkCall, n.info)
- result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
- for i in 0..<n.len: result.add(n[i])
- result = semTemplBodySons(c, result)
- of nkAsgn, nkFastAsgn, nkSinkAsgn:
- checkSonsLen(n, 2, c.c.config)
- let a = n[0]
- let b = n[1]
- let k = a.kind
- case k
- of nkBracketExpr:
- if a.typ == nil:
- # see nkBracketExpr case above for explanation
- result = newNodeI(nkCall, n.info)
- result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
- for i in 0..<a.len: result.add(a[i])
- result.add(b)
- let a0 = semTemplBody(c, a[0])
- result = semTemplBodySons(c, result)
- of nkCurlyExpr:
- if a.typ == nil:
- # see nkBracketExpr case above for explanation
- result = newNodeI(nkCall, n.info)
- result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
- for i in 0..<a.len: result.add(a[i])
- result.add(b)
- result = semTemplBodySons(c, result)
- else:
- result = semTemplBodySons(c, n)
- of nkCallKinds-{nkPostfix}:
- # do not transform runnableExamples (bug #9143)
- if not isRunnableExamples(n[0]):
- result = semTemplBodySons(c, n)
- of nkDotExpr, nkAccQuoted:
- # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
- # so we use the generic code for nkDotExpr too
- c.c.isAmbiguous = false
- let s = qualifiedLookUp(c.c, n, {})
- if s != nil:
- # mirror the nkIdent case
- # do not symchoice a quoted template parameter (bug #2390):
- if s.owner == c.owner and s.kind == skParam and
- n.kind == nkAccQuoted and n.len == 1:
- incl(s.flags, sfUsed)
- onUse(n.info, s)
- return newSymNode(s, n.info)
- elif contains(c.toBind, s.id):
- return symChoice(c.c, n, s, scClosed, c.noGenSym > 0)
- elif contains(c.toMixin, s.name.id):
- return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
- else:
- if s.kind in {skVar, skLet, skConst}:
- discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
- return semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
- if n.kind == nkDotExpr:
- result = n
- result[0] = semTemplBody(c, n[0])
- inc c.noGenSym
- result[1] = semTemplBody(c, n[1])
- dec c.noGenSym
- if result[1].kind == nkSym and result[1].sym.kind in routineKinds:
- # prevent `dotTransformation` from rewriting this node to `nkIdent`
- # by making it a symchoice
- # in generics this becomes `nkClosedSymChoice` but this breaks code
- # as the old behavior here was that this became `nkIdent`
- var choice = newNodeIT(nkOpenSymChoice, n[1].info, newTypeS(tyNone, c.c))
- choice.add result[1]
- result[1] = choice
- else:
- result = semTemplBodySons(c, n)
- of nkExprColonExpr, nkExprEqExpr:
- if n.len == 2:
- inc c.noGenSym
- result[0] = semTemplBody(c, n[0])
- dec c.noGenSym
- result[1] = semTemplBody(c, n[1])
- else:
- result = semTemplBodySons(c, n)
- of nkTableConstr:
- # also transform the keys (bug #12595)
- for i in 0..<n.len:
- result[i] = semTemplBodySons(c, n[i])
- else:
- result = semTemplBodySons(c, n)
- proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
- result = n
- semIdeForTemplateOrGenericCheck(c.c.config, n, c.cursorInBody)
- case n.kind
- of nkIdent:
- let s = qualifiedLookUp(c.c, n, {})
- if s != nil:
- if s.owner == c.owner and s.kind == skParam:
- result = newSymNode(s, n.info)
- elif contains(c.toBind, s.id):
- result = symChoice(c.c, n, s, scClosed)
- of nkBind:
- result = semTemplBodyDirty(c, n[0])
- of nkBindStmt:
- result = semBindStmt(c.c, n, c.toBind)
- of nkEmpty, nkSym..nkNilLit, nkComesFrom:
- discard
- else:
- # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
- # so we use the generic code for nkDotExpr too
- if n.kind == nkDotExpr or n.kind == nkAccQuoted:
- let s = qualifiedLookUp(c.c, n, {})
- if s != nil and contains(c.toBind, s.id):
- return symChoice(c.c, n, s, scClosed)
- result = n
- for i in 0..<n.len:
- result[i] = semTemplBodyDirty(c, n[i])
- # in semstmts.nim:
- proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode
- proc semTemplateDef(c: PContext, n: PNode): PNode =
- result = semProcAnnotation(c, n, templatePragmas)
- if result != nil: return result
- result = n
- var s: PSym
- if isTopLevel(c):
- s = semIdentVis(c, skTemplate, n[namePos], {sfExported})
- incl(s.flags, sfGlobal)
- else:
- s = semIdentVis(c, skTemplate, n[namePos], {})
- assert s.kind == skTemplate
- styleCheckDef(c, s)
- onDef(n[namePos].info, s)
- # check parameter list:
- #s.scope = c.currentScope
- # push noalias flag at first to prevent unwanted recursive calls:
- incl(s.flags, sfNoalias)
- pushOwner(c, s)
- openScope(c)
- n[namePos] = newSymNode(s)
- s.ast = n # for implicitPragmas to use
- pragmaCallable(c, s, n, templatePragmas)
- implicitPragmas(c, s, n.info, templatePragmas)
- setGenericParamsMisc(c, n)
- # process parameters:
- var allUntyped = true
- var nullary = true
- if n[paramsPos].kind != nkEmpty:
- semParamList(c, n[paramsPos], n[genericParamsPos], s)
- # a template's parameters are not gensym'ed even if that was originally the
- # case as we determine whether it's a template parameter in the template
- # body by the absence of the sfGenSym flag:
- let retType = s.typ.returnType
- if retType != nil and retType.kind != tyUntyped:
- allUntyped = false
- for i in 1..<s.typ.n.len:
- let param = s.typ.n[i].sym
- if param.name.id != ord(wUnderscore):
- param.flags.incl sfTemplateParam
- param.flags.excl sfGenSym
- if param.typ.kind != tyUntyped: allUntyped = false
- # no default value, parameters required in call
- if param.ast == nil: nullary = false
- else:
- s.typ = newTypeS(tyProc, c)
- # XXX why do we need tyTyped as a return type again?
- s.typ.n = newNodeI(nkFormalParams, n.info)
- rawAddSon(s.typ, newTypeS(tyTyped, c))
- s.typ.n.add newNodeIT(nkType, n.info, s.typ.returnType)
- if n[genericParamsPos].safeLen == 0:
- # restore original generic type params as no explicit or implicit were found
- n[genericParamsPos] = n[miscPos][1]
- n[miscPos] = c.graph.emptyNode
- if allUntyped: incl(s.flags, sfAllUntyped)
- if nullary and
- n[genericParamsPos].kind == nkEmpty and
- n[bodyPos].kind != nkEmpty:
- # template can be called with alias syntax, remove pushed noalias flag
- excl(s.flags, sfNoalias)
- if n[patternPos].kind != nkEmpty:
- n[patternPos] = semPattern(c, n[patternPos], s)
- var ctx = TemplCtx(
- toBind: initIntSet(),
- toMixin: initIntSet(),
- toInject: initIntSet(),
- c: c,
- owner: s
- )
- # handle default params:
- for i in 1..<s.typ.n.len:
- let param = s.typ.n[i].sym
- if param.ast != nil:
- # param default values need to be treated like template body:
- if sfDirty in s.flags:
- param.ast = semTemplBodyDirty(ctx, param.ast)
- else:
- param.ast = semTemplBody(ctx, param.ast)
- if param.ast.referencesAnotherParam(s):
- param.ast.flags.incl nfDefaultRefsParam
- if sfDirty in s.flags:
- n[bodyPos] = semTemplBodyDirty(ctx, n[bodyPos])
- else:
- n[bodyPos] = semTemplBody(ctx, n[bodyPos])
- # only parameters are resolved, no type checking is performed
- semIdeForTemplateOrGeneric(c, n[bodyPos], ctx.cursorInBody)
- closeScope(c)
- popOwner(c)
- if sfCustomPragma in s.flags:
- if n[bodyPos].kind != nkEmpty:
- localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s)
- elif n[bodyPos].kind == nkEmpty:
- localError(c.config, n.info, "implementation of '$1' expected" % s.name.s)
- var (proto, comesFromShadowscope) = searchForProc(c, c.currentScope, s)
- if proto == nil:
- addInterfaceOverloadableSymAt(c, c.currentScope, s)
- elif not comesFromShadowscope:
- if {sfTemplateRedefinition, sfGenSym} * s.flags == {}:
- #wrongRedefinition(c, n.info, proto.name.s, proto.info)
- message(c.config, n.info, warnImplicitTemplateRedefinition, s.name.s)
- symTabReplace(c.currentScope.symbols, proto, s)
- if n[patternPos].kind != nkEmpty:
- c.patterns.add(s)
- proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
- template templToExpand(s: untyped): untyped =
- s.kind == skTemplate and (s.typ.len == 1 or sfAllUntyped in s.flags)
- proc newParam(c: var TemplCtx, n: PNode, s: PSym): PNode =
- # the param added in the current scope is actually wrong here for
- # macros because they have a shadowed param of type 'PNimNode' (see
- # semtypes.addParamOrResult). Within the pattern we have to ensure
- # to use the param with the proper type though:
- incl(s.flags, sfUsed)
- onUse(n.info, s)
- let x = c.owner.typ.n[s.position+1].sym
- assert x.name == s.name
- result = newSymNode(x, n.info)
- proc handleSym(c: var TemplCtx, n: PNode, s: PSym): PNode =
- result = n
- if s != nil:
- if s.owner == c.owner and s.kind == skParam:
- result = newParam(c, n, s)
- elif contains(c.toBind, s.id):
- result = symChoice(c.c, n, s, scClosed)
- elif templToExpand(s):
- result = semPatternBody(c, semTemplateExpr(c.c, n, s, {efNoSemCheck}))
- else:
- discard
- # we keep the ident unbound for matching instantiated symbols and
- # more flexibility
- proc expectParam(c: var TemplCtx, n: PNode): PNode =
- let s = qualifiedLookUp(c.c, n, {})
- if s != nil and s.owner == c.owner and s.kind == skParam:
- result = newParam(c, n, s)
- else:
- localError(c.c.config, n.info, "invalid expression")
- result = n
- result = n
- case n.kind
- of nkIdent:
- let s = qualifiedLookUp(c.c, n, {})
- result = handleSym(c, n, s)
- of nkBindStmt:
- result = semBindStmt(c.c, n, c.toBind)
- of nkEmpty, nkSym..nkNilLit: discard
- of nkCurlyExpr:
- # we support '(pattern){x}' to bind a subpattern to a parameter 'x';
- # '(pattern){|x}' does the same but the matches will be gathered in 'x'
- if n.len != 2:
- localError(c.c.config, n.info, "invalid expression")
- elif n[1].kind == nkIdent:
- n[0] = semPatternBody(c, n[0])
- n[1] = expectParam(c, n[1])
- elif n[1].kind == nkPrefix and n[1][0].kind == nkIdent:
- let opr = n[1][0]
- if opr.ident.s == "|":
- n[0] = semPatternBody(c, n[0])
- n[1][1] = expectParam(c, n[1][1])
- else:
- localError(c.c.config, n.info, "invalid expression")
- else:
- localError(c.c.config, n.info, "invalid expression")
- of nkStmtList, nkStmtListExpr:
- if stupidStmtListExpr(n):
- result = semPatternBody(c, n.lastSon)
- else:
- for i in 0..<n.len:
- result[i] = semPatternBody(c, n[i])
- of nkCallKinds:
- let s = qualifiedLookUp(c.c, n[0], {})
- if s != nil:
- if s.owner == c.owner and s.kind == skParam: discard
- elif contains(c.toBind, s.id): discard
- elif templToExpand(s):
- return semPatternBody(c, semTemplateExpr(c.c, n, s, {efNoSemCheck}))
- if n.kind == nkInfix and (let id = considerQuotedIdent(c.c, n[0]); id != nil):
- # we interpret `*` and `|` only as pattern operators if they occur in
- # infix notation, so that '`*`(a, b)' can be used for verbatim matching:
- if id.s == "*" or id.s == "**":
- result = newNodeI(nkPattern, n.info, n.len)
- result[0] = newIdentNode(id, n.info)
- result[1] = semPatternBody(c, n[1])
- result[2] = expectParam(c, n[2])
- return
- elif id.s == "|":
- result = newNodeI(nkPattern, n.info, n.len)
- result[0] = newIdentNode(id, n.info)
- result[1] = semPatternBody(c, n[1])
- result[2] = semPatternBody(c, n[2])
- return
- if n.kind == nkPrefix and (let id = considerQuotedIdent(c.c, n[0]); id != nil):
- if id.s == "~":
- result = newNodeI(nkPattern, n.info, n.len)
- result[0] = newIdentNode(id, n.info)
- result[1] = semPatternBody(c, n[1])
- return
- for i in 0..<n.len:
- result[i] = semPatternBody(c, n[i])
- else:
- # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
- # so we use the generic code for nkDotExpr too
- case n.kind
- of nkDotExpr, nkAccQuoted:
- let s = qualifiedLookUp(c.c, n, {})
- if s != nil:
- if contains(c.toBind, s.id):
- return symChoice(c.c, n, s, scClosed)
- else:
- return newIdentNode(s.name, n.info)
- of nkPar:
- if n.len == 1: return semPatternBody(c, n[0])
- else: discard
- for i in 0..<n.len:
- result[i] = semPatternBody(c, n[i])
- proc semPattern(c: PContext, n: PNode; s: PSym): PNode =
- openScope(c)
- var ctx = TemplCtx(
- toBind: initIntSet(),
- toMixin: initIntSet(),
- toInject: initIntSet(),
- c: c,
- owner: getCurrOwner(c)
- )
- result = flattenStmts(semPatternBody(ctx, n))
- if result.kind in {nkStmtList, nkStmtListExpr}:
- if result.len == 1:
- result = result[0]
- elif result.len == 0:
- localError(c.config, n.info, "a pattern cannot be empty")
- closeScope(c)
- addPattern(c, LazySym(sym: s))
|