diff options
Diffstat (limited to 'compiler/sem.nim')
-rwxr-xr-x | compiler/sem.nim | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/compiler/sem.nim b/compiler/sem.nim new file mode 100755 index 000000000..bb948ffc9 --- /dev/null +++ b/compiler/sem.nim @@ -0,0 +1,234 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2011 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# This module implements the semantic checking pass. + +import + strutils, nhashes, lists, options, scanner, ast, astalgo, trees, treetab, + wordrecg, ropes, msgs, os, condsyms, idents, rnimsyn, types, platform, math, + magicsys, pnimsyn, nversion, nimsets, semdata, evals, semfold, importer, + procfind, lookups, rodread, pragmas, passes, semtypinst, sigmatch, suggest + +proc semPass*(): TPass +# implementation + +proc considerAcc(n: PNode): PIdent = + var x = n + if x.kind == nkAccQuoted: x = x.sons[0] + case x.kind + of nkIdent: result = x.ident + of nkSym: result = x.sym.name + else: + GlobalError(n.info, errIdentifierExpected, renderTree(n)) + result = nil + +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 + +proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym + # identifier with visability +proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, + allowed: TSymFlags): PSym +proc semStmtScope(c: PContext, n: PNode): PNode + +type + TExprFlag = enum + efAllowType, efLValue, efWantIterator + TExprFlags = set[TExprFlag] + +proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode +proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode +proc fitNode(c: PContext, formal: PType, arg: PNode): PNode +proc semLambda(c: PContext, n: PNode): PNode +proc semTypeNode(c: PContext, n: PNode, prev: PType): PType +proc semStmt(c: PContext, n: PNode): PNode +proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) +proc addParams(c: PContext, n: PNode) +proc addResult(c: PContext, t: PType, info: TLineInfo) +proc addResultNode(c: PContext, n: PNode) +proc instGenericContainer(c: PContext, n: PNode, header: PType): PType + +proc semConstExpr(c: PContext, n: PNode): PNode = + result = semExprWithType(c, n) + if result == nil: + GlobalError(n.info, errConstExprExpected) + return + result = getConstExpr(c.module, result) + if result == nil: GlobalError(n.info, errConstExprExpected) + +proc semAndEvalConstExpr(c: PContext, n: PNode): PNode = + var e = semExprWithType(c, n) + if e == nil: + GlobalError(n.info, errConstExprExpected) + return nil + result = getConstExpr(c.module, e) + if result == nil: + #writeln(output, renderTree(n)); + result = evalConstExpr(c.module, e) + if (result == nil) or (result.kind == nkEmpty): + GlobalError(n.info, errConstExprExpected) + +proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = + result = n + case s.typ.sons[0].kind + of tyExpr: + # BUGFIX: we cannot expect a type here, because module aliases would not + # work then (see the ``tmodulealias`` test) + result = semExpr(c, result) # semExprWithType(c, result) + of tyStmt: result = semStmt(c, result) + of tyTypeDesc: result.typ = semTypeNode(c, result, nil) + else: GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0])) + +include "semtempl.nim" + +proc semMacroExpr(c: PContext, n: PNode, sym: PSym, + semCheck: bool = true): PNode = + inc(evalTemplateCounter) + if evalTemplateCounter > 100: + GlobalError(n.info, errTemplateInstantiationTooNested) + markUsed(n, sym) + var p = newEvalContext(c.module, "", false) + var s = newStackFrame() + s.call = n + setlen(s.params, 2) + s.params[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0]) + s.params[1] = n + pushStackFrame(p, s) + discard eval(p, sym.ast.sons[codePos]) + result = s.params[0] + popStackFrame(p) + if cyclicTree(result): GlobalError(n.info, errCyclicTree) + if semCheck: result = semAfterMacroCall(c, result, sym) + dec(evalTemplateCounter) + +include seminst, semcall + +proc typeMismatch(n: PNode, formal, actual: PType) = + GlobalError(n.Info, errGenerated, msgKindToString(errTypeMismatch) & + typeToString(actual) & ") " & + `%`(msgKindToString(errButExpectedX), [typeToString(formal)])) + +proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = + result = IndexTypesMatch(c, formal, arg.typ, arg) + if result == nil: + #debug(arg) + typeMismatch(arg, formal, arg.typ) + +proc forceBool(c: PContext, n: PNode): PNode = + result = fitNode(c, getSysType(tyBool), n) + if result == nil: result = n + when false: + result = t + if (t.Typ == nil) or + (skipTypes(t.Typ, {tyGenericInst, tyVar, tyOrdinal}).kind != tyBool): + var a = ConvertTo(c, getSysType(tyBool), t) + if a != nil: result = a + else: LocalError(t.Info, errExprMustBeBool) + +proc semConstBoolExpr(c: PContext, n: PNode): PNode = + result = fitNode(c, getSysType(tyBool), semExprWithType(c, n)) + if result == nil: + GlobalError(n.info, errConstExprExpected) + return + result = getConstExpr(c.module, result) + if result == nil: GlobalError(n.info, errConstExprExpected) + +include semtypes, semexprs, semgnrc, semstmts + +proc addCodeForGenerics(c: PContext, n: PNode) = + for i in countup(c.lastGenericIdx, sonsLen(c.generics) - 1): + var it = c.generics.sons[i].sons[1] + if it.kind != nkSym: InternalError("addCodeForGenerics") + var prc = it.sym + if (prc.kind in {skProc, skMethod, skConverter}) and (prc.magic == mNone): + if (prc.ast == nil) or (prc.ast.sons[codePos] == nil): + InternalError(prc.info, "no code for " & prc.name.s) + addSon(n, prc.ast) + c.lastGenericIdx = sonsLen(c.generics) + +proc semExprNoFlags(c: PContext, n: PNode): PNode = + result = semExpr(c, n, {}) + +proc myOpen(module: PSym, filename: string): PPassContext = + var c = newContext(module, filename) + if (c.p != nil): InternalError(module.info, "sem.myOpen") + c.semConstExpr = semConstExpr + c.semExpr = semExprNoFlags + pushProcCon(c, module) + pushOwner(c.module) + openScope(c.tab) # scope for imported symbols + SymTabAdd(c.tab, module) # a module knows itself + if sfSystemModule in module.flags: + magicsys.SystemModule = module # set global variable! + InitSystem(c.tab) # currently does nothing + else: + SymTabAdd(c.tab, magicsys.SystemModule) # import the "System" identifier + importAllSymbols(c, magicsys.SystemModule) + openScope(c.tab) # scope for the module's symbols + result = c + +proc myOpenCached(module: PSym, filename: string, + rd: PRodReader): PPassContext = + var c = PContext(myOpen(module, filename)) + c.fromCache = true + result = c + +proc SemStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = + result = semStmt(c, n) + # BUGFIX: process newly generated generics here, not at the end! + if sonsLen(c.generics) > 0: + var a = newNodeI(nkStmtList, n.info) + addCodeForGenerics(c, a) + if sonsLen(a) > 0: + # a generic has been added to `a`: + if result.kind != nkEmpty: addSon(a, result) + result = a + +proc RecoverContext(c: PContext) = + # clean up in case of a semantic error: We clean up the stacks, etc. This is + # faster than wrapping every stack operation in a 'try finally' block and + # requires far less code. + while c.tab.tos-1 > ModuleTablePos: rawCloseScope(c.tab) + while getCurrOwner().kind != skModule: popOwner() + while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next + +proc myProcess(context: PPassContext, n: PNode): PNode = + var c = PContext(context) + # no need for an expensive 'try' if we stop after the first error anyway: + if msgs.gErrorMax <= 1: + result = SemStmtAndGenerateGenerics(c, n) + else: + try: + result = SemStmtAndGenerateGenerics(c, n) + except ERecoverableError: + RecoverContext(c) + result = ast.emptyNode + +proc myClose(context: PPassContext, n: PNode): PNode = + var c = PContext(context) + closeScope(c.tab) # close module's scope + rawCloseScope(c.tab) # imported symbols; don't check for unused ones! + if n == nil: + result = newNode(nkStmtList) + else: + InternalError(n.info, "n is not nil") #result := n; + addCodeForGenerics(c, result) + popOwner() + popProcCon(c) + +proc semPass(): TPass = + initPass(result) + result.open = myOpen + result.openCached = myOpenCached + result.close = myClose + result.process = myProcess |