next steps to hygienic templates

This commit is contained in:
Araq
2012-08-20 08:44:17 +02:00
parent 5e15dec175
commit da190876de
17 changed files with 97 additions and 76 deletions

View File

@@ -259,7 +259,10 @@ const
sfImmediate* = sfDeadCodeElim
# macro or template is immediately expanded
# without considering any possible overloads
sfDirty* = sfPure
# template is not hygienic (old styled template)
sfAnon* = sfDiscardable
# symbol name that was generated by the compiler
# the compiler will avoid printing such names
@@ -691,7 +694,8 @@ const
# creator procs:
proc NewSym*(symKind: TSymKind, Name: PIdent, owner: PSym): PSym
proc NewSym*(symKind: TSymKind, Name: PIdent, owner: PSym,
info: TLineInfo): PSym
proc NewType*(kind: TTypeKind, owner: PSym): PType
proc newNode*(kind: TNodeKind): PNode
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode
@@ -922,11 +926,10 @@ proc copyType(t: PType, owner: PSym, keepId: bool): PType =
result.sym = t.sym # backend-info should not be copied
proc copySym(s: PSym, keepId: bool = false): PSym =
result = newSym(s.kind, s.name, s.owner)
result = newSym(s.kind, s.name, s.owner, s.info)
result.ast = nil # BUGFIX; was: s.ast which made problems
result.info = s.info
result.typ = s.typ
if keepId:
if keepId:
result.id = s.id
else:
result.id = getID()
@@ -939,13 +942,14 @@ proc copySym(s: PSym, keepId: bool = false): PSym =
result.loc = s.loc
result.annex = s.annex # BUGFIX
proc NewSym(symKind: TSymKind, Name: PIdent, owner: PSym): PSym =
proc NewSym(symKind: TSymKind, Name: PIdent, owner: PSym,
info: TLineInfo): PSym =
# generates a symbol and initializes the hash field too
new(result)
result.Name = Name
result.Kind = symKind
result.flags = {}
result.info = UnknownLineInfo()
result.info = info
result.options = gOptions
result.owner = owner
result.offset = - 1

View File

@@ -254,7 +254,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
for i in countup(0, sonsLen(t) - 1):
var p = newNodeIT(nkExprColonExpr, info, t.sons[i])
var field = if t.n != nil: t.n.sons[i].sym else: newSym(
skField, getIdent(":tmp" & $i), t.owner)
skField, getIdent(":tmp" & $i), t.owner, info)
addSon(p, newSymNode(field, info))
addSon(p, getNullValue(t.sons[i], info))
addSon(result, p)
@@ -1358,7 +1358,7 @@ proc evalMacroCall*(c: PEvalContext, n: PNode, sym: PSym): PNode =
if evalTemplateCounter > 100:
GlobalError(n.info, errTemplateInstantiationTooNested)
inc genSymBaseId
#inc genSymBaseId
var s = newStackFrame()
s.call = n
setlen(s.params, 2)

View File

@@ -11,7 +11,7 @@
import idents, strutils, os, options
var gFrontEndId, gBackendId*, genSymBaseId*: int
var gFrontEndId, gBackendId*: int
const
debugIds* = false
@@ -34,9 +34,6 @@ proc backendId*(): int {.inline.} =
result = gBackendId
inc(gBackendId)
proc genSym*(basename: string): PIdent =
result = getIdent(basename & $genSymBaseId)
proc setId*(id: int) {.inline.} =
gFrontEndId = max(gFrontEndId, id + 1)
@@ -66,4 +63,3 @@ proc loadMaxIds*(project: string) =
gFrontEndId = max(gFrontEndId, frontEndId)
gBackEndId = max(gBackEndId, backEndId)
f.close()

View File

@@ -162,7 +162,7 @@ proc newEnv(outerProc: PSym, up: PEnv, n: PNode): PEnv =
result.attachedNode = n
proc addField(tup: PType, s: PSym) =
var field = newSym(skField, s.name, s.owner)
var field = newSym(skField, s.name, s.owner, s.info)
let t = skipIntLit(s.typ)
field.typ = t
field.position = sonsLen(tup)
@@ -179,7 +179,7 @@ proc addDep(e, d: PEnv, owner: PSym): PSym =
for x, field in items(e.deps):
if x == d: return field
var pos = sonsLen(e.tup)
result = newSym(skField, getIdent(upName & $pos), owner)
result = newSym(skField, getIdent(upName & $pos), owner, owner.info)
result.typ = newType(tyRef, owner)
result.position = pos
assert d.tup != nil
@@ -220,8 +220,7 @@ proc isInnerProc(s, outerProc: PSym): bool {.inline.} =
#s.typ.callConv == ccClosure
proc addClosureParam(i: PInnerContext, e: PEnv) =
var cp = newSym(skParam, getIdent(paramname), i.fn)
cp.info = i.fn.info
var cp = newSym(skParam, getIdent(paramname), i.fn, i.fn.info)
incl(cp.flags, sfFromGeneric)
cp.typ = newType(tyRef, i.fn)
rawAddSon(cp.typ, e.tup)
@@ -449,9 +448,8 @@ proc addVar*(father, v: PNode) =
proc getClosureVar(o: POuterContext, e: PEnv): PSym =
if e.closure == nil:
result = newSym(skVar, getIdent(envName), o.fn)
result = newSym(skVar, getIdent(envName), o.fn, e.attachedNode.info)
incl(result.flags, sfShadowed)
result.info = e.attachedNode.info
result.typ = newType(tyRef, o.fn)
result.typ.rawAddSon(e.tup)
e.closure = result
@@ -590,8 +588,7 @@ type
tup: PType
proc newIterResult(iter: PSym): PSym =
result = newSym(skResult, getIdent":result", iter)
result.info = iter.info
result = newSym(skResult, getIdent":result", iter, iter.info)
result.typ = iter.typ.sons[0]
incl(result.flags, sfUsed)
@@ -650,15 +647,14 @@ proc liftIterator*(iter: PSym, body: PNode): PNode =
c.tup = newType(tyTuple, iter)
c.tup.n = newNodeI(nkRecList, iter.info)
var cp = newSym(skParam, getIdent(paramname), iter)
cp.info = iter.info
var cp = newSym(skParam, getIdent(paramname), iter, iter.info)
incl(cp.flags, sfFromGeneric)
cp.typ = newType(tyRef, iter)
rawAddSon(cp.typ, c.tup)
c.closureParam = cp
addHiddenParam(iter, cp)
c.state = newSym(skField, getIdent(":state"), iter)
c.state = newSym(skField, getIdent(":state"), iter, iter.info)
c.state.typ = getStateType(iter)
addField(c.tup, c.state)

View File

@@ -22,18 +22,14 @@ proc considerAcc*(n: PNode): PIdent =
of 0: GlobalError(n.info, errIdentifierExpected, renderTree(n))
of 1: result = considerAcc(n.sons[0])
else:
if n.len == 2 and n[0].kind == nkIdent and n[0].ident.id == ord(wStar):
# XXX find a better way instead of `*x` for 'genSym'
result = genSym(n[1].ident.s)
else:
var id = ""
for i in 0.. <n.len:
let x = n.sons[i]
case x.kind
of nkIdent: id.add(x.ident.s)
of nkSym: id.add(x.sym.name.s)
else: GlobalError(n.info, errIdentifierExpected, renderTree(n))
result = getIdent(id)
var id = ""
for i in 0.. <n.len:
let x = n.sons[i]
case x.kind
of nkIdent: id.add(x.ident.s)
of nkSym: id.add(x.sym.name.s)
else: GlobalError(n.info, errIdentifierExpected, renderTree(n))
result = getIdent(id)
else:
GlobalError(n.info, errIdentifierExpected, renderTree(n))
@@ -46,8 +42,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
considerAcc(m)
else:
getIdent("err:" & renderTree(m))
result = newSym(skError, ident, getCurrOwner())
result.info = n.info
result = newSym(skError, ident, getCurrOwner(), n.info)
result.typ = errorType(c)
incl(result.flags, sfDiscardable)
# pretend it's imported from some unknown module to prevent cascading errors:

View File

@@ -27,7 +27,7 @@ const
wGenSym, wInject}
converterPragmas* = procPragmas
methodPragmas* = procPragmas
templatePragmas* = {wImmediate, wDeprecated, wError, wGenSym, wInject}
templatePragmas* = {wImmediate, wDeprecated, wError, wGenSym, wInject, wDirty}
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
wNodecl, wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
wImportcpp, wImportobjc, wError, wDiscardable, wGenSym, wInject}
@@ -454,8 +454,7 @@ proc processPragma(c: PContext, n: PNode, i: int) =
elif it.sons[0].kind != nkIdent: invalidPragma(n)
elif it.sons[1].kind != nkIdent: invalidPragma(n)
var userPragma = NewSym(skTemplate, it.sons[1].ident, nil)
userPragma.info = it.info
var userPragma = NewSym(skTemplate, it.sons[1].ident, nil, it.info)
var body = newNodeI(nkPragma, n.info)
for j in i+1 .. sonsLen(n)-1: addSon(body, n.sons[j])
userPragma.ast = body
@@ -488,6 +487,9 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
of wImmediate:
if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate)
else: invalidPragma(it)
of wDirty:
if sym.kind == skTemplate: incl(sym.flags, sfDirty)
else: invalidPragma(it)
of wImportCpp:
processImportCpp(sym, getOptionalStr(c, it, sym.name.s))
of wImportObjC:

View File

@@ -55,8 +55,7 @@ proc isTopLevel(c: PContext): bool {.inline.} =
result = c.tab.tos <= 2
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
result = newSym(kind, considerAcc(n), getCurrOwner())
result.info = n.info
result = newSym(kind, considerAcc(n), getCurrOwner(), n.info)
proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
# like newSymS, but considers gensym'ed symbols
@@ -65,8 +64,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
InternalAssert sfGenSym in result.flags
InternalAssert result.kind == kind
else:
result = newSym(kind, considerAcc(n), getCurrOwner())
result.info = n.info
result = newSym(kind, considerAcc(n), getCurrOwner(), n.info)
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym

View File

@@ -1470,7 +1470,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkTypeOfExpr:
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
typ = makeTypedesc(c, typ)
var sym = newSym(skType, getIdent"TypeOfExpr", typ.owner).linkTo(typ)
var sym = newSym(skType, getIdent"TypeOfExpr",
typ.owner, n.info).linkTo(typ)
sym.flags.incl(sfAnon)
result = newSymNode(sym, n.info)
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:

View File

@@ -255,7 +255,7 @@ proc semGenericStmt(c: PContext, n: PNode,
flags, toBind)
if n.sons[paramsPos].kind != nkEmpty:
if n.sons[paramsPos].sons[0].kind != nkEmpty:
addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil))
addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info))
n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, toBind)
n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, toBind)
var body: PNode

View File

@@ -21,8 +21,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
InternalError(a.info, "instantiateGenericParamList; no symbol")
var q = a.sym
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyTypeClass, tyExpr}: continue
var s = newSym(skType, q.name, getCurrOwner())
s.info = q.info
var s = newSym(skType, q.name, getCurrOwner(), q.info)
s.flags = s.flags + {sfUsed, sfFromGeneric}
var t = PType(IdTableGet(pt, q.typ))
if t == nil:

View File

@@ -380,7 +380,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
var trueSymbol = StrTableGet(magicsys.systemModule.Tab, getIdent"true")
if trueSymbol == nil:
LocalError(n.info, errSystemNeeds, "true")
trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner())
trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info)
trueSymbol.typ = getSysType(tyBool)
result.add(newSymNode(trueSymbol, n.info))
@@ -620,10 +620,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
aa.sons[0].kind == nkObjectTy:
# give anonymous object a dummy symbol:
assert s.typ.sons[0].sym == nil
var anonObj = newSym(skType, getIdent(s.name.s & ":ObjectType"),
getCurrOwner())
anonObj.info = s.info
s.typ.sons[0].sym = anonObj
s.typ.sons[0].sym = newSym(skType, getIdent(s.name.s & ":ObjectType"),
getCurrOwner(), s.info)
proc SemTypeSection(c: PContext, n: PNode): PNode =
typeSectionLeftSidePass(c, n)
@@ -650,8 +648,7 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) =
proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) =
if t != nil:
var s = newSym(skResult, getIdent"result", getCurrOwner())
s.info = info
var s = newSym(skResult, getIdent"result", getCurrOwner(), info)
s.typ = t
incl(s.flags, sfUsed)
addParamOrResult(c, s, owner)
@@ -697,8 +694,7 @@ proc semLambda(c: PContext, n: PNode): PNode =
if result != nil: return result
result = n
checkSonsLen(n, bodyPos + 1)
var s = newSym(skProc, getIdent":anonymous", getCurrOwner())
s.info = n.info
var s = newSym(skProc, getIdent":anonymous", getCurrOwner(), n.info)
s.ast = n
n.sons[namePos] = newSymNode(s)
pushOwner(s)
@@ -830,7 +826,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
popProcCon(c)
else:
if s.typ.sons[0] != nil and kind != skIterator:
addDecl(c, newSym(skUnknown, getIdent"result", nil))
addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
var toBind = initIntSet()
n.sons[bodyPos] = semGenericStmtScope(c, n.sons[bodyPos], {}, toBind)
fixupInstantiatedSymbols(c, s)

View File

@@ -104,9 +104,9 @@ proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
illFormedAst(n)
result = n
proc isTemplParam(n: PNode): bool {.inline.} =
proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} =
result = n.kind == nkSym and n.sym.kind == skParam and
n.sym.owner.kind == skTemplate
n.sym.owner == c.owner
proc semTemplBody(c: var TemplCtx, n: PNode): PNode
@@ -119,15 +119,15 @@ proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
closeScope(c)
proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
result = newSym(kind, considerAcc(n), c.owner)
result = newSym(kind, considerAcc(n), 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':
if n.kind != nkPragmaExpr or symBinding(n.sons[1]) != spInject:
let ident = getIdentNode(c, n)
if not isTemplParam(ident):
let ident = getIdentNode(c, n)
if not isTemplParam(c, ident):
let local = newGenSym(k, ident, c)
addPrelimDecl(c.c, local)
replaceIdentBySym(n, newSymNode(local, n.info))
@@ -142,7 +142,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
# routines default to 'inject':
if n.kind notin nkLambdaKinds and symBinding(n.sons[pragmasPos]) == spGenSym:
let ident = getIdentNode(c, n.sons[namePos])
if not isTemplParam(ident):
if not isTemplParam(c, ident):
let s = newGenSym(k, ident, c)
addPrelimDecl(c.c, s)
n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
@@ -164,6 +164,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
let s = QualifiedLookUp(c.c, n, {})
if s != nil:
if s.owner == c.owner and s.kind == skParam:
incl(s.flags, sfUsed)
result = newSymNode(s, n.info)
elif Contains(c.toBind, s.id):
result = symChoice(c.c, n, s)
@@ -286,6 +287,33 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result = n
for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semTemplBody(c, n.sons[i])
proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
result = n
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)
of nkBind:
result = semTemplBodyDirty(c, n.sons[0])
of nkBindStmt:
result = semBindStmt(c.c, n, c.toBind)
of nkEmpty, nkSym..nkNilLit:
nil
else:
# dotExpr is ambiguous: note that we explicitely 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)
result = n
for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semTemplBodyDirty(c, n.sons[i])
proc transformToExpr(n: PNode): PNode =
var realStmt: int
@@ -340,7 +368,10 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
ctx.toBind = initIntSet()
ctx.c = c
ctx.owner = s
n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos])
if sfDirty in s.flags:
n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos])
else:
n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos])
if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}:
n.sons[bodyPos] = transformToExpr(n.sons[bodyPos])
# only parameters are resolved, no type checking is performed

View File

@@ -622,7 +622,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result = genericParams.sons[i].typ
break addImplicitGeneric
var s = newSym(skType, paramTypId, getCurrOwner())
var s = newSym(skType, paramTypId, getCurrOwner(), info)
if isAnon: s.flags.incl(sfAnon)
s.linkTo(typeClass)
s.position = genericParams.len

View File

@@ -96,8 +96,7 @@ proc getCurrOwner(c: PTransf): PSym =
else: result = c.module
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym =
result = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c))
result.info = info
result = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
result.typ = skipTypes(typ, {tyGenericInst})
incl(result.flags, sfFromGeneric)
@@ -205,9 +204,8 @@ proc hasContinue(n: PNode): bool =
if hasContinue(n.sons[i]): return true
proc newLabel(c: PTransf, n: PNode): PSym =
result = newSym(skLabel, nil, getCurrOwner(c))
result = newSym(skLabel, nil, getCurrOwner(c), n.info)
result.name = getIdent(genPrefix & $result.id)
result.info = n.info
proc transformBlock(c: PTransf, n: PNode): PTransNode =
var labl: PSym

View File

@@ -60,7 +60,8 @@ type
wFieldChecks,
wWatchPoint, wSubsChar,
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
wWrite, wGensym, wInject, wInheritable, wThreadVar, wEmit, wNoStackFrame,
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wNoStackFrame,
wImplicitStatic, wGlobal, wHoist
wAuto, wBool, wCatch, wChar, wClass,
@@ -138,7 +139,7 @@ const
"passc", "passl", "borrow", "discardable", "fieldchecks",
"watchpoint",
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
"write", "gensym", "inject", "inheritable", "threadvar", "emit",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
"nostackframe", "implicitstatic", "global", "hoist",
"auto", "bool", "catch", "char", "class",

View File

@@ -2290,6 +2290,9 @@ template doAssert*(cond: bool, msg = "") =
if not cond:
raiseAssert(astToStr(cond) & ' ' & msg)
when not defined(nimhygiene):
{.pragma: inject.}
template onFailedAssert*(msg: expr, code: stmt): stmt =
## Sets an assertion failure handler that will intercept any assert statements
## following `onFailedAssert` in the current lexical scope.
@@ -2310,7 +2313,7 @@ template onFailedAssert*(msg: expr, code: stmt): stmt =
## assert(...)
##
template raiseAssert(msgIMPL: string): stmt =
let `msg` = msgIMPL
let msg {.inject.} = msgIMPL
code
proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =

View File

@@ -1,7 +1,8 @@
version 0.9.0
=============
- make templates hygienic by default: 'gensym', 'inject' pragmas
- make templates hygienic by default: 'gensym', 'inject' pragmas;
document 'gensym', 'inject' and 'dirty'
- make 'bind' default for templates and introduce 'mixin'
- use ``\`` for comment continuations
- ``final`` should be the default for objects