This commit is contained in:
Araq 2015-03-23 00:08:49 +01:00
commit bba551fd1c

View file

@ -10,17 +10,17 @@
# included from sem.nim # included from sem.nim
discard """ discard """
hygienic templates: hygienic templates:
template `||` (a, b: expr): expr = template `||` (a, b: expr): expr =
let aa = a let aa = a
if aa: aa else: b if aa: aa else: b
var var
a, b: T a, b: T
a || b || a a || b || a
Each evaluation context has to be different and we need to perform Each evaluation context has to be different and we need to perform
some form of preliminary symbol lookup in template definitions. Hygiene is some form of preliminary symbol lookup in template definitions. Hygiene is
a way to achieve lexical scoping at compile time. a way to achieve lexical scoping at compile time.
@ -50,7 +50,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
o: TOverloadIter o: TOverloadIter
var i = 0 var i = 0
a = initOverloadIter(o, c, n) a = initOverloadIter(o, c, n)
while a != nil: while a != nil:
a = nextOverloadIter(o, c, n) a = nextOverloadIter(o, c, n)
inc(i) inc(i)
if i > 1: break if i > 1: break
@ -96,7 +96,7 @@ proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
for i in 0 .. < n.len: for i in 0 .. < n.len:
toMixin.incl(considerQuotedIdent(n.sons[i]).id) toMixin.incl(considerQuotedIdent(n.sons[i]).id)
result = newNodeI(nkEmpty, n.info) result = newNodeI(nkEmpty, n.info)
proc replaceIdentBySym(n: var PNode, s: PNode) = proc replaceIdentBySym(n: var PNode, s: PNode) =
case n.kind case n.kind
of nkPostfix: replaceIdentBySym(n.sons[1], s) of nkPostfix: replaceIdentBySym(n.sons[1], s)
@ -135,7 +135,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode
proc openScope(c: var TemplCtx) = openScope(c.c) proc openScope(c: var TemplCtx) = openScope(c.c)
proc closeScope(c: var TemplCtx) = closeScope(c.c) proc closeScope(c: var TemplCtx) = closeScope(c.c)
proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode = proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
openScope(c) openScope(c)
result = semTemplBody(c, n) result = semTemplBody(c, n)
closeScope(c) closeScope(c)
@ -191,24 +191,24 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
else: else:
replaceIdentBySym(n, ident) replaceIdentBySym(n, ident)
proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode = proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
# we do not call styleCheckUse here, as the identifier is not really # we do not call styleCheckUse here, as the identifier is not really
# resolved here. We will fixup the used identifiers later. # resolved here. We will fixup the used identifiers later.
case s.kind case s.kind
of skUnknown: of skUnknown:
# Introduced in this pass! Leave it as an identifier. # Introduced in this pass! Leave it as an identifier.
result = n result = n
of OverloadableSyms: of OverloadableSyms:
result = symChoice(c, n, s, scOpen) result = symChoice(c, n, s, scOpen)
of skGenericParam: of skGenericParam:
result = newSymNodeTypeDesc(s, n.info) result = newSymNodeTypeDesc(s, n.info)
of skParam: of skParam:
result = n result = n
of skType: of skType:
if (s.typ != nil) and (s.typ.kind != tyGenericParam): if (s.typ != nil) and (s.typ.kind != tyGenericParam):
result = newSymNodeTypeDesc(s, n.info) result = newSymNodeTypeDesc(s, n.info)
else: else:
result = n result = n
else: else:
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
@ -305,21 +305,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
n.sons[i] = semTemplBodyScope(c, it) n.sons[i] = semTemplBodyScope(c, it)
of nkWhileStmt: of nkWhileStmt:
openScope(c) openScope(c)
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
n.sons[i] = semTemplBody(c, n.sons[i]) n.sons[i] = semTemplBody(c, n.sons[i])
closeScope(c) closeScope(c)
of nkCaseStmt: of nkCaseStmt:
openScope(c) openScope(c)
n.sons[0] = semTemplBody(c, n.sons[0]) n.sons[0] = semTemplBody(c, n.sons[0])
for i in countup(1, sonsLen(n)-1): for i in countup(1, sonsLen(n)-1):
var a = n.sons[i] var a = n.sons[i]
checkMinSonsLen(a, 1) checkMinSonsLen(a, 1)
var L = sonsLen(a) var L = sonsLen(a)
for j in countup(0, L-2): for j in countup(0, L-2):
a.sons[j] = semTemplBody(c, a.sons[j]) a.sons[j] = semTemplBody(c, a.sons[j])
a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1]) a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
closeScope(c) closeScope(c)
of nkForStmt, nkParForStmt: of nkForStmt, nkParForStmt:
var L = sonsLen(n) var L = sonsLen(n)
openScope(c) openScope(c)
n.sons[L-2] = semTemplBody(c, n.sons[L-2]) n.sons[L-2] = semTemplBody(c, n.sons[L-2])
@ -338,14 +338,14 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
n.sons[0] = newSymNode(s, n.sons[0].info) n.sons[0] = newSymNode(s, n.sons[0].info)
n.sons[1] = semTemplBody(c, n.sons[1]) n.sons[1] = semTemplBody(c, n.sons[1])
closeScope(c) closeScope(c)
of nkTryStmt: of nkTryStmt:
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
n.sons[0] = semTemplBodyScope(c, n.sons[0]) n.sons[0] = semTemplBodyScope(c, n.sons[0])
for i in countup(1, sonsLen(n)-1): for i in countup(1, sonsLen(n)-1):
var a = n.sons[i] var a = n.sons[i]
checkMinSonsLen(a, 1) checkMinSonsLen(a, 1)
var L = sonsLen(a) var L = sonsLen(a)
for j in countup(0, L-2): for j in countup(0, L-2):
a.sons[j] = semTemplBody(c, a.sons[j]) a.sons[j] = semTemplBody(c, a.sons[j])
a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1]) a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
of nkVarSection: semTemplSomeDecl(c, n, skVar) of nkVarSection: semTemplSomeDecl(c, n, skVar)
@ -355,32 +355,32 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
n.sons[0] = semTemplBody(c, n.sons[0]) n.sons[0] = semTemplBody(c, n.sons[0])
semTemplSomeDecl(c, n, skParam, 1) semTemplSomeDecl(c, n, skParam, 1)
of nkConstSection: of nkConstSection:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if (a.kind != nkConstDef): illFormedAst(a) if (a.kind != nkConstDef): illFormedAst(a)
checkSonsLen(a, 3) checkSonsLen(a, 3)
addLocalDecl(c, a.sons[0], skConst) addLocalDecl(c, a.sons[0], skConst)
a.sons[1] = semTemplBody(c, a.sons[1]) a.sons[1] = semTemplBody(c, a.sons[1])
a.sons[2] = semTemplBody(c, a.sons[2]) a.sons[2] = semTemplBody(c, a.sons[2])
of nkTypeSection: of nkTypeSection:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): illFormedAst(a) if (a.kind != nkTypeDef): illFormedAst(a)
checkSonsLen(a, 3) checkSonsLen(a, 3)
addLocalDecl(c, a.sons[0], skType) addLocalDecl(c, a.sons[0], skType)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): illFormedAst(a) if (a.kind != nkTypeDef): illFormedAst(a)
checkSonsLen(a, 3) checkSonsLen(a, 3)
if a.sons[1].kind != nkEmpty: if a.sons[1].kind != nkEmpty:
openScope(c) openScope(c)
a.sons[1] = semTemplBody(c, a.sons[1]) a.sons[1] = semTemplBody(c, a.sons[1])
a.sons[2] = semTemplBody(c, a.sons[2]) a.sons[2] = semTemplBody(c, a.sons[2])
closeScope(c) closeScope(c)
else: else:
a.sons[2] = semTemplBody(c, a.sons[2]) a.sons[2] = semTemplBody(c, a.sons[2])
of nkProcDef, nkLambdaKinds: of nkProcDef, nkLambdaKinds:
result = semRoutineInTemplBody(c, n, skProc) result = semRoutineInTemplBody(c, n, skProc)
@ -408,7 +408,13 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
if n.kind == nkDotExpr or n.kind == nkAccQuoted: if n.kind == nkDotExpr or n.kind == nkAccQuoted:
let s = qualifiedLookUp(c.c, n, {}) let s = qualifiedLookUp(c.c, n, {})
if s != nil: if s != nil:
if contains(c.toBind, s.id): # 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)
styleCheckUse(n.info, s)
return newSymNode(s, n.info)
elif contains(c.toBind, s.id):
return symChoice(c.c, n, s, scClosed) return symChoice(c.c, n, s, scClosed)
elif contains(c.toMixin, s.name.id): elif contains(c.toMixin, s.name.id):
return symChoice(c.c, n, s, scForceOpen) return symChoice(c.c, n, s, scForceOpen)
@ -445,31 +451,31 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
result = n result = n
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semTemplBodyDirty(c, n.sons[i]) result.sons[i] = semTemplBodyDirty(c, n.sons[i])
proc transformToExpr(n: PNode): PNode = proc transformToExpr(n: PNode): PNode =
var realStmt: int var realStmt: int
result = n result = n
case n.kind case n.kind
of nkStmtList: of nkStmtList:
realStmt = - 1 realStmt = - 1
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
case n.sons[i].kind case n.sons[i].kind
of nkCommentStmt, nkEmpty, nkNilLit: of nkCommentStmt, nkEmpty, nkNilLit:
discard discard
else: else:
if realStmt == - 1: realStmt = i if realStmt == - 1: realStmt = i
else: realStmt = - 2 else: realStmt = - 2
if realStmt >= 0: result = transformToExpr(n.sons[realStmt]) if realStmt >= 0: result = transformToExpr(n.sons[realStmt])
else: n.kind = nkStmtListExpr else: n.kind = nkStmtListExpr
of nkBlockStmt: of nkBlockStmt:
n.kind = nkBlockExpr n.kind = nkBlockExpr
#nkIfStmt: n.kind = nkIfExpr // this is not correct! #nkIfStmt: n.kind = nkIfExpr // this is not correct!
else: else:
discard discard
proc semTemplateDef(c: PContext, n: PNode): PNode = proc semTemplateDef(c: PContext, n: PNode): PNode =
var s: PSym var s: PSym
if c.p.owner.kind == skModule: if c.p.owner.kind == skModule:
s = semIdentVis(c, skTemplate, n.sons[0], {sfExported}) s = semIdentVis(c, skTemplate, n.sons[0], {sfExported})
incl(s.flags, sfGlobal) incl(s.flags, sfGlobal)
else: else:
@ -484,10 +490,10 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
pragma(c, s, n.sons[pragmasPos], templatePragmas) pragma(c, s, n.sons[pragmasPos], templatePragmas)
var gp: PNode var gp: PNode
if n.sons[genericParamsPos].kind != nkEmpty: if n.sons[genericParamsPos].kind != nkEmpty:
n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos]) n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
gp = n.sons[genericParamsPos] gp = n.sons[genericParamsPos]
else: else:
gp = newNodeI(nkGenericParams, n.info) gp = newNodeI(nkGenericParams, n.info)
# process parameters: # process parameters:
if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].kind != nkEmpty:
@ -525,14 +531,14 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
else: else:
n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos]) n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos])
if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}: if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}:
n.sons[bodyPos] = transformToExpr(n.sons[bodyPos]) n.sons[bodyPos] = transformToExpr(n.sons[bodyPos])
# only parameters are resolved, no type checking is performed # only parameters are resolved, no type checking is performed
semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody) semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody)
closeScope(c) closeScope(c)
popOwner() popOwner()
s.ast = n s.ast = n
result = n result = n
if n.sons[bodyPos].kind == nkEmpty: if n.sons[bodyPos].kind == nkEmpty:
localError(n.info, errImplOfXexpected, s.name.s) localError(n.info, errImplOfXexpected, s.name.s)
var proto = searchForProc(c, c.currentScope, s) var proto = searchForProc(c, c.currentScope, s)
if proto == nil: if proto == nil:
@ -545,7 +551,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
proc semPatternBody(c: var TemplCtx, n: PNode): PNode = proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
template templToExpand(s: expr): expr = template templToExpand(s: expr): expr =
s.kind == skTemplate and (s.typ.len == 1 or sfImmediate in s.flags) s.kind == skTemplate and (s.typ.len == 1 or sfImmediate in s.flags)
proc newParam(c: var TemplCtx, n: PNode, s: PSym): PNode = proc newParam(c: var TemplCtx, n: PNode, s: PSym): PNode =
# the param added in the current scope is actually wrong here for # the param added in the current scope is actually wrong here for
# macros because they have a shadowed param of type 'PNimNode' (see # macros because they have a shadowed param of type 'PNimNode' (see
@ -556,7 +562,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
let x = c.owner.typ.n.sons[s.position+1].sym let x = c.owner.typ.n.sons[s.position+1].sym
assert x.name == s.name assert x.name == s.name
result = newSymNode(x, n.info) result = newSymNode(x, n.info)
proc handleSym(c: var TemplCtx, n: PNode, s: PSym): PNode = proc handleSym(c: var TemplCtx, n: PNode, s: PSym): PNode =
result = n result = n
if s != nil: if s != nil:
@ -570,7 +576,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
discard discard
# we keep the ident unbound for matching instantiated symbols and # we keep the ident unbound for matching instantiated symbols and
# more flexibility # more flexibility
proc expectParam(c: var TemplCtx, n: PNode): PNode = proc expectParam(c: var TemplCtx, n: PNode): PNode =
let s = qualifiedLookUp(c.c, n, {}) let s = qualifiedLookUp(c.c, n, {})
if s != nil and s.owner == c.owner and s.kind == skParam: if s != nil and s.owner == c.owner and s.kind == skParam:
@ -578,7 +584,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
else: else:
localError(n.info, errInvalidExpression) localError(n.info, errInvalidExpression)
result = n result = n
result = n result = n
case n.kind case n.kind
of nkIdent: of nkIdent:
@ -588,7 +594,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
result = semBindStmt(c.c, n, c.toBind) result = semBindStmt(c.c, n, c.toBind)
of nkEmpty, nkSym..nkNilLit: discard of nkEmpty, nkSym..nkNilLit: discard
of nkCurlyExpr: of nkCurlyExpr:
# we support '(pattern){x}' to bind a subpattern to a parameter 'x'; # 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' # '(pattern){|x}' does the same but the matches will be gathered in 'x'
if n.len != 2: if n.len != 2:
localError(n.info, errInvalidExpression) localError(n.info, errInvalidExpression)
@ -611,7 +617,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
elif contains(c.toBind, s.id): discard elif contains(c.toBind, s.id): discard
elif templToExpand(s): elif templToExpand(s):
return semPatternBody(c, semTemplateExpr(c.c, n, s, {efNoSemCheck})) return semPatternBody(c, semTemplateExpr(c.c, n, s, {efNoSemCheck}))
if n.kind == nkInfix and n.sons[0].kind == nkIdent: if n.kind == nkInfix and n.sons[0].kind == nkIdent:
# we interpret `*` and `|` only as pattern operators if they occur in # we interpret `*` and `|` only as pattern operators if they occur in
# infix notation, so that '`*`(a, b)' can be used for verbatim matching: # infix notation, so that '`*`(a, b)' can be used for verbatim matching:
@ -628,7 +634,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
result.sons[1] = semPatternBody(c, n.sons[1]) result.sons[1] = semPatternBody(c, n.sons[1])
result.sons[2] = semPatternBody(c, n.sons[2]) result.sons[2] = semPatternBody(c, n.sons[2])
return return
if n.kind == nkPrefix and n.sons[0].kind == nkIdent: if n.kind == nkPrefix and n.sons[0].kind == nkIdent:
let opr = n.sons[0] let opr = n.sons[0]
if opr.ident.s == "~": if opr.ident.s == "~":
@ -636,13 +642,13 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
result.sons[0] = opr result.sons[0] = opr
result.sons[1] = semPatternBody(c, n.sons[1]) result.sons[1] = semPatternBody(c, n.sons[1])
return return
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semPatternBody(c, n.sons[i]) result.sons[i] = semPatternBody(c, n.sons[i])
else: else:
# dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
# so we use the generic code for nkDotExpr too # so we use the generic code for nkDotExpr too
case n.kind case n.kind
of nkDotExpr, nkAccQuoted: of nkDotExpr, nkAccQuoted:
let s = qualifiedLookUp(c.c, n, {}) let s = qualifiedLookUp(c.c, n, {})
if s != nil: if s != nil: