#
#
# 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.} =
# if we encountered an error, we treat as top-level so that
# cascading errors are not that strange:
result = c.tab.tos <= 2 or msgs.gErrorCounter > 0
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
c.p = newProcCon(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 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:
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()
c.p = nil
proc semPass(): TPass =
initPass(result)
result.open = myOpen
result.openCached = myOpenCached
result.close = myClose
result.process = myProcess