merged upstream master

This commit is contained in:
Zahary Karadjov 2013-01-27 23:41:45 +02:00
commit 81a3585872
127 changed files with 4440 additions and 1496 deletions

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -172,6 +172,9 @@ type
nkDiscardStmt, # a discard statement
nkStmtList, # a list of statements
nkImportStmt, # an import statement
nkImportExceptStmt, # an import x except a statement
nkExportStmt, # an export statement
nkExportExceptStmt, # an 'export except' statement
nkFromStmt, # a from * import statement
nkIncludeStmt, # an include statement
nkBindStmt, # a bind statement
@ -647,7 +650,8 @@ type
loc*: TLoc
annex*: PLib # additional fields (seldom used, so we use a
# reference to another object to safe space)
constraint*: PNode # additional constraints like 'lit|result'
TTypeSeq* = seq[PType]
TType* = object of TIdObj # types are identical iff they have the
# same id; there may be multiple copies of a type
@ -673,7 +677,6 @@ type
align*: int # the type's alignment requirements
containerID*: int # used for type checking of generics
loc*: TLoc
constraint*: PNode # additional constraints like 'lit|result'
TPair*{.final.} = object
key*, val*: PObject

View file

@ -429,7 +429,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope =
[istr, makeYamlString($n.kind)])
if maxRecDepth != 0:
case n.kind
of nkCharLit..nkInt64Lit:
of nkCharLit..nkUInt64Lit:
appf(result, ",$N$1\"intVal\": $2", [istr, toRope(n.intVal)])
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, ",$N$1\"floatVal\": $2",
@ -585,8 +585,9 @@ proc StrTableContains(t: TStrTable, n: PSym): bool =
proc StrTableRawInsert(data: var TSymSeq, n: PSym) =
var h: THash = n.name.h and high(data)
while data[h] != nil:
if data[h] == n:
InternalError(n.info, "StrTableRawInsert: " & n.name.s)
if data[h] == n:
# allowed for 'export' feature:
#InternalError(n.info, "StrTableRawInsert: " & n.name.s)
return
h = nextTry(h, high(data))
assert(data[h] == nil)
@ -617,23 +618,23 @@ proc StrTableAdd(t: var TStrTable, n: PSym) =
StrTableRawInsert(t.data, n)
inc(t.counter)
proc StrTableIncl*(t: var TStrTable, n: PSym): bool =
proc StrTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
# returns true if n is already in the string table:
# It is essential that `n` is written nevertheless!
# This way the newest redefinition is picked by the semantic analyses!
assert n.name != nil
var h: THash = n.name.h and high(t.data)
while true:
while true:
var it = t.data[h]
if it == nil: break
if it.name.id == n.name.id:
if it == nil: break
if it.name.id == n.name.id:
t.data[h] = n # overwrite it with newer definition!
return true # found it
h = nextTry(h, high(t.data))
if mustRehash(len(t.data), t.counter):
if mustRehash(len(t.data), t.counter):
StrTableEnlarge(t)
StrTableRawInsert(t.data, n)
else:
else:
assert(t.data[h] == nil)
t.data[h] = n
inc(t.counter)

90
compiler/babelcmd.nim Normal file
View file

@ -0,0 +1,90 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Implements some helper procs for Babel (Nimrod's package manager) support.
import parseutils, strutils, strtabs, os, options, msgs, lists
proc addPath*(path: string, info: TLineInfo) =
if not contains(options.searchPaths, path):
lists.PrependStr(options.searchPaths, path)
proc versionSplitPos(s: string): int =
result = s.len-2
while result > 1 and s[result] in {'0'..'9', '.'}: dec result
if s[result] != '-': result = s.len
const
latest = "head"
proc `<.`(a, b: string): bool =
# wether a has a smaller version than b:
if a == latest: return false
var i = 0
var j = 0
var verA = 0
var verB = 0
while true:
let ii = parseInt(a, verA, i)
let jj = parseInt(b, verB, j)
# if A has no number left, but B has, B is prefered: 0.8 vs 0.8.3
if ii <= 0 or jj <= 0: return jj > 0
if verA < verB: return true
elif verA > verB: return false
# else: same version number; continue:
inc i, ii
inc j, jj
if a[i] == '.': inc i
if b[j] == '.': inc j
proc addPackage(packages: PStringTable, p: string) =
let x = versionSplitPos(p)
let name = p.subStr(0, x-1)
if x < p.len:
let version = p.subStr(x+1)
if packages[name] <. version:
packages[name] = version
else:
packages[name] = latest
iterator chosen(packages: PStringTable): string =
for key, val in pairs(packages):
let res = if val == latest: key else: key & '-' & val
yield res
proc addBabelPath(p: string, info: TLineInfo) =
if not contains(options.searchPaths, p):
Message(info, hintPath, p)
lists.PrependStr(options.lazyPaths, p)
proc addPathWithNimFiles(p: string, info: TLineInfo) =
proc hasNimFile(dir: string): bool =
for kind, path in walkDir(dir):
if kind == pcFile and path.endsWith(".nim"):
result = true
break
if hasNimFile(p):
addBabelPath(p, info)
else:
for kind, p2 in walkDir(p):
if hasNimFile(p2): addBabelPath(p2, info)
proc addPathRec(dir: string, info: TLineInfo) =
var packages = newStringTable(modeStyleInsensitive)
var pos = dir.len-1
if dir[pos] in {DirSep, AltSep}: inc(pos)
for k,p in os.walkDir(dir):
if k == pcDir and p[pos] != '.':
addPackage(packages, p)
for p in packages.chosen:
addPathWithNimFiles(p, info)
proc babelPath*(path: string, info: TLineInfo) =
addPathRec(path, info)
addBabelPath(path, info)

View file

@ -42,6 +42,7 @@ proc parseDefine(p: var TParser): PNode =
result = newNodeP(nkTemplateDef, p)
getTok(p)
addSon(result, skipIdentExport(p))
addSon(result, ast.emptyNode)
eat(p, pxParLe)
var params = newNodeP(nkFormalParams, p)
# return type; not known yet:
@ -60,6 +61,7 @@ proc parseDefine(p: var TParser): PNode =
addSon(result, ast.emptyNode) # no generic parameters
addSon(result, params)
addSon(result, ast.emptyNode) # no pragmas
addSon(result, ast.emptyNode)
var kind = parseDefineBody(p, result)
params.sons[0] = newIdentNodeP(kind, p)
eatNewLine(p, result)

View file

@ -17,6 +17,8 @@ int aw_instance_callback_set (AW_CALLBACK c, callback_t callback);
unsigned long int wawa;
#define MAX(x, y) ((x) < (y)? (y) : (x))
#define AW_BUILD 85 // AW 5.0
// Limits
#define AW_MAX_AVCHANGE_PER_SECOND 10

View file

@ -146,7 +146,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc addComma(r: PRope): PRope =
result = if r == nil: r else: con(r, ~", ")
const CallPattern = "$1.ClEnv? $1.ClPrc($3$1.ClEnv) : (($4)($1.ClPrc))($2);$n"
const CallPattern = "$1.ClEnv? $1.ClPrc($3$1.ClEnv) : (($4)($1.ClPrc))($2)"
var op: TLoc
initLocExpr(p, ri.sons[0], op)
var pl: PRope
@ -164,7 +164,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
if i < length - 1: app(pl, ~", ")
template genCallPattern =
lineF(p, cpsStmts, CallPattern, op.r, pl, pl.addComma, rawProc)
lineF(p, cpsStmts, CallPattern & ";$n", op.r, pl, pl.addComma, rawProc)
let rawProc = getRawProcType(p, typ)
if typ.sons[0] != nil:

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -939,22 +939,31 @@ proc genNew(p: BProc, e: PNode) =
var
a, b: TLoc
reftype, bt: PType
sizeExpr: PRope
refType = skipTypes(e.sons[1].typ, abstractVarRange)
InitLocExpr(p, e.sons[1], a)
initLoc(b, locExpr, a.t, OnHeap)
# 'genNew' also handles 'unsafeNew':
if e.len == 3:
var se: TLoc
InitLocExpr(p, e.sons[2], se)
sizeExpr = se.rdLoc
else:
sizeExpr = ropef("sizeof($1)",
getTypeDesc(p.module, skipTypes(reftype.sons[0], abstractRange)))
let args = [getTypeDesc(p.module, reftype),
genTypeInfo(p.module, refType),
getTypeDesc(p.module, skipTypes(reftype.sons[0], abstractRange))]
sizeExpr]
if a.s == OnHeap and optRefcGc in gGlobalOptions:
# use newObjRC1 as an optimization; and we don't need 'keepAlive' either
if canFormAcycle(a.t):
linefmt(p, cpsStmts, "if ($1) #nimGCunref($1);$n", a.rdLoc)
else:
linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", a.rdLoc)
b.r = ropecg(p.module, "($1) #newObjRC1($2, sizeof($3))", args)
b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args)
linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc)
else:
b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", args)
b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
genAssignment(p, a, b, {needToKeepAlive}) # set the object type:
bt = skipTypes(refType.sons[0], abstractRange)
genObjectInit(p, cpsStmts, bt, a, false)
@ -1645,7 +1654,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
initLocExpr(p, n.sons[0], a)
var r = rdLoc(a)
if skipTypes(n.sons[0].typ, abstractInst).kind in {tyRef, tyPtr, tyVar} and
n.sons[0].kind notin {nkHiddenAddr, nkAddr}:
n.sons[0].kind notin {nkHiddenAddr, nkAddr, nkObjDownConv}:
app(r, "->Sup")
for i in countup(2, abs(inheritanceDiff(dest, src))): app(r, ".Sup")
r = con("&", r)

View file

@ -148,20 +148,47 @@ proc atEndMark(buf: cstring, pos: int): bool =
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
result = s == NimMergeEndMark.len
when false:
proc readVerbatimSection(L: var TBaseLexer): PRope =
var pos = L.bufpos
var buf = L.buf
result = newMutableRope(30_000)
while true:
case buf[pos]
of CR:
pos = lexbase.HandleCR(L, pos)
buf = L.buf
result.data.add(tnl)
of LF:
pos = lexbase.HandleLF(L, pos)
buf = L.buf
result.data.add(tnl)
of '\0':
InternalError("ccgmerge: expected: " & NimMergeEndMark)
break
else:
if atEndMark(buf, pos):
inc pos, NimMergeEndMark.len
break
result.data.add(buf[pos])
inc pos
L.bufpos = pos
freezeMutableRope(result)
proc readVerbatimSection(L: var TBaseLexer): PRope =
var pos = L.bufpos
var buf = L.buf
result = newMutableRope(30_000)
var r = newStringOfCap(30_000)
while true:
case buf[pos]
of CR:
pos = lexbase.HandleCR(L, pos)
buf = L.buf
result.data.add(tnl)
r.add(tnl)
of LF:
pos = lexbase.HandleLF(L, pos)
buf = L.buf
result.data.add(tnl)
r.add(tnl)
of '\0':
InternalError("ccgmerge: expected: " & NimMergeEndMark)
break
@ -169,10 +196,10 @@ proc readVerbatimSection(L: var TBaseLexer): PRope =
if atEndMark(buf, pos):
inc pos, NimMergeEndMark.len
break
result.data.add(buf[pos])
r.add(buf[pos])
inc pos
L.bufpos = pos
freezeMutableRope(result)
result = r.toRope
proc readKey(L: var TBaseLexer, result: var string) =
var pos = L.bufpos

View file

@ -122,6 +122,7 @@ proc genBreakState(p: BProc, n: PNode) =
lineF(p, cpsStmts, "if ((((NI*) $1.ClEnv)[0]) < 0) break;$n", [rdLoc(a)])
# lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)])
proc genVarPrototypeAux(m: BModule, sym: PSym)
proc genSingleVar(p: BProc, a: PNode) =
var v = a.sons[0].sym
if sfCompileTime in v.flags: return
@ -140,6 +141,9 @@ proc genSingleVar(p: BProc, a: PNode) =
genObjectInit(p.module.preInitProc, cpsInit, v.typ, v.loc, true)
# Alternative construction using default constructor (which may zeromem):
# if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
if sfExportc in v.flags and generatedHeader != nil:
genVarPrototypeAux(generatedHeader, v)
else:
assignLocalVar(p, v)
initLocalVar(p, v, immediateAsgn)
@ -878,7 +882,8 @@ proc genStmts(p: BProc, t: PNode) =
# we have to emit the type information for object types here to support
# separate compilation:
genTypeSection(p.module, t)
of nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, nkImportStmt,
of nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkTemplateDef, nkMacroDef:
nil
of nkPragma: genPragma(p, t)

View file

@ -558,17 +558,6 @@ include "ccgexprs.nim", "ccgstmts.nim"
# ----------------------------- dynamic library handling -----------------
# We don't finalize dynamic libs as this does the OS for us.
proc libCandidates(s: string, dest: var TStringSeq) =
var le = strutils.find(s, '(')
var ri = strutils.find(s, ')', le+1)
if le >= 0 and ri > le:
var prefix = substr(s, 0, le - 1)
var suffix = substr(s, ri + 1)
for middle in split(substr(s, le + 1, ri - 1), '|'):
libCandidates(prefix & middle & suffix, dest)
else:
add(dest, s)
proc isGetProcAddr(lib: PLib): bool =
let n = lib.path
result = n.kind in nkCallKinds and n.typ != nil and
@ -870,8 +859,6 @@ proc genVarPrototypeAux(m: BModule, sym: PSym) =
proc genVarPrototype(m: BModule, sym: PSym) =
genVarPrototypeAux(m, sym)
if sfExportc in sym.flags and generatedHeader != nil:
genVarPrototypeAux(generatedHeader, sym)
proc addIntTypes(result: var PRope) {.inline.} =
appf(result, "#define NIM_INTBITS $1", [

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -65,10 +65,10 @@ proc sameMethodBucket(a, b: PSym): bool =
break
if sameType(aa, bb) or
(aa.kind == tyObject) and (bb.kind == tyObject) and
(inheritanceDiff(bb, aa) < 0):
(inheritanceDiff(bb, aa) < 0):
nil
else:
return
else:
return
result = true
proc attachDispatcher(s: PSym, dispatcher: PNode) =
@ -106,17 +106,16 @@ proc methodDef*(s: PSym, fromCache: bool) =
# attach to itself to prevent bugs:
attachDispatcher(disp, newSymNode(disp))
proc relevantCol(methods: TSymSeq, col: int): bool =
proc relevantCol(methods: TSymSeq, col: int): bool =
# returns true iff the position is relevant
var t = methods[0].typ.sons[col]
result = false
if skipTypes(t, skipPtrs).kind == tyObject:
for i in countup(1, high(methods)):
if not SameType(methods[i].typ.sons[col], t):
if skipTypes(t, skipPtrs).kind == tyObject:
for i in countup(1, high(methods)):
let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
if not SameType(t2, t):
return true
proc cmpSignatures(a, b: PSym, relevantCols: TIntSet): int =
result = 0
for col in countup(1, sonsLen(a.typ) - 1):
if Contains(relevantCols, col):
var aa = skipTypes(a.typ.sons[col], skipPtrs)

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -11,7 +11,7 @@
import
os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists,
wordrecg, parseutils
wordrecg, parseutils, babelcmd
proc writeCommandLineUsage*()
@ -28,7 +28,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdlinePass, info: TLineInfo)
const
HelpMessage = "Nimrod Compiler Version $1 (" & compileDate & ") [$2: $3]\n" &
"Copyright (c) 2004-2012 by Andreas Rumpf\n"
"Copyright (c) 2004-2013 by Andreas Rumpf\n"
const
Usage = slurp"doc/basicopt.txt".replace("//", "")
@ -194,20 +194,6 @@ proc processPath(path: string): string =
"projectname", options.gProjectName,
"projectpath", options.gProjectPath])
proc addPath(path: string, info: TLineInfo) =
if not contains(options.searchPaths, path):
lists.PrependStr(options.searchPaths, path)
proc addPathRec(dir: string, info: TLineInfo) =
var pos = dir.len-1
if dir[pos] in {DirSep, AltSep}: inc(pos)
for k,p in os.walkDir(dir):
if k == pcDir and p[pos] != '.':
addPathRec(p, info)
if not contains(options.searchPaths, p):
Message(info, hintPath, p)
lists.PrependStr(options.searchPaths, p)
proc track(arg: string, info: TLineInfo) =
var a = arg.split(',')
if a.len != 3: LocalError(info, errTokenExpected, "FILE,LINE,COLUMN")
@ -227,11 +213,16 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
of "path", "p":
expectArg(switch, arg, pass, info)
addPath(processPath(arg), info)
of "recursivepath":
of "babelpath":
if pass in {passCmd2, passPP}:
expectArg(switch, arg, pass, info)
let path = processPath(arg)
babelpath(path, info)
of "excludepath":
expectArg(switch, arg, pass, info)
var path = processPath(arg)
addPathRec(path, info)
addPath(path, info)
let path = processPath(arg)
lists.ExcludeStr(options.searchPaths, path)
lists.ExcludeStr(options.lazyPaths, path)
of "nimcache":
expectArg(switch, arg, pass, info)
options.nimcacheDir = processPath(arg)
@ -474,6 +465,9 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
of "stdout":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optStdout)
of "listfullpaths":
expectNoArg(switch, arg, pass, info)
gListFullPaths = true
else:
if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
else: InvalidCmdLineOption(pass, switch, info)

View file

@ -60,6 +60,7 @@ proc InitDefines*() =
DefineSymbol("niminheritable")
DefineSymbol("nimmixin")
DefineSymbol("nimeffects")
DefineSymbol("nimbabel")
# add platform specific symbols:
case targetCPU

View file

@ -33,7 +33,7 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1):
var imported = getModuleName(n.sons[i])
addDependencyAux(g.module.name.s, imported)
of nkFromStmt:
of nkFromStmt, nkImportExceptStmt:
var imported = getModuleName(n.sons[0])
addDependencyAux(g.module.name.s, imported)
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:

View file

@ -137,7 +137,27 @@ proc genRecComment(d: PDoc, n: PNode): PRope =
if result != nil: return
else:
n.comment = nil
proc findDocComment(n: PNode): PNode =
if n == nil: return nil
if not isNil(n.comment) and startsWith(n.comment, "##"): return n
for i in countup(0, safeLen(n)-1):
result = findDocComment(n.sons[i])
if result != nil: return
proc extractDocComment*(s: PSym, d: PDoc = nil): string =
let n = findDocComment(s.ast)
result = ""
if not n.isNil:
if not d.isNil:
var dummyHasToc: bool
renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
toLineNumber(n.info), toColumn(n.info),
dummyHasToc, d.options + {roSkipPounds}),
result)
else:
result = n.comment.substr(2).replace("\n##", "\n").strip
proc isVisible(n: PNode): bool =
result = false
if n.kind == nkPostfix:
@ -145,6 +165,9 @@ proc isVisible(n: PNode): bool =
var v = n.sons[0].ident
result = v.id == ord(wStar) or v.id == ord(wMinus)
elif n.kind == nkSym:
# we cannot generate code for forwarded symbols here as we have no
# exception tracking information here. Instead we copy over the comment
# from the proc header.
result = {sfExported, sfFromGeneric, sfForward}*n.sym.flags == {sfExported}
elif n.kind == nkPragmaExpr:
result = isVisible(n.sons[0])
@ -273,7 +296,7 @@ proc generateDoc*(d: PDoc, n: PNode) =
generateDoc(d, lastSon(n.sons[0]))
of nkImportStmt:
for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i])
of nkFromStmt: traceDeps(d, n.sons[0])
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0])
else: nil
proc genSection(d: PDoc, kind: TSymKind) =
@ -350,9 +373,11 @@ proc CommandRstAux(filename, outExt: string) =
var d = newDocumentor(filen, options.gConfigVars)
var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc,
{roSupportRawDirective})
d.modDesc = newMutableRope(30_000)
renderRstToOut(d[], rst, d.modDesc.data)
freezeMutableRope(d.modDesc)
var modDesc = newStringOfCap(30_000)
#d.modDesc = newMutableRope(30_000)
renderRstToOut(d[], rst, modDesc)
#freezeMutableRope(d.modDesc)
d.modDesc = toRope(modDesc)
writeOutput(d, filename, outExt)
generateIndex(d)

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -207,27 +207,42 @@ proc genObjectInfo(p: var TProc, typ: PType, name: PRope) =
appf(p.g.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])])
proc genEnumInfo(p: var TProc, typ: PType, name: PRope) =
var
s, n: PRope
length: int
field: PSym
length = sonsLen(typ.n)
s = nil
proc genTupleFields(p: var TProc, typ: PType): PRope =
var s: PRope = nil
for i in 0 .. <typ.len:
if i > 0: app(s, ", " & tnl)
s.appf("{kind: 1, offset: \"Field$1\", len: 0, " &
"typ: $2, name: \"Field$1\", sons: null}",
[i.toRope, genTypeInfo(p, typ.sons[i])])
result = ropef("{kind: 2, len: $1, offset: 0, " &
"typ: null, name: null, sons: [$2]}", [toRope(typ.len), s])
proc genTupleInfo(p: var TProc, typ: PType, name: PRope) =
var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))])
prepend(p.g.typeInfo, s)
appf(p.g.typeInfo, "var NNI$1 = $2;$n",
[toRope(typ.id), genTupleFields(p, typ)])
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
proc genEnumInfo(p: var TProc, typ: PType, name: PRope) =
let length = sonsLen(typ.n)
var s: PRope = nil
for i in countup(0, length - 1):
if (typ.n.sons[i].kind != nkSym): InternalError(typ.n.info, "genEnumInfo")
field = typ.n.sons[i].sym
let field = typ.n.sons[i].sym
if i > 0: app(s, ", " & tnl)
let extName = if field.ast == nil: field.name.s else: field.ast.strVal
appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
[toRope(field.position), name, makeCString(field.name.s)])
n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
[toRope(field.position), name, makeCString(extName)])
var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
"name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s])
s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))])
prepend(p.g.typeInfo, s)
app(p.g.typeInfo, n)
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
if typ.sons[0] != nil:
if typ.sons[0] != nil:
appf(p.g.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])])
@ -259,7 +274,8 @@ proc genTypeInfo(p: var TProc, typ: PType): PRope =
appf(p.g.typeInfo, "$1.base = $2;$n",
[result, genTypeInfo(p, typ.sons[1])])
of tyEnum: genEnumInfo(p, t, result)
of tyObject, tyTuple: genObjectInfo(p, t, result)
of tyObject: genObjectInfo(p, t, result)
of tyTuple: genTupleInfo(p, t, result)
else: InternalError("genTypeInfo(" & $t.kind & ')')
proc gen(p: var TProc, n: PNode, r: var TCompRes)
@ -938,7 +954,9 @@ proc genSym(p: var TProc, n: PNode, r: var TCompRes) =
of skProc, skConverter, skMethod:
discard mangleName(s)
r.res = s.loc.r
if lfNoDecl in s.loc.flags or s.magic != mNone or isGenericRoutine(s): nil
if lfNoDecl in s.loc.flags or s.magic != mNone or isGenericRoutine(s) or
{sfImportc, sfInfixCall} * s.flags != {}:
nil
elif s.kind == skMethod and s.getBody.kind == nkEmpty:
# we cannot produce code for the dispatcher yet:
nil
@ -962,24 +980,45 @@ proc genDeref(p: var TProc, n: PNode, r: var TCompRes) =
if a.kind != etyBaseIndex: InternalError(n.info, "genDeref")
r.res = ropef("$1[$2]", [a.com, a.res])
proc genArg(p: var TProc, n: PNode, r: var TCompRes) =
var a: TCompRes
gen(p, n, a)
if a.kind == etyBaseIndex:
app(r.res, a.com)
app(r.res, ", ")
app(r.res, a.res)
else:
app(r.res, mergeExpr(a))
proc genArgs(p: var TProc, n: PNode, r: var TCompRes) =
app(r.res, "(")
for i in countup(1, sonsLen(n) - 1):
if i > 1: app(r.res, ", ")
var a: TCompRes
gen(p, n.sons[i], a)
if a.kind == etyBaseIndex:
app(r.res, a.com)
app(r.res, ", ")
app(r.res, a.res)
else:
app(r.res, mergeExpr(a))
genArg(p, n.sons[i], r)
app(r.res, ")")
proc genCall(p: var TProc, n: PNode, r: var TCompRes) =
gen(p, n.sons[0], r)
genArgs(p, n, r)
proc genInfixCall(p: var TProc, n: PNode, r: var TCompRes) =
gen(p, n.sons[1], r)
if r.kind == etyBaseIndex:
if r.com == nil:
GlobalError(n.info, "cannot invoke with infix syntax")
r.res = ropef("$1[0]", [r.res, r.com])
r.com = nil
app(r.res, ".")
var op: TCompRes
gen(p, n.sons[0], op)
app(r.res, mergeExpr(op))
app(r.res, "(")
for i in countup(2, sonsLen(n) - 1):
if i > 2: app(r.res, ", ")
genArg(p, n.sons[i], r)
app(r.res, ")")
proc genEcho(p: var TProc, n: PNode, r: var TCompRes) =
useMagic(p, "rawEcho")
app(r.res, "rawEcho")
@ -1176,10 +1215,13 @@ proc genConStrStr(p: var TProc, n: PNode, r: var TCompRes) =
proc genRepr(p: var TProc, n: PNode, r: var TCompRes) =
var t = skipTypes(n.sons[1].typ, abstractVarRange)
case t.kind
of tyInt..tyInt64:
unaryExpr(p, n, r, "", "reprInt($1)")
of tyInt..tyUInt64:
unaryExpr(p, n, r, "", "(\"\"+ ($1))")
of tyEnum, tyOrdinal:
binaryExpr(p, n, r, "", "reprEnum($1, $2)")
gen(p, n.sons[1], r)
useMagic(p, "cstrToNimstr")
r.res = ropef("cstrToNimstr($1.node.sons[$2].name)",
[genTypeInfo(p, t), r.res])
else:
# XXX:
internalError(n.info, "genRepr: Not implemented")
@ -1463,7 +1505,8 @@ proc genStmt(p: var TProc, n: PNode, r: var TCompRes) =
of nkAsmStmt: genAsmStmt(p, n, r)
of nkTryStmt: genTryStmt(p, n, r)
of nkRaiseStmt: genRaiseStmt(p, n, r)
of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt,
of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: nil
of nkProcDef, nkMethodDef, nkConverterDef:
var s = n.sons[namePos].sym
@ -1510,9 +1553,12 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) =
else: r.res = toRope(f.ToStrMaxPrecision)
of nkBlockExpr: genBlock(p, n, r)
of nkIfExpr: genIfExpr(p, n, r)
of nkCallKinds:
of nkCallKinds:
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
genMagic(p, n, r)
elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and
n.len >= 2:
genInfixCall(p, n, r)
else:
genCall(p, n, r)
of nkCurly: genSetConstr(p, n, r)
@ -1526,6 +1572,7 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) =
of nkCheckedFieldExpr: genCheckedFieldAccess(p, n, r)
of nkObjDownConv: gen(p, n.sons[0], r)
of nkObjUpConv: upConv(p, n, r)
of nkCast: gen(p, n.sons[1], r)
of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF")
of nkChckRange64: genRangeChck(p, n, r, "chckRange64")
of nkChckRange: genRangeChck(p, n, r, "chckRange")
@ -1555,7 +1602,7 @@ proc newModule(module: PSym): BModule =
proc genHeader(): PRope =
result = ropef("/* Generated by the Nimrod Compiler v$1 */$n" &
"/* (c) 2012 Andreas Rumpf */$n$n" & "$nvar Globals = this;$n" &
"/* (c) 2013 Andreas Rumpf */$n$n" & "$nvar Globals = this;$n" &
"var framePtr = null;$n" & "var excHandler = null;$n",
[toRope(versionAsString)])

443
compiler/evalffi.nim Normal file
View file

@ -0,0 +1,443 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This file implements the FFI part of the evaluator for Nimrod code.
import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs
when defined(windows):
const libcDll = "msvcrt.dll"
else:
const libcDll = "libc.so(.6|.5|)"
type
TDllCache = tables.TTable[string, TLibHandle]
var
gDllCache = initTable[string, TLibHandle]()
gExeHandle = LoadLib()
proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer =
result = cache[dll]
if result.isNil:
var libs: seq[string] = @[]
libCandidates(dll, libs)
for c in libs:
result = LoadLib(c)
if not result.isNil: break
if result.isNil:
GlobalError(info, "cannot load: " & dll)
cache[dll] = result
const
nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon
proc importcSymbol*(sym: PSym): PNode =
let name = ropeToStr(sym.loc.r)
# the AST does not support untyped pointers directly, so we use an nkIntLit
# that contains the address instead:
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
case name
of "stdin": result.intVal = cast[TAddress](system.stdin)
of "stdout": result.intVal = cast[TAddress](system.stdout)
of "stderr": result.intVal = cast[TAddress](system.stderr)
else:
let lib = sym.annex
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
GlobalError(sym.info, "dynlib needs to be a string lit for the REPL")
var theAddr: pointer
if lib.isNil and not gExehandle.isNil:
# first try this exe itself:
theAddr = gExehandle.symAddr(name)
# then try libc:
if theAddr.isNil:
let dllhandle = gDllCache.getDll(libcDll, sym.info)
theAddr = dllhandle.checkedSymAddr(name)
else:
let dllhandle = gDllCache.getDll(lib.path.strVal, sym.info)
theAddr = dllhandle.checkedSymAddr(name)
result.intVal = cast[TAddress](theAddr)
proc mapType(t: ast.PType): ptr libffi.TType =
if t == nil: return addr libffi.type_void
case t.kind
of tyBool, tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tySet:
case t.getSize
of 1: result = addr libffi.type_uint8
of 2: result = addr libffi.type_sint16
of 4: result = addr libffi.type_sint32
of 8: result = addr libffi.type_sint64
else: result = nil
of tyFloat, tyFloat64: result = addr libffi.type_double
of tyFloat32: result = addr libffi.type_float
of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr, tyNil:
result = addr libffi.type_pointer
of tyDistinct:
result = mapType(t.sons[0])
else:
result = nil
# too risky:
#of tyFloat128: result = addr libffi.type_longdouble
proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI =
case cc
of ccDefault: result = DEFAULT_ABI
of ccStdCall: result = when defined(windows): STDCALL else: DEFAULT_ABI
of ccCDecl: result = DEFAULT_ABI
else:
GlobalError(info, "cannot map calling convention to FFI")
template rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[]
template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v
template `+!`(x, y: expr): expr {.immediate.} =
cast[pointer](cast[TAddress](x) + y)
proc packSize(v: PNode, typ: PType): int =
## computes the size of the blob
case typ.kind
of tyPtr, tyRef, tyVar:
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
else:
result = sizeof(pointer) + packSize(v.sons[0], typ.sons[0])
of tyDistinct, tyGenericInst:
result = packSize(v, typ.sons[0])
of tyArray, tyArrayConstr:
# consider: ptr array[0..1000_000, int] which is common for interfacing;
# we use the real length here instead
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
elif v.len != 0:
result = v.len * packSize(v.sons[0], typ.sons[1])
else:
result = typ.getSize.int
proc pack(v: PNode, typ: PType, res: pointer)
proc getField(n: PNode; position: int): PSym =
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
result = getField(n.sons[i], position)
if result != nil: return
of nkRecCase:
result = getField(n.sons[0], position)
if result != nil: return
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
result = getField(lastSon(n.sons[i]), position)
if result != nil: return
else: internalError(n.info, "getField(record case branch)")
of nkSym:
if n.sym.position == position: result = n.sym
else: nil
proc packObject(x: PNode, typ: PType, res: pointer) =
InternalAssert x.kind == nkPar
# compute the field's offsets:
discard typ.getSize
for i in countup(0, sonsLen(x) - 1):
var it = x.sons[i]
if it.kind == nkExprColonExpr:
internalAssert it.sons[0].kind == nkSym
let field = it.sons[0].sym
pack(it.sons[1], field.typ, res +! field.offset)
elif typ.n != nil:
let field = getField(typ.n, i)
pack(it, field.typ, res +! field.offset)
else:
GlobalError(x.info, "cannot pack unnamed tuple")
const maxPackDepth = 20
var packRecCheck = 0
proc pack(v: PNode, typ: PType, res: pointer) =
template awr(T, v: expr) {.immediate, dirty.} =
wr(T, res, v)
case typ.kind
of tyBool: awr(bool, v.intVal != 0)
of tyChar: awr(char, v.intVal.chr)
of tyInt: awr(int, v.intVal.int)
of tyInt8: awr(int8, v.intVal.int8)
of tyInt16: awr(int16, v.intVal.int16)
of tyInt32: awr(int32, v.intVal.int32)
of tyInt64: awr(int64, v.intVal.int64)
of tyUInt: awr(uint, v.intVal.uint)
of tyUInt8: awr(uint8, v.intVal.uint8)
of tyUInt16: awr(uint16, v.intVal.uint16)
of tyUInt32: awr(uint32, v.intVal.uint32)
of tyUInt64: awr(uint64, v.intVal.uint64)
of tyEnum, tySet:
case v.typ.getSize
of 1: awr(uint8, v.intVal.uint8)
of 2: awr(uint16, v.intVal.uint16)
of 4: awr(int32, v.intVal.int32)
of 8: awr(int64, v.intVal.int64)
else:
GlobalError(v.info, "cannot map value to FFI (tyEnum, tySet)")
of tyFloat: awr(float, v.floatVal)
of tyFloat32: awr(float32, v.floatVal)
of tyFloat64: awr(float64, v.floatVal)
of tyPointer, tyProc, tyCString, tyString:
if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway
nil
elif v.kind == nkPtrLit:
awr(pointer, cast[pointer](v.intVal))
elif v.kind in {nkStrLit..nkTripleStrLit}:
awr(cstring, cstring(v.strVal))
else:
GlobalError(v.info, "cannot map pointer/proc value to FFI")
of tyPtr, tyRef, tyVar:
if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway
nil
elif v.kind == nkPtrLit:
awr(pointer, cast[pointer](v.intVal))
else:
if packRecCheck > maxPackDepth:
packRecCheck = 0
GlobalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
inc packRecCheck
pack(v.sons[0], typ.sons[0], res +! sizeof(pointer))
dec packRecCheck
awr(pointer, res +! sizeof(pointer))
of tyArray, tyArrayConstr:
let baseSize = typ.sons[1].getSize
for i in 0 .. <v.len:
pack(v.sons[i], typ.sons[1], res +! i * baseSize)
of tyObject, tyTuple:
packObject(v, typ, res)
of tyNil:
nil
of tyDistinct, tyGenericInst:
pack(v, typ.sons[0], res)
else:
GlobalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
proc unpack(x: pointer, typ: PType, n: PNode): PNode
proc unpackObjectAdd(x: pointer, n, result: PNode) =
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
unpackObjectAdd(x, n.sons[i], result)
of nkRecCase:
GlobalError(result.info, "case objects cannot be unpacked")
of nkSym:
var pair = newNodeI(nkExprColonExpr, result.info, 2)
pair.sons[0] = n
pair.sons[1] = unpack(x +! n.sym.offset, n.sym.typ, nil)
#echo "offset: ", n.sym.name.s, " ", n.sym.offset
result.add pair
else: nil
proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
# compute the field's offsets:
discard typ.getSize
# iterate over any actual field of 'n' ... if n is nil we need to create
# the nkPar node:
if n.isNil:
result = newNode(nkPar)
result.typ = typ
if typ.n.isNil:
InternalError("cannot unpack unnamed tuple")
unpackObjectAdd(x, typ.n, result)
else:
result = n
if result.kind != nkPar:
GlobalError(n.info, "cannot map value from FFI")
if typ.n.isNil:
GlobalError(n.info, "cannot unpack unnamed tuple")
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
if it.kind == nkExprColonExpr:
internalAssert it.sons[0].kind == nkSym
let field = it.sons[0].sym
it.sons[1] = unpack(x +! field.offset, field.typ, it.sons[1])
else:
let field = getField(typ.n, i)
n.sons[i] = unpack(x +! field.offset, field.typ, it)
proc unpackArray(x: pointer, typ: PType, n: PNode): PNode =
if n.isNil:
result = newNode(nkBracket)
result.typ = typ
newSeq(result.sons, lengthOrd(typ).int)
else:
result = n
if result.kind != nkBracket:
GlobalError(n.info, "cannot map value from FFI")
let baseSize = typ.sons[1].getSize
for i in 0 .. < result.len:
result.sons[i] = unpack(x +! i * baseSize, typ.sons[1], result.sons[i])
proc canonNodeKind(k: TNodeKind): TNodeKind =
case k
of nkCharLit..nkUInt64Lit: result = nkIntLit
of nkFloatLit..nkFloat128Lit: result = nkFloatLit
of nkStrLit..nkTripleStrLit: result = nkStrLit
else: result = k
proc unpack(x: pointer, typ: PType, n: PNode): PNode =
template aw(k, v, field: expr) {.immediate, dirty.} =
if n.isNil:
result = newNode(k)
result.typ = typ
else:
# check we have the right field:
result = n
if result.kind.canonNodeKind != k.canonNodeKind:
#echo "expected ", k, " but got ", result.kind
#debug result
return newNodeI(nkExceptBranch, n.info)
#GlobalError(n.info, "cannot map value from FFI")
result.field = v
template setNil() =
if n.isNil:
result = newNode(nkNilLit)
result.typ = typ
else:
reset n[]
result = n
result.kind = nkNilLit
result.typ = typ
template awi(kind, v: expr) {.immediate, dirty.} = aw(kind, v, intVal)
template awf(kind, v: expr) {.immediate, dirty.} = aw(kind, v, floatVal)
template aws(kind, v: expr) {.immediate, dirty.} = aw(kind, v, strVal)
case typ.kind
of tyBool: awi(nkIntLit, rd(bool, x).ord)
of tyChar: awi(nkCharLit, rd(char, x).ord)
of tyInt: awi(nkIntLit, rd(int, x))
of tyInt8: awi(nkInt8Lit, rd(int8, x))
of tyInt16: awi(nkInt16Lit, rd(int16, x))
of tyInt32: awi(nkInt32Lit, rd(int32, x))
of tyInt64: awi(nkInt64Lit, rd(int64, x))
of tyUInt: awi(nkUIntLit, rd(uint, x).biggestInt)
of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).biggestInt)
of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).biggestInt)
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).biggestInt)
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).biggestInt)
of tyEnum:
case typ.getSize
of 1: awi(nkIntLit, rd(uint8, x).biggestInt)
of 2: awi(nkIntLit, rd(uint16, x).biggestInt)
of 4: awi(nkIntLit, rd(int32, x).biggestInt)
of 8: awi(nkIntLit, rd(int64, x).biggestInt)
else:
GlobalError(n.info, "cannot map value from FFI (tyEnum, tySet)")
of tyFloat: awf(nkFloatLit, rd(float, x))
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
of tyPointer, tyProc:
let p = rd(pointer, x)
if p.isNil:
setNil()
elif n != nil and n.kind == nkStrLit:
# we passed a string literal as a pointer; however strings are already
# in their unboxed representation so nothing it to be unpacked:
result = n
else:
awi(nkPtrLit, cast[TAddress](p))
of tyPtr, tyRef, tyVar:
let p = rd(pointer, x)
if p.isNil:
setNil()
elif n == nil or n.kind == nkPtrLit:
awi(nkPtrLit, cast[TAddress](p))
elif n != nil and n.len == 1:
internalAssert n.kind == nkRefTy
n.sons[0] = unpack(p, typ.sons[0], n.sons[0])
result = n
else:
GlobalError(n.info, "cannot map value from FFI " & typeToString(typ))
of tyObject, tyTuple:
result = unpackObject(x, typ, n)
of tyArray, tyArrayConstr:
result = unpackArray(x, typ, n)
of tyCString, tyString:
let p = rd(cstring, x)
if p.isNil:
setNil()
else:
aws(nkStrLit, $p)
of tyNil:
setNil()
of tyDistinct, tyGenericInst:
result = unpack(x, typ.sons[0], n)
else:
# XXX what to do with 'array' here?
GlobalError(n.info, "cannot map value from FFI " & typeToString(typ))
proc fficast*(x: PNode, destTyp: PType): PNode =
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyPointer,
tyProc, tyCString, tyString,
tySequence}:
result = newNodeIT(x.kind, x.info, destTyp)
result.intVal = x.intVal
elif x.kind == nkNilLit:
result = newNodeIT(x.kind, x.info, destTyp)
else:
# we play safe here and allocate the max possible size:
let size = max(packSize(x, x.typ), packSize(x, destTyp))
var a = alloc0(size)
pack(x, x.typ, a)
# cast through a pointer needs a new inner object:
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
else: x.copyTree
y.typ = x.typ
result = unpack(a, destTyp, y)
dealloc a
proc callForeignFunction*(call: PNode): PNode =
InternalAssert call.sons[0].kind == nkPtrLit
var cif: TCif
var sig: TParamList
# use the arguments' types for varargs support:
for i in 1..call.len-1:
sig[i-1] = mapType(call.sons[i].typ)
if sig[i-1].isNil:
GlobalError(call.info, "cannot map FFI type")
let typ = call.sons[0].typ
if prep_cif(cif, mapCallConv(typ.callConv, call.info), cuint(call.len-1),
mapType(typ.sons[0]), sig) != OK:
GlobalError(call.info, "error in FFI call")
var args: TArgList
let fn = cast[pointer](call.sons[0].intVal)
for i in 1 .. call.len-1:
var t = call.sons[i].typ
args[i-1] = alloc0(packSize(call.sons[i], t))
pack(call.sons[i], t, args[i-1])
let retVal = if isEmptyType(typ.sons[0]): pointer(nil)
else: alloc(typ.sons[0].getSize.int)
libffi.call(cif, fn, retVal, args)
if retVal.isNil:
result = emptyNode
else:
result = unpack(retVal, typ.sons[0], nil)
result.info = call.info
if retVal != nil: dealloc retVal
for i in 1 .. call.len-1:
call.sons[i] = unpack(args[i-1], typ.sons[i], call[i])
dealloc args[i-1]

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -18,6 +18,9 @@ import
msgs, os, condsyms, idents, renderer, types, passes, semfold, transf,
parser, ropes, rodread, idgen, osproc, streams, evaltempl
when hasFFI:
import evalffi
type
PStackFrame* = ref TStackFrame
TStackFrame*{.final.} = object
@ -34,12 +37,20 @@ type
## emConst?)
emStatic ## evaluate for enforced compile time eval
## ('static' context)
TSandboxFlag* = enum ## what the evaluation engine should allow
allowCast, ## allow unsafe language feature: 'cast'
allowFFI, ## allow the FFI
allowInfiniteLoops ## allow endless loops
TSandboxFlags* = set[TSandboxFlag]
TEvalContext* = object of passes.TPassContext
module*: PSym
tos*: PStackFrame # top of stack
lastException*: PNode
callsite: PNode # for 'callsite' magic
mode*: TEvalMode
features: TSandboxFlags
globals*: TIdNodeTable # state of global vars
getType*: proc(n: PNode): PNode {.closure.}
@ -65,6 +76,7 @@ proc newEvalContext*(module: PSym, mode: TEvalMode): PEvalContext =
new(result)
result.module = module
result.mode = mode
result.features = {allowFFI}
initIdNodeTable(result.globals)
proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} =
@ -78,7 +90,7 @@ proc popStackFrame*(c: PEvalContext) {.inline.} =
proc evalMacroCall*(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode
proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode
proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode =
proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode =
result = newNodeI(nkExceptBranch, info)
# creating a nkExceptBranch without sons
# means that it could not be evaluated
@ -162,9 +174,12 @@ proc evalWhile(c: PEvalContext, n: PNode): PNode =
of nkExceptBranch, nkReturnToken: break
else: nil
dec(gWhileCounter)
if gWhileCounter <= 0:
stackTrace(c, n, errTooManyIterations)
break
if gWhileCounter <= 0:
if allowInfiniteLoops in c.features:
gWhileCounter = 0
else:
stackTrace(c, n, errTooManyIterations)
break
proc evalBlock(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n.sons[1], {})
@ -303,9 +318,54 @@ proc evalVar(c: PEvalContext, n: PNode): PNode =
else:
if x.kind notin {nkEmpty..nkNilLit}:
discardSons(x)
for i in countup(0, sonsLen(result) - 1): addSon(x, result.sons[i])
for j in countup(0, sonsLen(result) - 1): addSon(x, result.sons[j])
result = emptyNode
proc aliasNeeded(n: PNode, flags: TEvalFlags): bool =
result = efLValue in flags or n.typ == nil or
n.typ.kind in {tyExpr, tyStmt, tyTypeDesc}
proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode =
# We need to return a node to the actual value,
# which can be modified.
var x = c
while x != nil:
if sym.kind == skResult and x.params.len > 0:
result = x.params[0]
if result == nil: result = emptyNode
return
result = IdNodeTableGet(x.mapping, sym)
if result != nil and not aliasNeeded(result, flags):
result = copyTree(result)
if result != nil: return
x = x.next
#internalError(sym.info, "cannot eval " & sym.name.s)
result = raiseCannotEval(nil, sym.info)
#result = emptyNode
proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode =
if sfCompileTime in s.flags or c.mode == emRepl:
result = IdNodeTableGet(c.globals, s)
if result != nil:
if not aliasNeeded(result, flags):
result = copyTree(result)
else:
when hasFFI:
if sfImportc in s.flags and allowFFI in c.features:
result = importcSymbol(s)
IdNodeTablePut(c.globals, s, result)
return result
result = s.ast
if result == nil or result.kind == nkEmpty:
result = getNullValue(s.typ, s.info)
else:
result = evalAux(c, result, {})
if isSpecial(result): return
IdNodeTablePut(c.globals, s, result)
else:
result = raiseCannotEval(nil, s.info)
proc evalCall(c: PEvalContext, n: PNode): PNode =
var d = newStackFrame()
d.call = n
@ -336,51 +396,21 @@ proc evalCall(c: PEvalContext, n: PNode): PNode =
if isSpecial(result): return
d.params[i] = result
if n.typ != nil: d.params[0] = getNullValue(n.typ, n.info)
when hasFFI:
if sfImportc in prc.sym.flags and allowFFI in c.features:
var newCall = newNodeI(nkCall, n.info, n.len)
newCall.sons[0] = evalGlobalVar(c, prc.sym, {})
for i in 1 .. <n.len:
newCall.sons[i] = d.params[i]
return callForeignFunction(newCall)
pushStackFrame(c, d)
result = evalAux(c, prc.sym.getBody, {})
if result.kind == nkExceptBranch: return
if n.typ != nil: result = d.params[0]
popStackFrame(c)
proc aliasNeeded(n: PNode, flags: TEvalFlags): bool =
result = efLValue in flags or n.typ == nil or
n.typ.kind in {tyExpr, tyStmt, tyTypeDesc}
proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode =
# We need to return a node to the actual value,
# which can be modified.
var x = c
while x != nil:
if sym.kind == skResult and x.params.len > 0:
result = x.params[0]
if result == nil: result = emptyNode
return
result = IdNodeTableGet(x.mapping, sym)
if result != nil and not aliasNeeded(result, flags):
result = copyTree(result)
if result != nil: return
x = x.next
#internalError(sym.info, "cannot eval " & sym.name.s)
result = raiseCannotEval(nil, sym.info)
#result = emptyNode
proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode =
if sfCompileTime in s.flags or c.mode == emRepl:
result = IdNodeTableGet(c.globals, s)
if result != nil:
if not aliasNeeded(result, flags):
result = copyTree(result)
else:
result = s.ast
if result == nil or result.kind == nkEmpty:
result = getNullValue(s.typ, s.info)
else:
result = evalAux(c, result, {})
if isSpecial(result): return
IdNodeTablePut(c.globals, s, result)
else:
result = raiseCannotEval(nil, s.info)
proc evalArrayAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
result = evalAux(c, n.sons[0], flags)
if isSpecial(result): return
@ -519,7 +549,9 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
of skConst: result = s.ast
of skEnumField: result = newIntNodeT(s.position, n)
else: result = nil
if result == nil or {sfImportc, sfForward} * s.flags != {}:
let mask = if hasFFI and allowFFI in c.features: {sfForward}
else: {sfImportc, sfForward}
if result == nil or mask * s.flags != {}:
result = raiseCannotEval(c, n.info)
proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode =
@ -617,6 +649,18 @@ proc evalConv(c: PEvalContext, n: PNode): PNode =
# foldConv() cannot deal with everything that we want to do here:
result = a
proc evalCast(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
if allowCast in c.features:
when hasFFI:
result = evalAux(c, n.sons[1], {efLValue})
if isSpecial(result): return
InternalAssert result.typ != nil
result = fficast(result, n.typ)
else:
result = evalConv(c, n)
else:
result = raiseCannotEval(c, n.info)
proc evalCheckedFieldAccess(c: PEvalContext, n: PNode,
flags: TEvalFlags): PNode =
result = evalAux(c, n.sons[0], flags)
@ -1363,7 +1407,9 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
result.typ = n.typ
of nkPragmaBlock:
result = evalAux(c, n.sons[1], flags)
of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
of nkCast:
result = evalCast(c, n, flags)
of nkIdentDefs, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
nkLambdaKinds, nkContinueStmt, nkIdent, nkParForStmt, nkBindStmt:
result = raiseCannotEval(c, n.info)
of nkRefTy:
@ -1388,7 +1434,7 @@ proc eval*(c: PEvalContext, n: PNode): PNode =
if sonsLen(result) >= 1:
stackTrace(c, n, errUnhandledExceptionX, typeToString(result.typ))
else:
stackTrace(c, n, errCannotInterpretNodeX, renderTree(n))
stackTrace(c, result, errCannotInterpretNodeX, renderTree(n))
proc evalConstExprAux(module: PSym, e: PNode, mode: TEvalMode): PNode =
var p = newEvalContext(module, mode)
@ -1431,8 +1477,9 @@ proc evalMacroCall(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode =
dec(evalTemplateCounter)
c.callsite = nil
proc myOpen(module: PSym): PPassContext =
proc myOpen(module: PSym): PPassContext =
var c = newEvalContext(module, emRepl)
c.features = {allowCast, allowFFI, allowInfiniteLoops}
pushStackFrame(c, newStackFrame())
result = c

View file

@ -11,11 +11,10 @@
import
intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
semdata, passes
semdata, passes, renderer
proc evalImport*(c: PContext, n: PNode): PNode
proc evalFrom*(c: PContext, n: PNode): PNode
proc importAllSymbols*(c: PContext, fromMod: PSym)
proc getModuleName*(n: PNode): string =
# This returns a short relative module name without the nim extension
@ -29,8 +28,11 @@ proc getModuleName*(n: PNode): string =
of nkSym:
result = n.sym.name.s
else:
internalError(n.info, "getModuleName")
result = ""
# hacky way to implement 'x / y /../ z':
result = renderTree(n, {renderNoComments}).replace(" ")
#localError(n.info, errGenerated,
# "invalide module name: '$1'" % renderTree(n))
#result = ""
proc checkModuleName*(n: PNode): int32 =
# This returns the full canonical path for a given module import
@ -42,42 +44,43 @@ proc checkModuleName*(n: PNode): int32 =
else:
result = fullPath.fileInfoIdx
proc rawImportSymbol(c: PContext, s: PSym) =
proc rawImportSymbol(c: PContext, s: PSym) =
# This does not handle stubs, because otherwise loading on demand would be
# pointless in practice. So importing stubs is fine here!
var copy = s # do not copy symbols when importing!
# check if we have already a symbol of the same name:
var check = StrTableGet(c.tab.stack[importTablePos], s.name)
if check != nil and check.id != copy.id:
if s.kind notin OverloadableSyms:
if check != nil and check.id != s.id:
if s.kind notin OverloadableSyms:
# s and check need to be qualified:
Incl(c.AmbiguousSymbols, copy.id)
Incl(c.AmbiguousSymbols, s.id)
Incl(c.AmbiguousSymbols, check.id)
StrTableAdd(c.tab.stack[importTablePos], copy)
if s.kind == skType:
# thanks to 'export' feature, it could be we import the same symbol from
# multiple sources, so we need to call 'StrTableAdd' here:
StrTableAdd(c.tab.stack[importTablePos], s)
if s.kind == skType:
var etyp = s.typ
if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags:
for j in countup(0, sonsLen(etyp.n) - 1):
if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags:
for j in countup(0, sonsLen(etyp.n) - 1):
var e = etyp.n.sons[j].sym
if (e.Kind != skEnumField):
if e.Kind != skEnumField:
InternalError(s.info, "rawImportSymbol")
# BUGFIX: because of aliases for enums the symbol may already
# have been put into the symbol table
# BUGFIX: but only iff they are the same symbols!
var it: TIdentIter
check = InitIdentIter(it, c.tab.stack[importTablePos], e.name)
while check != nil:
if check.id == e.id:
while check != nil:
if check.id == e.id:
e = nil
break
break
check = NextIdentIter(it, c.tab.stack[importTablePos])
if e != nil:
if e != nil:
rawImportSymbol(c, e)
else:
# rodgen assures that converters and patterns are no stubs
if s.kind == skConverter: addConverter(c, s)
if hasPattern(s): addPattern(c, s)
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
let ident = lookups.considerAcc(n)
let s = StrTableGet(fromMod.tab, ident)
@ -98,20 +101,43 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
rawImportSymbol(c, e)
e = NextIdentIter(it, fromMod.tab)
else: rawImportSymbol(c, s)
proc importAllSymbols(c: PContext, fromMod: PSym) =
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) =
var i: TTabIter
var s = InitTabIter(i, fromMod.tab)
while s != nil:
if s.kind != skModule:
if s.kind != skEnumField:
if not (s.Kind in ExportableSymKinds):
while s != nil:
if s.kind != skModule:
if s.kind != skEnumField:
if s.Kind notin ExportableSymKinds:
InternalError(s.info, "importAllSymbols: " & $s.kind)
rawImportSymbol(c, s) # this is correct!
if exceptSet.empty or s.name.id notin exceptSet:
rawImportSymbol(c, s)
s = NextIter(i, fromMod.tab)
proc importAllSymbols*(c: PContext, fromMod: PSym) =
var exceptSet: TIntSet
importAllSymbolsExcept(c, fromMod, exceptSet)
proc importForwarded(c: PContext, n: PNode, exceptSet: TIntSet) =
if n.isNil: return
case n.kind
of nkExportStmt:
for a in n:
assert a.kind == nkSym
let s = a.sym
if s.kind == skModule:
importAllSymbolsExcept(c, s, exceptSet)
elif exceptSet.empty or s.name.id notin exceptSet:
rawImportSymbol(c, s)
of nkExportExceptStmt:
localError(n.info, errGenerated, "'export except' not implemented")
else:
for i in 0 ..safeLen(n)-1:
importForwarded(c, n.sons[i], exceptSet)
proc evalImport(c: PContext, n: PNode): PNode =
result = n
var emptySet: TIntSet
for i in countup(0, sonsLen(n) - 1):
var f = checkModuleName(n.sons[i])
if f != InvalidFileIDX:
@ -120,7 +146,8 @@ proc evalImport(c: PContext, n: PNode): PNode =
Message(n.sons[i].info, warnDeprecated, m.name.s)
# ``addDecl`` needs to be done before ``importAllSymbols``!
addDecl(c, m) # add symbol to symbol table of module
importAllSymbols(c, m)
importAllSymbolsExcept(c, m, emptySet)
importForwarded(c, m.ast, emptySet)
proc evalFrom(c: PContext, n: PNode): PNode =
result = n
@ -131,3 +158,18 @@ proc evalFrom(c: PContext, n: PNode): PNode =
n.sons[0] = newSymNode(m)
addDecl(c, m) # add symbol to symbol table of module
for i in countup(1, sonsLen(n) - 1): importSymbol(c, n.sons[i], m)
proc evalImportExcept*(c: PContext, n: PNode): PNode =
result = n
checkMinSonsLen(n, 2)
var f = checkModuleName(n.sons[0])
if f != InvalidFileIDX:
var m = gImportModule(c.module, f)
n.sons[0] = newSymNode(m)
addDecl(c, m) # add symbol to symbol table of module
var exceptSet = initIntSet()
for i in countup(1, sonsLen(n) - 1):
let ident = lookups.considerAcc(n.sons[i])
exceptSet.incl(ident.id)
importAllSymbolsExcept(c, m, exceptSet)
importForwarded(c, m.ast, exceptSet)

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -257,6 +257,8 @@ proc captureVar(o: POuterContext, i: PInnerContext, local: PSym,
# Currently captures are restricted to a single level of nesting:
LocalError(info, errIllegalCaptureX, local.name.s)
i.fn.typ.callConv = ccClosure
#echo "captureVar ", i.fn.name.s, i.fn.id, " ", local.name.s, local.id
incl(i.fn.typ.flags, tfCapturesEnv)
# we need to remember which inner most closure belongs to this lambda:
@ -286,9 +288,10 @@ proc interestingVar(s: PSym): bool {.inline.} =
sfGlobal notin s.flags
proc semCaptureSym*(s, owner: PSym) =
if interestingVar(s) and owner.id != s.owner.id:
if interestingVar(s) and owner.id != s.owner.id and s.kind != skResult:
if owner.typ != nil and not isGenericRoutine(owner):
owner.typ.callConv = ccClosure
#echo "semCaptureSym ", owner.name.s, owner.id, " ", s.name.s, s.id
# since the analysis is not entirely correct, we don't set 'tfCapturesEnv'
# here

View file

@ -89,9 +89,25 @@ proc Remove*(list: var TLinkedList, entry: PListEntry) =
list.head = entry.next
if entry.next != nil: entry.next.prev = entry.prev
if entry.prev != nil: entry.prev.next = entry.next
proc bringToFront*(list: var TLinkedList, entry: PListEntry) =
if entry == list.head: return
if entry == list.tail: list.tail = entry.prev
if entry.next != nil: entry.next.prev = entry.prev
if entry.prev != nil: entry.prev.next = entry.next
entry.prev = nil
entry.next = list.head
list.head = entry
proc ExcludeStr*(list: var TLinkedList, data: string) =
var it = list.head
while it != nil:
let nxt = it.next
if PStrEntry(it).data == data: remove(list, it)
it = nxt
proc Find*(list: TLinkedList, fn: TCompareProc, closure: Pointer): PListEntry =
result = list.head
while result != nil:
while result != nil:
if fn(result, closure): return
result = result.next

View file

@ -103,17 +103,24 @@ proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
line[0] == ' ' or
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs)
proc LLreadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
var inTripleString = false
proc countTriples(s: string): int =
var i = 0
while i < s.len:
if s[i] == '"' and s[i+1] == '"' and s[i+2] == '"':
inc result
inc i, 2
inc i
proc LLreadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
s.s = ""
s.rd = 0
var line = newStringOfCap(120)
var triples = 0
while ReadLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
add(s.s, line)
add(s.s, "\n")
if line.contains("\"\"\""):
inTripleString = not inTripleString
if not continueLine(line, inTripleString): break
inc triples, countTriples(line)
if not continueLine(line, (triples and 1) == 1): break
inc(s.lineOffset)
result = min(bufLen, len(s.s) - s.rd)
if result > 0:

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -302,7 +302,7 @@ when has_LLVM_Backend:
compileProject()
proc CommandCompileToEcmaScript =
incl(gGlobalOptions, optSafeCode)
#incl(gGlobalOptions, optSafeCode)
setTarget(osEcmaScript, cpuEcmaScript)
#initDefines()
DefineSymbol("nimrod") # 'nimrod' is always defined
@ -316,6 +316,7 @@ proc InteractivePasses =
#setTarget(osNimrodVM, cpuNimrodVM)
initDefines()
DefineSymbol("nimrodvm")
when hasFFI: DefineSymbol("nimffi")
registerPass(verbosePass)
registerPass(semPass)
registerPass(evalPass)
@ -524,11 +525,11 @@ proc MainCommand =
gCmd = cmdGenDepend
wantMainModule()
CommandGenDepend()
of "dump":
of "dump":
gCmd = cmdDump
condsyms.ListSymbols()
for it in iterSearchPath(): MsgWriteln(it)
of "check":
for it in iterSearchPath(searchPaths): MsgWriteln(it)
of "check":
gCmd = cmdCheck
wantMainModule()
CommandCheck()

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -132,7 +132,7 @@ const
errNumberOutOfRange: "number $1 out of valid range",
errNnotAllowedInCharacter: "\\n not allowed in character literal",
errClosingBracketExpected: "closing ']' expected, but end of file reached",
errMissingFinalQuote: "missing final \'",
errMissingFinalQuote: "missing final \' for character literal",
errIdentifierExpected: "identifier expected, but found \'$1\'",
errNewlineExpected: "newline expected, but found \'$1\'",
errInvalidModuleName: "invalid module name: '$1'",
@ -575,7 +575,15 @@ template toFilename*(info: TLineInfo): string =
template toFullPath*(info: TLineInfo): string =
info.fileIndex.toFullPath
proc toMsgFilename*(info: TLineInfo): string =
if info.fileIndex < 0: result = "???"
else:
if gListFullPaths:
result = fileInfos[info.fileIndex].fullPath
else:
result = fileInfos[info.fileIndex].projPath
proc toLinenumber*(info: TLineInfo): int {.inline.} =
result = info.line
@ -666,7 +674,7 @@ proc writeContext(lastinfo: TLineInfo) =
var info = lastInfo
for i in countup(0, len(msgContext) - 1):
if msgContext[i] != lastInfo and msgContext[i] != info:
MsgWriteln(posContextFormat % [toFilename(msgContext[i]),
MsgWriteln(posContextFormat % [toMsgFilename(msgContext[i]),
coordToStr(msgContext[i].line),
coordToStr(msgContext[i].col),
getMessageStr(errInstantiationFrom, "")])
@ -720,7 +728,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
ignoreMsg = optHints notin gOptions or msg notin gNotes
frmt = posHintFormat
inc(gHintCounter)
let s = frmt % [toFilename(info), coordToStr(info.line),
let s = frmt % [toMsgFilename(info), coordToStr(info.line),
coordToStr(info.col), getMessageStr(msg, arg)]
if not ignoreMsg:
MsgWriteln(s)
@ -732,6 +740,9 @@ proc Fatal*(info: TLineInfo, msg: TMsgKind, arg = "") =
proc GlobalError*(info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doRaise)
proc GlobalError*(info: TLineInfo, arg: string) =
liMessage(info, errGenerated, arg, doRaise)
proc LocalError*(info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doNothing)

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -13,6 +13,7 @@ import
const
hasTinyCBackend* = defined(tinyc)
useEffectSystem* = true
hasFFI* = defined(useFFI)
type # please make sure we have under 32 options
# (improves code efficiency a lot!)
@ -91,7 +92,7 @@ var
optPatterns}
gGlobalOptions*: TGlobalOptions = {optRefcGC, optThreadAnalysis}
gExitcode*: int8
searchPaths*: TLinkedList
searchPaths*, lazyPaths*: TLinkedList
outFile*: string = ""
headerFile*: string = ""
gCmd*: TCommands = cmdNone # the command
@ -100,7 +101,10 @@ var
gWholeProject*: bool # for 'doc2': output any dependency
gEvalExpr*: string # expression for idetools --eval
gLastCmdTime*: float # when caas is enabled, we measure each command
gListFullPaths*: bool
proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
const
genSubDir* = "nimcache"
NimExt* = "nim"
@ -199,27 +203,53 @@ proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
result = joinPath(subdir, tail)
#echo "completeGeneratedFilePath(", f, ") = ", result
iterator iterSearchPath*(): string =
iterator iterSearchPath*(SearchPaths: TLinkedList): string =
var it = PStrEntry(SearchPaths.head)
while it != nil:
while it != nil:
yield it.data
it = PStrEntry(it.Next)
proc rawFindFile(f: string): string =
for it in iterSearchPath():
for it in iterSearchPath(SearchPaths):
result = JoinPath(it, f)
if ExistsFile(result):
if existsFile(result):
return result.canonicalizePath
result = ""
proc rawFindFile2(f: string): string =
var it = PStrEntry(lazyPaths.head)
while it != nil:
result = JoinPath(it.data, f)
if existsFile(result):
bringToFront(lazyPaths, it)
return result.canonicalizePath
it = PStrEntry(it.Next)
result = ""
proc FindFile*(f: string): string {.procvar.} =
result = rawFindFile(f)
if len(result) == 0: result = rawFindFile(toLower(f))
result = f.rawFindFile
if result.len == 0:
result = f.toLower.rawFindFile
if result.len == 0:
result = f.rawFindFile2
if result.len == 0:
result = f.toLower.rawFindFile2
proc findModule*(modulename: string): string {.inline.} =
# returns path to module
result = FindFile(AddFileExt(modulename, nimExt))
proc libCandidates*(s: string, dest: var seq[string]) =
var le = strutils.find(s, '(')
var ri = strutils.find(s, ')', le+1)
if le >= 0 and ri > le:
var prefix = substr(s, 0, le - 1)
var suffix = substr(s, ri + 1)
for middle in split(substr(s, le + 1, ri - 1), '|'):
libCandidates(prefix & middle & suffix, dest)
else:
add(dest, s)
proc binaryStrSearch*(x: openarray[string], y: string): int =
var a = 0
var b = len(x) - 1

View file

@ -48,8 +48,8 @@ proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
add(code, chr(ord(op)))
proc whichAlias*(p: PSym): TAliasRequest =
if p.typ.constraint != nil:
result = TAliasRequest(p.typ.constraint.strVal[0].ord)
if p.constraint != nil:
result = TAliasRequest(p.constraint.strVal[0].ord)
proc compileConstraints(p: PNode, result: var TPatternCode) =
case p.kind

View file

@ -875,10 +875,10 @@ proc parseExprStmt(p: var TParser): PNode =
getTok(p)
skipComment(p, result)
if p.tok.tokType == tkSad: getTok(p)
if not (p.tok.TokType in {tkOf, tkElif, tkElse, tkExcept}):
if p.tok.TokType notin {tkOf, tkElif, tkElse, tkExcept}:
let body = parseStmt(p)
addSon(result, newProcNode(nkDo, body.info, body))
while true:
while true:
if p.tok.tokType == tkSad: getTok(p)
var b: PNode
case p.tok.tokType
@ -903,30 +903,34 @@ proc parseExprStmt(p: var TParser): PNode =
addSon(b, parseStmt(p))
addSon(result, b)
if b.kind == nkElse: break
proc parseImportOrIncludeStmt(p: var TParser, kind: TNodeKind): PNode =
var a: PNode
proc parseImport(p: var TParser, kind: TNodeKind): PNode =
result = newNodeP(kind, p)
getTok(p) # skip `import` or `export`
optInd(p, result)
var a = parseExpr(p)
addSon(result, a)
if p.tok.tokType in {tkComma, tkExcept}:
if p.tok.tokType == tkExcept:
result.kind = succ(kind)
getTok(p)
optInd(p, result)
while p.tok.tokType notin {tkEof, tkSad, tkDed}:
a = parseExpr(p)
if a.kind == nkEmpty: break
addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, a)
expectNl(p)
proc parseIncludeStmt(p: var TParser): PNode =
result = newNodeP(nkIncludeStmt, p)
getTok(p) # skip `import` or `include`
optInd(p, result)
while true:
case p.tok.tokType
of tkEof, tkSad, tkDed:
break
of tkSymbol, tkAccent:
a = parseSymbol(p)
of tkRStrLit:
a = newStrNodeP(nkRStrLit, p.tok.literal, p)
getTok(p)
of tkStrLit:
a = newStrNodeP(nkStrLit, p.tok.literal, p)
getTok(p)
of tkTripleStrLit:
a = newStrNodeP(nkTripleStrLit, p.tok.literal, p)
getTok(p)
else:
parMessage(p, errIdentifierExpected, p.tok)
break
while p.tok.tokType notin {tkEof, tkSad, tkDed}:
var a = parseExpr(p)
if a.kind == nkEmpty: break
addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
@ -934,37 +938,16 @@ proc parseImportOrIncludeStmt(p: var TParser, kind: TNodeKind): PNode =
expectNl(p)
proc parseFromStmt(p: var TParser): PNode =
var a: PNode
result = newNodeP(nkFromStmt, p)
getTok(p) # skip `from`
optInd(p, result)
case p.tok.tokType
of tkSymbol, tkAccent:
a = parseSymbol(p)
of tkRStrLit:
a = newStrNodeP(nkRStrLit, p.tok.literal, p)
getTok(p)
of tkStrLit:
a = newStrNodeP(nkStrLit, p.tok.literal, p)
getTok(p)
of tkTripleStrLit:
a = newStrNodeP(nkTripleStrLit, p.tok.literal, p)
getTok(p)
else:
parMessage(p, errIdentifierExpected, p.tok)
return
var a = parseExpr(p)
addSon(result, a) #optInd(p, a);
eat(p, tkImport)
optInd(p, result)
while true:
case p.tok.tokType #optInd(p, a);
of tkEof, tkSad, tkDed:
break
of tkSymbol, tkAccent:
a = parseSymbol(p)
else:
parMessage(p, errIdentifierExpected, p.tok)
break
while p.tok.tokType notin {tkEof, tkSad, tkDed}:
a = parseExpr(p)
if a.kind == nkEmpty: break
addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
@ -1279,14 +1262,7 @@ proc parseEnum(p: var TParser): PNode =
result = newNodeP(nkEnumTy, p)
a = nil
getTok(p)
if false and p.tok.tokType == tkOf:
a = newNodeP(nkOfInherit, p)
getTok(p)
optInd(p, a)
addSon(a, parseTypeDesc(p))
addSon(result, a)
else:
addSon(result, ast.emptyNode)
addSon(result, ast.emptyNode)
optInd(p, result)
while true:
case p.tok.tokType
@ -1425,18 +1401,6 @@ proc parseDistinct(p: var TParser): PNode =
optInd(p, result)
addSon(result, parseTypeDesc(p))
proc parsePointerInTypeSection(p: var TParser, kind: TNodeKind): PNode =
result = newNodeP(kind, p)
getTok(p)
optInd(p, result)
if not isOperator(p.tok):
case p.tok.tokType
of tkObject: addSon(result, parseObject(p))
of tkTuple: addSon(result, parseTuple(p, true))
else:
if isExprStart(p):
addSon(result, parseTypeDesc(p))
proc parseTypeDef(p: var TParser): PNode =
result = newNodeP(nkTypeDef, p)
addSon(result, identWithPragma(p))
@ -1445,15 +1409,6 @@ proc parseTypeDef(p: var TParser): PNode =
if p.tok.tokType == tkEquals:
getTok(p)
optInd(p, result)
#var a: PNode
#case p.tok.tokType
#of tkObject: a = parseObject(p)
#of tkEnum: a = parseEnum(p)
#of tkDistinct: a = parseDistinct(p)
#of tkTuple: a = parseTuple(p, true)
#of tkRef: a = parsePointerInTypeSection(p, nkRefTy)
#of tkPtr: a = parsePointerInTypeSection(p, nkPtrTy)
#else: a = parseTypeDesc(p)
addSon(result, parseTypeDefAux(p))
else:
addSon(result, ast.emptyNode)
@ -1511,11 +1466,12 @@ proc simpleStmt(p: var TParser): PNode =
of tkBreak: result = parseBreakOrContinue(p, nkBreakStmt)
of tkContinue: result = parseBreakOrContinue(p, nkContinueStmt)
of tkCurlyDotLe: result = parseStmtPragma(p)
of tkImport: result = parseImportOrIncludeStmt(p, nkImportStmt)
of tkImport: result = parseImport(p, nkImportStmt)
of tkExport: result = parseImport(p, nkExportStmt)
of tkFrom: result = parseFromStmt(p)
of tkInclude: result = parseImportOrIncludeStmt(p, nkIncludeStmt)
of tkInclude: result = parseIncludeStmt(p)
of tkComment: result = newCommentStmt(p)
else:
else:
if isExprStart(p): result = parseExprStmt(p)
else: result = ast.emptyNode
if result.kind != nkEmpty: skipComment(p, result)

View file

@ -71,8 +71,8 @@ proc inSymChoice(sc, x: PNode): bool =
proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
# check param constraints first here as this is quite optimized:
if p.typ.constraint != nil:
result = matchNodeKinds(p.typ.constraint, n)
if p.constraint != nil:
result = matchNodeKinds(p.constraint, n)
if not result: return
if isNil(n.typ):
result = p.typ.kind in {tyEmpty, tyStmt}
@ -237,6 +237,15 @@ proc addToArgList(result, n: PNode) =
else:
for i in 0 .. <n.len: result.add(n.sons[i])
when false:
proc procPatternMatches*(c: PContext, s: PSym, n: PNode): bool =
## for AST-based overloading:
var ctx: TPatternContext
ctx.owner = s
ctx.c = c
ctx.formals = sonsLen(s.typ)-1
result = matches(ctx, s.ast.sons[patternPos], n)
proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
## returns a tree to semcheck if the rule triggered; nil otherwise
var ctx: TPatternContext

View file

@ -29,7 +29,7 @@ proc equalGenericParams(procA, procB: PNode): bool =
a = procA.sons[i].sym
b = procB.sons[i].sym
if (a.name.id != b.name.id) or
not sameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}): return
not sameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}): return
if (a.ast != nil) and (b.ast != nil):
if not ExprStructuralEquivalent(a.ast, b.ast): return
result = true
@ -44,7 +44,7 @@ proc SearchForProc*(c: PContext, fn: PSym, tos: int): PSym =
if equalGenericParams(result.ast.sons[genericParamsPos],
fn.ast.sons[genericParamsPos]):
case equalParams(result.typ.n, fn.typ.n)
of paramsEqual:
of paramsEqual:
return
of paramsIncompatible:
LocalError(fn.info, errNotOverloadable, fn.name.s)

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -753,8 +753,7 @@ proc doParamsAux(g: var TSrcGen, params: PNode) =
proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
if isNil(n): return
var
L: int
var
a: TContext
if n.comment != nil: pushCom(g, n)
case n.kind # atoms:
@ -1096,7 +1095,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
incl(a.flags, rfInConstExpr)
gsection(g, n, a, tkConst, "const")
of nkVarSection, nkLetSection:
L = sonsLen(n)
var L = sonsLen(n)
if L == 0: return
if n.kind == nkVarSection: putWithSpace(g, tkVar, "var")
else: putWithSpace(g, tkLet, "let")
@ -1134,13 +1133,27 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkCurlyDotLe, "{.")
gcomma(g, n, emptyContext)
put(g, tkCurlyDotRi, ".}")
of nkImportStmt:
putWithSpace(g, tkImport, "import")
of nkImportStmt, nkExportStmt:
if n.kind == nkImportStmt:
putWithSpace(g, tkImport, "import")
else:
putWithSpace(g, tkExport, "export")
gcoms(g)
indentNL(g)
gcommaAux(g, n, g.indent)
dedent(g)
putNL(g)
of nkImportExceptStmt, nkExportExceptStmt:
if n.kind == nkImportExceptStmt:
putWithSpace(g, tkImport, "import")
else:
putWithSpace(g, tkExport, "export")
gsub(g, n.sons[0])
put(g, tkSpaces, Space)
putWithSpace(g, tkExcept, "except")
gcommaAux(g, n, g.indent, 1)
gcoms(g)
putNL(g)
of nkFromStmt:
putWithSpace(g, tkFrom, "from")
gsub(g, n.sons[0])

View file

@ -330,9 +330,6 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
if r.s[r.pos] == '@':
inc(r.pos)
result.containerID = decodeVInt(r.s, r.pos)
if r.s[r.pos] == '`':
inc(r.pos)
result.constraint = decodeNode(r, UnknownLineInfo())
decodeLoc(r, result.loc, info)
while r.s[r.pos] == '^':
inc(r.pos)
@ -423,6 +420,9 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
result.offset = - 1
decodeLoc(r, result.loc, result.info)
result.annex = decodeLib(r, info)
if r.s[r.pos] == '#':
inc(r.pos)
result.constraint = decodeNode(r, UnknownLineInfo())
if r.s[r.pos] == '(':
if result.kind in routineKinds:
result.ast = decodeNodeLazyBody(r, result.info, result)

View file

@ -233,9 +233,6 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
if t.containerID != 0:
add(result, '@')
encodeVInt(t.containerID, result)
if t.constraint != nil:
add(result, '`')
encodeNode(w, UnknownLineInfo(), t.constraint, result)
encodeLoc(w, t.loc, result)
for i in countup(0, sonsLen(t) - 1):
if t.sons[i] == nil:
@ -295,6 +292,9 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
encodeVInt(s.offset, result)
encodeLoc(w, s.loc, result)
if s.annex != nil: encodeLib(w, s.annex, s.info, result)
if s.constraint != nil:
add(result, '#')
encodeNode(w, UnknownLineInfo(), s.constraint, result)
# lazy loading will soon reload the ast lazily, so the ast needs to be
# the last entry of a symbol:
if s.ast != nil:

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -38,13 +38,15 @@ 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, kind: TSymKind)
proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind)
proc addResultNode(c: PContext, n: PNode)
proc maybeAddResult(c: PContext, s: PSym, n: PNode)
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc fixImmediateParams(n: PNode): PNode
proc activate(c: PContext, n: PNode)
proc semQuoteAst(c: PContext, n: PNode): PNode
proc finishMethod(c: PContext, s: PSym)
proc IndexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
proc typeMismatch(n: PNode, formal, actual: PType) =
if formal.kind != tyError and actual.kind != tyError:

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -15,8 +15,22 @@ proc sameMethodDispatcher(a, b: PSym): bool =
if a.kind == skMethod and b.kind == skMethod:
var aa = lastSon(a.ast)
var bb = lastSon(b.ast)
if aa.kind == nkSym and bb.kind == nkSym and aa.sym == bb.sym:
result = true
if aa.kind == nkSym and bb.kind == nkSym:
if aa.sym == bb.sym:
result = true
else:
nil
# generics have no dispatcher yet, so we need to compare the method
# names; however, the names are equal anyway because otherwise we
# wouldn't even consider them to be overloaded. But even this does
# not work reliably! See tmultim6 for an example:
# method collide[T](a: TThing, b: TUnit[T]) is instantiated and not
# method collide[T](a: TUnit[T], b: TThing)! This means we need to
# *instantiate* every candidate! However, we don't keep more than 2-3
# candidated around so we cannot implement that for now. So in order
# to avoid subtle problems, the call remains ambiguous and needs to
# be disambiguated by the programmer; this way the right generic is
# instantiated.
proc resolveOverloads(c: PContext, n, orig: PNode,
filter: TSymKinds): TCandidate =
@ -84,6 +98,35 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
getProcHeader(best.calleeSym), getProcHeader(alt.calleeSym),
args])
proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
if a.kind == nkHiddenCallConv and a.sons[0].kind == nkSym and
isGenericRoutine(a.sons[0].sym):
let finalCallee = generateInstance(c, a.sons[0].sym, x.bindings, a.info)
a.sons[0].sym = finalCallee
a.sons[0].typ = finalCallee.typ
#a.typ = finalCallee.typ.sons[0]
proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
assert n.kind in nkCallKinds
if x.genericConverter:
for i in 1 .. <n.len:
instGenericConvertersArg(c, n.sons[i], x)
proc IndexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
var m: TCandidate
initCandidate(m, f)
result = paramTypesMatch(c, m, f, a, arg, nil)
if m.genericConverter and result != nil:
instGenericConvertersArg(c, result, m)
proc ConvertTo*(c: PContext, f: PType, n: PNode): PNode =
var m: TCandidate
initCandidate(m, f)
result = paramTypesMatch(c, m, f, n.typ, n, nil)
if m.genericConverter and result != nil:
instGenericConvertersArg(c, result, m)
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
assert x.state == csMatch
var finalCallee = x.calleeSym
@ -101,6 +144,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
if ContainsGenericType(result.typ): result.typ = errorType(c)
return
result = x.call
instGenericConvertersSons(c, result, x)
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
result.typ = finalCallee.typ.sons[0]

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -318,7 +318,7 @@ proc semIs(c: PContext, n: PNode): PNode =
if not containsGenericType(t1): result = evalIsOp(n)
proc semOpAux(c: PContext, n: PNode) =
let flags = {efDetermineType}
const flags = {efDetermineType}
for i in countup(1, n.sonsLen- 1):
var a = n.sons[i]
if a.kind == nkExprEqExpr and sonsLen(a) == 2:
@ -589,11 +589,12 @@ proc semStaticExpr(c: PContext, n: PNode): PNode =
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags): PNode =
if efWantIterator in flags:
result = semOverloadedCall(c, n, nOrig, {skIterator})
elif efInTypeOf in flags:
if flags*{efInTypeOf, efWantIterator} != {}:
# consider: 'for x in pReturningArray()' --> we don't want the restriction
# to 'skIterator' anymore; skIterator is preferred in sigmatch already for
# typeof support.
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
result = semOverloadedCall(c, n, nOrig,
result = semOverloadedCall(c, n, nOrig,
{skProc, skMethod, skConverter, skMacro, skTemplate, skIterator})
else:
result = semOverloadedCall(c, n, nOrig,
@ -663,6 +664,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = nil
else:
result = m.call
instGenericConvertersSons(c, result, m)
# we assume that a procedure that calls something indirectly
# has side-effects:
if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect)
@ -1658,6 +1660,26 @@ proc fixImmediateParams(n: PNode): PNode =
result = n
proc semExport(c: PContext, n: PNode): PNode =
var x = newNodeI(n.kind, n.info)
#let L = if n.kind == nkExportExceptStmt: L = 1 else: n.len
for i in 0.. <n.len:
let a = n.sons[i]
var o: TOverloadIter
var s = initOverloadIter(o, c, a)
if s == nil:
localError(a.info, errGenerated, "invalid expr for 'export': " &
renderTree(a))
while s != nil:
if s.kind in ExportableSymKinds+{skModule}:
x.add(newSymNode(s, a.info))
s = nextOverloadIter(o, c, a)
if c.module.ast.isNil:
c.module.ast = newNodeI(nkStmtList, n.info)
assert c.module.ast.kind == nkStmtList
c.module.ast.add x
result = n
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = n
if gCmd == cmdIdeTools: suggestExpr(c, n)
@ -1851,12 +1873,18 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkImportStmt:
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "import")
result = evalImport(c, n)
of nkImportExceptStmt:
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "import")
result = evalImportExcept(c, n)
of nkFromStmt:
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "from")
result = evalFrom(c, n)
of nkIncludeStmt:
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include")
result = evalInclude(c, n)
of nkExportStmt, nkExportExceptStmt:
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "export")
result = semExport(c, n)
of nkPragmaBlock:
result = semPragmaBlock(c, n)
of nkStaticStmt:

View file

@ -89,9 +89,7 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
# add it here, so that recursive generic procs are possible:
addDecl(c, result)
pushProcCon(c, result)
if result.kind in {skProc, skMethod, skConverter, skMacro}:
addResult(c, result.typ.sons[0], n.info, result.kind)
addResultNode(c, n)
maybeAddResult(c, result, n)
var b = n.sons[bodyPos]
var symMap: TIdTable
InitIdTable symMap
@ -163,6 +161,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
result.typ = newTypeS(tyProc, c)
rawAddSon(result.typ, nil)
result.typ.callConv = fn.typ.callConv
if result.kind == skIterator: result.typ.flags.incl(tfIterator)
var oldPrc = GenericCacheGet(fn, entry[])
if oldPrc == nil:
fn.procInstCache.safeAdd(entry)
@ -182,6 +181,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
popOwner()
c.friendModule = oldFriend
dec(c.InstCounter)
if result.kind == skMethod: finishMethod(c, result)
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
var cl: TReplTypeVars

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -146,8 +146,11 @@ proc catches(tracked: PEffects, e: PType) =
dec L
else:
inc i
setLen(tracked.exc.sons, L)
if not isNil(tracked.exc.sons):
setLen(tracked.exc.sons, L)
else:
assert L == 0
proc catchesAll(tracked: PEffects) =
if not isNil(tracked.exc.sons):
setLen(tracked.exc.sons, tracked.bottom)

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -172,11 +172,16 @@ proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode =
changeType(result, typ)
proc findShadowedVar(c: PContext, v: PSym): PSym =
for i in countdown(c.tab.tos - 2, 0):
for i in countdown(c.tab.tos - 2, ModuleTablePos+1):
let shadowed = StrTableGet(c.tab.stack[i], v.name)
if shadowed != nil and shadowed.kind in skLocalVars:
return shadowed
proc identWithin(n: PNode, s: PIdent): bool =
for i in 0 .. n.safeLen-1:
if identWithin(n.sons[i], s): return true
result = n.kind == nkSym and n.sym.name.id == s.id
proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
if isTopLevel(c):
result = semIdentWithPragma(c, kind, n, {sfExported})
@ -239,7 +244,10 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
let shadowed = findShadowedVar(c, v)
if shadowed != nil:
shadowed.flags.incl(sfShadowed)
Message(a.info, warnShadowIdent, v.name.s)
# a shadowed variable is an error unless it appears on the right
# side of the '=':
if warnShadowIdent in gNotes and not identWithin(def, v.name):
Message(a.info, warnShadowIdent, v.name.s)
if def != nil and def.kind != nkEmpty:
# this is only needed for the evaluation pass:
v.ast = def
@ -247,7 +255,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if a.kind != nkVarTuple:
v.typ = typ
b = newNodeI(nkIdentDefs, a.info)
if gCmd == cmdDoc:
if importantComments():
# keep documentation information:
b.comment = a.comment
addSon(b, newSymNode(v))
@ -287,7 +295,7 @@ proc semConst(c: PContext, n: PNode): PNode =
v.ast = def # no need to copy
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
var b = newNodeI(nkConstDef, a.info)
if gCmd == cmdDoc: b.comment = a.comment
if importantComments(): b.comment = a.comment
addSon(b, newSymNode(v))
addSon(b, ast.emptyNode) # no type description
addSon(b, copyTree(def))
@ -368,6 +376,15 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
b.add(ast.emptyNode)
stmts.add(b)
proc addForVarDecl(c: PContext, v: PSym) =
if warnShadowIdent in gNotes:
let shadowed = findShadowedVar(c, v)
if shadowed != nil:
# XXX should we do this here?
#shadowed.flags.incl(sfShadowed)
Message(v.info, warnShadowIdent, v.name.s)
addDecl(c, v)
proc semForVars(c: PContext, n: PNode): PNode =
result = n
var length = sonsLen(n)
@ -383,7 +400,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
# for an example:
v.typ = n.sons[length-2].typ
n.sons[0] = newSymNode(v)
if sfGenSym notin v.flags: addDecl(c, v)
if sfGenSym notin v.flags: addForVarDecl(c, v)
else:
LocalError(n.info, errWrongNumberOfVariables)
elif length-2 != sonsLen(iter):
@ -394,7 +411,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
v.typ = iter.sons[i]
n.sons[i] = newSymNode(v)
if sfGenSym notin v.flags: addDecl(c, v)
if sfGenSym notin v.flags: addForVarDecl(c, v)
Inc(c.p.nestedLoopCounter)
n.sons[length-1] = SemStmt(c, n.sons[length-1])
Dec(c.p.nestedLoopCounter)
@ -669,17 +686,18 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
if n.sons[pragmasPos].kind != nkEmpty:
pragma(c, s, n.sons[pragmasPos], lambdaPragmas)
s.options = gOptions
if n.sons[bodyPos].kind != nkEmpty:
if sfImportc in s.flags:
if n.sons[bodyPos].kind != nkEmpty:
if sfImportc in s.flags:
LocalError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
if efDetermineType notin flags:
pushProcCon(c, s)
addResult(c, s.typ.sons[0], n.info, skProc)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, s)
addResultNode(c, n)
popProcCon(c)
sideEffectsCheck(c, s)
#if efDetermineType notin flags:
# XXX not good enough; see tnamedparamanonproc.nim
pushProcCon(c, s)
addResult(c, s.typ.sons[0], n.info, skProc)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, s)
addResultNode(c, n)
popProcCon(c)
sideEffectsCheck(c, s)
else:
LocalError(n.info, errImplOfXexpected, s.name.s)
closeScope(c.tab) # close scope for parameters
@ -689,13 +707,16 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc activate(c: PContext, n: PNode) =
# XXX: This proc is part of my plan for getting rid of
# forward declarations. stay tuned.
case n.kind
of nkLambdaKinds:
discard semLambda(c, n, {})
of nkCallKinds:
for i in 1 .. <n.len: activate(c, n[i])
else:
nil
when false:
# well for now it breaks code ... I added the test case in main.nim of the
# compiler itself to break bootstrapping :P
case n.kind
of nkLambdaKinds:
discard semLambda(c, n, {})
of nkCallKinds:
for i in 1 .. <n.len: activate(c, n[i])
else:
nil
proc instantiateDestructor*(c: PContext, typ: PType): bool
@ -713,6 +734,12 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
useSym(t.sons[i].destructor),
n.sons[paramsPos][1][0]]))
proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
if s.typ.sons[0] != nil and
(s.kind != skIterator or s.typ.callConv == ccClosure):
addResult(c, s.typ.sons[0], n.info, s.kind)
addResultNode(c, n)
proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
validPragmas: TSpecialWords): PNode =
result = semProcAnnotation(c, n)
@ -780,6 +807,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
n.sons[pragmasPos] = proto.ast.sons[pragmasPos]
if n.sons[namePos].kind != nkSym: InternalError(n.info, "semProcAux")
n.sons[namePos].sym = proto
if importantComments() and not isNil(proto.ast.comment):
n.comment = proto.ast.comment
proto.ast = n # needed for code generation
popOwner()
pushOwner(s)
@ -792,17 +821,13 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if n.sons[genericParamsPos].kind == nkEmpty:
ParamsTypeCheck(c, s.typ)
pushProcCon(c, s)
if s.typ.sons[0] != nil and
(kind != skIterator or s.typ.callConv == ccClosure):
addResult(c, s.typ.sons[0], n.info, kind)
addResultNode(c, n)
maybeAddResult(c, s, n)
if sfImportc notin s.flags:
# no semantic checking for importc:
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
# unfortunately we cannot skip this step when in 'system.compiles'
# context as it may even be evaluated in 'system.compiles':
n.sons[bodyPos] = transformBody(c.module, semBody, s)
#if s.typ.sons[0] != nil and kind != skIterator: addResultNode(c, n)
popProcCon(c)
else:
if s.typ.sons[0] != nil and kind != skIterator:
@ -846,31 +871,30 @@ proc semIterator(c: PContext, n: PNode): PNode =
proc semProc(c: PContext, n: PNode): PNode =
result = semProcAux(c, n, skProc, procPragmas)
proc hasObjParam(s: PSym): bool =
var t = s.typ
for col in countup(1, sonsLen(t)-1):
if skipTypes(t.sons[col], skipPtrs).kind == tyObject:
return true
proc finishMethod(c: PContext, s: PSym) =
if hasObjParam(s):
methodDef(s, false)
proc semMethod(c: PContext, n: PNode): PNode =
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "method")
result = semProcAux(c, n, skMethod, methodPragmas)
var s = result.sons[namePos].sym
var t = s.typ
var hasObjParam = false
for col in countup(1, sonsLen(t)-1):
if skipTypes(t.sons[col], skipPtrs).kind == tyObject:
hasObjParam = true
break
# XXX this not really correct way to do it: Perhaps it should be done after
# generic instantiation. Well it's good enough for now:
if hasObjParam:
methodDef(s, false)
else:
LocalError(n.info, errXNeedsParamObjectType, "method")
if not isGenericRoutine(s):
if hasObjParam(s):
methodDef(s, false)
else:
LocalError(n.info, errXNeedsParamObjectType, "method")
proc semConverterDef(c: PContext, n: PNode): PNode =
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "converter")
checkSonsLen(n, bodyPos + 1)
if n.sons[genericParamsPos].kind != nkEmpty:
LocalError(n.info, errNoGenericParamsAllowedForX, "converter")
result = semProcAux(c, n, skConverter, converterPragmas)
var s = result.sons[namePos].sym
var t = s.typ
@ -1075,7 +1099,8 @@ proc insertDestructors(c: PContext, varSection: PNode):
varTyp = varId.sym.typ
info = varId.info
if varTyp != nil and instantiateDestructor(c, varTyp):
if varTyp != nil and instantiateDestructor(c, varTyp) and
sfGlobal notin varId.sym.flags:
var tryStmt = newNodeI(nkTryStmt, info)
if j < totalVars - 1:

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View file

@ -635,6 +635,13 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
genericParams.addSon(newSymNode(s))
result = typeClass
proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
if n.kind == nkCurlyExpr:
result = semTypeNode(c, n.sons[0], nil)
constraint = semNodeKindConstraints(n)
else:
result = semTypeNode(c, n, nil)
proc semProcTypeNode(c: PContext, n, genericParams: PNode,
prev: PType, kind: TSymKind): PType =
var
@ -660,13 +667,14 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
checkMinSonsLen(a, 3)
var
typ: PType = nil
def: PNode = nil
def: PNode = nil
constraint: PNode = nil
length = sonsLen(a)
hasType = a.sons[length-2].kind != nkEmpty
hasDefault = a.sons[length-1].kind != nkEmpty
if hasType:
typ = semTypeNode(c, a.sons[length-2], nil)
typ = semParamType(c, a.sons[length-2], constraint)
if hasDefault:
def = semExprWithType(c, a.sons[length-1])
@ -689,6 +697,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
arg.name.s, arg.info).skipIntLit
arg.typ = finalType
arg.position = counter
arg.constraint = constraint
inc(counter)
if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
if ContainsOrIncl(check, arg.name.id):
@ -787,6 +796,12 @@ proc semTypeExpr(c: PContext, n: PNode): PType =
else:
LocalError(n.info, errTypeExpected, n.renderTree)
proc freshType(res, prev: PType): PType {.inline.} =
if prev.isNil:
result = copyType(res, res.owner, keepId=false)
else:
result = res
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = nil
if gCmd == cmdIdeTools: suggestExpr(c, n)
@ -825,6 +840,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
checkSonsLen(n, 3)
result = semTypeNode(c, n.sons[1], prev)
if result.kind in NilableTypes and n.sons[2].kind == nkNilLit:
result = freshType(result, prev)
result.flags.incl(tfNotNil)
else:
LocalError(n.info, errGenerated, "invalid type")
@ -832,11 +848,6 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = semTypeExpr(c, n)
else:
result = semTypeExpr(c, n)
of nkCurlyExpr:
result = semTypeNode(c, n.sons[0], nil)
if result != nil:
result = copyType(result, getCurrOwner(), true)
result.constraint = semNodeKindConstraints(n)
of nkWhenStmt:
var whenResult = semWhen(c, n, false)
if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType
@ -919,6 +930,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkSharedTy:
checkSonsLen(n, 1)
result = semTypeNode(c, n.sons[0], prev)
result = freshType(result, prev)
result.flags.incl(tfShared)
else:
LocalError(n.info, errTypeExpected)

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -12,18 +12,19 @@
import
intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
magicsys, condsyms, idents, lexer, options
magicsys, condsyms, idents, lexer, options, parampatterns, strutils,
docgen
type
TCandidateState* = enum
csEmpty, csMatch, csNoMatch
TCandidate* {.final.} = object
exactMatches*: int
exactMatches*: int # also misused to prefer iters over procs
genericMatches: int # also misused to prefer constraints
subtypeMatches: int
intConvMatches: int # conversions to int are not as expensive
convMatches: int
genericMatches: int
state*: TCandidateState
callee*: PType # may not be nil!
calleeSym*: PSym # may be nil
@ -33,6 +34,8 @@ type
baseTypeMatch: bool # needed for conversions from T to openarray[T]
# for example
proxyMatch*: bool # to prevent instantiations
genericConverter*: bool # true if a generic converter needs to
# be instantiated
inheritancePenalty: int # to prefer closest father object type
TTypeRelation* = enum # order is important!
@ -57,6 +60,7 @@ proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} =
c.callee = callee
c.call = nil
c.baseTypeMatch = false
c.genericConverter = false
c.inheritancePenalty = 0
proc initCandidate*(c: var TCandidate, callee: PType) =
@ -571,17 +575,27 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
for i in countup(0, len(c.converters) - 1):
var src = c.converters[i].typ.sons[1]
var dest = c.converters[i].typ.sons[0]
if (typeRel(m, f, dest) == isEqual) and
(typeRel(m, src, a) == isEqual):
# for generic type converters we need to check 'src <- a' before
# 'f <- dest' in order to not break the unification:
# see tests/tgenericconverter:
let srca = typeRel(m, src, a)
if srca notin {isEqual, isGeneric}: continue
let destIsGeneric = containsGenericType(dest)
if destIsGeneric:
dest = generateTypeInstance(c, m.bindings, arg, dest)
let fdest = typeRel(m, f, dest)
if fdest in {isEqual, isGeneric}:
markUsed(arg, c.converters[i])
var s = newSymNode(c.converters[i])
s.typ = c.converters[i].typ
s.info = arg.info
result = newNodeIT(nkHiddenCallConv, arg.info, s.typ.sons[0])
result = newNodeIT(nkHiddenCallConv, arg.info, dest)
addSon(result, s)
addSon(result, copyTree(arg))
inc(m.convMatches)
return
m.genericConverter = srca == isGeneric or destIsGeneric
return result
proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
arg: PNode): PNode =
@ -692,8 +706,8 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
else:
result = userConvMatch(c, m, base(f), a, arg)
proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType,
arg, argOrig: PNode): PNode =
proc ParamTypesMatch*(c: PContext, m: var TCandidate, f, a: PType,
arg, argOrig: PNode): PNode =
if arg == nil or arg.kind notin nkSymChoices:
result = ParamTypesMatchAux(c, m, f, a, arg, argOrig)
else:
@ -739,27 +753,21 @@ proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType,
result = ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best],
argOrig)
proc IndexTypesMatch*(c: PContext, f, a: PType, arg: PNode): PNode =
var m: TCandidate
initCandidate(m, f)
result = paramTypesMatch(c, m, f, a, arg, nil)
proc ConvertTo*(c: PContext, f: PType, n: PNode): PNode =
var m: TCandidate
initCandidate(m, f)
result = paramTypesMatch(c, m, f, n.typ, n, nil)
proc argtypeMatches*(c: PContext, f, a: PType): bool =
var m: TCandidate
initCandidate(m, f)
result = paramTypesMatch(c, m, f, a, ast.emptyNode, nil) != nil
proc setSon(father: PNode, at: int, son: PNode) =
if sonsLen(father) <= at: setlen(father.sons, at + 1)
father.sons[at] = son
proc matchesAux*(c: PContext, n, nOrig: PNode,
m: var TCandidate, marker: var TIntSet) =
proc matchesAux(c: PContext, n, nOrig: PNode,
m: var TCandidate, marker: var TIntSet) =
template checkConstraint(n: expr) {.immediate, dirty.} =
if not formal.constraint.isNil:
if matchNodeKinds(formal.constraint, n):
# better match over other routines with no such restriction:
inc(m.genericMatches, 100)
else:
m.state = csNoMatch
return
var f = 1 # iterates over formal parameters
var a = 1 # iterates over the actual given arguments
m.state = csMatch # until proven otherwise
@ -792,7 +800,8 @@ proc matchesAux*(c: PContext, n, nOrig: PNode,
n.sons[a].sons[1], nOrig.sons[a].sons[1])
if arg == nil:
m.state = csNoMatch
return
return
checkConstraint(n.sons[a].sons[1])
if m.baseTypeMatch:
assert(container == nil)
container = newNodeI(nkBracket, n.sons[a].info)
@ -837,10 +846,10 @@ proc matchesAux*(c: PContext, n, nOrig: PNode,
m.baseTypeMatch = false
var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ,
n.sons[a], nOrig.sons[a])
if arg == nil:
if arg == nil:
m.state = csNoMatch
return
if m.baseTypeMatch:
return
if m.baseTypeMatch:
assert(container == nil)
container = newNodeI(nkBracket, n.sons[a].info)
addSon(container, arg)
@ -849,6 +858,7 @@ proc matchesAux*(c: PContext, n, nOrig: PNode,
if f != formalLen - 1: container = nil
else:
setSon(m.call, formal.position + 1, arg)
checkConstraint(n.sons[a])
inc(a)
inc(f)
@ -880,4 +890,13 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
setSon(m.call, formal.position + 1, copyTree(formal.ast))
inc(f)
proc argtypeMatches*(c: PContext, f, a: PType): bool =
var m: TCandidate
initCandidate(m, f)
let res = paramTypesMatch(c, m, f, a, ast.emptyNode, nil)
#instantiateGenericConverters(c, res, m)
# XXX this is used by patterns.nim too; I think it's better to not
# instantiate generic converters for that
result = res != nil
include suggest

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -39,6 +39,8 @@ proc SymToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string =
result.add($ToLinenumber(li))
result.add(sep)
result.add($ToColumn(li))
result.add(sep)
result.add(s.extractDocComment.escape)
proc SymToStr(s: PSym, isLocal: bool, section: string): string =
result = SymToStr(s, isLocal, section, s.info)

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -150,7 +150,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
newVar.owner = getCurrOwner(c)
IdNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar))
var defs = newTransNode(nkIdentDefs, it.info, 3)
if gCmd == cmdDoc:
if importantComments():
# keep documentation information:
pnode(defs).comment = it.comment
defs[0] = newSymNode(newVar).PTransNode
@ -665,7 +665,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
of nkIdentDefs, nkConstDef:
result = transformSons(c, n)
# XXX comment handling really sucks:
if gCmd == cmdDoc:
if importantComments():
pnode(result).comment = n.comment
else:
result = transformSons(c, n)

View file

@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -632,7 +632,8 @@ proc SameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool =
result = SameTypeAux(a, b, c)
proc equalParam(a, b: PSym): TParamsEquality =
if SameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}):
if SameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}) and
ExprStructuralEquivalent(a.constraint, b.constraint):
if a.ast == b.ast:
result = paramsEqual
elif a.ast != nil and b.ast != nil:
@ -875,20 +876,24 @@ proc inheritanceDiff*(a, b: PType): int =
# | returns: -x iff `a` is the x'th direct superclass of `b`
# | returns: +x iff `a` is the x'th direct subclass of `b`
# | returns: `maxint` iff `a` and `b` are not compatible at all
assert a.kind == tyObject
assert b.kind == tyObject
var x = a
result = 0
while x != nil:
x = skipTypes(x, skipPtrs)
if sameObjectTypes(x, b): return
x = x.sons[0]
dec(result)
var y = b
result = 0
while y != nil:
y = skipTypes(y, skipPtrs)
if sameObjectTypes(y, a): return
y = y.sons[0]
inc(result)
result = high(int)
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool
proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool =
result = true